1#![allow(clippy::use_self)]
10
11use alloc::vec::Vec;
12use core::fmt;
13
14#[cfg(feature = "serde")]
15use serde::{Deserialize, Serialize};
16
17use data_encoding::{Encoding, Specification};
18use once_cell::sync::Lazy;
19
20use crate::{
21 error::{ProtoError, ProtoResult},
22 rr::{RData, RecordData, RecordDataDecodable, RecordType},
23 serialize::binary::{BinDecoder, BinEncodable, BinEncoder, Restrict, RestrictedMath},
24};
25
26pub static HEX: Lazy<Encoding> = Lazy::new(|| {
28 let mut spec = Specification::new();
29 spec.symbols.push_str("0123456789abcdef");
30 spec.ignore.push_str(" \t\r\n");
31 spec.translate.from.push_str("ABCDEF");
32 spec.translate.to.push_str("abcdef");
33 spec.encoding().expect("error in sshfp HEX encoding")
34});
35
36#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
63#[derive(Debug, PartialEq, Eq, Hash, Clone)]
64pub struct SSHFP {
65 algorithm: Algorithm,
66 fingerprint_type: FingerprintType,
67 fingerprint: Vec<u8>,
68}
69
70impl SSHFP {
71 pub fn new(
79 algorithm: Algorithm,
80 fingerprint_type: FingerprintType,
81 fingerprint: Vec<u8>,
82 ) -> Self {
83 Self {
84 algorithm,
85 fingerprint_type,
86 fingerprint,
87 }
88 }
89
90 pub fn algorithm(&self) -> Algorithm {
92 self.algorithm
93 }
94
95 pub fn fingerprint_type(&self) -> FingerprintType {
97 self.fingerprint_type
98 }
99
100 pub fn fingerprint(&self) -> &[u8] {
102 &self.fingerprint
103 }
104}
105
106#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
126#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
127pub enum Algorithm {
128 Reserved,
130
131 RSA,
133
134 DSA,
136
137 ECDSA,
139
140 Ed25519,
142
143 Ed448,
145
146 Unassigned(u8),
148}
149
150impl From<u8> for Algorithm {
151 fn from(alg: u8) -> Self {
152 match alg {
153 0 => Self::Reserved,
154 1 => Self::RSA,
155 2 => Self::DSA,
156 3 => Self::ECDSA,
157 4 => Self::Ed25519, 6 => Self::Ed448,
159 _ => Self::Unassigned(alg),
160 }
161 }
162}
163
164impl From<Algorithm> for u8 {
165 fn from(algorithm: Algorithm) -> Self {
166 match algorithm {
167 Algorithm::Reserved => 0,
168 Algorithm::RSA => 1,
169 Algorithm::DSA => 2,
170 Algorithm::ECDSA => 3,
171 Algorithm::Ed25519 => 4,
172 Algorithm::Ed448 => 6,
173 Algorithm::Unassigned(alg) => alg,
174 }
175 }
176}
177
178#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
200#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
201pub enum FingerprintType {
202 Reserved,
204
205 #[cfg_attr(feature = "serde", serde(rename = "SHA-1"))]
207 SHA1,
208
209 #[cfg_attr(feature = "serde", serde(rename = "SHA-256"))]
211 SHA256,
212
213 Unassigned(u8),
215}
216
217impl From<u8> for FingerprintType {
218 fn from(ft: u8) -> Self {
219 match ft {
220 0 => Self::Reserved,
221 1 => Self::SHA1,
222 2 => Self::SHA256,
223 _ => Self::Unassigned(ft),
224 }
225 }
226}
227
228impl From<FingerprintType> for u8 {
229 fn from(fingerprint_type: FingerprintType) -> Self {
230 match fingerprint_type {
231 FingerprintType::Reserved => 0,
232 FingerprintType::SHA1 => 1,
233 FingerprintType::SHA256 => 2,
234 FingerprintType::Unassigned(ft) => ft,
235 }
236 }
237}
238
239impl BinEncodable for SSHFP {
240 fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
241 encoder.emit_u8(self.algorithm().into())?;
242 encoder.emit_u8(self.fingerprint_type().into())?;
243 encoder.emit_vec(self.fingerprint())
244 }
245}
246
247impl<'r> RecordDataDecodable<'r> for SSHFP {
248 fn read_data(decoder: &mut BinDecoder<'r>, length: Restrict<u16>) -> ProtoResult<Self> {
249 let algorithm = decoder.read_u8()?.unverified().into();
250 let fingerprint_type = decoder.read_u8()?.unverified().into();
251 let fingerprint_len = length
252 .map(|l| l as usize)
253 .checked_sub(2)
254 .map_err(|_| ProtoError::from("invalid rdata length in SSHFP"))?
255 .unverified();
256 let fingerprint = decoder.read_vec(fingerprint_len)?.unverified();
257 Ok(SSHFP::new(algorithm, fingerprint_type, fingerprint))
258 }
259}
260
261impl RecordData for SSHFP {
262 fn try_from_rdata(data: RData) -> Result<Self, RData> {
263 match data {
264 RData::SSHFP(data) => Ok(data),
265 _ => Err(data),
266 }
267 }
268
269 fn try_borrow(data: &RData) -> Option<&Self> {
270 match data {
271 RData::SSHFP(data) => Some(data),
272 _ => None,
273 }
274 }
275
276 fn record_type(&self) -> RecordType {
277 RecordType::SSHFP
278 }
279
280 fn into_rdata(self) -> RData {
281 RData::SSHFP(self)
282 }
283}
284
285impl fmt::Display for SSHFP {
299 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
300 write!(
301 f,
302 "{algorithm} {ty} {fingerprint}",
303 algorithm = u8::from(self.algorithm),
304 ty = u8::from(self.fingerprint_type),
305 fingerprint = HEX.encode(&self.fingerprint),
306 )
307 }
308}
309
310#[cfg(test)]
311mod tests {
312 use super::*;
313
314 #[test]
315 fn read_algorithm() {
316 assert_eq!(Algorithm::Reserved, 0.into());
317 assert_eq!(Algorithm::RSA, 1.into());
318 assert_eq!(Algorithm::DSA, 2.into());
319 assert_eq!(Algorithm::ECDSA, 3.into());
320 assert_eq!(Algorithm::Ed25519, 4.into());
321 assert_eq!(Algorithm::Ed448, 6.into());
322 assert_eq!(Algorithm::Unassigned(17), 17.into());
323 assert_eq!(Algorithm::Unassigned(42), 42.into());
324
325 assert_eq!(0u8, Algorithm::Reserved.into());
326 assert_eq!(1u8, Algorithm::RSA.into());
327 assert_eq!(2u8, Algorithm::DSA.into());
328 assert_eq!(3u8, Algorithm::ECDSA.into());
329 assert_eq!(4u8, Algorithm::Ed25519.into());
330 assert_eq!(6u8, Algorithm::Ed448.into());
331 assert_eq!(17u8, Algorithm::Unassigned(17).into());
332 assert_eq!(42u8, Algorithm::Unassigned(42).into());
333 }
334
335 #[test]
336 fn read_fingerprint_type() {
337 assert_eq!(FingerprintType::Reserved, 0.into());
338 assert_eq!(FingerprintType::SHA1, 1.into());
339 assert_eq!(FingerprintType::SHA256, 2.into());
340 assert_eq!(FingerprintType::Unassigned(12), 12.into());
341 assert_eq!(FingerprintType::Unassigned(89), 89.into());
342
343 assert_eq!(0u8, FingerprintType::Reserved.into());
344 assert_eq!(1u8, FingerprintType::SHA1.into());
345 assert_eq!(2u8, FingerprintType::SHA256.into());
346 assert_eq!(12u8, FingerprintType::Unassigned(12).into());
347 assert_eq!(89u8, FingerprintType::Unassigned(89).into());
348 }
349
350 fn test_encode_decode(rdata: SSHFP, result: &[u8]) {
351 let mut bytes = Vec::new();
352 let mut encoder = BinEncoder::new(&mut bytes);
353 rdata.emit(&mut encoder).expect("failed to emit SSHFP");
354 let bytes = encoder.into_bytes();
355 assert_eq!(bytes, &result);
356
357 let mut decoder = BinDecoder::new(result);
358 let read_rdata = SSHFP::read_data(&mut decoder, Restrict::new(result.len() as u16))
359 .expect("failed to read SSHFP");
360 assert_eq!(read_rdata, rdata)
361 }
362
363 #[test]
364 fn test_encode_decode_sshfp() {
365 test_encode_decode(
366 SSHFP::new(Algorithm::RSA, FingerprintType::SHA256, vec![]),
367 &[1, 2],
368 );
369 test_encode_decode(
370 SSHFP::new(
371 Algorithm::ECDSA,
372 FingerprintType::SHA1,
373 vec![115, 115, 104, 102, 112],
374 ),
375 &[3, 1, 115, 115, 104, 102, 112],
376 );
377 test_encode_decode(
378 SSHFP::new(
379 Algorithm::Reserved,
380 FingerprintType::Reserved,
381 b"ssh fingerprint".to_vec(),
382 ),
383 &[
384 0, 0, 115, 115, 104, 32, 102, 105, 110, 103, 101, 114, 112, 114, 105, 110, 116,
385 ],
386 );
387 test_encode_decode(
388 SSHFP::new(
389 Algorithm::Unassigned(255),
390 FingerprintType::Unassigned(13),
391 vec![100, 110, 115, 115, 101, 99, 32, 100, 97, 110, 101],
392 ),
393 &[255, 13, 100, 110, 115, 115, 101, 99, 32, 100, 97, 110, 101],
394 );
395 }
396}