minisign_verify/
base64.rs

1#![forbid(unsafe_code)]
2
3use core::fmt::{self, Display};
4
5#[derive(Debug, Copy, Clone, Eq, PartialEq)]
6pub enum Error {
7    /// The provided output buffer would be too small.
8    Overflow,
9    /// The input isn't valid for the given encoding.
10    InvalidInput,
11}
12
13impl std::error::Error for Error {}
14
15impl Display for Error {
16    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17        match self {
18            Error::Overflow => write!(f, "Overflow"),
19            Error::InvalidInput => write!(f, "Invalid input"),
20        }
21    }
22}
23
24pub trait Decoder {
25    /// Decode `encoded` into `bin`.
26    /// The output buffer can be larger than required; the returned slice is
27    /// a view of the buffer with the correct length.
28    fn decode<IN: AsRef<[u8]>>(bin: &mut [u8], encoded: IN) -> Result<&[u8], Error>;
29
30    /// Decode `encoded` into a `Vec<u8>`.
31    fn decode_to_vec<IN: AsRef<[u8]>>(encoded: IN) -> Result<Vec<u8>, Error> {
32        let mut bin = vec![0u8; encoded.as_ref().len()];
33        let bin_len = Self::decode(&mut bin, encoded)?.len();
34        bin.truncate(bin_len);
35        Ok(bin)
36    }
37}
38
39struct Base64Impl;
40
41impl Base64Impl {
42    #[inline]
43    fn _eq(x: u8, y: u8) -> u8 {
44        !(((0u16.wrapping_sub((x as u16) ^ (y as u16))) >> 8) as u8)
45    }
46
47    #[inline]
48    fn _gt(x: u8, y: u8) -> u8 {
49        (((y as u16).wrapping_sub(x as u16)) >> 8) as u8
50    }
51
52    #[inline]
53    fn _ge(x: u8, y: u8) -> u8 {
54        !Self::_gt(y, x)
55    }
56
57    #[inline]
58    fn _lt(x: u8, y: u8) -> u8 {
59        Self::_gt(y, x)
60    }
61
62    #[inline]
63    fn _le(x: u8, y: u8) -> u8 {
64        Self::_ge(y, x)
65    }
66
67    #[inline]
68    fn b64_char_to_byte(c: u8) -> u8 {
69        let x = (Self::_ge(c, b'A') & Self::_le(c, b'Z') & (c.wrapping_sub(b'A')))
70            | (Self::_ge(c, b'a') & Self::_le(c, b'z') & (c.wrapping_sub(b'a'.wrapping_sub(26))))
71            | (Self::_ge(c, b'0') & Self::_le(c, b'9') & (c.wrapping_sub(b'0'.wrapping_sub(52))))
72            | (Self::_eq(c, b'+') & 62)
73            | (Self::_eq(c, b'/') & 63);
74        x | (Self::_eq(x, 0) & (Self::_eq(c, b'A') ^ 0xff))
75    }
76
77    fn skip_padding(b64: &[u8], mut padding_len: usize) -> Result<&[u8], Error> {
78        let b64_len = b64.len();
79        let mut b64_pos = 0usize;
80        while padding_len > 0 {
81            if b64_pos >= b64_len {
82                return Err(Error::InvalidInput);
83            }
84            let c = b64[b64_pos];
85            if c == b'=' {
86                padding_len -= 1
87            } else {
88                return Err(Error::InvalidInput);
89            }
90            b64_pos += 1
91        }
92        Ok(&b64[b64_pos..])
93    }
94
95    pub fn decode<'t>(bin: &'t mut [u8], b64: &[u8]) -> Result<&'t [u8], Error> {
96        let bin_maxlen = bin.len();
97        let mut acc = 0u16;
98        let mut acc_len = 0usize;
99        let mut bin_pos = 0usize;
100        let mut premature_end = None;
101        for (b64_pos, &c) in b64.iter().enumerate() {
102            let d = Self::b64_char_to_byte(c);
103            if d == 0xff {
104                premature_end = Some(b64_pos);
105                break;
106            }
107            acc = (acc << 6) + d as u16;
108            acc_len += 6;
109            if acc_len >= 8 {
110                acc_len -= 8;
111                if bin_pos >= bin_maxlen {
112                    return Err(Error::Overflow);
113                }
114                bin[bin_pos] = (acc >> acc_len) as u8;
115                bin_pos += 1;
116            }
117        }
118        if acc_len > 4 || (acc & ((1u16 << acc_len).wrapping_sub(1))) != 0 {
119            return Err(Error::InvalidInput);
120        }
121        let padding_len = acc_len / 2;
122        if let Some(premature_end) = premature_end {
123            let remaining = Self::skip_padding(&b64[premature_end..], padding_len)?;
124            if !remaining.is_empty() {
125                return Err(Error::InvalidInput);
126            }
127        } else if padding_len != 0 {
128            return Err(Error::InvalidInput);
129        }
130        Ok(&bin[..bin_pos])
131    }
132}
133
134pub struct Base64;
135
136impl Decoder for Base64 {
137    #[inline]
138    fn decode<IN: AsRef<[u8]>>(bin: &mut [u8], b64: IN) -> Result<&[u8], Error> {
139        Base64Impl::decode(bin, b64.as_ref())
140    }
141}
142
143#[test]
144fn test_base64_mising_padding() {
145    let missing_padding = "AA";
146    assert!(Base64::decode_to_vec(missing_padding).is_err());
147    let missing_padding = "AAA";
148    assert!(Base64::decode_to_vec(missing_padding).is_err());
149}
150
151#[test]
152fn test_base64_invalid_padding() {
153    let valid_padding = "AA==";
154    assert_eq!(Base64::decode_to_vec(valid_padding), Ok(vec![0u8; 1]));
155    let invalid_padding = "AA=";
156    assert_eq!(
157        Base64::decode_to_vec(invalid_padding),
158        Err(Error::InvalidInput)
159    );
160}