1
//! Implement parsing for the `pow-params` line within the `HsDescInner` layer
2

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

            
6
use crate::doc::hsdesc::inner::HsInnerKwd;
7
use crate::parse::tokenize::Item;
8
use crate::{NetdocErrorKind as EK, Result};
9
use std::collections::HashSet;
10
use std::mem::Discriminant;
11
use v1::PowParamsV1;
12

            
13
/// A list of parsed `pow-params` lines, at most one per scheme
14
///
15
#[derive(Debug, Clone)]
16
pub struct PowParamSet(Vec<PowParams>);
17

            
18
impl PowParamSet {
19
    /// Reference all parameters as a slice in arbitrary order
20
51
    pub(super) fn slice(&self) -> &[PowParams] {
21
51
        &self.0
22
51
    }
23

            
24
    /// Parse a slice of `pow-params` items
25
    ///
26
    /// Unrecognized schemes are ignored. Duplicate schemes result in an error.
27
    ///
28
317
    pub(super) fn from_items(items: &[Item<'_, HsInnerKwd>]) -> Result<Self> {
29
317
        // Parse each one individually,
30
317
        // verifying each time we don't have a duplicated enum discriminant.
31
317
        let mut inner = Vec::new();
32
317
        let mut schemes_seen: HashSet<Discriminant<PowParams>> = HashSet::new();
33
331
        for item in items {
34
18
            if let Some(parsed) = PowParams::try_from_item(item)? {
35
8
                if schemes_seen.insert(std::mem::discriminant(&parsed)) {
36
6
                    // Parsed params with a scheme we haven't seen before
37
6
                    inner.push(parsed);
38
6
                } else {
39
2
                    return Err(EK::DuplicateToken
40
2
                        .with_msg(item.kwd_str().to_owned())
41
2
                        .at_pos(item.pos()));
42
                }
43
8
            }
44
        }
45
313
        Ok(Self(inner))
46
317
    }
47
}
48

            
49
/// The contents of a `pow-params` line with any recognized scheme.
50
///
51
/// These use a text format defined by:
52
/// <https://spec.torproject.org/rend-spec/hsdesc-encrypt.html#second-layer-plaintext>
53
///
54
#[derive(Debug, Clone)]
55
#[non_exhaustive]
56
pub enum PowParams {
57
    /// Parameters for the `v1` scheme
58
    ///
59
    /// This scheme uses the Equi-X asymmetric puzzle, in an iterated
60
    /// construction with Blake2b for effort adjustment. The Tor specification
61
    /// describes this puzzle construction and references the underlying
62
    /// algorithms:
63
    ///
64
    /// <https://spec.torproject.org/hspow-spec/v1-equix.html>
65
    V1(PowParamsV1),
66
}
67

            
68
impl PowParams {
69
    /// Parse a single `pow-params` line from an `Item`
70
    ///
71
    /// If the scheme is recognized, returns a PowParams or a parse error.
72
    /// If the scheme is unknown, returns None.
73
    /// If the scheme field is missing entirely, returns a parse error.
74
18
    fn try_from_item(item: &Item<'_, HsInnerKwd>) -> Result<Option<Self>> {
75
18
        let scheme = item.required_arg(0)?;
76
18
        if scheme == "v1" {
77
10
            Ok(Some(PowParams::V1(PowParamsV1::from_item(item)?)))
78
        } else {
79
8
            Ok(None)
80
        }
81
18
    }
82
}