il2_utils/mem/
mod.rs

1/*
2 * BSD 3-Clause License
3 *
4 * Copyright (c) 2019-2020, InterlockLedger Network
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * * Redistributions of source code must retain the above copyright notice, this
11 *   list of conditions and the following disclaimer.
12 *
13 * * Redistributions in binary form must reproduce the above copyright notice,
14 *   this list of conditions and the following disclaimer in the documentation
15 *   and/or other materials provided with the distribution.
16 *
17 * * Neither the name of the copyright holder nor the names of its
18 *   contributors may be used to endorse or promote products derived from
19 *   this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32//! This module implement functions that can be used to control the page locking
33//! in memory. This is useful to prevent critical values from being written into
34//! the the disk by the virtual memory system.
35#[cfg(not(any(target_os = "windows", target_os = "linux")))]
36pub mod impl_default;
37#[cfg(target_os = "linux")]
38pub mod impl_linux;
39#[cfg(target_os = "windows")]
40pub mod impl_win32;
41#[cfg(test)]
42mod tests;
43
44use core::ffi::c_void;
45#[cfg(not(any(target_os = "windows", target_os = "linux")))]
46use impl_default::*;
47#[cfg(target_os = "linux")]
48use impl_linux::*;
49#[cfg(target_os = "windows")]
50use impl_win32::*;
51use rand::random;
52use std::cmp::min;
53use std::mem::size_of;
54use std::ops::{Deref, DerefMut};
55use std::sync::Arc;
56use zeroize::Zeroize;
57
58/// Try to lock the memory segment into memory, preventing it from
59/// being moved to the disk. All calls to this function must be
60/// followed by a call to [`unlock_mem()`].
61///
62/// Use this method with extreme care because it interferes with tne
63/// OS ability to manage virtual memory.
64///
65/// Arguments:
66/// - `ptr`: The pointer to the memory segment;
67/// - `size`: The size of the ptr in units;
68///
69/// Retunrs true on success or false otherwise.
70pub fn lock_mem<T: Sized>(ptr: *const T, size: usize) -> bool {
71    if size > 0 {
72        lock_mem_core(ptr as *const c_void, size * size_of::<T>())
73    } else {
74        false
75    }
76}
77
78/// Unlocks the memory segment. It reverts the effects of [`lock_mem()`].
79///
80/// Arguments:
81/// - `ptr`: The pointer to the memory segment;
82/// - `size`: The size of the ptr in units;
83///
84/// Retunrs true on success or false otherwise.
85pub fn unlock_mem<T: Sized>(ptr: *const T, size: usize) -> bool {
86    if size > 0 {
87        unlock_mem_core(ptr as *const c_void, size * size_of::<T>())
88    } else {
89        false
90    }
91}
92
93/// Determines if this platform supports memory locking or not.
94///
95/// Returns true if it is supported or false otherwise.
96pub fn lock_supported() -> bool {
97    lock_supported_core()
98}
99
100//=============================================================================
101// SecretBytes
102//-----------------------------------------------------------------------------
103/// This struct wraps a byte array that is guaranteed to have its contents
104/// shredded upon destruction.
105///
106/// It also allows the locking of the value in memory if required, preventing it
107/// from being moved into the disk.
108///
109/// This struct also implements a mechanism to set a logical length that differs
110pub struct SecretBytes {
111    value: Vec<u8>,
112    locked: bool,
113    len: usize,
114}
115
116impl SecretBytes {
117    /// Creates a new `SecretBytes`.
118    ///
119    /// Arguments:
120    /// - `size`: The size in bytes;
121    /// - `locked`: Locks the value in memory;
122    pub fn new(size: usize, locked: bool) -> Self {
123        let mut ret = Self {
124            value: Vec::<u8>::with_capacity(size),
125            locked: false,
126            len: size,
127        };
128        ret.value.resize(size, 0);
129        if locked {
130            ret.lock();
131        }
132        ret
133    }
134
135    /// Creates a new `SecretBytes` and initializes it
136    /// with the given value.
137    ///
138    /// Arguments:
139    /// - `value`: The initial value;
140    /// - `locked`: Locks the value in memory;
141    pub fn with_value(value: &[u8], locked: bool) -> Self {
142        let mut ret = Self::new(value.len(), locked);
143        ret.value.copy_from_slice(value);
144        ret
145    }
146
147    /// Returns the value as a mutable byte slice.
148    pub fn mut_value(&mut self) -> &mut [u8] {
149        &mut self.value.as_mut_slice()[..self.len]
150    }
151
152    /// Returns the value as an immutable byte slice.
153    pub fn value(&self) -> &[u8] {
154        &self.value.as_slice()[..self.len]
155    }
156
157    /// Returns the buffer as a mutable byte slice. The buffer may be larger
158    /// than the value itself.
159    pub fn mut_buffer(&mut self) -> &mut [u8] {
160        self.value.as_mut_slice()
161    }
162
163    /// Returns the buffer as an immutable byte slice. The buffer may be larger
164    /// than the value itself.
165    pub fn buffer(&self) -> &[u8] {
166        self.value.as_slice()
167    }
168
169    /// Returns true if the value is locked in memory or false
170    /// otherwise.
171    pub fn locked(&self) -> bool {
172        self.locked
173    }
174
175    /// Returns the logical size of this value. It may be equal
176    /// or smaller than the actual buffer size.
177    pub fn len(&self) -> usize {
178        self.len
179    }
180
181    /// Sets the logical size of this value. If the new size is larger
182    /// than the buffer size, this method will set the logical size to the
183    /// current buffer size.
184    ///
185    /// Arguments:
186    ///
187    /// - `size`: The logical size of the value.
188    pub fn set_len(&mut self, size: usize) {
189        self.len = min(size, self.buffer_len());
190    }
191
192    /// Returns true if this value has length 0.
193    pub fn is_empty(&self) -> bool {
194        self.len == 0
195    }
196
197    /// Returns the size of the inner buffer of this value.
198    pub fn buffer_len(&self) -> usize {
199        self.value.len()
200    }
201
202    /// Locks the value in memory, preventing it from being moved
203    /// into the disk by the the virtual memory system.
204    ///
205    /// If this feature is not supported, this function does nothing.
206    fn lock(&mut self) {
207        if !self.is_empty() && !self.locked {
208            self.locked = lock_mem(self.value.as_ptr(), self.value.len());
209        }
210    }
211
212    /// Unlocks the value in memory.
213    ///
214    /// This function does nothing if the memory
215    fn unlock(&mut self) {
216        if self.locked {
217            self.locked = !unlock_mem(self.value.as_ptr(), self.value.len());
218        }
219    }
220
221    /// Verifies if the underlying platform supports memory locking.
222    ///
223    /// Returns true if locking is supported or false otherwise.
224    pub fn lock_supported() -> bool {
225        lock_supported()
226    }
227}
228
229impl Clone for SecretBytes {
230    fn clone(&self) -> Self {
231        let mut ret = Self::with_value(self.value.as_slice(), self.locked);
232        ret.set_len(self.len());
233        ret
234    }
235}
236
237impl Drop for SecretBytes {
238    fn drop(&mut self) {
239        self.value.as_mut_slice().zeroize();
240        self.unlock();
241    }
242}
243
244impl Deref for SecretBytes {
245    type Target = [u8];
246
247    fn deref(&self) -> &Self::Target {
248        self.value()
249    }
250}
251
252impl DerefMut for SecretBytes {
253    fn deref_mut(&mut self) -> &mut Self::Target {
254        self.mut_value()
255    }
256}
257
258//=============================================================================
259// ByteMaskGenerator
260//-----------------------------------------------------------------------------
261struct ByteMaskGenerator {
262    state: u64,
263}
264
265impl ByteMaskGenerator {
266    pub fn new(seed: u64) -> Self {
267        Self { state: seed }
268    }
269
270    pub fn next(&mut self) -> u8 {
271        // This code is partially based on the random implementation by Newlib
272        self.state = self.state.wrapping_mul(6364136223846793005) + 1;
273        ((self.state >> 32) & 0xFF) as u8
274    }
275}
276
277//=============================================================================
278// ProtectedValue
279//-----------------------------------------------------------------------------
280/// This trait implements a way to protect secret values stored in memory
281/// against potential memory scan techniques. The value is stored in a
282/// obfuscated and/or encrypted form that is reversed only when the actual value
283/// is needed by the application.
284///
285/// Although not enough to provide a long term protection, it should be enough
286/// to make memory scan techniques way more difficult to perform.
287pub trait ProtectedValue: Send + Sync {
288    /// Returns the protected value as a [`SecretBytes`] instance.
289    fn get_secret(&self) -> SecretBytes;
290}
291
292//=============================================================================
293// DefaultProtectedValue
294//-----------------------------------------------------------------------------
295/// This struct implements the the default implementation of the
296/// [`ProtectedValue`] trait. It uses a random mask to protect the value stored
297/// in memory from simple memory scan attacks.
298///
299/// It is not the most sophisticated approach to this problem but is guaranteed
300/// to work on all platforms.
301pub struct DefaultProtectedValue {
302    secret: SecretBytes,
303    seed: u64,
304}
305
306impl DefaultProtectedValue {
307    /// Creates a new DefaultProtectedValue with the given value.
308    ///
309    /// Arguments:
310    /// - `value`: The value to be protected;
311    pub fn new(value: &[u8]) -> Self {
312        let mut secret = SecretBytes::with_value(value, true);
313        let mut seed: u64 = 0;
314        while seed == 0 {
315            seed = random();
316        }
317        Self::apply_mask(seed, &mut secret);
318        Self { secret, seed }
319    }
320
321    fn apply_mask(seed: u64, value: &mut [u8]) {
322        let mut g = ByteMaskGenerator::new(seed);
323        for v in value {
324            *v ^= g.next();
325        }
326    }
327}
328
329impl ProtectedValue for DefaultProtectedValue {
330    fn get_secret(&self) -> SecretBytes {
331        let mut ret = self.secret.clone();
332        Self::apply_mask(self.seed, &mut ret);
333        ret
334    }
335}
336
337/// Creates a protected value repository. It always uses the best
338/// protection method available to the underlying platform.
339///
340/// It always returns a [`std::sync::Arc`] of the value because the
341/// protection mechanism may be too expensive to create and/or maintain.
342/// Furthermore, it is better to keep this kind of secret as isolated as
343/// possible inside the memory.
344///
345/// Returns the protected value.
346#[cfg(not(target_os = "windows"))]
347pub fn create_protected_value(value: &[u8]) -> Arc<dyn ProtectedValue> {
348    Arc::new(DefaultProtectedValue::new(value))
349}
350
351/// Creates a protected value repository. It always uses the best
352/// protection method available to the underlying platform.
353///
354/// On Windows platforms, it uses an opaque implementation that relies on
355/// `CryptProtectMemory()` and `CryptUnprotectMemory()` to protect the value
356/// in memory.
357///
358/// It always returns a [`std::sync::Arc`] of the value because the
359/// protection mechanism may be too expensive to create and/or maintain.
360/// Furthermore, it is better to keep this kind of secret as isolated as
361/// possible inside the memory.
362///
363/// Returns the protected value.
364#[cfg(target_os = "windows")]
365pub fn create_protected_value(value: &[u8]) -> Arc<dyn ProtectedValue> {
366    Arc::new(impl_win32::Win32ProtectedValue::new(value))
367}