lnk_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    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    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!(lnk_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!(lnk_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 = lnk_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 = lnk_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 s = s.to_be();
302        let x: [u8; 4] = unsafe { std::mem::transmute(s) };
303        self.extend(&x)
304    }
305
306    /// Read a big endian-encoded u32 from this CryptoVec, with the
307    /// first byte at position `i`.
308    ///
309    /// ```
310    /// let mut v = lnk_cryptovec::CryptoVec::new();
311    /// let n = 99485710;
312    /// v.push_u32_be(n);
313    /// assert_eq!(n, v.read_u32_be(0))
314    /// ```
315    pub fn read_u32_be(&self, i: usize) -> u32 {
316        assert!(i + 4 <= self.size);
317        let mut x: u32 = 0;
318        unsafe {
319            libc::memcpy(
320                (&mut x) as *mut u32 as *mut c_void,
321                self.p.offset(i as isize) as *const c_void,
322                4,
323            );
324        }
325        u32::from_be(x)
326    }
327
328    /// Read `n_bytes` from `r`, and append them at the end of this
329    /// `CryptoVec`. Returns the number of bytes read (and appended).
330    pub fn read<R: std::io::Read>(
331        &mut self,
332        n_bytes: usize,
333        mut r: R,
334    ) -> Result<usize, std::io::Error> {
335        let cur_size = self.size;
336        self.resize(cur_size + n_bytes);
337        let s =
338            unsafe { std::slice::from_raw_parts_mut(self.p.offset(cur_size as isize), n_bytes) };
339        // Resize the buffer to its appropriate size.
340        match r.read(s) {
341            Ok(n) => {
342                self.resize(cur_size + n);
343                Ok(n)
344            }
345            Err(e) => {
346                self.resize(cur_size);
347                Err(e)
348            }
349        }
350    }
351
352    /// Write all this CryptoVec to the provided `Write`. Returns the
353    /// number of bytes actually written.
354    ///
355    /// ```
356    /// let mut v = lnk_cryptovec::CryptoVec::new();
357    /// v.extend(b"blabla");
358    /// let mut s = std::io::stdout();
359    /// v.write_all_from(0, &mut s).unwrap();
360    /// ```
361    pub fn write_all_from<W: std::io::Write>(
362        &self,
363        offset: usize,
364        mut w: W,
365    ) -> Result<usize, std::io::Error> {
366        assert!(offset < self.size);
367        // if we're past this point, self.p cannot be null.
368        unsafe {
369            let s = std::slice::from_raw_parts(self.p.offset(offset as isize), self.size - offset);
370            w.write(s)
371        }
372    }
373
374    /// Resize this CryptoVec, returning a mutable borrow to the extra bytes.
375    ///
376    /// ```
377    /// let mut v = lnk_cryptovec::CryptoVec::new();
378    /// v.resize_mut(4).clone_from_slice(b"test");
379    /// ```
380    pub fn resize_mut(&mut self, n: usize) -> &mut [u8] {
381        let size = self.size;
382        self.resize(size + n);
383        unsafe { std::slice::from_raw_parts_mut(self.p.offset(size as isize), n) }
384    }
385
386    /// Append a slice at the end of this CryptoVec.
387    ///
388    /// ```
389    /// let mut v = lnk_cryptovec::CryptoVec::new();
390    /// v.extend(b"test");
391    /// ```
392    pub fn extend(&mut self, s: &[u8]) {
393        let size = self.size;
394        self.resize(size + s.len());
395        unsafe {
396            std::ptr::copy_nonoverlapping(s.as_ptr(), self.p.offset(size as isize), s.len());
397        }
398    }
399
400    /// Create a `CryptoVec` from a slice
401    ///
402    /// ```
403    /// use lnk_cryptovec::CryptoVec;
404    ///
405    /// CryptoVec::from_slice(b"test");
406    /// ```
407    pub fn from_slice(s: &[u8]) -> CryptoVec {
408        let mut v = CryptoVec::new();
409        v.resize(s.len());
410        unsafe {
411            std::ptr::copy_nonoverlapping(s.as_ptr(), v.p, s.len());
412        }
413        v
414    }
415}
416
417impl Drop for CryptoVec {
418    fn drop(&mut self) {
419        if self.capacity > 0 {
420            unsafe {
421                for i in 0..self.size {
422                    std::ptr::write_volatile(self.p.offset(i as isize), 0)
423                }
424                munlock(self.p, self.capacity);
425                let layout = std::alloc::Layout::from_size_align_unchecked(self.capacity, 1);
426                std::alloc::dealloc(self.p, layout);
427            }
428        }
429    }
430}