diff --git a/include/decaf.hxx b/include/decaf.hxx index 106d037..e1e2b8b 100644 --- a/include/decaf.hxx +++ b/include/decaf.hxx @@ -49,16 +49,141 @@ namespace decaf { +typedef uint32_t GroupId; + +static const GroupId Ed448Goldilocks = 448; + /** * Securely erase contents of memory. */ static inline void really_bzero(void *data, size_t size) { decaf_bzero(data,size); } + +/** Block object */ +class Block { +protected: + unsigned char *data_; + size_t size_; + +public: + /** Empty init */ + inline Block() : data_(NULL), size_(0) {} + + /** Unowned init */ + inline Block(const unsigned char *data, size_t size) NOEXCEPT : data_((unsigned char *)data), size_(size) {} -/** - * @brief Group with prime order. - * @todo Move declarations of functions up here? - */ -template struct decaf; + /** Block from std::string */ + inline Block(const std::string &s) : data_((unsigned char *)GET_DATA(s)), size_(s.size()) {} + + /** Get const data */ + inline const unsigned char *data() const NOEXCEPT { return data_; } + + /** Get the size */ + inline size_t size() const NOEXCEPT { return size_; } + + /** Autocast to const unsigned char * */ + inline operator const unsigned char*() const NOEXCEPT { return data_; } + + /** Convert to C++ string */ + inline std::string get_string() const { + return std::string((const char *)data_,size_); + } + + /** Virtual destructor for SecureBlock. TODO: probably means vtable? Make bool? */ + inline virtual ~Block() {}; +}; + +class Buffer : public Block { +public: + /** Null init */ + inline Buffer() : Block() {} + + /** Unowned init */ + inline Buffer(unsigned char *data, size_t size) NOEXCEPT : Block(data,size) {} + + /** Get unconst data */ + inline unsigned char *data() NOEXCEPT { return data_; } + + /** Get const data */ + inline const unsigned char *data() const NOEXCEPT { return data_; } + + /** Autocast to const unsigned char * */ + inline operator const unsigned char*() const NOEXCEPT { return data_; } + + /** Autocast to unsigned char */ + inline operator unsigned char*() NOEXCEPT { return data_; } +}; + +/** A self-erasing block of data */ +class SecureBuffer : public Buffer { +public: + /** Null secure block */ + inline SecureBuffer() NOEXCEPT : Buffer() {} + + /** Construct empty from size */ + inline SecureBuffer(size_t size) { + data_ = new unsigned char[size_ = size]; + memset(data_,0,size); + } + + /** Construct from data */ + inline SecureBuffer(const unsigned char *data, size_t size){ + data_ = new unsigned char[size_ = size]; + memcpy(data_, data, size); + } + + /** Copy constructor */ + inline SecureBuffer(const Block ©) : Buffer() { *this = copy; } + + /** Copy-assign constructor */ + inline SecureBuffer& operator=(const Block ©) throw(std::bad_alloc) { + if (© == this) return *this; + clear(); + data_ = new unsigned char[size_ = copy.size()]; + memcpy(data_,copy.data(),size_); + return *this; + } + + /** Copy-assign constructor */ + inline SecureBuffer& operator=(const SecureBuffer ©) throw(std::bad_alloc) { + if (© == this) return *this; + clear(); + data_ = new unsigned char[size_ = copy.size()]; + memcpy(data_,copy.data(),size_); + return *this; + } + + /** Destructor erases data */ + ~SecureBuffer() NOEXCEPT { clear(); } + + /** Clear data */ + inline void clear() NOEXCEPT { + if (data_ == NULL) return; + really_bzero(data_,size_); + delete[] data_; + data_ = NULL; + size_ = 0; + } + +#if __cplusplus >= 201103L + /** Move constructor */ + inline SecureBuffer(SecureBuffer &&move) { *this = move; } + + /** Move non-constructor */ + inline SecureBuffer(const Block &&move) { *this = (Block &)move; } + + /** Move-assign constructor */ + inline SecureBuffer& operator=(SecureBuffer &&move) { + clear(); + data_ = move.data_; move.data_ = NULL; + size_ = move.size_; move.size_ = 0; + return *this; + } + + /** C++11-only explicit cast */ + inline explicit operator std::string() const { return get_string(); } +#endif +}; + /** @brief Passed to constructors to avoid (conservative) initialization */ struct NOINIT {}; @@ -68,10 +193,17 @@ struct NOINIT {}; class SpongeRng; /**@endcond*/ + +/** + * @brief Group with prime order. + * @todo Move declarations of functions up here? + */ +template struct decaf; + /** * @brief Ed448-Goldilocks/Decaf instantiation of group. */ -template<> struct decaf<448> { +template<> struct decaf { /** @brief An exception for when crypto (ie point decode) has failed. */ class CryptoException : public std::exception { @@ -116,16 +248,13 @@ public: inline Scalar(const Scalar &x) NOEXCEPT { *this = x; } /** @brief Construct from arbitrary-length little-endian byte sequence. */ - inline explicit Scalar(const std::string &str) NOEXCEPT { *this = str; } - - /** @brief Construct from arbitrary-length little-endian byte sequence. */ - inline Scalar(const unsigned char *buffer, size_t n) NOEXCEPT { decaf_448_scalar_decode_long(s,buffer,n); } - - /** @brief Construct from arbitrary-length little-endian byte sequence. */ - inline Scalar(const char *buffer, size_t n) NOEXCEPT { decaf_448_scalar_decode_long(s,(const unsigned char *)buffer,n); } + inline Scalar(const unsigned char *buffer, size_t n) NOEXCEPT { decode(buffer,n); } /** @brief Construct from arbitrary-length little-endian byte sequence. */ - inline Scalar(const void *buffer, size_t n) NOEXCEPT { decaf_448_scalar_decode_long(s,(const unsigned char *)buffer,n); } + inline Scalar(const Block &buffer) NOEXCEPT { decode(buffer.data(),buffer.size()); } + + /** @brief Decode from long buffer. */ + inline void decode(const unsigned char *buffer, size_t n) NOEXCEPT { decaf_448_scalar_decode_long(s,buffer,n); } /** @brief Assignment. */ inline Scalar& operator=(const Scalar &x) NOEXCEPT { decaf_448_scalar_copy(s,x.s); return *this; } @@ -144,9 +273,9 @@ public: /** Destructor securely erases the scalar. */ inline ~Scalar() NOEXCEPT { decaf_448_scalar_destroy(s); } - /** @brief Assign from arbitrary-length little-endian byte sequence in C++ string. */ - inline Scalar &operator=(const std::string &str) NOEXCEPT { - decaf_448_scalar_decode_long(s,GET_DATA(str),str.size()); return *this; + /** @brief Assign from arbitrary-length little-endian byte sequence in a Block. */ + inline Scalar &operator=(const Block &bl) NOEXCEPT { + decaf_448_scalar_decode_long(s,bl.data(),bl.size()); return *this; } /** @@ -161,17 +290,15 @@ public: /** @brief Decode from correct-length little-endian byte sequence in C++ string. */ static inline decaf_bool_t __attribute__((warn_unused_result)) decode ( - Scalar &sc, const std::string buffer + Scalar &sc, const Block &buffer ) NOEXCEPT { if (buffer.size() != SER_BYTES) return DECAF_FAILURE; - return decaf_448_scalar_decode(sc.s,GET_DATA(buffer)); + return decaf_448_scalar_decode(sc.s,buffer); } /** @brief Encode to fixed-length string */ - inline EXPLICIT_CON operator std::string() const NOEXCEPT { - unsigned char buffer[SER_BYTES]; - decaf_448_scalar_encode(buffer, s); - return std::string((char*)buffer,sizeof(buffer)); + inline EXPLICIT_CON operator SecureBuffer() const NOEXCEPT { + SecureBuffer buf(SER_BYTES); decaf_448_scalar_encode(buf,s); return buf; } /** @brief Encode to fixed-length buffer */ @@ -204,13 +331,13 @@ public: inline Scalar inverse() const NOEXCEPT { Scalar r; decaf_448_scalar_invert(r.s,s); return r; } /** @brief Divide by inverting q. If q == 0, return 0. */ - inline Scalar operator/ (const Scalar &q) const NOEXCEPT { Scalar r((NOINIT())); decaf_448_scalar_mul(r.s,s,q.inverse().s); return r; } + inline Scalar operator/ (const Scalar &q) const NOEXCEPT { return *this * q.inverse(); } /** @brief Divide by inverting q. If q == 0, return 0. */ - inline Scalar &operator/=(const Scalar &q) NOEXCEPT { decaf_448_scalar_mul(s,s,q.inverse().s); return *this; } + inline Scalar &operator/=(const Scalar &q) NOEXCEPT { return *this *= q.inverse(); } /** @brief Compare in constant time */ - inline bool operator!=(const Scalar &q) const NOEXCEPT { return ! decaf_448_scalar_eq(s,q.s); } + inline bool operator!=(const Scalar &q) const NOEXCEPT { return !(*this == q); } /** @brief Compare in constant time */ inline bool operator==(const Scalar &q) const NOEXCEPT { return !!decaf_448_scalar_eq(s,q.s); } @@ -221,33 +348,17 @@ public: /** @brief Scalarmul-precomputed with scalar on left. */ inline Point operator* (const Precomputed &q) const NOEXCEPT { return q * (*this); } - /** @brief Direct scalar multiplication. - * @todo Fix up bools. - */ - inline decaf_bool_t direct_scalarmul( - unsigned char out[SER_BYTES], - const unsigned char in[SER_BYTES], + /** @brief Direct scalar multiplication. */ + inline SecureBuffer direct_scalarmul( + const Block &in, decaf_bool_t allow_identity=DECAF_FALSE, decaf_bool_t short_circuit=DECAF_TRUE - ) const NOEXCEPT { - return decaf_448_direct_scalarmul(out, in, s, allow_identity, short_circuit); + ) const throw(CryptoException) { + SecureBuffer out(/*FIXME Point::*/SER_BYTES); + if (!decaf_448_direct_scalarmul(out, in.data(), s, allow_identity, short_circuit)) + throw CryptoException(); + return out; } - - /** @brief Direct scalar multiplication. - * @todo Fix up bools. - */ - inline std::string direct_scalarmul( - const std::string in, - decaf_bool_t allow_identity=DECAF_FALSE, - decaf_bool_t short_circuit=DECAF_TRUE - ) const NOEXCEPT { - unsigned char out[SER_BYTES]; - if (decaf_448_direct_scalarmul(out, GET_DATA(in), s, allow_identity, short_circuit)) { - return std::string((char *)out,sizeof(out)); - } else { - return ""; - } - } }; /** @@ -289,7 +400,7 @@ public: * @throw CryptoException the string was the wrong length, or wasn't the encoding of a point, * or was the identity and allow_identity was DECAF_FALSE. */ - inline explicit Point(const std::string &s, decaf_bool_t allow_identity=DECAF_TRUE) throw(CryptoException) { + inline explicit Point(const Block &s, decaf_bool_t allow_identity=DECAF_TRUE) throw(CryptoException) { if (!decode(*this,s,allow_identity)) throw CryptoException(); } @@ -326,15 +437,16 @@ public: * or was the identity and allow_identity was DECAF_FALSE. Contents of the buffer are undefined. */ static inline decaf_bool_t __attribute__((warn_unused_result)) decode ( - Point &p, const std::string &buffer, decaf_bool_t allow_identity=DECAF_TRUE + Point &p, const Block &buffer, decaf_bool_t allow_identity=DECAF_TRUE ) NOEXCEPT { if (buffer.size() != SER_BYTES) return DECAF_FAILURE; - return decaf_448_point_decode(p.p,GET_DATA(buffer),allow_identity); + return decaf_448_point_decode(p.p,buffer.data(),allow_identity); } /** * @brief Map to the curve from a C buffer. * The all-zero buffer maps to the identity, as does the buffer {1,0...} + * @todo remove? */ static inline Point from_hash_nonuniform ( const unsigned char buffer[SER_BYTES] ) NOEXCEPT { Point p((NOINIT())); decaf_448_point_from_hash_nonuniform(p.p,buffer); return p; @@ -345,10 +457,16 @@ public: * The empty or all-zero string maps to the identity, as does the string "\x01". * If the buffer is shorter than (TODO) SER_BYTES, it will be zero-padded on the right. */ - static inline Point from_hash_nonuniform ( const std::string &s ) NOEXCEPT { - std::string t = s; - if (t.size() < SER_BYTES) t.insert(t.size(),SER_BYTES-t.size(),0); - Point p((NOINIT())); decaf_448_point_from_hash_nonuniform(p.p,GET_DATA(t)); return p; + static inline Point from_hash_nonuniform ( const Block &s ) NOEXCEPT { + Point p((NOINIT())); + if (s.size() < SER_BYTES) { + SecureBuffer b(SER_BYTES); + memcpy(b.data(), s.data(), s.size()); + decaf_448_point_from_hash_nonuniform(p.p,b); + } else { + decaf_448_point_from_hash_nonuniform(p.p,s); + } + return p; } @@ -366,20 +484,29 @@ public: * If the buffer is shorter than (TODO) 2*SER_BYTES, well, it won't be as uniform, * but the buffer will be zero-padded on the right. */ - static inline Point from_hash ( const std::string &s ) NOEXCEPT { - std::string t = s; - if (t.size() <= SER_BYTES) return from_hash_nonuniform(s); - if (t.size() < 2*SER_BYTES) t.insert(t.size(),2*SER_BYTES-t.size(),0); - Point p((NOINIT())); decaf_448_point_from_hash_uniform(p.p,GET_DATA(t)); return p; + static inline Point from_hash ( const Block &s ) NOEXCEPT { + if (s.size() <= SER_BYTES) { + return from_hash_nonuniform(s); + } + + Point p((NOINIT())); + if (s.size() < 2*SER_BYTES) { + SecureBuffer b(SER_BYTES); + memcpy(b.data(), s.data(), s.size()); + decaf_448_point_from_hash_nonuniform(p.p,b); + } else { + decaf_448_point_from_hash_nonuniform(p.p,s); + } + return p; } /** * @brief Encode to string. The identity encodes to the all-zero string. */ - inline EXPLICIT_CON operator std::string() const NOEXCEPT { - unsigned char buffer[SER_BYTES]; + inline EXPLICIT_CON operator SecureBuffer() const NOEXCEPT { + SecureBuffer buffer(SER_BYTES); decaf_448_point_encode(buffer, p); - return std::string((char*)buffer,sizeof(buffer)); + return buffer; } /** diff --git a/include/shake.hxx b/include/shake.hxx index 7fcfc19..494f016 100644 --- a/include/shake.hxx +++ b/include/shake.hxx @@ -65,13 +65,13 @@ public: inline void update(const uint8_t *__restrict__ in, size_t len) { sha3_update(sp,in,len); } /** Add more data to running hash, C++ version. */ - inline void update(const std::string &s) { sha3_update(sp,GET_DATA(s),s.size()); } + inline void update(const Block &s) { sha3_update(sp,s.data(),s.size()); } /** Add more data, stream version. */ - inline KeccakHash &operator<<(const std::string &s) { update(s); return *this; } + inline KeccakHash &operator<<(const Block &s) { update(s); return *this; } /** Same as <<. */ - inline KeccakHash &operator+=(const std::string &s) { return *this << s; } + inline KeccakHash &operator+=(const Block &s) { return *this << s; } /** * @brief Output bytes from the sponge. @@ -82,12 +82,10 @@ public: } /** @brief Output bytes from the sponge. */ - inline std::string output(size_t len) { - unsigned char *buffer = new unsigned char[len]; + inline SecureBuffer output(size_t len) { + SecureBuffer buffer(len); sha3_output(sp,buffer,len); - std::string out((char *)buffer, len); - delete[] buffer; - return out; + return buffer; } /** @brief Return the sponge's default output size. */ @@ -96,7 +94,7 @@ public: } /** Output the default number of bytes. */ - inline std::string output() { + inline SecureBuffer output() { return output(default_output_size()); } }; @@ -157,6 +155,12 @@ public: spongerng_init_from_buffer(sp,GET_DATA(in),in.size(),deterministic); } + /** Initialize, deterministically by default, from block */ + inline SpongeRng( const FROM_BUFFER &, const Block &in, bool deterministic = true ) + : KeccakSponge((NOINIT())) { + spongerng_init_from_buffer(sp,in.data(),in.size(),deterministic); + } + /** Initialize, non-deterministically by default, from C/C++ filename */ inline SpongeRng( const FROM_FILE &, const std::string &in = "/dev/urandom", size_t len = 32, bool deterministic = false ) throw(RngException) @@ -179,12 +183,8 @@ public: * @warning TODO Future versions of this function may throw RngException if a * nondeterministic RNG fails a reseed. */ - inline std::string read(size_t length) throw(std::bad_alloc) { - uint8_t *buffer = new uint8_t[length]; - spongerng_next(sp,buffer,length); - std::string out((const char *)buffer, length); - delete[] buffer; - return out; + inline SecureBuffer read(size_t length) throw(std::bad_alloc) { + SecureBuffer out(length); spongerng_next(sp,out,length); return out; } private: @@ -193,7 +193,6 @@ private: }; /**@cond internal*/ - /* FIXME: MAGIC; should use buffer or erase temporary string */ /* FIXME: multiple sizes */ decaf<448>::Scalar::Scalar(SpongeRng &rng) { uint8_t buffer[SER_BYTES]; diff --git a/test/bench_decaf.cxx b/test/bench_decaf.cxx index 6aa8817..e4a88c3 100644 --- a/test/bench_decaf.cxx +++ b/test/bench_decaf.cxx @@ -131,7 +131,7 @@ int main(int argc, char **argv) { Precomputed pBase; Point p,q; Scalar s,t; - std::string ep; + decaf::SecureBuffer ep, ep2(Point::SER_BYTES*2); printf("Micro-benchmarks:\n"); for (Benchmark b("Scalar add", 1000); b.iter(); ) { s+=t; } @@ -140,11 +140,11 @@ int main(int argc, char **argv) { for (Benchmark b("Point add", 100); b.iter(); ) { p += q; } for (Benchmark b("Point double", 100); b.iter(); ) { p.double_in_place(); } for (Benchmark b("Point scalarmul"); b.iter(); ) { p * s; } - for (Benchmark b("Point encode"); b.iter(); ) { ep = std::string(p); } + for (Benchmark b("Point encode"); b.iter(); ) { ep = decaf::SecureBuffer(p); } for (Benchmark b("Point decode"); b.iter(); ) { p = Point(ep); } for (Benchmark b("Point create/destroy"); b.iter(); ) { Point r; } for (Benchmark b("Point hash nonuniform"); b.iter(); ) { Point::from_hash(ep); } - for (Benchmark b("Point hash uniform"); b.iter(); ) { Point::from_hash(ep+ep); } + for (Benchmark b("Point hash uniform"); b.iter(); ) { Point::from_hash(ep2); } for (Benchmark b("Point double scalarmul"); b.iter(); ) { Point::double_scalarmul(p,s,q,t); } for (Benchmark b("Point precmp scalarmul"); b.iter(); ) { pBase * s; } /* TODO: scalarmul for verif, etc */ diff --git a/test/test_decaf.cxx b/test/test_decaf.cxx index e905f12..3980d39 100644 --- a/test/test_decaf.cxx +++ b/test/test_decaf.cxx @@ -170,7 +170,7 @@ static void test_ec() { rng.read(buffer, 2*DECAF_448_SER_BYTES); Point r = Point::from_hash(buffer); - point_check(test,p,q,r,0,0,p,Point((std::string)p),"round-trip"); + point_check(test,p,q,r,0,0,p,Point((decaf::SecureBuffer)p),"round-trip"); point_check(test,p,q,r,0,0,p+q,q+p,"commute add"); point_check(test,p,q,r,0,0,p+(q+r),(p+q)+r,"assoc add"); @@ -187,7 +187,7 @@ static void test_ec() { ); - point_check(test,p,q,r,x,0,Point(x.direct_scalarmul(p)),x*p,"direct mul"); + point_check(test,p,q,r,x,0,Point(x.direct_scalarmul(decaf::SecureBuffer(p))),x*p,"direct mul"); } }