/** * @file decaf/crypto.hxx * @author Mike Hamburg * * @copyright * Copyright (c) 2015 Cryptography Research, Inc. \n * Released under the MIT License. See LICENSE.txt for license information. * * @brief Example cryptography using Decaf. * @warning FIXME: this is out of sync with crypto_*.h */ #ifndef __DECAF_CRYPTO_HXX__ #define __DECAF_CRYPTO_HXX__ 1 #include #include /** @cond internal */ #if __cplusplus >= 201103L #define NOEXCEPT noexcept #else #define NOEXCEPT throw() #endif /** @endcond */ /* TODO: decide on copy vs reference */ namespace decaf { template class PrivateKey; /** @brief A public key using a particular EC group */ template class PublicKey : public Serializable > { private: /** @cond internal */ friend class PrivateKey; static const size_t CHALLENGE_BYTES = Group::Scalar::SER_BYTES; //const typename Group::Point p; FixedArrayBuffer ser; /** @endcond */ public: /** Create without init */ PublicKey(NOINIT) : ser(NOINIT()) {} /** SHAKE instance size for sigs etc */ static const size_t SHAKE_BITS = 256; /** Size of a signature */ static const size_t SIG_BYTES = Group::Point::SER_BYTES + Group::Scalar::SER_BYTES; /** @brief Set the public key to a point */ inline explicit PublicKey(const typename Group::Point &p) NOEXCEPT : ser(p.serialize()) {} /** @brief Get the private key for a given public key */ inline explicit PublicKey(const PrivateKey &priv) NOEXCEPT; /** @brief Read a private key from a string*/ inline explicit PublicKey(const FixedBlock &b) NOEXCEPT : ser(b) {} /** @brief Return the corresponding EC point */ inline typename Group::Point point() const throw(CryptoException) { return typename Group::Point(ser); } /** @brief Verify a sig. TODO: nothrow version? */ inline void verify_shake(const SHAKE &ctx_, const FixedBlock &sig) throw(CryptoException) { SHAKE ctx(ctx_); ctx << ser << sig.slice(0,Group::Point::SER_BYTES); FixedArrayBuffer challenge; ctx.output(challenge); typename Group::Scalar response; decaf_error_t scalar_OK = Group::Scalar::decode( response, sig.slice(Group::Point::SER_BYTES, Group::Scalar::SER_BYTES) ); const typename Group::Point combo = point().non_secret_combo_with_base( typename Group::Scalar(challenge), response ); if (!decaf_successful(scalar_OK) || combo != typename Group::Point(sig.slice(0,Group::Point::SER_BYTES))) throw CryptoException(); //return scalar_OK & (combo == typename Group::Point(sig.slice(0,Group::Point::SER_BYTES))); } /** @brief Sign from a message. */ inline void verify(const Block &message, const FixedBlock &sig) throw(CryptoException) { SHAKE ctx; ctx << message; verify_shake(ctx,sig); } /** @brief Serialize into a buffer. */ inline void serializeInto(unsigned char *x) const NOEXCEPT { memcpy(x,ser.data(),Group::Point::SER_BYTES); } /** @brief Serialize into a buffer. */ inline size_t serSize() const NOEXCEPT { return Group::Point::SER_BYTES; } /** @brief Copy operator */ inline PublicKey &operator=(const PublicKey &x) NOEXCEPT { ser = x.ser; return *this; } }; /** @brief A private key using a particular EC group */ template class PrivateKey : public Serializable > { public: /** Size of associated symmetric key */ static const size_t SYM_BYTES = 32; /** SHAKE instance size for sigs etc */ static const size_t SHAKE_BITS = PublicKey::SHAKE_BITS; private: /** @cond internal */ static const size_t SCALAR_HASH_BYTES = Group::Scalar::SER_BYTES + 8; friend class PublicKey; FixedArrayBuffer sym; typename Group::Scalar scalar; PublicKey pub_; /** @endcond */ public: /** @brief Don't initialize */ inline PrivateKey(const NOINIT &ni) NOEXCEPT : sym(ni), scalar(ni), pub_(ni) {} /** @brief Construct at random */ inline PrivateKey(Rng &r) : sym(r), scalar(SHAKE::hash(sym, SCALAR_HASH_BYTES)), pub_((Group::Precomputed::base() * scalar).serialize()) {} /** @brief Construct from buffer */ inline PrivateKey(const FixedBlock &sym_) : sym(sym_), scalar(SHAKE::hash(sym, SCALAR_HASH_BYTES)), pub_(SecureBuffer(Group::Precomputed::Base * scalar)) {} /** @brief Compressed representation */ inline const FixedBlock &ser_compressed() const NOEXCEPT { return sym; } /** @brief Serialize */ inline size_t serSize() const NOEXCEPT { return SYM_BYTES; } /** @brief Serialize */ inline void serializeInto(unsigned char *target) const NOEXCEPT { memcpy(target,sym.data(),serSize()); } /** @brief Uncompressed representation */ inline SecureBuffer ser_uncompressed() const throw(std::bad_alloc) { SecureBuffer b(SYM_BYTES + Group::Scalar::SER_BYTES + Group::Point::SER_BYTES); Buffer(b).slice(0,SYM_BYTES).assign(sym); Buffer(b).slice(SYM_BYTES,Group::Scalar::SER_BYTES).assign(scalar); Buffer(b).slice(SYM_BYTES+Group::Scalar::SER_BYTES,Group::Point::SER_BYTES).assign(pub_.ser); return b; } /** @brief Sign from a SHAKE context. TODO: double check random oracle eval of this; destructive version? */ inline SecureBuffer sign_shake(const SHAKE &ctx_) throw(std::bad_alloc) { SHAKE ctx(ctx_); ctx << sym << "decaf_255_sign_shake"; typename Group::Scalar nonce(ctx.output(SCALAR_HASH_BYTES)); SecureBuffer g_nonce = (Group::Precomputed::base() * nonce).serialize(); ctx = ctx_; ctx << pub_.ser << g_nonce; SecureBuffer challenge(ctx.output(PublicKey::CHALLENGE_BYTES)); SecureBuffer response((nonce - scalar * typename Group::Scalar(challenge)).serialize()); SecureBuffer ret(PublicKey::SIG_BYTES); Buffer(ret).slice(0,Group::Point::SER_BYTES).assign(g_nonce); Buffer(ret).slice(Group::Point::SER_BYTES, Group::Scalar::SER_BYTES).assign(response); return ret; } /** @brief Sign from a message. */ inline SecureBuffer sign(const Block &message) { SHAKE ctx; ctx << message; return sign_shake(ctx); } /** @brief Get the corresponding public key */ inline const PublicKey &pub() const { return pub_; } /** @brief Copy operator */ inline PrivateKey &operator=(const PrivateKey &x) NOEXCEPT { sym = x.sym; scalar = x.scalar; pub_ = x.pub_; return *this; } }; /** @cond internal */ template inline PublicKey::PublicKey( const PrivateKey &priv ) NOEXCEPT : ser(priv.pub_.ser){} /** @endcond */ #undef NOEXCEPT } /* namespace decaf */ #endif /* __DECAF_CRYPTO_HXX__ */