/*
 * Argon2 source code package
 *
 * Written by Daniel Dinu and Dmitry Khovratovich, 2015
 *
 * This work is licensed under a Creative Commons CC0 1.0 License/Waiver.
 *
 * You should have received a copy of the CC0 Public Domain Dedication along
 * with
 * this software. If not, see
 * <http://creativecommons.org/publicdomain/zero/1.0/>.
 */

#ifndef argon2_core_H
#define argon2_core_H

#include <string.h>

#include "argon2.h"

/*************************Argon2 internal
 * constants**************************************************/

enum argon2_ctx_constants {
    /* Version of the algorithm */
    ARGON2_VERSION_NUMBER = 0x13,

    /* Memory block size in bytes */
    ARGON2_BLOCK_SIZE      = 1024,
    ARGON2_QWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 8,
    ARGON2_OWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 16,
    ARGON2_HWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 32,

    /* Number of pseudo-random values generated by one call to Blake in Argon2i
       to
       generate reference block positions */
    ARGON2_ADDRESSES_IN_BLOCK = 128,

    /* Pre-hashing digest length and its extension*/
    ARGON2_PREHASH_DIGEST_LENGTH = 64,
    ARGON2_PREHASH_SEED_LENGTH   = 72
};

/*************************Argon2 internal data
 * types**************************************************/

/*
 * Structure for the (1KB) memory block implemented as 128 64-bit words.
 * Memory blocks can be copied, XORed. Internal words can be accessed by [] (no
 * bounds checking).
 */
typedef struct block_ {
    uint64_t v[ARGON2_QWORDS_IN_BLOCK];
} block;

typedef struct block_region_ {
    void * base;
    block *memory;
    size_t size;
} block_region;

/*****************Functions that work with the block******************/

/* Initialize each byte of the block with @in */
static inline void
init_block_value(block *b, uint8_t in)
{
    memset(b->v, in, sizeof(b->v));
}

/* Copy block @src to block @dst */
static inline void
copy_block(block *dst, const block *src)
{
    memcpy(dst->v, src->v, sizeof(uint64_t) * ARGON2_QWORDS_IN_BLOCK);
}

/* XOR @src onto @dst bytewise */
static inline void
xor_block(block *dst, const block *src)
{
    int i;
    for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) {
        dst->v[i] ^= src->v[i];
    }
}

/*
 * Argon2 instance: memory pointer, number of passes, amount of memory, type,
 * and derived values.
 * Used to evaluate the number and location of blocks to construct in each
 * thread
 */
typedef struct Argon2_instance_t {
    block_region *region;        /* Memory region pointer */
    uint64_t     *pseudo_rands;
    uint32_t      passes;        /* Number of passes */
    uint32_t      memory_blocks; /* Number of blocks in memory */
    uint32_t      segment_length;
    uint32_t      lane_length;
    uint32_t      lanes;
    uint32_t      threads;
    argon2_type   type;
    int           print_internals; /* whether to print the memory blocks */
} argon2_instance_t;

/*
 * Argon2 position: where we construct the block right now. Used to distribute
 * work between threads.
 */
typedef struct Argon2_position_t {
    uint32_t pass;
    uint32_t lane;
    uint8_t  slice;
    uint32_t index;
} argon2_position_t;

/*Struct that holds the inputs for thread handling FillSegment*/
typedef struct Argon2_thread_data {
    argon2_instance_t *instance_ptr;
    argon2_position_t  pos;
} argon2_thread_data;

/*************************Argon2 core
 * functions**************************************************/

/*
 * Computes absolute position of reference block in the lane following a skewed
 * distribution and using a pseudo-random value as input
 * @param instance Pointer to the current instance
 * @param position Pointer to the current position
 * @param pseudo_rand 32-bit pseudo-random value used to determine the position
 * @param same_lane Indicates if the block will be taken from the current lane.
 * If so we can reference the current segment
 * @pre All pointers must be valid
 */
uint32_t index_alpha(const argon2_instance_t *instance,
                     const argon2_position_t *position, uint32_t pseudo_rand,
                     int same_lane);

/*
 * Function that validates all inputs against predefined restrictions and return
 * an error code
 * @param context Pointer to current Argon2 context
 * @return ARGON2_OK if everything is all right, otherwise one of error codes
 * (all defined in <argon2.h>
 */
int validate_inputs(const argon2_context *context);

/*
 * Hashes all the inputs into @a blockhash[PREHASH_DIGEST_LENGTH], clears
 * password and secret if needed
 * @param  context  Pointer to the Argon2 internal structure containing memory
 * pointer, and parameters for time and space requirements.
 * @param  blockhash Buffer for pre-hashing digest
 * @param  type Argon2 type
 * @pre    @a blockhash must have at least @a PREHASH_DIGEST_LENGTH bytes
 * allocated
 */
void initial_hash(uint8_t *blockhash, argon2_context *context,
                  argon2_type type);

/*
 * Function creates first 2 blocks per lane
 * @param instance Pointer to the current instance
 * @param blockhash Pointer to the pre-hashing digest
 * @pre blockhash must point to @a PREHASH_SEED_LENGTH allocated values
 */
void fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance);

/*
 * Function allocates memory, hashes the inputs with Blake,  and creates first
 * two blocks. Returns the pointer to the main memory with 2 blocks per lane
 * initialized
 * @param  context  Pointer to the Argon2 internal structure containing memory
 * pointer, and parameters for time and space requirements.
 * @param  instance Current Argon2 instance
 * @return Zero if successful, -1 if memory failed to allocate. @context->state
 * will be modified if successful.
 */
int initialize(argon2_instance_t *instance, argon2_context *context);

/*
 * Deallocates memory. Used on error path.
 */
void free_instance(argon2_instance_t *instance, int flags);

/*
 * XORing the last block of each lane, hashing it, making the tag. Deallocates
 * the memory.
 * @param context Pointer to current Argon2 context (use only the out parameters
 * from it)
 * @param instance Pointer to current instance of Argon2
 * @pre instance->state must point to necessary amount of memory
 * @pre context->out must point to outlen bytes of memory
 * @pre if context->free_cbk is not NULL, it should point to a function that
 * deallocates memory
 */
void finalize(const argon2_context *context, argon2_instance_t *instance);

/*
 * Function that fills the segment using previous segments also from other
 * threads
 * @param instance Pointer to the current instance
 * @param position Current position
 * @pre all block pointers must be valid
 */
typedef void (*fill_segment_fn)(const argon2_instance_t *instance,
                                argon2_position_t        position);
int argon2_pick_best_implementation(void);
void fill_segment_avx2(const argon2_instance_t *instance,
                       argon2_position_t        position);
void fill_segment_ssse3(const argon2_instance_t *instance,
                        argon2_position_t        position);
void fill_segment_ref(const argon2_instance_t *instance,
                      argon2_position_t        position);

/*
 * Function that fills the entire memory t_cost times based on the first two
 * blocks in each lane
 * @param instance Pointer to the current instance
 * @return Zero if successful, -1 if memory failed to allocate
 */
void fill_memory_blocks(argon2_instance_t *instance);

#endif
