/** * @file decaf/strobe.hxx * @copyright * Based on CC0 code by David Leon Gil, 2015 \n * Copyright (c) 2015 Cryptography Research, Inc. \n * Released under the MIT License. See LICENSE.txt for license information. * @author Mike Hamburg * @brief STROBE instances, C++ wrapper. * @warning This protocol framework is entirely experimental, and shouldn't be * relied on for anything serious yet. */ #ifndef __DECAF_STROBE_HXX__ #define __DECAF_STROBE_HXX__ #include #include /** @cond internal */ #if __cplusplus >= 201103L #define NOEXCEPT noexcept #define DELETE = delete #else #define NOEXCEPT throw() #define DELETE #endif /** @endcond */ namespace decaf { /** @brief An exception for misused protocol, eg encrypt with no key. */ class ProtocolException : public std::exception { public: /** @return "ProtocolException" */ virtual const char * what() const NOEXCEPT { return "ProtocolException"; } }; /** STROBE protocol framework object */ class Strobe { public: /** The wrapped object */ keccak_decaf_TOY_strobe_t wrapped; /** Number of bytes in a default authentication size. */ static const uint16_t DEFAULT_AUTH_SIZE = 16; /** Am I a server or a client? */ enum client_or_server { SERVER, CLIENT }; /** Create protocol object. */ inline Strobe ( const char *description, /**< Description of this protocol. */ client_or_server whoami, /**< Am I client or server? */ const decaf_kparams_s ¶ms = STROBE_256 /**< Strength parameters */ ) NOEXCEPT { decaf_TOY_strobe_init(wrapped, ¶ms, description, whoami == CLIENT); keyed = false; } /** Securely destroy by overwriting state. */ inline ~Strobe() NOEXCEPT { decaf_TOY_strobe_destroy(wrapped); } /** Stir in fixed key, from a C++ block. */ inline void fixed_key ( const Block &data /**< The key. */ ) throw(ProtocolException) { decaf_TOY_strobe_fixed_key(wrapped, data.data(), data.size()); keyed = true; } /** Stir in fixed key, from a serializeable object. */ template inline void fixed_key ( const Serializable &data /**< The key. */ ) throw(ProtocolException) { fixed_key(data.serialize()); } /** Stir in DH key, from a C++ block. */ inline void dh_key ( const Block &data /**< The key. */ ) throw(ProtocolException) { decaf_TOY_strobe_dh_key(wrapped, data.data(), data.size()); keyed = true; } /** Stir in DH key, from a serializeable object. */ template inline void dh_key ( const Serializable &data /**< The key. */ ) throw(ProtocolException) { dh_key(data.serialize()); } /** Stir in an explicit nonce. */ inline void nonce(const Block &data) NOEXCEPT { decaf_TOY_strobe_nonce(wrapped, data.data(), data.size()); } /** Stir in data we sent as plaintext. NB This doesn't actually send anything. */ inline void send_plaintext(const Block &data) NOEXCEPT { decaf_TOY_strobe_plaintext(wrapped, data.data(), data.size(), true); } /** Stir in serializeable data we sent as plaintext. NB This doesn't actually send anything. */ template inline void send_plaintext(const Serializable &data) NOEXCEPT { send_plaintext(data.serialize()); } /** Stir in data we received as plaintext. NB This doesn't actually receive anything. */ inline void recv_plaintext(const Block &data) NOEXCEPT { decaf_TOY_strobe_plaintext(wrapped, data.data(), data.size(), false); } /** Stir in associated data. */ inline void ad(const Block &data) { decaf_TOY_strobe_ad(wrapped, data.data(), data.size()); } /** Stir in associated serializable data. */ template inline void ad(const Serializable &data) NOEXCEPT { ad(data.serialize()); } /** Encrypt into a buffer, without appending authentication data */ inline void encrypt_no_auth(Buffer out, const Block &data) throw(LengthException,ProtocolException) { if (!keyed) throw ProtocolException(); if (out.size() != data.size()) throw LengthException(); decaf_TOY_strobe_encrypt(wrapped, out.data(), data.data(), data.size()); } /** Encrypt, without appending authentication data */ inline SecureBuffer encrypt_no_auth(const Block &data) throw(ProtocolException) { SecureBuffer out(data.size()); encrypt_no_auth(out, data); return out; } /** Encrypt a serializable object, without appending authentication data */ template inline SecureBuffer encrypt_no_auth(const Serializable &data) throw(ProtocolException) { return encrypt_no_auth(data.serialize()); } /** Decrypt into a buffer, without checking authentication data. */ inline void decrypt_no_auth(Buffer out, const Block &data) throw(LengthException,ProtocolException) { if (!keyed) throw ProtocolException(); if (out.size() != data.size()) throw LengthException(); decaf_TOY_strobe_decrypt(wrapped, out.data(), data.data(), data.size()); } /** Decrypt, without checking authentication data. */ inline SecureBuffer decrypt_no_auth(const Block &data) throw(ProtocolException) { SecureBuffer out(data.size()); decrypt_no_auth(out, data); return out; } /** Produce an authenticator into a buffer. */ inline void produce_auth(Buffer out, bool even_though_unkeyed = false) throw(LengthException,ProtocolException) { if (!keyed && !even_though_unkeyed) throw ProtocolException(); if (out.size() > STROBE_MAX_AUTH_BYTES) throw LengthException(); decaf_TOY_strobe_produce_auth(wrapped, out.data(), out.size()); } /** Produce an authenticator. */ inline SecureBuffer produce_auth(uint8_t bytes = DEFAULT_AUTH_SIZE) throw(ProtocolException) { SecureBuffer out(bytes); produce_auth(out); return out; } /** Encrypt into a buffer and append authentication data */ inline void encrypt( Buffer out, const Block &data, uint8_t auth = DEFAULT_AUTH_SIZE ) throw(LengthException,ProtocolException) { if (out.size() < data.size() || out.size() != data.size() + auth) throw LengthException(); encrypt_no_auth(out.slice(0,data.size()), data); produce_auth(out.slice(data.size(),auth)); } /** Encrypt and append authentication data */ inline SecureBuffer encrypt ( const Block &data, uint8_t auth = DEFAULT_AUTH_SIZE ) throw(LengthException,ProtocolException,std::bad_alloc ){ SecureBuffer out(data.size() + auth); encrypt(out, data, auth); return out; } /** Encrypt a serializable object and append authentication data */ template inline SecureBuffer encrypt ( const Serializable &data, uint8_t auth = DEFAULT_AUTH_SIZE ) throw(LengthException,ProtocolException,std::bad_alloc ){ return encrypt(data.serialize(), auth); } /** Decrypt into a buffer and check authentication data */ inline void decrypt ( Buffer out, const Block &data, uint8_t bytes = DEFAULT_AUTH_SIZE ) throw(LengthException, CryptoException, ProtocolException) { if (out.size() > data.size() || out.size() != data.size() - bytes) throw LengthException(); decrypt_no_auth(out, data.slice(0,out.size())); verify_auth(data.slice(out.size(),bytes)); } /** Decrypt and check authentication data */ inline SecureBuffer decrypt ( const Block &data, uint8_t bytes = DEFAULT_AUTH_SIZE ) throw(LengthException,CryptoException,ProtocolException,std::bad_alloc) { if (data.size() < bytes) throw LengthException(); SecureBuffer out(data.size() - bytes); decrypt(out, data, bytes); return out; } /** Check authentication data */ inline void verify_auth(const Block &auth) throw(LengthException,CryptoException) { if (auth.size() == 0 || auth.size() > STROBE_MAX_AUTH_BYTES) throw LengthException(); if (decaf_TOY_strobe_verify_auth(wrapped, auth.data(), auth.size()) != DECAF_SUCCESS) throw CryptoException(); } /** Fill pseudorandom data into a buffer */ inline void prng(Buffer out) NOEXCEPT { (void)decaf_TOY_strobe_prng(wrapped, out.data(), out.size()); } /** Return pseudorandom data */ inline SecureBuffer prng(size_t bytes) { SecureBuffer out(bytes); prng(out); return out; } /** Change specs, perhaps to a faster spec that takes advantage of being keyed. * @warning Experimental. */ inline void respec(const decaf_kparams_s ¶ms) throw(ProtocolException) { if (!keyed) throw(ProtocolException()); decaf_TOY_strobe_respec(wrapped, ¶ms); } private: bool keyed; }; } /* namespace decaf */ #undef NOEXCEPT #undef DELETE #endif /* __DECAF_STROBE_HXX__ */