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