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 super::ext::Ext;
8
use super::intro_payload::IntroPayloadExtType;
9
use caret::caret_int;
10
use tor_bytes::{EncodeResult, Reader, Result, Writer};
11

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

            
27
impl Ext for ProofOfWork {
28
    type Id = IntroPayloadExtType;
29

            
30
    fn type_id(&self) -> IntroPayloadExtType {
31
        IntroPayloadExtType::PROOF_OF_WORK
32
    }
33

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

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

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

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

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

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