2 releases (1 stable)
Uses new Rust 2024
| 1.0.0 | Nov 4, 2025 |
|---|---|
| 0.2.0 | Nov 2, 2025 |
#1557 in Web programming
475KB
999 lines
geronimo-captcha
Secure, AI-resistant, JavaScript-free CAPTCHA built in Rust. Confuses bots, but delights humans.
What it does
- Renders a 3×3 sprite with one correctly oriented tile
- Random jitter, label offset, colored noise, JPEG artifacts
- Stateless HMAC-signed challenge id with TTL
Challenge examples
Roadmap
- Captcha core, image and sprite generation helpers
- In-memory challenge registry impl
- Sprite as binary (in addition to base64)
- WebP format (in addition to JPEG)
- Code examples, demo webpage
- Custom fonts and sample sets
- Redis challenge registry impl
Generate and verify
use geronimo_captcha::{
CaptchaManager, ChallengeInMemoryRegistry,
GenerationOptions, NoiseOptions,
SpriteFormat, SpriteUri, SpriteBinary
};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let secret = "your-secret-key".to_string();
let ttl_secs = 60;
let noise = NoiseOptions::default();
let gen = GenerationOptions {
cell_size: 150,
sprite_format: SpriteFormat::Jpeg {
quality: 20,
},
limits: None,
};
let registry = std::sync::Arc::new(ChallengeInMemoryRegistry::new(ttl_secs, 3));
let mgr = CaptchaManager::new(secret, ttl_secs, noise, Some(registry), gen);
let challenge = mgr.generate_challenge::<SpriteUri>()?;
// Generate sprite (as binary) if needed
// let challenge = mgr.generate_challenge_with::<SpriteBinary>()?;
// let img_binary = challenge.sprite.bytes;
// Render to client
let img_src = challenge.sprite.0; // data:image/*;base64,...
let challenge_id = challenge.challenge_id; // send/store with form
println!("img_src prefix: {}", &img_src[..32.min(img_src.len())]);
println!("challenge_id: {}", challenge_id);
// Normally you get these from the client in your API handlers/routes
let client_challenge_id = "nonce:1730534400:BASE64_HMAC".to_string();
let client_choice_idx: u8 = 7;
let ok = mgr.verify_challenge(&client_challenge_id, client_choice_idx)?;
println!("verified: {ok}");
Ok(())
}
Benchmarks
- JPEG generate: ~6.7 ms / ~11.1 ms / ~17.5 ms
- WebP generate: ~11.5 ms / ~21.0 ms / ~33.1 ms
- Verify: ~2.5 µs
With feature parallel enabled: ~5.0 ms / ~9.6 ms / ~15.6 ms (JPEG) and ~10.7 ms / ~20.0 ms / ~32.6 ms (WebP).
Apple M3 Max
How to run:
cargo bench --bench captcha -- --noplot
cargo bench --features parallel --bench captcha -- --noplot
License
This project is licensed under the Apache 2.0 License. See LICENSE for details.
Dependencies
~30MB
~488K SLoC