libdecaf
secure_buffer.hxx
Go to the documentation of this file.
1 
11 #ifndef __DECAF_SECURE_BUFFER_HXX__
12 #define __DECAF_SECURE_BUFFER_HXX__ 1
13 
14 #include <string>
15 #include <sys/types.h>
16 #include <stdio.h>
17 #include <vector>
18 #include <stdexcept>
19 #include <cstddef>
20 #include <limits>
21 
22 #if defined(_MSC_VER) // MSVC does not have built in posix_memalign
23 #define posix_memalign(p, a, s) (((*(p)) = _aligned_malloc((s), (a))), *(p) ?0 :errno)
24 #endif
25 
27 #if __cplusplus >= 201103L
28 #define DECAF_NOEXCEPT noexcept
29 #define DECAF_DELETE = delete
30 #else
31 #define DECAF_NOEXCEPT throw()
32 #define DECAF_DELETE
33 #endif
36 namespace decaf {
37 
41 static inline void really_bzero(void *data, size_t size) { decaf_bzero(data,size); }
42 
44 template<typename T, size_t alignment = 0> class SanitizingAllocator {
46 /* Based on http://www.codeproject.com/Articles/4795/C-Standard-Allocator-An-Introduction-and-Implement */
47 public:
48  typedef T value_type;
49  typedef T* pointer;
50  typedef const T* const_pointer;
51  typedef T& reference;
52  typedef const T& const_reference;
53  typedef size_t size_type;
54  typedef std::ptrdiff_t difference_type;
55 
56  template<typename U> struct rebind { typedef SanitizingAllocator<U> other; };
57  inline SanitizingAllocator() DECAF_NOEXCEPT {}
58  inline ~SanitizingAllocator() DECAF_NOEXCEPT {}
59  inline SanitizingAllocator(const SanitizingAllocator &) DECAF_NOEXCEPT {}
60  template<typename U, size_t a> inline SanitizingAllocator(const SanitizingAllocator<U, a> &) DECAF_NOEXCEPT {}
61 
62  inline T* address(T& r) const DECAF_NOEXCEPT { return &r; }
63  inline const T* address(const T& r) const DECAF_NOEXCEPT { return &r; }
64  inline T* allocate (
65  size_type cnt,
66  typename std::allocator<void>::const_pointer = 0
67  ) /*throw(std::bad_alloc)*/;
68  inline void deallocate(T* p, size_t size) DECAF_NOEXCEPT;
69  inline size_t max_size() const DECAF_NOEXCEPT { return std::numeric_limits<size_t>::max() / sizeof(T); }
70  inline void construct(T* p, const T& t) { new(p) T(t); }
71  inline void destroy(T* p) { p->~T(); }
72 
73  inline bool operator==(SanitizingAllocator const&) const DECAF_NOEXCEPT { return true; }
74  inline bool operator!=(SanitizingAllocator const&) const DECAF_NOEXCEPT { return false; }
76 };
77 
79 typedef std::vector<unsigned char, SanitizingAllocator<unsigned char, 0> > SecureBuffer;
80 
82 template<class T,class U, class V, class W>
83 inline bool memeq(const std::vector<T,U> &a, const std::vector<V,W> &b) {
84  if (a.size() != b.size()) return false;
85  return decaf_memeq(a.data(),b.data(),a.size());
86 }
87 
89 template<class Base> class Serializable {
90 public:
92  inline size_t ser_size() const DECAF_NOEXCEPT { return static_cast<const Base*>(this)->ser_size(); }
93 
95  inline void serialize_into(unsigned char *buf) const DECAF_NOEXCEPT {
96  static_cast<const Base*>(this)->serialize_into(buf);
97  }
98 
100  inline SecureBuffer serialize() const /*throw(std::bad_alloc)*/ {
101  SecureBuffer out(ser_size());
102  serialize_into(out.data());
103  return out;
104  }
105 
107 #if __cplusplus >= 201103L
108  explicit inline operator SecureBuffer() const /*throw(std::bad_alloc)*/ {
109  return serialize();
110  }
111 #endif
112 };
113 
115 class Buffer;
119 class CryptoException : public std::exception {
120 public:
122  virtual const char * what() const DECAF_NOEXCEPT { return "CryptoException"; }
123 };
124 
126 class LengthException : public std::exception {
127 public:
129  virtual const char * what() const DECAF_NOEXCEPT { return "LengthException"; }
130 };
131 
133 struct NOINIT {};
134 
138 class Rng {
139 protected:
141  Rng() {}
142 
144  Rng(const Rng &) DECAF_DELETE;
145 
147  Rng &operator=(const Rng &) DECAF_DELETE;
148 
149 public:
151  virtual void read(Buffer buffer) DECAF_NOEXCEPT = 0;
152 
154  inline SecureBuffer read(size_t length) /*throw(std::bad_alloc)*/;
155 };
156 
157 
159 class Block {
160 protected:
162  unsigned char *data_;
163  size_t size_;
164  const bool zero_on_destroy_;
167 public:
169  inline Block() : data_(NULL), size_(0), zero_on_destroy_(false) {}
170 
172  inline Block(const char *data) DECAF_NOEXCEPT : data_((unsigned char *)data),
173  size_(strlen(data)), zero_on_destroy_(false) {}
174 
176  inline Block(const unsigned char *data, size_t size, bool zero_on_destroy=false) DECAF_NOEXCEPT : data_((unsigned char *)data),
177  size_(size), zero_on_destroy_(zero_on_destroy) {}
178 
180  inline Block(const std::string &s) : data_(
181 #if __cplusplus >= 201103L
182  ((unsigned char *)(s.data()))
183 #else
184  ((unsigned char *)&(s)[0])
185 #endif
186  ), size_(s.size()), zero_on_destroy_(false) {}
187 
189  template<class alloc> inline Block(const std::vector<unsigned char,alloc> &s)
190  : data_(
191 #if __cplusplus >= 201103L
192  ((unsigned char *)(s.data()))
193 #else
194  ((unsigned char *)&(s)[0])
195 #endif
196  ), size_(s.size()), zero_on_destroy_(false) {}
197 
199  inline const unsigned char *data() const DECAF_NOEXCEPT { return data_; }
200 
202  inline const unsigned char &operator[](size_t off) const /*throw(std::out_of_range)*/ {
203  if (off >= size()) throw(std::out_of_range("decaf::Block"));
204  return data_[off];
205  }
206 
208  inline size_t size() const DECAF_NOEXCEPT { return size_; }
209 
211  inline std::string get_string() const {
212  return std::string((const char *)data_,size_);
213  }
214 
216  inline Block slice(size_t off, size_t length) const /*throw(LengthException)*/ {
217  if (off > size() || length > size() - off) throw LengthException();
218  return Block(data()+off, length);
219  }
220 
222  inline decaf_bool_t contents_equal(const Block &b) const DECAF_NOEXCEPT {
223  if (b.size() != size()) return false;
224  return decaf_memeq(b.data(),data(),size());
225  }
226 
228  inline operator SecureBuffer() const /*throw(std::bad_alloc)*/ {
229  return SecureBuffer(data_,data_+size_);
230  }
231 
233  inline void zeroize() DECAF_NOEXCEPT { really_bzero(data_,size()); }
234 
236  inline void debug_print_hex(const char *name = NULL) {
237  if (name) printf("%s = ", name);
238  for (size_t s = 0; s < size(); s++) printf("%02x", data_[s]);
239  printf("\n");
240  }
241 
242 private:
244  inline decaf_bool_t operator>=(const Block &b) const DECAF_NOEXCEPT DECAF_DELETE;
245  inline decaf_bool_t operator<=(const Block &b) const DECAF_NOEXCEPT DECAF_DELETE;
246  inline decaf_bool_t operator> (const Block &b) const DECAF_NOEXCEPT DECAF_DELETE;
247  inline decaf_bool_t operator< (const Block &b) const DECAF_NOEXCEPT DECAF_DELETE;
248  inline void operator= (const Block &b) const DECAF_NOEXCEPT DECAF_DELETE;
250 };
251 
253 template<size_t Size> class FixedBlock : public Block {
254 public:
256  inline FixedBlock(const Block &b) /*throw(LengthException)*/ : Block(b.data(),Size) {
257  if (Size != b.size()) throw LengthException();
258  }
259 
261  template<class alloc> inline FixedBlock(const std::vector<unsigned char,alloc> &s) : Block(s) {
262  if (Size != s.size()) throw LengthException();
263  }
264 
266  inline explicit FixedBlock(const uint8_t data[Size]) DECAF_NOEXCEPT : Block(data,Size) {}
267 };
268 
270 class Buffer : public Block {
271 public:
273  inline Buffer() DECAF_NOEXCEPT : Block() {}
274 
276  inline Buffer(unsigned char *data, size_t size, bool zero_on_destroy=false) DECAF_NOEXCEPT : Block(data,size,zero_on_destroy) {}
277 
279  template<class alloc> inline Buffer(std::vector<unsigned char,alloc> &s) : Block(s) {}
280 
282  inline const unsigned char *data() const DECAF_NOEXCEPT { return data_; }
283 
285  inline unsigned char* data() DECAF_NOEXCEPT { return data_; }
286 
288  inline Buffer slice(size_t off, size_t length) /*throw(LengthException)*/;
289 
291  inline unsigned char &operator[](size_t off) /*throw(std::out_of_range)*/ {
292  if (off >= size()) throw(std::out_of_range("decaf::Buffer"));
293  return data_[off];
294  }
295 
297  inline void assign(const Block b) /*throw(LengthException)*/ {
298  if (b.size() != size()) throw LengthException();
299  memmove(data(),b.data(),size());
300  }
301 
302 private:
304  inline void operator= (const Block &b) const DECAF_NOEXCEPT DECAF_DELETE;
306 };
307 
308 
310 template<size_t Size> class FixedBuffer : public Buffer {
311 public:
313  inline FixedBuffer(Buffer b) /*throw(LengthException)*/ : Buffer(b) {
314  if (Size != b.size()) throw LengthException();
315  }
316 
318  inline FixedBuffer(SecureBuffer &b) /*throw(LengthException)*/ : Buffer(b) {
319  if (Size != b.size()) throw LengthException();
320  }
321 
323  inline explicit FixedBuffer(uint8_t dat[Size],bool zero_on_destroy = false) DECAF_NOEXCEPT : Buffer(dat,Size,zero_on_destroy) {}
324 
326  inline operator FixedBlock<Size>() const DECAF_NOEXCEPT {
327  return FixedBlock<Size>(data());
328  }
329 
330 private:
332  inline void operator= (const Block &b) const DECAF_NOEXCEPT DECAF_DELETE;
334 };
335 
337 template<size_t Size> class FixedArrayBuffer : public FixedBuffer<Size> {
338 private:
339  uint8_t storage[Size];
340 public:
341  using Buffer::zeroize;
342 
344  inline explicit FixedArrayBuffer() DECAF_NOEXCEPT : FixedBuffer<Size>(storage,true) { memset(storage,0,Size); }
345 
347  inline explicit FixedArrayBuffer(const NOINIT &) DECAF_NOEXCEPT : FixedBuffer<Size>(storage,true) { }
348 
350  inline explicit FixedArrayBuffer(Rng &r) DECAF_NOEXCEPT : FixedBuffer<Size>(storage,true) { r.read(*this); }
351 
353  inline explicit FixedArrayBuffer(const FixedBlock<Size> &b) DECAF_NOEXCEPT : FixedBuffer<Size>(storage,true) {
354  memcpy(storage,b.data(),Size);
355  }
356 
358  inline FixedArrayBuffer& operator=(const FixedBlock<Size> &b) DECAF_NOEXCEPT {
359  memcpy(storage,b.data(),Size); return *this;
360  }
361 
363  inline FixedArrayBuffer& operator=(const FixedArrayBuffer<Size> &b) DECAF_NOEXCEPT {
364  memcpy(storage,b.data(),Size); return *this;
365  }
366 
368  inline FixedArrayBuffer& operator=(const Block &b) /*throw(LengthException)*/ {
369  *this = FixedBlock<Size>(b);
370  }
371 
373  inline explicit FixedArrayBuffer(const Block &b) /*throw(LengthException)*/ : FixedBuffer<Size>(storage,true) {
374  if (b.size() != Size) throw LengthException();
375  memcpy(storage,b.data(),Size);
376  }
377 
379  inline explicit FixedArrayBuffer(const FixedArrayBuffer<Size> &b) DECAF_NOEXCEPT : FixedBuffer<Size>(storage,true) {
380  memcpy(storage,b.data(),Size);
381  }
382 
384  ~FixedArrayBuffer() DECAF_NOEXCEPT { zeroize(); }
385 };
386 
388 Buffer Buffer::slice(size_t off, size_t length) /*throw(LengthException)*/ {
389  if (off > size() || length > size() - off) throw LengthException();
390  return Buffer(data()+off, length);
391 }
392 
393 inline SecureBuffer Rng::read(size_t length) /*throw(std::bad_alloc)*/ {
394  SecureBuffer out(length); read(out); return out;
395 }
402 template <class T, class Wrapped>
403 class OwnedOrUnowned {
404 protected:
405  union {
406  Wrapped *mine;
407  const Wrapped *yours;
408  } ours;
409  bool is_mine;
410 
411  inline void clear() DECAF_NOEXCEPT {
412  if (is_mine) {
413  really_bzero(ours.mine, T::size());
414 #ifdef _MSC_VER
415  _aligned_free(ours.mine);
416 #else
417  free(ours.mine);
418 #endif // _MSC_VER
419  ours.yours = T::default_value();
420  is_mine = false;
421  }
422  }
423  inline void alloc() /*throw(std::bad_alloc)*/ {
424  if (is_mine) return;
425  int ret = posix_memalign((void**)&ours.mine, T::alignment(), T::size());
426  if (ret || !ours.mine) {
427  is_mine = false;
428  throw std::bad_alloc();
429  }
430  is_mine = true;
431  }
432  inline const Wrapped *get() const DECAF_NOEXCEPT { return is_mine ? ours.mine : ours.yours; }
433 
434  inline OwnedOrUnowned(
435  const Wrapped &yours = *T::default_value()
436  ) DECAF_NOEXCEPT {
437  ours.yours = &yours;
438  is_mine = false;
439  }
440 
444  inline T &operator=(const OwnedOrUnowned &it) /*throw(std::bad_alloc)*/ {
445  if (this == &it) return *(T*)this;
446  if (it.is_mine) {
447  alloc();
448  memcpy(ours.mine,it.ours.mine,T::size());
449  } else {
450  clear();
451  ours.yours = it.ours.yours;
452  }
453  is_mine = it.is_mine;
454  return *(T*)this;
455  }
456 
457 #if __cplusplus >= 201103L
458  inline T &operator=(OwnedOrUnowned &&it) DECAF_NOEXCEPT {
459  if (this == &it) return *(T*)this;
460  clear();
461  ours = it.ours;
462  is_mine = it.is_mine;
463  it.is_mine = false;
464  it.ours.yours = T::default_value;
465  return *this;
466  }
467 #endif
468 };
471 /*******************************************/
472 /* Inline implementations below this point */
473 /*******************************************/
474 
476 template<typename T, size_t alignment>
477 T* SanitizingAllocator<T,alignment>::allocate (
478  size_type cnt,
479  typename std::allocator<void>::const_pointer
480 ) /*throw(std::bad_alloc)*/ {
481  void *v;
482  int ret = 0;
483 
484  if (alignment) ret = posix_memalign(&v, alignment, cnt * sizeof(T));
485  else v = malloc(cnt * sizeof(T));
486 
487  if (ret || v==NULL) throw(std::bad_alloc());
488  return reinterpret_cast<T*>(v);
489 }
490 
491 template<typename T, size_t alignment>
492 void SanitizingAllocator<T,alignment>::deallocate(T* p, size_t size) DECAF_NOEXCEPT {
493  if (p==NULL) return;
494  really_bzero(reinterpret_cast<void*>(p), size);
495 #ifdef _MSC_VER
496  if (alignment)
497  _aligned_free(reinterpret_cast<void*>(p));
498  else
499 #endif // _MSC_VER
500  free(reinterpret_cast<void*>(p));
501 }
502 
505 } /* namespace decaf */
506 
507 
508 #undef DECAF_NOEXCEPT
509 #undef DECAF_DELETE
510 
511 #endif /* __DECAF_SECURE_BUFFER_HXX__ */
A reference to a block of data, which (when accessed through this base class) is const.
Definition: secure_buffer.hxx:159
Block(const std::vector< unsigned char, alloc > &s)
Block from std::vector.
Definition: secure_buffer.hxx:189
Block(const unsigned char *data, size_t size, bool zero_on_destroy=false) DECAF_NOEXCEPT
Unowned init.
Definition: secure_buffer.hxx:176
Block(const char *data) DECAF_NOEXCEPT
Init from C string.
Definition: secure_buffer.hxx:172
Block()
Null initialization.
Definition: secure_buffer.hxx:169
void zeroize() DECAF_NOEXCEPT
Securely set the buffer to 0.
Definition: secure_buffer.hxx:233
decaf_bool_t contents_equal(const Block &b) const DECAF_NOEXCEPT
Content-wise comparison; constant-time if they are the same length.
Definition: secure_buffer.hxx:222
size_t size() const DECAF_NOEXCEPT
Get the size.
Definition: secure_buffer.hxx:208
Block(const std::string &s)
Block from std::string.
Definition: secure_buffer.hxx:180
void debug_print_hex(const char *name=NULL)
Debugging print in hex.
Definition: secure_buffer.hxx:236
std::string get_string() const
Convert to C++ string.
Definition: secure_buffer.hxx:211
const unsigned char * data() const DECAF_NOEXCEPT
Get const data.
Definition: secure_buffer.hxx:199
const unsigned char & operator[](size_t off) const
Subscript.
Definition: secure_buffer.hxx:202
Block slice(size_t off, size_t length) const
Slice the buffer.
Definition: secure_buffer.hxx:216
A reference to a writable block of data.
Definition: secure_buffer.hxx:270
const unsigned char * data() const DECAF_NOEXCEPT
Get const data.
Definition: secure_buffer.hxx:282
unsigned char * data() DECAF_NOEXCEPT
Cast to unsigned char.
Definition: secure_buffer.hxx:285
unsigned char & operator[](size_t off)
Subscript.
Definition: secure_buffer.hxx:291
Buffer() DECAF_NOEXCEPT
Null init.
Definition: secure_buffer.hxx:273
Buffer(std::vector< unsigned char, alloc > &s)
Block from std::vector.
Definition: secure_buffer.hxx:279
Buffer slice(size_t off, size_t length)
Slice the buffer.
void assign(const Block b)
Copy from another block.
Definition: secure_buffer.hxx:297
Buffer(unsigned char *data, size_t size, bool zero_on_destroy=false) DECAF_NOEXCEPT
Unowned init.
Definition: secure_buffer.hxx:276
An exception for when crypto (ie point decode) has failed.
Definition: secure_buffer.hxx:119
virtual const char * what() const DECAF_NOEXCEPT
Definition: secure_buffer.hxx:122
A fixed-size stack-allocated buffer (for DECAF_NOEXCEPT semantics)
Definition: secure_buffer.hxx:337
FixedArrayBuffer(Rng &r) DECAF_NOEXCEPT
New random buffer.
Definition: secure_buffer.hxx:350
FixedArrayBuffer() DECAF_NOEXCEPT
New buffer initialized to zero.
Definition: secure_buffer.hxx:344
FixedArrayBuffer & operator=(const FixedArrayBuffer< Size > &b) DECAF_NOEXCEPT
Copy operator.
Definition: secure_buffer.hxx:363
FixedArrayBuffer & operator=(const FixedBlock< Size > &b) DECAF_NOEXCEPT
Copy operator.
Definition: secure_buffer.hxx:358
FixedArrayBuffer(const FixedBlock< Size > &b) DECAF_NOEXCEPT
Copy constructor.
Definition: secure_buffer.hxx:353
FixedArrayBuffer(const NOINIT &) DECAF_NOEXCEPT
New uninitialized buffer.
Definition: secure_buffer.hxx:347
FixedArrayBuffer(const Block &b)
Copy constructor.
Definition: secure_buffer.hxx:373
FixedArrayBuffer & operator=(const Block &b)
Copy operator.
Definition: secure_buffer.hxx:368
FixedArrayBuffer(const FixedArrayBuffer< Size > &b) DECAF_NOEXCEPT
Copy constructor.
Definition: secure_buffer.hxx:379
~FixedArrayBuffer() DECAF_NOEXCEPT
Destroy the buffer.
Definition: secure_buffer.hxx:384
A fixed-size block.
Definition: secure_buffer.hxx:253
FixedBlock(const Block &b)
Check a block's length.
Definition: secure_buffer.hxx:256
FixedBlock(const uint8_t data[Size]) DECAF_NOEXCEPT
Explicitly pass a C buffer.
Definition: secure_buffer.hxx:266
FixedBlock(const std::vector< unsigned char, alloc > &s)
Block from std::vector.
Definition: secure_buffer.hxx:261
A fixed-size block.
Definition: secure_buffer.hxx:310
FixedBuffer(Buffer b)
Check a block's length.
Definition: secure_buffer.hxx:313
FixedBuffer(uint8_t dat[Size], bool zero_on_destroy=false) DECAF_NOEXCEPT
Explicitly pass a C buffer.
Definition: secure_buffer.hxx:323
FixedBuffer(SecureBuffer &b)
Check a block's length.
Definition: secure_buffer.hxx:318
An exception for when crypto (ie point decode) has failed.
Definition: secure_buffer.hxx:126
virtual const char * what() const DECAF_NOEXCEPT
Definition: secure_buffer.hxx:129
Prototype of a random number generator.
Definition: secure_buffer.hxx:138
SecureBuffer read(size_t length)
Read into a SecureBuffer.
Rng()
Empty initializer.
Definition: secure_buffer.hxx:141
Rng & operator=(const Rng &) DECAF_DELETE
Not copyable.
Rng(const Rng &) DECAF_DELETE
Not copyable.
virtual void read(Buffer buffer) DECAF_NOEXCEPT=0
Read into a Buffer.
An allocator which zeros its memory on free.
Definition: secure_buffer.hxx:44
Base class of objects which support serialization.
Definition: secure_buffer.hxx:89
size_t ser_size() const DECAF_NOEXCEPT
Return the number of bytes needed to serialize this object.
Definition: secure_buffer.hxx:92
void serialize_into(unsigned char *buf) const DECAF_NOEXCEPT
Serialize this object into a buffer.
Definition: secure_buffer.hxx:95
SecureBuffer serialize() const
Serialize this object into a SecureBuffer and return it.
Definition: secure_buffer.hxx:100
void DECAF_API_VIS decaf_bzero(void *data, size_t size) DECAF_NONNULL
Overwrite data with zeros.
decaf_bool_t DECAF_API_VIS decaf_memeq(const void *data1, const void *data2, size_t size) DECAF_NONNULL DECAF_WARN_UNUSED
Compare two buffers, returning DECAF_TRUE if they are equal.
uint32_t decaf_bool_t
"Boolean" type, will be set to all-zero or all-one (i.e.
Definition: common.h:89
Namespace for all libdecaf C++ objects.
Definition: ed255.hxx:41
bool memeq(const std::vector< T, U > &a, const std::vector< V, W > &b)
Constant-time compare two buffers.
Definition: secure_buffer.hxx:83
std::vector< unsigned char, SanitizingAllocator< unsigned char, 0 > > SecureBuffer
A variant of std::vector which securely zerozes its state when destructed.
Definition: secure_buffer.hxx:79
Passed to constructors to avoid (conservative) initialization.
Definition: secure_buffer.hxx:133