subtle_encoding/
encoding.rs

1//! The `Encoding` trait: common operations across all encoders
2
3#[cfg(feature = "alloc")]
4use alloc::{string::String, vec::Vec};
5#[cfg(feature = "std")]
6use std::{
7    fs::File,
8    io::{Read, Write},
9    path::Path,
10};
11#[cfg(all(unix, feature = "std"))]
12use std::{fs::OpenOptions, os::unix::fs::OpenOptionsExt};
13#[cfg(feature = "std")]
14use zeroize::Zeroize;
15
16use super::Error;
17
18/// Mode to use for newly created files
19// TODO: make this configurable?
20#[cfg(unix)]
21pub const FILE_MODE: u32 = 0o600;
22
23/// All encoding types in this crate implement this trait
24pub trait Encoding: Send + Sync {
25    /// Encode the given slice into the destination buffer.
26    ///
27    /// Returns the size of the encoded output, or `Error` if the destination
28    /// buffer was too small to hold the encoded output.
29    fn encode_to_slice(&self, src: &[u8], dst: &mut [u8]) -> Result<usize, Error>;
30
31    /// Calculate the length of the given data after encoding.
32    fn encoded_len(&self, bytes: &[u8]) -> usize;
33
34    /// Encode the given buffer, returning a `Vec<u8>`
35    #[cfg(feature = "alloc")]
36    fn encode<B: AsRef<[u8]>>(&self, bytes: B) -> Vec<u8> {
37        let expected_length = self.encoded_len(bytes.as_ref());
38        let mut encoded = vec![0u8; expected_length];
39
40        let actual_length = self.encode_to_slice(bytes.as_ref(), &mut encoded).unwrap();
41        debug_assert_eq!(expected_length, actual_length);
42
43        encoded
44    }
45
46    /// Encode the given slice to a `String` with this `Encoding`.
47    ///
48    /// Returns an `Error` in the event this encoding does not produce a
49    /// well-formed UTF-8 string.
50    #[cfg(feature = "alloc")]
51    fn encode_to_string<B: AsRef<[u8]>>(&self, bytes: B) -> Result<String, Error> {
52        Ok(String::from_utf8(self.encode(bytes))?)
53    }
54
55    /// Encode the given slice with this `Encoding`, writing the result to the
56    /// supplied `io::Write` type, returning the number of bytes written or a `Error`.
57    #[cfg(feature = "std")]
58    fn encode_to_writer<B, W>(&self, bytes: B, writer: &mut W) -> Result<usize, Error>
59    where
60        B: AsRef<[u8]>,
61        W: Write,
62    {
63        let mut encoded_bytes = self.encode(bytes);
64        writer.write_all(encoded_bytes.as_ref())?;
65        encoded_bytes.zeroize();
66        Ok(encoded_bytes.len())
67    }
68
69    /// Encode `self` and write it to a file at the given path, returning the
70    /// resulting `File` or a `Error`.
71    ///
72    /// If the file does not exist, it will be created with a mode of
73    /// `FILE_MODE` (i.e. `600`). If the file does exist, it will be erased
74    /// and replaced.
75    #[cfg(all(unix, feature = "std"))]
76    fn encode_to_file<B, P>(&self, bytes: B, path: P) -> Result<File, Error>
77    where
78        B: AsRef<[u8]>,
79        P: AsRef<Path>,
80    {
81        let mut file = OpenOptions::new()
82            .create(true)
83            .write(true)
84            .truncate(true)
85            .mode(FILE_MODE)
86            .open(path)?;
87
88        self.encode_to_writer(bytes, &mut file)?;
89        Ok(file)
90    }
91
92    /// Encode `self` and write it to a file at the given path, returning the
93    /// resulting `File` or a `Error`.
94    ///
95    /// If the file does not exist, it will be created.
96    #[cfg(all(not(unix), feature = "std"))]
97    fn encode_to_file<B, P>(&self, bytes: B, path: P) -> Result<File, Error>
98    where
99        B: AsRef<[u8]>,
100        P: AsRef<Path>,
101    {
102        let mut file = File::create(path.as_ref())?;
103        self.encode_to_writer(bytes, &mut file)?;
104        Ok(file)
105    }
106
107    /// Decode hexadecimal (upper or lower case) with branchless / secret-independent logic
108    fn decode_to_slice(&self, src: &[u8], dst: &mut [u8]) -> Result<usize, Error>;
109
110    /// Calculate the length of the given data after decoding.
111    fn decoded_len(&self, encoded_bytes: &[u8]) -> Result<usize, Error>;
112
113    /// Decode the given buffer, returning a `Vec<u8>`
114    #[cfg(feature = "alloc")]
115    fn decode<B: AsRef<[u8]>>(&self, encoded_bytes: B) -> Result<Vec<u8>, Error> {
116        let expected_length = self.decoded_len(encoded_bytes.as_ref())?;
117        let mut decoded = vec![0u8; expected_length];
118
119        let actual_length = self.decode_to_slice(encoded_bytes.as_ref(), &mut decoded)?;
120        debug_assert_eq!(expected_length, actual_length);
121
122        Ok(decoded)
123    }
124
125    /// Decode the given string-alike type with this `Encoding`, returning the
126    /// decoded value or a `Error`.
127    #[cfg(feature = "std")]
128    fn decode_from_str<S: AsRef<str>>(&self, encoded: S) -> Result<Vec<u8>, Error> {
129        self.decode(encoded.as_ref().as_bytes())
130    }
131
132    /// Decode the data read from the given `io::Read` type with this
133    /// `Encoding`, returning the decoded value or a `Error`.
134    #[cfg(feature = "std")]
135    fn decode_from_reader<R: Read>(&self, reader: &mut R) -> Result<Vec<u8>, Error> {
136        let mut bytes = vec![];
137        reader.read_to_end(bytes.as_mut())?;
138        let result = self.decode(&bytes);
139        bytes.zeroize();
140        result
141    }
142
143    /// Read a file at the given path, decoding the data it contains using
144    /// the provided `Encoding`, returning the decoded value or a `Error`.
145    #[cfg(feature = "std")]
146    fn decode_from_file<P: AsRef<Path>>(&self, path: P) -> Result<Vec<u8>, Error> {
147        self.decode_from_reader(&mut File::open(path.as_ref())?)
148    }
149}
150
151// TODO(tarcieri): `no_std` tests
152#[cfg(feature = "alloc")]
153#[cfg(test)]
154mod tests {
155    use super::*;
156
157    /// Test input for encoding/decoding cases
158    const TEST_DATA: &[u8] = b"Testing 1, 2, 3...";
159
160    /// Dummy encoding we use to test `Encoding` methods
161    struct TestEncoding {}
162
163    impl Default for TestEncoding {
164        fn default() -> Self {
165            Self {}
166        }
167    }
168
169    impl Encoding for TestEncoding {
170        fn encode_to_slice(&self, src: &[u8], dst: &mut [u8]) -> Result<usize, Error> {
171            let length = self.encoded_len(src);
172            assert_eq!(dst.len(), length);
173            Ok(length)
174        }
175
176        fn encoded_len(&self, bytes: &[u8]) -> usize {
177            bytes.len() * 4 / 3
178        }
179
180        fn decode_to_slice(&self, src: &[u8], dst: &mut [u8]) -> Result<usize, Error> {
181            let length = self.decoded_len(src)?;
182            assert_eq!(dst.len(), length);
183            Ok(length)
184        }
185
186        fn decoded_len(&self, bytes: &[u8]) -> Result<usize, Error> {
187            Ok(bytes.len() * 3 / 4)
188        }
189    }
190
191    /// Make sure `encode()` doesn't panic
192    #[test]
193    fn test_encode() {
194        TestEncoding::default().encode(TEST_DATA);
195    }
196
197    /// Make sure `decode()` doesn't panic
198    #[test]
199    fn test_decode() {
200        let encoding = TestEncoding::default();
201        let encoded = encoding.encode(TEST_DATA);
202        encoding.decode(&encoded).unwrap();
203    }
204}