Skip to main content

cryptovec/
lib.rs

1// Copyright 2016 Pierre-Étienne Meunier
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15extern crate libc;
16extern crate winapi;
17use libc::c_void;
18#[cfg(not(windows))]
19use libc::size_t;
20use std::ops::{Deref, DerefMut, Index, IndexMut, Range, RangeFrom, RangeFull, RangeTo};
21
22/// A buffer which zeroes its memory on `.clear()`, `.resize()` and
23/// reallocations, to avoid copying secrets around.
24#[derive(Debug)]
25pub struct CryptoVec {
26    p: *mut u8,
27    size: usize,
28    capacity: usize,
29}
30
31impl Unpin for CryptoVec {}
32
33unsafe impl Send for CryptoVec {}
34unsafe impl Sync for CryptoVec {}
35
36impl AsRef<[u8]> for CryptoVec {
37    fn as_ref(&self) -> &[u8] {
38        self.deref()
39    }
40}
41impl AsMut<[u8]> for CryptoVec {
42    fn as_mut(&mut self) -> &mut [u8] {
43        self.deref_mut()
44    }
45}
46impl Deref for CryptoVec {
47    type Target = [u8];
48    fn deref(&self) -> &[u8] {
49        unsafe { std::slice::from_raw_parts(self.p, self.size) }
50    }
51}
52impl DerefMut for CryptoVec {
53    fn deref_mut(&mut self) -> &mut [u8] {
54        unsafe { std::slice::from_raw_parts_mut(self.p, self.size) }
55    }
56}
57
58impl From<String> for CryptoVec {
59    fn from(e: String) -> Self {
60        CryptoVec::from(e.into_bytes())
61    }
62}
63
64impl From<Vec<u8>> for CryptoVec {
65    fn from(e: Vec<u8>) -> Self {
66        let mut c = CryptoVec::new_zeroed(e.len());
67        c.clone_from_slice(&e[..]);
68        c
69    }
70}
71
72impl Index<RangeFrom<usize>> for CryptoVec {
73    type Output = [u8];
74    fn index(&self, index: RangeFrom<usize>) -> &[u8] {
75        self.deref().index(index)
76    }
77}
78impl Index<RangeTo<usize>> for CryptoVec {
79    type Output = [u8];
80    fn index(&self, index: RangeTo<usize>) -> &[u8] {
81        self.deref().index(index)
82    }
83}
84impl Index<Range<usize>> for CryptoVec {
85    type Output = [u8];
86    fn index(&self, index: Range<usize>) -> &[u8] {
87        self.deref().index(index)
88    }
89}
90impl Index<RangeFull> for CryptoVec {
91    type Output = [u8];
92    fn index(&self, _: RangeFull) -> &[u8] {
93        self.deref()
94    }
95}
96impl IndexMut<RangeFull> for CryptoVec {
97    fn index_mut(&mut self, _: RangeFull) -> &mut [u8] {
98        self.deref_mut()
99    }
100}
101
102impl IndexMut<RangeFrom<usize>> for CryptoVec {
103    fn index_mut(&mut self, index: RangeFrom<usize>) -> &mut [u8] {
104        self.deref_mut().index_mut(index)
105    }
106}
107impl IndexMut<RangeTo<usize>> for CryptoVec {
108    fn index_mut(&mut self, index: RangeTo<usize>) -> &mut [u8] {
109        self.deref_mut().index_mut(index)
110    }
111}
112impl IndexMut<Range<usize>> for CryptoVec {
113    fn index_mut(&mut self, index: Range<usize>) -> &mut [u8] {
114        self.deref_mut().index_mut(index)
115    }
116}
117
118impl Index<usize> for CryptoVec {
119    type Output = u8;
120    fn index(&self, index: usize) -> &u8 {
121        self.deref().index(index)
122    }
123}
124
125impl std::io::Write for CryptoVec {
126    fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
127        self.extend(buf);
128        Ok(buf.len())
129    }
130    fn flush(&mut self) -> Result<(), std::io::Error> {
131        Ok(())
132    }
133}
134
135impl Default for CryptoVec {
136    fn default() -> Self {
137        CryptoVec {
138            p: std::ptr::NonNull::dangling().as_ptr(),
139            size: 0,
140            capacity: 0,
141        }
142    }
143}
144
145#[cfg(not(windows))]
146unsafe fn mlock(ptr: *const u8, len: usize) {
147    unsafe { libc::mlock(ptr as *const c_void, len as size_t) };
148}
149#[cfg(not(windows))]
150unsafe fn munlock(ptr: *const u8, len: usize) {
151    unsafe { libc::munlock(ptr as *const c_void, len as size_t) };
152}
153
154#[cfg(windows)]
155use winapi::shared::basetsd::SIZE_T;
156#[cfg(windows)]
157use winapi::shared::minwindef::LPVOID;
158#[cfg(windows)]
159use winapi::um::memoryapi::{VirtualLock, VirtualUnlock};
160#[cfg(windows)]
161unsafe fn mlock(ptr: *const u8, len: usize) {
162    VirtualLock(ptr as LPVOID, len as SIZE_T);
163}
164#[cfg(windows)]
165unsafe fn munlock(ptr: *const u8, len: usize) {
166    VirtualUnlock(ptr as LPVOID, len as SIZE_T);
167}
168
169impl Clone for CryptoVec {
170    fn clone(&self) -> Self {
171        let mut v = Self::new();
172        v.extend(self);
173        v
174    }
175}
176
177impl CryptoVec {
178    /// Creates a new `CryptoVec`.
179    pub fn new() -> CryptoVec {
180        CryptoVec::default()
181    }
182
183    /// Creates a new `CryptoVec` with `n` zeros.
184    pub fn new_zeroed(size: usize) -> CryptoVec {
185        unsafe {
186            let capacity = size.next_power_of_two();
187            let layout = std::alloc::Layout::from_size_align_unchecked(capacity, 1);
188            let p = std::alloc::alloc_zeroed(layout);
189            mlock(p, capacity);
190            CryptoVec { p, capacity, size }
191        }
192    }
193
194    /// Creates a new `CryptoVec` with capacity `capacity`.
195    pub fn with_capacity(capacity: usize) -> CryptoVec {
196        unsafe {
197            let capacity = capacity.next_power_of_two();
198            let layout = std::alloc::Layout::from_size_align_unchecked(capacity, 1);
199            let p = std::alloc::alloc_zeroed(layout);
200            mlock(p, capacity);
201            CryptoVec {
202                p,
203                capacity,
204                size: 0,
205            }
206        }
207    }
208
209    /// Length of this `CryptoVec`.
210    ///
211    /// ```
212    /// assert_eq!(cryptovec::CryptoVec::new().len(), 0)
213    /// ```
214    pub fn len(&self) -> usize {
215        self.size
216    }
217
218    /// Returns `true` if and only if this CryptoVec is empty.
219    ///
220    /// ```
221    /// assert!(cryptovec::CryptoVec::new().is_empty())
222    /// ```
223    pub fn is_empty(&self) -> bool {
224        self.len() == 0
225    }
226
227    /// Resize this CryptoVec, appending zeros at the end. This may
228    /// perform at most one reallocation, overwriting the previous
229    /// version with zeros.
230    pub fn resize(&mut self, size: usize) {
231        if size <= self.capacity && size > self.size {
232            // If this is an expansion, just resize.
233            self.size = size
234        } else if size <= self.size {
235            // If this is a truncation, resize and erase the extra memory.
236            unsafe {
237                libc::memset(
238                    self.p.offset(size as isize) as *mut c_void,
239                    0,
240                    self.size - size,
241                );
242            }
243            self.size = size;
244        } else {
245            // realloc ! and erase the previous memory.
246            unsafe {
247                let next_capacity = size.next_power_of_two();
248                let old_ptr = self.p;
249                let next_layout = std::alloc::Layout::from_size_align_unchecked(next_capacity, 1);
250                self.p = std::alloc::alloc_zeroed(next_layout);
251                mlock(self.p, next_capacity);
252
253                if self.capacity > 0 {
254                    std::ptr::copy_nonoverlapping(old_ptr, self.p, self.size);
255                    for i in 0..self.size {
256                        std::ptr::write_volatile(old_ptr.offset(i as isize), 0)
257                    }
258                    munlock(old_ptr, self.capacity);
259                    let layout = std::alloc::Layout::from_size_align_unchecked(self.capacity, 1);
260                    std::alloc::dealloc(old_ptr, layout);
261                }
262
263                if self.p.is_null() {
264                    panic!("Realloc failed, pointer = {:?} {:?}", self, size)
265                } else {
266                    self.capacity = next_capacity;
267                    self.size = size;
268                }
269            }
270        }
271    }
272
273    /// Clear this CryptoVec (retaining the memory).
274    ///
275    /// ```
276    /// let mut v = cryptovec::CryptoVec::new();
277    /// v.extend(b"blabla");
278    /// v.clear();
279    /// assert!(v.is_empty())
280    /// ```
281    pub fn clear(&mut self) {
282        self.resize(0);
283    }
284
285    /// Append a new byte at the end of this CryptoVec.
286    pub fn push(&mut self, s: u8) {
287        let size = self.size;
288        self.resize(size + 1);
289        unsafe { *(self.p.offset(size as isize)) = s }
290    }
291
292    /// Append a new u32, big endian-encoded, at the end of this CryptoVec.
293    ///
294    /// ```
295    /// let mut v = cryptovec::CryptoVec::new();
296    /// let n = 43554;
297    /// v.push_u32_be(n);
298    /// assert_eq!(n, v.read_u32_be(0))
299    /// ```
300    pub fn push_u32_be(&mut self, s: u32) {
301        let x: [u8; 4] = s.to_be_bytes();
302        self.extend(&x)
303    }
304
305    /// Read a big endian-encoded u32 from this CryptoVec, with the
306    /// first byte at position `i`.
307    ///
308    /// ```
309    /// let mut v = cryptovec::CryptoVec::new();
310    /// let n = 99485710;
311    /// v.push_u32_be(n);
312    /// assert_eq!(n, v.read_u32_be(0))
313    /// ```
314    pub fn read_u32_be(&self, i: usize) -> u32 {
315        assert!(i + 4 <= self.size);
316        let mut x: u32 = 0;
317        unsafe {
318            libc::memcpy(
319                (&mut x) as *mut u32 as *mut c_void,
320                self.p.offset(i as isize) as *const c_void,
321                4,
322            );
323        }
324        u32::from_be(x)
325    }
326
327    /// Read `n_bytes` from `r`, and append them at the end of this
328    /// `CryptoVec`. Returns the number of bytes read (and appended).
329    pub fn read<R: std::io::Read>(
330        &mut self,
331        n_bytes: usize,
332        mut r: R,
333    ) -> Result<usize, std::io::Error> {
334        let cur_size = self.size;
335        self.resize(cur_size + n_bytes);
336        let s =
337            unsafe { std::slice::from_raw_parts_mut(self.p.offset(cur_size as isize), n_bytes) };
338        // Resize the buffer to its appropriate size.
339        match r.read(s) {
340            Ok(n) => {
341                self.resize(cur_size + n);
342                Ok(n)
343            }
344            Err(e) => {
345                self.resize(cur_size);
346                Err(e)
347            }
348        }
349    }
350
351    /// Write all this CryptoVec to the provided `Write`. Returns the
352    /// number of bytes actually written.
353    ///
354    /// ```
355    /// let mut v = cryptovec::CryptoVec::new();
356    /// v.extend(b"blabla");
357    /// let mut s = std::io::stdout();
358    /// v.write_all_from(0, &mut s).unwrap();
359    /// ```
360    pub fn write_all_from<W: std::io::Write>(
361        &self,
362        offset: usize,
363        mut w: W,
364    ) -> Result<usize, std::io::Error> {
365        assert!(offset < self.size);
366        // if we're past this point, self.p cannot be null.
367        unsafe {
368            let s = std::slice::from_raw_parts(self.p.offset(offset as isize), self.size - offset);
369            w.write(s)
370        }
371    }
372
373    /// Resize this CryptoVec, returning a mutable borrow to the extra bytes.
374    ///
375    /// ```
376    /// let mut v = cryptovec::CryptoVec::new();
377    /// v.resize_mut(4).clone_from_slice(b"test");
378    /// ```
379    pub fn resize_mut(&mut self, n: usize) -> &mut [u8] {
380        let size = self.size;
381        self.resize(size + n);
382        unsafe { std::slice::from_raw_parts_mut(self.p.offset(size as isize), n) }
383    }
384
385    /// Append a slice at the end of this CryptoVec.
386    ///
387    /// ```
388    /// let mut v = cryptovec::CryptoVec::new();
389    /// v.extend(b"test");
390    /// ```
391    pub fn extend(&mut self, s: &[u8]) {
392        let size = self.size;
393        self.resize(size + s.len());
394        unsafe {
395            std::ptr::copy_nonoverlapping(s.as_ptr(), self.p.offset(size as isize), s.len());
396        }
397    }
398
399    /// Create a `CryptoVec` from a slice
400    ///
401    /// ```
402    /// cryptovec::CryptoVec::from_slice(b"test");
403    /// ```
404    pub fn from_slice(s: &[u8]) -> CryptoVec {
405        let mut v = CryptoVec::new();
406        v.resize(s.len());
407        unsafe {
408            std::ptr::copy_nonoverlapping(s.as_ptr(), v.p, s.len());
409        }
410        v
411    }
412}
413
414impl Drop for CryptoVec {
415    fn drop(&mut self) {
416        if self.capacity > 0 {
417            unsafe {
418                for i in 0..self.size {
419                    std::ptr::write_volatile(self.p.offset(i as isize), 0)
420                }
421                munlock(self.p, self.capacity);
422                let layout = std::alloc::Layout::from_size_align_unchecked(self.capacity, 1);
423                std::alloc::dealloc(self.p, layout);
424            }
425        }
426    }
427}