From 595855b4343fe7891feee90771cedc890a68dbe7 Mon Sep 17 00:00:00 2001 From: Michael Hamburg Date: Sun, 28 Feb 2016 21:48:01 -0800 Subject: [PATCH] move eddsa.hxx to its own header. sha512.hxx; rework shake.hxx header; create prehash object. TODO: test prehash --- Makefile | 2 +- src/gen_headers/template.py | 2 +- src/per_curve/decaf.tmpl.hxx | 99 +------ src/per_curve/eddsa.tmpl.hxx | 341 ++++++++++++++++++++++++ src/public_include/decaf/eddsa.tmpl.hxx | 7 + src/public_include/decaf/sha512.hxx | 103 +++++++ src/public_include/decaf/shake.h | 33 +++ src/public_include/decaf/shake.hxx | 73 +++-- src/shake.c | 35 ++- test/bench_decaf.cxx | 17 +- test/test_decaf.cxx | 21 +- 11 files changed, 595 insertions(+), 138 deletions(-) create mode 100644 src/per_curve/eddsa.tmpl.hxx create mode 100644 src/public_include/decaf/eddsa.tmpl.hxx create mode 100644 src/public_include/decaf/sha512.hxx diff --git a/Makefile b/Makefile index 76d29e7..10549f6 100644 --- a/Makefile +++ b/Makefile @@ -170,7 +170,7 @@ LIBCOMPONENTS += $$(BUILD_OBJ)/$(1)/decaf.o $$(BUILD_OBJ)/$(1)/elligator.o $$(BU PER_OBJ_DIRS += $$(BUILD_OBJ)/$(1) GLOBAL_HEADERS_OF_$(1) = $(BUILD_INC)/decaf/decaf_$(3).h $(BUILD_INC)/decaf/decaf_$(3).hxx \ $(BUILD_INC)/decaf/crypto_$(3).h $(BUILD_INC)/decaf/crypto_$(3).hxx \ - $(BUILD_INC)/decaf/eddsa_$(3).h + $(BUILD_INC)/decaf/eddsa_$(3).h $(BUILD_INC)/decaf/eddsa_$(3).hxx HEADERS_OF_$(1) = $$(HEADERS_OF_$(2)) $$(GLOBAL_HEADERS_OF_$(1)) HEADERS += $$(GLOBAL_HEADERS_OF_$(1)) diff --git a/src/gen_headers/template.py b/src/gen_headers/template.py index 9f54824..ce127a3 100644 --- a/src/gen_headers/template.py +++ b/src/gen_headers/template.py @@ -50,7 +50,7 @@ def fillin(template,data): if template[position] == '(': parens += 1 elif template[position] == ')': parens -= 1 position += 1 - ret += str(eval(template[dollars+2:position-1],{'ser':ser,'msqrt':msqrt,'ceil_log2':ceil_log2},data)) + ret += str(eval(template[dollars+2:position-1],{'re':re,'ser':ser,'msqrt':msqrt,'ceil_log2':ceil_log2},data)) author = "Mike Hamburg" # FUTURE for name in args.files: diff --git a/src/per_curve/decaf.tmpl.hxx b/src/per_curve/decaf.tmpl.hxx index ca85fe7..f4dfff8 100644 --- a/src/per_curve/decaf.tmpl.hxx +++ b/src/per_curve/decaf.tmpl.hxx @@ -21,7 +21,7 @@ #include /* for memcpy */ #include -#include /* TODO: move eddsa to another file? */ +#include #include #include #include @@ -679,103 +679,6 @@ public: } }; - -struct EdDSA { /* TODO: make into a utility class. Possibly move to another include file. */ -public: - /** The size of a public key */ - static const size_t PUBLIC_BYTES = $(C_NS)_EDDSA_PUBLIC_BYTES; - - /** The size of a private key */ - static const size_t PRIVATE_BYTES = $(C_NS)_EDDSA_PRIVATE_BYTES; - - /** The size of a private key */ - static const size_t SIGNATURE_BYTES = $(C_NS)_EDDSA_SIGNATURE_BYTES; - - /** Do we support contexts for signatures? If not, they must always be NULL */ - static const bool SUPPORTS_CONTEXTS = $(C_NS)_EDDSA_SUPPORTS_CONTEXTS; - - static inline SecureBuffer generate_key ( - const FixedBlock &priv - ) { - SecureBuffer out(PUBLIC_BYTES); - $(c_ns)_eddsa_derive_public_key(out.data(), priv.data()); - return out; - } - - static inline SecureBuffer sign ( - const FixedBlock &priv, - const FixedBlock &pub, - const Block &message, - bool prehashed = false, - const Block &context = Block(NULL,0) - ) throw(LengthException) { - if (context.size() > 255 - || (context.size() != 0 && !SUPPORTS_CONTEXTS) - ) { - throw LengthException(); - } - - SecureBuffer out(SIGNATURE_BYTES); - $(c_ns)_eddsa_sign ( - out.data(), - priv.data(), - pub.data(), - message.data(), - message.size(), - prehashed -#if $(C_NS)_EDDSA_SUPPORTS_CONTEXTS - , context.data(), - context.size() -#endif - ); - return out; - } - - static inline decaf_error_t WARN_UNUSED verify_noexcept ( - const FixedBlock &sig, - const FixedBlock &pub, - const Block &message, - bool prehashed = false, - const Block &context = Block(NULL,0) - ) { - if (context.size() > 255 - || (context.size() != 0 && !SUPPORTS_CONTEXTS) - ) { - return DECAF_FAILURE; - } - - return $(c_ns)_eddsa_verify ( - sig.data(), - pub.data(), - message.data(), - message.size(), - prehashed -#if $(C_NS)_EDDSA_SUPPORTS_CONTEXTS - , context.data(), - context.size() -#endif - ); - } - - static inline void verify ( - const FixedBlock &sig, - const FixedBlock &pub, - const Block &message, - bool prehashed = false, - const Block &context = Block(NULL,0) - ) throw(LengthException,CryptoException) { - if (context.size() > 255 - || (context.size() != 0 && !SUPPORTS_CONTEXTS) - ) { - throw LengthException(); - } - - if (DECAF_SUCCESS != verify_noexcept( sig, pub, message, prehashed, context )) { - throw CryptoException(); - } - } -}; - }; /* struct $(cxx_ns) */ /** @cond internal */ diff --git a/src/per_curve/eddsa.tmpl.hxx b/src/per_curve/eddsa.tmpl.hxx new file mode 100644 index 0000000..862a49a --- /dev/null +++ b/src/per_curve/eddsa.tmpl.hxx @@ -0,0 +1,341 @@ + +/* + * Example Decaf cyrpto routines, C++ wrapper. + * @warning These are merely examples, though they ought to be secure. But real + * protocols will decide differently on magic numbers, formats, which items to + * hash, etc. + * @warning Experimental! The names, parameter orders etc are likely to change. + */ + +#include +#include + +#include +#include + +/** @cond internal */ +#if __cplusplus >= 201103L +#define NOEXCEPT noexcept +#else +#define NOEXCEPT throw() +#endif +/** @endcond */ + +namespace decaf { + +/** A public key for crypto over some Group */ +template struct EdDSA; + +/** A public key for crypto over $(name) */ +template<> struct EdDSA<$(cxx_ns)> { + +/** @cond internal */ +class PrivateKey; +class PublicKey; +/** @endcond */ + +class Prehash : public $(re.sub(r"SHAKE(\d+)",r"SHAKE<\1>", eddsa_hash.upper())) { +public: + /** Do we support contexts for signatures? If not, they must always be NULL */ + static const bool SUPPORTS_CONTEXTS = $(C_NS)_EDDSA_SUPPORTS_CONTEXTS; + +private: + typedef $(re.sub(r"SHAKE(\d+)",r"SHAKE<\1>", eddsa_hash.upper())) Super; + SecureBuffer context_; + friend class PrivateKey; + friend class PublicKey; + + void init() throw(LengthException) { + Super::reset(); + + if (context_.size() > 255 + || (context_.size() != 0 && !SUPPORTS_CONTEXTS) + ) { + throw LengthException(); + } + + if (SUPPORTS_CONTEXTS) { + uint8_t dom[2] = {2, context_.size() }; + update(dom,2); + update(context_); + } + } + +public: + /** Number of output bytes in prehash */ + static const size_t OUTPUT_BYTES = Super::DEFAULT_OUTPUT_BYTES; + + /** Create the prehash */ + Prehash(Block context = Block(NULL,0)) throw(LengthException) { + context_ = context; + init(); + } + + /** Reset this hash */ + void reset() NOEXCEPT { init(); } + + /** Output from this hash */ + SecureBuffer final() throw(std::bad_alloc) { + SecureBuffer ret = Super::final(OUTPUT_BYTES); + reset(); + return ret; + } + + /** Output from this hash */ + void final(Buffer &b) throw(LengthException) { + if (b.size() != OUTPUT_BYTES) throw LengthException(); + Super::final(b); + reset(); + } +}; + +class PrivateKey : public Serializable { +private: +/** @cond internal */ + friend class PublicKey; +/** @endcond */ + + /** The pre-expansion form of the signing key. */ + FixedArrayBuffer<$(C_NS)_EDDSA_PRIVATE_BYTES> priv_; + + /** The post-expansion public key. */ + FixedArrayBuffer<$(C_NS)_EDDSA_PUBLIC_BYTES> pub_; + +public: + /** Underlying group */ + typedef $(cxx_ns) Group; + + /** Signature size. */ + static const size_t SIG_BYTES = $(C_NS)_EDDSA_SIGNATURE_BYTES; + + /** Serialization size. */ + static const size_t SER_BYTES = $(C_NS)_EDDSA_PRIVATE_BYTES; + + /** Do we support contexts for signatures? If not, they must always be NULL */ + static const bool SUPPORTS_CONTEXTS = $(C_NS)_EDDSA_SUPPORTS_CONTEXTS; + + + /** Create but don't initialize */ + inline explicit PrivateKey(const NOINIT&) NOEXCEPT : priv_((NOINIT())), pub_((NOINIT())) { } + + /** Read a private key from a string */ + inline explicit PrivateKey(const FixedBlock &b) NOEXCEPT { *this = b; } + + /** Copy constructor */ + inline PrivateKey(const PrivateKey &k) NOEXCEPT { *this = k; } + + /** Create at random */ + inline explicit PrivateKey(Rng &r) NOEXCEPT : priv_(r) { + $(c_ns)_eddsa_derive_public_key(pub_.data(), priv_.data()); + } + + /** Assignment from string */ + inline PrivateKey &operator=(const FixedBlock &b) NOEXCEPT { + memcpy(priv_.data(),b.data(),b.size()); + $(c_ns)_eddsa_derive_public_key(pub_.data(), priv_.data()); + return *this; + } + + /** Copy assignment */ + inline PrivateKey &operator=(const PrivateKey &k) NOEXCEPT { + memcpy(priv_.data(),k.priv_.data(), priv_.size()); + memcpy(pub_.data(),k.pub_.data(), pub_.size()); + return *this; + } + + /** Serialization size. */ + inline size_t serSize() const NOEXCEPT { return SER_BYTES; } + + /** Serialize into a buffer. */ + inline void serialize_into(unsigned char *x) const NOEXCEPT { + memcpy(x,priv_.data(), priv_.size()); + } + + /** Return the corresponding public key */ + inline PublicKey pub() const NOEXCEPT { + PublicKey pub(*this); + return pub; + } + + /** + * Sign a message. + * @param [in] message The message to be signed. + * @param [in] prehashed If true, the message to be signed is already hashed. + * @param [in] context A context for the signature; must be at most 255 bytes; + * must be absent if SUPPORTS_CONTEXTS == false. + * + * @warning It is generally unsafe to use Ed25519 with both prehashed and non-prehashed messages. + */ + inline SecureBuffer sign ( + const Block &message, + bool prehashed = false, + const Block &context = Block(NULL,0) + ) const throw(LengthException, std::bad_alloc) { + if (context.size() > 255 + || (context.size() != 0 && !SUPPORTS_CONTEXTS) + ) { + throw LengthException(); + } + + SecureBuffer out(SIG_BYTES); + $(c_ns)_eddsa_sign ( + out.data(), + priv_.data(), + pub_.data(), + message.data(), + message.size(), + prehashed +#if $(C_NS)_EDDSA_SUPPORTS_CONTEXTS + , context.data(), + context.size() +#endif + ); + return out; + } + + /* Sign a prehash context, and reset the context */ + inline SecureBuffer sign ( Prehash &ph ) const throw(std::bad_alloc) { + FixedArrayBuffer m; + ph.final(m); + return sign(m, true, ph.context_); + } +}; /* class PrivateKey */ + +class PublicKey : public Serializable { +private: +/** @cond internal */ + friend class PrivateKey; +/** @endcond */ + +public: + /** The pre-expansion form of the signature */ + FixedArrayBuffer<$(C_NS)_EDDSA_PUBLIC_BYTES> pub_; + +public: + /** Underlying group */ + typedef $(cxx_ns) Group; + + /** Signature size. */ + static const size_t SIG_BYTES = $(C_NS)_EDDSA_SIGNATURE_BYTES; + + /** Serialization size. */ + static const size_t SER_BYTES = $(C_NS)_EDDSA_PRIVATE_BYTES; + + /** Do we support contexts for signatures? If not, they must always be NULL */ + static const bool SUPPORTS_CONTEXTS = $(C_NS)_EDDSA_SUPPORTS_CONTEXTS; + + + /** Create but don't initialize */ + inline explicit PublicKey(const NOINIT&) NOEXCEPT : pub_((NOINIT())) { } + + /** Read a private key from a string */ + inline explicit PublicKey(const FixedBlock &b) NOEXCEPT { *this = b; } + + /** Copy constructor */ + inline PublicKey(const PublicKey &k) NOEXCEPT { *this = k; } + + /** Copy constructor */ + inline explicit PublicKey(const PrivateKey &k) NOEXCEPT { *this = k; } + + /** Assignment from string */ + inline PublicKey &operator=(const FixedBlock &b) NOEXCEPT { + memcpy(pub_.data(),b.data(),b.size()); + return *this; + } + + /** Assignment from private key */ + inline PublicKey &operator=(const PublicKey &p) NOEXCEPT { + return *this = p.pub_; + } + + /** Assignment from private key */ + inline PublicKey &operator=(const PrivateKey &p) NOEXCEPT { + return *this = p.pub_; + } + + + /** Serialization size. */ + inline size_t serSize() const NOEXCEPT { return SER_BYTES; } + + /** Serialize into a buffer. */ + inline void serialize_into(unsigned char *x) const NOEXCEPT { + memcpy(x,pub_.data(), pub_.size()); + } + + /** Verify a signature, returning DECAF_FAILURE if verification fails */ + inline decaf_error_t WARN_UNUSED verify_noexcept ( + const FixedBlock &sig, + const Block &message, + bool prehashed = false, + const Block &context = Block(NULL,0) + ) const NOEXCEPT { + if (context.size() > 255 + || (context.size() != 0 && !SUPPORTS_CONTEXTS) + ) { + return DECAF_FAILURE; + } + + return $(c_ns)_eddsa_verify ( + sig.data(), + pub_.data(), + message.data(), + message.size(), + prehashed +#if $(C_NS)_EDDSA_SUPPORTS_CONTEXTS + , context.data(), + context.size() +#endif + ); + } + + /** Verify a signature, throwing an exception if verification fails + * @param [in] sig The signature. + * @param [in] message The signed message. + * @param [in] prehashed If true, the message is already hashed. + * @param [in] context A context for the signature; must be at most 255 bytes; + * must be absent if SUPPORTS_CONTEXTS == false. + * + * @warning It is generally unsafe to use Ed25519 with both prehashed and non-prehashed messages. + */ + inline void verify ( + const FixedBlock &sig, + const Block &message, + bool prehashed = false, + const Block &context = Block(NULL,0) + ) const throw(LengthException,CryptoException) { + if (context.size() > 255 + || (context.size() != 0 && !SUPPORTS_CONTEXTS) + ) { + throw LengthException(); + } + + if (DECAF_SUCCESS != verify_noexcept( sig, message, prehashed, context )) { + throw CryptoException(); + } + } + + /* Verify a prehash context, and reset the context */ + inline decaf_error_t WARN_UNUSED verify_noexcept ( + const FixedBlock &sig, + Prehash &ph + ) const NOEXCEPT { + FixedArrayBuffer m; + ph.final(m); + return verify_noexcept(sig, m, true, ph.context_); + } + + /* Verify a prehash context, and reset the context */ + inline void verify ( + const FixedBlock &sig, + Prehash &ph + ) const throw(CryptoException) { + FixedArrayBuffer m; + ph.final(m); + verify(sig, m, true, ph.context_); + } +}; /* class PublicKey */ + +}; /* template<> struct EdDSA<$(cxx_ns)> */ + +#undef NOEXCEPT +} /* namespace decaf */ diff --git a/src/public_include/decaf/eddsa.tmpl.hxx b/src/public_include/decaf/eddsa.tmpl.hxx new file mode 100644 index 0000000..08fa7cc --- /dev/null +++ b/src/public_include/decaf/eddsa.tmpl.hxx @@ -0,0 +1,7 @@ +/** + * EdDSA crypto routines, metaheader. + */ + +$("\n".join([ + "#include " % g for g in sorted([c["bits"] for _,c in curve.iteritems()]) +])) diff --git a/src/public_include/decaf/sha512.hxx b/src/public_include/decaf/sha512.hxx new file mode 100644 index 0000000..2509eaf --- /dev/null +++ b/src/public_include/decaf/sha512.hxx @@ -0,0 +1,103 @@ +/** + * @file decaf/sha512.hxx + * @copyright + * Based on public domain code by Dan Bernstein \n + * Copyright (c) 2015 Cryptography Research, Inc. \n + * Released under the MIT License. See LICENSE.txt for license information. + * @author Mike Hamburg + * @brief SHA512 instance, C++ wrapper. + */ + +#ifndef __DECAF_SHA512_HXX__ +#define __DECAF_SHA512_HXX__ + +#include +#include +#include + +/** @cond internal */ +#if __cplusplus >= 201103L + #define NOEXCEPT noexcept +#else + #define NOEXCEPT throw() +#endif +/** @endcond */ + +namespace decaf { + +/** SHA512 wrapper function */ +class SHA512 { +protected: + /** @cond internal */ + /** The C-wrapper sponge state */ + sha512_ctx_t sha; + +public: + + /** Number of bytes ouf output */ + static const size_t OUTPUT_BYTES = 64; + + /** Number of bytes of output */ + static const size_t MAX_OUTPUT_BYTES = OUTPUT_BYTES; + + /** Default number of bytes to output */ + static const size_t DEFAULT_OUTPUT_BYTES = OUTPUT_BYTES; + + /** Constructor */ + inline SHA512() NOEXCEPT { sha512_init(sha); } + + /** Add more data to running hash */ + inline void update(const uint8_t *__restrict__ in, size_t len) NOEXCEPT { sha512_update(sha,in,len); } + + /** Add more data to running hash, C++ version. */ + inline void update(const Block &s) NOEXCEPT { update(s.data(),s.size()); } + + /** Add more data, stream version. */ + inline SHA512 &operator<<(const Block &s) { update(s); return *this; } + + /** Same as <<. */ + inline SHA512 &operator+=(const Block &s) { return *this << s; } + + /** @brief Output bytes from the SHA context, and resets it. */ + inline void final(Buffer b) throw(LengthException) { + if (b.size() > OUTPUT_BYTES) throw LengthException(); + sha512_final(sha,b.data(),b.size()); + } + + /** Resets the SHA context */ + inline void reset() NOEXCEPT { sha512_init(sha); } + + /** @brief Output bytes from the sponge. */ + inline SecureBuffer final(size_t len = OUTPUT_BYTES) throw(LengthException) { + if (len > OUTPUT_BYTES) throw LengthException(); + SecureBuffer buffer(len); + sha512_final(sha,buffer.data(),len); + return buffer; + } + + /** @brief Return the sponge's default output size. */ + inline size_t default_output_size() const NOEXCEPT { return OUTPUT_BYTES; } + + /** @brief Return the sponge's maximum output size. */ + inline size_t max_output_size() const NOEXCEPT { return MAX_OUTPUT_BYTES; } + + /** @brief Hash a message in one pass */ + static inline SecureBuffer hash ( + const Block &message, + size_t outlen = OUTPUT_BYTES + ) throw(LengthException, std::bad_alloc) { + if (outlen > OUTPUT_BYTES) throw LengthException(); + SecureBuffer buffer(outlen); + sha512_hash(buffer.data(),outlen,message.data(),message.size()); + return buffer; + } + + /** Destructor zeroizes state */ + inline ~SHA512() NOEXCEPT { sha512_destroy(sha); } +}; + +} /* namespace decaf */ + +#undef NOEXCEPT + +#endif /* __DECAF_SHA512_HXX__ */ diff --git a/src/public_include/decaf/shake.h b/src/public_include/decaf/shake.h index 6d5b792..cab7e18 100644 --- a/src/public_include/decaf/shake.h +++ b/src/public_include/decaf/shake.h @@ -76,6 +76,29 @@ void sha3_output ( size_t len ) API_VIS; +/** + * @brief Squeeze output data from a SHA3 or SHAKE hash context. + * This re-initializes the context to its starting parameters. + * + * @param [inout] sponge The context. + * @param [out] out The output data. + * @param [in] len The requested output data length in bytes. + */ +void sha3_final ( + keccak_sponge_t sponge, + uint8_t * __restrict__ out, + size_t len +) API_VIS; + +/** + * @brief Reset the sponge to the empty string. + * + * @param [inout] sponge The context. + */ +void sha3_reset ( + keccak_sponge_t sponge +) API_VIS; + /** * @brief Return the default output length of the sponge construction, * for the purpose of C++ default operators. @@ -86,6 +109,16 @@ size_t sponge_default_output_bytes ( const keccak_sponge_t sponge /**< [inout] The context. */ ) API_VIS; +/** + * @brief Return the default output length of the sponge construction, + * for the purpose of C++ default operators. + * + * Returns n/8 for SHA3-n and SIZE_MAX for SHAKE-n. + */ +size_t sponge_max_output_bytes ( + const keccak_sponge_t sponge /**< [inout] The context. */ +) API_VIS; + /** * @brief Destroy a SHA3 or SHAKE sponge context by overwriting it with 0. * @param [out] sponge The context. diff --git a/src/public_include/decaf/shake.hxx b/src/public_include/decaf/shake.hxx index 970a76b..630da20 100644 --- a/src/public_include/decaf/shake.hxx +++ b/src/public_include/decaf/shake.hxx @@ -12,7 +12,7 @@ #define __DECAF_SHAKE_HXX__ #include - +#include #include /** @cond internal */ @@ -43,28 +43,44 @@ protected: public: /** Add more data to running hash */ - inline void update(const uint8_t *__restrict__ in, size_t len) { sha3_update(sp,in,len); } + inline void update(const uint8_t *__restrict__ in, size_t len) NOEXCEPT { sha3_update(sp,in,len); } /** Add more data to running hash, C++ version. */ - inline void update(const Block &s) { sha3_update(sp,s.data(),s.size()); } + inline void update(const Block &s) NOEXCEPT { sha3_update(sp,s.data(),s.size()); } /** Add more data, stream version. */ - inline KeccakHash &operator<<(const Block &s) { update(s); return *this; } + inline KeccakHash &operator<<(const Block &s) NOEXCEPT { update(s); return *this; } /** Same as <<. */ - inline KeccakHash &operator+=(const Block &s) { return *this << s; } + inline KeccakHash &operator+=(const Block &s) NOEXCEPT { return *this << s; } + /** @brief Output bytes from the sponge. */ + inline SecureBuffer output(size_t len) throw(std::bad_alloc, LengthException) { + if (len > max_output_size()) throw LengthException(); + SecureBuffer buffer(len); + sha3_output(sp,buffer.data(),len); + return buffer; + } + + /** @brief Output bytes from the sponge. */ + inline SecureBuffer final(size_t len) throw(std::bad_alloc, LengthException) { + if (len > max_output_size()) throw LengthException(); + SecureBuffer buffer(len); + sha3_final(sp,buffer.data(),len); + return buffer; + } + /** * @brief Output bytes from the sponge. * @todo make this throw exceptions. */ - inline void output(Buffer b) { sha3_output(sp,b.data(),b.size()); } + inline void output(Buffer b) throw(LengthException) { + sha3_output(sp,b.data(),b.size()); + } - /** @brief Output bytes from the sponge. */ - inline SecureBuffer output(size_t len) { - SecureBuffer buffer(len); - sha3_output(sp,buffer.data(),len); - return buffer; + /** @brief Output bytes from the sponge and reinitialize it. */ + inline void final(Buffer b) throw(LengthException) { + sha3_final(sp,b.data(),b.size()); } /** @brief Return the sponge's default output size. */ @@ -72,11 +88,24 @@ public: return sponge_default_output_bytes(sp); } + /** @brief Return the sponge's maximum output size. */ + inline size_t max_output_size() const NOEXCEPT { + return sponge_max_output_bytes(sp); + } + /** Output the default number of bytes. */ - inline SecureBuffer output() { + inline SecureBuffer output() throw(std::bad_alloc) { return output(default_output_size()); } + /** Output the default number of bytes, and reset hash. */ + inline SecureBuffer final() throw(std::bad_alloc) { + return final(default_output_size()); + } + + /** Reset the hash to the empty string */ + inline void reset() NOEXCEPT { sha3_reset(sp); } + /** Destructor zeroizes state */ inline ~KeccakHash() NOEXCEPT { sponge_destroy(sp); } }; @@ -91,12 +120,12 @@ public: /** Number of bytes of output */ static const size_t MAX_OUTPUT_BYTES = bits/8; + /** Number of bytes of output */ + static const size_t DEFAULT_OUTPUT_BYTES = bits/8; + /** Initializer */ inline SHA3() NOEXCEPT : KeccakHash(get_params()) {} - /** Reset the hash to the empty string */ - inline void reset() NOEXCEPT { sponge_init(sp, get_params()); } - /** Hash bytes with this SHA3 instance. * @throw LengthException if nbytes > MAX_OUTPUT_BYTES */ @@ -114,12 +143,20 @@ class SHAKE : public KeccakHash { private: /** Get the parameter template block for this hash */ static inline const struct kparams_s *get_params(); + public: + /** Number of bytes of output */ +#if __cplusplus >= 201103L + static const size_t MAX_OUTPUT_BYTES = SIZE_MAX; +#else + static const size_t MAX_OUTPUT_BYTES = (size_t)-1; +#endif + + /** Default number of bytes to output */ + static const size_t DEFAULT_OUTPUT_BYTES = bits/4; + /** Initializer */ inline SHAKE() NOEXCEPT : KeccakHash(get_params()) {} - - /** Reset the hash to the empty string */ - inline void reset() NOEXCEPT { sponge_init(sp, get_params()); } /** Hash bytes with this SHAKE instance */ static inline SecureBuffer hash(const Block &b, size_t outlen) throw(std::bad_alloc) { diff --git a/src/shake.c b/src/shake.c index 32814de..17a318e 100644 --- a/src/shake.c +++ b/src/shake.c @@ -57,7 +57,7 @@ typedef union { } kdomain_t[1]; typedef struct kparams_s { - uint8_t position, flags, rate, startRound, pad, ratePad, maxOut, client; + uint8_t position, flags, rate, startRound, pad, ratePad, maxOut, client; /* client = maxOutRemaining for sha3 */ } kparams_t[1]; typedef struct keccak_sponge_s { @@ -174,8 +174,8 @@ void sha3_output ( assert(sponge->params->rate < sizeof(sponge->state)); if (sponge->params->maxOut != 0xFF) { - assert(sponge->params->maxOut >= len); - sponge->params->maxOut -= len; + assert(sponge->params->client >= len); + sponge->params->client -= len; } switch (sponge->params->flags) { @@ -208,6 +208,22 @@ void sha3_output ( } } +void sha3_final ( + keccak_sponge_t sponge, + uint8_t * __restrict__ out, + size_t len +) { + sha3_output(sponge,out,len); + sha3_reset(sponge); +} + +void sha3_reset ( + keccak_sponge_t sponge +) { + sponge_init(sponge, sponge->params); + sponge->params->client = sponge->params->maxOut; +} + void sponge_destroy (keccak_sponge_t sponge) { decaf_bzero(sponge, sizeof(keccak_sponge_t)); } void sponge_init ( @@ -216,6 +232,7 @@ void sponge_init ( ) { memset(sponge->state, 0, sizeof(sponge->state)); sponge->params[0] = params[0]; + sponge->params->position = 0; } void sponge_hash ( @@ -234,11 +251,11 @@ void sponge_hash ( #define DEFSHAKE(n) \ const struct kparams_s SHAKE##n##_params_s = \ - { 0, FLAG_ABSORBING, 200-n/4, 0, 0x1f, 0x80, 0xFF, 0 }; + { 0, FLAG_ABSORBING, 200-n/4, 0, 0x1f, 0x80, 0xFF, 0xFF }; #define DEFSHA3(n) \ const struct kparams_s SHA3_##n##_params_s = \ - { 0, FLAG_ABSORBING, 200-n/4, 0, 0x06, 0x80, n/8, 0 }; + { 0, FLAG_ABSORBING, 200-n/4, 0, 0x06, 0x80, n/8, n/8 }; size_t sponge_default_output_bytes ( const keccak_sponge_t s @@ -248,6 +265,14 @@ size_t sponge_default_output_bytes ( : ((200-s->params->rate)/2); } +size_t sponge_max_output_bytes ( + const keccak_sponge_t s +) { + return (s->params->maxOut == 0xFF) + ? SIZE_MAX + : ((200-s->params->rate)/2); +} + DEFSHAKE(128) DEFSHAKE(256) DEFSHA3(224) diff --git a/test/bench_decaf.cxx b/test/bench_decaf.cxx index f49227c..f01e073 100644 --- a/test/bench_decaf.cxx +++ b/test/bench_decaf.cxx @@ -11,11 +11,13 @@ #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -295,11 +297,14 @@ static void cfrg() { for (Benchmark b("RFC 7748 keygen"); b.iter(); ) { Group::DhLadder::generate_key(s1); } for (Benchmark b("RFC 7748 shared secret"); b.iter(); ) { Group::DhLadder::shared_secret(base,s1); } - FixedArrayBuffer e1(rng); - SecureBuffer pk, sig; - for (Benchmark b("EdDSA keygen"); b.iter(); ) { pk = Group::EdDSA::generate_key(e1); } - for (Benchmark b("EdDSA sign"); b.iter(); ) { sig = Group::EdDSA::sign(e1,pk,Block(NULL,0)); } - for (Benchmark b("EdDSA verify"); b.iter(); ) { Group::EdDSA::verify(sig,pk,Block(NULL,0)); } + FixedArrayBuffer::PrivateKey::SER_BYTES> e1(rng); + typename EdDSA::PublicKey pub((NOINIT())); + typename EdDSA::PrivateKey priv((NOINIT())); + SecureBuffer sig; + for (Benchmark b("EdDSA keygen"); b.iter(); ) { priv = e1; } + for (Benchmark b("EdDSA sign"); b.iter(); ) { sig = priv.sign(Block(NULL,0)); } + pub = priv; + for (Benchmark b("EdDSA verify"); b.iter(); ) { pub.verify(sig,Block(NULL,0)); } } static void macro() { @@ -408,11 +413,13 @@ int main(int argc, char **argv) { SHAKE<128> shake1; SHAKE<256> shake2; SHA3<512> sha5; + SHA512 sha2; Strobe strobe("example::bench",Strobe::CLIENT); unsigned char b1024[1024] = {1}; for (Benchmark b("SHAKE128 1kiB", 30); b.iter(); ) { shake1 += Buffer(b1024,1024); } for (Benchmark b("SHAKE256 1kiB", 30); b.iter(); ) { shake2 += Buffer(b1024,1024); } for (Benchmark b("SHA3-512 1kiB", 30); b.iter(); ) { sha5 += Buffer(b1024,1024); } + for (Benchmark b("SHA512 1kiB", 30); b.iter(); ) { sha2 += Buffer(b1024,1024); } strobe.dh_key(Buffer(b1024,1024)); strobe.respec(STROBE_128); for (Benchmark b("STROBE128 1kiB", 10); b.iter(); ) { diff --git a/test/test_decaf.cxx b/test/test_decaf.cxx index ec05637..bad8bde 100644 --- a/test/test_decaf.cxx +++ b/test/test_decaf.cxx @@ -13,6 +13,7 @@ #include #include #include +#include #include using namespace decaf; @@ -56,7 +57,6 @@ template struct Tests { typedef typename Group::Scalar Scalar; typedef typename Group::Point Point; typedef typename Group::DhLadder DhLadder; -typedef typename Group::EdDSA EdDSA; typedef typename Group::Precomputed Precomputed; static void print(const char *name, const Scalar &x) { @@ -477,7 +477,8 @@ static void test_cfrg_vectors() { /* EdDSA */ for (unsigned int t=0; eddsa_sk[t].size(); t++) { - SecureBuffer eddsa_pk2 = EdDSA::generate_key(eddsa_sk[t]); + typename EdDSA::PrivateKey priv(eddsa_sk[t]); + SecureBuffer eddsa_pk2 = priv.pub().serialize(); if (!memeq(SecureBuffer(eddsa_pk[t]), eddsa_pk2)) { test.fail(); printf(" EdDSA PK vectors disagree."); @@ -489,10 +490,10 @@ static void test_cfrg_vectors() { printf("\n"); } SecureBuffer sig; - if (EdDSA::SUPPORTS_CONTEXTS) { - sig = EdDSA::sign(eddsa_sk[t],eddsa_pk[t],eddsa_message[t],false,eddsa_context[t]); + if (priv.SUPPORTS_CONTEXTS) { + sig = priv.sign(eddsa_message[t],false,eddsa_context[t]); } else { - sig = EdDSA::sign(eddsa_sk[t],eddsa_pk[t],eddsa_message[t]); + sig = priv.sign(eddsa_message[t]); } if (!memeq(SecureBuffer(eddsa_sig[t]),sig)) { @@ -536,19 +537,19 @@ static void test_eddsa() { for (int i=0; i priv(rng); - SecureBuffer pub = EdDSA::generate_key(priv); + typename EdDSA::PrivateKey priv(rng); + typename EdDSA::PublicKey pub(priv); SecureBuffer message(i); rng.read(message); - SecureBuffer context(EdDSA::SUPPORTS_CONTEXTS ? i%256 : 0); + SecureBuffer context(priv.SUPPORTS_CONTEXTS ? i%256 : 0); rng.read(message); - SecureBuffer sig = EdDSA::sign(priv,pub,message,i%2,context); + SecureBuffer sig = priv.sign(message,i%2,context); try { - EdDSA::verify(sig,pub,message,i%2,context); + pub.verify(sig,message,i%2,context); } catch(CryptoException) { test.fail(); printf(" Signature validation failed on sig %d\n", i);