You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

303 lines
8.8 KiB

  1. /**
  2. * @file secure_buffer.hxx
  3. * @author Mike Hamburg
  4. *
  5. * @copyright
  6. * Copyright (c) 2015 Cryptography Research, Inc. \n
  7. * Released under the MIT License. See LICENSE.txt for license information.
  8. *
  9. * @brief C++ self-zeroizing buffer.
  10. */
  11. #ifndef __DECAF_SECURE_BUFFER_HXX__
  12. #define __DECAF_SECURE_BUFFER_HXX__ 1
  13. #include <string>
  14. #include <sys/types.h>
  15. /** @cond internal */
  16. #if __cplusplus >= 201103L
  17. #define NOEXCEPT noexcept
  18. #define DELETE = delete
  19. #else
  20. #define NOEXCEPT throw()
  21. #define DELETE
  22. #endif
  23. /** @endcond */
  24. namespace decaf {
  25. /**@cond internal*/
  26. /** Forward-declare sponge RNG object */
  27. class Buffer;
  28. class TmpBuffer;
  29. class SecureBuffer;
  30. /**@endcond*/
  31. /** @brief An exception for when crypto (ie point decode) has failed. */
  32. class CryptoException : public std::exception {
  33. public:
  34. /** @return "CryptoException" */
  35. virtual const char * what() const NOEXCEPT { return "CryptoException"; }
  36. };
  37. /** @brief An exception for when crypto (ie point decode) has failed. */
  38. class LengthException : public std::exception {
  39. public:
  40. /** @return "CryptoException" */
  41. virtual const char * what() const NOEXCEPT { return "LengthException"; }
  42. };
  43. /** @brief Passed to constructors to avoid (conservative) initialization */
  44. struct NOINIT {};
  45. /** @brief Prototype of a random number generator.
  46. * FUTURE: Are the noexcept methods really noexcept? What about self-reseeding RNGs?
  47. */
  48. class Rng {
  49. protected:
  50. /** Empty initializer */
  51. Rng() {}
  52. /** Not copyable */
  53. Rng(const Rng &) DELETE;
  54. /** Not copyable */
  55. Rng &operator=(const Rng &) DELETE;
  56. public:
  57. /** @brief Read into a Buffer */
  58. virtual inline void read(Buffer &buffer) NOEXCEPT = 0;
  59. /** @brief Read into a value-passed (eg temporary) TmpBuffer. */
  60. inline void read(TmpBuffer buffer) NOEXCEPT;
  61. /** @brief Read into a SecureBuffer. */
  62. inline SecureBuffer read(size_t length) throw(std::bad_alloc);
  63. };
  64. /**
  65. * Securely zeorize contents of memory.
  66. */
  67. static inline void really_bzero(void *data, size_t size) { decaf_bzero(data,size); }
  68. /** A reference to a block of data, which (when accessed through this base class) is const. */
  69. class Block {
  70. protected:
  71. unsigned char *data_;
  72. size_t size_;
  73. public:
  74. /** Empty init */
  75. inline Block() NOEXCEPT : data_(NULL), size_(0) {}
  76. /** Init from C string */
  77. inline Block(const char *data) NOEXCEPT : data_((unsigned char *)data), size_(strlen(data)) {}
  78. /** Unowned init */
  79. inline Block(const unsigned char *data, size_t size) NOEXCEPT : data_((unsigned char *)data), size_(size) {}
  80. /** Block from std::string */
  81. inline Block(const std::string &s) : data_(
  82. #if __cplusplus >= 201103L
  83. ((unsigned char *)&(s)[0])
  84. #else
  85. ((unsigned char *)(s.data()))
  86. #endif
  87. ), size_(s.size()) {}
  88. /** Get const data */
  89. inline const unsigned char *data() const NOEXCEPT { return data_; }
  90. /** Get the size */
  91. inline size_t size() const NOEXCEPT { return size_; }
  92. /** Autocast to const unsigned char * */
  93. inline operator const unsigned char*() const NOEXCEPT { return data_; }
  94. /** Convert to C++ string */
  95. inline std::string get_string() const {
  96. return std::string((const char *)data_,size_);
  97. }
  98. /** Slice the buffer*/
  99. inline Block slice(size_t off, size_t length) const throw(LengthException) {
  100. if (off > size() || length > size() - off)
  101. throw LengthException();
  102. return Block(data()+off, length);
  103. }
  104. /** @cond internal */
  105. inline decaf_bool_t operator>=(const Block &b) const NOEXCEPT DELETE;
  106. inline decaf_bool_t operator<=(const Block &b) const NOEXCEPT DELETE;
  107. inline decaf_bool_t operator> (const Block &b) const NOEXCEPT DELETE;
  108. inline decaf_bool_t operator< (const Block &b) const NOEXCEPT DELETE;
  109. /** @endcond */
  110. /* Content-wise comparison; constant-time if they are the same length. */
  111. inline decaf_bool_t operator!=(const Block &b) const NOEXCEPT {
  112. if (b.size() != size()) return true;
  113. return ~decaf_memeq(b,*this,size());
  114. }
  115. /* Content-wise comparison; constant-time if they are the same length. */
  116. inline decaf_bool_t operator==(const Block &b) const NOEXCEPT {
  117. return ~(*this == b);
  118. }
  119. /** Virtual destructor for SecureBlock. TODO: probably means vtable? Make bool? */
  120. inline virtual ~Block() {};
  121. };
  122. /** A reference to a writable block of data */
  123. class Buffer : public Block {
  124. public:
  125. /** Null init */
  126. inline Buffer() NOEXCEPT : Block() {}
  127. /** Unowned init */
  128. inline Buffer(unsigned char *data, size_t size) NOEXCEPT : Block(data,size) {}
  129. /** Get unconst data */
  130. inline unsigned char *data() NOEXCEPT { return data_; }
  131. /** Get const data */
  132. inline const unsigned char *data() const NOEXCEPT { return data_; }
  133. /** Autocast to const unsigned char * */
  134. inline operator const unsigned char*() const NOEXCEPT { return data_; }
  135. /** Autocast to unsigned char */
  136. inline operator unsigned char*() NOEXCEPT { return data_; }
  137. /** Slice the buffer*/
  138. inline TmpBuffer slice(size_t off, size_t length) throw(LengthException);
  139. /** Securely set the buffer to 0. */
  140. inline void zeorize() NOEXCEPT { really_bzero(data(),size()); }
  141. };
  142. /** A temporary reference to a writeable buffer, for converting C to C++. */
  143. class TmpBuffer : public Buffer {
  144. public:
  145. /** Unowned init */
  146. inline TmpBuffer(unsigned char *data, size_t size) NOEXCEPT : Buffer(data,size) {}
  147. };
  148. /** A fixed-size stack-allocated buffer (for NOEXCEPT semantics) */
  149. template<size_t Size> class StackBuffer : public Buffer {
  150. private:
  151. uint8_t storage[Size];
  152. public:
  153. /** New buffer initialized to zero. */
  154. inline StackBuffer() NOEXCEPT : Buffer(storage, Size) { memset(storage,0,Size); }
  155. /** New uninitialized buffer. */
  156. inline StackBuffer(const NOINIT &) NOEXCEPT : Buffer(storage, Size) { }
  157. /** New random buffer */
  158. inline StackBuffer(Rng &r) NOEXCEPT : Buffer(storage, Size) { r.read(*this); }
  159. /** Destroy the buffer */
  160. ~StackBuffer() NOEXCEPT { zeorize(); }
  161. };
  162. /** @cond internal */
  163. inline void Rng::read(TmpBuffer buffer) NOEXCEPT { read((Buffer &)buffer); }
  164. /** @endcond */
  165. /** A self-erasing block of data */
  166. class SecureBuffer : public Buffer {
  167. public:
  168. /** Null secure block */
  169. inline SecureBuffer() NOEXCEPT : Buffer() {}
  170. /** Construct empty from size */
  171. inline SecureBuffer(size_t size) {
  172. data_ = new unsigned char[size_ = size];
  173. memset(data_,0,size);
  174. }
  175. /** Construct from data */
  176. inline SecureBuffer(const unsigned char *data, size_t size) {
  177. data_ = new unsigned char[size_ = size];
  178. memcpy(data_, data, size);
  179. }
  180. /** Construct from random */
  181. inline SecureBuffer(Rng &r, size_t size) NOEXCEPT {
  182. data_ = new unsigned char[size_ = size];
  183. r.read(*this);
  184. }
  185. /** Copy constructor */
  186. inline SecureBuffer(const Block &copy) : Buffer() { *this = copy; }
  187. /** Copy-assign constructor */
  188. inline SecureBuffer& operator=(const Block &copy) throw(std::bad_alloc) {
  189. if (&copy == this) return *this;
  190. clear();
  191. data_ = new unsigned char[size_ = copy.size()];
  192. memcpy(data_,copy.data(),size_);
  193. return *this;
  194. }
  195. /** Copy-assign constructor */
  196. inline SecureBuffer& operator=(const SecureBuffer &copy) throw(std::bad_alloc) {
  197. if (&copy == this) return *this;
  198. clear();
  199. data_ = new unsigned char[size_ = copy.size()];
  200. memcpy(data_,copy.data(),size_);
  201. return *this;
  202. }
  203. /** Destructor zeorizes data */
  204. ~SecureBuffer() NOEXCEPT { clear(); }
  205. /** Clear data */
  206. inline void clear() NOEXCEPT {
  207. if (data_ == NULL) return;
  208. zeorize();
  209. delete[] data_;
  210. data_ = NULL;
  211. size_ = 0;
  212. }
  213. #if __cplusplus >= 201103L
  214. /** Move constructor */
  215. inline SecureBuffer(SecureBuffer &&move) { *this = move; }
  216. /** Move non-constructor */
  217. inline SecureBuffer(Block &&move) { *this = (Block &)move; }
  218. /** Move-assign constructor. TODO: check that this actually gets used.*/
  219. inline SecureBuffer& operator=(SecureBuffer &&move) {
  220. clear();
  221. data_ = move.data_; move.data_ = NULL;
  222. size_ = move.size_; move.size_ = 0;
  223. return *this;
  224. }
  225. /** C++11-only explicit cast */
  226. inline explicit operator std::string() const { return get_string(); }
  227. #endif
  228. };
  229. /** @cond internal */
  230. TmpBuffer Buffer::slice(size_t off, size_t length) throw(LengthException) {
  231. if (off > size() || length > size() - off) throw LengthException();
  232. return TmpBuffer(data()+off, length);
  233. }
  234. inline SecureBuffer Rng::read(size_t length) throw(std::bad_alloc) {
  235. SecureBuffer out(length); read(out); return out;
  236. }
  237. /** @endcond */
  238. } /* namespace decaf */
  239. #undef NOEXCEPT
  240. #undef DELETE
  241. #endif /* __DECAF_SECURE_BUFFER_HXX__ */