1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
/*
 * mCaptcha - A proof of work based DoS protection system
 * Copyright © 2021 Aravinth Manivannan <realravinth@batsense.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
//! mCaptcha is a proof of work based Denaial-of-Service attack protection system.
//! This is is a server library that you can embed in your services to protect your
//! servers.
//!
//! A commercial managed solution is in the works but I'd much rather prefer
//! folks host their own instances as it will make the more decentralized and free.
//!
//! In mCaptcha, defense is adjusted in discrete levels that depend on the
//! ammount of traffic that a service is experiencing. So users of this library are
//! requested to benchmark their target machines before configuring their mCaptcha
//! component.
//!
//! ## Terminology:
//! - Difficulty(Factor): Minimum ammount of work that a client must do to make a valid
//! request.
//! - [Defense]: A datatype that various visitor-difficulty mappigns
//! - Visitor: Smallest unit of traffic, usually a single request. The more you have, the busier
//! your service is. Determines mCaptcha defense defense
//! - Visitor threshold: The threshold at which [MCaptcha] will adjust defense defense
//! - [Cache][crate::cache] : A datatype that implements [Save][crate::cache::Save]. Used to store
//! PoW requirements to defend against replay attacks and dictionary attacks.
//! - [Master][crate::master::Master]: A datatype that manages
//! [MCaptcha][crate::mcaptcha::MCaptcha] actors. Works like a DNS for
//! [AddVisitor][crate::mcaptcha::AddVisitor] messages.
//! - [System][crate::system::System]: mCaptcha system that manages cache, master and provides
//! useful abstractions. An mCaptcha system/instance can have only a single
//! [System][crate::system::System]
//!
//! ## Example:
//!
//! ```rust
//! use libmcaptcha::{
//!     cache::{messages::VerifyCaptchaResult, hashcache::HashCache},
//!     master::embedded::master:: Master,
//!     master::messages::AddSiteBuilder,
//!     pow::{ConfigBuilder, Work},
//!     system::SystemBuilder,
//!     DefenseBuilder, LevelBuilder, MCaptchaBuilder,
//! };
//! // traits from actix needs to be in scope for starting actor
//! use actix::prelude::*;
//!
//! #[actix_rt::main]
//! async fn main() -> std::io::Result<()> {
//!     // start cahce actor
//!     // cache is used to store PoW requirements that are sent to clients
//!     // This way, it can be verified that the client computed work over a config
//!     // that _we_ sent. Offers protection against rainbow tables powered dictionary attacks
//!     let cache = HashCache::default().start();
//!
//!     // create PoW config with unique salt. Salt has to be safely guarded.
//!     // salts protect us from replay attacks
//!     let pow = ConfigBuilder::default()
//!         .salt("myrandomsaltisnotlongenoug".into())
//!         .build()
//!         .unwrap();
//!
//!     // start master actor. Master actor is responsible for managing MCaptcha actors
//!     // each mCaptcha system should have only one master
//!     let master = Master::new(5).start();
//!
//!     // Create system. System encapsulates master and cache and provides useful abstraction
//!     // each mCaptcha system should have only one system
//!     let system = SystemBuilder::default()
//!         .master(master)
//!         .cache(cache)
//!         .pow(pow.clone())
//!         .runners(4)
//!         .queue_length(2000)
//!         .build();
//!
//!     // configure defense. This is a per site configuration. A site can have several levels
//!     // of defenses configured
//!     let defense = DefenseBuilder::default()
//!         // add as many defense as you see fit
//!         .add_level(
//!             LevelBuilder::default()
//!                 // visitor_threshold is the threshold/limit at which
//!                 // mCaptcha will adjust difficulty defense
//!                 // it is advisable to set small values for the first
//!                 // defense visitor_threshold and difficulty_factor
//!                 // as this will be the work that clients will be
//!                 // computing when there's no load
//!                 .visitor_threshold(50)
//!                 .difficulty_factor(500)
//!                 .unwrap()
//!                 .build()
//!                 .unwrap(),
//!         )
//!         .unwrap()
//!         .add_level(
//!             LevelBuilder::default()
//!                 .visitor_threshold(5000)
//!                 .difficulty_factor(50000)
//!                 .unwrap()
//!                 .build()
//!                 .unwrap(),
//!         )
//!         .unwrap()
//!         .build()
//!         .unwrap();
//!
//!     // create and start MCaptcha actor that uses the above defense configuration
//!     // This is what manages the difficulty factor of sites that an mCaptcha protects
//!     let mcaptcha = MCaptchaBuilder::default()
//!         .defense(defense)
//!         // leaky bucket algorithm's emission interval
//!         .duration(30)
//!         //   .cache(cache)
//!         .build()
//!         .unwrap();
//!
//!     // unique value identifying an MCaptcha actor
//!     let mcaptcha_name = "batsense.net";
//!
//!     // add MCaptcha to Master
//!     let msg = AddSiteBuilder::default()
//!         .id(mcaptcha_name.into())
//!         .mcaptcha(mcaptcha)
//!         .build()
//!         .unwrap();
//!     system.master.send(msg).await.unwrap();
//!
//!     // Get PoW config. Should be called everytime there's a visitor for a
//!     // managed site(here mcaptcha_name)
//!     let work_req = system.get_pow(mcaptcha_name.into()).await.unwrap().unwrap();
//!
//!     // the following computation should be done on the client but for the purpose
//!     // of this illustration, we are going to do it on the server it self
//!     let work = pow
//!         .prove_work(&work_req.string, work_req.difficulty_factor)
//!         .unwrap();
//!
//!     // the payload that the client sends to the server
//!     let payload = Work {
//!         string: work_req.string,
//!         result: work.result,
//!         nonce: work.nonce,
//!         key: mcaptcha_name.into(),
//!     };
//!
//!     // Server evaluates client's work. Returns true if everything
//!     // checksout and Err() if something fishy is happening
//!     let res = system.verify_pow(payload.clone(), "192.168.0.103".into()).await;
//!     assert!(res.is_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)]
pub mod defense;
pub mod errors;
pub mod master;

#[cfg(feature = "full")]
pub mod redis;

/// message datatypes to interact with [MCaptcha] actor
pub mod cache;
pub mod mcaptcha;
#[cfg(feature = "full")]
pub mod pow;
#[cfg(feature = "full")]
mod queue;
#[cfg(feature = "full")]
pub mod system;
#[cfg(feature = "full")]
mod utils;

#[cfg(feature = "full")]
pub use crate::cache::hashcache::HashCache;
#[cfg(feature = "full")]
pub use master::embedded::counter::Counter;

pub use crate::defense::{Defense, DefenseBuilder, LevelBuilder};
pub use crate::master::AddVisitorResult;
pub use crate::master::CreateMCaptcha;
pub use crate::mcaptcha::{MCaptcha, MCaptchaBuilder};

#[cfg(feature = "minimal")]
pub mod dev {
    pub use super::AddVisitorResult;
    pub use super::CreateMCaptcha;
    pub use crate::defense;
    pub use crate::defense::{Defense, DefenseBuilder, LevelBuilder};
    pub use crate::mcaptcha;
    pub use crate::mcaptcha::{MCaptcha, MCaptchaBuilder};
}