1
//! Support for the proof-of-work intro payload extension
2

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

            
6
use self::v1::ProofOfWorkV1;
7
use crate::relaycell::{extend::CircRequestExtType, extlist::Ext};
8
use caret::caret_int;
9
use tor_bytes::{EncodeResult, Reader, Result, Writer};
10

            
11
/// Extension to provide a proof of work for denial of service mitigation
12
///
13
/// Documented at <https://spec.torproject.org/rend-spec/introduction-protocol.html#INTRO1_POW_EXT>
14
///
15
/// The extension has a variable format depending on the specific scheme that was chosen.
16
///
17
#[derive(Debug, Clone, PartialEq)]
18
#[non_exhaustive]
19
pub enum ProofOfWork {
20
    /// A potential solution with unrecognized scheme
21
    Unrecognized(UnrecognizedProofOfWork),
22
    /// A potential solution using the `v1` scheme
23
    V1(v1::ProofOfWorkV1),
24
}
25

            
26
impl Ext for ProofOfWork {
27
    type Id = CircRequestExtType;
28

            
29
    fn type_id(&self) -> CircRequestExtType {
30
        CircRequestExtType::PROOF_OF_WORK
31
    }
32

            
33
    fn take_body_from(b: &mut Reader<'_>) -> Result<Self> {
34
        let scheme = b.take_u8()?;
35
        if let Some(v1) = ProofOfWorkV1::try_take_body_from(scheme, b)? {
36
            return Ok(ProofOfWork::V1(v1));
37
        }
38
        Ok(ProofOfWork::Unrecognized(
39
            UnrecognizedProofOfWork::take_body_from(scheme, b),
40
        ))
41
    }
42

            
43
    fn write_body_onto<B: Writer + ?Sized>(&self, b: &mut B) -> EncodeResult<()> {
44
        match self {
45
            ProofOfWork::V1(v1) => v1.write_onto(b),
46
            ProofOfWork::Unrecognized(unrecognized) => {
47
                unrecognized.write_onto(b);
48
                Ok(())
49
            }
50
        }
51
    }
52
}
53

            
54
caret_int! {
55
    /// Recognized numeric codes for the scheme-specific [`ProofOfWork`] formats
56
    #[non_exhaustive]
57
    pub struct ProofOfWorkType(u8) {
58
        /// Solution for the `v1` scheme
59
        V1 = 1,
60
    }
61
}
62

            
63
/// A proof of work with unknown scheme
64
///
65
/// The reader needs a way to represent future schemes when we can't fail to parse.
66
/// This is similar to [`super::UnrecognizedExt`], but specific to an unrecognized scheme
67
/// within a known type of extension.
68
///
69
#[derive(Debug, Clone, Eq, PartialEq, amplify::Getters, derive_more::Constructor)]
70
pub struct UnrecognizedProofOfWork {
71
    /// The `scheme` byte
72
    ///
73
    /// Intended usage is that this won't be any of the known `ProofOfWorkType`
74
    /// values. We don't strictly verify this, to avoid breaking the API every
75
    /// time a new type is added.
76
    ///
77
    #[getter(as_copy)]
78
    scheme: u8,
79
    /// Arbitrary contents with an unknown format
80
    #[getter(as_ref)]
81
    data: Vec<u8>,
82
}
83

            
84
impl UnrecognizedProofOfWork {
85
    /// Construct by taking the remaining scheme-specific unknown data
86
    pub(super) fn take_body_from(scheme: u8, b: &mut Reader<'_>) -> Self {
87
        Self::new(scheme, b.take_rest().to_vec())
88
    }
89

            
90
    /// Write the unrecognized proof's scheme and data
91
    pub(super) fn write_onto<B: Writer + ?Sized>(&self, b: &mut B) {
92
        b.write_u8(self.scheme());
93
        b.write_all(self.data());
94
    }
95
}