| @@ -170,7 +170,7 @@ LIBCOMPONENTS += $$(BUILD_OBJ)/$(1)/decaf.o $$(BUILD_OBJ)/$(1)/elligator.o $$(BU | |||||
| PER_OBJ_DIRS += $$(BUILD_OBJ)/$(1) | PER_OBJ_DIRS += $$(BUILD_OBJ)/$(1) | ||||
| GLOBAL_HEADERS_OF_$(1) = $(BUILD_INC)/decaf/decaf_$(3).h $(BUILD_INC)/decaf/decaf_$(3).hxx \ | 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/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_OF_$(1) = $$(HEADERS_OF_$(2)) $$(GLOBAL_HEADERS_OF_$(1)) | ||||
| HEADERS += $$(GLOBAL_HEADERS_OF_$(1)) | HEADERS += $$(GLOBAL_HEADERS_OF_$(1)) | ||||
| @@ -50,7 +50,7 @@ def fillin(template,data): | |||||
| if template[position] == '(': parens += 1 | if template[position] == '(': parens += 1 | ||||
| elif template[position] == ')': parens -= 1 | elif template[position] == ')': parens -= 1 | ||||
| position += 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 | author = "Mike Hamburg" # FUTURE | ||||
| for name in args.files: | for name in args.files: | ||||
| @@ -21,7 +21,7 @@ | |||||
| #include <string.h> /* for memcpy */ | #include <string.h> /* for memcpy */ | ||||
| #include <decaf/decaf_$(gf_bits).h> | #include <decaf/decaf_$(gf_bits).h> | ||||
| #include <decaf/eddsa_$(gf_bits).h> /* TODO: move eddsa to another file? */ | |||||
| #include <decaf/eddsa_$(gf_bits).h> | |||||
| #include <decaf/secure_buffer.hxx> | #include <decaf/secure_buffer.hxx> | ||||
| #include <string> | #include <string> | ||||
| #include <sys/types.h> | #include <sys/types.h> | ||||
| @@ -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<PRIVATE_BYTES> &priv | |||||
| ) { | |||||
| SecureBuffer out(PUBLIC_BYTES); | |||||
| $(c_ns)_eddsa_derive_public_key(out.data(), priv.data()); | |||||
| return out; | |||||
| } | |||||
| static inline SecureBuffer sign ( | |||||
| const FixedBlock<PRIVATE_BYTES> &priv, | |||||
| const FixedBlock<PUBLIC_BYTES> &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<SIGNATURE_BYTES> &sig, | |||||
| const FixedBlock<PUBLIC_BYTES> &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<SIGNATURE_BYTES> &sig, | |||||
| const FixedBlock<PUBLIC_BYTES> &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) */ | }; /* struct $(cxx_ns) */ | ||||
| /** @cond internal */ | /** @cond internal */ | ||||
| @@ -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 <decaf/decaf_$(gf_bits).hxx> | |||||
| #include <decaf/eddsa_$(gf_bits).h> | |||||
| #include <decaf/shake.hxx> | |||||
| #include <decaf/sha512.hxx> | |||||
| /** @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 <typename Group> 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<PrivateKey> { | |||||
| 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<SER_BYTES> &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<SER_BYTES> &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<Prehash::OUTPUT_BYTES> m; | |||||
| ph.final(m); | |||||
| return sign(m, true, ph.context_); | |||||
| } | |||||
| }; /* class PrivateKey */ | |||||
| class PublicKey : public Serializable<PublicKey> { | |||||
| 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<SER_BYTES> &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<SER_BYTES> &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_BYTES> &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_BYTES> &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_BYTES> &sig, | |||||
| Prehash &ph | |||||
| ) const NOEXCEPT { | |||||
| FixedArrayBuffer<Prehash::OUTPUT_BYTES> 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_BYTES> &sig, | |||||
| Prehash &ph | |||||
| ) const throw(CryptoException) { | |||||
| FixedArrayBuffer<Prehash::OUTPUT_BYTES> m; | |||||
| ph.final(m); | |||||
| verify(sig, m, true, ph.context_); | |||||
| } | |||||
| }; /* class PublicKey */ | |||||
| }; /* template<> struct EdDSA<$(cxx_ns)> */ | |||||
| #undef NOEXCEPT | |||||
| } /* namespace decaf */ | |||||
| @@ -0,0 +1,7 @@ | |||||
| /** | |||||
| * EdDSA crypto routines, metaheader. | |||||
| */ | |||||
| $("\n".join([ | |||||
| "#include <decaf/eddsa_%s.hxx>" % g for g in sorted([c["bits"] for _,c in curve.iteritems()]) | |||||
| ])) | |||||
| @@ -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 <decaf/secure_buffer.hxx> | |||||
| #include <decaf/sha512.h> | |||||
| #include <sys/types.h> | |||||
| /** @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__ */ | |||||
| @@ -76,6 +76,29 @@ void sha3_output ( | |||||
| size_t len | size_t len | ||||
| ) API_VIS; | ) 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, | * @brief Return the default output length of the sponge construction, | ||||
| * for the purpose of C++ default operators. | * 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. */ | const keccak_sponge_t sponge /**< [inout] The context. */ | ||||
| ) API_VIS; | ) 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. | * @brief Destroy a SHA3 or SHAKE sponge context by overwriting it with 0. | ||||
| * @param [out] sponge The context. | * @param [out] sponge The context. | ||||
| @@ -12,7 +12,7 @@ | |||||
| #define __DECAF_SHAKE_HXX__ | #define __DECAF_SHAKE_HXX__ | ||||
| #include <decaf/shake.h> | #include <decaf/shake.h> | ||||
| #include <decaf/secure_buffer.hxx> | |||||
| #include <sys/types.h> | #include <sys/types.h> | ||||
| /** @cond internal */ | /** @cond internal */ | ||||
| @@ -43,28 +43,44 @@ protected: | |||||
| public: | public: | ||||
| /** Add more data to running hash */ | /** 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. */ | /** 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. */ | /** 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 <<. */ | /** 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. | * @brief Output bytes from the sponge. | ||||
| * @todo make this throw exceptions. | * @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. */ | /** @brief Return the sponge's default output size. */ | ||||
| @@ -72,11 +88,24 @@ public: | |||||
| return sponge_default_output_bytes(sp); | 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. */ | /** Output the default number of bytes. */ | ||||
| inline SecureBuffer output() { | |||||
| inline SecureBuffer output() throw(std::bad_alloc) { | |||||
| return output(default_output_size()); | 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 */ | /** Destructor zeroizes state */ | ||||
| inline ~KeccakHash() NOEXCEPT { sponge_destroy(sp); } | inline ~KeccakHash() NOEXCEPT { sponge_destroy(sp); } | ||||
| }; | }; | ||||
| @@ -91,12 +120,12 @@ public: | |||||
| /** Number of bytes of output */ | /** Number of bytes of output */ | ||||
| static const size_t MAX_OUTPUT_BYTES = bits/8; | static const size_t MAX_OUTPUT_BYTES = bits/8; | ||||
| /** Number of bytes of output */ | |||||
| static const size_t DEFAULT_OUTPUT_BYTES = bits/8; | |||||
| /** Initializer */ | /** Initializer */ | ||||
| inline SHA3() NOEXCEPT : KeccakHash(get_params()) {} | 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. | /** Hash bytes with this SHA3 instance. | ||||
| * @throw LengthException if nbytes > MAX_OUTPUT_BYTES | * @throw LengthException if nbytes > MAX_OUTPUT_BYTES | ||||
| */ | */ | ||||
| @@ -114,12 +143,20 @@ class SHAKE : public KeccakHash { | |||||
| private: | private: | ||||
| /** Get the parameter template block for this hash */ | /** Get the parameter template block for this hash */ | ||||
| static inline const struct kparams_s *get_params(); | static inline const struct kparams_s *get_params(); | ||||
| public: | 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 */ | /** Initializer */ | ||||
| inline SHAKE() NOEXCEPT : KeccakHash(get_params()) {} | 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 */ | /** Hash bytes with this SHAKE instance */ | ||||
| static inline SecureBuffer hash(const Block &b, size_t outlen) throw(std::bad_alloc) { | static inline SecureBuffer hash(const Block &b, size_t outlen) throw(std::bad_alloc) { | ||||
| @@ -57,7 +57,7 @@ typedef union { | |||||
| } kdomain_t[1]; | } kdomain_t[1]; | ||||
| typedef struct kparams_s { | 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]; | } kparams_t[1]; | ||||
| typedef struct keccak_sponge_s { | typedef struct keccak_sponge_s { | ||||
| @@ -174,8 +174,8 @@ void sha3_output ( | |||||
| assert(sponge->params->rate < sizeof(sponge->state)); | assert(sponge->params->rate < sizeof(sponge->state)); | ||||
| if (sponge->params->maxOut != 0xFF) { | 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) { | 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_destroy (keccak_sponge_t sponge) { decaf_bzero(sponge, sizeof(keccak_sponge_t)); } | ||||
| void sponge_init ( | void sponge_init ( | ||||
| @@ -216,6 +232,7 @@ void sponge_init ( | |||||
| ) { | ) { | ||||
| memset(sponge->state, 0, sizeof(sponge->state)); | memset(sponge->state, 0, sizeof(sponge->state)); | ||||
| sponge->params[0] = params[0]; | sponge->params[0] = params[0]; | ||||
| sponge->params->position = 0; | |||||
| } | } | ||||
| void sponge_hash ( | void sponge_hash ( | ||||
| @@ -234,11 +251,11 @@ void sponge_hash ( | |||||
| #define DEFSHAKE(n) \ | #define DEFSHAKE(n) \ | ||||
| const struct kparams_s SHAKE##n##_params_s = \ | 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) \ | #define DEFSHA3(n) \ | ||||
| const struct kparams_s SHA3_##n##_params_s = \ | 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 ( | size_t sponge_default_output_bytes ( | ||||
| const keccak_sponge_t s | const keccak_sponge_t s | ||||
| @@ -248,6 +265,14 @@ size_t sponge_default_output_bytes ( | |||||
| : ((200-s->params->rate)/2); | : ((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(128) | ||||
| DEFSHAKE(256) | DEFSHAKE(256) | ||||
| DEFSHA3(224) | DEFSHA3(224) | ||||
| @@ -11,11 +11,13 @@ | |||||
| #include <decaf.hxx> | #include <decaf.hxx> | ||||
| #include <decaf/shake.hxx> | #include <decaf/shake.hxx> | ||||
| #include <decaf/sha512.hxx> | |||||
| #include <decaf/strobe.hxx> | #include <decaf/strobe.hxx> | ||||
| #include <decaf/spongerng.hxx> | #include <decaf/spongerng.hxx> | ||||
| #include <decaf/crypto_255.h> | #include <decaf/crypto_255.h> | ||||
| #include <decaf/crypto_448.h> | #include <decaf/crypto_448.h> | ||||
| #include <decaf/crypto.hxx> | #include <decaf/crypto.hxx> | ||||
| #include <decaf/eddsa.hxx> | |||||
| #include <stdio.h> | #include <stdio.h> | ||||
| #include <sys/time.h> | #include <sys/time.h> | ||||
| #include <assert.h> | #include <assert.h> | ||||
| @@ -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 keygen"); b.iter(); ) { Group::DhLadder::generate_key(s1); } | ||||
| for (Benchmark b("RFC 7748 shared secret"); b.iter(); ) { Group::DhLadder::shared_secret(base,s1); } | for (Benchmark b("RFC 7748 shared secret"); b.iter(); ) { Group::DhLadder::shared_secret(base,s1); } | ||||
| FixedArrayBuffer<Group::EdDSA::PRIVATE_BYTES> 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<EdDSA<Group>::PrivateKey::SER_BYTES> e1(rng); | |||||
| typename EdDSA<Group>::PublicKey pub((NOINIT())); | |||||
| typename EdDSA<Group>::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() { | static void macro() { | ||||
| @@ -408,11 +413,13 @@ int main(int argc, char **argv) { | |||||
| SHAKE<128> shake1; | SHAKE<128> shake1; | ||||
| SHAKE<256> shake2; | SHAKE<256> shake2; | ||||
| SHA3<512> sha5; | SHA3<512> sha5; | ||||
| SHA512 sha2; | |||||
| Strobe strobe("example::bench",Strobe::CLIENT); | Strobe strobe("example::bench",Strobe::CLIENT); | ||||
| unsigned char b1024[1024] = {1}; | unsigned char b1024[1024] = {1}; | ||||
| for (Benchmark b("SHAKE128 1kiB", 30); b.iter(); ) { shake1 += Buffer(b1024,1024); } | 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("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("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.dh_key(Buffer(b1024,1024)); | ||||
| strobe.respec(STROBE_128); | strobe.respec(STROBE_128); | ||||
| for (Benchmark b("STROBE128 1kiB", 10); b.iter(); ) { | for (Benchmark b("STROBE128 1kiB", 10); b.iter(); ) { | ||||
| @@ -13,6 +13,7 @@ | |||||
| #include <decaf/spongerng.hxx> | #include <decaf/spongerng.hxx> | ||||
| #include <decaf/crypto.h> | #include <decaf/crypto.h> | ||||
| #include <decaf/crypto.hxx> | #include <decaf/crypto.hxx> | ||||
| #include <decaf/eddsa.hxx> | |||||
| #include <stdio.h> | #include <stdio.h> | ||||
| using namespace decaf; | using namespace decaf; | ||||
| @@ -56,7 +57,6 @@ template<typename Group> struct Tests { | |||||
| typedef typename Group::Scalar Scalar; | typedef typename Group::Scalar Scalar; | ||||
| typedef typename Group::Point Point; | typedef typename Group::Point Point; | ||||
| typedef typename Group::DhLadder DhLadder; | typedef typename Group::DhLadder DhLadder; | ||||
| typedef typename Group::EdDSA EdDSA; | |||||
| typedef typename Group::Precomputed Precomputed; | typedef typename Group::Precomputed Precomputed; | ||||
| static void print(const char *name, const Scalar &x) { | static void print(const char *name, const Scalar &x) { | ||||
| @@ -477,7 +477,8 @@ static void test_cfrg_vectors() { | |||||
| /* EdDSA */ | /* EdDSA */ | ||||
| for (unsigned int t=0; eddsa_sk[t].size(); t++) { | for (unsigned int t=0; eddsa_sk[t].size(); t++) { | ||||
| SecureBuffer eddsa_pk2 = EdDSA::generate_key(eddsa_sk[t]); | |||||
| typename EdDSA<Group>::PrivateKey priv(eddsa_sk[t]); | |||||
| SecureBuffer eddsa_pk2 = priv.pub().serialize(); | |||||
| if (!memeq(SecureBuffer(eddsa_pk[t]), eddsa_pk2)) { | if (!memeq(SecureBuffer(eddsa_pk[t]), eddsa_pk2)) { | ||||
| test.fail(); | test.fail(); | ||||
| printf(" EdDSA PK vectors disagree."); | printf(" EdDSA PK vectors disagree."); | ||||
| @@ -489,10 +490,10 @@ static void test_cfrg_vectors() { | |||||
| printf("\n"); | printf("\n"); | ||||
| } | } | ||||
| SecureBuffer sig; | 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 { | } 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)) { | if (!memeq(SecureBuffer(eddsa_sig[t]),sig)) { | ||||
| @@ -536,19 +537,19 @@ static void test_eddsa() { | |||||
| for (int i=0; i<NTESTS && test.passing_now; i++) { | for (int i=0; i<NTESTS && test.passing_now; i++) { | ||||
| FixedArrayBuffer<EdDSA::PRIVATE_BYTES> priv(rng); | |||||
| SecureBuffer pub = EdDSA::generate_key(priv); | |||||
| typename EdDSA<Group>::PrivateKey priv(rng); | |||||
| typename EdDSA<Group>::PublicKey pub(priv); | |||||
| SecureBuffer message(i); | SecureBuffer message(i); | ||||
| rng.read(message); | rng.read(message); | ||||
| SecureBuffer context(EdDSA::SUPPORTS_CONTEXTS ? i%256 : 0); | |||||
| SecureBuffer context(priv.SUPPORTS_CONTEXTS ? i%256 : 0); | |||||
| rng.read(message); | rng.read(message); | ||||
| SecureBuffer sig = EdDSA::sign(priv,pub,message,i%2,context); | |||||
| SecureBuffer sig = priv.sign(message,i%2,context); | |||||
| try { | try { | ||||
| EdDSA::verify(sig,pub,message,i%2,context); | |||||
| pub.verify(sig,message,i%2,context); | |||||
| } catch(CryptoException) { | } catch(CryptoException) { | ||||
| test.fail(); | test.fail(); | ||||
| printf(" Signature validation failed on sig %d\n", i); | printf(" Signature validation failed on sig %d\n", i); | ||||