1use signature::Verifier;
8use ssh_encoding::{CheckedSum, Decode, Encode, Error as EncodingError, Reader, Writer};
9use ssh_key::{public::KeyData, Signature};
10
11use super::MessageExtension;
12use crate::proto::ProtoError;
13
14#[derive(Debug, Clone, PartialEq)]
21pub struct QueryResponse {
22 pub extensions: Vec<String>,
24}
25
26impl Encode for QueryResponse {
27 fn encoded_len(&self) -> Result<usize, EncodingError> {
28 self.extensions.encoded_len()
29 }
30
31 fn encode(&self, writer: &mut impl Writer) -> Result<(), EncodingError> {
32 self.extensions.encode(writer)
33 }
34}
35
36impl Decode for QueryResponse {
37 type Error = ProtoError;
38
39 fn decode(reader: &mut impl Reader) -> Result<Self, Self::Error> {
40 let extensions = Vec::<String>::decode(reader)?;
41
42 Ok(Self { extensions })
43 }
44}
45
46impl MessageExtension for QueryResponse {
47 const NAME: &'static str = "query";
48}
49
50#[derive(Debug, Clone, PartialEq)]
59pub struct SessionBind {
60 pub host_key: KeyData,
62
63 pub session_id: Vec<u8>,
65
66 pub signature: Signature,
68
69 pub is_forwarding: bool,
71}
72
73impl Decode for SessionBind {
74 type Error = crate::proto::error::ProtoError;
75
76 fn decode(reader: &mut impl Reader) -> Result<Self, Self::Error> {
77 let host_key = reader.read_prefixed(KeyData::decode)?;
78 let session_id = Vec::decode(reader)?;
79 let signature = reader.read_prefixed(Signature::decode)?;
80 Ok(Self {
81 host_key,
82 session_id,
83 signature,
84 is_forwarding: u8::decode(reader)? != 0,
85 })
86 }
87}
88
89impl Encode for SessionBind {
90 fn encoded_len(&self) -> ssh_encoding::Result<usize> {
91 [
92 self.host_key.encoded_len_prefixed()?,
93 self.session_id.encoded_len()?,
94 self.signature.encoded_len_prefixed()?,
95 1u8.encoded_len()?,
96 ]
97 .checked_sum()
98 }
99
100 fn encode(&self, writer: &mut impl Writer) -> ssh_encoding::Result<()> {
101 self.host_key.encode_prefixed(writer)?;
102 self.session_id.encode(writer)?;
103 self.signature.encode_prefixed(writer)?;
104
105 if self.is_forwarding {
106 1u8.encode(writer)
107 } else {
108 0u8.encode(writer)
109 }
110 }
111}
112
113impl SessionBind {
114 pub fn verify_signature(&self) -> Result<(), ProtoError> {
122 self.host_key
123 .verify(self.session_id.as_slice(), &self.signature)?;
124 Ok(())
125 }
126}
127
128impl MessageExtension for SessionBind {
129 const NAME: &'static str = "session-bind@openssh.com";
130}
131
132#[cfg(test)]
133mod tests {
134 use testresult::TestResult;
135
136 use super::*;
137
138 fn round_trip<T>(msg: T) -> TestResult
139 where
140 T: Encode + Decode<Error = ProtoError> + std::fmt::Debug + std::cmp::PartialEq,
141 {
142 let mut buf: Vec<u8> = vec![];
143 msg.encode(&mut buf)?;
144 let mut re_encoded = &buf[..];
145
146 let msg2 = T::decode(&mut re_encoded)?;
147 assert_eq!(msg, msg2);
148
149 Ok(())
150 }
151
152 #[test]
153 fn parse_bind() -> TestResult {
154 let mut buffer: &[u8] = &[
155 0, 0, 0, 51, 0, 0, 0, 11, 115, 115, 104, 45, 101, 100, 50, 53, 53, 49, 57, 0, 0, 0, 32,
156 177, 185, 198, 92, 165, 45, 127, 95, 202, 195, 226, 63, 6, 115, 10, 104, 18, 137, 172,
157 240, 153, 154, 174, 74, 83, 7, 1, 204, 14, 177, 153, 40, 0, 0, 0, 32, 138, 165, 196,
158 144, 149, 107, 183, 188, 222, 182, 34, 173, 59, 118, 9, 35, 186, 147, 114, 114, 50,
159 106, 41, 182, 196, 119, 226, 82, 233, 148, 236, 135, 0, 0, 0, 83, 0, 0, 0, 11, 115,
160 115, 104, 45, 101, 100, 50, 53, 53, 49, 57, 0, 0, 0, 64, 95, 212, 52, 189, 8, 162, 17,
161 3, 15, 218, 2, 4, 136, 7, 47, 57, 121, 6, 194, 165, 221, 27, 175, 241, 6, 57, 84, 141,
162 77, 55, 235, 9, 77, 160, 32, 76, 11, 227, 240, 235, 122, 178, 80, 133, 183, 91, 89, 89,
163 142, 115, 145, 15, 78, 112, 139, 28, 201, 8, 197, 222, 117, 141, 88, 5, 0,
164 ];
165 let bind = SessionBind::decode(&mut buffer)?;
166 eprintln!("Bind: {bind:#?}");
167
168 bind.verify_signature()?;
171
172 round_trip(bind)?;
173
174 Ok(())
175 }
176}