ssh_key/fingerprint/
randomart.rs1use super::Fingerprint;
10use core::fmt;
11
12const WIDTH: usize = 17;
13const HEIGHT: usize = 9;
14const VALUES: &[u8; 17] = b" .o+=*BOX@%&#/^SE";
15const NVALUES: u8 = VALUES.len() as u8 - 1;
16
17type Field = [[u8; WIDTH]; HEIGHT];
18
19pub(super) struct Randomart<'a> {
21 header: &'a str,
22 field: Field,
23 footer: &'static str,
24}
25
26impl<'a> Randomart<'a> {
27 #[allow(clippy::arithmetic_side_effects)]
30 pub(super) fn new(header: &'a str, fingerprint: Fingerprint) -> Self {
31 let mut field = Field::default();
32 let mut x = WIDTH / 2;
33 let mut y = HEIGHT / 2;
34
35 for mut byte in fingerprint.as_bytes().iter().copied() {
36 for _ in 0..4 {
37 if byte & 0x1 == 0 {
38 x = x.saturating_sub(1);
39 } else {
40 x = x.saturating_add(1);
41 }
42
43 if byte & 0x2 == 0 {
44 y = y.saturating_sub(1);
45 } else {
46 y = y.saturating_add(1);
47 }
48
49 x = x.min(WIDTH.saturating_sub(1));
50 y = y.min(HEIGHT.saturating_sub(1));
51
52 if field[y][x] < NVALUES - 2 {
53 field[y][x] = field[y][x].saturating_add(1);
54 }
55
56 byte >>= 2;
57 }
58 }
59
60 field[HEIGHT / 2][WIDTH / 2] = NVALUES - 1;
61 field[y][x] = NVALUES;
62
63 Self {
64 header,
65 field,
66 footer: fingerprint.footer(),
67 }
68 }
69}
70
71impl fmt::Display for Randomart<'_> {
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 writeln!(f, "+{:-^width$}+", self.header, width = WIDTH)?;
74
75 for row in self.field {
76 write!(f, "|")?;
77
78 for c in row {
79 write!(f, "{}", VALUES[c as usize] as char)?;
80 }
81
82 writeln!(f, "|")?;
83 }
84
85 write!(f, "+{:-^width$}+", self.footer, width = WIDTH)
86 }
87}
88
89#[cfg(all(test, feature = "alloc"))]
90mod tests {
91 use super::Fingerprint;
92
93 const EXAMPLE_FINGERPRINT: &str = "SHA256:UCUiLr7Pjs9wFFJMDByLgc3NrtdU344OgUM45wZPcIQ";
94 const EXAMPLE_RANDOMART: &str = "\
95+--[ED25519 256]--+
96|o+oO==+ o.. |
97|.o++Eo+o.. |
98|. +.oO.o . . |
99| . o..B.. . . |
100| ...+ .S. o |
101| .o. . . . . |
102| o.. o |
103| B . |
104| .o* |
105+----[SHA256]-----+";
106
107 #[test]
108 fn generation() {
109 let fingerprint = EXAMPLE_FINGERPRINT.parse::<Fingerprint>().unwrap();
110 let randomart = fingerprint.to_randomart("[ED25519 256]");
111 assert_eq!(EXAMPLE_RANDOMART, randomart);
112 }
113}