/* * Copyright (C) 2020 Tobias Brunner * * Copyright (C) secunet Security Networks AG * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See . * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ #include /* SHA3 was added with 1.1.1 */ #if OPENSSL_VERSION_NUMBER >= 0x1010100fL && !defined(OPENSSL_NO_SHAKE) #include "openssl_xof.h" #define KECCAK_STATE_SIZE 200 /* 1600 bits*/ typedef struct private_xof_t private_xof_t; /** * Private data */ struct private_xof_t { /** * Public interface. */ xof_t public; /** * XOF algorithm to be used */ ext_out_function_t algorithm; /** * Internal type reference */ const EVP_MD *md; /** * Internal context */ EVP_MD_CTX *ctx; /** * Current seed */ chunk_t seed; /** * Offset into generated data */ size_t offset; }; METHOD(xof_t, get_type, ext_out_function_t, private_xof_t *this) { return this->algorithm; } METHOD(xof_t, get_bytes, bool, private_xof_t *this, size_t out_len, uint8_t *buffer) { bool success = FALSE; chunk_t data; /* we can call EVP_DigestFinalXOF() only once, so to support an arbitrary * number of calls to get_bytes(), we request all the data we already * requested previously and just ignore what we already handed out */ if (EVP_DigestInit_ex(this->ctx, this->md, NULL) == 1 && EVP_DigestUpdate(this->ctx, this->seed.ptr, this->seed.len) == 1) { data = chunk_alloc(out_len + this->offset); if (EVP_DigestFinalXOF(this->ctx, data.ptr, data.len) == 1) { memcpy(buffer, data.ptr + this->offset, out_len); this->offset += out_len; success = TRUE; } chunk_clear(&data); } return success; } METHOD(xof_t, allocate_bytes, bool, private_xof_t *this, size_t out_len, chunk_t *chunk) { *chunk = chunk_alloc(out_len); return get_bytes(this, out_len, chunk->ptr); } METHOD(xof_t, get_block_size, size_t, private_xof_t *this) { return EVP_MD_block_size(this->md); } METHOD(xof_t, get_seed_size, size_t, private_xof_t *this) { return KECCAK_STATE_SIZE - EVP_MD_block_size(this->md); } METHOD(xof_t, set_seed, bool, private_xof_t *this, chunk_t seed) { chunk_clear(&this->seed); this->seed = chunk_clone(seed); this->offset = 0; return TRUE; } METHOD(xof_t, destroy, void, private_xof_t *this) { EVP_MD_CTX_free(this->ctx); chunk_clear(&this->seed); free(this); } /* * Described in header */ xof_t *openssl_xof_create(ext_out_function_t algorithm) { private_xof_t *this; const EVP_MD *md; switch (algorithm) { case XOF_SHAKE_128: md = EVP_shake128(); break; case XOF_SHAKE_256: md = EVP_shake256(); break; default: return NULL; } INIT(this, .public = { .get_type = _get_type, .get_bytes = _get_bytes, .allocate_bytes = _allocate_bytes, .get_block_size = _get_block_size, .get_seed_size = _get_seed_size, .set_seed = _set_seed, .destroy = _destroy, }, .algorithm = algorithm, .md = md, .ctx = EVP_MD_CTX_new(), ); return &this->public; } #endif /* OPENSSL_NO_ECDH */