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
use derive_more::Display;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

mod client;
pub use client::*;

mod handshake;
pub use handshake::*;

mod server;
pub use server::*;

/// Represents authentication messages that can be sent over the wire
///
/// NOTE: Must use serde's content attribute with the tag attribute. Just the tag attribute will
///       cause deserialization to fail
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "type", content = "data")]
pub enum Auth {
    /// Represents a request to perform an authentication handshake,
    /// providing the public key and salt from one side in order to
    /// derive the shared key
    #[serde(rename = "auth_handshake")]
    Handshake {
        /// Bytes of the public key
        #[serde(with = "serde_bytes")]
        public_key: PublicKeyBytes,

        /// Randomly generated salt
        #[serde(with = "serde_bytes")]
        salt: Salt,
    },

    /// Represents the bytes of an encrypted message
    ///
    /// Underneath, will be one of either [`AuthRequest`] or [`AuthResponse`]
    #[serde(rename = "auth_msg")]
    Msg {
        #[serde(with = "serde_bytes")]
        encrypted_payload: Vec<u8>,
    },
}

/// Represents authentication messages that act as initiators such as providing
/// a challenge, verifying information, presenting information, or highlighting an error
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "type")]
pub enum AuthRequest {
    /// Represents a challenge comprising a series of questions to be presented
    Challenge {
        questions: Vec<AuthQuestion>,
        options: HashMap<String, String>,
    },

    /// Represents an ask to verify some information
    Verify { kind: AuthVerifyKind, text: String },

    /// Represents some information to be presented
    Info { text: String },

    /// Represents some error that occurred
    Error { kind: AuthErrorKind, text: String },
}

/// Represents authentication messages that are responses to auth requests such
/// as answers to challenges or verifying information
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "type")]
pub enum AuthResponse {
    /// Represents the answers to a previously-asked challenge
    Challenge { answers: Vec<String> },

    /// Represents the answer to a previously-asked verify
    Verify { valid: bool },
}

/// Represents the type of verification being requested
#[derive(Copy, Clone, Debug, Display, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum AuthVerifyKind {
    /// An ask to verify the host such as with SSH
    #[display(fmt = "host")]
    Host,
}

/// Represents a single question in a challenge
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct AuthQuestion {
    /// The text of the question
    pub text: String,

    /// Any options information specific to a particular auth domain
    /// such as including a username and instructions for SSH authentication
    pub options: HashMap<String, String>,
}

impl AuthQuestion {
    /// Creates a new question without any options data
    pub fn new(text: impl Into<String>) -> Self {
        Self {
            text: text.into(),
            options: HashMap::new(),
        }
    }
}

/// Represents the type of error encountered during authentication
#[derive(Copy, Clone, Debug, Display, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AuthErrorKind {
    /// When the answer(s) to a challenge do not pass authentication
    FailedChallenge,

    /// When verification during authentication fails
    /// (e.g. a host is not allowed or blocked)
    FailedVerification,

    /// When the error is unknown
    Unknown,
}