Sourcemeta Core 0.0.0
Loading...
Searching...
No Matches
crypto_secure.h
1#ifndef SOURCEMETA_CORE_CRYPTO_SECURE_H_
2#define SOURCEMETA_CORE_CRYPTO_SECURE_H_
3
4#ifndef SOURCEMETA_CORE_CRYPTO_EXPORT
5#include <sourcemeta/core/crypto_export.h>
6#endif
7
8#include <cstddef> // std::size_t
9#include <functional> // std::less
10#include <limits> // std::numeric_limits
11#include <new> // operator new, operator delete, std::align_val_t
12#include <string> // std::string
13#include <string_view> // std::string_view
14#include <vector> // std::vector
15
16namespace sourcemeta::core {
17
32inline auto secure_zero(void *const data, const std::size_t size) noexcept
33 -> void {
34 if (data == nullptr) {
35 return;
36 }
37
38 auto *pointer{static_cast<volatile unsigned char *>(data)};
39 for (std::size_t index{0}; index < size; index += 1) {
40 pointer[index] = 0;
41 }
42}
43
57inline auto secure_zero(std::string &value) noexcept -> void {
58 secure_zero(value.data(), value.size());
59}
60
84 explicit SecureStringScope(std::string &value) noexcept : target{value} {}
85 SecureStringScope(const SecureStringScope &) = delete;
86 auto operator=(const SecureStringScope &) -> SecureStringScope & = delete;
88 auto operator=(SecureStringScope &&) -> SecureStringScope & = delete;
89 ~SecureStringScope() { secure_zero(this->target); }
91 std::string &target;
92};
93
108template <typename T> struct SecureAllocator {
110 using value_type = T;
111
113 SecureAllocator() noexcept = default;
114
116 template <typename Other>
117 constexpr SecureAllocator(const SecureAllocator<Other> &) noexcept {}
118
120 [[nodiscard]] auto allocate(const std::size_t count) -> T * {
121 if (count > std::numeric_limits<std::size_t>::max() / sizeof(T)) {
122 throw std::bad_array_new_length{};
123 }
124
125 return static_cast<T *>(
126 ::operator new(count * sizeof(T), std::align_val_t{alignof(T)}));
127 }
128
130 auto deallocate(T *const pointer, const std::size_t count) noexcept -> void {
131 secure_zero(pointer, count * sizeof(T));
132 ::operator delete(pointer, std::align_val_t{alignof(T)});
133 }
134
136 template <typename Other>
137 auto operator==(const SecureAllocator<Other> &) const noexcept -> bool {
138 return true;
139 }
140
142 template <typename Other>
143 auto operator!=(const SecureAllocator<Other> &) const noexcept -> bool {
144 return false;
145 }
146};
147
163public:
165 using size_type = std::vector<char, SecureAllocator<char>>::size_type;
166
168 SecureString() = default;
169
171 SecureString(const std::string_view value)
172 : buffer_(value.begin(), value.end()) {}
173
175 SecureString(const char *const data, const size_type length)
176 : buffer_(data, data + length) {}
177
179 SecureString(const size_type count, const char value)
180 : buffer_(count, value) {}
181
183 [[nodiscard]] auto size() const noexcept -> size_type {
184 return this->buffer_.size();
185 }
186
188 [[nodiscard]] auto empty() const noexcept -> bool {
189 return this->buffer_.empty();
190 }
191
193 auto reserve(const size_type capacity) -> void {
194 this->buffer_.reserve(capacity);
195 }
196
198 [[nodiscard]] auto capacity() const noexcept -> size_type {
199 return this->buffer_.capacity();
200 }
201
204 auto resize(const size_type count, const char value) -> void {
205 this->buffer_.resize(count, value);
206 }
207
209 auto push_back(const char value) -> void { this->buffer_.push_back(value); }
210
212 auto append(const std::string_view value) -> void {
213 // Inserting a range whose iterators point into this container is undefined,
214 // so a view that aliases the storage is taken through an independent buffer
215 // that wipes itself, while an independent view is inserted directly. The
216 // ordering uses the total order over pointers, which is defined even for
217 // pointers into different objects
218 const char *const first{this->buffer_.data()};
219 const std::less<const char *> before{};
220 if (!this->buffer_.empty() && !before(value.data(), first) &&
221 before(value.data(), first + this->buffer_.size())) {
222 const std::vector<char, SecureAllocator<char>> copy(value.begin(),
223 value.end());
224 this->buffer_.insert(this->buffer_.end(), copy.begin(), copy.end());
225 } else {
226 this->buffer_.insert(this->buffer_.end(), value.begin(), value.end());
227 }
228 }
229
231 auto append(const size_type count, const char value) -> void {
232 this->buffer_.insert(this->buffer_.end(), count, value);
233 }
234
236 [[nodiscard]] auto operator[](const size_type index) noexcept -> char & {
237 return this->buffer_[index];
238 }
239
241 [[nodiscard]] auto operator[](const size_type index) const noexcept -> char {
242 return this->buffer_[index];
243 }
244
246 [[nodiscard]] auto front() const noexcept -> char {
247 return this->buffer_.front();
248 }
249
251 [[nodiscard]] auto back() const noexcept -> char {
252 return this->buffer_.back();
253 }
254
256 [[nodiscard]] operator std::string_view() const noexcept {
257 // An empty buffer may expose a null data pointer, so a default view is
258 // returned rather than constructing one from a possibly-null pointer
259 if (this->buffer_.empty()) {
260 return {};
261 }
262
263 return {this->buffer_.data(), this->buffer_.size()};
264 }
265
267 [[nodiscard]] auto data() const noexcept -> const char * {
268 return this->buffer_.data();
269 }
270
272 [[nodiscard]] auto operator==(const std::string_view other) const noexcept
273 -> bool {
274 return std::string_view{*this} == other;
275 }
276
277private:
278 std::vector<char, SecureAllocator<char>> buffer_;
279};
280
281} // namespace sourcemeta::core
282
283#endif
SecureString(const char *const data, const size_type length)
Construct from a pointer and a length.
Definition crypto_secure.h:175
auto deallocate(T *const pointer, const std::size_t count) noexcept -> void
Wipe and release the storage of the given number of objects.
Definition crypto_secure.h:130
auto capacity() const noexcept -> size_type
The number of bytes that can be held before growing the storage.
Definition crypto_secure.h:198
auto size() const noexcept -> size_type
The number of bytes held.
Definition crypto_secure.h:183
auto push_back(const char value) -> void
Append a single byte.
Definition crypto_secure.h:209
auto empty() const noexcept -> bool
Whether no bytes are held.
Definition crypto_secure.h:188
auto reserve(const size_type capacity) -> void
Reserve storage for at least the given number of bytes.
Definition crypto_secure.h:193
SecureString(const size_type count, const char value)
Construct a run of a repeated byte.
Definition crypto_secure.h:179
auto allocate(const std::size_t count) -> T *
Allocate storage for the given number of objects.
Definition crypto_secure.h:120
auto resize(const size_type count, const char value) -> void
Definition crypto_secure.h:204
auto data() const noexcept -> const char *
A pointer to the held bytes, valid until the next mutation.
Definition crypto_secure.h:267
auto back() const noexcept -> char
The last byte.
Definition crypto_secure.h:251
auto append(const std::string_view value) -> void
Append a view of bytes.
Definition crypto_secure.h:212
SecureAllocator() noexcept=default
Construct an allocator, which holds no state of its own.
auto append(const size_type count, const char value) -> void
Append a run of a repeated byte.
Definition crypto_secure.h:231
std::string & target
The captured string, whose storage is wiped at scope exit.
Definition crypto_secure.h:91
SecureString()=default
Construct an empty string.
auto front() const noexcept -> char
The first byte.
Definition crypto_secure.h:246
SecureString(const std::string_view value)
Construct from a view of bytes.
Definition crypto_secure.h:171
SecureStringScope(std::string &value) noexcept
Capture the string to wipe when leaving the current scope.
Definition crypto_secure.h:84
auto secure_zero(void *const data, const std::size_t size) noexcept -> void
Definition crypto_secure.h:32
Definition crypto_secure.h:108