/* * 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 */ /** Prehash context for EdDSA. TODO: test me! */ 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 ser_size() 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_; /* PERF FUTURE: Pre-cached decoding? Precomputed table?? */ 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 ser_size() 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 */