From db0a12de2a136a37f91b475c684cf0a51df3392a Mon Sep 17 00:00:00 2001 From: Michael Hamburg Date: Wed, 1 Jul 2015 14:39:36 -0700 Subject: [PATCH] working on breaking up include files --- include/decaf.h | 60 +++++++- include/decaf_255.h | 79 +++------- include/decaf_255.hxx | 265 ++++++++------------------------- include/decaf_448.h | 55 +------ include/secure_buffer.hxx | 302 ++++++++++++++++++++++++++++++++++++++ include/shake.hxx | 52 +------ src/decaf_fast.c | 33 +++-- test/test_decaf.cxx | 33 ++++- 8 files changed, 500 insertions(+), 379 deletions(-) create mode 100644 include/secure_buffer.hxx diff --git a/include/decaf.h b/include/decaf.h index 2a55920..49e6b0c 100644 --- a/include/decaf.h +++ b/include/decaf.h @@ -2,7 +2,65 @@ #ifndef __DECAF_H__ #define __DECAF_H__ 1 -#include "decaf_255.h" // MAGIC +#include +#include + +/* Goldilocks' build flags default to hidden and stripping executables. */ +/** @cond internal */ +#if defined(DOXYGEN) && !defined(__attribute__) +#define __attribute__((x)) +#endif +#define API_VIS __attribute__((visibility("default"))) +#define NOINLINE __attribute__((noinline)) +#define WARN_UNUSED __attribute__((warn_unused_result)) +#define NONNULL1 __attribute__((nonnull(1))) +#define NONNULL2 __attribute__((nonnull(1,2))) +#define NONNULL3 __attribute__((nonnull(1,2,3))) +#define NONNULL4 __attribute__((nonnull(1,2,3,4))) +#define NONNULL5 __attribute__((nonnull(1,2,3,4,5))) +/** @endcond */ + +/* Internal word types */ +#if (defined(__ILP64__) || defined(__amd64__) || defined(__x86_64__) || (((__UINT_FAST32_MAX__)>>30)>>30)) \ + && !defined(DECAF_FORCE_32_BIT) +#define DECAF_WORD_BITS 64 +typedef uint64_t decaf_word_t, decaf_bool_t; +typedef __uint128_t decaf_dword_t; +#else +#define DECAF_WORD_BITS 32 +typedef uint32_t decaf_word_t, decaf_bool_t; +typedef uint64_t decaf_dword_t; +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + + /** DECAF_TRUE = -1 so that DECAF_TRUE & x = x */ + static const decaf_bool_t DECAF_TRUE = -(decaf_bool_t)1, DECAF_FALSE = 0; + + /** NB Success is -1, failure is 0. TODO: see if people would rather the reverse. */ + static const decaf_bool_t DECAF_SUCCESS = -(decaf_bool_t)1 /*DECAF_TRUE*/, + DECAF_FAILURE = 0 /*DECAF_FALSE*/; + + #include "decaf_255.h" + #include "decaf_448.h" + + +#ifdef __cplusplus +} +#endif + + +#undef API_VIS +#undef WARN_UNUSED +#undef NOINLINE +#undef NONNULL1 +#undef NONNULL2 +#undef NONNULL3 +#undef NONNULL4 +#undef NONNULL5 #endif /* __DECAF_H__ */ diff --git a/include/decaf_255.h b/include/decaf_255.h index f41ed6b..3c1ace1 100644 --- a/include/decaf_255.h +++ b/include/decaf_255.h @@ -25,33 +25,8 @@ #ifndef __DECAF_255_H__ #define __DECAF_255_H__ 1 -#include -#include - -/* Goldilocks' build flags default to hidden and stripping executables. */ -/** @cond internal */ -#if defined(DOXYGEN) && !defined(__attribute__) -#define __attribute__((x)) -#endif -#define API_VIS __attribute__((visibility("default"))) -#define NOINLINE __attribute__((noinline)) -#define WARN_UNUSED __attribute__((warn_unused_result)) -#define NONNULL1 __attribute__((nonnull(1))) -#define NONNULL2 __attribute__((nonnull(1,2))) -#define NONNULL3 __attribute__((nonnull(1,2,3))) -#define NONNULL4 __attribute__((nonnull(1,2,3,4))) -#define NONNULL5 __attribute__((nonnull(1,2,3,4,5))) - -/* Internal word types */ -#if (defined(__ILP64__) || defined(__amd64__) || defined(__x86_64__) || (((__UINT_FAST32_MAX__)>>30)>>30)) \ - && !defined(DECAF_FORCE_32_BIT) -#define DECAF_WORD_BITS 64 -typedef uint64_t decaf_word_t, decaf_bool_t; -typedef __uint128_t decaf_dword_t; -#else -#define DECAF_WORD_BITS 32 -typedef uint32_t decaf_word_t, decaf_bool_t; -typedef uint64_t decaf_dword_t; +#ifndef __DECAF_H__ +#error "include , not ." #endif #define DECAF_255_LIMBS (320/DECAF_WORD_BITS) @@ -59,9 +34,9 @@ typedef uint64_t decaf_dword_t; #define DECAF_255_SCALAR_LIMBS (256/DECAF_WORD_BITS) /** Galois field element internal structure */ -typedef struct gf_s { +typedef struct gf_255_s { decaf_word_t limb[DECAF_255_LIMBS]; -} gf_s, gf[1]; +} gf_255_s, gf_255_t[1]; /** @endcond */ /** Number of bytes in a serialized point. */ @@ -71,7 +46,7 @@ typedef struct gf_s { #define DECAF_255_SCALAR_BYTES 32 /** Twisted Edwards (-1,d-1) extended homogeneous coordinates */ -typedef struct decaf_255_point_s { /**@cond internal*/gf x,y,z,t;/**@endcond*/ } decaf_255_point_t[1]; +typedef struct decaf_255_point_s { /**@cond internal*/gf_255_t x,y,z,t;/**@endcond*/ } decaf_255_point_t[1]; /** Precomputed table based on a point. Can be trivial implementation. */ struct decaf_255_precomputed_s; @@ -89,13 +64,6 @@ typedef struct decaf_255_scalar_s { /** @endcond */ } decaf_255_scalar_t[1]; -/** DECAF_TRUE = -1 so that DECAF_TRUE & x = x */ -static const decaf_bool_t DECAF_TRUE = -(decaf_bool_t)1, DECAF_FALSE = 0; - -/** NB Success is -1, failure is 0. TODO: see if people would rather the reverse. */ -static const decaf_bool_t DECAF_SUCCESS = -(decaf_bool_t)1 /*DECAF_TRUE*/, - DECAF_FAILURE = 0 /*DECAF_FALSE*/; - /** A scalar equal to 1. */ extern const decaf_255_scalar_t decaf_255_scalar_one API_VIS; @@ -115,10 +83,6 @@ extern const decaf_255_point_t decaf_255_point_base API_VIS; /** Precomputed table for the base point on the curve. */ extern const struct decaf_255_precomputed_s *decaf_255_precomputed_base API_VIS; -#ifdef __cplusplus -extern "C" { -#endif - /** * @brief Read a scalar from wire format or from bytes. * @@ -481,7 +445,8 @@ decaf_bool_t decaf_255_point_valid ( ) API_VIS WARN_UNUSED NONNULL1 NOINLINE; /** - * @brief 2-torque a point, for debugging purposes. + * @brief Torque a point, for debugging purposes. The output + * will be equal to the input. * * @param [out] q The point to torque. * @param [in] p The point to torque. @@ -491,6 +456,21 @@ void decaf_255_point_debugging_torque ( const decaf_255_point_t p ) API_VIS NONNULL2 NOINLINE; +/** + * @brief Projectively scale a point, for debugging purposes. + * The output will be equal to the input, and will be valid + * even if the factor is zero. + * + * @param [out] q The point to scale. + * @param [in] p The point to scale. + * @param [in] factor Serialized GF factor to scale. + */ +void decaf_255_point_debugging_pscale ( + decaf_255_point_t q, + const decaf_255_point_t p, + const unsigned char factor[DECAF_255_SER_BYTES] +) API_VIS NONNULL2 NOINLINE; + /** * @brief Almost-Elligator-like hash to curve. * @@ -624,19 +604,4 @@ void decaf_255_precomputed_destroy ( decaf_255_precomputed_s *pre ) NONNULL1 API_VIS; -/* TODO: functions to invert point_from_hash?? */ - -#undef API_VIS -#undef WARN_UNUSED -#undef NOINLINE -#undef NONNULL1 -#undef NONNULL2 -#undef NONNULL3 -#undef NONNULL4 -#undef NONNULL5 - -#ifdef __cplusplus -} /* extern "C" */ -#endif - #endif /* __DECAF_255_H__ */ diff --git a/include/decaf_255.hxx b/include/decaf_255.hxx index 3a43df3..5331591 100644 --- a/include/decaf_255.hxx +++ b/include/decaf_255.hxx @@ -28,6 +28,7 @@ #include /* for memcpy */ #include "decaf.h" +#include "secure_buffer.hxx" #include #include #include @@ -39,209 +40,18 @@ #if __cplusplus >= 201103L #define NOEXCEPT noexcept #define EXPLICIT_CON explicit -#define GET_DATA(str) ((const unsigned char *)&(str)[0]) +#define FINAL final +#define DELETE = delete #else #define NOEXCEPT throw() #define EXPLICIT_CON -#define GET_DATA(str) ((const unsigned char *)((str).data())) +#define FINAL +#define DELETE #endif /** @endcond */ namespace decaf { -/** @brief An exception for when crypto (ie point decode) has failed. */ -class CryptoException : public std::exception { -public: - /** @return "CryptoException" */ - virtual const char * what() const NOEXCEPT { return "CryptoException"; } -}; - -/** @brief An exception for when crypto (ie point decode) has failed. */ -class LengthException : public std::exception { -public: - /** @return "CryptoException" */ - virtual const char * what() const NOEXCEPT { return "LengthException"; } -}; - -/** - * 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() NOEXCEPT : data_(NULL), size_(0) {} - - /** Init from C string */ - inline Block(const char *data) NOEXCEPT : data_((unsigned char *)data), size_(strlen(data)) {} - - /** Unowned init */ - inline Block(const unsigned char *data, size_t size) NOEXCEPT : data_((unsigned char *)data), size_(size) {} - - /** 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_); - } - - /** Slice the buffer*/ - inline Block slice(size_t off, size_t length) const throw(LengthException) { - if (off > size() || length > size() - off) - throw LengthException(); - return Block(data()+off, length); - } - - /* Content-wise comparison; constant-time if they are the same length. - * FIXME: is it wise to have a content-wise compare on objects that may be mutable? - */ - inline decaf_bool_t operator==(const Block &b) const NOEXCEPT { - return ~(*this != b); - } - - inline decaf_bool_t operator!=(const Block &b) const NOEXCEPT { - if (b.size() != size()) return true; - return ~decaf_memeq(b,*this,size()); - } - - /** Virtual destructor for SecureBlock. TODO: probably means vtable? Make bool? */ - inline virtual ~Block() {}; -}; - -class TmpBuffer; - -class Buffer : public Block { -public: - /** Null init */ - inline Buffer() NOEXCEPT : 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_; } - - /** Slice the buffer*/ - inline TmpBuffer slice(size_t off, size_t length) throw(LengthException); -}; - -class TmpBuffer : public Buffer { -public: - /** Unowned init */ - inline TmpBuffer(unsigned char *data, size_t size) NOEXCEPT : Buffer(data,size) {} -}; - -TmpBuffer Buffer::slice(size_t off, size_t length) throw(LengthException) { - if (off > size() || length > size() - off) throw LengthException(); - return TmpBuffer(data()+off, length); -} - -/** 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(Block &&move) { *this = (Block &)move; } - - /** Move-assign constructor. TODO: check that this actually gets used.*/ - 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 {}; - -/**@cond internal*/ -/** Forward-declare sponge RNG object */ -class SpongeRng; -/**@endcond*/ - - /** * @brief Ed255-Goldilocks/Decaf instantiation of group. */ @@ -274,7 +84,10 @@ public: inline Scalar(const int w) NOEXCEPT { *this = w; } /** @brief Construct from RNG */ - inline explicit Scalar(SpongeRng &rng) NOEXCEPT; + inline explicit Scalar(Rng &rng) NOEXCEPT { + StackBuffer sb(rng); + *this = sb; + } /** @brief Construct from decaf_scalar_t object. */ inline Scalar(const decaf_255_scalar_t &t = decaf_255_scalar_zero) NOEXCEPT { decaf_255_scalar_copy(s,t); } @@ -299,7 +112,7 @@ public: return *this; } - /** Destructor securely erases the scalar. */ + /** Destructor securely zeorizes the scalar. */ inline ~Scalar() NOEXCEPT { decaf_255_scalar_destroy(s); } /** @brief Assign from arbitrary-length little-endian byte sequence in a Block. */ @@ -414,16 +227,24 @@ public: inline Point(const decaf_255_point_t &q = decaf_255_point_identity) NOEXCEPT { decaf_255_point_copy(p,q); } /** @brief Copy constructor. */ - inline Point(const Point &q) NOEXCEPT { decaf_255_point_copy(p,q.p); } + inline Point(const Point &q) NOEXCEPT { *this = q; } /** @brief Assignment. */ inline Point& operator=(const Point &q) NOEXCEPT { decaf_255_point_copy(p,q.p); return *this; } - /** @brief Destructor securely erases the point. */ + /** @brief Destructor securely zeorizes the point. */ inline ~Point() NOEXCEPT { decaf_255_point_destroy(p); } /** @brief Construct from RNG */ - inline explicit Point(SpongeRng &rng, bool uniform = true) NOEXCEPT; + inline explicit Point(Rng &rng, bool uniform = true) NOEXCEPT { + if (uniform) { + StackBuffer<2*HASH_BYTES> b(rng); + set_to_hash(b); + } else { + StackBuffer b(rng); + set_to_hash(b); + } + } /** * @brief Initialize from C++ fixed-length byte string. @@ -591,11 +412,30 @@ public: Point r((NOINIT())); decaf_255_base_double_scalarmul_non_secret(r.p,s_base.s,p,s.s); return r; } - inline Point& debugging_torque_in_place() { - decaf_255_point_debugging_torque(p,p); - return *this; + /** @brief Return a point equal to *this, whose internal data is rotated by a torsion element. */ + inline Point debugging_torque() const NOEXCEPT { + Point q; + decaf_255_point_debugging_torque(q.p,p); + return q; + } + + /** @brief Return a point equal to *this, whose internal data has a modified representation. */ + inline Point debugging_pscale(const uint8_t factor[/*SER_BYTES*/]) const NOEXCEPT { + Point q; + decaf_255_point_debugging_pscale(q.p,p,factor); + return q; + } + + /** @brief Return a point equal to *this, whose internal data has a randomized representation. */ + inline Point debugging_pscale(Rng &r) const NOEXCEPT { + StackBuffer sb(r); + return debugging_pscale(sb); } + /** + * Modify buffer so that Point::from_hash(Buffer) == *this, and return true; + * or leave buf unmodified and return false. + */ inline bool invert_elligator ( Buffer &buf, uint16_t hint ) const NOEXCEPT { @@ -611,13 +451,24 @@ public: if (buf.size() < HASH_BYTES) { ret &= decaf_memeq(&buf2[buf.size()], &buf2[HASH_BYTES], HASH_BYTES - buf.size()); } - memcpy(buf,buf2,(buf.size() < HASH_BYTES) ? buf.size() : HASH_BYTES); + if (ret) { + /* TODO: make this constant time?? */ + memcpy(buf,buf2,(buf.size() < HASH_BYTES) ? buf.size() : HASH_BYTES); + } decaf_bzero(buf2,sizeof(buf2)); return !!ret; } /** @brief Steganographically encode this */ - inline SecureBuffer steg_encode(SpongeRng &rng) const NOEXCEPT; + inline SecureBuffer steg_encode(Rng &rng) const throw(std::bad_alloc) { + SecureBuffer out(STEG_BYTES); + bool done; + do { + rng.read(out.slice(HASH_BYTES-1,STEG_BYTES-HASH_BYTES+1)); + done = invert_elligator(out, out[HASH_BYTES-1]); + } while (!done); + return out; + } /** @brief Return the base point */ static inline const Point base() NOEXCEPT { return Point(decaf_255_point_base); } @@ -661,7 +512,7 @@ private: inline const decaf_255_precomputed_s *get() const NOEXCEPT { return isMine ? ours.mine : ours.yours; } /** @endcond */ public: - /** Destructor securely erases the memory. */ + /** Destructor securely zeorizes the memory. */ inline ~Precomputed() NOEXCEPT { clear(); } /** @@ -744,7 +595,7 @@ public: #undef NOEXCEPT #undef EXPLICIT_CON -#undef GET_DATA +#undef FINAL } /* namespace decaf */ #endif /* __DECAF_255_HXX__ */ diff --git a/include/decaf_448.h b/include/decaf_448.h index 45deff8..317a088 100644 --- a/include/decaf_448.h +++ b/include/decaf_448.h @@ -25,33 +25,8 @@ #ifndef __DECAF_448_H__ #define __DECAF_448_H__ 1 -#include -#include - -/* Goldilocks' build flags default to hidden and stripping executables. */ -/** @cond internal */ -#if defined(DOXYGEN) && !defined(__attribute__) -#define __attribute__((x)) -#endif -#define API_VIS __attribute__((visibility("default"))) -#define NOINLINE __attribute__((noinline)) -#define WARN_UNUSED __attribute__((warn_unused_result)) -#define NONNULL1 __attribute__((nonnull(1))) -#define NONNULL2 __attribute__((nonnull(1,2))) -#define NONNULL3 __attribute__((nonnull(1,2,3))) -#define NONNULL4 __attribute__((nonnull(1,2,3,4))) -#define NONNULL5 __attribute__((nonnull(1,2,3,4,5))) - -/* Internal word types */ -#if (defined(__ILP64__) || defined(__amd64__) || defined(__x86_64__) || (((__UINT_FAST32_MAX__)>>30)>>30)) \ - && !defined(DECAF_FORCE_32_BIT) -#define DECAF_WORD_BITS 64 -typedef uint64_t decaf_word_t, decaf_bool_t; -typedef __uint128_t decaf_dword_t; -#else -#define DECAF_WORD_BITS 32 -typedef uint32_t decaf_word_t, decaf_bool_t; -typedef uint64_t decaf_dword_t; +#ifndef __DECAF_H__ +#error "include , not ." #endif #define DECAF_448_LIMBS (512/DECAF_WORD_BITS) @@ -89,13 +64,6 @@ typedef struct decaf_448_scalar_s { /** @endcond */ } decaf_448_scalar_t[1]; -/** DECAF_TRUE = -1 so that DECAF_TRUE & x = x */ -static const decaf_bool_t DECAF_TRUE = -(decaf_bool_t)1, DECAF_FALSE = 0; - -/** NB Success is -1, failure is 0. TODO: see if people would rather the reverse. */ -static const decaf_bool_t DECAF_SUCCESS = -(decaf_bool_t)1 /*DECAF_TRUE*/, - DECAF_FAILURE = 0 /*DECAF_FALSE*/; - /** A scalar equal to 1. */ extern const decaf_448_scalar_t decaf_448_scalar_one API_VIS; @@ -115,10 +83,6 @@ extern const decaf_448_point_t decaf_448_point_base API_VIS; /** Precomputed table for the base point on the curve. */ extern const struct decaf_448_precomputed_s *decaf_448_precomputed_base API_VIS; -#ifdef __cplusplus -extern "C" { -#endif - /** * @brief Read a scalar from wire format or from bytes. * @@ -633,19 +597,4 @@ void decaf_448_precomputed_destroy ( decaf_448_precomputed_s *pre ) NONNULL1 API_VIS; -/* TODO: functions to invert point_from_hash?? */ - -#undef API_VIS -#undef WARN_UNUSED -#undef NOINLINE -#undef NONNULL1 -#undef NONNULL2 -#undef NONNULL3 -#undef NONNULL4 -#undef NONNULL5 - -#ifdef __cplusplus -} /* extern "C" */ -#endif - #endif /* __DECAF_448_H__ */ diff --git a/include/secure_buffer.hxx b/include/secure_buffer.hxx new file mode 100644 index 0000000..0db90ab --- /dev/null +++ b/include/secure_buffer.hxx @@ -0,0 +1,302 @@ +/** + * @file secure_buffer.hxx + * @author Mike Hamburg + * + * @copyright + * Copyright (c) 2015 Cryptography Research, Inc. \n + * Released under the MIT License. See LICENSE.txt for license information. + * + * @brief C++ self-zeroizing buffer. + */ +#ifndef __DECAF_SECURE_BUFFER_HXX__ +#define __DECAF_SECURE_BUFFER_HXX__ 1 + +#include +#include + +/** @cond internal */ +#if __cplusplus >= 201103L +#define NOEXCEPT noexcept +#define DELETE = delete +#else +#define NOEXCEPT throw() +#define DELETE +#endif +/** @endcond */ + +namespace decaf { + +/**@cond internal*/ +/** Forward-declare sponge RNG object */ +class Buffer; +class TmpBuffer; +class SecureBuffer; +/**@endcond*/ + +/** @brief An exception for when crypto (ie point decode) has failed. */ +class CryptoException : public std::exception { +public: + /** @return "CryptoException" */ + virtual const char * what() const NOEXCEPT { return "CryptoException"; } +}; + +/** @brief An exception for when crypto (ie point decode) has failed. */ +class LengthException : public std::exception { +public: + /** @return "CryptoException" */ + virtual const char * what() const NOEXCEPT { return "LengthException"; } +}; + +/** @brief Passed to constructors to avoid (conservative) initialization */ +struct NOINIT {}; + +/** @brief Prototype of a random number generator. + * FUTURE: Are the noexcept methods really noexcept? What about self-reseeding RNGs? + */ +class Rng { +protected: + /** Empty initializer */ + Rng() {} + + /** Not copyable */ + Rng(const Rng &) DELETE; + + /** Not copyable */ + Rng &operator=(const Rng &) DELETE; + +public: + /** @brief Read into a Buffer */ + virtual inline void read(Buffer &buffer) NOEXCEPT = 0; + + /** @brief Read into a value-passed (eg temporary) TmpBuffer. */ + inline void read(TmpBuffer buffer) NOEXCEPT; + + /** @brief Read into a SecureBuffer. */ + inline SecureBuffer read(size_t length) throw(std::bad_alloc); +}; + +/** + * Securely zeorize contents of memory. + */ +static inline void really_bzero(void *data, size_t size) { decaf_bzero(data,size); } + +/** A reference to a block of data, which (when accessed through this base class) is const. */ +class Block { +protected: + unsigned char *data_; + size_t size_; + +public: + /** Empty init */ + inline Block() NOEXCEPT : data_(NULL), size_(0) {} + + /** Init from C string */ + inline Block(const char *data) NOEXCEPT : data_((unsigned char *)data), size_(strlen(data)) {} + + /** Unowned init */ + inline Block(const unsigned char *data, size_t size) NOEXCEPT : data_((unsigned char *)data), size_(size) {} + + /** Block from std::string */ + inline Block(const std::string &s) : data_( + #if __cplusplus >= 201103L + ((unsigned char *)&(s)[0]) + #else + ((unsigned char *)(s.data())) + #endif + ), 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_); + } + + /** Slice the buffer*/ + inline Block slice(size_t off, size_t length) const throw(LengthException) { + if (off > size() || length > size() - off) + throw LengthException(); + return Block(data()+off, length); + } + + /** @cond internal */ + inline decaf_bool_t operator>=(const Block &b) const NOEXCEPT DELETE; + inline decaf_bool_t operator<=(const Block &b) const NOEXCEPT DELETE; + inline decaf_bool_t operator> (const Block &b) const NOEXCEPT DELETE; + inline decaf_bool_t operator< (const Block &b) const NOEXCEPT DELETE; + /** @endcond */ + + /* Content-wise comparison; constant-time if they are the same length. */ + inline decaf_bool_t operator!=(const Block &b) const NOEXCEPT { + if (b.size() != size()) return true; + return ~decaf_memeq(b,*this,size()); + } + + /* Content-wise comparison; constant-time if they are the same length. */ + inline decaf_bool_t operator==(const Block &b) const NOEXCEPT { + return ~(*this == b); + } + + /** Virtual destructor for SecureBlock. TODO: probably means vtable? Make bool? */ + inline virtual ~Block() {}; +}; + +/** A reference to a writable block of data */ +class Buffer : public Block { +public: + /** Null init */ + inline Buffer() NOEXCEPT : 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_; } + + /** Slice the buffer*/ + inline TmpBuffer slice(size_t off, size_t length) throw(LengthException); + + /** Securely set the buffer to 0. */ + inline void zeorize() NOEXCEPT { really_bzero(data(),size()); } +}; + +/** A temporary reference to a writeable buffer, for converting C to C++. */ +class TmpBuffer : public Buffer { +public: + /** Unowned init */ + inline TmpBuffer(unsigned char *data, size_t size) NOEXCEPT : Buffer(data,size) {} +}; + +/** A fixed-size stack-allocated buffer (for NOEXCEPT semantics) */ +template class StackBuffer : public Buffer { +private: + uint8_t storage[Size]; +public: + /** New buffer initialized to zero. */ + inline StackBuffer() NOEXCEPT : Buffer(storage, Size) { memset(storage,0,Size); } + + /** New uninitialized buffer. */ + inline StackBuffer(const NOINIT &) NOEXCEPT : Buffer(storage, Size) { } + + /** New random buffer */ + inline StackBuffer(Rng &r) NOEXCEPT : Buffer(storage, Size) { r.read(*this); } + + /** Destroy the buffer */ + ~StackBuffer() NOEXCEPT { zeorize(); } +}; + +/** @cond internal */ +inline void Rng::read(TmpBuffer buffer) NOEXCEPT { read((Buffer &)buffer); } +/** @endcond */ + +/** 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); + } + + /** Construct from random */ + inline SecureBuffer(Rng &r, size_t size) NOEXCEPT { + data_ = new unsigned char[size_ = size]; + r.read(*this); + } + + /** 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 zeorizes data */ + ~SecureBuffer() NOEXCEPT { clear(); } + + /** Clear data */ + inline void clear() NOEXCEPT { + if (data_ == NULL) return; + zeorize(); + delete[] data_; + data_ = NULL; + size_ = 0; + } + +#if __cplusplus >= 201103L + /** Move constructor */ + inline SecureBuffer(SecureBuffer &&move) { *this = move; } + + /** Move non-constructor */ + inline SecureBuffer(Block &&move) { *this = (Block &)move; } + + /** Move-assign constructor. TODO: check that this actually gets used.*/ + 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 +}; + +/** @cond internal */ +TmpBuffer Buffer::slice(size_t off, size_t length) throw(LengthException) { + if (off > size() || length > size() - off) throw LengthException(); + return TmpBuffer(data()+off, length); +} + +inline SecureBuffer Rng::read(size_t length) throw(std::bad_alloc) { + SecureBuffer out(length); read(out); return out; +} +/** @endcond */ + +} /* namespace decaf */ + + +#undef NOEXCEPT +#undef DELETE + +#endif /* __DECAF_SECURE_BUFFER_HXX__ */ diff --git a/include/shake.hxx b/include/shake.hxx index 613442c..956e7f0 100644 --- a/include/shake.hxx +++ b/include/shake.hxx @@ -18,15 +18,9 @@ /** @cond internal */ #if __cplusplus >= 201103L -#define DELETE = delete #define NOEXCEPT noexcept -#define EXPLICIT_CON explicit -#define GET_DATA(str) ((const unsigned char *)&(str)[0]) #else -#define DELETE #define NOEXCEPT throw() -#define EXPLICIT_CON -#define GET_DATA(str) ((const unsigned char *)((str).data())) #endif /** @endcond */ @@ -143,9 +137,9 @@ public: /** @return "ProtocolException" */ virtual const char * what() const NOEXCEPT { return "ProtocolException"; } }; - + /** Sponge-based random-number generator */ -class SpongeRng : private KeccakSponge { +class SpongeRng : public Rng, private KeccakSponge { public: class RngException : public std::exception { private: @@ -172,46 +166,19 @@ public: } } - /** Read data to a buffer. */ - inline void read(Buffer &buffer) NOEXCEPT { spongerng_next(sp,buffer.data(),buffer.size()); } + using Rng::read; /** Read data to a buffer. */ - inline void read(TmpBuffer buffer) NOEXCEPT { read((Buffer &)buffer); } - - /** Read data to a C++ string - * @warning TODO Future versions of this function may throw RngException if a - * nondeterministic RNG fails a reseed. - */ - inline SecureBuffer read(size_t length) throw(std::bad_alloc) { - SecureBuffer out(length); read(out); return out; - } + virtual inline void read(Buffer &buffer) NOEXCEPT +#if __cplusplus >= 201103L + final +#endif + { spongerng_next(sp,buffer.data(),buffer.size()); } private: SpongeRng(const SpongeRng &) DELETE; SpongeRng &operator=(const SpongeRng &) DELETE; }; - -/**@cond internal*/ -inline Ed255::Scalar::Scalar(SpongeRng &rng) NOEXCEPT { - *this = rng.read(SER_BYTES); -} - -inline Ed255::Point::Point(SpongeRng &rng, bool uniform) NOEXCEPT { - SecureBuffer buffer((uniform ? 2 : 1) * HASH_BYTES); - rng.read(buffer); - set_to_hash(buffer); -} - - -inline SecureBuffer Ed255::Point::steg_encode(SpongeRng &rng) const NOEXCEPT { - SecureBuffer out(STEG_BYTES); - bool done; - do { - rng.read(out.slice(HASH_BYTES-1,STEG_BYTES-HASH_BYTES+1)); - done = invert_elligator(out, out[HASH_BYTES-1]); - } while (!done); - return out; -} /**@endcond*/ class Strobe : private KeccakSponge { @@ -374,8 +341,5 @@ public: } /* namespace decaf */ #undef NOEXCEPT -#undef EXPLICIT_CON -#undef GET_DATA -#undef DELETE #endif /* __SHAKE_HXX__ */ diff --git a/src/decaf_fast.c b/src/decaf_fast.c index d2f001b..260b220 100644 --- a/src/decaf_fast.c +++ b/src/decaf_fast.c @@ -29,6 +29,8 @@ #define point_t decaf_255_point_t #define precomputed_s decaf_255_precomputed_s #define SER_BYTES DECAF_255_SER_BYTES +#define gf_s gf_255_s +#define gf gf_255_t #if WBITS == 64 typedef __int128_t decaf_sdword_t; @@ -40,23 +42,14 @@ typedef int64_t decaf_sdword_t; #error "Only supporting 32- and 64-bit platforms right now" #endif -//static const int QUADRATIC_NONRESIDUE = -1; - #define sv static void #define snv static void __attribute__((noinline)) #define siv static inline void __attribute__((always_inline)) -static const gf ZERO = {{{0}}}, ONE = {{{1}}};//, TWO = {{{2}}}; +static const gf ZERO = {{{0}}}, ONE = {{{1}}}; static const int EDWARDS_D = -121665; - // PinkBikeShed: -89747; static const scalar_t sc_p = {{{ - /* PinkBikeShed: - SC_LIMB(0xb6b98fd8849faf35), - SC_LIMB(0x16241e6093b2ce59), - SC_LIMB(0), - SC_LIMB(0x2000000000000000) - */ SC_LIMB(0x5812631a5cf5d3ed), SC_LIMB(0x14def9dea2f79cd6), SC_LIMB(0), @@ -119,7 +112,7 @@ siv gf_isqrt(gf y, const gf x) { field_isr((field_t *)y, (const field_t *)x); } -/** Inverse. TODO: adapt to 5-mod-8 fields? */ +/** Inverse. */ sv gf_invert(gf y, const gf x) { gf t1, t2; gf_sqr(t1, x); // o^2 @@ -1271,6 +1264,24 @@ void API_NS(point_debugging_torque) ( #endif } +void API_NS(point_debugging_pscale) ( + point_t q, + const point_t p, + const uint8_t factor[SER_BYTES] +) { + gf gfac,tmp; + ignore_result(gf_deser(gfac,factor)); + cond_sel(gfac,gfac,ONE,gf_eq(gfac,ZERO)); + gf_mul(tmp,p->x,gfac); + gf_cpy(q->x,tmp); + gf_mul(tmp,p->y,gfac); + gf_cpy(q->y,tmp); + gf_mul(tmp,p->z,gfac); + gf_cpy(q->z,tmp); + gf_mul(tmp,p->t,gfac); + gf_cpy(q->t,tmp); +} + static void gf_batch_invert ( gf *__restrict__ out, /* const */ gf *in, diff --git a/test/test_decaf.cxx b/test/test_decaf.cxx index 6ac111c..a1811ec 100644 --- a/test/test_decaf.cxx +++ b/test/test_decaf.cxx @@ -167,6 +167,8 @@ static void test_elligator() { const int NHINTS = 1<<4; decaf::SecureBuffer *alts[NHINTS]; bool successes[NHINTS]; + decaf::SecureBuffer *alts2[NHINTS]; + bool successes2[NHINTS]; for (int i=0; i= Point::HASH_BYTES) b1[Point::HASH_BYTES-1] &= 0x7F; // FIXME MAGIC - Point s = Point::from_hash(b1); - for (int j=0; j<(i&3); j++) s.debugging_torque_in_place(); + + Point s = Point::from_hash(b1), ss=s; + for (int j=0; j<(i&3); j++) ss = ss.debugging_torque(); + + ss = ss.debugging_pscale(rng); bool good = false; for (int j=0; j Point::HASH_BYTES) memcpy(&(*alts[j])[Point::HASH_BYTES], &b1[Point::HASH_BYTES], len-Point::HASH_BYTES); - successes[j] = s.invert_elligator(*alts[j],j); + if (len > Point::HASH_BYTES) + memcpy(&(*alts2[j])[Point::HASH_BYTES], &b1[Point::HASH_BYTES], len-Point::HASH_BYTES); + + successes[j] = s.invert_elligator(*alts[j], j); + successes2[j] = ss.invert_elligator(*alts2[j],j); + + if (successes[j] != successes2[j] + || (successes[j] && successes2[j] && *alts[j] != *alts2[j]) + ) { + test.fail(); + printf(" Unscalable Elligator inversion: i=%d, hint=%d, s=%d,%d\n",i,j, + -int(successes[j]),-int(successes2[j])); + hexprint("x",b1); + hexprint("X",*alts[j]); + hexprint("X",*alts2[j]); + } if (successes[j]) { good = good || (b1 == *alts[j]); @@ -228,6 +249,8 @@ static void test_elligator() { for (int j=0; j