commit 1519ed69ca54aad5ce4bd54a1a0ee293c5d7193c Author: Aravinth Manivannan Date: Mon Jan 8 04:19:36 2024 +0530 feat: define FFI interfaces to mcaptcha_pow_sha256 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1428b3e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +lib/ +main diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..190a55e --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +build-rust: + @-rm -rf lib + mkdir lib/ + cd ./mcaptcha-pow-ffi/ && cargo build --release \ + && cp ./target/release/libmcaptcha_pow_ffi.a ../lib/ \ + && cp ./target/release/libmcaptcha_pow_ffi.so ../lib/ + +test: + cd ./mcaptcha-pow-ffi/ && cargo test diff --git a/mcaptcha-pow-ffi/.gitignore b/mcaptcha-pow-ffi/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/mcaptcha-pow-ffi/.gitignore @@ -0,0 +1 @@ +/target diff --git a/mcaptcha-pow-ffi/Cargo.lock b/mcaptcha-pow-ffi/Cargo.lock new file mode 100644 index 0000000..cfea0a8 --- /dev/null +++ b/mcaptcha-pow-ffi/Cargo.lock @@ -0,0 +1,351 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_builder" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_builder_macro" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" +dependencies = [ + "derive_builder_core", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "libc" +version = "0.2.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" + +[[package]] +name = "mcaptcha-pow-ffi" +version = "0.1.0" +dependencies = [ + "bincode", + "libc", + "mcaptcha_pow_sha256", +] + +[[package]] +name = "mcaptcha_pow_sha256" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b007473b7f3ef294022e8034f309b31ff0ba4c891c3bdf9dea7cd223f8ba45" +dependencies = [ + "bincode", + "derive_builder", + "num", + "serde", + "sha2", +] + +[[package]] +name = "num" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "num-complex" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +dependencies = [ + "num-traits", + "serde", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "proc-macro2" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "serde" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" diff --git a/mcaptcha-pow-ffi/Cargo.toml b/mcaptcha-pow-ffi/Cargo.toml new file mode 100644 index 0000000..83db2be --- /dev/null +++ b/mcaptcha-pow-ffi/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "mcaptcha-pow-ffi" +version = "0.1.0" +edition = "2021" +authors = ["realaravinth "] +description = "FFI library to compute mCaptcha Proof-of-Work" +keywords = ["DDoS", "mcaptcha", "captcha", "pow"] +homepage = "https://mcaptcha.org" +repository = "https://git.batsense.net/mCaptcha/mcaptcha-pow-rs" +documentation = "https://docs.rs/mcaptcha-api-rs" +license = "MIT OR Apache-2.0" +readme = "README.md" + +[lib] +# If you only wanted dynamic library, you'd use only "cdylib". +# If you only wanted static library, you'd use only "staticlib". +# This demo shows both. See https://doc.rust-lang.org/reference/linkage.html +# for more information. +crate-type = ["cdylib", "staticlib"] + +[dependencies] +bincode = "1.3.3" +libc = "0.2.151" +mcaptcha_pow_sha256 = "0.5.0" diff --git a/mcaptcha-pow-ffi/mcaptcha_pow_ffi.h b/mcaptcha-pow-ffi/mcaptcha_pow_ffi.h new file mode 100644 index 0000000..8196760 --- /dev/null +++ b/mcaptcha-pow-ffi/mcaptcha_pow_ffi.h @@ -0,0 +1,13 @@ +#include + +struct ProofOfWork { + unsigned long int nonce; + char *result; +}; +bool is_valid_proof(unsigned long int nonce, char *result, char *phrase, + char *salt); + +bool is_sufficient_difficulty(unsigned long int nonce, char *result, char *salt, + unsigned difficulty); + +struct ProofOfWork prove_work(char *salt, char *phrase, unsigned difficulty); diff --git a/mcaptcha-pow-ffi/src/lib.rs b/mcaptcha-pow-ffi/src/lib.rs new file mode 100644 index 0000000..0f560bf --- /dev/null +++ b/mcaptcha-pow-ffi/src/lib.rs @@ -0,0 +1,155 @@ +use mcaptcha_pow_sha256::ConfigBuilder; +use std::ffi::CStr; +use std::ffi::CString; + +#[repr(C)] +pub struct ProofOfWork { + pub nonce: u64, + pub result: *const libc::c_char, +} + +impl From> for ProofOfWork { + fn from(value: mcaptcha_pow_sha256::PoW) -> Self { + Self { + nonce: value.nonce, + result: CString::new(value.result).unwrap().into_raw(), + } + } +} + +#[no_mangle] +pub extern "C" fn prove_work( + salt: *const libc::c_char, + phrase: *const libc::c_char, + difficulty: u32, +) -> ProofOfWork { + let (salt, phrase) = unsafe { (CStr::from_ptr(salt), CStr::from_ptr(phrase)) }; + let salt = salt.to_str().unwrap(); + let phrase = phrase.to_str().unwrap().to_string(); + let config = ConfigBuilder::default() + .salt(salt.to_string()) + .build() + .unwrap(); + config.prove_work(&phrase, difficulty).unwrap().into() +} + +#[no_mangle] +pub extern "C" fn is_valid_proof( + nonce: u64, + result: *const libc::c_char, + phrase: *const libc::c_char, + salt: *const libc::c_char, +) -> bool { + let (result, phrase, salt) = unsafe { + ( + CStr::from_ptr(result), + CStr::from_ptr(phrase), + CStr::from_ptr(salt), + ) + }; + let result = result.to_str().unwrap().to_string(); + let pow: mcaptcha_pow_sha256::PoW = mcaptcha_pow_sha256::PoWBuilder::default() + .nonce(nonce) + .result(result) + .build() + .unwrap(); + let config = ConfigBuilder::default() + .salt(salt.to_str().unwrap().to_string()) + .build() + .unwrap(); + config.is_valid_proof(&pow, &phrase.to_str().unwrap().to_string()) +} + +#[no_mangle] +pub extern "C" fn is_sufficient_difficulty( + nonce: u64, + result: *const libc::c_char, + salt: *const libc::c_char, + difficulty: u32, +) -> bool { + let (result, salt) = unsafe { (CStr::from_ptr(result), CStr::from_ptr(salt)) }; + let result = result.to_str().unwrap().to_string(); + let pow: mcaptcha_pow_sha256::PoW = mcaptcha_pow_sha256::PoWBuilder::default() + .nonce(nonce) + .result(result) + .build() + .unwrap(); + let config = ConfigBuilder::default() + .salt(salt.to_str().unwrap().to_string()) + .build() + .unwrap(); + config.is_sufficient_difficulty(&pow, difficulty) +} + +#[cfg(test)] +pub mod test { + + use super::*; + use std::ffi::CString; + + #[test] + fn test_pow() { + let salt: String = + "27b7eb6be3437b7da0d9329dea9a7c76f1640cd16d39fd94b5d0dd42c763c63c".into(); + let phrase: String = + "b641c711c01079805a2a0888bf716b66c83419ab0c65455a49cb4d8b3e36c4ec".into(); + let difficulty = 50_000; + + let c = mcaptcha_pow_sha256::ConfigBuilder::default() + .salt(salt.clone()) + .build() + .unwrap(); + + // calculate pow + let mcaptcha_work = c.prove_work(&phrase, difficulty).unwrap(); + + let ffi_work = super::prove_work( + CString::new(salt.clone()).unwrap().into_raw(), + CString::new(phrase.clone()).unwrap().into_raw(), + difficulty, + ); + + assert_eq!(ffi_work.nonce, mcaptcha_work.nonce); + + let res = unsafe { CStr::from_ptr(ffi_work.result) }; + let res = res.to_str().unwrap(); + assert_eq!(res, mcaptcha_work.result); + + // verify pow + assert!(super::is_valid_proof( + mcaptcha_work.nonce, + CString::new(mcaptcha_work.result.clone()) + .unwrap() + .into_raw(), + CString::new(phrase.clone()).unwrap().into_raw(), + CString::new(salt.clone()).unwrap().into_raw(), + )); + + assert!(!super::is_valid_proof( + mcaptcha_work.nonce, + CString::new(phrase.clone()).unwrap().into_raw(), + CString::new(phrase.clone()).unwrap().into_raw(), + CString::new(salt.clone()).unwrap().into_raw(), + )); + + // is sufficient difficulty + assert!(super::is_sufficient_difficulty( + mcaptcha_work.nonce, + CString::new(mcaptcha_work.result.clone()) + .unwrap() + .into_raw(), + CString::new(salt.clone()).unwrap().into_raw(), + difficulty + )); + + // is sufficient difficulty + assert!(super::is_sufficient_difficulty( + mcaptcha_work.nonce, + CString::new(mcaptcha_work.result.clone()) + .unwrap() + .into_raw(), + CString::new(salt.clone()).unwrap().into_raw(), + difficulty + )); + } +}