From 8b24277c444fdafa244c87d0ca5e3a7c47eaf76d Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Sat, 25 Jul 2015 17:08:25 +0200 Subject: beginning of hmac implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/libkeccak.h | 1 + src/libkeccak/mac/hmac.c | 273 +++++++++++++++++++++++++++++++++++ src/libkeccak/mac/hmac.h | 366 +++++++++++++++++++++++++++++++++++++++++++++++ src/libkeccak/state.h | 17 ++- 4 files changed, 648 insertions(+), 9 deletions(-) create mode 100644 src/libkeccak/mac/hmac.c create mode 100644 src/libkeccak/mac/hmac.h (limited to 'src') diff --git a/src/libkeccak.h b/src/libkeccak.h index 3812a6b..cedbc94 100644 --- a/src/libkeccak.h +++ b/src/libkeccak.h @@ -26,6 +26,7 @@ #include "libkeccak/digest.h" #include "libkeccak/hex.h" #include "libkeccak/files.h" +#include "libkeccak/mac/hmac.h" #endif diff --git a/src/libkeccak/mac/hmac.c b/src/libkeccak/mac/hmac.c new file mode 100644 index 0000000..b679e8e --- /dev/null +++ b/src/libkeccak/mac/hmac.c @@ -0,0 +1,273 @@ +/** + * libkeccak – Keccak-family hashing library + * + * Copyright © 2014, 2015 Mattias Andrée (maandree@member.fsf.org) + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this library. If not, see . + */ +#include "hmac.h" + +#include "../digest.h" + + + +/** + * The outer pad pattern + */ +#define OUTER_PAD 0x5C + +/** + * The inner pad pattern + */ +#define INNER_PAD 0x36 + + + +/** + * Change to HMAC-hashing key on the state + * + * @param state The state that should be reset + * @param key The new key + * @param key_length The length of key, in bits + * @return Zero on success, -1 on error + */ +int libkeccak_hmac_set_key(libkeccak_hmac_state_t* restrict state, const char* restrict key, size_t key_length) +{ + size_t i, size, new_key_length, key_bytes; + char* old; + + size = (size_t)(state->sponge.r) > key_length ? (size_t)(state->sponge.r) : key_length; + new_key_length = size; + size = (size + 7) >> 3; + key_bytes = (key_length + 7) >> 3; + + if (size != key_bytes) + { + state->key_opad = realloc(old = state->key_opad, 2 * size); + if (state->key_opad == NULL) + return state->key_opad = old, -1; + state->key_ipad = state->key_opad + size / sizeof(char); + } + + memcpy(state->key_opad, key, key_bytes); + if (key_length & 7) + state->key_opad[(key_bytes >> 3) - 1] &= (1 << (key_length & 7)) - 1; + + if ((size_t)(state->sponge.r) > key_length) + __builtin_memset(state->key_opad + key_bytes / sizeof(char), 0, size - key_bytes); + + for (i = 0; i < size; i++) + state->key_ipad[i] = state->key_opad[i] ^ INNER_PAD, + state->key_opad[i] ^= OUTER_PAD; + + state->key_length = new_key_length; + + return 0; +} + + +/** + * Wipe sensitive data wihout freeing any data + * + * @param state The state that should be wipe + */ +void libkeccak_hmac_wipe(volatile libkeccak_hmac_state_t* restrict state) +{ + volatile char* restrict key_pads; + size_t i, size; + key_pads = state->key_opad; + size = 2 * ((state->key_length + 7) >> 3); + libkeccak_state_wipe(&(state->sponge)); + for (i = 0; i < size; i++) + key_pads[i] = 0; + state->leftover = 0; + __builtin_memset(state->buffer, 0, state->buffer_size); +} + + +/** + * Make a copy of an HMAC hashing-state + * + * @param dest The slot for the duplicate, must not be initialised (memory leak otherwise) + * @param src The state to duplicate + * @return Zero on success, -1 on error + */ +int libkeccak_hmac_copy(libkeccak_hmac_state_t* restrict dest, const libkeccak_hmac_state_t* restrict src) +{ + int saved_errno; + size_t size; + + dest->key_opad = NULL; + dest->key_ipad = NULL; + + if (libkeccak_state_copy(&(dest->sponge), &(src->sponge)) < 0) + return -1; + + dest->key_length = src->key_length; + dest->leftover = src->leftover; + + size = (src->key_length + 7) >> 3; + dest->key_opad = malloc(2 * size); + if (dest->key_opad == NULL) + return saved_errno = errno, libkeccak_state_destroy(&(dest->sponge)), errno = saved_errno, -1; + dest->key_ipad = dest->key_opad + size / sizeof(char); + + memcpy(dest->key_opad, src->key_opad, size); + memcpy(dest->key_ipad, src->key_ipad, size); + + return 0; +} + + +/** + * Unmarshal a `libkeccak_hmac_state_t` from a buffer + * + * @param state The slot for the unmarshalled state, must not be initialised (memory leak otherwise) + * @param data The input buffer + * @return The number of bytes read from `data`, 0 on error + */ +size_t libkeccak_hmac_unmarshal(libkeccak_hmac_state_t* restrict state, const char* restrict data) +{ + size_t parsed, size, i; + int saved_errno; + + state->key_opad = NULL; + state->key_ipad = NULL; + + parsed = libkeccak_state_unmarshal(&(state->sponge), data); + if (parsed == 0) + return 0; + + data += parsed / sizeof(char); + state->key_length = *(const size_t*)data; + data += sizeof(size_t) / sizeof(char); + size = (state->key_length + 7) >> 3; + + state->key_opad = malloc(2 * size); + if (state->key_opad == NULL) + return saved_errno = errno, libkeccak_state_destroy(&(state->sponge)), errno = saved_errno, -1; + memcpy(state->key_opad, data, size); + data += size / sizeof(char); + + if (data[0]) + { + state->key_ipad = state->key_opad + size / sizeof(char); + memcpy(state->key_ipad, state->key_opad, size); + for (i = 0; i < size / sizeof(char); i++) + state->key_ipad[i] ^= (char)(OUTER_PAD ^ INNER_PAD); + } + + state->leftover = data[1]; + state->buffer = NULL; + state->buffer_size = 0; + + return parsed + sizeof(size_t) + size + 2 * sizeof(char); +} + + +/** + * Absorb more, or the first part, of the message + * without wiping sensitive data when possible + * + * @param state The hashing state + * @param msg The partial message + * @param msglen The length of the partial message, in bytes + * @return Zero on success, -1 on error + */ +__attribute__((nonnull)) +int libkeccak_hmac_fast_update(libkeccak_hmac_state_t* restrict state, const char* restrict msg, size_t msglen) +{ + char* old; + size_t i; + int n, cn; + + if (state->key_ipad != NULL) + { + if (libkeccak_fast_update(&(state->sponge), state->key_ipad, state->key_length >> 3) < 0) + return -1; + if (state->key_length & 7) + state->leftover = state->key_ipad[(state->key_length >> 3)]; + state->key_ipad = NULL; + } + + if (!(state->key_length & 7)) + return libkeccak_fast_update(&(state->sponge), msg, msglen); + + if (msglen != state->buffer_size) + { + state->buffer = realloc(old = state->buffer, state->buffer_size = msglen); + if (state->buffer == NULL) + return state->buffer = old, -1; + } + + n = (int)(state->key_length & 7); + cn = 8 - n; + for (i = 1; i < msglen; i++) + state->buffer[i] = (((unsigned char)(msg[i - 1])) >> cn) | (msg[i] << n); + state->buffer[0] = (state->leftover & ((1 << n) - 1)) | (msg[0] << n); + state->leftover = ((unsigned char)(msg[msglen - 1])) >> cn; + + return libkeccak_fast_update(&(state->sponge), state->buffer, msglen); +} + + +/** + * Absorb more, or the first part, of the message + * and wipe sensitive data when possible + * + * @param state The hashing state + * @param msg The partial message + * @param msglen The length of the partial message, in bytes + * @return Zero on success, -1 on error + */ +__attribute__((nonnull)) +int libkeccak_hmac_update(libkeccak_hmac_state_t* restrict state, const char* restrict msg, size_t msglen) +{ + size_t i; + int n, cn, r, saved_errno; + + if (state->key_ipad != NULL) + { + if (libkeccak_update(&(state->sponge), state->key_ipad, state->key_length >> 3) < 0) + return -1; + if (state->key_length & 7) + state->leftover = state->key_ipad[(state->key_length >> 3)]; + state->key_ipad = NULL; + } + + if (!(state->key_length & 7)) + return libkeccak_update(&(state->sponge), msg, msglen); + + if (msglen != state->buffer_size) + { + free(state->buffer); + state->buffer = malloc(state->buffer_size = msglen); + if (state->buffer == NULL) + return -1; + } + + n = (int)(state->key_length & 7); + cn = 8 - n; + for (i = 1; i < msglen; i++) + state->buffer[i] = (((unsigned char)(msg[i - 1])) >> cn) | (msg[i] << n); + state->buffer[0] = (state->leftover & ((1 << n) - 1)) | (msg[0] << n); + state->leftover = ((unsigned char)(msg[msglen - 1])) >> cn; + + r = libkeccak_update(&(state->sponge), state->buffer, msglen); + saved_errno = errno; + __builtin_memset(state->buffer, 0, msglen); + errno = saved_errno; + return r; +} + diff --git a/src/libkeccak/mac/hmac.h b/src/libkeccak/mac/hmac.h new file mode 100644 index 0000000..9526ef8 --- /dev/null +++ b/src/libkeccak/mac/hmac.h @@ -0,0 +1,366 @@ +/** + * libkeccak – Keccak-family hashing library + * + * Copyright © 2014, 2015 Mattias Andrée (maandree@member.fsf.org) + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this library. If not, see . + */ +#ifndef LIBKECCAK_MAC_HMAC_H +#define LIBKECCAK_MAC_HMAC_H 1 + + +/* The Keccak hash-function, that was selected by NIST as the SHA-3 competition winner, + * doesn't need this nested approach and can be used to generate a MAC by simply prepending + * the key to the message. [http://keccak.noekeon.org] + */ + + +#include "../spec.h" +#include "../state.h" + +#include +#include +#include +#include +#include + + + +/** + * Datastructure that describes the state of an HMAC-hashing process + */ +typedef struct libkeccak_hmac_state +{ + /** + * The key right-padded and XOR:ed with the outer pad + */ + char* restrict key_opad; + + /** + * The key right-padded and XOR:ed with the inner pad + */ + char* restrict key_ipad; + /* Not marshalled, implicitly unmarshalled using `key_opad`. */ + /* Shares allocation with `key_opad`, do not `free`. */ + + /** + * The length of key, but at least the input block size, in bits + */ + size_t key_length; + + /** + * The state of the underlaying hash-algorithm + */ + libkeccak_state_t sponge; + + /** + * Buffer used to temporarily store bit shift message if + * `key_length` is not zero modulus 8 + */ + char* restrict buffer; + + /** + * The allocation size of `buffer` + */ + size_t buffer_size; + + /** + * Part of feed key, message or digest that have not been passed yet + */ + char leftover; + + char __pad[sizeof(void*) / sizeof(char) - 1]; + +} libkeccak_hmac_state_t; + + + +/** + * Change to HMAC-hashing key on the state + * + * @param state The state that should be reset + * @param key The new key + * @param key_length The length of key, in bits + * @return Zero on success, -1 on error + */ +__attribute__((nonnull(1), unused)) +int libkeccak_hmac_set_key(libkeccak_hmac_state_t* restrict state, const char* restrict key, size_t key_length); + + +/** + * Initialise an HMAC hashing-state according to hashing specifications + * + * @param state The state that should be initialised + * @param spec The specifications for the state + * @param key The key + * @param key_length The length of key, in bits + * @return Zero on success, -1 on error + */ +static inline __attribute__((nonnull)) +int libkeccak_hmac_initialise(libkeccak_hmac_state_t* restrict state, const libkeccak_spec_t* restrict spec, + const char* restrict key, size_t key_length) +{ + int saved_errno; + if (libkeccak_state_initialise(&(state->sponge), spec) < 0) + return -1; + if (libkeccak_hmac_set_key(state, key, key_length) < 0) + return saved_errno = errno, libkeccak_state_destroy(&(state->sponge)), errno = saved_errno, -1; + state->leftover = 0; + state->buffer = NULL; + state->buffer_size = 0; + return 0; +} + + +/** + * Wrapper for `libkeccak_hmac_initialise` that also allocates the states + * + * @param spec The specifications for the state + * @param key The key + * @param key_length The length of key, in bits + * @return The state, `NULL` on error + */ +static inline __attribute__((nonnull, unused, warn_unused_result)) +libkeccak_hmac_state_t* libkeccak_hmac_create(const libkeccak_spec_t* restrict spec, + const char* restrict key, size_t key_length) +{ + libkeccak_hmac_state_t* restrict state = malloc(sizeof(libkeccak_hmac_state_t)); + int saved_errno; + if ((state == NULL) || libkeccak_hmac_initialise(state, spec, key, key_length)) + return saved_errno = errno, free(state), errno = saved_errno, NULL; + return state; +} + + +/** + * Reset an HMAC-hashing state according to hashing specifications, + * you can choose whether to change the key + * + * @param state The state that should be reset + * @param key The new key, `NULL` to keep the old key + * @param key_length The length of key, in bits, ignored if `key == NULL` + * @return Zero on success, -1 on error + */ +static inline __attribute__((nonnull(1), unused)) +int libkeccak_hmac_reset(libkeccak_hmac_state_t* restrict state, const char* restrict key, size_t key_length) +{ + libkeccak_state_reset(&(state->sponge)); + return key != NULL ? libkeccak_hmac_set_key(state, key, key_length) : 0; +} + + +/** + * Wipe sensitive data wihout freeing any data + * + * @param state The state that should be wipe + */ +__attribute__((nonnull, nothrow, optimize("-O0"))) +void libkeccak_hmac_wipe(volatile libkeccak_hmac_state_t* restrict state); + + +/** + * Release resources allocation for an HMAC hashing-state without wiping sensitive data + * + * @param state The state that should be destroyed + */ +static inline +void libkeccak_hmac_fast_destroy(libkeccak_hmac_state_t* restrict state) +{ + if (state == NULL) + return; + free(state->key_opad); + state->key_opad = NULL; + state->key_ipad = NULL; + state->key_length = 0; + free(state->buffer); + state->buffer = NULL; + state->buffer_size = 0; +} + + +/** + * Release resources allocation for an HMAC hasing-state and wipe sensitive data + * + * @param state The state that should be destroyed + */ +static inline __attribute__((unused, optimize("-O0"))) +void libkeccak_hmac_destroy(volatile libkeccak_hmac_state_t* restrict state) +{ + if (state == NULL) + return; + libkeccak_hmac_wipe(state); + free(state->key_opad); + state->key_opad = NULL; + state->key_ipad = NULL; + state->key_length = 0; + state->leftover = 0; + free(state->buffer); + state->buffer = NULL; + state->buffer_size = 0; +} + + +/** + * Wrapper for `libkeccak_fast_destroy` that also frees the allocation of the state + * + * @param state The state that should be freed + */ +static inline __attribute__((unused)) +void libkeccak_hmac_fast_free(libkeccak_hmac_state_t* restrict state) +{ + libkeccak_hmac_fast_destroy(state); + free(state); +} + + +/** + * Wrapper for `libkeccak_hmac_destroy` that also frees the allocation of the state + * + * @param state The state that should be freed + */ +static inline __attribute__((unused, optimize("-O0"))) +void libkeccak_hmac_free(volatile libkeccak_hmac_state_t* restrict state) +{ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcast-qual" + libkeccak_hmac_destroy(state); + free((libkeccak_hmac_state_t*)state); +# pragma GCC diagnostic pop +} + + +/** + * Make a copy of an HMAC hashing-state + * + * @param dest The slot for the duplicate, must not be initialised (memory leak otherwise) + * @param src The state to duplicate + * @return Zero on success, -1 on error + */ +__attribute__((nonnull)) +int libkeccak_hmac_copy(libkeccak_hmac_state_t* restrict dest, const libkeccak_hmac_state_t* restrict src); + + +/** + * A wrapper for `libkeccak_hmac_copy` that also allocates the duplicate + * + * @param src The state to duplicate + * @return The duplicate, `NULL` on error + */ +static inline __attribute__((nonnull, unused, warn_unused_result)) +libkeccak_hmac_state_t* libkeccak_hmac_duplicate(const libkeccak_hmac_state_t* restrict src) +{ + libkeccak_hmac_state_t* restrict dest = malloc(sizeof(libkeccak_hmac_state_t)); + int saved_errno; + if ((dest == NULL) || libkeccak_hmac_copy(dest, src)) + return saved_errno = errno, libkeccak_hmac_free(dest), errno = saved_errno, NULL; + return dest; +} + + +/** + * Calculates the allocation size required for the second argument + * of `libkeccak_hmac_marshal` (`char* restrict data)`) + * + * @param state The state as it will be marshalled by a subsequent call to `libkeccak_hamc_marshal` + * @return The allocation size needed for the buffer to which the state will be marshalled + */ +static inline __attribute__((nonnull, nothrow, unused, warn_unused_result, pure)) +size_t libkeccak_hmac_marshal_size(const libkeccak_hmac_state_t* restrict state) +{ + return libkeccak_state_marshal_size(&(state->sponge)) + sizeof(size_t) + + ((state->key_length + 7) >> 3) + 2 * sizeof(char); +} + + +/** + * Marshal a `libkeccak_hmac_state_t` into a buffer + * + * @param state The state to marshal + * @param data The output buffer + * @return The number of bytes stored to `data` + */ +static inline __attribute__((nonnull, nothrow)) +size_t libkeccak_hmac_marshal(const libkeccak_hmac_state_t* restrict state, char* restrict data) +{ + size_t written = libkeccak_state_marshal(&(state->sponge), data); + data += written / sizeof(char); + *(size_t*)data = state->key_length; + data += sizeof(size_t) / sizeof(char); + memcpy(data, state->key_opad, (state->key_length + 7) >> 3); + data += ((state->key_length + 7) >> 3) / sizeof(char); + data[0] = (char)(state->key_ipad != NULL); + data[1] = state->leftover; + return written + sizeof(size_t) + ((state->key_length + 7) >> 3) + 2 * sizeof(char); +} + + +/** + * Unmarshal a `libkeccak_hmac_state_t` from a buffer + * + * @param state The slot for the unmarshalled state, must not be initialised (memory leak otherwise) + * @param data The input buffer + * @return The number of bytes read from `data`, 0 on error + */ +__attribute__((nonnull)) +size_t libkeccak_hmac_unmarshal(libkeccak_hmac_state_t* restrict state, const char* restrict data); + + +/** + * Gets the number of bytes the `libkeccak_hmac_state_t` stored + * at the beginning of `data` occupies + * + * @param data The data buffer + * @return The byte size of the stored state + */ +static inline __attribute__((nonnull, nothrow, warn_unused_result, pure)) +size_t libkeccak_hmac_unmarshal_skip(const char* restrict data) +{ + size_t skip = libkeccak_state_unmarshal_skip(data); + data += skip / sizeof(char); + return skip + sizeof(size_t) + *(const size_t*)data + 2 * sizeof(char); +} + + +/** + * Absorb more, or the first part, of the message + * without wiping sensitive data when possible + * + * @param state The hashing state + * @param msg The partial message + * @param msglen The length of the partial message, in bytes + * @return Zero on success, -1 on error + */ +__attribute__((nonnull)) +int libkeccak_hmac_fast_update(libkeccak_hmac_state_t* restrict state, const char* restrict msg, size_t msglen); + + +/** + * Absorb more, or the first part, of the message + * and wipe sensitive data when possible + * + * @param state The hashing state + * @param msg The partial message + * @param msglen The length of the partial message, in bytes + * @return Zero on success, -1 on error + */ +__attribute__((nonnull)) +int libkeccak_hmac_update(libkeccak_hmac_state_t* restrict state, const char* restrict msg, size_t msglen); + + +/* TODO libkeccak_hmac_fast_digest(); */ +/* TODO libkeccak_hmac_digest(); */ + + +#endif + diff --git a/src/libkeccak/state.h b/src/libkeccak/state.h index 33785a4..69a2455 100644 --- a/src/libkeccak/state.h +++ b/src/libkeccak/state.h @@ -30,7 +30,7 @@ /** - * Satastructure that describes the state of a hashing process + * Datastructure that describes the state of a hashing process * * The `char`-size of the output hashsum is calculated by `(.n + 7) / 8` */ @@ -114,11 +114,10 @@ int libkeccak_state_initialise(libkeccak_state_t* restrict state, const libkecca /** * Reset a state according to hashing specifications * - * @param state The state that should be reset - * @return Zero on success, -1 on error + * @param state The state that should be reset */ -__attribute__((nonnull, nothrow, unused)) -static inline void libkeccak_state_reset(libkeccak_state_t* restrict state) +static inline __attribute__((nonnull, nothrow, unused)) +void libkeccak_state_reset(libkeccak_state_t* restrict state) { state->mptr = 0; __builtin_memset(state->S, 0, sizeof(state->S)); @@ -184,8 +183,8 @@ void libkeccak_state_destroy(volatile libkeccak_state_t* restrict state) /** * Wrapper for `libkeccak_state_initialise` that also allocates the states * - * @param spec The specifications for the state - * @return The state, `NULL` on error + * @param spec The specifications for the state + * @return The state, `NULL` on error */ static inline __attribute__((nonnull, unused, warn_unused_result)) libkeccak_state_t* libkeccak_state_create(const libkeccak_spec_t* restrict spec) @@ -241,8 +240,8 @@ int libkeccak_state_copy(libkeccak_state_t* restrict dest, const libkeccak_state /** * A wrapper for `libkeccak_state_copy` that also allocates the duplicate * - * @param src The state to duplicate - * @return The duplicate, `NULL` on error + * @param src The state to duplicate + * @return The duplicate, `NULL` on error */ static inline __attribute__((nonnull, unused, warn_unused_result)) libkeccak_state_t* libkeccak_state_duplicate(const libkeccak_state_t* restrict src) -- cgit v1.2.3-70-g09d2