11#ifndef HMAC_CPP_SECURE_BUFFER_HPP
22#define HMAC_CPP_SECURE_BUFFER_HPP
33
4+ #include < atomic>
45#include < cstddef>
56#include < cstdint>
7+ #include < cstring>
68#include < vector>
79#include < type_traits>
810#include < string>
911#include " hmac_cpp/memlock.hpp"
1012
13+ #if defined(HAVE_EXPLICIT_BZERO)
14+ #include < strings.h>
15+ #endif
16+
1117// Macro to mark deprecated APIs in a compiler-portable way
1218#ifndef HMACCPP_DEPRECATED
1319#if defined(__clang__) || defined(__GNUC__)
@@ -25,10 +31,17 @@ namespace hmac_cpp {
2531// / \param ptr Pointer to the memory to wipe.
2632// / \param len Number of bytes to set to zero.
2733inline void secure_zero (void * ptr, size_t len) {
34+ #if defined(__STDC_LIB_EXT1__)
35+ (void )memset_s (ptr, len, 0 , len);
36+ #elif defined(HAVE_EXPLICIT_BZERO)
37+ explicit_bzero (ptr, len);
38+ #else
2839 volatile unsigned char * p = static_cast <volatile unsigned char *>(ptr);
2940 while (len--) {
3041 *p++ = 0 ;
3142 }
43+ std::atomic_signal_fence (std::memory_order_seq_cst);
44+ #endif
3245}
3346
3447// / \brief Vector-like buffer that zeroizes its contents on destruction.
@@ -114,11 +127,64 @@ struct secure_buffer {
114127 }
115128
116129 // / \brief Zeroize contents on destruction.
117- ~secure_buffer () {
130+ ~secure_buffer () noexcept { clear (); }
131+
132+ // / \brief Check whether pages are locked.
133+ bool is_locked () const noexcept { return locked_; }
134+
135+ // / \brief Clear and deallocate the buffer.
136+ void clear () noexcept {
137+ secure_zero (buf.data (), buf.size () * sizeof (T));
138+ if (locked_) {
139+ unlock_pages (buf.data (), buf.size () * sizeof (T));
140+ locked_ = false ;
141+ }
142+ buf.clear ();
143+ buf.shrink_to_fit ();
144+ }
145+
146+ // / \brief Resize the buffer, zeroizing truncated data.
147+ void resize (size_t n) {
148+ T* old_ptr = buf.data ();
149+ size_t old_sz = buf.size ();
150+ if (n < old_sz) {
151+ secure_zero (old_ptr + n, (old_sz - n) * sizeof (T));
152+ }
153+ buf.resize (n);
154+ if (LockOnAlloc && old_ptr != buf.data ()) {
155+ if (locked_) {
156+ unlock_pages (old_ptr, old_sz * sizeof (T));
157+ }
158+ if (!buf.empty ()) {
159+ locked_ = lock_pages (buf.data (), buf.size () * sizeof (T));
160+ } else {
161+ locked_ = false ;
162+ }
163+ }
164+ }
165+
166+ // / \brief Assign from raw pointer.
167+ void assign (const T* p, size_t n) {
118168 secure_zero (buf.data (), buf.size () * sizeof (T));
119169 if (locked_) {
120170 unlock_pages (buf.data (), buf.size () * sizeof (T));
121171 }
172+ buf.assign (p, p + n);
173+ if (LockOnAlloc && !buf.empty ()) {
174+ locked_ = lock_pages (buf.data (), buf.size () * sizeof (T));
175+ } else {
176+ locked_ = false ;
177+ }
178+ }
179+
180+ // / \brief Assign from std::string rvalue and zeroize the source.
181+ template <class U = T, typename std::enable_if<std::is_same<U, uint8_t >::value, int >::type = 0 >
182+ void assign (std::string&& s) {
183+ assign (reinterpret_cast <const uint8_t *>(s.data ()), s.size ());
184+ if (!s.empty ()) {
185+ secure_zero (&s[0 ], s.size ());
186+ s.clear ();
187+ }
122188 }
123189
124190 T* data () { return buf.data (); }
0 commit comments