@@ -2,7 +2,65 @@ | |||||
#ifndef __DECAF_H__ | #ifndef __DECAF_H__ | ||||
#define __DECAF_H__ 1 | #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__ */ | #endif /* __DECAF_H__ */ | ||||
@@ -25,33 +25,8 @@ | |||||
#ifndef __DECAF_255_H__ | #ifndef __DECAF_255_H__ | ||||
#define __DECAF_255_H__ 1 | #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 | #endif | ||||
#define DECAF_255_LIMBS (320/DECAF_WORD_BITS) | #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) | #define DECAF_255_SCALAR_LIMBS (256/DECAF_WORD_BITS) | ||||
/** Galois field element internal structure */ | /** Galois field element internal structure */ | ||||
typedef struct gf_s { | |||||
typedef struct gf_255_s { | |||||
decaf_word_t limb[DECAF_255_LIMBS]; | decaf_word_t limb[DECAF_255_LIMBS]; | ||||
} gf_s, gf[1]; | |||||
} gf_255_s, gf_255_t[1]; | |||||
/** @endcond */ | /** @endcond */ | ||||
/** Number of bytes in a serialized point. */ | /** Number of bytes in a serialized point. */ | ||||
@@ -71,7 +46,7 @@ typedef struct gf_s { | |||||
#define DECAF_255_SCALAR_BYTES 32 | #define DECAF_255_SCALAR_BYTES 32 | ||||
/** Twisted Edwards (-1,d-1) extended homogeneous coordinates */ | /** 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. */ | /** Precomputed table based on a point. Can be trivial implementation. */ | ||||
struct decaf_255_precomputed_s; | struct decaf_255_precomputed_s; | ||||
@@ -89,13 +64,6 @@ typedef struct decaf_255_scalar_s { | |||||
/** @endcond */ | /** @endcond */ | ||||
} decaf_255_scalar_t[1]; | } 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. */ | /** A scalar equal to 1. */ | ||||
extern const decaf_255_scalar_t decaf_255_scalar_one API_VIS; | 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. */ | /** Precomputed table for the base point on the curve. */ | ||||
extern const struct decaf_255_precomputed_s *decaf_255_precomputed_base API_VIS; | 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. | * @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; | ) 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 [out] q The point to torque. | ||||
* @param [in] p 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 | const decaf_255_point_t p | ||||
) API_VIS NONNULL2 NOINLINE; | ) 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. | * @brief Almost-Elligator-like hash to curve. | ||||
* | * | ||||
@@ -624,19 +604,4 @@ void decaf_255_precomputed_destroy ( | |||||
decaf_255_precomputed_s *pre | decaf_255_precomputed_s *pre | ||||
) NONNULL1 API_VIS; | ) 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__ */ | #endif /* __DECAF_255_H__ */ |
@@ -28,6 +28,7 @@ | |||||
#include <string.h> /* for memcpy */ | #include <string.h> /* for memcpy */ | ||||
#include "decaf.h" | #include "decaf.h" | ||||
#include "secure_buffer.hxx" | |||||
#include <string> | #include <string> | ||||
#include <sys/types.h> | #include <sys/types.h> | ||||
#include <limits.h> | #include <limits.h> | ||||
@@ -39,209 +40,18 @@ | |||||
#if __cplusplus >= 201103L | #if __cplusplus >= 201103L | ||||
#define NOEXCEPT noexcept | #define NOEXCEPT noexcept | ||||
#define EXPLICIT_CON explicit | #define EXPLICIT_CON explicit | ||||
#define GET_DATA(str) ((const unsigned char *)&(str)[0]) | |||||
#define FINAL final | |||||
#define DELETE = delete | |||||
#else | #else | ||||
#define NOEXCEPT throw() | #define NOEXCEPT throw() | ||||
#define EXPLICIT_CON | #define EXPLICIT_CON | ||||
#define GET_DATA(str) ((const unsigned char *)((str).data())) | |||||
#define FINAL | |||||
#define DELETE | |||||
#endif | #endif | ||||
/** @endcond */ | /** @endcond */ | ||||
namespace decaf { | 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. | * @brief Ed255-Goldilocks/Decaf instantiation of group. | ||||
*/ | */ | ||||
@@ -274,7 +84,10 @@ public: | |||||
inline Scalar(const int w) NOEXCEPT { *this = w; } | inline Scalar(const int w) NOEXCEPT { *this = w; } | ||||
/** @brief Construct from RNG */ | /** @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. */ | /** @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); } | 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; | return *this; | ||||
} | } | ||||
/** Destructor securely erases the scalar. */ | |||||
/** Destructor securely zeorizes the scalar. */ | |||||
inline ~Scalar() NOEXCEPT { decaf_255_scalar_destroy(s); } | inline ~Scalar() NOEXCEPT { decaf_255_scalar_destroy(s); } | ||||
/** @brief Assign from arbitrary-length little-endian byte sequence in a Block. */ | /** @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); } | inline Point(const decaf_255_point_t &q = decaf_255_point_identity) NOEXCEPT { decaf_255_point_copy(p,q); } | ||||
/** @brief Copy constructor. */ | /** @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. */ | /** @brief Assignment. */ | ||||
inline Point& operator=(const Point &q) NOEXCEPT { decaf_255_point_copy(p,q.p); return *this; } | 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); } | inline ~Point() NOEXCEPT { decaf_255_point_destroy(p); } | ||||
/** @brief Construct from RNG */ | /** @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. | * @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; | 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 ( | inline bool invert_elligator ( | ||||
Buffer &buf, uint16_t hint | Buffer &buf, uint16_t hint | ||||
) const NOEXCEPT { | ) const NOEXCEPT { | ||||
@@ -611,13 +451,24 @@ public: | |||||
if (buf.size() < HASH_BYTES) { | if (buf.size() < HASH_BYTES) { | ||||
ret &= decaf_memeq(&buf2[buf.size()], &buf2[HASH_BYTES], HASH_BYTES - buf.size()); | 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)); | decaf_bzero(buf2,sizeof(buf2)); | ||||
return !!ret; | return !!ret; | ||||
} | } | ||||
/** @brief Steganographically encode this */ | /** @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 */ | /** @brief Return the base point */ | ||||
static inline const Point base() NOEXCEPT { return Point(decaf_255_point_base); } | 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; } | inline const decaf_255_precomputed_s *get() const NOEXCEPT { return isMine ? ours.mine : ours.yours; } | ||||
/** @endcond */ | /** @endcond */ | ||||
public: | public: | ||||
/** Destructor securely erases the memory. */ | |||||
/** Destructor securely zeorizes the memory. */ | |||||
inline ~Precomputed() NOEXCEPT { clear(); } | inline ~Precomputed() NOEXCEPT { clear(); } | ||||
/** | /** | ||||
@@ -744,7 +595,7 @@ public: | |||||
#undef NOEXCEPT | #undef NOEXCEPT | ||||
#undef EXPLICIT_CON | #undef EXPLICIT_CON | ||||
#undef GET_DATA | |||||
#undef FINAL | |||||
} /* namespace decaf */ | } /* namespace decaf */ | ||||
#endif /* __DECAF_255_HXX__ */ | #endif /* __DECAF_255_HXX__ */ |
@@ -25,33 +25,8 @@ | |||||
#ifndef __DECAF_448_H__ | #ifndef __DECAF_448_H__ | ||||
#define __DECAF_448_H__ 1 | #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 | #endif | ||||
#define DECAF_448_LIMBS (512/DECAF_WORD_BITS) | #define DECAF_448_LIMBS (512/DECAF_WORD_BITS) | ||||
@@ -89,13 +64,6 @@ typedef struct decaf_448_scalar_s { | |||||
/** @endcond */ | /** @endcond */ | ||||
} decaf_448_scalar_t[1]; | } 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. */ | /** A scalar equal to 1. */ | ||||
extern const decaf_448_scalar_t decaf_448_scalar_one API_VIS; | 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. */ | /** Precomputed table for the base point on the curve. */ | ||||
extern const struct decaf_448_precomputed_s *decaf_448_precomputed_base API_VIS; | 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. | * @brief Read a scalar from wire format or from bytes. | ||||
* | * | ||||
@@ -633,19 +597,4 @@ void decaf_448_precomputed_destroy ( | |||||
decaf_448_precomputed_s *pre | decaf_448_precomputed_s *pre | ||||
) NONNULL1 API_VIS; | ) 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__ */ | #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 */ | /** @cond internal */ | ||||
#if __cplusplus >= 201103L | #if __cplusplus >= 201103L | ||||
#define DELETE = delete | |||||
#define NOEXCEPT noexcept | #define NOEXCEPT noexcept | ||||
#define EXPLICIT_CON explicit | |||||
#define GET_DATA(str) ((const unsigned char *)&(str)[0]) | |||||
#else | #else | ||||
#define DELETE | |||||
#define NOEXCEPT throw() | #define NOEXCEPT throw() | ||||
#define EXPLICIT_CON | |||||
#define GET_DATA(str) ((const unsigned char *)((str).data())) | |||||
#endif | #endif | ||||
/** @endcond */ | /** @endcond */ | ||||
@@ -143,9 +137,9 @@ public: | |||||
/** @return "ProtocolException" */ | /** @return "ProtocolException" */ | ||||
virtual const char * what() const NOEXCEPT { return "ProtocolException"; } | virtual const char * what() const NOEXCEPT { return "ProtocolException"; } | ||||
}; | }; | ||||
/** Sponge-based random-number generator */ | /** Sponge-based random-number generator */ | ||||
class SpongeRng : private KeccakSponge { | |||||
class SpongeRng : public Rng, private KeccakSponge { | |||||
public: | public: | ||||
class RngException : public std::exception { | class RngException : public std::exception { | ||||
private: | 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. */ | /** 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: | private: | ||||
SpongeRng(const SpongeRng &) DELETE; | SpongeRng(const SpongeRng &) DELETE; | ||||
SpongeRng &operator=(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*/ | /**@endcond*/ | ||||
class Strobe : private KeccakSponge { | class Strobe : private KeccakSponge { | ||||
@@ -374,8 +341,5 @@ public: | |||||
} /* namespace decaf */ | } /* namespace decaf */ | ||||
#undef NOEXCEPT | #undef NOEXCEPT | ||||
#undef EXPLICIT_CON | |||||
#undef GET_DATA | |||||
#undef DELETE | |||||
#endif /* __SHAKE_HXX__ */ | #endif /* __SHAKE_HXX__ */ |
@@ -29,6 +29,8 @@ | |||||
#define point_t decaf_255_point_t | #define point_t decaf_255_point_t | ||||
#define precomputed_s decaf_255_precomputed_s | #define precomputed_s decaf_255_precomputed_s | ||||
#define SER_BYTES DECAF_255_SER_BYTES | #define SER_BYTES DECAF_255_SER_BYTES | ||||
#define gf_s gf_255_s | |||||
#define gf gf_255_t | |||||
#if WBITS == 64 | #if WBITS == 64 | ||||
typedef __int128_t decaf_sdword_t; | 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" | #error "Only supporting 32- and 64-bit platforms right now" | ||||
#endif | #endif | ||||
//static const int QUADRATIC_NONRESIDUE = -1; | |||||
#define sv static void | #define sv static void | ||||
#define snv static void __attribute__((noinline)) | #define snv static void __attribute__((noinline)) | ||||
#define siv static inline void __attribute__((always_inline)) | #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; | static const int EDWARDS_D = -121665; | ||||
// PinkBikeShed: -89747; | |||||
static const scalar_t sc_p = {{{ | static const scalar_t sc_p = {{{ | ||||
/* PinkBikeShed: | |||||
SC_LIMB(0xb6b98fd8849faf35), | |||||
SC_LIMB(0x16241e6093b2ce59), | |||||
SC_LIMB(0), | |||||
SC_LIMB(0x2000000000000000) | |||||
*/ | |||||
SC_LIMB(0x5812631a5cf5d3ed), | SC_LIMB(0x5812631a5cf5d3ed), | ||||
SC_LIMB(0x14def9dea2f79cd6), | SC_LIMB(0x14def9dea2f79cd6), | ||||
SC_LIMB(0), | SC_LIMB(0), | ||||
@@ -119,7 +112,7 @@ siv gf_isqrt(gf y, const gf x) { | |||||
field_isr((field_t *)y, (const field_t *)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) { | sv gf_invert(gf y, const gf x) { | ||||
gf t1, t2; | gf t1, t2; | ||||
gf_sqr(t1, x); // o^2 | gf_sqr(t1, x); // o^2 | ||||
@@ -1271,6 +1264,24 @@ void API_NS(point_debugging_torque) ( | |||||
#endif | #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 ( | static void gf_batch_invert ( | ||||
gf *__restrict__ out, | gf *__restrict__ out, | ||||
/* const */ gf *in, | /* const */ gf *in, | ||||
@@ -167,6 +167,8 @@ static void test_elligator() { | |||||
const int NHINTS = 1<<4; | const int NHINTS = 1<<4; | ||||
decaf::SecureBuffer *alts[NHINTS]; | decaf::SecureBuffer *alts[NHINTS]; | ||||
bool successes[NHINTS]; | bool successes[NHINTS]; | ||||
decaf::SecureBuffer *alts2[NHINTS]; | |||||
bool successes2[NHINTS]; | |||||
for (int i=0; i<NTESTS/10 && (test.passing_now || i < 100); i++) { | for (int i=0; i<NTESTS/10 && (test.passing_now || i < 100); i++) { | ||||
size_t len = (i % (2*Point::HASH_BYTES + 3)); // FIXME: 0 | 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!=Point::HASH_BYTES) rng.read(b1); /* special test case */ | ||||
if (i==1) b1[0] = 1; /* special case test */ | if (i==1) b1[0] = 1; /* special case test */ | ||||
if (len >= Point::HASH_BYTES) b1[Point::HASH_BYTES-1] &= 0x7F; // FIXME MAGIC | 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; | bool good = false; | ||||
for (int j=0; j<NHINTS; j++) { | for (int j=0; j<NHINTS; j++) { | ||||
alts[j] = new decaf::SecureBuffer(len); | alts[j] = new decaf::SecureBuffer(len); | ||||
alts2[j] = new decaf::SecureBuffer(len); | |||||
if (len > Point::HASH_BYTES) | if (len > Point::HASH_BYTES) | ||||
memcpy(&(*alts[j])[Point::HASH_BYTES], &b1[Point::HASH_BYTES], 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]) { | if (successes[j]) { | ||||
good = good || (b1 == *alts[j]); | good = good || (b1 == *alts[j]); | ||||
@@ -228,6 +249,8 @@ static void test_elligator() { | |||||
for (int j=0; j<NHINTS; j++) { | for (int j=0; j<NHINTS; j++) { | ||||
delete alts[j]; | delete alts[j]; | ||||
alts[j] = NULL; | alts[j] = NULL; | ||||
delete alts2[j]; | |||||
alts2[j] = NULL; | |||||
} | } | ||||
Point t(rng); | Point t(rng); | ||||
@@ -260,9 +283,7 @@ static void test_ec() { | |||||
Point r = Point::from_hash(buffer); | Point r = Point::from_hash(buffer); | ||||
point_check(test,p,q,r,0,0,p,Point((decaf::SecureBuffer)p),"round-trip"); | 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)) { | if (decaf::SecureBuffer(pp) != decaf::SecureBuffer(p)) { | ||||
test.fail(); | test.fail(); | ||||
printf("Fail torque seq test\n"); | printf("Fail torque seq test\n"); | ||||