Inspired by reading "How-to write a password-safe class?", I tried some clever (or dumb-fool) hack to create a widely-useable secure string using the std::basic_string
-template, which does not need to be explicitly securely erased itself.
At least gcc and clang seem not to choke on it (coliru):
#include <string>
namespace my_secure {
void SecureZeroMemory(void* p, std::size_t n) {
for(volatile char* x = static_cast<char*>(p); n; --n)
*x++ = 0;
}
// Minimal allocator zeroing on deallocation
template <typename T> struct secure_allocator {
using value_type = T;
secure_allocator() = default;
template <class U> secure_allocator(const secure_allocator<U>&) {}
T* allocate(std::size_t n) { return new T[n]; }
void deallocate(T* p, std::size_t n) {
SecureZeroMemory(p, n * sizeof *p);
delete [] p;
}
};
template <typename T, typename U>
inline bool operator== (const secure_allocator<T>&, const secure_allocator<U>&) {
return true;
}
template <typename T, typename U>
inline bool operator!= (const secure_allocator<T>&, const secure_allocator<U>&) {
return false;
}
using secure_string = std::basic_string<char, std::char_traits<char>,
secure_allocator<char>>;
}
namespace std {
// Zero the strings own memory on destruction
template<> my_secure::secure_string::~basic_string() {
using X =std::basic_string<char, std::char_traits<char>,
my_secure::secure_allocator<float>>;
((X*)this)->~X();
my_secure::SecureZeroMemory(this, sizeof *this);
}
}
And a short program using it to do nothing much:
//#include "my_secure.h"
using my_secure::secure_string;
#include <iostream>
int main() {
secure_string s = "Hello World!";
std::cout << s << '\n';
}
Some specific concerns:
How badly did I break the standard?
- Does the fact that one of the template-arguments is my own type heal the fact that I added my own explicit specialization to
::std
? - Are the two types actually guaranteed to be similar enough that my bait-and-switch in the destructor is ok?
- Does the fact that one of the template-arguments is my own type heal the fact that I added my own explicit specialization to
Is there any actual implementation where the liberties I took with the standard will come back to haunt me?
Did I miss any place where I should zero memory after use? Or is there any chance that anything will slip by?