/*
* Copyright (C) 2013-2016 Andreas Steffen
* HSR Hochschule fuer Technik Rapperswil
*
* 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 <http://www.fsf.org/copyleft/gpl.txt>.
*
* 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 "mgf1_xof.h"
#include "crypto/hashers/hasher.h"
#include "utils/debug.h"
typedef struct private_mgf1_xof_t private_mgf1_xof_t;
/**
* Private data of an mgf1_xof_t object.
*/
struct private_mgf1_xof_t {
/**
* Public mgf1_xof_t interface.
*/
mgf1_xof_t public;
/**
* XOF type of the MGF1 Mask Generation Function
*/
ext_out_function_t type;
/**
* Hasher the MGF1 Mask Generation Function is based on
*/
hasher_t *hasher;
/**
* Is the seed hashed before using it as a seed for MGF1 ?
*/
bool hash_seed;
/**
* Counter
*/
uint32_t counter;
/**
* Set if counter has reached 2^32
*/
bool overflow;
/**
* Current state to be hashed
*/
chunk_t state;
/**
* Position of the 4 octet counter string
*/
uint8_t *ctr_str;
/**
* Latest hash block
*/
uint8_t buf[HASH_SIZE_SHA512];
/**
* Index pointing to the current position in the hash block
*/
size_t buf_index;
};
METHOD(xof_t, get_type, ext_out_function_t,
private_mgf1_xof_t *this)
{
return this->type;
}
static bool get_next_block(private_mgf1_xof_t *this, uint8_t *buffer)
{
/* detect overflow, set counter string and increment counter */
if (this->overflow)
{
DBG1(DBG_LIB, "MGF1 overflow occurred");
return FALSE;
}
htoun32(this->ctr_str, this->counter++);
if (this->counter == 0)
{
this->overflow = TRUE;
}
/* get the next block from the hash function */
if (!this->hasher->get_hash(this->hasher, this->state, buffer))
{
return FALSE;
}
return TRUE;
}
METHOD(xof_t, get_bytes, bool,
private_mgf1_xof_t *this, size_t out_len, uint8_t *buffer)
{
size_t index = 0, blocks, len, hash_size;
hash_size = this->hasher->get_hash_size(this->hasher);
/* empty the current hash block buffer first */
len = min(out_len, hash_size - this->buf_index);
if (len)
{
memcpy(buffer, this->buf + this->buf_index, len);
index += len;
this->buf_index += len;
}
/* copy whole hash blocks directly to output buffer */
blocks = (out_len - index) / hash_size;
while (blocks--)
{
if (!get_next_block(this, buffer + index))
{
return FALSE;
}
index += hash_size;
}
/* get another hash block if some more output bytes are needed */
len = out_len - index;
if (len)
{
if (!get_next_block(this, this->buf))
{
return FALSE;
}
memcpy(buffer + index, this->buf, len);
this->buf_index = len;
}
return TRUE;
}
METHOD(xof_t, allocate_bytes, bool,
private_mgf1_xof_t *this, size_t out_len, chunk_t *chunk)
{
*chunk = chunk_alloc(out_len);
if (!get_bytes(this, out_len, chunk->ptr))
{
chunk_free(chunk);
return FALSE;
}
return TRUE;
}
METHOD(xof_t, get_block_size, size_t,
private_mgf1_xof_t *this)
{
return this->hasher->get_hash_size(this->hasher);
}
METHOD(xof_t, get_seed_size, size_t,
private_mgf1_xof_t *this)
{
return this->hasher->get_hash_size(this->hasher);
}
METHOD(xof_t, set_seed, bool,
private_mgf1_xof_t *this, chunk_t seed)
{
size_t hash_size, state_len;
if (seed.len == 0)
{
DBG1(DBG_LIB, "empty seed for MGF1");
return FALSE;
}
/* determine state size and allocate space accordingly */
hash_size = this->hasher->get_hash_size(this->hasher);
state_len = (this->hash_seed ? hash_size : seed.len) + 4;
chunk_clear(&this->state);
this->state = chunk_alloc(state_len);
/* hash block buffer is empty */
this->buf_index = hash_size;
/* reset counter */
this->counter = 0;
/* determine position of the 4 octet counter string */
this->ctr_str = this->state.ptr + state_len - 4;
if (this->hash_seed)
{
if (!this->hasher->get_hash(this->hasher, seed, this->state.ptr))
{
DBG1(DBG_LIB, "failed to hash seed for MGF1");
return FALSE;
}
}
else
{
memcpy(this->state.ptr, seed.ptr, seed.len);
}
return TRUE;
}
METHOD(xof_t, destroy, void,
private_mgf1_xof_t *this)
{
this->hasher->destroy(this->hasher);
chunk_clear(&this->state);
free(this);
}
METHOD(mgf1_t, set_hash_seed, void,
private_mgf1_xof_t *this, bool yes)
{
this->hash_seed = yes;
}
/*
* Described in header.
*/
mgf1_xof_t *mgf1_xof_create(ext_out_function_t algorithm)
{
private_mgf1_xof_t *this;
hash_algorithm_t hash_alg;
hasher_t *hasher;
switch (algorithm)
{
case XOF_MGF1_SHA1:
hash_alg = HASH_SHA1;
break;
case XOF_MGF1_SHA224:
hash_alg = HASH_SHA224;
break;
case XOF_MGF1_SHA256:
hash_alg = HASH_SHA256;
break;
case XOF_MGF1_SHA384:
hash_alg = HASH_SHA384;
break;
case XOF_MGF1_SHA512:
hash_alg = HASH_SHA512;
break;
default:
return NULL;
}
hasher = lib->crypto->create_hasher(lib->crypto, hash_alg);
if (!hasher)
{
DBG1(DBG_LIB, "failed to create %N hasher for MGF1",
hash_algorithm_names, hash_alg);
return NULL;
}
INIT(this,
.public = {
.mgf1_interface = {
.xof_interface = {
.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,
},
.set_hash_seed = _set_hash_seed,
},
},
.type = algorithm,
.hasher = hasher,
);
return &this->public;
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>