| @@ -211,7 +211,7 @@ public: | |||||
| /** Move non-constructor */ | /** Move non-constructor */ | ||||
| inline SecureBuffer(Block &&move) { *this = (Block &)move; } | inline SecureBuffer(Block &&move) { *this = (Block &)move; } | ||||
| /** Move-assign constructor */ | |||||
| /** Move-assign constructor. TODO: check that this actually gets used.*/ | |||||
| inline SecureBuffer& operator=(SecureBuffer &&move) { | inline SecureBuffer& operator=(SecureBuffer &&move) { | ||||
| clear(); | clear(); | ||||
| data_ = move.data_; move.data_ = NULL; | data_ = move.data_; move.data_ = NULL; | ||||
| @@ -21,6 +21,10 @@ | |||||
| /** @cond internal */ | /** @cond internal */ | ||||
| #define API_VIS __attribute__((visibility("default"))) | #define API_VIS __attribute__((visibility("default"))) | ||||
| #define WARN_UNUSED __attribute__((warn_unused_result)) | #define WARN_UNUSED __attribute__((warn_unused_result)) | ||||
| #define NONNULL1 __attribute__((nonnull(1))) | |||||
| #define NONNULL2 __attribute__((nonnull(1,2))) | |||||
| #define NONNULL13 __attribute__((nonnull(1,3))) | |||||
| #define NONNULL3 __attribute__((nonnull(1,2,3))) | |||||
| /** @endcond */ | /** @endcond */ | ||||
| /* TODO: different containing structs for each primitive? */ | /* TODO: different containing structs for each primitive? */ | ||||
| @@ -32,14 +36,8 @@ | |||||
| /** @endcond */ | /** @endcond */ | ||||
| } keccak_sponge_t[1]; | } keccak_sponge_t[1]; | ||||
| struct kparams_s; | struct kparams_s; | ||||
| typedef struct { uint64_t opaque; } strobe_params_t[1]; | |||||
| #endif | #endif | ||||
| typedef struct strobe_s { | |||||
| keccak_sponge_t sponge; | |||||
| strobe_params_t params; | |||||
| } strobe_s, strobe_t[1]; | |||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||
| extern "C" { | extern "C" { | ||||
| #endif | #endif | ||||
| @@ -122,39 +120,39 @@ void sponge_hash ( | |||||
| /** @cond internal */ | /** @cond internal */ | ||||
| #define DECSHAKE(n) \ | #define DECSHAKE(n) \ | ||||
| extern const struct kparams_s SHAKE##n##_params_s API_VIS; \ | extern const struct kparams_s SHAKE##n##_params_s API_VIS; \ | ||||
| static inline void shake##n##_init(keccak_sponge_t sponge) { \ | |||||
| static inline void NONNULL1 shake##n##_init(keccak_sponge_t sponge) { \ | |||||
| sponge_init(sponge, &SHAKE##n##_params_s); \ | sponge_init(sponge, &SHAKE##n##_params_s); \ | ||||
| } \ | } \ | ||||
| static inline void shake##n##_update(keccak_sponge_t sponge, const uint8_t *in, size_t inlen ) { \ | |||||
| static inline void NONNULL2 shake##n##_update(keccak_sponge_t sponge, const uint8_t *in, size_t inlen ) { \ | |||||
| sha3_update(sponge, in, inlen); \ | sha3_update(sponge, in, inlen); \ | ||||
| } \ | } \ | ||||
| static inline void shake##n##_final(keccak_sponge_t sponge, uint8_t *out, size_t outlen ) { \ | |||||
| static inline void NONNULL2 shake##n##_final(keccak_sponge_t sponge, uint8_t *out, size_t outlen ) { \ | |||||
| sha3_output(sponge, out, outlen); \ | sha3_output(sponge, out, outlen); \ | ||||
| sponge_init(sponge, &SHAKE##n##_params_s); \ | sponge_init(sponge, &SHAKE##n##_params_s); \ | ||||
| } \ | } \ | ||||
| static inline void shake##n##_hash(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen) { \ | |||||
| static inline void NONNULL13 shake##n##_hash(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen) { \ | |||||
| sponge_hash(in,inlen,out,outlen,&SHAKE##n##_params_s); \ | sponge_hash(in,inlen,out,outlen,&SHAKE##n##_params_s); \ | ||||
| } \ | } \ | ||||
| static inline void shake##n##_destroy( keccak_sponge_t sponge ) { \ | |||||
| static inline void NONNULL1 shake##n##_destroy( keccak_sponge_t sponge ) { \ | |||||
| sponge_destroy(sponge); \ | sponge_destroy(sponge); \ | ||||
| } | } | ||||
| #define DECSHA3(n) \ | #define DECSHA3(n) \ | ||||
| extern const struct kparams_s SHA3_##n##_params_s API_VIS; \ | extern const struct kparams_s SHA3_##n##_params_s API_VIS; \ | ||||
| static inline void sha3_##n##_init(keccak_sponge_t sponge) { \ | |||||
| static inline void NONNULL1 sha3_##n##_init(keccak_sponge_t sponge) { \ | |||||
| sponge_init(sponge, &SHA3_##n##_params_s); \ | sponge_init(sponge, &SHA3_##n##_params_s); \ | ||||
| } \ | } \ | ||||
| static inline void sha3_##n##_update(keccak_sponge_t sponge, const uint8_t *in, size_t inlen ) { \ | |||||
| static inline void NONNULL2 sha3_##n##_update(keccak_sponge_t sponge, const uint8_t *in, size_t inlen ) { \ | |||||
| sha3_update(sponge, in, inlen); \ | sha3_update(sponge, in, inlen); \ | ||||
| } \ | } \ | ||||
| static inline void sha3_##n##_final(keccak_sponge_t sponge, uint8_t *out, size_t outlen ) { \ | |||||
| static inline void NONNULL2 sha3_##n##_final(keccak_sponge_t sponge, uint8_t *out, size_t outlen ) { \ | |||||
| sha3_output(sponge, out, outlen); \ | sha3_output(sponge, out, outlen); \ | ||||
| sponge_init(sponge, &SHA3_##n##_params_s); \ | sponge_init(sponge, &SHA3_##n##_params_s); \ | ||||
| } \ | } \ | ||||
| static inline void sha3_##n##_hash(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen) { \ | |||||
| static inline void NONNULL13 sha3_##n##_hash(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen) { \ | |||||
| sponge_hash(in,inlen,out,outlen,&SHA3_##n##_params_s); \ | sponge_hash(in,inlen,out,outlen,&SHA3_##n##_params_s); \ | ||||
| } \ | } \ | ||||
| static inline void sha3_##n##_destroy( keccak_sponge_t sponge ) { \ | |||||
| static inline void NONNULL1 sha3_##n##_destroy( keccak_sponge_t sponge ) { \ | |||||
| sponge_destroy(sponge); \ | sponge_destroy(sponge); \ | ||||
| } | } | ||||
| /** @endcond */ | /** @endcond */ | ||||
| @@ -180,7 +178,7 @@ void spongerng_init_from_buffer ( | |||||
| const uint8_t * __restrict__ in, | const uint8_t * __restrict__ in, | ||||
| size_t len, | size_t len, | ||||
| int deterministic | int deterministic | ||||
| ) API_VIS; | |||||
| ) NONNULL2 API_VIS; | |||||
| /* FIXME!! This interface has the opposite retval convention from other functions | /* FIXME!! This interface has the opposite retval convention from other functions | ||||
| * in the library. (0=success). Should they be harmonized? | * in the library. (0=success). Should they be harmonized? | ||||
| @@ -205,7 +203,7 @@ int spongerng_init_from_file ( | |||||
| const char *file, | const char *file, | ||||
| size_t len, | size_t len, | ||||
| int deterministic | int deterministic | ||||
| ) API_VIS WARN_UNUSED; | |||||
| ) NONNULL2 API_VIS WARN_UNUSED; | |||||
| /* FIXME!! This interface has the opposite retval convention from other functions | /* FIXME!! This interface has the opposite retval convention from other functions | ||||
| @@ -249,7 +247,16 @@ void spongerng_stir ( | |||||
| keccak_sponge_t sponge, | keccak_sponge_t sponge, | ||||
| const uint8_t * __restrict__ in, | const uint8_t * __restrict__ in, | ||||
| size_t len | size_t len | ||||
| ) API_VIS; | |||||
| ) NONNULL2 API_VIS; | |||||
| extern const struct kparams_s STROBE_256 API_VIS; | |||||
| extern const struct kparams_s STROBE_KEYED_128 API_VIS; | |||||
| extern const struct kparams_s STROBE_KEYED_256 API_VIS; | |||||
| /** TODO: remove this restriction?? */ | |||||
| #define STROBE_MAX_AUTH_BYTES 255 | |||||
| /** TODO: check "more" flags? */ | |||||
| /** | /** | ||||
| * @brief Initialize Strobe protocol context. | * @brief Initialize Strobe protocol context. | ||||
| @@ -258,11 +265,88 @@ void spongerng_stir ( | |||||
| * @param [in] am_client Nonzero if this party | * @param [in] am_client Nonzero if this party | ||||
| * is the client. | * is the client. | ||||
| */ | */ | ||||
| void strobe_init( | |||||
| strobe_t strobe, | |||||
| void strobe_init ( | |||||
| keccak_sponge_t sponge, | |||||
| const struct kparams_s *params, | const struct kparams_s *params, | ||||
| uint8_t am_client | uint8_t am_client | ||||
| ); | |||||
| ) NONNULL2 API_VIS; | |||||
| /** | |||||
| * @brief Send plaintext in strobe context. | |||||
| * @param [inout] The initialized strobe object. | |||||
| * @param [in] Strobe parameter descriptor | |||||
| * @param [in] in The plaintext. | |||||
| * @param [in] len The length of the plaintext. | |||||
| * @param [in] iSent Nonzero if this side of exchange sent the plaintext. | |||||
| * @param [in] more Nonzero if this is a continuation. | |||||
| * @retval DECAF_SUCCESS The operation applied successfully. | |||||
| * @retval DECAF_FAILURE The operation applied, but is dangerous | |||||
| * because it breaks the usual flow (by doing keyed operations | |||||
| * before a key is specified, or by specifying more when the previous | |||||
| * operation didn't match). | |||||
| */ | |||||
| decaf_bool_t strobe_plaintext ( | |||||
| keccak_sponge_t sponge, | |||||
| const unsigned char *in, | |||||
| size_t len, | |||||
| uint8_t iSent, | |||||
| uint8_t more | |||||
| ) NONNULL2 API_VIS; | |||||
| /** | |||||
| * @brief Report authenticated data in strobe context. | |||||
| * @param [inout] The initialized strobe object. | |||||
| * @param [in] Strobe parameter descriptor | |||||
| * @param [in] in The plaintext. | |||||
| * @param [in] len The length of the ad. | |||||
| * @param [in] more Nonzero if this is a continuation. | |||||
| * @retval DECAF_SUCCESS The operation applied successfully. | |||||
| * @retval DECAF_FAILURE The operation applied, but is dangerous | |||||
| * because it breaks the usual flow (by doing keyed operations | |||||
| * before a key is specified, or by specifying more when the previous | |||||
| * operation didn't match). | |||||
| */ | |||||
| decaf_bool_t strobe_ad ( | |||||
| keccak_sponge_t sponge, | |||||
| const unsigned char *in, | |||||
| size_t len, | |||||
| uint8_t more | |||||
| ) NONNULL2 API_VIS; | |||||
| /** | |||||
| * @brief Set nonce in strobe context. | |||||
| * @param [inout] The initialized strobe object. | |||||
| * @param [in] Strobe parameter descriptor | |||||
| * @param [in] in The nonce. | |||||
| * @param [in] len The length of the nonce. | |||||
| * @param [in] more Nonzero if this is a continuation. | |||||
| * @retval DECAF_SUCCESS The operation applied successfully. | |||||
| * @retval DECAF_FAILURE The operation applied, but is dangerous | |||||
| * because it breaks the usual flow (by doing keyed operations | |||||
| * before a key is specified, or by specifying more when the previous | |||||
| * operation didn't match). | |||||
| */ | |||||
| decaf_bool_t strobe_nonce ( | |||||
| keccak_sponge_t sponge, | |||||
| const unsigned char *in, | |||||
| size_t len, | |||||
| uint8_t more | |||||
| ) NONNULL2 API_VIS; | |||||
| /** | |||||
| * @brief Set key in strobe context. | |||||
| * @param [inout] The initialized strobe object. | |||||
| * @param [in] Strobe parameter descriptor | |||||
| * @param [in] in The key. | |||||
| * @param [in] len The length of the key. | |||||
| * @param [in] more Nonzero if this is a continuation. | |||||
| */ | |||||
| decaf_bool_t strobe_key ( | |||||
| keccak_sponge_t sponge, | |||||
| const unsigned char *in, | |||||
| size_t len, | |||||
| uint8_t more | |||||
| ) NONNULL2 API_VIS; | |||||
| /** | /** | ||||
| * @brief Produce an authenticator. | * @brief Produce an authenticator. | ||||
| @@ -270,12 +354,61 @@ void strobe_init( | |||||
| * @param [out] out The authenticator | * @param [out] out The authenticator | ||||
| * @param len The length, which must be no more than | * @param len The length, which must be no more than | ||||
| * @todo 32? | * @todo 32? | ||||
| * @retval DECAF_SUCCESS The operation applied successfully. | |||||
| * @retval DECAF_FAILURE The operation applied, but is dangerous | |||||
| * because it breaks the usual flow (by doing keyed operations | |||||
| * before a key is specified, or by specifying more when the previous | |||||
| * operation didn't match). | |||||
| */ | */ | ||||
| void strobe_produce_auth ( | |||||
| strobe_t strobe, | |||||
| decaf_bool_t strobe_produce_auth ( | |||||
| keccak_sponge_t sponge, | |||||
| unsigned char *out, | unsigned char *out, | ||||
| size_t len | size_t len | ||||
| ); | |||||
| ) NONNULL2 API_VIS; | |||||
| /** | |||||
| * @brief Encrypt bytes from in to out. | |||||
| * @warning Doesn't produce an auth tag (TODO?) | |||||
| * @param [inout] strobe The Strobe protocol context. | |||||
| * @param [in] in The plaintext. | |||||
| * @param [out] out The ciphertext. | |||||
| * @param [in] len The length of plaintext and ciphertext. | |||||
| * @param [in] more This is a continuation. | |||||
| * @retval DECAF_SUCCESS The operation applied successfully. | |||||
| * @retval DECAF_FAILURE The operation applied, but is dangerous | |||||
| * because it breaks the usual flow (by doing keyed operations | |||||
| * before a key is specified, or by specifying more when the previous | |||||
| * operation didn't match). | |||||
| */ | |||||
| decaf_bool_t strobe_encrypt ( | |||||
| keccak_sponge_t sponge, | |||||
| unsigned char *out, | |||||
| const unsigned char *in, | |||||
| size_t len, | |||||
| uint8_t more | |||||
| ) NONNULL3 API_VIS; | |||||
| /** | |||||
| * @brief Decrypt bytes from in to out. | |||||
| * @warning Doesn't check an auth tag (TODO?) | |||||
| * @param [inout] strobe The Strobe protocol context. | |||||
| * @param [in] in The ciphertext. | |||||
| * @param [out] out The plaintext. | |||||
| * @param [in] len The length of plaintext and ciphertext. | |||||
| * @param [in] more This is a continuation. | |||||
| * @retval DECAF_SUCCESS The operation applied successfully. | |||||
| * @retval DECAF_FAILURE The operation applied, but is dangerous | |||||
| * because it breaks the usual flow (by doing keyed operations | |||||
| * before a key is specified, or by specifying more when the previous | |||||
| * operation didn't match). | |||||
| */ | |||||
| decaf_bool_t strobe_decrypt ( | |||||
| keccak_sponge_t sponge, | |||||
| unsigned char *out, | |||||
| const unsigned char *in, | |||||
| size_t len, | |||||
| uint8_t more | |||||
| ) NONNULL3 API_VIS; | |||||
| /** | /** | ||||
| * @brief Produce a session-bound pseudorandom value. | * @brief Produce a session-bound pseudorandom value. | ||||
| @@ -289,12 +422,19 @@ void strobe_produce_auth ( | |||||
| * @param [inout] strobe The Strobe protocol context | * @param [inout] strobe The Strobe protocol context | ||||
| * @param [out] out The authenticator | * @param [out] out The authenticator | ||||
| * @param len The length. | * @param len The length. | ||||
| * | |||||
| * @retval DECAF_SUCCESS The operation applied successfully. | |||||
| * @retval DECAF_FAILURE The operation applied, but is dangerous | |||||
| * because it breaks the usual flow (by doing keyed operations | |||||
| * before a key is specified, or by specifying more when the previous | |||||
| * operation didn't match). | |||||
| */ | */ | ||||
| void strobe_prng ( | |||||
| strobe_t strobe, | |||||
| decaf_bool_t strobe_prng ( | |||||
| keccak_sponge_t sponge, | |||||
| unsigned char *out, | unsigned char *out, | ||||
| size_t len | |||||
| ); | |||||
| size_t len, | |||||
| uint8_t more | |||||
| ) NONNULL2 API_VIS; | |||||
| /** | /** | ||||
| * @brief Verify an authenticator. | * @brief Verify an authenticator. | ||||
| @@ -302,12 +442,15 @@ void strobe_prng ( | |||||
| * @param [in] in The authenticator | * @param [in] in The authenticator | ||||
| * @param len The length, which must be no more than | * @param len The length, which must be no more than | ||||
| * @todo 32? | * @todo 32? | ||||
| * @retval DECAF_SUCCESS The operation applied successfully. | |||||
| * @retval DECAF_FAILURE The operation failed because of a | |||||
| * bad validator (or because you aren't keyed) | |||||
| */ | */ | ||||
| decaf_bool_t strobe_verify_auth ( | decaf_bool_t strobe_verify_auth ( | ||||
| strobe_t strobe, | |||||
| keccak_sponge_t sponge, | |||||
| const unsigned char *in, | const unsigned char *in, | ||||
| size_t len | size_t len | ||||
| ); | |||||
| ) WARN_UNUSED NONNULL2 API_VIS; | |||||
| /** | /** | ||||
| * @brief Respecify Strobe protocol object's crypto. | * @brief Respecify Strobe protocol object's crypto. | ||||
| @@ -315,19 +458,14 @@ decaf_bool_t strobe_verify_auth ( | |||||
| * @param [in] Strobe parameter descriptor | * @param [in] Strobe parameter descriptor | ||||
| * @param [in] am_client Nonzero if this party | * @param [in] am_client Nonzero if this party | ||||
| * is the client. | * is the client. | ||||
| * @retval DECAF_SUCCESS The operation applied successfully. | |||||
| * @retval DECAF_FAILURE The operation failed because of a | |||||
| * bad validator (or because you aren't keyed) | |||||
| */ | */ | ||||
| void strobe_respec ( | |||||
| strobe_t strobe, | |||||
| decaf_bool_t strobe_respec ( | |||||
| keccak_sponge_t sponge, | |||||
| const struct kparams_s *params | const struct kparams_s *params | ||||
| ); | |||||
| /** | |||||
| * @brief Destroy a Strobe context. | |||||
| * @param [out] strobe The object to destroy. | |||||
| */ | |||||
| void strobe_destroy ( | |||||
| strobe_t strobe | |||||
| ); | |||||
| ) NONNULL2 API_VIS; | |||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||
| } /* extern "C" */ | } /* extern "C" */ | ||||
| @@ -335,5 +473,9 @@ void strobe_destroy ( | |||||
| #undef API_VIS | #undef API_VIS | ||||
| #undef WARN_UNUSED | #undef WARN_UNUSED | ||||
| #undef NONNULL1 | |||||
| #undef NONNULL13 | |||||
| #undef NONNULL2 | |||||
| #undef NONNULL3 | |||||
| #endif /* __SHAKE_H__ */ | #endif /* __SHAKE_H__ */ | ||||
| @@ -130,6 +130,13 @@ template<> const struct kparams_s *SHA3<256>::get_params() { return &SHA3_256_pa | |||||
| template<> const struct kparams_s *SHA3<384>::get_params() { return &SHA3_384_params_s; } | template<> const struct kparams_s *SHA3<384>::get_params() { return &SHA3_384_params_s; } | ||||
| template<> const struct kparams_s *SHA3<512>::get_params() { return &SHA3_512_params_s; } | template<> const struct kparams_s *SHA3<512>::get_params() { return &SHA3_512_params_s; } | ||||
| /** @endcond */ | /** @endcond */ | ||||
| /** @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"; } | |||||
| }; | |||||
| /** Sponge-based random-number generator */ | /** Sponge-based random-number generator */ | ||||
| class SpongeRng : private KeccakSponge { | class SpongeRng : private KeccakSponge { | ||||
| @@ -194,6 +201,157 @@ decaf<448>::Point::Point(SpongeRng &rng, bool uniform) { | |||||
| } | } | ||||
| } | } | ||||
| /**@endcond*/ | /**@endcond*/ | ||||
| class Strobe : private KeccakSponge { | |||||
| public: | |||||
| /* TODO: pull out parameters */ | |||||
| /** Am I a server or a client? */ | |||||
| enum client_or_server { SERVER, CLIENT }; | |||||
| inline Strobe ( | |||||
| client_or_server whoami, | |||||
| const kparams_s ¶ms = STROBE_256 | |||||
| ) NOEXCEPT : KeccakSponge(NOINIT()) { | |||||
| strobe_init(sp, ¶ms, whoami == CLIENT); | |||||
| } | |||||
| inline void key ( | |||||
| const Block &data, bool more = false | |||||
| ) throw(ProtocolException) { | |||||
| if (!strobe_key(sp, data, data.size(), more)) throw ProtocolException(); | |||||
| } | |||||
| inline void nonce(const Block &data, bool more = false | |||||
| ) throw(ProtocolException) { | |||||
| if (!strobe_nonce(sp, data, data.size(), more)) throw ProtocolException(); | |||||
| } | |||||
| inline void plaintext(const Block &data, bool iSent, bool more = false | |||||
| ) throw(ProtocolException) { | |||||
| if (!strobe_plaintext(sp, data, data.size(), iSent, more)) | |||||
| throw(ProtocolException()); | |||||
| } | |||||
| inline void ad(const Block &data, bool more = false | |||||
| ) throw(ProtocolException) { | |||||
| if (!strobe_ad(sp, data, data.size(), more)) | |||||
| throw(ProtocolException()); | |||||
| } | |||||
| inline void encrypt_no_auth( | |||||
| Buffer &out, const Block &data, bool more = false | |||||
| ) throw(LengthException,ProtocolException) { | |||||
| if (out.size() != data.size()) throw LengthException(); | |||||
| if (!strobe_encrypt(sp, out, data, data.size(), more)) throw(ProtocolException()); | |||||
| } | |||||
| inline void encrypt_no_auth( | |||||
| TmpBuffer out, const Block &data, bool more = false | |||||
| ) throw(LengthException,ProtocolException) { | |||||
| encrypt_no_auth((Buffer &)out, data, more); | |||||
| } | |||||
| inline SecureBuffer encrypt_no_auth(const Block &data, bool more = false | |||||
| ) throw(ProtocolException) { | |||||
| SecureBuffer out(data.size()); encrypt_no_auth(out, data, more); return out; | |||||
| } | |||||
| inline void decrypt_no_auth( | |||||
| Buffer &out, const Block &data, bool more = false | |||||
| ) throw(LengthException,ProtocolException) { | |||||
| if (out.size() != data.size()) throw LengthException(); | |||||
| if (!strobe_decrypt(sp, out, data, data.size(), more)) throw ProtocolException(); | |||||
| } | |||||
| inline void decrypt_no_auth( | |||||
| TmpBuffer out, const Block &data, bool more = false | |||||
| ) throw(LengthException,ProtocolException) { | |||||
| decrypt_no_auth((Buffer &)out, data, more); | |||||
| } | |||||
| inline SecureBuffer decrypt_no_auth(const Block &data, bool more = false | |||||
| ) throw(ProtocolException) { | |||||
| SecureBuffer out(data.size()); decrypt_no_auth(out, data, more); return out; | |||||
| } | |||||
| inline void produce_auth(Buffer &out) throw(LengthException,ProtocolException) { | |||||
| if (out.size() > STROBE_MAX_AUTH_BYTES) throw LengthException(); | |||||
| if (!strobe_produce_auth(sp, out, out.size())) throw ProtocolException(); | |||||
| } | |||||
| inline void produce_auth(TmpBuffer out) throw(LengthException,ProtocolException) { | |||||
| produce_auth((Buffer &)out); | |||||
| } | |||||
| inline SecureBuffer produce_auth( | |||||
| uint8_t bytes = 8 | |||||
| ) throw(ProtocolException) { | |||||
| SecureBuffer out(bytes); produce_auth(out); return out; | |||||
| } | |||||
| inline void encrypt( | |||||
| Buffer &out, const Block &data, uint8_t auth = 8 | |||||
| ) throw(LengthException,ProtocolException) { | |||||
| if (out.size() < data.size() || out.size() != data.size() + auth) throw LengthException(); | |||||
| encrypt(out.slice(0,data.size()), data); | |||||
| produce_auth(out.slice(data.size(),auth)); | |||||
| } | |||||
| inline void encrypt ( | |||||
| TmpBuffer out, const Block &data, uint8_t auth = 8 | |||||
| ) throw(LengthException,ProtocolException) { | |||||
| encrypt((Buffer &)out, data, auth); | |||||
| } | |||||
| inline SecureBuffer encrypt ( | |||||
| const Block &data, uint8_t auth = 8 | |||||
| ) throw(LengthException,ProtocolException,std::bad_alloc ){ | |||||
| SecureBuffer out(data.size() + auth); encrypt(out, data, auth); return out; | |||||
| } | |||||
| inline void decrypt ( | |||||
| Buffer &out, const Block &data, uint8_t bytes = 8 | |||||
| ) throw(LengthException, CryptoException, ProtocolException) { | |||||
| if (out.size() > data.size() || out.size() != data.size() - bytes) throw LengthException(); | |||||
| decrypt(out, data.slice(0,out.size())); | |||||
| verify_auth(data.slice(out.size(),bytes)); | |||||
| } | |||||
| inline void decrypt ( | |||||
| TmpBuffer out, const Block &data, uint8_t bytes = 8 | |||||
| ) throw(LengthException,CryptoException,ProtocolException) { | |||||
| decrypt((Buffer &)out, data, bytes); | |||||
| } | |||||
| inline SecureBuffer decrypt ( | |||||
| const Block &data, uint8_t bytes = 8 | |||||
| ) throw(LengthException,CryptoException,ProtocolException,std::bad_alloc) { | |||||
| if (data.size() < bytes) throw LengthException(); | |||||
| SecureBuffer out(data.size() - bytes); decrypt(out, data, bytes); return out; | |||||
| } | |||||
| inline void verify_auth(const Block &auth) throw(LengthException,CryptoException) { | |||||
| if (auth.size() == 0 || auth.size() > STROBE_MAX_AUTH_BYTES) throw LengthException(); | |||||
| if (!strobe_verify_auth(sp, auth, auth.size())) throw CryptoException(); | |||||
| } | |||||
| inline void prng(Buffer &out, bool more = false) NOEXCEPT { | |||||
| (void)strobe_prng(sp, out, out.size(), more); | |||||
| } | |||||
| inline void prng(TmpBuffer out, bool more = false) NOEXCEPT { | |||||
| prng((Buffer &)out, more); | |||||
| } | |||||
| inline SecureBuffer prng(size_t bytes, bool more = false) { | |||||
| SecureBuffer out(bytes); prng(out, more); return out; | |||||
| } | |||||
| inline void respec(const kparams_s ¶ms) throw(ProtocolException) { | |||||
| if (!strobe_respec(sp, ¶ms)) throw(ProtocolException()); | |||||
| } | |||||
| }; | |||||
| } /* namespace decaf */ | } /* namespace decaf */ | ||||
| @@ -58,13 +58,9 @@ typedef union { | |||||
| } kdomain_t[1]; | } kdomain_t[1]; | ||||
| typedef struct kparams_s { | typedef struct kparams_s { | ||||
| uint8_t position, flags, rate, startRound, pad, ratePad, maxOut, _; | |||||
| uint8_t position, flags, rate, startRound, pad, ratePad, maxOut, client; | |||||
| } kparams_t[1]; | } kparams_t[1]; | ||||
| typedef struct strobe_params_s { | |||||
| uint8_t client, _[7]; | |||||
| } strobe_params_t[1]; | |||||
| typedef struct keccak_sponge_s { | typedef struct keccak_sponge_s { | ||||
| kdomain_t state; | kdomain_t state; | ||||
| kparams_t params; | kparams_t params; | ||||
| @@ -451,14 +447,18 @@ int spongerng_init_from_dev_urandom ( | |||||
| return spongerng_init_from_file(sponge, "/dev/urandom", 64, 0); | return spongerng_init_from_file(sponge, "/dev/urandom", 64, 0); | ||||
| } | } | ||||
| const struct kparams_s STROBE_256 = { 0, 0, 200-256/4, 0, 0, 0, 0, 0 }; | |||||
| const struct kparams_s STROBE_KEYED_256 = { 0, 0, 200-256/4, 12, 0, 0, 0, 0 }; | |||||
| const struct kparams_s STROBE_KEYED_128 = { 0, 0, 200-128/4, 12, 0, 0, 0, 0 }; | |||||
| /* Strobe is different in that its rate is padded by one byte. */ | /* Strobe is different in that its rate is padded by one byte. */ | ||||
| void strobe_init( | void strobe_init( | ||||
| strobe_t strobe, | |||||
| keccak_sponge_t sponge, | |||||
| const struct kparams_s *params, | const struct kparams_s *params, | ||||
| uint8_t am_client | uint8_t am_client | ||||
| ) { | ) { | ||||
| sponge_init(strobe->sponge,params); | |||||
| strobe->params->client = !!am_client; | |||||
| sponge_init(sponge,params); | |||||
| sponge->params->client = !!am_client; | |||||
| } | } | ||||
| static void strobe_duplex ( | static void strobe_duplex ( | ||||
| @@ -493,6 +493,28 @@ static void strobe_duplex ( | |||||
| } | } | ||||
| } | } | ||||
| static void strobe_forget ( | |||||
| keccak_sponge_t sponge, | |||||
| size_t len | |||||
| ) { | |||||
| assert(sponge->params->rate < sizeof(sponge->state)); | |||||
| assert(sponge->params->position <= sizeof(sponge->params->rate)); | |||||
| if (sizeof(sponge->state) - sponge->params->rate < len) { | |||||
| /** Tiny case */ | |||||
| unsigned char tmp[len]; | |||||
| strobe_duplex(sponge,tmp,NULL,len); | |||||
| if (sponge->params->position) dokeccak(sponge); | |||||
| strobe_duplex(sponge,tmp,NULL,len); | |||||
| decaf_bzero(tmp,len); | |||||
| } else { | |||||
| if (sponge->params->rate < len + sponge->params->position) { | |||||
| dokeccak(sponge); | |||||
| } | |||||
| memset(sponge->state->b, 0, len); | |||||
| sponge->params->position = len; | |||||
| } | |||||
| } | |||||
| static void strobe_unduplex ( | static void strobe_unduplex ( | ||||
| keccak_sponge_t sponge, | keccak_sponge_t sponge, | ||||
| unsigned char *out, | unsigned char *out, | ||||
| @@ -525,138 +547,190 @@ static void strobe_unduplex ( | |||||
| } | } | ||||
| } | } | ||||
| enum { KEY, NONCE, AD, PLAINTEXT, CIPHERTEXT, TAGFORGET, DIVERSIFIER, PRNG, FORK, JOIN, RESPEC }; | |||||
| enum { KEY=1, NONCE, AD, PLAINTEXT, CIPHERTEXT, TAGFORGET, DIVERSIFIER, PRNG, FORK, JOIN, RESPEC }; | |||||
| #define CLIENT_TO_SERVER 0 | #define CLIENT_TO_SERVER 0 | ||||
| #define SERVER_TO_CLIENT 1 | |||||
| #define SERVER_TO_CLIENT 0x80 | |||||
| struct strobe_control { | |||||
| uint8_t next; | |||||
| uint8_t bytes; | |||||
| uint8_t flags; | |||||
| } __attribute__((packed)); | |||||
| static void strobe_control_word ( | |||||
| strobe_t strobe, | |||||
| const struct strobe_control *control | |||||
| static decaf_bool_t strobe_control_word ( | |||||
| keccak_sponge_t sponge, | |||||
| const unsigned char *control, | |||||
| size_t len, | |||||
| uint8_t more | |||||
| ) { | ) { | ||||
| assert(strobe->sponge->params->rate < sizeof(strobe->sponge->state)); | |||||
| strobe_duplex(strobe->sponge,NULL,(const unsigned char *)control,sizeof(*control)); | |||||
| strobe->sponge->state->b[strobe->sponge->params->position] ^= 0x1; | |||||
| strobe->sponge->state->b[strobe->sponge->params->rate] ^= 0x2; | |||||
| dokeccak(strobe->sponge); | |||||
| assert(sponge->params->rate < sizeof(sponge->state)); | |||||
| decaf_bool_t ret = DECAF_SUCCESS; | |||||
| if (!more) { | |||||
| strobe_duplex(sponge,NULL,control,len); | |||||
| sponge->state->b[sponge->params->position] ^= 0x1; | |||||
| sponge->state->b[sponge->params->rate] ^= 0x2; | |||||
| dokeccak(sponge); | |||||
| sponge->params->flags = control[len-1]; | |||||
| } else if (sponge->params->flags && sponge->params->flags != control[len-1]) { | |||||
| ret = DECAF_FAILURE; | |||||
| } | |||||
| sponge->params->flags = control[len-1]; | |||||
| return ret; | |||||
| } | } | ||||
| void strobe_encrypt ( | |||||
| strobe_t strobe, | |||||
| decaf_bool_t strobe_encrypt ( | |||||
| keccak_sponge_t sponge, | |||||
| unsigned char *out, | unsigned char *out, | ||||
| const unsigned char *in, | const unsigned char *in, | ||||
| size_t len | |||||
| size_t len, | |||||
| uint8_t more | |||||
| ) { | ) { | ||||
| struct strobe_control cont = { CIPHERTEXT, 0, | |||||
| strobe->params->client ? CLIENT_TO_SERVER : SERVER_TO_CLIENT }; | |||||
| strobe_control_word(strobe, &cont); | |||||
| strobe_duplex(strobe->sponge, out, in, len); | |||||
| unsigned char control[] = { CIPHERTEXT | | |||||
| (sponge->params->client ? CLIENT_TO_SERVER : SERVER_TO_CLIENT) | |||||
| }; | |||||
| decaf_bool_t ret = strobe_control_word(sponge, control, sizeof(control), more); | |||||
| strobe_duplex(sponge, out, in, len); | |||||
| if (!sponge->params->pad/*keyed*/) ret = DECAF_FAILURE; | |||||
| return ret; | |||||
| } | } | ||||
| void strobe_decrypt ( | |||||
| strobe_t strobe, | |||||
| decaf_bool_t strobe_decrypt ( | |||||
| keccak_sponge_t sponge, | |||||
| unsigned char *out, | unsigned char *out, | ||||
| const unsigned char *in, | const unsigned char *in, | ||||
| size_t len | |||||
| size_t len, | |||||
| uint8_t more | |||||
| ) { | |||||
| unsigned char control[] = { CIPHERTEXT | | |||||
| (sponge->params->client ? SERVER_TO_CLIENT : CLIENT_TO_SERVER) | |||||
| }; | |||||
| decaf_bool_t ret = strobe_control_word(sponge, control, sizeof(control), more); | |||||
| strobe_duplex(sponge, out, in, len); | |||||
| if (!sponge->params->pad/*keyed*/) ret = DECAF_FAILURE; | |||||
| return ret; | |||||
| } | |||||
| decaf_bool_t strobe_plaintext ( | |||||
| keccak_sponge_t sponge, | |||||
| const unsigned char *in, | |||||
| size_t len, | |||||
| uint8_t iSent, | |||||
| uint8_t more | |||||
| ) { | |||||
| unsigned char control[] = { PLAINTEXT | | |||||
| ((sponge->params->client == !!iSent) ? CLIENT_TO_SERVER : SERVER_TO_CLIENT) | |||||
| }; | |||||
| decaf_bool_t ret = strobe_control_word(sponge, control, sizeof(control), more); | |||||
| strobe_duplex(sponge, NULL, in, len); | |||||
| return ret; | |||||
| } | |||||
| decaf_bool_t strobe_key ( | |||||
| keccak_sponge_t sponge, | |||||
| const unsigned char *in, | |||||
| size_t len, | |||||
| uint8_t more | |||||
| ) { | |||||
| unsigned char control[] = { KEY }; | |||||
| decaf_bool_t ret = strobe_control_word(sponge, control, sizeof(control), more); | |||||
| strobe_duplex(sponge, NULL, in, len); | |||||
| sponge->params->pad/*=keyed*/ = 1; | |||||
| return ret; | |||||
| } | |||||
| decaf_bool_t strobe_nonce ( | |||||
| keccak_sponge_t sponge, | |||||
| const unsigned char *in, | |||||
| size_t len, | |||||
| uint8_t more | |||||
| ) { | ) { | ||||
| struct strobe_control cont = { CIPHERTEXT, 0, | |||||
| strobe->params->client ? SERVER_TO_CLIENT : CLIENT_TO_SERVER }; | |||||
| strobe_control_word(strobe, &cont); | |||||
| strobe_duplex(strobe->sponge, out, in, len); | |||||
| unsigned char control[] = { NONCE }; | |||||
| decaf_bool_t ret = strobe_control_word(sponge, control, sizeof(control), more); | |||||
| strobe_duplex(sponge, NULL, in, len); | |||||
| return ret; | |||||
| } | } | ||||
| void strobe_plaintext ( | |||||
| strobe_t strobe, | |||||
| decaf_bool_t strobe_ad ( | |||||
| keccak_sponge_t sponge, | |||||
| const unsigned char *in, | const unsigned char *in, | ||||
| size_t len, | size_t len, | ||||
| uint8_t iSent | |||||
| uint8_t more | |||||
| ) { | ) { | ||||
| struct strobe_control cont = { CIPHERTEXT, 0, | |||||
| (strobe->params->client == !!iSent) ? CLIENT_TO_SERVER : SERVER_TO_CLIENT }; | |||||
| strobe_control_word(strobe, &cont); | |||||
| strobe_duplex(strobe->sponge, NULL, in, len); | |||||
| unsigned char control[] = { AD }; | |||||
| decaf_bool_t ret = strobe_control_word(sponge, control, sizeof(control), more); | |||||
| strobe_duplex(sponge, NULL, in, len); | |||||
| return ret; | |||||
| } | } | ||||
| #define STROBE_FORGET_BYTES 32 | #define STROBE_FORGET_BYTES 32 | ||||
| void strobe_produce_auth ( | |||||
| strobe_t strobe, | |||||
| decaf_bool_t strobe_produce_auth ( | |||||
| keccak_sponge_t sponge, | |||||
| unsigned char *out, | unsigned char *out, | ||||
| size_t len | size_t len | ||||
| ) { | ) { | ||||
| assert(len < strobe->sponge->params->rate - STROBE_FORGET_BYTES); | |||||
| struct strobe_control cont = { TAGFORGET, len, | |||||
| strobe->params->client ? CLIENT_TO_SERVER : SERVER_TO_CLIENT }; | |||||
| strobe_control_word(strobe, &cont); | |||||
| strobe_duplex(strobe->sponge, out, NULL, len); | |||||
| strobe_duplex(strobe->sponge, NULL, | |||||
| &strobe->sponge->state->b[strobe->sponge->params->position], | |||||
| STROBE_FORGET_BYTES | |||||
| ); | |||||
| unsigned char control[] = { | |||||
| (unsigned char)len, | |||||
| (unsigned char)STROBE_FORGET_BYTES, | |||||
| TAGFORGET | (sponge->params->client ? CLIENT_TO_SERVER : SERVER_TO_CLIENT) | |||||
| }; | |||||
| decaf_bool_t ret = strobe_control_word(sponge, control, sizeof(control), 0); | |||||
| strobe_duplex(sponge, out, NULL, len); | |||||
| strobe_forget(sponge, STROBE_FORGET_BYTES); | |||||
| if (!sponge->params->pad/*keyed*/) ret = DECAF_FAILURE; | |||||
| return ret; | |||||
| } | } | ||||
| void strobe_prng ( | |||||
| strobe_t strobe, | |||||
| decaf_bool_t strobe_prng ( | |||||
| keccak_sponge_t sponge, | |||||
| unsigned char *out, | unsigned char *out, | ||||
| size_t len | |||||
| size_t len, | |||||
| uint8_t more | |||||
| ) { | ) { | ||||
| struct strobe_control cont = { PRNG, 0, 0 }; | |||||
| strobe_control_word(strobe, &cont); | |||||
| strobe_duplex(strobe->sponge, out, NULL, len); | |||||
| /* Forget. TODO: ORLY? */ | |||||
| cont.next = TAGFORGET; | |||||
| strobe_control_word(strobe, &cont); | |||||
| strobe_duplex(strobe->sponge, NULL, | |||||
| &strobe->sponge->state->b[0], | |||||
| STROBE_FORGET_BYTES | |||||
| ); | |||||
| /* FIXME: length?? */ | |||||
| unsigned char control[] = { PRNG }; | |||||
| decaf_bool_t ret = strobe_control_word(sponge, control, sizeof(control), more); | |||||
| strobe_duplex(sponge, out, NULL, len); | |||||
| // /** TODO: orly? */ | |||||
| // unsigned char control2[] = { 0, STROBE_FORGET_BYTES, TAGFORGET }; | |||||
| // ret &= strobe_control_word(sponge, control2, sizeof(control2)); | |||||
| // strobe_forget(sponge, STROBE_FORGET_BYTES); | |||||
| if (!sponge->params->pad/*keyed*/) ret = DECAF_FAILURE; | |||||
| return ret; | |||||
| } | } | ||||
| /* TODO: remove reliance on decaf? */ | /* TODO: remove reliance on decaf? */ | ||||
| decaf_bool_t strobe_verify_auth ( | decaf_bool_t strobe_verify_auth ( | ||||
| strobe_t strobe, | |||||
| keccak_sponge_t sponge, | |||||
| const unsigned char *in, | const unsigned char *in, | ||||
| size_t len | size_t len | ||||
| ) { | ) { | ||||
| unsigned char control[] = { | |||||
| (unsigned char)len, | |||||
| (unsigned char)STROBE_FORGET_BYTES, | |||||
| TAGFORGET | (sponge->params->client ? SERVER_TO_CLIENT : CLIENT_TO_SERVER) | |||||
| }; | |||||
| decaf_bool_t ret = strobe_control_word(sponge, control, sizeof(control), 0); | |||||
| unsigned char zero[len]; | unsigned char zero[len]; | ||||
| decaf_bool_t chain=0; | |||||
| assert(len < strobe->sponge->params->rate - STROBE_FORGET_BYTES); | |||||
| struct strobe_control cont = { TAGFORGET, len, | |||||
| strobe->params->client ? CLIENT_TO_SERVER : SERVER_TO_CLIENT }; | |||||
| strobe_control_word(strobe, &cont); | |||||
| strobe_unduplex(strobe->sponge, zero, in, len); | |||||
| strobe_duplex(strobe->sponge, NULL, | |||||
| &strobe->sponge->state->b[strobe->sponge->params->position], | |||||
| STROBE_FORGET_BYTES); | |||||
| strobe_unduplex(sponge, zero, in, len); | |||||
| strobe_forget(sponge, STROBE_FORGET_BYTES); | |||||
| /* Check for 0 */ | /* Check for 0 */ | ||||
| decaf_bool_t chain=0; | |||||
| unsigned i; | unsigned i; | ||||
| for (i=0; i<len; i++) { | for (i=0; i<len; i++) { | ||||
| chain |= zero[i]; | chain |= zero[i]; | ||||
| } | } | ||||
| return ((decaf_dword_t)chain-1)>>(8*sizeof(decaf_word_t)); | |||||
| ret &= ((decaf_dword_t)chain-1)>>(8*sizeof(decaf_word_t)); | |||||
| if (!sponge->params->pad/*keyed*/) ret = DECAF_FAILURE; | |||||
| return ret; | |||||
| } | } | ||||
| void strobe_destroy ( | |||||
| strobe_t strobe | |||||
| ) { | |||||
| decaf_bzero(strobe,sizeof(strobe_t)); | |||||
| } | |||||
| void strobe_respec ( | |||||
| strobe_t strobe, | |||||
| decaf_bool_t strobe_respec ( | |||||
| keccak_sponge_t sponge, | |||||
| const struct kparams_s *params | const struct kparams_s *params | ||||
| ) { | ) { | ||||
| struct strobe_control cont = { RESPEC, params->rate, params->startRound }; | |||||
| strobe_control_word(strobe, &cont); | |||||
| strobe->sponge->params[0] = params[0]; | |||||
| unsigned char control[] = { params->rate, params->startRound, RESPEC }; | |||||
| decaf_bool_t ret = strobe_control_word(sponge, control, sizeof(control), 0); | |||||
| if (!sponge->params->pad/*keyed*/) ret = DECAF_FAILURE; | |||||
| sponge->params->rate = params->rate; | |||||
| sponge->params->startRound = params->startRound; | |||||
| return ret; | |||||
| } | } | ||||
| /* TODO: Keyak instances, etc */ | /* TODO: Keyak instances, etc */ | ||||
| @@ -66,6 +66,7 @@ static void printSI(double x, const char *unit, const char *spacer = " ") { | |||||
| class Benchmark { | class Benchmark { | ||||
| static const int NTESTS = 1000; | static const int NTESTS = 1000; | ||||
| static double totalCy, totalS; | static double totalCy, totalS; | ||||
| /* FIXME Tcy if get descheduled */ | |||||
| public: | public: | ||||
| int i, ntests; | int i, ntests; | ||||
| double begin; | double begin; | ||||
| @@ -138,10 +139,19 @@ int main(int argc, char **argv) { | |||||
| decaf::SHAKE<128> shake1; | decaf::SHAKE<128> shake1; | ||||
| decaf::SHAKE<256> shake2; | decaf::SHAKE<256> shake2; | ||||
| decaf::SHA3<512> sha5; | decaf::SHA3<512> sha5; | ||||
| decaf::Strobe strobe(decaf::Strobe::CLIENT); | |||||
| unsigned char b1024[1024] = {1}; | unsigned char b1024[1024] = {1}; | ||||
| for (Benchmark b("SHAKE128 1kiB", 30); b.iter(); ) { shake1 += decaf::TmpBuffer(b1024,1024); } | for (Benchmark b("SHAKE128 1kiB", 30); b.iter(); ) { shake1 += decaf::TmpBuffer(b1024,1024); } | ||||
| for (Benchmark b("SHAKE256 1kiB", 30); b.iter(); ) { shake2 += decaf::TmpBuffer(b1024,1024); } | for (Benchmark b("SHAKE256 1kiB", 30); b.iter(); ) { shake2 += decaf::TmpBuffer(b1024,1024); } | ||||
| for (Benchmark b("SHA3-512 1kiB", 30); b.iter(); ) { sha5 += decaf::TmpBuffer(b1024,1024); } | for (Benchmark b("SHA3-512 1kiB", 30); b.iter(); ) { sha5 += decaf::TmpBuffer(b1024,1024); } | ||||
| strobe.key(decaf::TmpBuffer(b1024,1024)); | |||||
| for (Benchmark b("STROBE256 1kiB", 30); b.iter(); ) { | |||||
| strobe.encrypt_no_auth(decaf::TmpBuffer(b1024,1024),decaf::TmpBuffer(b1024,1024),b.i>1); | |||||
| } | |||||
| strobe.respec(STROBE_KEYED_128); | |||||
| for (Benchmark b("STROBEk128 1kiB", 30); b.iter(); ) { | |||||
| strobe.encrypt_no_auth(decaf::TmpBuffer(b1024,1024),decaf::TmpBuffer(b1024,1024),b.i>1); | |||||
| } | |||||
| for (Benchmark b("Scalar add", 1000); b.iter(); ) { s+=t; } | for (Benchmark b("Scalar add", 1000); b.iter(); ) { s+=t; } | ||||
| for (Benchmark b("Scalar times", 100); b.iter(); ) { s*=t; } | for (Benchmark b("Scalar times", 100); b.iter(); ) { s*=t; } | ||||
| for (Benchmark b("Scalar inv", 1); b.iter(); ) { s.inverse(); } | for (Benchmark b("Scalar inv", 1); b.iter(); ) { s.inverse(); } | ||||