Scrub keys from memory just before scope end.

Partially implements #74.

Securely erases keys from memory after they are no longer needed. Might have a
performance impact, which I haven't measured (perf measurements aren't
generally reliable on laptops).

Thanks to @stoffu for the suggestion to specialize the pod_to_hex/hex_to_pod
functions. Using overloads + SFINAE instead generalizes it so other types can
be marked as scrubbed without adding more boilerplate.
This commit is contained in:
moneromooo-monero 2017-10-26 10:21:06 +01:00 committed by Jonathan Roelofs
parent 38ecd0526e
commit 7193b89fe5
6 changed files with 60 additions and 23 deletions

View File

@ -108,7 +108,7 @@ namespace epee
template<typename T> template<typename T>
constexpr bool has_padding() noexcept constexpr bool has_padding() noexcept
{ {
return !std::is_pod<T>() || alignof(T) != 1; return !std::is_pod<T>::value || alignof(T) != 1;
} }
//! \return Cast data from `src` as `span<const std::uint8_t>`. //! \return Cast data from `src` as `span<const std::uint8_t>`.

View File

@ -31,6 +31,8 @@
#pragma once #pragma once
#ifdef __cplusplus #ifdef __cplusplus
#include <array>
extern "C" { extern "C" {
#endif #endif
@ -39,3 +41,44 @@ void *memwipe(void *src, size_t n);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
#ifdef __cplusplus
namespace tools {
/// Scrubs data in the contained type upon destruction.
///
/// Primarily useful for making sure that private keys don't stick around in
/// memory after the objects that held them have gone out of scope.
template <class T>
struct scrubbed : public T {
using type = T;
~scrubbed() {
scrub();
}
/// Destroy the contents of the contained type.
void scrub() {
static_assert(std::is_pod<T>::value,
"T cannot be auto-scrubbed. T must be POD.");
static_assert(std::is_trivially_destructible<T>::value,
"T cannot be auto-scrubbed. T must be trivially destructable.");
memwipe(this, sizeof(T));
}
};
template <class T, size_t N>
using scrubbed_arr = scrubbed<std::array<T, N>>;
} // namespace tools
// Partial specialization for std::is_pod<tools::scrubbed<T>> so that it can
// pretend to be the containted type in those contexts.
namespace std
{
template<class t_scrubbee>
struct is_pod<tools::scrubbed<t_scrubbee>> {
static const bool value = is_pod<t_scrubbee>::value;
};
}
#endif // __cplusplus

View File

@ -49,16 +49,9 @@ namespace crypto {
#if defined(__cplusplus) #if defined(__cplusplus)
} }
using chacha8_key = tools::scrubbed_arr<uint8_t, CHACHA8_KEY_SIZE>;
#pragma pack(push, 1) #pragma pack(push, 1)
struct chacha8_key {
uint8_t data[CHACHA8_KEY_SIZE];
~chacha8_key()
{
memwipe(data, sizeof(data));
}
};
// MS VC 2012 doesn't interpret `class chacha8_iv` as POD in spite of [9.0.10], so it is a struct // MS VC 2012 doesn't interpret `class chacha8_iv` as POD in spite of [9.0.10], so it is a struct
struct chacha8_iv { struct chacha8_iv {
uint8_t data[CHACHA8_IV_SIZE]; uint8_t data[CHACHA8_IV_SIZE];
@ -68,15 +61,14 @@ namespace crypto {
static_assert(sizeof(chacha8_key) == CHACHA8_KEY_SIZE && sizeof(chacha8_iv) == CHACHA8_IV_SIZE, "Invalid structure size"); static_assert(sizeof(chacha8_key) == CHACHA8_KEY_SIZE && sizeof(chacha8_iv) == CHACHA8_IV_SIZE, "Invalid structure size");
inline void chacha8(const void* data, std::size_t length, const chacha8_key& key, const chacha8_iv& iv, char* cipher) { inline void chacha8(const void* data, std::size_t length, const chacha8_key& key, const chacha8_iv& iv, char* cipher) {
chacha8(data, length, reinterpret_cast<const uint8_t*>(&key), reinterpret_cast<const uint8_t*>(&iv), cipher); chacha8(data, length, key.data(), reinterpret_cast<const uint8_t*>(&iv), cipher);
} }
inline void generate_chacha8_key(const void *data, size_t size, chacha8_key& key) { inline void generate_chacha8_key(const void *data, size_t size, chacha8_key& key) {
static_assert(sizeof(chacha8_key) <= sizeof(hash), "Size of hash must be at least that of chacha8_key"); static_assert(sizeof(chacha8_key) <= sizeof(hash), "Size of hash must be at least that of chacha8_key");
char pwd_hash[HASH_SIZE]; tools::scrubbed_arr<char, HASH_SIZE> pwd_hash;
crypto::cn_slow_hash(data, size, pwd_hash); crypto::cn_slow_hash(data, size, pwd_hash.data());
memcpy(&key, pwd_hash, sizeof(key)); memcpy(&key, pwd_hash.data(), sizeof(key));
memwipe(pwd_hash, sizeof(pwd_hash));
} }
inline void generate_chacha8_key(std::string password, chacha8_key& key) { inline void generate_chacha8_key(std::string password, chacha8_key& key) {

View File

@ -36,9 +36,12 @@
#include <boost/thread/lock_guard.hpp> #include <boost/thread/lock_guard.hpp>
#include <boost/utility/value_init.hpp> #include <boost/utility/value_init.hpp>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <type_traits>
#include <vector> #include <vector>
#include "common/pod-class.h" #include "common/pod-class.h"
#include "common/util.h"
#include "common/memwipe.h"
#include "generic-ops.h" #include "generic-ops.h"
#include "hex.h" #include "hex.h"
#include "span.h" #include "span.h"
@ -65,9 +68,7 @@ namespace crypto {
friend class crypto_ops; friend class crypto_ops;
}; };
POD_CLASS secret_key: ec_scalar { using secret_key = tools::scrubbed<ec_scalar>;
friend class crypto_ops;
};
POD_CLASS public_keyV { POD_CLASS public_keyV {
std::vector<public_key> keys; std::vector<public_key> keys;

View File

@ -57,6 +57,7 @@ using namespace epee;
#include "rapidjson/writer.h" #include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h" #include "rapidjson/stringbuffer.h"
#include "common/json_util.h" #include "common/json_util.h"
#include "common/memwipe.h"
#include "common/base58.h" #include "common/base58.h"
#include "ringct/rctSigs.h" #include "ringct/rctSigs.h"
@ -2761,12 +2762,11 @@ bool wallet2::generate_chacha8_key_from_secret_keys(crypto::chacha8_key &key) co
const account_keys &keys = m_account.get_keys(); const account_keys &keys = m_account.get_keys();
const crypto::secret_key &view_key = keys.m_view_secret_key; const crypto::secret_key &view_key = keys.m_view_secret_key;
const crypto::secret_key &spend_key = keys.m_spend_secret_key; const crypto::secret_key &spend_key = keys.m_spend_secret_key;
char data[sizeof(view_key) + sizeof(spend_key) + 1]; tools::scrubbed_arr<char, sizeof(view_key) + sizeof(spend_key) + 1> data;
memcpy(data, &view_key, sizeof(view_key)); memcpy(data.data(), &view_key, sizeof(view_key));
memcpy(data + sizeof(view_key), &spend_key, sizeof(spend_key)); memcpy(data.data() + sizeof(view_key), &spend_key, sizeof(spend_key));
data[sizeof(data) - 1] = CHACHA8_KEY_TAIL; data[sizeof(data) - 1] = CHACHA8_KEY_TAIL;
crypto::generate_chacha8_key(data, sizeof(data), key); crypto::generate_chacha8_key(data.data(), sizeof(data), key);
memset(data, 0, sizeof(data));
return true; return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------

View File

@ -42,6 +42,7 @@ add_executable(cncrypto-tests
${crypto_headers}) ${crypto_headers})
target_link_libraries(cncrypto-tests target_link_libraries(cncrypto-tests
PRIVATE PRIVATE
common
${Boost_SYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY}
${EXTRA_LIBRARIES}) ${EXTRA_LIBRARIES})
set_property(TARGET cncrypto-tests set_property(TARGET cncrypto-tests