diff --git a/Makefile b/Makefile index 10549f6..092d340 100644 --- a/Makefile +++ b/Makefile @@ -298,7 +298,7 @@ doc: Doxyfile $(BUILD_OBJ)/timestamp $(HEADERS) # (cd $(BATNAME)/.. && tar czf $(BATBASE).tgz $(BATBASE) ) # Finds todo items in .h and .c files -TODO_TYPES ?= HACK TODO FIXME BUG XXX PERF FUTURE REMOVE MAGIC UNIFY +TODO_TYPES ?= HACK TODO @todo FIXME BUG XXX PERF FUTURE REMOVE MAGIC UNIFY TODO_LOCATIONS ?= src test Makefile Doxyfile todo:: @(find $(TODO_LOCATIONS) -name '*.h' -or -name '*.c' -or -name '*.cxx' -or -name '*.hxx' -or -name '*.py') | xargs egrep --color=auto -w \ diff --git a/src/per_curve/eddsa.tmpl.hxx b/src/per_curve/eddsa.tmpl.hxx index 0e2bdcf..2e60f36 100644 --- a/src/per_curve/eddsa.tmpl.hxx +++ b/src/per_curve/eddsa.tmpl.hxx @@ -28,10 +28,25 @@ template struct EdDSA; /** A public key for crypto over $(name) */ template<> struct EdDSA<$(cxx_ns)> { + +enum Prehashed { PURE, PREHASHED }; /** @cond internal */ -class PrivateKey; -class PublicKey; +template class Signing; +template class Verification; +$(""" +class PublicKeyBase; +class PrivateKeyBase; +typedef class PrivateKeyBase PrivateKey, PrivateKeyPure, PrivateKeyPh; +typedef class PublicKeyBase PublicKey, PublicKeyPure, PublicKeyPh; + """ if eddsa_supports_contexts else """ +template class PublicKeyBase; +template class PrivateKeyBase; +typedef class PublicKeyBase PublicKey, PublicKeyPure; +typedef class PublicKeyBase PublicKeyPh; +typedef class PrivateKeyBase PrivateKey, PrivateKeyPure; +typedef class PrivateKeyBase PrivateKeyPh; +""") /** @endcond */ /** Prehash context for EdDSA. TODO: test me! */ @@ -43,8 +58,8 @@ public: private: typedef $(re.sub(r"SHAKE(\d+)",r"SHAKE<\1>", eddsa_hash.upper())) Super; SecureBuffer context_; - friend class PrivateKey; - friend class PublicKey; + template friend class Signing; + template friend class Verification; void init() throw(LengthException) { Super::reset(); @@ -90,11 +105,94 @@ public: } }; -class PrivateKey : public Serializable { +template class Signing; + +template class Signing { +public: + /* Sign a prehash context, and reset the context */ + inline SecureBuffer sign_prehashed ( Prehash &ph ) const throw(std::bad_alloc) { + SecureBuffer out(CRTP::SIG_BYTES); + FixedArrayBuffer tmp; + ph.final(tmp); + $(c_ns)_eddsa_sign ( + out.data(), + ((const CRTP*)this)->priv_.data(), + ((const CRTP*)this)->pub_.data(), + tmp.data(), + tmp.size(), + 1 +#if $(C_NS)_EDDSA_SUPPORTS_CONTEXTS + , ph.context_.data(), + ph.context_.size() +#endif + ); + } +}; + +template class Signing { +public: + /** + * Sign a message. + * @param [in] message The message to be signed. + * @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, + const Block &context = Block(NULL,0) + ) const /* TODO: this exn spec tickles a Clang bug? + * throw(LengthException, std::bad_alloc) + */ { + if (context.size() > 255 + || (context.size() != 0 && !CRTP::SUPPORTS_CONTEXTS) + ) { + throw LengthException(); + } + + SecureBuffer out(CRTP::SIG_BYTES); + $(c_ns)_eddsa_sign ( + out.data(), + ((const CRTP*)this)->priv_.data(), + ((const CRTP*)this)->pub_.data(), + message.data(), + message.size(), + 0 +#if $(C_NS)_EDDSA_SUPPORTS_CONTEXTS + , context.data(), + context.size() +#endif + ); + return out; + } +}; + +$(""" +class PrivateKeyBase + : public Serializable + , public Signing + , public Signing { +public: + typedef class PublicKeyBase MyPublicKey; +private: +/** @cond internal */ + friend class PublicKeyBase; + friend class Signing; + friend class Signing; +/** @endcond */ +""" if eddsa_supports_contexts else """ +template class PrivateKeyBase + : public Serializable > + , public Signing,ph> { +public: + typedef class PublicKeyBase MyPublicKey; private: /** @cond internal */ - friend class PublicKey; + friend class PublicKeyBase; + friend class Signing, ph>; /** @endcond */ +""") /** The pre-expansion form of the signing key. */ FixedArrayBuffer<$(C_NS)_EDDSA_PRIVATE_BYTES> priv_; @@ -117,16 +215,16 @@ public: /** Create but don't initialize */ - inline explicit PrivateKey(const NOINIT&) NOEXCEPT : priv_((NOINIT())), pub_((NOINIT())) { } + inline explicit PrivateKeyBase(const NOINIT&) NOEXCEPT : priv_((NOINIT())), pub_((NOINIT())) { } /** Read a private key from a string */ - inline explicit PrivateKey(const FixedBlock &b) NOEXCEPT { *this = b; } + inline explicit PrivateKeyBase(const FixedBlock &b) NOEXCEPT { *this = b; } /** Copy constructor */ - inline PrivateKey(const PrivateKey &k) NOEXCEPT { *this = k; } + inline PrivateKeyBase(const PrivateKey &k) NOEXCEPT { *this = k; } /** Create at random */ - inline explicit PrivateKey(Rng &r) NOEXCEPT : priv_(r) { + inline explicit PrivateKeyBase(Rng &r) NOEXCEPT : priv_(r) { $(c_ns)_eddsa_derive_public_key(pub_.data(), priv_.data()); } @@ -153,68 +251,148 @@ public: } /** Return the corresponding public key */ - inline PublicKey pub() const NOEXCEPT { - PublicKey pub(*this); + inline MyPublicKey pub() const NOEXCEPT { + MyPublicKey pub(*this); return pub; } +}; /* class PrivateKey */ + + + +template class Verification { +public: + /** Verify a signature, returning DECAF_FAILURE if verification fails */ + inline decaf_error_t WARN_UNUSED verify_noexcept ( + const FixedBlock<$(C_NS)_EDDSA_SIGNATURE_BYTES> &sig, + const Block &message, + const Block &context = Block(NULL,0) + ) const /*NOEXCEPT*/ { + if (context.size() > 255 + || (context.size() != 0 && !CRTP::SUPPORTS_CONTEXTS) + ) { + return DECAF_FAILURE; + } + + return $(c_ns)_eddsa_verify ( + sig.data(), + ((const CRTP*)this)->pub_.data(), + message.data(), + message.size(), + 0 +#if $(C_NS)_EDDSA_SUPPORTS_CONTEXTS + , context.data(), + context.size() +#endif + ); + } - /** - * Sign a message. - * @param [in] message The message to be signed. - * @param [in] prehashed If true, the message to be signed is already hashed. + /** Verify a signature, throwing an exception if verification fails + * @param [in] sig The signature. + * @param [in] message The signed message. * @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 ( + inline void verify ( + const FixedBlock<$(C_NS)_EDDSA_SIGNATURE_BYTES> &sig, const Block &message, - bool prehashed = false, const Block &context = Block(NULL,0) - ) const throw(LengthException, std::bad_alloc) { + ) const /*throw(LengthException,CryptoException)*/ { if (context.size() > 255 - || (context.size() != 0 && !SUPPORTS_CONTEXTS) + || (context.size() != 0 && !CRTP::SUPPORTS_CONTEXTS) ) { throw LengthException(); } - SecureBuffer out(SIG_BYTES); - $(c_ns)_eddsa_sign ( - out.data(), - priv_.data(), - pub_.data(), - message.data(), - message.size(), - prehashed + if (DECAF_SUCCESS != verify_noexcept( sig, message, context )) { + throw CryptoException(); + } + } +}; + + +template class Verification { +public: + /* Verify a prehash context, and reset the context */ + inline decaf_error_t WARN_UNUSED verify_prehashed_noexcept ( + const FixedBlock<$(C_NS)_EDDSA_SIGNATURE_BYTES> &sig, + Prehash &ph + ) const /*NOEXCEPT*/ { + FixedArrayBuffer m; + ph.final(m); + return $(c_ns)_eddsa_verify ( + sig.data(), + ((const CRTP*)this)->pub_.data(), + m.data(), + m.size(), + 1 #if $(C_NS)_EDDSA_SUPPORTS_CONTEXTS - , context.data(), - context.size() + , ph.context_.data(), + ph.context_.size() #endif ); - return out; } - - /* Sign a prehash context, and reset the context */ - inline SecureBuffer sign ( Prehash &ph ) const throw(std::bad_alloc) { + + /* Verify a prehash context, and reset the context */ + inline void verify_prehashed ( + const FixedBlock<$(C_NS)_EDDSA_SIGNATURE_BYTES> &sig, + Prehash &ph + ) const /*throw(CryptoException)*/ { FixedArrayBuffer m; ph.final(m); - return sign(m, true, ph.context_); + if (DECAF_SUCCESS != $(c_ns)_eddsa_verify ( + sig.data(), + ((const CRTP*)this)->pub_.data(), + m.data(), + m.size(), + 1 +#if $(C_NS)_EDDSA_SUPPORTS_CONTEXTS + , ph.context_.data(), + ph.context_.size() +#endif + )) { + throw CryptoException(); + } } -}; /* class PrivateKey */ +}; -class PublicKey : public Serializable { + +$(""" +class PublicKeyBase + : public Serializable + , public Verification + , public Verification { +public: + typedef class PrivateKeyBase MyPrivateKey; + private: /** @cond internal */ - friend class PrivateKey; + friend class PrivateKeyBase; + friend class Verification; + friend class Verification; /** @endcond */ - +""" if eddsa_supports_contexts else """ +template class PublicKeyBase + : public Serializable > + , public Verification,ph> { public: + typedef class PrivateKeyBase MyPrivateKey; + +private: +/** @cond internal */ + friend class PrivateKeyBase; + friend class Verification, ph>; +/** @endcond */ +""") + +private: /** The pre-expansion form of the signature */ FixedArrayBuffer<$(C_NS)_EDDSA_PUBLIC_BYTES> pub_; - /* PERF FUTURE: Pre-cached decoding? Precomputed table?? */ - public: + /* PERF FUTURE: Pre-cached decoding? Precomputed table?? */ + /** Underlying group */ typedef $(cxx_ns) Group; @@ -229,16 +407,16 @@ public: /** Create but don't initialize */ - inline explicit PublicKey(const NOINIT&) NOEXCEPT : pub_((NOINIT())) { } + inline explicit PublicKeyBase(const NOINIT&) NOEXCEPT : pub_((NOINIT())) { } /** Read a private key from a string */ - inline explicit PublicKey(const FixedBlock &b) NOEXCEPT { *this = b; } + inline explicit PublicKeyBase(const FixedBlock &b) NOEXCEPT { *this = b; } /** Copy constructor */ - inline PublicKey(const PublicKey &k) NOEXCEPT { *this = k; } + inline PublicKeyBase(const PublicKeyBase &k) NOEXCEPT { *this = k; } /** Copy constructor */ - inline explicit PublicKey(const PrivateKey &k) NOEXCEPT { *this = k; } + inline explicit PublicKeyBase(const MyPrivateKey &k) NOEXCEPT { *this = k; } /** Assignment from string */ inline PublicKey &operator=(const FixedBlock &b) NOEXCEPT { @@ -252,11 +430,10 @@ public: } /** Assignment from private key */ - inline PublicKey &operator=(const PrivateKey &p) NOEXCEPT { + inline PublicKey &operator=(const MyPrivateKey &p) NOEXCEPT { return *this = p.pub_; } - /** Serialization size. */ inline size_t ser_size() const NOEXCEPT { return SER_BYTES; } @@ -264,78 +441,6 @@ public: 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)> */ diff --git a/test/test_decaf.cxx b/test/test_decaf.cxx index 1ac8568..583b1f0 100644 --- a/test/test_decaf.cxx +++ b/test/test_decaf.cxx @@ -491,7 +491,7 @@ static void test_cfrg_vectors() { } SecureBuffer sig; if (priv.SUPPORTS_CONTEXTS) { - sig = priv.sign(eddsa_message[t],false,eddsa_context[t]); + sig = priv.sign(eddsa_message[t],eddsa_context[t]); } else { sig = priv.sign(eddsa_message[t]); } @@ -546,10 +546,10 @@ static void test_eddsa() { SecureBuffer context(priv.SUPPORTS_CONTEXTS ? i%256 : 0); rng.read(message); - SecureBuffer sig = priv.sign(message,i%2,context); + SecureBuffer sig = priv.sign(message,context); try { - pub.verify(sig,message,i%2,context); + pub.verify(sig,message,context); } catch(CryptoException) { test.fail(); printf(" Signature validation failed on sig %d\n", i);