Skip to main content

ed448/
hex.rs

1//! Hexadecimal encoding support
2use crate::{Error, Signature};
3use core::{fmt, str};
4
5impl fmt::LowerHex for Signature {
6    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
7        for component in [&self.R, &self.s] {
8            for byte in component {
9                write!(f, "{byte:02x}")?;
10            }
11        }
12        Ok(())
13    }
14}
15
16impl fmt::UpperHex for Signature {
17    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18        for component in [&self.R, &self.s] {
19            for byte in component {
20                write!(f, "{byte:02X}")?;
21            }
22        }
23        Ok(())
24    }
25}
26
27/// Decode a signature from hexadecimal.
28///
29/// Upper and lower case hexadecimal are both accepted, however mixed case is
30/// rejected.
31impl str::FromStr for Signature {
32    type Err = Error;
33
34    fn from_str(hex: &str) -> signature::Result<Self> {
35        if hex.len() != Signature::BYTE_SIZE * 2 {
36            return Err(Error::new());
37        }
38
39        let mut upper_case = None;
40
41        // Ensure all characters are valid and case is not mixed
42        for &byte in hex.as_bytes() {
43            match byte {
44                b'0'..=b'9' => (),
45                b'a'..=b'z' => match upper_case {
46                    Some(true) => return Err(Error::new()),
47                    Some(false) => (),
48                    None => upper_case = Some(false),
49                },
50                b'A'..=b'Z' => match upper_case {
51                    Some(true) => (),
52                    Some(false) => return Err(Error::new()),
53                    None => upper_case = Some(true),
54                },
55                _ => return Err(Error::new()),
56            }
57        }
58
59        let mut result = [0u8; Self::BYTE_SIZE];
60        for (digit, byte) in hex.as_bytes().chunks_exact(2).zip(result.iter_mut()) {
61            *byte = str::from_utf8(digit)
62                .ok()
63                .and_then(|s| u8::from_str_radix(s, 16).ok())
64                .ok_or_else(Error::new)?;
65        }
66
67        Self::try_from(&result[..])
68    }
69}