token validation

This commit is contained in:
Aravinth Manivannan 2021-04-10 13:14:48 +05:30
parent 6f83b3cd0c
commit 2d120d6791
Signed by: realaravinth
GPG Key ID: AD9F0F08E855ED88
6 changed files with 95 additions and 31 deletions

View File

@ -6,6 +6,7 @@
as a result: as a result:
- <strike>`Retrieve`</strike> `RetrievePoW` now returns `CachedPoWConfig` - <strike>`Retrieve`</strike> `RetrievePoW` now returns `CachedPoWConfig`
- random token generation post `PoW` verification - random token generation post `PoW` verification
- token validation
## Changed ## Changed
- `Cache` became `CachePoW` (`HashCache` extension) - `Cache` became `CachePoW` (`HashCache` extension)

View File

@ -1,5 +1,5 @@
use m_captcha::{ use m_captcha::{
cache::HashCache, cache::{messages::VerifyCaptchaResult, HashCache},
master::{AddSiteBuilder, Master}, master::{AddSiteBuilder, Master},
pow::{ConfigBuilder, Work}, pow::{ConfigBuilder, Work},
system::SystemBuilder, system::SystemBuilder,
@ -107,11 +107,29 @@ async fn main() -> std::io::Result<()> {
key: mcaptcha_name.into(), key: mcaptcha_name.into(),
}; };
// Server evaluates client's work. Returns true if everything // mCAptcha evaluates client's work. Returns a token if everything
// checksout and Err() if something fishy is happening // checksout and Err() if something fishy is happening
let res = system.verify_pow(payload.clone()).await; let res = system.verify_pow(payload.clone()).await;
assert!(res.is_ok()); assert!(res.is_ok());
// TODO add server-sideverification
// The client should submit the token to the mCaptcha protected service
// The service should validate the token received from the client
// with the mCaptcha server before processing client's
// request
// mcaptcha protected service sends the following paylaod to mCaptcha
// server:
let verify_msg = VerifyCaptchaResult {
token: res.unwrap(),
key: mcaptcha_name.into(),
};
// on mCaptcha server:
let res = system.validate_verification_tokens(verify_msg).await;
// mCaptcha will return true if token is valid and false if
// token is invalid
assert!(res.is_ok());
assert!(res.unwrap());
Ok(()) Ok(())
} }

View File

