1
//! Identifier objects used to specify guards and/or fallbacks.
2

            
3
use derive_more::AsRef;
4
use serde::{Deserialize, Serialize};
5
use tor_linkspec::{HasRelayIds, RelayIds};
6
#[cfg(test)]
7
use tor_llcrypto::pk;
8

            
9
use crate::GuardSetSelector;
10

            
11
/// An identifier for a fallback directory cache.
12
///
13
/// This is a separate type from GuardId and FirstHopId to avoid confusion
14
/// about what kind of object we're identifying.
15
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, AsRef)]
16
pub(crate) struct FallbackId(pub(crate) RelayIds);
17

            
18
impl FallbackId {
19
    /// Return a new, manually constructed `FallbackId`
20
    /// Extract a `FallbackId` from a ChanTarget object.
21
56
    pub(crate) fn from_relay_ids<T>(target: &T) -> Self
22
56
    where
23
56
        T: tor_linkspec::HasRelayIds + ?Sized,
24
56
    {
25
56
        Self(RelayIds::from_relay_ids(target))
26
56
    }
27
}
28

            
29
impl HasRelayIds for FallbackId {
30
212
    fn identity(
31
212
        &self,
32
212
        key_type: tor_linkspec::RelayIdType,
33
212
    ) -> Option<tor_linkspec::RelayIdRef<'_>> {
34
212
        self.0.identity(key_type)
35
212
    }
36
}
37

            
38
/// An identifier for a sampled guard.
39
///
40
/// This is a separate type from GuardId and FirstHopId to avoid confusion
41
/// about what kind of object we're identifying.
42
48
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash, Ord, PartialOrd, AsRef)]
43
#[serde(transparent)]
44
pub(crate) struct GuardId(pub(crate) RelayIds);
45

            
46
impl GuardId {
47
    /// Return a new, manually constructed `GuardId`
48
    #[cfg(test)]
49
36
    pub(crate) fn new(ed25519: pk::ed25519::Ed25519Identity, rsa: pk::rsa::RsaIdentity) -> Self {
50
36
        Self(
51
36
            RelayIds::builder()
52
36
                .ed_identity(ed25519)
53
36
                .rsa_identity(rsa)
54
36
                .build()
55
36
                .expect("Couldn't build RelayIds"),
56
36
        )
57
36
    }
58
    /// Extract a `GuardId` from a ChanTarget object.
59
904058
    pub(crate) fn from_relay_ids<T>(target: &T) -> Self
60
904058
    where
61
904058
        T: tor_linkspec::HasRelayIds + ?Sized,
62
904058
    {
63
904058
        Self(RelayIds::from_relay_ids(target))
64
904058
    }
65
}
66

            
67
impl HasRelayIds for GuardId {
68
27778471
    fn identity(
69
27778471
        &self,
70
27778471
        key_type: tor_linkspec::RelayIdType,
71
27778471
    ) -> Option<tor_linkspec::RelayIdRef<'_>> {
72
27778471
        self.0.identity(key_type)
73
27778471
    }
74
}
75

            
76
/// Implementation type held inside of FirstHopId.
77
///
78
/// This exists as a separate type from FirstHopId because Rust requires that a pub enum's variants are all public.
79
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
80
pub(crate) enum FirstHopIdInner {
81
    /// Identifies a guard.
82
    Guard(GuardSetSelector, GuardId),
83
    /// Identifies a fallback.
84
    Fallback(FallbackId),
85
}
86

            
87
/// A unique cryptographic identifier for a selected guard or fallback
88
/// directory.
89
///
90
/// (This is implemented internally using all of the guard's known identities.)
91
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
92
pub struct FirstHopId(pub(crate) FirstHopIdInner);
93

            
94
impl From<FallbackId> for FirstHopId {
95
    fn from(id: FallbackId) -> Self {
96
        Self(FirstHopIdInner::Fallback(id))
97
    }
98
}
99
impl AsRef<RelayIds> for FirstHopId {
100
    /// Return the inner `RelayIds` for this object.
101
    ///
102
    /// Only use this when it's okay to erase the type information about
103
    /// whether this identifies a guard or a fallback.
104
74
    fn as_ref(&self) -> &RelayIds {
105
74
        match &self.0 {
106
74
            FirstHopIdInner::Guard(_, id) => id.as_ref(),
107
            FirstHopIdInner::Fallback(id) => id.as_ref(),
108
        }
109
74
    }
110
}
111
impl tor_linkspec::HasRelayIds for FirstHopId {
112
74
    fn identity(
113
74
        &self,
114
74
        key_type: tor_linkspec::RelayIdType,
115
74
    ) -> Option<tor_linkspec::RelayIdRef<'_>> {
116
74
        self.as_ref().identity(key_type)
117
74
    }
118
}
119

            
120
impl FirstHopId {
121
    /// Return the relay in `netdir` that corresponds to this ID, if there
122
    /// is one.
123
    //
124
    // We have to define this function so it'll be public.
125
36
    pub fn get_relay<'a>(&self, netdir: &'a tor_netdir::NetDir) -> Option<tor_netdir::Relay<'a>> {
126
36
        netdir.by_ids(self)
127
36
    }
128

            
129
    /// Construct a FirstHopId for a guard in a given sample.
130
348544
    pub(crate) fn in_sample(sample: GuardSetSelector, id: GuardId) -> Self {
131
348544
        Self(FirstHopIdInner::Guard(sample, id))
132
348544
    }
133
}