/*
 * Copyright 2006 - 2018, Werner Dittmann
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma clang diagnostic push
#pragma ide diagnostic ignored "OCUnusedGlobalDeclarationInspection"
#ifndef SKEINAPI_H
#define SKEINAPI_H

/**
 * @file skeinApi.h
 * @brief A Skein API and its functions.
 * @{
 *
 * This API and the functions that implement this API simplify usage
 * of Skein. The design and the way to use the functions follow the openSSL
 * design but at the same time take care of some Skein specific behaviour
 * and possibilities.
 * 
 * The functions enable applications to create normal Skein hashes and
 * message authentication codes (MAC).
 * 
 * Using these functions is simple and straight forward:
 * 
 * @code
 * 
 * #include <skeinApi.h>
 * 
 * ...
 * SkeinCtx_t ctx;             // a Skein hash or MAC context
 * 
 * // prepare context, here for a Skein with a state size of 512 bits.
 * skeinCtxPrepare(&ctx, Skein512);
 * 
 * // Initialize the context to set the requested hash length in bits:
 * // request an output hash size of 31 bits (Skein supports variable
 * // output sizes, even very strange sizes)
 * skeinInit(&ctx, 31);
 * 
 * // Now update Skein with any number of message bits. A function that
 * // takes a number of bytes is also available.
 * skeinUpdateBits(&ctx, message, msgLength);
 * 
 * // Now get the result of the Skein hash. The output buffer must be
 * // large enough to hold the request number of output bits. The application
 * // may now extract the bits.
 * skeinFinal(&ctx, result);
 * ...
 * @endcode
 * 
 * An application may use @c skeinReset to reset a Skein context and use
 * it for creation of another hash with the same Skein state size and output
 * bit length. In this case the API implementation restores some internal
 * state data and saves a full Skein initialization round.
 * 
 * To create a MAC the application just uses @c skeinMacInit instead of 
 * @c skeinInit. All other function calls remain the same.
 * 
 */

#include <cryptcommon/skein.h>

#ifdef _MSC_VER
typedef signed __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef signed __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef signed __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
#include <stdint.h>
#endif

#ifdef __cplusplus
extern "C"
{
#endif

    /**
     * Which Skein size to use
     */
    typedef enum SkeinSize {
        Skein256 = 256,     /*!< Skein with 256 bit state */
        Skein512 = 512,     /*!< Skein with 512 bit state */
        Skein1024 = 1024    /*!< Skein with 1024 bit state */
    } SkeinSize_t;

    /**
     * Context for Skein.
     *
     * This structure was setup with some know-how of the internal
     * Skein structures, in particular ordering of header and size dependent
     * variables. If Skein implementation changes this, then adapt these
     * structures as well.
     */
    typedef struct SkeinCtx {
        u64b_t skeinSize;
        u64b_t  XSave[SKEIN_MAX_STATE_WORDS];   /* save area for state variables */
        union {
            Skein_Ctxt_Hdr_t h;
            Skein_256_Ctxt_t s256;
            Skein_512_Ctxt_t s512;
            Skein1024_Ctxt_t s1024;
        } m;
    } SkeinCtx_t;

    /**
     * Prepare a Skein context.
     * 
     * An application must call this function before it can use the Skein
     * context. The functions clears memory and initializes size dependent
     * variables.
     *
     * @param ctx
     *     Pointer to a Skein context.
     * @param size
     *     Which Skein size to use.
     * @return
     *     SKEIN_SUCESS of SKEIN_FAIL
     */
    int skeinCtxPrepare(SkeinCtx_t* ctx, SkeinSize_t size);

    /**
     * Initialize a Skein context.
     *
     * Initializes the context with this data and saves the resulting Skein 
     * state variables for further use.
     *
     * @param ctx
     *     Pointer to a Skein context.
     * @param hashBitLen
     *     Number of MAC hash bits to compute or zero
     * @return
     *     SKEIN_SUCESS of SKEIN_FAIL
     * @see skeinReset
     */
    int skeinInit(SkeinCtx_t* ctx, size_t hashBitLen);

    /**
     * Resets a Skein context for furter use.
     * 
     * Restores the saved chaining variables to reset the Skein context. 
     * Thus applications can reuse the same setup to  process several 
     * messages. This saves a complete Skein initialization cycle.
     * 
     * @param ctx
     *     Pointer to a pre-initialized Skein MAC context
     */
    void skeinReset(SkeinCtx_t* ctx);
    
    /**
     * Initializes or reuses a Skein context for MAC usage.
     * 
     * Initializes the context with this data and saves the resulting Skein 
     * state variables for further use.
     *
     * Applications call the normal Skein functions to update the MAC and
     * get the final result.
     *
     * @param ctx
     *     Pointer to an empty or preinitialized Skein MAC context
     * @param key
     *     Pointer to key bytes or NULL
     * @param keyLen
     *     Length of the key in bytes or zero
     * @param hashBitLen
     *     Number of MAC hash bits to compute or zero
     * @return
     *     SKEIN_SUCESS of SKEIN_FAIL
     */
    int skeinMacInit(SkeinCtx_t* ctx, const uint8_t *key, size_t keyLen,
                     size_t hashBitLen);

    /**
     * Update Skein with the next part of the message.
     *
     * @param ctx
     *     Pointer to initialized Skein context
     * @param msg
     *     Pointer to the message.
     * @param msgByteCnt
     *     Length of the message in @b bytes
     * @return
     *     Success or error code.
     */
    int skeinUpdate(SkeinCtx_t *ctx, const uint8_t *msg,
                    size_t msgByteCnt);

    /**
     * Update the hash with a message bit string.
     *
     * Skein can handle data not only as bytes but also as bit strings of
     * arbitrary length (up to its maximum design size).
     *
     * @param ctx
     *     Pointer to initialized Skein context
     * @param msg
     *     Pointer to the message.
     * @param msgBitCnt
     *     Length of the message in @b bits.
     */
    int skeinUpdateBits(SkeinCtx_t *ctx, const uint8_t *msg,
                        size_t msgBitCnt);

    /**
     * Finalize Skein and return the hash.
     * 
     * Before an application can reuse a Skein setup the application must
     * reinitialize the Skein context.See the approriate initialization 
     * methods how to achieve this.
     *
     * @param ctx
     *     Pointer to initialized Skein context
     * @param hash
     *     Pointer to buffer that receives the hash. The buffer must be large
     *     enough to store @c hashBitLen bits.
     * @return
     *     Success or error code.
     * @see skeinInit
     * @see skeinMacInit
     */
    int skeinFinal(SkeinCtx_t* ctx, uint8_t* hash);

#ifdef __cplusplus
}
#endif

/**
 * @}
 */
#endif

#pragma clang diagnostic pop