views:

73

answers:

1

This question follows a suggestion made by @sharptooth in this related question.

Can std::string be tweaked so that it becomes password-safe ?

If not, what would be the guidelines to write a password-handling class (thus a class that takes big care about what it writes to memory and clears it before destruction) ?

+6  A: 

Yes, first define a custom allocator:

template <class T> class SecureAllocator : public std::allocator<T>
{
public:
    template<class U> struct rebind { typedef SecureAllocator<U> other; };

    SecureAllocator() throw() {}
    SecureAllocator(const SecureAllocator&) throw() {}
    template <class U> SecureAllocator(const SecureAllocator<U>&) throw() {}

    void deallocate(pointer p, size_type n)
    {
        std::fill_n((volatile char*)p, n*sizeof(T), 0);
        std::allocator<T>::deallocate(p, n);
    }
};

This allocator zeros the memory before deallocating. Now you typedef:

typedef std::basic_string<char, std::char_traits<char>, SecureAllocator<char>> SecureString;

However there is a small problem, std::string may use small string optimization and store some data inside itself, without dynamic allocation. So you must explicitly clear it on destruction or allocate on the heap with our custom allocator:

int main(int, char**)
{
    using boost::shared_ptr;
    using boost::allocate_shared;
    shared_ptr<SecureString> str = allocate_shared<SecureString>(SecureAllocator<SecureString>(), "aaa");

}

This guarantees that all the data is zeroed before deallocation, including the size of the string, for example.

ybungalobill
Don't use `std::fill_n`, use something like `SecureZeroMemory()` (http://msdn.microsoft.com/en-us/library/aa366877(VS.85).aspx) - with `std::fill_n` the compiler might optimize the erasing away.
sharptooth
Also is there a tweak to force `std::string` erase the space when the calling code causes the string to be shortened - for example removes first N elements and so the tail is moved "leftwards" and leaves characters "on the right"?
sharptooth
@sharptooth SecureZeroMemory is not standard, and volatile prevents optimization. Yes, if there is no system call some data may remain in memory before the CPU flushes the cache.
ybungalobill
@ybungalobill: Not sure, but looks like a neat way of using `volatile` for good.
sharptooth
@sharptooth in C++0x there is shrink_to_fit, however it's not binding request. To force shrinking you can do a copy and swap. (Well, this also won't work with reference counted string... so I guess that you can't do it with string. You **can** do it with vector, since it destructs the elements, so you need to zero the memory in SecureAllocator::destroy too).
ybungalobill
@ybungalobill: Thanks for this complete answer. Would you mind adding a non-boost version of the last code sample ? Using boost where I work is *sadly* not an option... I can use `tr1::shared_ptr` thought.
ereOn
@ereOn you can use tr1::shared_ptr with custom deleter. std::allocate_shared is going to be included in C++0x.
ybungalobill
@ybungalobill: congratz on your first K :-)
Matthieu M.