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
//! A JavaScript-free CAPTCHA. A speed bump for bulk
//! automation, layered over server-side rate limiting.
//! Not a wall.
//!
//! A challenge is one to five rounds. Each round
//! renders its own source image at nine orientations
//! in a 3x3 grid, exactly one of them upright. The
//! challenge id is an HMAC-SHA256 tag binding the
//! length-prefixed correct tile numbers to a nonce
//! and a timestamp. Verification recomputes the tag
//! over the client's guesses and compares it in
//! constant time. No answer is stored server-side.
//!
//! Replay and brute-force limiting live entirely
//! in [`ChallengeRegistry`]. Without one, nothing
//! limits guesses; a single round falls in nine.
//!
//! The [`SampleSet`] is the security boundary.
//! Supply your own corpus and rotate it.
//!
//! ```no_run
//! use geronimo_captcha::{
//! CaptchaManager, ChallengeInMemoryRegistry, GenerationOptions,
//! NoiseOptions, SampleFormat, SampleSet, SpriteUri,
//! };
//! use std::sync::Arc;
//!
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let ttl_secs = 60;
//! let registry = Arc::new(ChallengeInMemoryRegistry::new(ttl_secs, 3, 10_000)?);
//!
//! let images = std::fs::read_dir("/srv/captcha-corpus")?
//! .filter_map(|e| std::fs::read(e.ok()?.path()).ok())
//! .collect();
//! let samples = SampleSet::new(SampleFormat::Jpeg, images)?;
//!
//! let mgr = CaptchaManager::new(
//! "an-example-secret-key-of-32-bytes".to_string(),
//! ttl_secs,
//! NoiseOptions::default(),
//! Some(registry),
//! GenerationOptions::default(),
//! &samples,
//! )?;
//!
//! let challenge = mgr.generate_challenge::<SpriteUri>()?;
//!
//! // One sprite per round, rendered into the form
//! let img_srcs: Vec<String> = challenge.sprites.into_iter().map(|s| s.0).collect();
//! let challenge_id = challenge.challenge_id;
//!
//! // One index per round, in issue order
//! let verified = mgr.verify_challenge(&challenge_id, &[5])?;
//!
//! Ok(())
//! }
//! ```
pub use ;
pub use ;
pub use ;
pub use CaptchaManager;
pub use ;
pub use ;
pub use ;
;