| @@ -2,7 +2,65 @@ | |||
| #ifndef __DECAF_H__ | |||
| #define __DECAF_H__ 1 | |||
| #include "decaf_255.h" // MAGIC | |||
| #include <stdint.h> | |||
| #include <sys/types.h> | |||
| /* 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__ */ | |||
| @@ -25,33 +25,8 @@ | |||
| #ifndef __DECAF_255_H__ | |||
| #define __DECAF_255_H__ 1 | |||
| #include <stdint.h> | |||
| #include <sys/types.h> | |||
| /* 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 <decaf.h>, not <decaf_255.h>." | |||
| #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__ */ | |||
| @@ -28,6 +28,7 @@ | |||
| #include <string.h> /* for memcpy */ | |||
| #include "decaf.h" | |||
| #include "secure_buffer.hxx" | |||
| #include <string> | |||
| #include <sys/types.h> | |||
| #include <limits.h> | |||
| @@ -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<SER_BYTES> 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<HASH_BYTES> 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<SER_BYTES> 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__ */ | |||
| @@ -25,33 +25,8 @@ | |||
| #ifndef __DECAF_448_H__ | |||
| #define __DECAF_448_H__ 1 | |||
| #include <stdint.h> | |||
| #include <sys/types.h> | |||
| /* 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 <decaf.h>, not <decaf_448.h>." | |||
| #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__ */ | |||
| @@ -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 <string> | |||
| #include <sys/types.h> | |||
| /** @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<size_t Size> 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__ */ | |||
| @@ -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__ */ | |||
| @@ -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, | |||
| @@ -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<NTESTS/10 && (test.passing_now || i < 100); i++) { | |||
| size_t len = (i % (2*Point::HASH_BYTES + 3)); // FIXME: 0 | |||
| @@ -174,17 +176,36 @@ static void test_elligator() { | |||
| if (i!=Point::HASH_BYTES) rng.read(b1); /* special test case */ | |||
| if (i==1) b1[0] = 1; /* special case test */ | |||
| if (len >= 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<NHINTS; j++) { | |||
| alts[j] = new decaf::SecureBuffer(len); | |||
| alts2[j] = new decaf::SecureBuffer(len); | |||
| if (len > 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<NHINTS; j++) { | |||
| delete alts[j]; | |||
| alts[j] = NULL; | |||
| delete alts2[j]; | |||
| alts2[j] = NULL; | |||
| } | |||
| Point t(rng); | |||
| @@ -260,9 +283,7 @@ static void test_ec() { | |||
| Point r = Point::from_hash(buffer); | |||
| point_check(test,p,q,r,0,0,p,Point((decaf::SecureBuffer)p),"round-trip"); | |||
| Point pp = p; | |||
| pp = p + q - q; | |||
| pp.debugging_torque_in_place(); | |||
| Point pp = p.debugging_torque().debugging_pscale(rng); | |||
| if (decaf::SecureBuffer(pp) != decaf::SecureBuffer(p)) { | |||
| test.fail(); | |||
| printf("Fail torque seq test\n"); | |||