feat: Compute PoW by fetching parameters from CAPTCHA URL. Use reuse.software.

This commit is contained in:
Aravinth Manivannan 2023-08-13 13:24:09 +05:30
parent 3b0f8ffba0
commit 10d5e4e4ef
Signed by: realaravinth
GPG key ID: AD9F0F08E855ED88
3 changed files with 1086 additions and 55 deletions

943
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -12,3 +12,8 @@ edition = "2021"
[dependencies] [dependencies]
pow_sha256 = { version = "0.3.1", git = "https://github.com/mCaptcha/pow_sha256" } pow_sha256 = { version = "0.3.1", git = "https://github.com/mCaptcha/pow_sha256" }
clap = { version = "4.3", features = ["derive"] } clap = { version = "4.3", features = ["derive"] }
tokio = { version = "1.31.0", features = ["rt", "rt-multi-thread", "macros"] }
reqwest = { version = "0.11.18", features = ["json", "gzip"] }
serde = { version = "1.0.183", features = ["derive"] }
serde_json = "1.0.104"
url = { version = "2.4.0", features = ["serde"] }

View file

@ -1,29 +1,20 @@
/* // Copyright © 2023 Aravinth Manivannan <realravinth@batsense.net>
* mCaptcha - A proof of work based DoS protection system // SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
* Copyright © 2023 Aravinth Manivannan <realravinth@batsense.net> //
* // SPDX-License-Identifier: AGPL-3.0-or-later
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as use std::time::Instant;
* 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/>.
*/
use clap::*; use clap::*;
use pow_sha256::ConfigBuilder; use pow_sha256::ConfigBuilder;
use pow_sha256::PoW;
use reqwest::Client;
use serde::{Deserialize, Serialize};
use url::Url;
#[derive(Deserialize, Parser, Serialize, Clone, Debug)]
/// Compute PoW with offline parameters
/// Simple program to greet a person struct Offline {
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
/// Salt with which PoW should be computed /// Salt with which PoW should be computed
#[arg(short, long)] #[arg(short, long)]
salt: String, salt: String,
@ -32,22 +23,150 @@ struct Args {
#[arg(short, long)] #[arg(short, long)]
phrase: String, phrase: String,
/// Difficulty Factor /// Difficulty Factor
#[arg(short, long)] #[arg(short, long)]
difficulty_factor: u32, difficulty_factor: u32,
} }
fn main() { #[derive(Deserialize, Parser, Serialize, Clone, Debug)]
let matches = Args::parse(); /// Compute PoW by fetching parameters from CAPTCHA URL
struct Online {
/// URL of the CAPTCHA. Example: https://example.org/widget?sitekey=foo
#[arg(short, long)]
url: Url,
}
impl Online {
fn extract_sitekey(&self) -> Option<String> {
let mut sitekey = None;
for (k, v) in self.url.query_pairs() {
if &k == "sitekey" {
let x: &str = &v;
sitekey = Some(x.to_string())
}
}
sitekey
}
fn get_config_url(&self) -> Url {
let mut url = self.url.clone();
url.set_path("/api/v1/pow/config");
url
}
fn get_verify_url(&self) -> Url {
let mut url = self.url.clone();
url.set_path("/api/v1/pow/verify");
url
}
}
#[derive(Deserialize, Parser, Serialize, Clone, Debug)]
#[command(author, version, about, long_about = None)]
enum Args {
Offline(Offline),
Online(Online),
}
fn prove_work(phrase: String, salt: String, difficulty_factor: u32) -> PoW<String> {
let config = ConfigBuilder::default().salt(salt).build().unwrap();
config.prove_work(&phrase, difficulty_factor).unwrap()
}
#[tokio::main]
async fn main() {
let args = Args::parse();
match args {
Args::Offline(matches) => {
let phrase = matches.phrase; let phrase = matches.phrase;
let salt = matches.salt; let salt = matches.salt;
let difficulty_factor = matches.difficulty_factor; let difficulty_factor = matches.difficulty_factor;
let work = prove_work(phrase.clone(), salt, difficulty_factor);
let config = ConfigBuilder::default().salt(salt.into()).build().unwrap(); // let config = ConfigBuilder::default().salt(salt.into()).build().unwrap();
let work = config.prove_work(&phrase, difficulty_factor).unwrap(); // let work = config.prove_work(&phrase, difficulty_factor).unwrap();
println!("difficulty: {}", &difficulty_factor); println!("difficulty: {}", &difficulty_factor);
println!("nonce: {}", &work.nonce); println!("nonce: {}", &work.nonce);
println!("original phrase: {}", &phrase); println!("original phrase: {}", &phrase);
println!("result: {}", &work.result); println!("result: {}", &work.result);
} }
Args::Online(matches) => {
let sitekey = matches.extract_sitekey();
if sitekey.is_none() {
println!("ERROR: Sitekey not found in URL. Please enter correct URL");
return;
}
let sitekey = sitekey.unwrap();
let c = Client::default();
let url = matches.get_config_url();
#[derive(Clone, Debug, Serialize, Deserialize)]
struct ConfigRequest {
key: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
struct ConfigResp {
string: String,
difficulty_factor: u32,
salt: String,
}
let req = ConfigRequest {
key: sitekey.to_string(),
};
let resp: ConfigResp = c
.post(url)
.json(&req)
.send()
.await
.unwrap()
.json()
.await
.unwrap();
let start = Instant::now();
let work = prove_work(resp.string.clone(), resp.salt, resp.difficulty_factor);
let finish = Instant::now();
let time_elapsed = finish.duration_since(start);
let time = time_elapsed.as_millis() as usize;
#[derive(Clone, Debug, Serialize, Deserialize)]
struct VerifyRequest {
key: String,
nonce: u64,
result: String,
string: String,
time: usize,
worker_type: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
struct VerifyResp {
token: String,
}
let req = VerifyRequest {
key: sitekey.clone(),
nonce: work.nonce,
result: work.result,
string: resp.string,
time,
worker_type: String::from("mCaptcha CLI"),
};
let url = matches.get_verify_url();
let resp: VerifyResp = c
.post(url)
.json(&req)
.send()
.await
.unwrap()
.json()
.await
.unwrap();
println!("Authorization token: {}", resp.token);
}
}
}