1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//! Hexadecimal encoding helpers

use crate::{Error, Result};
use core::{fmt, str};

/// Write the provided slice to the formatter as lower case hexadecimal
#[inline]
pub(crate) fn write_lower(slice: &[u8], formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
    for byte in slice {
        write!(formatter, "{:02x}", byte)?;
    }
    Ok(())
}

/// Write the provided slice to the formatter as upper case hexadecimal
#[inline]
pub(crate) fn write_upper(slice: &[u8], formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
    for byte in slice {
        write!(formatter, "{:02X}", byte)?;
    }
    Ok(())
}

/// Decode the provided hexadecimal string into the provided buffer.
///
/// Accepts either lower case or upper case hexadecimal, but not mixed.
// TODO(tarcieri): constant-time hex decoder?
pub(crate) fn decode(hex: &str, out: &mut [u8]) -> Result<()> {
    if hex.as_bytes().len() != out.len() * 2 {
        return Err(Error);
    }

    let mut upper_case = None;

    // Ensure all characters are valid and case is not mixed
    for &byte in hex.as_bytes() {
        match byte {
            b'0'..=b'9' => (),
            b'a'..=b'z' => match upper_case {
                Some(true) => return Err(Error),
                Some(false) => (),
                None => upper_case = Some(false),
            },
            b'A'..=b'Z' => match upper_case {
                Some(true) => (),
                Some(false) => return Err(Error),
                None => upper_case = Some(true),
            },
            _ => return Err(Error),
        }
    }

    for (digit, byte) in hex.as_bytes().chunks_exact(2).zip(out.iter_mut()) {
        *byte = str::from_utf8(digit)
            .ok()
            .and_then(|s| u8::from_str_radix(s, 16).ok())
            .ok_or(Error)?;
    }

    Ok(())
}

#[cfg(all(test, feature = "std"))]
mod tests {
    use core::fmt;
    use hex_literal::hex;

    const EXAMPLE_DATA: &[u8] = &hex!("0123456789ABCDEF");
    const EXAMPLE_HEX_LOWER: &str = "0123456789abcdef";
    const EXAMPLE_HEX_UPPER: &str = "0123456789ABCDEF";

    struct Wrapper<'a>(&'a [u8]);

    impl fmt::LowerHex for Wrapper<'_> {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            super::write_lower(self.0, f)
        }
    }

    impl fmt::UpperHex for Wrapper<'_> {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            super::write_upper(self.0, f)
        }
    }

    #[test]
    fn decode_lower() {
        let mut buf = [0u8; 8];
        super::decode(EXAMPLE_HEX_LOWER, &mut buf).unwrap();
        assert_eq!(buf, EXAMPLE_DATA);
    }

    #[test]
    fn decode_upper() {
        let mut buf = [0u8; 8];
        super::decode(EXAMPLE_HEX_LOWER, &mut buf).unwrap();
        assert_eq!(buf, EXAMPLE_DATA);
    }

    #[test]
    fn decode_rejects_mixed_case() {
        let mut buf = [0u8; 8];
        assert!(super::decode("0123456789abcDEF", &mut buf).is_err());
    }

    #[test]
    fn decode_rejects_too_short() {
        let mut buf = [0u8; 9];
        assert!(super::decode(EXAMPLE_HEX_LOWER, &mut buf).is_err());
    }

    #[test]
    fn decode_rejects_too_long() {
        let mut buf = [0u8; 7];
        assert!(super::decode(EXAMPLE_HEX_LOWER, &mut buf).is_err());
    }

    #[test]
    fn encode_lower() {
        assert_eq!(format!("{:x}", Wrapper(EXAMPLE_DATA)), EXAMPLE_HEX_LOWER);
    }

    #[test]
    fn encode_upper() {
        assert_eq!(format!("{:X}", Wrapper(EXAMPLE_DATA)), EXAMPLE_HEX_UPPER);
    }
}