1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
use std::fmt;
use std::net;

use borsh::{BorshDeserialize, BorshSerialize};

#[derive(Clone, Debug, Eq, PartialEq, Default)]
pub struct PeerData {
    agent: String,
    location: Location,
    identity: Identity,
}

impl PeerData {
    pub fn new_loc(location: Location) -> Self {
        Self {
            location,
            ..Self::default()
        }
    }

    pub fn agent(&self) -> &str {
        &self.agent
    }

    pub fn set_agent(&mut self, agent: String) {
        self.agent = agent;
    }

    pub fn location(&self) -> &Location {
        &self.location
    }

    pub fn set_location(&mut self, loc: Location) {
        self.location = loc;
    }

    pub fn identity(&self) -> &Identity {
        &self.identity
    }

    pub fn set_identity(&mut self, ident: Identity) {
        self.identity = ident;
    }
}

/// The remote location of the connection, if set.
#[derive(Clone, Debug, Eq, PartialEq, Default)]
pub enum Location {
    /// Remote location unspecified by the transport.
    #[default]
    Unspecified,

    /// Connected to an IP address through TCP, possibly inside of TLS.
    Ip(net::SocketAddr),

    /// Connected to a URL over some transport probably specified by the URL.
    Url(String),
}

impl fmt::Display for Location {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Unspecified => f.write_str("unspecified"),
            Self::Ip(sock_addr) => sock_addr.fmt(f),
            Self::Url(url) => f.write_str(url),
        }
    }
}

/// The identity of the remote, if set.  If this is observed we are sure this
/// identification is authentic within the bounds of the protocol.
#[derive(Clone, Debug, Eq, PartialEq, Default)]
pub enum Identity {
    /// The remote has not been identified.
    #[default]
    Unidentified,

    /// Ecksport native key system.
    EcksKey(EcksKeyId),

    /// Authentication on session setup via TLS as some particular domain.
    TlsDomain(String),

    /// Custom authentication key with application-defined authentication.
    CustomKey(CustomKeyId),

    /// Custom application-specific username logged in somehow.
    CustomUsername(String),
}

impl Identity {
    /// Returns is the identity is authenticated.
    pub fn is_authed(&self) -> bool {
        !matches!(self, Identity::Unidentified)
    }
}

impl fmt::Display for Identity {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Unidentified => f.write_str("unidentified"),
            Self::EcksKey(k) => k.fmt(f),
            Self::TlsDomain(domain) => f.write_str(domain),
            Self::CustomKey(k) => k.fmt(f),
            Self::CustomUsername(name) => {
                f.write_str("name:")?;
                f.write_str(name)
            }
        }
    }
}

/// Ecksport native authentication system key ID.  This is really a sodiumoxide
/// ed25519 pubkey.
#[derive(Copy, Clone, Debug, Eq, PartialEq, BorshDeserialize, BorshSerialize)]
pub struct EcksKeyId([u8; 32]);

impl From<[u8; 32]> for EcksKeyId {
    fn from(value: [u8; 32]) -> Self {
        Self(value)
    }
}

impl Into<[u8; 32]> for EcksKeyId {
    fn into(self) -> [u8; 32] {
        self.0
    }
}

impl fmt::Display for EcksKeyId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut buf = [0; 64];
        hex::encode_to_slice(self.0, &mut buf).expect("peer: encode key ID");
        f.write_str("ek:")?;
        f.write_str(unsafe { std::str::from_utf8_unchecked(&buf) })
    }
}

/// Custom key ID.  This may be a hash of something or it may be a pubkey.  It's
/// entirely dependent on the implementation.
#[derive(Copy, Clone, Debug, Eq, PartialEq, BorshDeserialize, BorshSerialize)]
pub struct CustomKeyId([u8; 32]);

impl From<[u8; 32]> for CustomKeyId {
    fn from(value: [u8; 32]) -> Self {
        Self(value)
    }
}

impl Into<[u8; 32]> for CustomKeyId {
    fn into(self) -> [u8; 32] {
        self.0
    }
}

impl fmt::Display for CustomKeyId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut buf = [0; 64];
        hex::encode_to_slice(self.0, &mut buf).expect("peer: encode key ID");
        f.write_str("ck:")?;
        f.write_str(unsafe { std::str::from_utf8_unchecked(&buf) })
    }
}