1
//! Common support for proof of work denial of service mitigation on the client side
2

            
3
#[cfg_attr(not(feature = "hs-pow-full"), path = "pow/v1_stub.rs")]
4
mod v1;
5

            
6
use crate::err::ProofOfWorkError;
7
use tor_cell::relaycell::hs::pow::ProofOfWork;
8
use tor_hscrypto::pk::HsBlindId;
9
use tor_netdoc::doc::hsdesc::HsDesc;
10
use tor_netdoc::doc::hsdesc::pow::PowParams;
11
use v1::HsPowClientV1;
12

            
13
/// Client-side state for a series of connection attempts that might use proof-of-work.
14
///
15
/// The `HsPowClient` can be initialized using a recent `HsDesc`, at which point
16
/// we choose a proof of work scheme and its initial parameters.
17
///
18
/// When an attempt fails, we can increase the effort in an algorithm-specific way.
19
///
20
/// For now we have only scheme, `v1`. We try to make only minimal assumptions
21
/// about how future schemes may interact with each other.
22
#[derive(Default)]
23
pub(crate) struct HsPowClient {
24
    /// Client state specifically for the `v1` scheme
25
    v1: Option<HsPowClientV1>,
26
}
27

            
28
impl HsPowClient {
29
    /// Initialize a new group of connection attempts, given the required context
30
2
    pub(crate) fn new(hs_blind_id: &HsBlindId, desc: &HsDesc) -> Self {
31
2
        let mut client: HsPowClient = Default::default();
32
2
        for params in desc.pow_params() {
33
            if let PowParams::V1(v1) = params {
34
                client.v1 = Some(HsPowClientV1::new(hs_blind_id, v1));
35
            }
36
        }
37
2
        client
38
2
    }
39

            
40
    /// Increase effort in response to a failed connection attempt.
41
    ///
42
    /// If no proof of work scheme is in use or the effort cannot be increased, this has no effect.
43
    ///
44
    /// Specified in <https://spec.torproject.org/hspow-spec/common-protocol.html#client-timeout>
45
    ///
46
    pub(crate) fn increase_effort(&mut self) {
47
        if let Some(v1) = &mut self.v1 {
48
            v1.increase_effort();
49
        }
50
    }
51

            
52
    /// If we have an applicable proof of work scheme, do the work and return a proof
53
    pub(crate) async fn solve(&self) -> Result<Option<ProofOfWork>, ProofOfWorkError> {
54
        if let Some(v1) = &self.v1 {
55
            Ok(v1.solve().await?.map(ProofOfWork::V1))
56
        } else {
57
            Ok(None)
58
        }
59
    }
60
}