diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/snow-scanner/.gitignore b/snow-scanner/.gitignore new file mode 100644 index 0000000..6985cf1 --- /dev/null +++ b/snow-scanner/.gitignore @@ -0,0 +1,14 @@ +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb diff --git a/snow-scanner/Cargo.toml b/snow-scanner/Cargo.toml new file mode 100644 index 0000000..f5e159d --- /dev/null +++ b/snow-scanner/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "snow-scanner" +version = "0.1.0" +authors = ["William Desportes "] +edition = "2021" +rust-version = "1.78.0" # MSRV +description = "A program to scan internet and find scanners" +homepage = "https://github.com/wdes/snow-scanner/tree/v1.2.0-dev#readme" +repository = "https://github.com/wdes/snow-scanner" +readme = "README.md" +keywords = ["dns", "validator"] +categories = ["command-line-utilities"] +license = "MPL-2.0" +include = [ + "/src/**/*.rs", + "/Cargo.toml", + "/LICENSE", +] + +[badges] +github = { repository = "security", branch = "master" } +is-it-maintained-issue-resolution = { repository = "security" } +is-it-maintained-open-issues = { repository = "security" } +maintenance = { status = "passively-maintained" } + +[[bin]] +name = "snow-scanner" +path = "src/main.rs" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rouille = "3.6.2" +hmac = "0.12.1" +sha2 = "0.10.8" +hex = "0.4.3" diff --git a/snow-scanner/README.md b/snow-scanner/README.md new file mode 100644 index 0000000..d1720e3 --- /dev/null +++ b/snow-scanner/README.md @@ -0,0 +1,9 @@ +# Snow scanner + +This project name is inspired by the Netflix series "The Snowpiercer" + +## Run it + +```sh +cargo run --release +``` diff --git a/snow-scanner/src/main.rs b/snow-scanner/src/main.rs new file mode 100644 index 0000000..abf3028 --- /dev/null +++ b/snow-scanner/src/main.rs @@ -0,0 +1,99 @@ +#[macro_use] +extern crate rouille; + +use rouille::Response; +use std::io; + +use hmac::{Hmac, Mac}; +use sha2::Sha256; + +// Create alias for HMAC-SHA256 +type HmacSha256 = Hmac; + +// The HTML document of the home page. +static FORM: &str = r#" + + + Wdes - snow scanner + + +
+

+

+
+ + +"#; + +fn main() { + println!("Now listening on localhost:8000"); + + rouille::start_server("localhost:8000", move |request| { + router!(request, + (GET) (/) => { + rouille::Response::html(FORM) + }, + + (GET) (/ping) => { + rouille::Response::text("pong") + }, + + (POST) (/register) => { + let data = try_or_400!(post_input!(request, { + email: String, + })); + + // We just print what was received on stdout. Of course in a real application + // you probably want to process the data, eg. store it in a database. + println!("Received data: {:?}", data); + + + let mut mac = HmacSha256::new_from_slice(b"my secret and secure key") + .expect("HMAC can take key of any size"); + mac.update(data.email.as_bytes()); + + // `result` has type `CtOutput` which is a thin wrapper around array of + // bytes for providing constant time equality check + let result = mac.finalize(); + // To get underlying array use `into_bytes`, but be careful, since + // incorrect use of the code value may permit timing attacks which defeats + // the security provided by the `CtOutput` + let code_bytes = result.into_bytes(); + rouille::Response::html(format!("Success! {}.", hex::encode(code_bytes))) + }, + + (GET) (/{api_key: String}/scanners/{scanner_name: String}) => { + let mut mac = HmacSha256::new_from_slice(b"my secret and secure key") + .expect("HMAC can take key of any size"); + + mac.update(b"williamdes@wdes.fr"); + + println!("{}", api_key); + let hex_key = hex::decode(&api_key).unwrap(); + // `verify_slice` will return `Ok(())` if code is correct, `Err(MacError)` otherwise + mac.verify_slice(&hex_key).unwrap(); + + if let Some(request) = request.remove_prefix(format!("/{}", api_key).as_str()) { + // The `match_assets` function tries to find a file whose name corresponds to the URL + // of the request. The second parameter (`"."`) tells where the files to look for are + // located. + // In order to avoid potential security threats, `match_assets` will never return any + // file outside of this directory even if the URL is for example `/../../foo.txt`. + let response = rouille::match_assets(&request, "../data/"); + + // If a file is found, the `match_assets` function will return a response with a 200 + // status code and the content of the file. If no file is found, it will instead return + // an empty 404 response. + // Here we check whether if a file is found, and if so we return the response. + if response.is_success() { + return response; + } + } + rouille::Response::empty_404() + }, + // The code block is called if none of the other blocks matches the request. + // We return an empty response with a 404 status code. + _ => rouille::Response::empty_404() + ) + }); +}