/* * 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 #include /** @cond internal */ #if __cplusplus >= 201103L #define DECAF_NOEXCEPT noexcept #else #define DECAF_NOEXCEPT throw() #endif /** @endcond */ /** Namespace for all libdecaf C++ objects. */ 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 */ template class Signing; template class Verification; class PublicKeyBase; class PrivateKeyBase; typedef class PrivateKeyBase PrivateKey, PrivateKeyPure, PrivateKeyPh; typedef class PublicKeyBase PublicKey, PublicKeyPure, PublicKeyPh; /** @endcond */ /** * Signatures support a "context" block, which allows you to domain separate them if * (for some reason) it's annoying to domain separate the message itself. The default * is no context. For Ed25519, the spec defining contexts is an extension, and the * default is not to use that extension. This makes "no context" different from * the empty string. For Ed448, contexts are built-in and mandatory, so "no context" * is the same as the empty string. */ #if DECAF_EDDSA_$(gf_shortname)_SUPPORTS_CONTEXTLESS_SIGS static inline const Block NO_CONTEXT() { return Block(DECAF_ED$(gf_shortname)_NO_CONTEXT,0); } #else static inline const Block NO_CONTEXT() { return Block(NULL,0); } #endif /** Prehash context for EdDSA. */ class Prehash : public $(re.sub(r"SHAKE(\d+)",r"SHAKE<\1>", eddsa_hash.upper())) { private: /** @cond internal */ typedef $(re.sub(r"SHAKE(\d+)",r"SHAKE<\1>", eddsa_hash.upper())) Super; SecureBuffer context_; template friend class Signing; template friend class Verification; void init() /*throw(LengthException)*/ { Super::reset(); if (context_.size() > 255) { throw LengthException(); } decaf_ed$(gf_shortname)_prehash_init((decaf_$(eddsa_hash)_ctx_s *)wrapped); } /** @endcond */ public: /** Number of output bytes in prehash */ static const size_t OUTPUT_BYTES = Super::DEFAULT_OUTPUT_BYTES; /** Create the prehash */ Prehash(const Block &context = NO_CONTEXT()) /*throw(LengthException)*/ { context_ = context; init(); } /** Reset this hash */ void reset() DECAF_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(); } }; /** Signing (i.e. private) key class template */ template class Signing; /** Signing (i.e. private) key class, PureEdDSA version */ 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. * * @warning It is generally unsafe to use Ed25519 with both prehashed and non-prehashed messages. */ inline SecureBuffer sign ( const Block &message, const Block &context = NO_CONTEXT() ) const /* throw(LengthException, std::bad_alloc) */ { if (context.size() > 255) { throw LengthException(); } SecureBuffer out(CRTP::SIG_BYTES); decaf_ed$(gf_shortname)_keypair_sign ( out.data(), ((const CRTP*)this)->keypair_, message.data(), message.size(), 0, context.data(), static_cast(context.size()) ); return out; } }; /** Signing (i.e. private) key class, prehashed version */ template class Signing { public: /** Sign a prehash context, and reset the context */ inline SecureBuffer sign_prehashed ( const Prehash &ph ) const /*throw(std::bad_alloc)*/ { SecureBuffer out(CRTP::SIG_BYTES); decaf_ed$(gf_shortname)_keypair_sign_prehash ( out.data(), ((const CRTP*)this)->keypair_, (const decaf_ed$(gf_shortname)_prehash_ctx_s*)ph.wrapped, ph.context_.data(), static_cast(ph.context_.size()) ); return out; } /** Sign a message using the prehasher */ inline SecureBuffer sign_with_prehash ( const Block &message, const Block &context = NO_CONTEXT() ) const /*throw(LengthException,CryptoException)*/ { Prehash ph(context); ph += message; return sign_prehashed(ph); } }; /** Signing (i.e. private) key base class */ class PrivateKeyBase : public Serializable , public Signing , public Signing { public: /** Type of public key corresponding to this private key */ typedef class PublicKeyBase PublicKey; private: /** @cond internal */ friend class PublicKeyBase; friend class Signing; friend class Signing; /** @endcond */ /** The expanded keypair. */ decaf_eddsa_$(gf_shortname)_keypair_t keypair_; public: /** Underlying group */ typedef $(cxx_ns) Group; /** Signature size. */ static const size_t SIG_BYTES = DECAF_EDDSA_$(gf_shortname)_SIGNATURE_BYTES; /** Serialization size. */ static const size_t SER_BYTES = DECAF_EDDSA_$(gf_shortname)_PRIVATE_BYTES; /** Create but don't initialize */ inline explicit PrivateKeyBase(const NOINIT&) DECAF_NOEXCEPT { } /** Read a private key from a string */ inline explicit PrivateKeyBase(const FixedBlock &b) DECAF_NOEXCEPT { *this = b; } /** Copy constructor */ inline PrivateKeyBase(const PrivateKeyBase &k) DECAF_NOEXCEPT { *this = k; } /** Create at random */ inline explicit PrivateKeyBase(Rng &r) DECAF_NOEXCEPT { FixedArrayBuffer priv(r); decaf_ed$(gf_shortname)_derive_keypair(keypair_, priv.data()); } /** Copy assignment */ inline PrivateKeyBase &operator=(const PrivateKey &k) DECAF_NOEXCEPT { memcpy(keypair_,k.keypair_,sizeof(keypair_)); return *this; } /** Create at random */ inline ~PrivateKeyBase() { decaf_ed$(gf_shortname)_keypair_destroy(keypair_); } /** Assignment from string */ inline PrivateKeyBase &operator=(const FixedBlock &b) DECAF_NOEXCEPT { decaf_ed$(gf_shortname)_derive_keypair(keypair_, b.data()); return *this; } /** Serialization size. */ inline size_t ser_size() const DECAF_NOEXCEPT { return SER_BYTES; } /** Serialize into a buffer. */ inline void serialize_into(unsigned char *x) const DECAF_NOEXCEPT { decaf_ed$(gf_shortname)_keypair_extract_private_key(x, keypair_); } /** Convert to X format (to be used for key exchange) */ inline SecureBuffer convert_to_x() const { SecureBuffer out(DECAF_X$(gf_shortname)_PRIVATE_BYTES); FixedArrayBuffer priv; serialize_into(priv.data()); decaf_ed$(gf_shortname)_convert_private_key_to_x$(gf_shortname)(out.data(), priv.data()); return out; } /** Return the corresponding public key */ inline PublicKey pub() const DECAF_NOEXCEPT { PublicKey pub(*this); return pub; } }; /* class PrivateKey */ /** Verification (i.e. public) EdDSA key, PureEdDSA version. */ template class Verification { public: /** Verify a signature, returning DECAF_FAILURE if verification fails */ inline decaf_error_t DECAF_WARN_UNUSED verify_noexcept ( const FixedBlock &sig, const Block &message, const Block &context = NO_CONTEXT() ) const /*DECAF_NOEXCEPT*/ { if (context.size() > 255) { return DECAF_FAILURE; } return decaf_ed$(gf_shortname)_verify ( sig.data(), ((const CRTP*)this)->pub_.data(), message.data(), message.size(), 0, context.data(), static_cast(context.size()) ); } /** 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. * * @warning It is generally unsafe to use Ed25519 with both prehashed and non-prehashed messages. */ inline void verify ( const FixedBlock &sig, const Block &message, const Block &context = NO_CONTEXT() ) const /*throw(LengthException,CryptoException)*/ { if (context.size() > 255) { throw LengthException(); } if (DECAF_SUCCESS != verify_noexcept( sig, message, context )) { throw CryptoException(); } } }; /** Verification (i.e. public) EdDSA key, prehashed version. */ template class Verification { public: /** Verify that a signature is valid for a given prehashed message, given the context. */ inline decaf_error_t DECAF_WARN_UNUSED verify_prehashed_noexcept ( const FixedBlock &sig, const Prehash &ph ) const /*DECAF_NOEXCEPT*/ { return decaf_ed$(gf_shortname)_verify_prehash ( sig.data(), ((const CRTP*)this)->pub_.data(), (const decaf_ed$(gf_shortname)_prehash_ctx_s*)ph.wrapped, ph.context_.data(), ph.context_.size() ); } /** Verify that a signature is valid for a given prehashed message, given the context. */ inline void verify_prehashed ( const FixedBlock &sig, const Prehash &ph ) const /*throw(CryptoException)*/ { if (DECAF_SUCCESS != decaf_ed$(gf_shortname)_verify_prehash ( sig.data(), ((const CRTP*)this)->pub_.data(), (const decaf_ed$(gf_shortname)_prehash_ctx_s*)ph.wrapped, ph.context_.data(), static_cast(ph.context_.size()) )) { throw CryptoException(); } } /** Hash and verify a message, using the prehashed verification mode. */ inline void verify_with_prehash ( const FixedBlock &sig, const Block &message, const Block &context = NO_CONTEXT() ) const /*throw(LengthException,CryptoException)*/ { Prehash ph(context); ph += message; verify_prehashed(sig,ph); } }; /** EdDSA Public key base class. */ class PublicKeyBase : public Serializable , public Verification , public Verification { public: /** Private key corresponding to this type of public key */ typedef class PrivateKeyBase PrivateKey; private: /** @cond internal */ friend class PrivateKeyBase; friend class Verification; friend class Verification; private: /** The pre-expansion form of the signature */ FixedArrayBuffer pub_; /** @endcond */ public: /* PERF FUTURE: Pre-cached decoding? Precomputed table?? */ /** Underlying group */ typedef $(cxx_ns) Group; /** Signature size. */ static const size_t SIG_BYTES = DECAF_EDDSA_$(gf_shortname)_SIGNATURE_BYTES; /** Serialization size. */ static const size_t SER_BYTES = DECAF_EDDSA_$(gf_shortname)_PRIVATE_BYTES; /** Create but don't initialize */ inline explicit PublicKeyBase(const NOINIT&) DECAF_NOEXCEPT : pub_((NOINIT())) { } /** Read a private key from a string */ inline explicit PublicKeyBase(const FixedBlock &b) DECAF_NOEXCEPT { *this = b; } /** Copy constructor */ inline PublicKeyBase(const PublicKeyBase &k) DECAF_NOEXCEPT { *this = k; } /** Copy constructor */ inline explicit PublicKeyBase(const PrivateKey &k) DECAF_NOEXCEPT { *this = k; } /** Assignment from string */ inline PublicKey &operator=(const FixedBlock &b) DECAF_NOEXCEPT { memcpy(pub_.data(),b.data(),b.size()); return *this; } /** Assignment from public key */ inline PublicKey &operator=(const PublicKey &p) DECAF_NOEXCEPT { return *this = p.pub_; } /** Assignment from private key */ inline PublicKey &operator=(const PrivateKey &p) DECAF_NOEXCEPT { decaf_ed$(gf_shortname)_keypair_extract_public_key(pub_.data(), p.keypair_); return *this; } /** Serialization size. */ inline size_t ser_size() const DECAF_NOEXCEPT { return SER_BYTES; } /** Serialize into a buffer. */ inline void serialize_into(unsigned char *x) const DECAF_NOEXCEPT { memcpy(x,pub_.data(), pub_.size()); } /** Convert to X format (to be used for key exchange) */ inline SecureBuffer convert_to_x() const { SecureBuffer out(DECAF_X$(gf_shortname)_PRIVATE_BYTES); decaf_ed$(gf_shortname)_convert_public_key_to_x$(gf_shortname)(out.data(), pub_.data()); return out; } }; /* class PublicKey */ }; /* template<> struct EdDSA<$(cxx_ns)> */ #undef DECAF_NOEXCEPT } /* namespace decaf */