/*
  Copyright 2009 (Y) Yellowbank
  https://www.yellowbank.com/
  Ronald Peterson

  https://www.yellowbank.com/

  This file is part of y_clib.

  y_clib is free software; you can redistribute it and/or modify it
  under the terms of the GNU Affero GPL version 3.0.  These license
  terms can be found in the included file agpl-3.0.txt.
*/

#ifndef _Y_CLIB_
#define _Y_CLIB_

// #include <stdlib.h>
// #include <stdio.h>
#include <sys/types.h>
// #include <fcntl.h>
// #include <unistd.h>

#include <gmp.h>
#include <mhash.h>

// Undefine conflicting macros used by autoconf that
// shouldn't be in library headers anyway
#undef PACKAGE_VERSION
#undef PACKAGE_TARNAME
#undef PACKAGE_STRING
#undef PACKAGE_NAME
#undef PACKAGE_BUGREPORT

#define MAX_ERR_LEN 256
#define MAX_HASH_FILE_SIZE 2000000000

#define Y_OK 0
#define Y_OUT_OF_RANGE 1

#define Y_BAD_RANDOM -1
#define Y_NO_RANDOM 0
#define Y_GOOD_RANDOM 1

#define Y_HASH_ALLOC_FAILURE -1
#define Y_HASH_INIT_FAILURE 0
#define Y_HASH_OK 1

#define Y_LABEL_TOO_LONG 1
#define Y_MESSAGE_TOO_LONG 2
#define Y_HASH_ERROR 3

// in octets
#define Y_RAND_SEED_SIZE 8
#define Y_RAND_STATE_SIZE 128

typedef unsigned char y_oct_bin_t;
typedef unsigned char *y_oct_bin;
typedef struct {
    // This structure intentionally matches PostgreSQL's
    // expectation of TOAST'able data (i.e. varlena).
    u_int32_t length;
    y_oct_bin buf;
} y_octstr[1];

// blum blum shub: to store current 'x'
typedef struct {
    mpz_t    x;
    mpz_t    p;
    mpz_t    q;
    mpz_t    M;
} y_bbs_state[1];

typedef struct {
    mpz_t    x;
    mpz_t    p;
    mpz_t    q;
    mpz_t    M;
} y_bbs_state_t;

#define Y_OS_LEN(os) (os->length)
#define Y_OS_STR(os) (os->buf)

void y_octstr_init( y_octstr );

// zero all bytes and free memory
void y_octstr_clear( y_octstr );

// args: os (to modify), byte, number of bytes
void y_octstr_set( y_octstr, int, uint );

// args: os (to modify), byte, start (zero indexed), number of bytes
void y_octstr_set_count( y_octstr, int, uint, uint );

// args: os (to modify), number of bits to set. e.g.
// setting 6 bits -> 11111100
// setting 9 bits -> 11111111 10000000
void y_octstr_set_mask( y_octstr, uint );

// set all bits in 'string' where 'mask' bits are 1 to zero.
// args: os, mask
void y_octstr_mask_out( y_octstr, y_octstr );

// args: os (return), replacement string, insert position (zero indexed)
void y_octstr_replace_substr( y_octstr, y_octstr, uint );

// set from null terminated hex string
void y_octstr_set_from_cp_hex( y_octstr, char * );

// set from null terminated hex string
char *y_cp_hex_realloc_from_octstr( char *, y_octstr );

// return TRUE or FALSE
bool y_are_octstr_equal( y_octstr left, y_octstr right );

void y_print_os_as_hex( y_octstr );

uint y_octets_required_by_bigint( mpz_t );

// Same as above, but calc len internally.  Must use above version
// to properly implement RSA PKCS standard.
void y_octstr_set_from_gmp_int( y_octstr, mpz_t );

// equivalent to PKCS 1v2.1 OS2IP function
void y_gmp_int_set_from_octstr( mpz_t, y_octstr );

// args: destination, source
void y_octstr_copy( y_octstr, y_octstr );

