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::ProtoResult,
22 rr::{RData, RecordData, RecordDataDecodable, RecordType},
23 serialize::{
24 binary::{BinDecoder, BinEncodable, BinEncoder, DecodeError, Restrict, RestrictedMath},
25 txt::ParseError,
26 },
27};
28
29pub static HEX: Lazy<Encoding> = Lazy::new(|| {
31 let mut spec = Specification::new();
32 spec.symbols.push_str("0123456789abcdef");
33 spec.ignore.push_str(" \t\r\n");
34 spec.translate.from.push_str("ABCDEF");
35 spec.translate.to.push_str("abcdef");
36 spec.encoding().expect("error in sshfp HEX encoding")
37});
38
39#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
66#[derive(Debug, PartialEq, Eq, Hash, Clone)]
67#[non_exhaustive]
68pub struct SSHFP {
69 pub algorithm: Algorithm,
71
72 pub fingerprint_type: FingerprintType,
74
75 pub fingerprint: Vec<u8>,
77}
78
79impl SSHFP {
80 pub fn new(
88 algorithm: Algorithm,
89 fingerprint_type: FingerprintType,
90 fingerprint: Vec<u8>,
91 ) -> Self {
92 Self {
93 algorithm,
94 fingerprint_type,
95 fingerprint,
96 }
97 }
98
99 pub(crate) fn from_tokens<'i, I: Iterator<Item = &'i str>>(
115 mut tokens: I,
116 ) -> Result<Self, ParseError> {
117 fn missing_field<E: From<ParseError>>(field: &str) -> E {
118 ParseError::Msg(format!("SSHFP {field} field missing")).into()
119 }
120 let (algorithm, fingerprint_type) = {
121 let mut parse_u8 = |field: &str| {
122 tokens
123 .next()
124 .ok_or_else(|| missing_field(field))
125 .and_then(|t| t.parse::<u8>().map_err(ParseError::from))
126 };
127 (
128 parse_u8("algorithm")?.into(),
129 parse_u8("fingerprint type")?.into(),
130 )
131 };
132 let fingerprint = HEX.decode(
133 tokens
134 .next()
135 .filter(|fp| !fp.is_empty())
136 .ok_or_else(|| missing_field::<ParseError>("fingerprint"))?
137 .as_bytes(),
138 )?;
139 if tokens.next().is_some() {
140 return Err(ParseError::Message("too many fields for SSHFP"));
141 }
142 Ok(Self::new(algorithm, fingerprint_type, fingerprint))
143 }
144}
145
146#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
166#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
167pub enum Algorithm {
168 Reserved,
170
171 RSA,
173
174 DSA,
176
177 ECDSA,
179
180 Ed25519,
182
183 Ed448,
185
186 Unassigned(u8),
188}
189
190impl From<u8> for Algorithm {
191 fn from(alg: u8) -> Self {
192 match alg {
193 0 => Self::Reserved,
194 1 => Self::RSA,
195 2 => Self::DSA,
196 3 => Self::ECDSA,
197 4 => Self::Ed25519, 6 => Self::Ed448,
199 _ => Self::Unassigned(alg),
200 }
201 }
202}
203
204impl From<Algorithm> for u8 {
205 fn from(algorithm: Algorithm) -> Self {
206 match algorithm {
207 Algorithm::Reserved => 0,
208 Algorithm::RSA => 1,
209 Algorithm::DSA => 2,
210 Algorithm::ECDSA => 3,
211 Algorithm::Ed25519 => 4,
212 Algorithm::Ed448 => 6,
213 Algorithm::Unassigned(alg) => alg,
214 }
215 }
216}
217
218#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
240#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
241pub enum FingerprintType {
242 Reserved,
244
245 #[cfg_attr(feature = "serde", serde(rename = "SHA-1"))]
247 SHA1,
248
249 #[cfg_attr(feature = "serde", serde(rename = "SHA-256"))]
251 SHA256,
252
253 Unassigned(u8),
255}
256
257impl From<u8> for FingerprintType {
258 fn from(ft: u8) -> Self {
259 match ft {
260 0 => Self::Reserved,
261 1 => Self::SHA1,
262 2 => Self::SHA256,
263 _ => Self::Unassigned(ft),
264 }
265 }
266}
267
268impl From<FingerprintType> for u8 {
269 fn from(fingerprint_type: FingerprintType) -> Self {
270 match fingerprint_type {
271 FingerprintType::Reserved => 0,
272 FingerprintType::SHA1 => 1,
273 FingerprintType::SHA256 => 2,
274 FingerprintType::Unassigned(ft) => ft,
275 }
276 }
277}
278
279impl BinEncodable for SSHFP {
280 fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
281 encoder.emit_u8(self.algorithm.into())?;
282 encoder.emit_u8(self.fingerprint_type.into())?;
283 encoder.emit_vec(&self.fingerprint)
284 }
285}
286
287impl<'r> RecordDataDecodable<'r> for SSHFP {
288 fn read_data(decoder: &mut BinDecoder<'r>, length: Restrict<u16>) -> Result<Self, DecodeError> {
289 let algorithm = decoder.read_u8()?.unverified().into();
290 let fingerprint_type = decoder.read_u8()?.unverified().into();
291 let fingerprint_len = length
292 .map(|l| l as usize)
293 .checked_sub(2)
294 .map_err(|len| DecodeError::IncorrectRDataLengthRead { read: 2, len })?
295 .unverified();
296 let fingerprint = decoder.read_vec(fingerprint_len)?.unverified();
297 Ok(SSHFP::new(algorithm, fingerprint_type, fingerprint))
298 }
299}
300
301impl RecordData for SSHFP {
302 fn try_borrow(data: &RData) -> Option<&Self> {
303 match data {
304 RData::SSHFP(data) => Some(data),
305 _ => None,
306 }
307 }
308
309 fn record_type(&self) -> RecordType {
310 RecordType::SSHFP
311 }
312
313 fn into_rdata(self) -> RData {
314 RData::SSHFP(self)
315 }
316}
317
318impl fmt::Display for SSHFP {
332 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
333 write!(
334 f,
335 "{algorithm} {ty} {fingerprint}",
336 algorithm = u8::from(self.algorithm),
337 ty = u8::from(self.fingerprint_type),
338 fingerprint = HEX.encode(&self.fingerprint),
339 )
340 }
341}
342
343#[cfg(test)]
344mod tests {
345 use super::*;
346
347 #[test]
348 fn read_algorithm() {
349 assert_eq!(Algorithm::Reserved, 0.into());
350 assert_eq!(Algorithm::RSA, 1.into());
351 assert_eq!(Algorithm::DSA, 2.into());
352 assert_eq!(Algorithm::ECDSA, 3.into());
353 assert_eq!(Algorithm::Ed25519, 4.into());
354 assert_eq!(Algorithm::Ed448, 6.into());
355 assert_eq!(Algorithm::Unassigned(17), 17.into());
356 assert_eq!(Algorithm::Unassigned(42), 42.into());
357
358 assert_eq!(0u8, Algorithm::Reserved.into());
359 assert_eq!(1u8, Algorithm::RSA.into());
360 assert_eq!(2u8, Algorithm::DSA.into());
361 assert_eq!(3u8, Algorithm::ECDSA.into());
362 assert_eq!(4u8, Algorithm::Ed25519.into());
363 assert_eq!(6u8, Algorithm::Ed448.into());
364 assert_eq!(17u8, Algorithm::Unassigned(17).into());
365 assert_eq!(42u8, Algorithm::Unassigned(42).into());
366 }
367
368 #[test]
369 fn read_fingerprint_type() {
370 assert_eq!(FingerprintType::Reserved, 0.into());
371 assert_eq!(FingerprintType::SHA1, 1.into());
372 assert_eq!(FingerprintType::SHA256, 2.into());
373 assert_eq!(FingerprintType::Unassigned(12), 12.into());
374 assert_eq!(FingerprintType::Unassigned(89), 89.into());
375
376 assert_eq!(0u8, FingerprintType::Reserved.into());
377 assert_eq!(1u8, FingerprintType::SHA1.into());
378 assert_eq!(2u8, FingerprintType::SHA256.into());
379 assert_eq!(12u8, FingerprintType::Unassigned(12).into());
380 assert_eq!(89u8, FingerprintType::Unassigned(89).into());
381 }
382
383 fn test_encode_decode(rdata: SSHFP, result: &[u8]) {
384 let mut bytes = Vec::new();
385 let mut encoder = BinEncoder::new(&mut bytes);
386 rdata.emit(&mut encoder).expect("failed to emit SSHFP");
387 let bytes = encoder.into_bytes();
388 assert_eq!(bytes, &result);
389
390 let mut decoder = BinDecoder::new(result);
391 let read_rdata = SSHFP::read_data(&mut decoder, Restrict::new(result.len() as u16))
392 .expect("failed to read SSHFP");
393 assert_eq!(read_rdata, rdata)
394 }
395
396 #[test]
397 fn test_encode_decode_sshfp() {
398 test_encode_decode(
399 SSHFP::new(Algorithm::RSA, FingerprintType::SHA256, vec![]),
400 &[1, 2],
401 );
402 test_encode_decode(
403 SSHFP::new(
404 Algorithm::ECDSA,
405 FingerprintType::SHA1,
406 vec![115, 115, 104, 102, 112],
407 ),
408 &[3, 1, 115, 115, 104, 102, 112],
409 );
410 test_encode_decode(
411 SSHFP::new(
412 Algorithm::Reserved,
413 FingerprintType::Reserved,
414 b"ssh fingerprint".to_vec(),
415 ),
416 &[
417 0, 0, 115, 115, 104, 32, 102, 105, 110, 103, 101, 114, 112, 114, 105, 110, 116,
418 ],
419 );
420 test_encode_decode(
421 SSHFP::new(
422 Algorithm::Unassigned(255),
423 FingerprintType::Unassigned(13),
424 vec![100, 110, 115, 115, 101, 99, 32, 100, 97, 110, 101],
425 ),
426 &[255, 13, 100, 110, 115, 115, 101, 99, 32, 100, 97, 110, 101],
427 );
428 }
429
430 #[test]
431 fn test_parsing() {
432 assert!(SSHFP::from_tokens(core::iter::empty()).is_err());
433 assert!(SSHFP::from_tokens(vec!["51", "13"].into_iter()).is_err());
434 assert!(SSHFP::from_tokens(vec!["1", "-1"].into_iter()).is_err());
435 assert!(SSHFP::from_tokens(vec!["1", "1", "abcd", "foo"].into_iter()).is_err());
436
437 use crate::rr::rdata::sshfp::Algorithm::*;
438 use crate::rr::rdata::sshfp::FingerprintType::*;
439 use crate::rr::rdata::sshfp::{Algorithm, FingerprintType};
440
441 fn test_parsing(input: Vec<&str>, a: Algorithm, ft: FingerprintType, f: &[u8]) {
442 assert!(
443 SSHFP::from_tokens(input.into_iter())
444 .map(|rd| rd == SSHFP::new(a, ft, f.to_vec()))
445 .unwrap_or(false)
446 );
447 }
448
449 test_parsing(
450 vec!["1", "1", "dd465c09cfa51fb45020cc83316fff21b9ec74ac"],
451 RSA,
452 SHA1,
453 &[
454 221, 70, 92, 9, 207, 165, 31, 180, 80, 32, 204, 131, 49, 111, 255, 33, 185, 236,
455 116, 172,
456 ],
457 );
458 test_parsing(
459 vec![
460 "1",
461 "2",
462 "b049f950d1397b8fee6a61e4d14a9acdc4721e084eff5460bbed80cfaa2ce2cb",
463 ],
464 RSA,
465 SHA256,
466 &[
467 176, 73, 249, 80, 209, 57, 123, 143, 238, 106, 97, 228, 209, 74, 154, 205, 196,
468 114, 30, 8, 78, 255, 84, 96, 187, 237, 128, 207, 170, 44, 226, 203,
469 ],
470 );
471 test_parsing(
472 vec!["2", "1", "3b6ba6110f5ffcd29469fc1ec2ee25d61718badd"],
473 DSA,
474 SHA1,
475 &[
476 59, 107, 166, 17, 15, 95, 252, 210, 148, 105, 252, 30, 194, 238, 37, 214, 23, 24,
477 186, 221,
478 ],
479 );
480 test_parsing(
481 vec![
482 "2",
483 "2",
484 "f9b8a6a460639306f1b38910456a6ae1018a253c47ecec12db77d7a0878b4d83",
485 ],
486 DSA,
487 SHA256,
488 &[
489 249, 184, 166, 164, 96, 99, 147, 6, 241, 179, 137, 16, 69, 106, 106, 225, 1, 138,
490 37, 60, 71, 236, 236, 18, 219, 119, 215, 160, 135, 139, 77, 131,
491 ],
492 );
493 test_parsing(
494 vec!["3", "1", "c64607a28c5300fec1180b6e417b922943cffcdd"],
495 ECDSA,
496 SHA1,
497 &[
498 198, 70, 7, 162, 140, 83, 0, 254, 193, 24, 11, 110, 65, 123, 146, 41, 67, 207, 252,
499 221,
500 ],
501 );
502 test_parsing(
503 vec![
504 "3",
505 "2",
506 "821eb6c1c98d9cc827ab7f456304c0f14785b7008d9e8646a8519de80849afc7",
507 ],
508 ECDSA,
509 SHA256,
510 &[
511 130, 30, 182, 193, 201, 141, 156, 200, 39, 171, 127, 69, 99, 4, 192, 241, 71, 133,
512 183, 0, 141, 158, 134, 70, 168, 81, 157, 232, 8, 73, 175, 199,
513 ],
514 );
515 test_parsing(
516 vec!["4", "1", "6b6f6165636874657266696e6765727072696e74"],
517 Ed25519,
518 SHA1,
519 &[
520 107, 111, 97, 101, 99, 104, 116, 101, 114, 102, 105, 110, 103, 101, 114, 112, 114,
521 105, 110, 116,
522 ],
523 );
524 test_parsing(
525 vec![
526 "4",
527 "2",
528 "a87f1b687ac0e57d2a081a2f282672334d90ed316d2b818ca9580ea384d92401",
529 ],
530 Ed25519,
531 SHA256,
532 &[
533 168, 127, 27, 104, 122, 192, 229, 125, 42, 8, 26, 47, 40, 38, 114, 51, 77, 144,
534 237, 49, 109, 43, 129, 140, 169, 88, 14, 163, 132, 217, 36, 1,
535 ],
536 );
537 }
538}