@@ -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"); | |||