solidity_bindgen/
secrets.rs

1use secp256k1::key::{SecretKey, ONE_KEY};
2
3use sodiumoxide::utils::{mlock, munlock};
4use std::convert::TryFrom;
5use std::fmt;
6use std::ops::Deref;
7use std::pin::Pin;
8use std::slice;
9use zeroize::{DefaultIsZeroes, Zeroize};
10
11/// Securely stores a secret key in memory that is zeroized on drop. Care is
12/// taken so that when this struct is constructed or moved that additional
13/// copies of the secret are not made in memory or disk.
14/// https://github.com/veorq/cryptocoding#clean-memory-of-secret-data
15///
16/// Unfortunately the SafeSecretKey is not magic, there are some things to be
17/// aware of when using it...
18///   * The memory used when constructing the secret key must also be zeroized,
19///     but this is left as an exercise to the caller.
20///   * If you mem::forget the SafeSecretKey or otherwise don't drop it, the
21///     secret will not be zeroized.
22///   * When the caller lends out a reference to the SecretKey (available for
23///     example via Deref) it is the responsibility of the caller to not Clone
24///     the SecretKey or otherwise make a copy of it's memory
25pub struct SafeSecretKey {
26    safe: Pin<Box<ZeroizedSecretKey>>,
27}
28
29impl fmt::Debug for SafeSecretKey {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        f.debug_struct("SecretKey").finish()
32    }
33}
34
35#[derive(Copy, Clone)]
36struct ZeroizedSecretKey(SecretKey);
37
38impl DefaultIsZeroes for ZeroizedSecretKey {}
39impl Default for ZeroizedSecretKey {
40    fn default() -> Self {
41        Self(ONE_KEY)
42    }
43}
44
45impl ZeroizedSecretKey {
46    fn as_mut_bytes(&mut self) -> &mut [u8] {
47        unsafe {
48            let ptr = self.0.as_mut_ptr();
49            slice::from_raw_parts_mut(ptr, self.0.len())
50        }
51    }
52}
53
54impl SafeSecretKey {
55    fn new(secret: &SecretKey) -> Result<Self, ()> {
56        // Allocate to a fixed location in memory
57        let mut safe = Pin::new(Box::<ZeroizedSecretKey>::default());
58
59        // Tell the OS that it's not ok to page this memory to disk
60        let mem = safe.as_mut_bytes();
61        mlock(mem)?;
62
63        // Copy to that location without moving secret onto the stack
64        safe.0 = *secret;
65
66        // Now the only copy of the memory managed by Self is committed to be zeroized later.
67        // This operation (and subsequent) don't make copies because it's the Pin that moves,
68        // not the underlying allocation.
69        Ok(Self { safe })
70    }
71}
72
73impl<'a> TryFrom<&'a SecretKey> for SafeSecretKey {
74    type Error = ();
75    fn try_from(secret: &'a SecretKey) -> Result<Self, ()> {
76        Self::new(secret)
77    }
78}
79
80impl Drop for SafeSecretKey {
81    fn drop(&mut self) {
82        self.safe.zeroize();
83        let mem = self.safe.as_mut_bytes();
84        // If the OS cannot unlock this memory, it's not a problem. There is no
85        // reasonable way to propagate an error so just ignore it.
86        let _ignore = munlock(mem);
87    }
88}
89
90impl Deref for SafeSecretKey {
91    type Target = SecretKey;
92    fn deref(&self) -> &Self::Target {
93        &self.safe.0
94    }
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100    use secp256k1::key::ONE_KEY;
101
102    /// It's a bit hard to verify the inner workings like zeroing without relying on undefined behavior.
103    /// But, we can check that this at least runs
104    #[test]
105    pub fn no_panic() {
106        let key = ONE_KEY;
107        let safe = SafeSecretKey::new(&key).unwrap();
108        drop(safe);
109    }
110}