Skip to main content
Code Review

Return to Question

copy-edited
Source Link
Deduplicator
  • 19.6k
  • 1
  • 32
  • 65

Inspired by reading "How-to write a password-safe class? 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 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:

  1. 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?
  2. Is there any actual implementation where the liberties I took with the standard will come back to haunt me?

  3. Did I miss any place where I should zero memory after use? Or is there any chance that anything will slip by?

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:

  1. 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?
  2. Is there any actual implementation where the liberties I took with the standard will come back to haunt me?

  3. Did I miss any place where I should zero memory after use? Or is there any chance that anything will slip by?

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:

  1. 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?
  2. Is there any actual implementation where the liberties I took with the standard will come back to haunt me?

  3. Did I miss any place where I should zero memory after use? Or is there any chance that anything will slip by?

Tweeted twitter.com/StackCodeReview/status/1353447515715235842
replaced http://stackoverflow.com/ with https://stackoverflow.com/
Source Link

Inspired by reading "How-to write a password-safe class? 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:

  1. 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?
  2. Is there any actual implementation where the liberties I took with the standard will come back to haunt me?

  3. Did I miss any place where I should zero memory after use? Or is there any chance that anything will slip by?

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:

  1. 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?
  2. Is there any actual implementation where the liberties I took with the standard will come back to haunt me?

  3. Did I miss any place where I should zero memory after use? Or is there any chance that anything will slip by?

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:

  1. 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?
  2. Is there any actual implementation where the liberties I took with the standard will come back to haunt me?

  3. Did I miss any place where I should zero memory after use? Or is there any chance that anything will slip by?

Source Link
Deduplicator
  • 19.6k
  • 1
  • 32
  • 65

Hacking a SecureString based on std::basic_string for C++

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:

  1. 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?
  2. Is there any actual implementation where the liberties I took with the standard will come back to haunt me?

  3. Did I miss any place where I should zero memory after use? Or is there any chance that anything will slip by?

lang-cpp

AltStyle によって変換されたページ (->オリジナル) /