1
//! Declare traits to be implemented by types that describe a place
2
//! that Tor can connect to, directly or indirectly.
3

            
4
use std::net::SocketAddr;
5
use tor_llcrypto::pk;
6

            
7
use std::convert::TryInto;
8

            
9
/// Information about a Tor relay used to connect to it.
10
///
11
/// Anything that implements 'ChanTarget' can be used as the
12
/// identity of a relay for the purposes of launching a new
13
/// channel.
14
pub trait ChanTarget {
15
    /// Return the addresses at which you can connect to this relay
16
    // TODO: This is a questionable API. I'd rather return an iterator
17
    // of addresses or references to addresses, but both of those options
18
    // make defining the right associated types rather tricky.
19
    fn addrs(&self) -> &[SocketAddr];
20
    /// Return the ed25519 identity for this relay.
21
    fn ed_identity(&self) -> &pk::ed25519::Ed25519Identity;
22
    /// Return the ed25519 identity key for this relay, if it is valid.
23
    ///
24
    /// This can be costly.
25
2
    fn ed_identity_key(&self) -> Option<pk::ed25519::PublicKey> {
26
2
        self.ed_identity().try_into().ok()
27
2
    }
28
    /// Return the RSA identity for this relay.
29
    fn rsa_identity(&self) -> &pk::rsa::RsaIdentity;
30
}
31

            
32
/// Information about a Tor relay used to extend a circuit to it.
33
///
34
/// Anything that implements 'CircTarget' can be used as the
35
/// identity of a relay for the purposes of extending a circuit.
36
pub trait CircTarget: ChanTarget {
37
    /// Return a new vector of link specifiers for this relay.
38
    // TODO: This is a questionable API. I'd rather return an iterator
39
    // of link specifiers, but that's not so easy to do, since it seems
40
    // doing so correctly would require default associated types.
41
21
    fn linkspecs(&self) -> Vec<crate::LinkSpec> {
42
21
        let mut result = vec![(*self.ed_identity()).into(), (*self.rsa_identity()).into()];
43
22
        for addr in self.addrs().iter() {
44
2
            result.push(addr.into());
45
2
        }
46
21
        result
47
21
    }
48
    /// Return the ntor onion key for this relay
49
    fn ntor_onion_key(&self) -> &pk::curve25519::PublicKey;
50
    /// Return the subprotocols implemented by this relay.
51
    fn protovers(&self) -> &tor_protover::Protocols;
52
}
53

            
54
#[cfg(test)]
55
mod test {
56
    #![allow(clippy::unwrap_used)]
57
    use super::*;
58
    use hex_literal::hex;
59
    use std::net::IpAddr;
60
    use tor_llcrypto::pk;
61

            
62
    struct Example {
63
        addrs: Vec<SocketAddr>,
64
        ed_id: pk::ed25519::Ed25519Identity,
65
        rsa_id: pk::rsa::RsaIdentity,
66
        ntor: pk::curve25519::PublicKey,
67
        pv: tor_protover::Protocols,
68
    }
69
    impl ChanTarget for Example {
70
        fn addrs(&self) -> &[SocketAddr] {
71
            &self.addrs[..]
72
        }
73
        fn ed_identity(&self) -> &pk::ed25519::Ed25519Identity {
74
            &self.ed_id
75
        }
76
        fn rsa_identity(&self) -> &pk::rsa::RsaIdentity {
77
            &self.rsa_id
78
        }
79
    }
80
    impl CircTarget for Example {
81
        fn ntor_onion_key(&self) -> &pk::curve25519::PublicKey {
82
            &self.ntor
83
        }
84
        fn protovers(&self) -> &tor_protover::Protocols {
85
            &self.pv
86
        }
87
    }
88

            
89
    /// Return an `Example` object, for use in tests below.
90
    fn example() -> Example {
91
        Example {
92
            addrs: vec![
93
                "127.0.0.1:99".parse::<SocketAddr>().unwrap(),
94
                "[::1]:909".parse::<SocketAddr>().unwrap(),
95
            ],
96
            ed_id: pk::ed25519::PublicKey::from_bytes(&hex!(
97
                "fc51cd8e6218a1a38da47ed00230f058
98
                 0816ed13ba3303ac5deb911548908025"
99
            ))
100
            .unwrap()
101
            .into(),
102
            rsa_id: pk::rsa::RsaIdentity::from_bytes(&hex!(
103
                "1234567890abcdef12341234567890abcdef1234"
104
            ))
105
            .unwrap(),
106
            ntor: pk::curve25519::PublicKey::from(hex!(
107
                "e6db6867583030db3594c1a424b15f7c
108
                 726624ec26b3353b10a903a6d0ab1c4c"
109
            )),
110
            pv: tor_protover::Protocols::default(),
111
        }
112
    }
113

            
114
    #[test]
115
    fn test_linkspecs() {
116
        let ex = example();
117
        let specs = ex.linkspecs();
118
        assert_eq!(4, specs.len());
119

            
120
        use crate::ls::LinkSpec;
121
        assert_eq!(
122
            specs[0],
123
            LinkSpec::Ed25519Id(
124
                pk::ed25519::PublicKey::from_bytes(&hex!(
125
                    "fc51cd8e6218a1a38da47ed00230f058
126
                     0816ed13ba3303ac5deb911548908025"
127
                ))
128
                .unwrap()
129
                .into()
130
            )
131
        );
132
        assert_eq!(
133
            specs[1],
134
            LinkSpec::RsaId(
135
                pk::rsa::RsaIdentity::from_bytes(&hex!("1234567890abcdef12341234567890abcdef1234"))
136
                    .unwrap()
137
            )
138
        );
139
        assert_eq!(
140
            specs[2],
141
            LinkSpec::OrPort("127.0.0.1".parse::<IpAddr>().unwrap(), 99)
142
        );
143
        assert_eq!(
144
            specs[3],
145
            LinkSpec::OrPort("::1".parse::<IpAddr>().unwrap(), 909)
146
        );
147
    }
148

            
149
    #[test]
150
    fn key_accessor() {
151
        let ex = example();
152
        // We can get the ed25519 key if it's valid...
153
        let key = ex.ed_identity_key().unwrap();
154
        assert_eq!(&pk::ed25519::Ed25519Identity::from(key), ex.ed_identity());
155

            
156
        // Now try an invalid example.
157
        let a = hex!("6d616e79737472696e677361726565646b6579736e6f74746869736f6e654091");
158
        let ex = Example {
159
            ed_id: pk::ed25519::Ed25519Identity::from_bytes(&a).unwrap(),
160
            ..ex
161
        };
162
        let key = ex.ed_identity_key();
163
        assert!(key.is_none());
164
    }
165
}