// args: dest (return), left, right
void y_octstr_concat( y_octstr, y_octstr, y_octstr );

// args: os, byte_count, random_source ("random" or "urandom")
// uint y_get_uint_dev_random( uint, uint, const char * );
// args: octet string (return), octets, random source
// int y_get_os_dev_random( y_octstr, uint, const char * );
// int y_get_os_dev_random( y_octstr, uint );
// args: returned data, octet count, device name
int y_get_os_dev_random( y_octstr, uint, char * );
int y_get_mpz_dev_random( mpz_t, uint, char * );

// args: hex, octets
char *y_realloc_random_hex( char *, uint );

// args: random_ctx, len
//
// initialize gmp_randstate_t by seeding with len octects from
// /dev/random
int y_gmp_random_init( gmp_randstate_t *, int );

// args: random_ctx, length, dst
// 
// Random number generation function to match requirements of Nettle
// library for function to pass to rsa keygen function.
//
// First initialize random state (random_ctx) using y_gmp_random_init.
// Fill dst with length random octets; dst must already be allocated.
void y_gmp_random_func( gmp_randstate_t *, unsigned, uint8_t * );


// initialize blum blum shub random state
void y_bbs_rand_init( y_bbs_state );
// args: state, seed length in octets, key size in bits
void y_bbs_rand_seed( y_bbs_state, int, int );
// args: state
void y_bbs_rand_clear( y_bbs_state );

// args: new state (return), old state, octets
// seed new state using random data from old state.
void y_bbs_spawn( y_bbs_state, y_bbs_state, unsigned );

// blum blum shub: generate random data
// args: random state, octets, destination
// fill destination with random octets
// function prototype designed to be compatible with nettle
void y_bbs_rand_octets( y_bbs_state, unsigned, uint8_t * );
// same as above, but return mpz_t value
void y_bbs_rand_mpz_octets( y_bbs_state, unsigned, mpz_t );

// args: random state, max, destination
// return random value in the range 0 to max-1, inclusive
void y_bbs_rand_range( y_bbs_state, mpz_t, mpz_t );

// args: rand (return), random_source, seed_len, max_bits
void y_gen_mpz_random( mpz_t, const char *, uint, uint );

// args: rand (return), random_source, seed_len, bits
void y_gen_mpz_random_of_bit_len( mpz_t, uint, uint );

// args: prime (return), random_source, seed_len, bits );
void y_generate_random_prime( mpz_t, uint, uint );
// args: key (return), random_source, seed_length, bits

// args: ret (return), left, right
void y_exclusive_or( y_octstr, y_octstr, y_octstr );

//_______________________________________________________________________
// args: hash (return),
//       hash type,
//       data,
//       data length,
//       hash length (return)
//_______________________________________________________________________
int
y_calc_mhash( y_octstr,
              const char *,
              char *,
              uint,
              uint * );


//______________________________________________________________________
//______________________________________________________________________
int
y_octstr_from_file( y_octstr filedat,
                    const char *filename,
                    char *errstr );


//______________________________________________________________________
// append 'depth' directories to 'basedir' using characters from the
// beginning of 'hash'.  E.g. - if 'basedir' is '/some/place', 'hash' is
// 'abc123', and 'depth' is 2, then the result would be
// '/some/place/a/b'.
//______________________________________________________________________
char
*y_mkdir_and_realloc_hashdir_name( char *hashdir,
                                   const char *basedir,
                                   const char *hash,
                                   uint depth,
                                   char *errstr );


//______________________________________________________________________
int
y_octstr_from_file( y_octstr filedat,
                    const char *filename,
                    char *errstr );

//______________________________________________________________________
// be sure to free hashFile and errstr
//______________________________________________________________________
char
*y_octstr_to_hashfile( y_octstr os,
                       const char *basedir,
                       const char *hashType,
                       uint depth,
                       char *errstr );

#endif // _Y_CLIB_