@ -61,13 +61,13 @@ impl HashCache {
// save captcha result // save captcha result
fn save_captcha_result(&mut self, res: CacheResult) -> CaptchaResult<()> { fn save_captcha_result(&mut self, res: CacheResult) -> CaptchaResult<()> {
self.result_map.insert(res.result, res.key); self.result_map.insert(res.token, res.key);
Ok(()) Ok(())
} }
// verify captcha result // verify captcha result
fn verify_captcha_result(&mut self, challenge: VerifyCaptchaResult) -> CaptchaResult<bool> { fn verify_captcha_result(&mut self, challenge: VerifyCaptchaResult) -> CaptchaResult<bool> {
if let Some(captcha_id) = self.remove_cache_result(&challenge.result) { if let Some(captcha_id) = self.remove_cache_result(&challenge.token) {
if captcha_id == challenge.key { if captcha_id == challenge.key {
return Ok(true); return Ok(true);
} else { } else {
@ -141,7 +141,7 @@ impl Handler<CacheResult> for HashCache {
let addr = ctx.address(); let addr = ctx.address();
let del_msg = DeleteCaptchaResult { let del_msg = DeleteCaptchaResult {
result: msg.result.clone(), token: msg.token.clone(),
}; };
let duration: Duration = Duration::new(msg.duration.clone(), 0); let duration: Duration = Duration::new(msg.duration.clone(), 0);
@ -161,7 +161,7 @@ impl Handler<CacheResult> for HashCache {
impl Handler<DeleteCaptchaResult> for HashCache { impl Handler<DeleteCaptchaResult> for HashCache {
type Result = MessageResult<DeleteCaptchaResult>; type Result = MessageResult<DeleteCaptchaResult>;
fn handle(&mut self, msg: DeleteCaptchaResult, _ctx: &mut Self::Context) -> Self::Result { fn handle(&mut self, msg: DeleteCaptchaResult, _ctx: &mut Self::Context) -> Self::Result {
self.remove_cache_result(&msg.result); self.remove_cache_result(&msg.token);
MessageResult(Ok(())) MessageResult(Ok(()))
} }
} }
@ -252,7 +252,7 @@ mod tests {
let add_cache = CacheResult { let add_cache = CacheResult {
key: KEY.into(), key: KEY.into(),
result: RES.into(), token: RES.into(),
duration: DURATION, duration: DURATION,
}; };
@ -260,14 +260,14 @@ mod tests {
let verify_msg = VerifyCaptchaResult { let verify_msg = VerifyCaptchaResult {
key: KEY.into(), key: KEY.into(),
result: RES.into(), token: RES.into(),
}; };
assert!(addr.send(verify_msg).await.unwrap().unwrap()); assert!(addr.send(verify_msg).await.unwrap().unwrap());
let verify_msg = VerifyCaptchaResult { let verify_msg = VerifyCaptchaResult {
key: "cz".into(), key: "cz".into(),
result: RES.into(), token: RES.into(),
}; };
assert!(!addr.send(verify_msg).await.unwrap().unwrap()); assert!(!addr.send(verify_msg).await.unwrap().unwrap());
@ -276,7 +276,7 @@ mod tests {
let verify_msg = VerifyCaptchaResult { let verify_msg = VerifyCaptchaResult {
key: KEY.into(), key: KEY.into(),
result: RES.into(), token: RES.into(),
}; };
assert!(!addr.send(verify_msg).await.unwrap().unwrap()); assert!(!addr.send(verify_msg).await.unwrap().unwrap());
} }

14
src/cache/mod.rs vendored
View File

@ -42,7 +42,7 @@ pub mod messages {
use crate::errors::*; use crate::errors::*;
/// 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, Clone)]
#[rtype(result = "CaptchaResult<()>")] #[rtype(result = "CaptchaResult<()>")]
pub struct CachePoW { pub struct CachePoW {
pub string: String, pub string: String,
@ -68,7 +68,7 @@ pub mod messages {
#[rtype(result = "CaptchaResult<Option<CachedPoWConfig>>")] #[rtype(result = "CaptchaResult<Option<CachedPoWConfig>>")]
pub struct RetrivePoW(pub String); pub struct RetrivePoW(pub String);
#[derive(Clone, PartialEq, Debug, Default)] #[derive(Clone, PartialEq, Debug, Default, Deserialize, Serialize)]
pub struct CachedPoWConfig { pub struct CachedPoWConfig {
pub key: String, pub key: String,
pub difficulty_factor: u32, pub difficulty_factor: u32,
@ -86,7 +86,7 @@ pub mod messages {
#[derive(Message, Serialize, Deserialize, Builder)] #[derive(Message, Serialize, Deserialize, Builder)]
#[rtype(result = "CaptchaResult<()>")] #[rtype(result = "CaptchaResult<()>")]
pub struct CacheResult { pub struct CacheResult {
pub result: String, pub token: String,
// key is Captcha identifier // key is Captcha identifier
pub key: String, pub key: String,
pub duration: u64, pub duration: u64,
@ -99,7 +99,7 @@ pub mod messages {
CacheResultBuilder::default() CacheResultBuilder::default()
.key(c.key) .key(c.key)
.duration(c.duration) .duration(c.duration)
.result(get_random(32)) .token(get_random(32))
.build() .build()
.unwrap() .unwrap()
} }
@ -107,10 +107,10 @@ pub mod messages {
/// Message to verify captcha result against /// Message to verify captcha result against
/// the stored captcha key /// the stored captcha key
#[derive(Message)] #[derive(Message, Clone, Deserialize, Serialize)]
#[rtype(result = "CaptchaResult<bool>")] #[rtype(result = "CaptchaResult<bool>")]
pub struct VerifyCaptchaResult { pub struct VerifyCaptchaResult {
pub result: String, pub token: String,
pub key: String, pub key: String,
} }
@ -118,6 +118,6 @@ pub mod messages {
#[derive(Message)] #[derive(Message)]
#[rtype(result = "CaptchaResult<()>")] #[rtype(result = "CaptchaResult<()>")]
pub struct DeleteCaptchaResult { pub struct DeleteCaptchaResult {
pub result: String, pub token: String,
} }
} }

View File

@ -47,7 +47,7 @@
//! //!
//! ```rust //! ```rust
//! use m_captcha::{ //! use m_captcha::{
//! cache::HashCache, //! cache::{messages::VerifyCaptchaResult, HashCache},
//! master::{AddSiteBuilder, Master}, //! master::{AddSiteBuilder, Master},
//! pow::{ConfigBuilder, Work}, //! pow::{ConfigBuilder, Work},
//! system::SystemBuilder, //! system::SystemBuilder,
@ -160,7 +160,26 @@
//! let res = system.verify_pow(payload.clone()).await; //! let res = system.verify_pow(payload.clone()).await;
//! assert!(res.is_ok()); //! assert!(res.is_ok());
//! //!
//! Ok(()) //! // The client should submit the token to the mCaptcha protected service
//! // The service should validate the token received from the client
//! // with the mCaptcha server before processing client's
//! // request
//!
//! // mcaptcha protected service sends the following paylaod to mCaptcha
//! // server:
//! let verify_msg = VerifyCaptchaResult {
//! token: res.unwrap(),
//! key: mcaptcha_name.into(),
//! };
//!
//! // on mCaptcha server:
//! let res = system.validate_verification_tokens(verify_msg).await;
//! // mCaptcha will return true if token is valid and false if
//! // token is invalid
//! assert!(res.is_ok());
//! assert!(res.unwrap());
//!
//! Ok(())
//! } //! }
//! ``` //! ```
#![forbid(unsafe_code)] #![forbid(unsafe_code)]

View File

@ -20,7 +20,7 @@ use actix::dev::*;
use derive_builder::Builder; use derive_builder::Builder;
use pow_sha256::Config; use pow_sha256::Config;
use crate::cache::messages; use crate::cache::messages::*;
use crate::cache::Save; use crate::cache::Save;
use crate::errors::*; use crate::errors::*;
use crate::master::Master; use crate::master::Master;
@ -37,14 +37,13 @@ pub struct System<T: Save> {
impl<T> System<T> impl<T> System<T>
where where
T: Save, T: Save,
<T as actix::Actor>::Context: ToEnvelope<T, messages::CachePoW> <T as actix::Actor>::Context: ToEnvelope<T, CachePoW>
+ ToEnvelope<T, messages::RetrivePoW> + ToEnvelope<T, RetrivePoW>
+ ToEnvelope<T, messages::CacheResult> + ToEnvelope<T, CacheResult>
+ ToEnvelope<T, messages::VerifyCaptchaResult>, + ToEnvelope<T, VerifyCaptchaResult>,
{ {
/// 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::CachePoWBuilder;
use crate::master::GetSite; use crate::master::GetSite;
use crate::mcaptcha::AddVisitor; use crate::mcaptcha::AddVisitor;
@ -55,8 +54,6 @@ where
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 = CachePoWBuilder::default() let cache_msg = CachePoWBuilder::default()
.string(pow_config.string.clone()) .string(pow_config.string.clone())
.difficulty_factor(mcaptcha.difficulty_factor) .difficulty_factor(mcaptcha.difficulty_factor)
@ -71,8 +68,6 @@ where
/// utility function to verify [Work] /// utility function to verify [Work]
pub async fn verify_pow(&self, work: Work) -> CaptchaResult<String> { pub async fn verify_pow(&self, work: Work) -> CaptchaResult<String> {
use crate::cache::messages::*;
let string = work.string.clone(); let string = work.string.clone();
let msg = RetrivePoW(string.clone()); let msg = RetrivePoW(string.clone());
@ -102,10 +97,18 @@ where
} }
let msg: CacheResult = cached_config.into(); let msg: CacheResult = cached_config.into();
let res = msg.result.clone(); let res = msg.token.clone();
self.cache.send(msg).await.unwrap()?; self.cache.send(msg).await.unwrap()?;
Ok(res) Ok(res)
} }
/// utility function to validate verification tokens
pub async fn validate_verification_tokens(
&self,
msg: VerifyCaptchaResult,
) -> CaptchaResult<bool> {
self.cache.send(msg).await.unwrap()
}
} }
#[cfg(test)] #[cfg(test)]
@ -159,13 +162,18 @@ mod tests {
#[actix_rt::test] #[actix_rt::test]
async fn verify_pow_works() { async fn verify_pow_works() {
// start system
let actors = boostrap_system(10).await; let actors = boostrap_system(10).await;
// get work
let work_req = actors.get_pow(MCAPTCHA_NAME.into()).await.unwrap(); let work_req = actors.get_pow(MCAPTCHA_NAME.into()).await.unwrap();
// get config
let config = get_config(); let config = get_config();
// generate proof
let work = config let work = config
.prove_work(&work_req.string, work_req.difficulty_factor) .prove_work(&work_req.string, work_req.difficulty_factor)
.unwrap(); .unwrap();
// generate proof payload
let mut payload = Work { let mut payload = Work {
string: work_req.string, string: work_req.string,
result: work.result, result: work.result,
@ -173,9 +181,27 @@ mod tests {
key: MCAPTCHA_NAME.into(), key: MCAPTCHA_NAME.into(),
}; };
// verifiy proof
let res = actors.verify_pow(payload.clone()).await; let res = actors.verify_pow(payload.clone()).await;
assert!(res.is_ok()); assert!(res.is_ok());
// verify validation token
let mut verifi_msg = VerifyCaptchaResult {
token: res.unwrap(),
key: MCAPTCHA_NAME.into(),
};
assert!(actors
.validate_verification_tokens(verifi_msg.clone())
.await
.unwrap());
// verify wrong validation token
verifi_msg.token = MCAPTCHA_NAME.into();
assert!(!actors
.validate_verification_tokens(verifi_msg)
.await
.unwrap());
payload.string = "wrongstring".into(); payload.string = "wrongstring".into();
let res = actors.verify_pow(payload.clone()).await; let res = actors.verify_pow(payload.clone()).await;
assert_eq!(res, Err(CaptchaError::StringNotFound)); assert_eq!(res, Err(CaptchaError::StringNotFound));