diff --git a/Makefile b/Makefile index dbb8568..6be457f 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ INCFLAGS = -Isrc/include -Iinclude -Isrc/$(FIELD) -Isrc/$(FIELD)/$(ARCH) LANGFLAGS = -std=c99 -fno-strict-aliasing LANGXXFLAGS = -fno-strict-aliasing GENFLAGS = -ffunction-sections -fdata-sections -fvisibility=hidden -fomit-frame-pointer -fPIC -OFLAGS = -O3 +OFLAGS ?= -O3 TODAY = $(shell date "+%Y-%m-%d") diff --git a/include/decaf.hxx b/include/decaf.hxx index 788c2ca..914b7d0 100644 --- a/include/decaf.hxx +++ b/include/decaf.hxx @@ -234,480 +234,683 @@ class SpongeRng; /**@endcond*/ +template struct WrappedTypes; + /** * @brief Group with prime order. * @todo Move declarations of functions up here? */ -template struct EcGroup; - -/** - * @brief Ed448-Goldilocks/Decaf instantiation of group. - */ -template<> struct EcGroup { - -/** @cond internal */ -class Point; -class Precomputed; -/** @endcond */ - -/** - * @brief A scalar modulo the curve order. - * Supports the usual arithmetic operations, all in constant time. - */ -class Scalar { -public: - /** @brief Size of a serialized element */ - static const size_t SER_BYTES = DECAF_448_SCALAR_BYTES; - - /** @brief access to the underlying scalar object */ - decaf_448_scalar_t s; - - /** @brief Don't initialize. */ - inline Scalar(const NOINIT &) {} - - /** @brief Set to an unsigned word */ - inline Scalar(const decaf_word_t w) NOEXCEPT { *this = w; } +template struct EcGroup { + /** @cond internal */ + class Point; + class Precomputed; + /** @endcond */ - /** @brief Set to a signed word */ - inline Scalar(const int w) NOEXCEPT { *this = w; } - - /** @brief Construct from RNG */ - inline explicit Scalar(SpongeRng &rng); - - /** @brief Construct from decaf_scalar_t object. */ - inline Scalar(const decaf_448_scalar_t &t = decaf_448_scalar_zero) NOEXCEPT { decaf_448_scalar_copy(s,t); } - - /** @brief Copy constructor. */ - inline Scalar(const Scalar &x) NOEXCEPT { *this = x; } - - /** @brief Construct from arbitrary-length little-endian byte sequence. */ - inline Scalar(const Block &buffer) NOEXCEPT { *this = buffer; } - - /** @brief Assignment. */ - inline Scalar& operator=(const Scalar &x) NOEXCEPT { decaf_448_scalar_copy(s,x.s); return *this; } - - /** @brief Assign from unsigned word. */ - inline Scalar& operator=(decaf_word_t w) NOEXCEPT { decaf_448_scalar_set(s,w); return *this; } - - /** @brief Assign from signed int. */ - inline Scalar& operator=(int w) { - Scalar t(-(decaf_word_t)INT_MIN); - decaf_448_scalar_set(s,(decaf_word_t)w - (decaf_word_t)INT_MIN); - *this -= t; - return *this; - } - - /** Destructor securely erases the scalar. */ - inline ~Scalar() NOEXCEPT { decaf_448_scalar_destroy(s); } - - /** @brief Assign from arbitrary-length little-endian byte sequence in a Block. */ - inline Scalar &operator=(const Block &bl) NOEXCEPT { - decaf_448_scalar_decode_long(s,bl.data(),bl.size()); return *this; - } - - /** - * @brief Decode from correct-length little-endian byte sequence. - * @return DECAF_FAILURE if the scalar is greater than or equal to the group order q. - */ - static inline decaf_bool_t __attribute__((warn_unused_result)) decode ( - Scalar &sc, const unsigned char buffer[SER_BYTES] - ) NOEXCEPT { - return decaf_448_scalar_decode(sc.s,buffer); - } - - /** @brief Decode from correct-length little-endian byte sequence in C++ string. */ - static inline decaf_bool_t __attribute__((warn_unused_result)) decode ( - Scalar &sc, const Block &buffer - ) NOEXCEPT { - if (buffer.size() != SER_BYTES) return DECAF_FAILURE; - return decaf_448_scalar_decode(sc.s,buffer); - } - - /** @brief Encode to fixed-length string */ - inline EXPLICIT_CON operator SecureBuffer() const NOEXCEPT { - SecureBuffer buf(SER_BYTES); decaf_448_scalar_encode(buf,s); return buf; - } - - /** @brief Encode to fixed-length buffer */ - inline void encode(unsigned char buffer[SER_BYTES]) const NOEXCEPT{ - decaf_448_scalar_encode(buffer, s); - } - - /** Add. */ - inline Scalar operator+ (const Scalar &q) const NOEXCEPT { Scalar r((NOINIT())); decaf_448_scalar_add(r.s,s,q.s); return r; } - - /** Add to this. */ - inline Scalar &operator+=(const Scalar &q) NOEXCEPT { decaf_448_scalar_add(s,s,q.s); return *this; } - - /** Subtract. */ - inline Scalar operator- (const Scalar &q) const NOEXCEPT { Scalar r((NOINIT())); decaf_448_scalar_sub(r.s,s,q.s); return r; } - - /** Subtract from this. */ - inline Scalar &operator-=(const Scalar &q) NOEXCEPT { decaf_448_scalar_sub(s,s,q.s); return *this; } + /** + * @brief A scalar modulo the curve order. + * Supports the usual arithmetic operations, all in constant time. + */ + class Scalar { + private: + /** @cond internal */ + /** @brief Wrapped C object */ + friend class Point; + friend class Precomputed; + typedef typename WrappedTypes::Scalar Wrapped; + static const Wrapped &ZERO, &ONE; + static inline void add3(Wrapped, const Wrapped, const Wrapped) NOEXCEPT; + static inline void setu(Wrapped, decaf_word_t) NOEXCEPT; + static inline void sub3(Wrapped, const Wrapped, const Wrapped) NOEXCEPT; + static inline void mul3(Wrapped, const Wrapped, const Wrapped) NOEXCEPT; + static inline void dl3(Wrapped, const unsigned char *buffer, size_t size) NOEXCEPT; + static inline decaf_word_t eq2(const Wrapped, const Wrapped) NOEXCEPT; + static inline void assign2(Wrapped, const Wrapped) NOEXCEPT; + static inline void inv2(Wrapped, const Wrapped) NOEXCEPT; + /** @endcond */ + + public: + /** @brief Size of a serialized element */ + static const size_t SER_BYTES; + + /** @brief access to the Wrapped scalar object */ + Wrapped s; + + /** @brief Don't initialize. */ + inline Scalar(const NOINIT &) {} + + /** @brief Set to an unsigned word */ + inline Scalar(const decaf_word_t w = 0) NOEXCEPT { *this = w; } + + /** @brief Set to a signed word */ + inline Scalar(const int w) NOEXCEPT { *this = w; } + + /** @brief Construct from RNG */ + inline explicit Scalar(SpongeRng &rng) NOEXCEPT; + + /** @brief Construct from decaf_scalar_t object. */ + inline Scalar(const Wrapped &x) NOEXCEPT { *this = x; } + + /** @brief Copy constructor. */ + inline Scalar(const Scalar &x) NOEXCEPT { *this = x; } + + /** @brief Construct from arbitrary-length little-endian byte sequence. */ + inline Scalar(const Block &buffer) NOEXCEPT { *this = buffer; } + + /** Destructor securely erases the scalar. */ + inline ~Scalar() NOEXCEPT; + + /** @brief Assign from arbitrary-length little-endian byte sequence in a Block. */ + inline Scalar &operator=(const Block &bl) NOEXCEPT { dl3(s, bl, bl.size()); return *this; } + + /** @brief Assignment. */ + inline Scalar &operator=(const Scalar &t) NOEXCEPT { assign2(s, t.s); return *this; } + + /** @brief Assignment. */ + inline Scalar &operator=(decaf_word_t w) NOEXCEPT { setu(s, w); return *this; } + + /** @brief Assignment. */ + inline Scalar &operator=(int w) NOEXCEPT { + Scalar t(-(decaf_word_t)INT_MIN); + setu(s,(decaf_word_t)w - (decaf_word_t)INT_MIN); + *this -= t; + return *this; + } - /** Multiply */ - inline Scalar operator* (const Scalar &q) const NOEXCEPT { Scalar r((NOINIT())); decaf_448_scalar_mul(r.s,s,q.s); return r; } + /** + * @brief Decode from correct-length little-endian byte sequence. + * @return DECAF_FAILURE if the scalar is greater than or equal to the group order q. + */ + static inline decaf_bool_t __attribute__((warn_unused_result)) decode ( + Scalar &sc, const unsigned char buffer[/*SER_BYTES*/] // TODO + ) NOEXCEPT; + + /** @brief Decode from correct-length little-endian byte sequence in C++ string. */ + static inline decaf_bool_t __attribute__((warn_unused_result)) decode ( + Scalar &sc, const Block &buffer + ) NOEXCEPT { + if (buffer.size() != SER_BYTES) return DECAF_FAILURE; + return decode(sc.s,(const unsigned char *)buffer); + } - /** Multiply into this. */ - inline Scalar &operator*=(const Scalar &q) NOEXCEPT { decaf_448_scalar_mul(s,s,q.s); return *this; } + /** @brief Encode to fixed-length buffer */ + inline void encode(unsigned char buffer[/*SER_BYTES*/]) const NOEXCEPT; - /** Negate */ - inline Scalar operator- () const NOEXCEPT { Scalar r((NOINIT())); decaf_448_scalar_sub(r.s,decaf_448_scalar_zero,s); return r; } + /** @brief Encode to fixed-length string */ + inline EXPLICIT_CON operator SecureBuffer() const NOEXCEPT { + SecureBuffer buf(SER_BYTES); encode((unsigned char *)buf,s); return buf; + } - /** @brief Invert with Fermat's Little Theorem (slow!). If *this == 0, return 0. */ - inline Scalar inverse() const NOEXCEPT { Scalar r; decaf_448_scalar_invert(r.s,s); return r; } + public: + /** Add. */ + inline Scalar operator+ (const Scalar &q) const NOEXCEPT { Scalar r((NOINIT())); add3(r.s,s,q.s); return r; } - /** @brief Divide by inverting q. If q == 0, return 0. */ - inline Scalar operator/ (const Scalar &q) const NOEXCEPT { return *this * q.inverse(); } + /** Add to this. */ + inline Scalar &operator+=(const Scalar &q) NOEXCEPT { add3(s,s,q.s); return *this; } - /** @brief Divide by inverting q. If q == 0, return 0. */ - inline Scalar &operator/=(const Scalar &q) NOEXCEPT { return *this *= q.inverse(); } + /** Subtract. */ + inline Scalar operator- (const Scalar &q) const NOEXCEPT { Scalar r((NOINIT())); sub3(r.s,s,q.s); return r; } - /** @brief Compare in constant time */ - inline bool operator!=(const Scalar &q) const NOEXCEPT { return !(*this == q); } + /** Subtract from this. */ + inline Scalar &operator-=(const Scalar &q) NOEXCEPT { sub3(s,s,q.s); return *this; } - /** @brief Compare in constant time */ - inline bool operator==(const Scalar &q) const NOEXCEPT { return !!decaf_448_scalar_eq(s,q.s); } + /** Multiply */ + inline Scalar operator* (const Scalar &q) const NOEXCEPT { Scalar r((NOINIT())); mul3(r.s,s,q.s); return r; } - /** @brief Scalarmul with scalar on left. */ - inline Point operator* (const Point &q) const NOEXCEPT { return q * (*this); } + /** Multiply into this. */ + inline Scalar &operator*=(const Scalar &q) NOEXCEPT { mul3(s,s,q.s); return *this; } - /** @brief Scalarmul-precomputed with scalar on left. */ - inline Point operator* (const Precomputed &q) const NOEXCEPT { return q * (*this); } + /** Negate */ + inline Scalar operator- () const NOEXCEPT { Scalar r((NOINIT())); sub3(r.s,ZERO,s); return r; } - /** @brief Direct scalar multiplication. */ - inline SecureBuffer direct_scalarmul( - const Block &in, - decaf_bool_t allow_identity=DECAF_FALSE, - decaf_bool_t short_circuit=DECAF_TRUE - ) const throw(CryptoException) { - SecureBuffer out(/*FIXME Point::*/SER_BYTES); - if (!decaf_448_direct_scalarmul(out, in.data(), s, allow_identity, short_circuit)) - throw CryptoException(); - return out; - } -}; - -/** - * @brief Element of prime-order group. - */ -class Point { -public: - /** @brief Size of a serialized element */ - static const size_t SER_BYTES = DECAF_448_SER_BYTES; + /** @brief Invert with Fermat's Little Theorem (slow!). If *this == 0, return 0. */ + inline Scalar inverse() const NOEXCEPT { Scalar q((NOINIT())); inv2(q.s,s); return q; } - /** @brief Bytes required for hash */ - static const size_t HASH_BYTES = DECAF_448_SER_BYTES; + /** @brief Divide by inverting q. If q == 0, return 0. */ + inline Scalar operator/ (const Scalar &q) const NOEXCEPT { return *this * q.inverse(); } - /** The c-level object. */ - decaf_448_point_t p; + /** @brief Divide by inverting q. If q == 0, return 0. */ + inline Scalar &operator/=(const Scalar &q) NOEXCEPT { return *this *= q.inverse(); } - /** @brief Don't initialize. */ - inline Point(const NOINIT &) {} + /** @brief Compare in constant time */ + inline bool operator==(const Scalar &q) const NOEXCEPT { return !!eq2(s,q.s); } - /** @brief Constructor sets to identity by default. */ - inline Point(const decaf_448_point_t &q = decaf_448_point_identity) { decaf_448_point_copy(p,q); } + /** @brief Compare in constant time */ + inline bool operator!=(const Scalar &q) const NOEXCEPT { return !(*this == q); } - /** @brief Copy constructor. */ - inline Point(const Point &q) { decaf_448_point_copy(p,q.p); } + /** @brief Scalarmul with scalar on left. */ + inline Point operator* (const Point &q) const NOEXCEPT { return q * (*this); } - /** @brief Assignment. */ - inline Point& operator=(const Point &q) { decaf_448_point_copy(p,q.p); return *this; } + /** @brief Scalarmul-precomputed with scalar on left. */ + inline Point operator* (const Precomputed &q) const NOEXCEPT { return q * (*this); } - /** @brief Destructor securely erases the point. */ - inline ~Point() { decaf_448_point_destroy(p); } + /** @brief Direct scalar multiplication. */ + inline SecureBuffer direct_scalarmul( + const Block &in, + decaf_bool_t allow_identity=DECAF_FALSE, + decaf_bool_t short_circuit=DECAF_TRUE + ) const throw(CryptoException); + }; - /** @brief Construct from RNG */ - inline explicit Point(SpongeRng &rng, bool uniform = true); - /** - * @brief Initialize from C++ fixed-length byte string. - * The all-zero string maps to the identity. - * - * @throw CryptoException the string was the wrong length, or wasn't the encoding of a point, - * or was the identity and allow_identity was DECAF_FALSE. - */ - inline explicit Point(const Block &s, decaf_bool_t allow_identity=DECAF_TRUE) throw(CryptoException) { - if (!decode(*this,s,allow_identity)) throw CryptoException(); - } - /** - * @brief Initialize from C fixed-length byte string. - * The all-zero string maps to the identity. - * - * @throw CryptoException the string was the wrong length, or wasn't the encoding of a point, - * or was the identity and allow_identity was DECAF_FALSE. + * @brief Element of prime-order group. */ - inline explicit Point(const unsigned char buffer[SER_BYTES], decaf_bool_t allow_identity=DECAF_TRUE) - throw(CryptoException) { if (!decode(*this,buffer,allow_identity)) throw CryptoException(); } - - /** - * @brief Initialize from C fixed-length byte string. - * The all-zero string maps to the identity. - * - * @retval DECAF_SUCCESS the string was successfully decoded. - * @return DECAF_FAILURE the string wasn't the encoding of a point, or was the identity - * and allow_identity was DECAF_FALSE. Contents of the buffer are undefined. - */ - static inline decaf_bool_t __attribute__((warn_unused_result)) decode ( - Point &p, const unsigned char buffer[SER_BYTES], decaf_bool_t allow_identity=DECAF_TRUE - ) NOEXCEPT { - return decaf_448_point_decode(p.p,buffer,allow_identity); - } - - /** - * @brief Initialize from C++ fixed-length byte string. - * The all-zero string maps to the identity. - * - * @retval DECAF_SUCCESS the string was successfully decoded. - * @return DECAF_FAILURE the string was the wrong length, or wasn't the encoding of a point, - * or was the identity and allow_identity was DECAF_FALSE. Contents of the buffer are undefined. - */ - static inline decaf_bool_t __attribute__((warn_unused_result)) decode ( - Point &p, const Block &buffer, decaf_bool_t allow_identity=DECAF_TRUE - ) NOEXCEPT { - if (buffer.size() != SER_BYTES) return DECAF_FAILURE; - return decaf_448_point_decode(p.p,buffer.data(),allow_identity); - } + class Point { + private: + /** @cond internal */ + typedef typename WrappedTypes::Point Wrapped; + friend class Scalar; + friend class Precomputed; + static const Wrapped &IDENTITY, &GENERATOR; + static inline void add3(Wrapped, const Wrapped, const Wrapped) NOEXCEPT; + static inline void sub3(Wrapped, const Wrapped, const Wrapped) NOEXCEPT; + static inline void dbl2(Wrapped, const Wrapped) NOEXCEPT; + static inline void neg2(Wrapped, const Wrapped) NOEXCEPT; + static inline decaf_word_t eq2(const Wrapped, const Wrapped) NOEXCEPT; + static inline void assign2(Wrapped, const Wrapped) NOEXCEPT; + static inline void sm3(Wrapped, const Wrapped, const typename Scalar::Wrapped) NOEXCEPT; + static inline void dsm5( + Wrapped, + const Wrapped, const typename Scalar::Wrapped, + const Wrapped, const typename Scalar::Wrapped + ) NOEXCEPT; + static inline void dsmns( + Wrapped, + const typename Scalar::Wrapped, + const Wrapped, const typename Scalar::Wrapped + ) NOEXCEPT; + /** @endcond */ + + public: + /** @brief Size of a serialized element */ + static const size_t SER_BYTES; + + /** @brief Bytes required for hash */ + static const size_t HASH_BYTES; + + /** The c-level object. */ + Wrapped p; + + /** @brief Don't initialize. */ + inline Point(const NOINIT &) {} + + /** @brief Constructor sets to identity by default. */ + inline Point(const decaf_448_point_t &q = IDENTITY) { *this = q; } + + /** @brief Copy constructor. */ + inline Point(const Point &q) { *this = q; } + + /** @brief Assignment. */ + inline Point& operator=(const Point &q) NOEXCEPT { assign2(p,q.p); return *this; } + + /** @brief Assignment from wrapped. */ + inline Point& operator=(const Wrapped &q) NOEXCEPT { assign2(p,q); return *this; } + + /** @brief Destructor securely erases the point. */ + inline ~Point() NOEXCEPT; + + /** @brief Construct from RNG */ + inline explicit Point(SpongeRng &rng, bool uniform = true) NOEXCEPT; + + /** + * @brief Initialize from C++ fixed-length byte string. + * The all-zero string maps to the identity. + * + * @throw CryptoException the string was the wrong length, or wasn't the encoding of a point, + * or was the identity and allow_identity was DECAF_FALSE. + */ + inline explicit Point(const Block &s, decaf_bool_t allow_identity=DECAF_TRUE) throw(CryptoException) { + if (!decode(*this,s,allow_identity)) throw CryptoException(); + } - /** - * @brief Map uniformly to the curve from a hash buffer. - * The empty or all-zero string maps to the identity, as does the string "\x01". - * If the buffer is shorter than 2*HASH_BYTES, well, it won't be as uniform, - * but the buffer will be zero-padded on the right. - */ - static inline Point from_hash ( const Block &s ) NOEXCEPT { - Point p((NOINIT())); p.set_to_hash(s); return p; - } - - /** - * @brief Map to the curve from a hash buffer. - * The empty or all-zero string maps to the identity, as does the string "\x01". - * If the buffer is shorter than 2*HASH_BYTES, well, it won't be as uniform, - * but the buffer will be zero-padded on the right. - */ - inline void set_to_hash( const Block &s ) NOEXCEPT { - if (s.size() < HASH_BYTES) { - SecureBuffer b(HASH_BYTES); - memcpy(b.data(), s.data(), s.size()); - decaf_448_point_from_hash_nonuniform(p,b); - } else if (s.size() == HASH_BYTES) { - decaf_448_point_from_hash_nonuniform(p,s); - } else if (s.size() < 2*HASH_BYTES) { - SecureBuffer b(2*HASH_BYTES); - memcpy(b.data(), s.data(), s.size()); - decaf_448_point_from_hash_uniform(p,b); - } else { - decaf_448_point_from_hash_uniform(p,s); - } - } - - /** - * @brief Encode to string. The identity encodes to the all-zero string. - */ - inline EXPLICIT_CON operator SecureBuffer() const NOEXCEPT { - SecureBuffer buffer(SER_BYTES); - decaf_448_point_encode(buffer, p); - return buffer; - } + /** + * @brief Initialize from C fixed-length byte string. + * The all-zero string maps to the identity. + * + * @throw CryptoException the string was the wrong length, or wasn't the encoding of a point, + * or was the identity and allow_identity was DECAF_FALSE. + */ + inline explicit Point(const unsigned char buffer[/*SER_BYTES*/], decaf_bool_t allow_identity=DECAF_TRUE) + throw(CryptoException) { if (!decode(*this,buffer,allow_identity)) throw CryptoException(); } + + /** + * @brief Initialize from C fixed-length byte string. + * The all-zero string maps to the identity. + * + * @retval DECAF_SUCCESS the string was successfully decoded. + * @return DECAF_FAILURE the string wasn't the encoding of a point, or was the identity + * and allow_identity was DECAF_FALSE. Contents of the buffer are undefined. + */ + static inline decaf_bool_t __attribute__((warn_unused_result)) decode ( + Point &p, const unsigned char buffer[/*SER_BYTES*/], decaf_bool_t allow_identity=DECAF_TRUE + ) NOEXCEPT; + + /** + * @brief Initialize from C++ fixed-length byte string. + * The all-zero string maps to the identity. + * + * @retval DECAF_SUCCESS the string was successfully decoded. + * @return DECAF_FAILURE the string was the wrong length, or wasn't the encoding of a point, + * or was the identity and allow_identity was DECAF_FALSE. Contents of the buffer are undefined. + */ + static inline decaf_bool_t __attribute__((warn_unused_result)) decode ( + Point &p, const Block &buffer, decaf_bool_t allow_identity=DECAF_TRUE + ) NOEXCEPT { + if (buffer.size() != SER_BYTES) return DECAF_FAILURE; + return decode(p,buffer.data(),allow_identity); + } - /** - * @brief Encode to a C buffer. The identity encodes to all zeros. - */ - inline void encode(unsigned char buffer[SER_BYTES]) const NOEXCEPT{ - decaf_448_point_encode(buffer, p); - } - - /** @brief Point add. */ - inline Point operator+ (const Point &q) const NOEXCEPT { Point r((NOINIT())); decaf_448_point_add(r.p,p,q.p); return r; } - - /** @brief Point add. */ - inline Point &operator+=(const Point &q) NOEXCEPT { decaf_448_point_add(p,p,q.p); return *this; } - - /** @brief Point subtract. */ - inline Point operator- (const Point &q) const NOEXCEPT { Point r((NOINIT())); decaf_448_point_sub(r.p,p,q.p); return r; } - - /** @brief Point subtract. */ - inline Point &operator-=(const Point &q) NOEXCEPT { decaf_448_point_sub(p,p,q.p); return *this; } - - /** @brief Point negate. */ - inline Point operator- () const NOEXCEPT { Point r((NOINIT())); decaf_448_point_negate(r.p,p); return r; } - - /** @brief Double the point out of place. */ - inline Point times_two () const NOEXCEPT { Point r((NOINIT())); decaf_448_point_double(r.p,p); return r; } - - /** @brief Double the point in place. */ - inline Point &double_in_place() NOEXCEPT { decaf_448_point_double(p,p); return *this; } - - /** @brief Constant-time compare. */ - inline bool operator!=(const Point &q) const NOEXCEPT { return ! decaf_448_point_eq(p,q.p); } + /** + * @brief Map uniformly to the curve from a hash buffer. + * The empty or all-zero string maps to the identity, as does the string "\x01". + * If the buffer is shorter than 2*HASH_BYTES, well, it won't be as uniform, + * but the buffer will be zero-padded on the right. + */ + static inline Point from_hash ( const Block &s ) NOEXCEPT { + Point p((NOINIT())); p.set_to_hash(s); return p; + } + + /** + * @brief Map to the curve from a hash buffer. + * The empty or all-zero string maps to the identity, as does the string "\x01". + * If the buffer is shorter than 2*HASH_BYTES, well, it won't be as uniform, + * but the buffer will be zero-padded on the right. + */ + inline void set_to_hash( const Block &s ) NOEXCEPT; + + /** + * @brief Encode to a C buffer. The identity encodes to all zeros. + */ + inline void encode(unsigned char buffer[/*SER_BYTES*/]) const NOEXCEPT{ + decaf_448_point_encode(buffer, p); + } + + /** + * @brief Encode to string. The identity encodes to the all-zero string. + */ + inline EXPLICIT_CON operator SecureBuffer() const NOEXCEPT { + SecureBuffer buffer(SER_BYTES); encode(buffer.data()); return buffer; + } + + /** @brief Point add. */ + inline Point operator+ (const Point &q) const NOEXCEPT { Point r((NOINIT())); add3(r.p,p,q.p); return r; } + + /** @brief Point add. */ + inline Point &operator+=(const Point &q) NOEXCEPT { add3(p,p,q.p); return *this; } + + /** @brief Point subtract. */ + inline Point operator- (const Point &q) const NOEXCEPT { Point r((NOINIT())); sub3(r.p,p,q.p); return r; } + + /** @brief Point subtract. */ + inline Point &operator-=(const Point &q) NOEXCEPT { sub3(p,p,q.p); return *this; } + + /** @brief Point negate. */ + inline Point operator- () const NOEXCEPT { Point r((NOINIT())); neg2(r.p,p); return r; } + + /** @brief Double the point out of place. */ + inline Point times_two () const NOEXCEPT { Point r((NOINIT())); dbl2(r.p,p); return r; } + + /** @brief Double the point in place. */ + inline Point &double_in_place() NOEXCEPT { dbl2(p,p); return *this; } + + /** @brief Constant-time compare. */ + inline bool operator!=(const Point &q) const NOEXCEPT { return !eq2(p,q.p); } + + /** @brief Constant-time compare. */ + inline bool operator==(const Point &q) const NOEXCEPT { return !!eq2(p,q.p); } + + /** @brief Scalar multiply. */ + inline Point operator* (const Scalar &s) const NOEXCEPT { Point r((NOINIT())); sm3(r.p,p,s.s); return r; } + + /** @brief Scalar multiply in place. */ + inline Point &operator*=(const Scalar &s) NOEXCEPT { sm3(p,p,s.s); return *this; } + + /** @brief Multiply by s.inverse(). If s=0, maps to the identity. */ + inline Point operator/ (const Scalar &s) const NOEXCEPT { return (*this) * s.inverse(); } + + /** @brief Multiply by s.inverse(). If s=0, maps to the identity. */ + inline Point &operator/=(const Scalar &s) NOEXCEPT { return (*this) *= s.inverse(); } + + /** @brief Validate / sanity check */ + inline bool validate() const NOEXCEPT; + + /** @brief Double-scalar multiply, equivalent to q*qs + r*rs but faster. */ + static inline Point double_scalarmul ( + const Point &q, const Scalar &qs, const Point &r, const Scalar &rs + ) NOEXCEPT { + Point p((NOINIT())); dsm5(p.p,q.p,qs.s,r.p,rs.s); return p; + } + + /** + * @brief Double-scalar multiply, equivalent to q*qs + r*rs but faster. + * For those who like their scalars before the point. + */ + static inline Point double_scalarmul ( + const Scalar &qs, const Point &q, const Scalar &rs, const Point &r + ) NOEXCEPT { + Point p((NOINIT())); dsm5(p.p,q.p,qs.s,r.p,rs.s); return p; + } + + /** + * @brief Double-scalar multiply: this point by the first scalar and base by the second scalar. + * @warning This function takes variable time, and may leak the scalars (or points, but currently + * it doesn't). + */ + inline Point non_secret_combo_with_base(const Scalar &s, const Scalar &s_base) { + Point r((NOINIT())); dsmns(r.p,s_base.s,p,s.s); return r; + } + + /** @brief Return the base point */ + static inline const Point base() NOEXCEPT { return GENERATOR; } + + /** @brief Return the identity point */ + static inline const Point identity() NOEXCEPT { return IDENTITY; } + }; + + /** + * @brief Precomputed table of points. + * Minor difficulties arise here because the decaf API doesn't expose, as a constant, how big such an object is. + * Therefore we have to call malloc() or friends, but that's probably for the best, because you don't want to + * stack-allocate a 15kiB object anyway. + */ + class Precomputed { + private: + + /** @cond internal */ + static const size_t sizeof_this, alignof_this; + typedef typename WrappedTypes::Precomputed Wrapped; + static const Wrapped *GENERATOR; + static inline void destroy(Wrapped*) NOEXCEPT; + static inline void precompute(Wrapped*, const typename Point::Wrapped) NOEXCEPT; + static inline void psmul3(typename Point::Wrapped, const Wrapped*, const typename Scalar::Wrapped) NOEXCEPT; + + union { + Wrapped *mine; + const Wrapped *yours; + } ours; + bool isMine; + + inline void clear() NOEXCEPT { + if (isMine) { + destroy(ours.mine); + free(ours.mine); + ours.yours = GENERATOR; + isMine = false; + } + } + inline void alloc() throw(std::bad_alloc) { + if (isMine) return; + int ret = posix_memalign((void**)&ours.mine, alignof_this,sizeof_this); + if (ret || !ours.mine) { + isMine = false; + throw std::bad_alloc(); + } + isMine = true; + } + inline const Wrapped *get() const NOEXCEPT { return isMine ? ours.mine : ours.yours; } + /** @endcond */ + public: + /** Destructor securely erases the memory. */ + inline ~Precomputed() NOEXCEPT { clear(); } + + /** + * @brief Initialize from Wrapped type, declared as a reference to prevent + * it from being called with 0, thereby breaking override. + * + * The Wrapped object must remain valid throughout the lifetime of this one. + * + * By default, initializes to the table for the base point. + * + * @warning The empty initializer makes this equal to base, unlike the empty + * initializer for points which makes this equal to the identity. + */ + inline Precomputed( + const Wrapped &yours = *GENERATOR + ) NOEXCEPT { + ours.yours = &yours; + isMine = false; + } + + /** + * @brief Assign. This may require an allocation and memcpy. + */ + inline Precomputed &operator=(const Precomputed &it) throw(std::bad_alloc) { + if (this == &it) return *this; + if (it.isMine) { + alloc(); + memcpy(ours.mine,it.ours.mine,sizeof_this); + } else { + clear(); + ours.yours = it.ours.yours; + } + isMine = it.isMine; + return *this; + } + + /** + * @brief Initilaize from point. Must allocate memory, and may throw. + */ + inline Precomputed &operator=(const Point &it) throw(std::bad_alloc) { + alloc(); precompute(ours.mine,it.p); return *this; + } + + /** + * @brief Copy constructor. + */ + inline Precomputed(const Precomputed &it) throw(std::bad_alloc) : isMine(false) { *this = it; } + + /** + * @brief Constructor which initializes from point. + */ + inline explicit Precomputed(const Point &it) throw(std::bad_alloc) : isMine(false) { *this = it; } + + #if __cplusplus >= 201103L + inline Precomputed &operator=(Precomputed &&it) NOEXCEPT { + if (this == &it) return *this; + clear(); + ours = it.ours; + isMine = it.isMine; + it.isMine = false; + it.ours.yours = base; + return *this; + } + inline Precomputed(Precomputed &&it) NOEXCEPT : isMine(false) { *this = it; } + #endif + + /** @brief Fixed base scalarmul. */ + inline Point operator* (const Scalar &s) const NOEXCEPT { Point r; psmul3(r.p,get(),s.s); return r; } + + /** @brief Multiply by s.inverse(). If s=0, maps to the identity. */ + inline Point operator/ (const Scalar &s) const NOEXCEPT { return (*this) * s.inverse(); } + + /** @brief Return the table for the base point. */ + static inline const Precomputed base() NOEXCEPT { return Precomputed(*GENERATOR); } + }; +}; - /** @brief Constant-time compare. */ - inline bool operator==(const Point &q) const NOEXCEPT { return !!decaf_448_point_eq(p,q.p); } - - /** @brief Scalar multiply. */ - inline Point operator* (const Scalar &s) const NOEXCEPT { Point r((NOINIT())); decaf_448_point_scalarmul(r.p,p,s.s); return r; } - - /** @brief Scalar multiply in place. */ - inline Point &operator*=(const Scalar &s) NOEXCEPT { decaf_448_point_scalarmul(p,p,s.s); return *this; } - - /** @brief Multiply by s.inverse(). If s=0, maps to the identity. */ - inline Point operator/ (const Scalar &s) const NOEXCEPT { return (*this) * s.inverse(); } - - /** @brief Multiply by s.inverse(). If s=0, maps to the identity. */ - inline Point &operator/=(const Scalar &s) NOEXCEPT { return (*this) *= s.inverse(); } - - /** @brief Validate / sanity check */ - inline bool validate() const NOEXCEPT { return !!decaf_448_point_valid(p); } - - /** @brief Double-scalar multiply, equivalent to q*qs + r*rs but faster. */ - static inline Point double_scalarmul ( - const Point &q, const Scalar &qs, const Point &r, const Scalar &rs - ) NOEXCEPT { - Point p((NOINIT())); decaf_448_point_double_scalarmul(p.p,q.p,qs.s,r.p,rs.s); return p; - } - - /** - * @brief Double-scalar multiply, equivalent to q*qs + r*rs but faster. - * For those who like their scalars before the point. - */ - static inline Point double_scalarmul ( - const Scalar &qs, const Point &q, const Scalar &rs, const Point &r - ) NOEXCEPT { - Point p((NOINIT())); decaf_448_point_double_scalarmul(p.p,q.p,qs.s,r.p,rs.s); return p; - } - - /** - * @brief Double-scalar multiply: this point by the first scalar and base by the second scalar. - * @warning This function takes variable time, and may leak the scalars (or points, but currently - * it doesn't). - */ - inline Point non_secret_combo_with_base(const Scalar &s, const Scalar &s_base) { - Point r((NOINIT())); decaf_448_base_double_scalarmul_non_secret(r.p,s_base.s,p,s.s); return r; - } - - /** @brief Return the base point */ - static inline const Point base() NOEXCEPT { return Point(decaf_448_point_base); } - - /** @brief Return the identity point */ - static inline const Point identity() NOEXCEPT { return Point(decaf_448_point_identity); } +/***************************************************************/ +/* Instantiation */ +/***************************************************************/ + + +/** @cond internal */ +template<> struct WrappedTypes { + typedef decaf_448_point_t Point; + typedef decaf_448_scalar_t Scalar; + typedef decaf_448_precomputed_s Precomputed; }; -/** - * @brief Precomputed table of points. - * Minor difficulties arise here because the decaf API doesn't expose, as a constant, how big such an object is. - * Therefore we have to call malloc() or friends, but that's probably for the best, because you don't want to - * stack-allocate a 15kiB object anyway. - */ -class Precomputed { -private: - /** @cond internal */ - union { - decaf_448_precomputed_s *mine; - const decaf_448_precomputed_s *yours; - } ours; - bool isMine; +/* Scalar instantiation */ +template<> const size_t EcGroup::Scalar::SER_BYTES = 56; + +template<> inline void EcGroup::Scalar::add3( + Wrapped a, const Wrapped b, const Wrapped c +) NOEXCEPT { decaf_448_scalar_add(a,b,c); } + +template<> inline void EcGroup::Scalar::sub3( + Wrapped a, const Wrapped b, const Wrapped c +) NOEXCEPT { decaf_448_scalar_sub(a,b,c); } + +template<> inline void EcGroup::Scalar::mul3( + Wrapped a, const Wrapped b, const Wrapped c +) NOEXCEPT { decaf_448_scalar_mul(a,b,c); } + +template<> inline void EcGroup::Scalar::dl3( + Wrapped a, const unsigned char *b, size_t c +) NOEXCEPT { decaf_448_scalar_decode_long(a,b,c); } + +template<> inline void EcGroup::Scalar::assign2( + Wrapped a, const Wrapped b +) NOEXCEPT { decaf_448_scalar_copy(a,b); } + +template<> inline void EcGroup::Scalar::setu( + Wrapped a, decaf_word_t w +) NOEXCEPT { decaf_448_scalar_set(a,w); } + +template<> inline void EcGroup::Scalar::inv2( + Wrapped a, const Wrapped b +) NOEXCEPT { decaf_448_scalar_invert(a,b); } + +template<> inline decaf_word_t EcGroup::Scalar::eq2( + const Wrapped a, const Wrapped b +) NOEXCEPT { return decaf_448_scalar_eq(a,b); } + + /* CLASSY */ +template<> inline SecureBuffer EcGroup::Scalar::direct_scalarmul( + const Block &in, decaf_bool_t allow_identity, decaf_bool_t short_circuit +) const throw(CryptoException) { + SecureBuffer out(SER_BYTES); + if (!decaf_448_direct_scalarmul(out, in.data(), s, allow_identity, short_circuit)) + throw CryptoException(); + return out; +} + +template<> inline void EcGroup::Scalar::encode( + unsigned char buffer[SER_BYTES] +) const NOEXCEPT { + decaf_448_scalar_encode(buffer,s); +} + +template<> inline decaf_bool_t __attribute__((warn_unused_result)) +EcGroup::Scalar::decode ( + Scalar &s, const unsigned char buffer[SER_BYTES] +) NOEXCEPT { + return decaf_448_scalar_decode(s.s,buffer); +} + + /* CLASSY */ +template<> inline EcGroup::Scalar::~Scalar() NOEXCEPT { decaf_448_scalar_destroy(s); } +template<> const EcGroup::Scalar::Wrapped& + EcGroup::Scalar::ZERO = decaf_448_scalar_zero; +template<> const EcGroup::Scalar::Wrapped& + EcGroup::Scalar::ONE = decaf_448_scalar_one; - inline void clear() NOEXCEPT { - if (isMine) { - decaf_448_precomputed_destroy(ours.mine); - free(ours.mine); - ours.yours = decaf_448_precomputed_base; - isMine = false; - } - } - inline void alloc() throw(std::bad_alloc) { - if (isMine) return; - int ret = posix_memalign((void**)&ours.mine, alignof_decaf_448_precomputed_s,sizeof_decaf_448_precomputed_s); - if (ret || !ours.mine) { - isMine = false; - throw std::bad_alloc(); - } - isMine = true; - } - inline const decaf_448_precomputed_s *get() const NOEXCEPT { return isMine ? ours.mine : ours.yours; } - /** @endcond */ -public: - /** Destructor securely erases the memory. */ - inline ~Precomputed() NOEXCEPT { clear(); } - - /** - * @brief Initialize from underlying type, declared as a reference to prevent - * it from being called with 0, thereby breaking override. - * - * The underlying object must remain valid throughout the lifetime of this one. - * - * By default, initializes to the table for the base point. - * - * @warning The empty initializer makes this equal to base, unlike the empty - * initializer for points which makes this equal to the identity. - */ - inline Precomputed( - const decaf_448_precomputed_s &yours = *decaf_448_precomputed_base - ) NOEXCEPT { - ours.yours = &yours; - isMine = false; - } - /** - * @brief Assign. This may require an allocation and memcpy. - */ - inline Precomputed &operator=(const Precomputed &it) throw(std::bad_alloc) { - if (this == &it) return *this; - if (it.isMine) { - alloc(); - memcpy(ours.mine,it.ours.mine,sizeof_decaf_448_precomputed_s); - } else { - clear(); - ours.yours = it.ours.yours; - } - isMine = it.isMine; - return *this; - } + +/* Point instantiation */ +template<> const size_t EcGroup::Point::SER_BYTES = 56; +template<> const size_t EcGroup::Point::HASH_BYTES = 56; + + /* CLASSY */ +template<> inline EcGroup::Point::~Point() NOEXCEPT { decaf_448_point_destroy(p); } + +template<> inline void EcGroup::Point::add3( + Wrapped a, const Wrapped b, const Wrapped c +) NOEXCEPT { decaf_448_point_add(a,b,c); } + +template<> inline void EcGroup::Point::sub3( + Wrapped a, const Wrapped b, const Wrapped c +) NOEXCEPT { decaf_448_point_sub(a,b,c); } - /** - * @brief Initilaize from point. Must allocate memory, and may throw. - */ - inline Precomputed &operator=(const Point &it) throw(std::bad_alloc) { - alloc(); - decaf_448_precompute(ours.mine,it.p); - return *this; - } +template<> inline void EcGroup::Point::assign2( + Wrapped a, const Wrapped b +) NOEXCEPT { decaf_448_point_copy(a,b); } - /** - * @brief Copy constructor. - */ - inline Precomputed(const Precomputed &it) throw(std::bad_alloc) : isMine(false) { *this = it; } - - /** - * @brief Constructor which initializes from point. - */ - inline explicit Precomputed(const Point &it) throw(std::bad_alloc) : isMine(false) { *this = it; } +template<> inline void EcGroup::Point::dbl2( + Wrapped a, const Wrapped b +) NOEXCEPT { decaf_448_point_double(a,b); } -#if __cplusplus >= 201103L - inline Precomputed &operator=(Precomputed &&it) NOEXCEPT { - if (this == &it) return *this; - clear(); - ours = it.ours; - isMine = it.isMine; - it.isMine = false; - it.ours.yours = decaf_448_precomputed_base; - return *this; - } - inline Precomputed(Precomputed &&it) NOEXCEPT : isMine(false) { *this = it; } -#endif +template<> inline decaf_word_t EcGroup::Point::eq2( + const Wrapped a, const Wrapped b +) NOEXCEPT { return decaf_448_point_eq(a,b); } + + /* CLASSY */ +template<> inline bool EcGroup::Point::validate() const NOEXCEPT { return !!decaf_448_point_valid(p); } - /** @brief Fixed base scalarmul. */ - inline Point operator* (const Scalar &s) const NOEXCEPT { Point r; decaf_448_precomputed_scalarmul(r.p,get(),s.s); return r; } +template<> inline void EcGroup::Point::sm3( + Wrapped a, const Wrapped b, const decaf_448_scalar_t c +) NOEXCEPT { decaf_448_point_scalarmul(a,b,c); } - /** @brief Multiply by s.inverse(). If s=0, maps to the identity. */ - inline Point operator/ (const Scalar &s) const NOEXCEPT { return (*this) * s.inverse(); } +template<> inline void EcGroup::Point::dsm5( + Wrapped a, const Wrapped b, const decaf_448_scalar_t c, const Wrapped d, const decaf_448_scalar_t e +) NOEXCEPT { decaf_448_point_double_scalarmul(a,b,c,d,e); } - /** @brief Return the table for the base point. */ - static inline const Precomputed base() NOEXCEPT { return Precomputed(*decaf_448_precomputed_base); } -}; +template<> inline void EcGroup::Point::dsmns( + Wrapped a, const decaf_448_scalar_t b, const Wrapped c, const decaf_448_scalar_t d +) NOEXCEPT { decaf_448_base_double_scalarmul_non_secret(a,b,c,d); } + + /* CLASSY */ +template<> inline decaf_bool_t __attribute__((warn_unused_result)) +EcGroup::Point::decode ( + Point &p, const unsigned char buffer[SER_BYTES], decaf_bool_t allow_identity +) NOEXCEPT { + return decaf_448_point_decode(p.p,buffer,allow_identity); +} + /* CLASSY */ +template<> inline void EcGroup::Point::set_to_hash( const Block &s ) NOEXCEPT { + if (s.size() < HASH_BYTES) { + SecureBuffer b(HASH_BYTES); + memcpy(b.data(), s.data(), s.size()); + decaf_448_point_from_hash_nonuniform(p,b); + } else if (s.size() == HASH_BYTES) { + decaf_448_point_from_hash_nonuniform(p,s); + } else if (s.size() < 2*HASH_BYTES) { + SecureBuffer b(2*HASH_BYTES); + memcpy(b.data(), s.data(), s.size()); + decaf_448_point_from_hash_uniform(p,b); + } else { + decaf_448_point_from_hash_uniform(p,s); + } +} + + /* CLASSY */ +template<> inline void EcGroup::Point::encode( + unsigned char buffer[SER_BYTES] +) const NOEXCEPT { + decaf_448_point_encode(buffer,p); +} + +template<> const EcGroup::Point::Wrapped& + EcGroup::Point::IDENTITY = decaf_448_point_identity; +template<> const EcGroup::Point::Wrapped& + EcGroup::Point::GENERATOR = decaf_448_point_base; -}; /* struct decaf<448> */ +/* Precomputed instantiation */ +template<> inline void EcGroup::Precomputed::destroy( + Wrapped *doomed +) NOEXCEPT { + decaf_448_precomputed_destroy(doomed); +} + +/* Precomputed instantiation */ +template<> inline void EcGroup::Precomputed::precompute( + Wrapped *pre, const decaf_448_point_t point +) NOEXCEPT { + decaf_448_precompute(pre,point); +} + +template<> inline void EcGroup::Precomputed::psmul3( + decaf_448_point_t out, const Wrapped *pre, const decaf_448_scalar_t sc +) NOEXCEPT { + decaf_448_precomputed_scalarmul(out,pre,sc); +} + +template<> const size_t EcGroup::Precomputed:: sizeof_this = sizeof_decaf_448_precomputed_s; +template<> const size_t EcGroup::Precomputed::alignof_this = alignof_decaf_448_precomputed_s; +template<> const EcGroup::Precomputed::Wrapped* + EcGroup::Precomputed::GENERATOR = decaf_448_precomputed_base; + +/** @endcond */ #undef NOEXCEPT #undef EXPLICIT_CON diff --git a/include/shake.hxx b/include/shake.hxx index 1e401ae..3e1d071 100644 --- a/include/shake.hxx +++ b/include/shake.hxx @@ -173,10 +173,10 @@ public: } /** Read data to a buffer. */ - inline void read(Buffer &buffer) { spongerng_next(sp,buffer.data(),buffer.size()); } + inline void read(Buffer &buffer) NOEXCEPT { spongerng_next(sp,buffer.data(),buffer.size()); } /** Read data to a buffer. */ - inline void read(TmpBuffer buffer) { read((Buffer &)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 @@ -192,19 +192,14 @@ private: }; /**@cond internal*/ -/* FIXME: multiple sizes */ -EcGroup<448>::Scalar::Scalar(SpongeRng &rng) { - *this = rng.read(SER_BYTES); +template EcGroup::Scalar::Scalar(SpongeRng &rng) NOEXCEPT { + *this = rng.read(EcGroup::Scalar::SER_BYTES); } -EcGroup<448>::Point::Point(SpongeRng &rng, bool uniform) { +template EcGroup::Point::Point(SpongeRng &rng, bool uniform) NOEXCEPT { SecureBuffer buffer((uniform ? 2 : 1) * HASH_BYTES); rng.read(buffer); - if (uniform) { - decaf_448_point_from_hash_uniform(p,buffer); - } else { - decaf_448_point_from_hash_nonuniform(p,buffer); - } + set_to_hash(buffer); } /**@endcond*/ diff --git a/test/test_decaf.cxx b/test/test_decaf.cxx index 1cf43ec..fe3e82d 100644 --- a/test/test_decaf.cxx +++ b/test/test_decaf.cxx @@ -47,7 +47,7 @@ typedef typename decaf::EcGroup::Point Point; typedef typename decaf::EcGroup::Precomputed Precomputed; static void print(const char *name, const Scalar &x) { - unsigned char buffer[DECAF_448_SCALAR_BYTES]; + unsigned char buffer[Scalar::SER_BYTES]; x.encode(buffer); printf(" %s = 0x", name); for (int i=sizeof(buffer)-1; i>=0; i--) { @@ -57,7 +57,7 @@ static void print(const char *name, const Scalar &x) { } static void print(const char *name, const Point &x) { - unsigned char buffer[DECAF_448_SER_BYTES]; + unsigned char buffer[Point::SER_BYTES]; x.encode(buffer); printf(" %s = 0x", name); for (int i=sizeof(buffer)-1; i>=0; i--) { @@ -175,6 +175,7 @@ static void test_ec() { point_check(test,p,q,r,0,0,p,Point((decaf::SecureBuffer)p),"round-trip"); point_check(test,p,q,r,0,0,p+q,q+p,"commute add"); point_check(test,p,q,r,0,0,p+(q+r),(p+q)+r,"assoc add"); + point_check(test,p,q,r,0,0,p.times_two(),p+p,"dbl add"); if (i%10) continue; point_check(test,p,q,r,x,0,x*(p+q),x*p+x*q,"distr mul");