/* ctr_prng.h - TinyCrypt interface to a CTR-PRNG implementation */

/*
 * Copyright (c) 2016, Chris Morrison
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @file
 * @brief Interface to a CTR-PRNG implementation.
 *
 *  Overview:   A pseudo-random number generator (PRNG) generates a sequence
 *              of numbers that have a distribution close to the one expected
 *              for a sequence of truly random numbers. The NIST Special
 *              Publication 800-90A specifies several mechanisms to generate
 *              sequences of pseudo random numbers, including the CTR-PRNG one
 *              which is based on AES. TinyCrypt implements CTR-PRNG with
 *              AES-128.
 *
 *  Security:   A cryptographically secure PRNG depends on the existence of an
 *              entropy source to provide a truly random seed as well as the
 *              security of the primitives used as the building blocks (AES-128
 *              in this instance).
 *
 *  Requires:   - AES-128
 *
 *  Usage:      1) call tc_ctr_prng_init to seed the prng context
 *
 *              2) call tc_ctr_prng_reseed to mix in additional entropy into
 *              the prng context
 *
 *              3) call tc_ctr_prng_generate to output the pseudo-random data
 *
 *              4) call tc_ctr_prng_uninstantiate to zero out the prng context
 */

#ifndef __BLE_MESH_TC_CTR_PRNG_H__
#define __BLE_MESH_TC_CTR_PRNG_H__

#include <tinycrypt/aes.h>

#define TC_CTR_PRNG_RESEED_REQ -1

#ifdef __cplusplus
extern "C" {
#endif

typedef struct {
    /* updated each time another BLOCKLEN_BYTES bytes are produced */
    uint8_t V[TC_AES_BLOCK_SIZE];

    /* updated whenever the PRNG is reseeded */
    struct tc_aes_key_sched_struct key;

    /* number of requests since initialization/reseeding */
    uint64_t reseedCount;
} TCCtrPrng_t;


/**
 *  @brief CTR-PRNG initialization procedure
 *  Initializes prng context with entropy and personalization string (if any)
 *  @return returns TC_CRYPTO_SUCCESS (1)
 *          returns TC_CRYPTO_FAIL (0) if:
 *                ctx == NULL,
 *                entropy == NULL,
 *                entropyLen < (TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE)
 *  @note       Only the first (TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE) bytes of
 *              both the entropy and personalization inputs are used -
 *              supplying additional bytes has no effect.
 *  @param ctx IN/OUT -- the PRNG context to initialize
 *  @param entropy IN -- entropy used to seed the PRNG
 *  @param entropyLen IN -- entropy length in bytes
 *  @param personalization IN -- personalization string used to seed the PRNG
 *  (may be null)
 *  @param plen IN -- personalization length in bytes
 *
 */
int tc_ctr_prng_init(TCCtrPrng_t *const ctx,
                     uint8_t const *const entropy,
                     unsigned int entropyLen,
                     uint8_t const *const personalization,
                     unsigned int pLen);

/**
 *  @brief CTR-PRNG reseed procedure
 *  Mixes entropy and additional_input into the prng context
 *  @return returns  TC_CRYPTO_SUCCESS (1)
 *  returns TC_CRYPTO_FAIL (0) if:
 *          ctx == NULL,
 *          entropy == NULL,
 *          entropylen < (TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE)
 *  @note It is better to reseed an existing prng context rather than
 *        re-initialise, so that any existing entropy in the context is
 *        presereved.  This offers some protection against undetected failures
 *        of the entropy source.
 *  @note Assumes tc_ctr_prng_init has been called for ctx
 *  @param ctx IN/OUT -- the PRNG state
 *  @param entropy IN -- entropy to mix into the prng
 *  @param entropylen IN -- length of entropy in bytes
 *  @param additional_input IN -- additional input to the prng (may be null)
 *  @param additionallen IN -- additional input length in bytes
 */
int tc_ctr_prng_reseed(TCCtrPrng_t *const ctx,
                       uint8_t const *const entropy,
                       unsigned int entropyLen,
                       uint8_t const *const additional_input,
                       unsigned int additionallen);

/**
 *  @brief CTR-PRNG generate procedure
 *  Generates outlen pseudo-random bytes into out buffer, updates prng
 *  @return returns TC_CRYPTO_SUCCESS (1)
 *          returns TC_CTR_PRNG_RESEED_REQ (-1) if a reseed is needed
 *             returns TC_CRYPTO_FAIL (0) if:
 *                ctx == NULL,
 *                out == NULL,
 *                outlen >= 2^16
 *  @note Assumes tc_ctr_prng_init has been called for ctx
 *  @param ctx IN/OUT -- the PRNG context
 *  @param additional_input IN -- additional input to the prng (may be null)
 *  @param additionallen IN -- additional input length in bytes
 *  @param out IN/OUT -- buffer to receive output
 *  @param outlen IN -- size of out buffer in bytes
 */
int tc_ctr_prng_generate(TCCtrPrng_t *const ctx,
                         uint8_t const *const additional_input,
                         unsigned int additionallen,
                         uint8_t *const out,
                         unsigned int outlen);

/**
 *  @brief CTR-PRNG uninstantiate procedure
 *  Zeroes the internal state of the supplied prng context
 *  @return none
 *  @param ctx IN/OUT -- the PRNG context
 */
void tc_ctr_prng_uninstantiate(TCCtrPrng_t *const ctx);

#ifdef __cplusplus
}
#endif

#endif /* __BLE_MESH_TC_CTR_PRNG_H__ */
