1#![allow(clippy::use_self)]
10
11use std::fmt;
12
13#[cfg(feature = "serde-config")]
14use serde::{Deserialize, Serialize};
15
16use data_encoding::{Encoding, Specification};
17use lazy_static::lazy_static;
18
19use crate::error::*;
20use crate::serialize::binary::*;
21
22lazy_static! {
23 pub static ref HEX: Encoding = {
25 let mut spec = Specification::new();
26 spec.symbols.push_str("0123456789abcdef");
27 spec.ignore.push_str(" \t\r\n");
28 spec.translate.from.push_str("ABCDEF");
29 spec.translate.to.push_str("abcdef");
30 spec.encoding().expect("error in sshfp HEX encoding")
31 };
32}
33
34#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
61#[derive(Debug, PartialEq, Eq, Hash, Clone)]
62pub struct SSHFP {
63 algorithm: Algorithm,
64 fingerprint_type: FingerprintType,
65 fingerprint: Vec<u8>,
66}
67
68impl SSHFP {
69 pub fn new(
77 algorithm: Algorithm,
78 fingerprint_type: FingerprintType,
79 fingerprint: Vec<u8>,
80 ) -> Self {
81 Self {
82 algorithm,
83 fingerprint_type,
84 fingerprint,
85 }
86 }
87
88 pub fn algorithm(&self) -> Algorithm {
90 self.algorithm
91 }
92
93 pub fn fingerprint_type(&self) -> FingerprintType {
95 self.fingerprint_type
96 }
97
98 pub fn fingerprint(&self) -> &[u8] {
100 &self.fingerprint
101 }
102}
103
104#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
124#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
125pub enum Algorithm {
126 Reserved,
128
129 RSA,
131
132 DSA,
134
135 ECDSA,
137
138 Ed25519,
140
141 Ed448,
143
144 Unassigned(u8),
146}
147
148impl From<u8> for Algorithm {
149 fn from(alg: u8) -> Self {
150 match alg {
151 0 => Self::Reserved,
152 1 => Self::RSA,
153 2 => Self::DSA,
154 3 => Self::ECDSA,
155 4 => Self::Ed25519, 6 => Self::Ed448,
157 _ => Self::Unassigned(alg),
158 }
159 }
160}
161
162impl From<Algorithm> for u8 {
163 fn from(algorithm: Algorithm) -> Self {
164 match algorithm {
165 Algorithm::Reserved => 0,
166 Algorithm::RSA => 1,
167 Algorithm::DSA => 2,
168 Algorithm::ECDSA => 3,
169 Algorithm::Ed25519 => 4,
170 Algorithm::Ed448 => 6,
171 Algorithm::Unassigned(alg) => alg,
172 }
173 }
174}
175
176#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
198#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
199pub enum FingerprintType {
200 Reserved,
202
203 SHA1,
205
206 SHA256,
208
209 Unassigned(u8),
211}
212
213impl From<u8> for FingerprintType {
214 fn from(ft: u8) -> Self {
215 match ft {
216 0 => Self::Reserved,
217 1 => Self::SHA1,
218 2 => Self::SHA256,
219 _ => Self::Unassigned(ft),
220 }
221 }
222}
223
224impl From<FingerprintType> for u8 {
225 fn from(fingerprint_type: FingerprintType) -> Self {
226 match fingerprint_type {
227 FingerprintType::Reserved => 0,
228 FingerprintType::SHA1 => 1,
229 FingerprintType::SHA256 => 2,
230 FingerprintType::Unassigned(ft) => ft,
231 }
232 }
233}
234
235pub fn read(decoder: &mut BinDecoder<'_>, rdata_length: Restrict<u16>) -> ProtoResult<SSHFP> {
237 let algorithm = decoder.read_u8()?.unverified().into();
238 let fingerprint_type = decoder.read_u8()?.unverified().into();
239 let fingerprint_len = rdata_length
240 .map(|l| l as usize)
241 .checked_sub(2)
242 .map_err(|_| ProtoError::from("invalid rdata length in SSHFP"))?
243 .unverified();
244 let fingerprint = decoder.read_vec(fingerprint_len)?.unverified();
245 Ok(SSHFP::new(algorithm, fingerprint_type, fingerprint))
246}
247
248pub fn emit(encoder: &mut BinEncoder<'_>, sshfp: &SSHFP) -> ProtoResult<()> {
250 encoder.emit_u8(sshfp.algorithm().into())?;
251 encoder.emit_u8(sshfp.fingerprint_type().into())?;
252 encoder.emit_vec(sshfp.fingerprint())
253}
254
255impl fmt::Display for SSHFP {
269 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
270 write!(
271 f,
272 "{algorithm} {ty} {fingerprint}",
273 algorithm = u8::from(self.algorithm),
274 ty = u8::from(self.fingerprint_type),
275 fingerprint = HEX.encode(&self.fingerprint),
276 )
277 }
278}
279
280#[cfg(test)]
281mod tests {
282 use super::*;
283
284 #[test]
285 fn read_algorithm() {
286 assert_eq!(Algorithm::Reserved, 0.into());
287 assert_eq!(Algorithm::RSA, 1.into());
288 assert_eq!(Algorithm::DSA, 2.into());
289 assert_eq!(Algorithm::ECDSA, 3.into());
290 assert_eq!(Algorithm::Ed25519, 4.into());
291 assert_eq!(Algorithm::Ed448, 6.into());
292 assert_eq!(Algorithm::Unassigned(17), 17.into());
293 assert_eq!(Algorithm::Unassigned(42), 42.into());
294
295 assert_eq!(0u8, Algorithm::Reserved.into());
296 assert_eq!(1u8, Algorithm::RSA.into());
297 assert_eq!(2u8, Algorithm::DSA.into());
298 assert_eq!(3u8, Algorithm::ECDSA.into());
299 assert_eq!(4u8, Algorithm::Ed25519.into());
300 assert_eq!(6u8, Algorithm::Ed448.into());
301 assert_eq!(17u8, Algorithm::Unassigned(17).into());
302 assert_eq!(42u8, Algorithm::Unassigned(42).into());
303 }
304
305 #[test]
306 fn read_fingerprint_type() {
307 assert_eq!(FingerprintType::Reserved, 0.into());
308 assert_eq!(FingerprintType::SHA1, 1.into());
309 assert_eq!(FingerprintType::SHA256, 2.into());
310 assert_eq!(FingerprintType::Unassigned(12), 12.into());
311 assert_eq!(FingerprintType::Unassigned(89), 89.into());
312
313 assert_eq!(0u8, FingerprintType::Reserved.into());
314 assert_eq!(1u8, FingerprintType::SHA1.into());
315 assert_eq!(2u8, FingerprintType::SHA256.into());
316 assert_eq!(12u8, FingerprintType::Unassigned(12).into());
317 assert_eq!(89u8, FingerprintType::Unassigned(89).into());
318 }
319
320 fn test_encode_decode(rdata: SSHFP, result: &[u8]) {
321 let mut bytes = Vec::new();
322 let mut encoder = BinEncoder::new(&mut bytes);
323 emit(&mut encoder, &rdata).expect("failed to emit SSHFP");
324 let bytes = encoder.into_bytes();
325 assert_eq!(bytes, &result);
326
327 let mut decoder = BinDecoder::new(result);
328 let read_rdata =
329 read(&mut decoder, Restrict::new(result.len() as u16)).expect("failed to read SSHFP");
330 assert_eq!(read_rdata, rdata)
331 }
332
333 #[test]
334 fn test_encode_decode_sshfp() {
335 test_encode_decode(
336 SSHFP::new(Algorithm::RSA, FingerprintType::SHA256, vec![]),
337 &[1, 2],
338 );
339 test_encode_decode(
340 SSHFP::new(
341 Algorithm::ECDSA,
342 FingerprintType::SHA1,
343 vec![115, 115, 104, 102, 112],
344 ),
345 &[3, 1, 115, 115, 104, 102, 112],
346 );
347 test_encode_decode(
348 SSHFP::new(
349 Algorithm::Reserved,
350 FingerprintType::Reserved,
351 b"ssh fingerprint".to_vec(),
352 ),
353 &[
354 0, 0, 115, 115, 104, 32, 102, 105, 110, 103, 101, 114, 112, 114, 105, 110, 116,
355 ],
356 );
357 test_encode_decode(
358 SSHFP::new(
359 Algorithm::Unassigned(255),
360 FingerprintType::Unassigned(13),
361 vec![100, 110, 115, 115, 101, 99, 32, 100, 97, 110, 101],
362 ),
363 &[255, 13, 100, 110, 115, 115, 101, 99, 32, 100, 97, 110, 101],
364 );
365 }
366}