/*
 * vfpe.h
 *
 *	VFPE = Visa Format-Preserving Encryption
 *	Principles: Encrypt data in a given format into another data in the same format.
 *				Encrypt by xor-ing the data with a block ciphered by a counter
 *				and a block cipher (TDES or AES).
 *
 *  vfpe.c is adapted, from a vfpe.c (which used NN numbers), for the
 *  wolfSSL lib and its integer.h's mp_int datas.
 *
 *  Two structures created to unite variables:
 *      - Alphabet = { Charset, Radix }
 *      - Ciph_CtrMode = { CiphType, BlockSize
 *                         Key, KeyLength,
 *                         CounterBlock }
 *
 *  Created on: Jun 22, 2016
 *      Author: Antoine CHEN
 */

#ifndef VFPE_H_
#define VFPE_H_

#include <wolfssl/wolfcrypt/integer.h>
#include <wolfssl/wolfcrypt/error-crypt.h>
#include <wolfssl/wolfcrypt/des3.h>
#include <wolfssl/wolfcrypt/aes.h>

/*******************************************************************************
 * Added by Rajani(ID TECH)
 ******************************************************************************/
#include <wolfssl/wolfcrypt/calendar.h>

/******************************************************************************/

/* Originally included in vfpe.c */
#include <math.h>
#include <ctype.h>


#define TDES     1
#define AES      2

#define VFPE_ENC 1
#define VFPE_DEC 0

/* defining error value not supported by wolfSSL's error-crypt.h */
#define MP_SET_INT_E -122 // error while using mp_set_int from wolfSSL integer.h
#define MP_DIV_E     -123 // error while using mp_div   from wolfSSL integer.h
#define FPCM_CIPH    -127 // not able to cipher the counter
#define FPCM_KSBLK_E -128 // not able to generate a keyStreamBlock
#define FPCM_INCR_E  -129 // not able to increment the counter T more
#define FPCM_OKAY    0    // good termination of FPCM functions

/*******************************************************************************
 * Modified by Rajani(ID TECH)
 * changed type from const unsigned int to word32
 ******************************************************************************/
extern word32 Charset1[10]; // Radix is 10
extern word32 Charset2[45]; // Radix is 45
extern word32 Charset3[47]; // Radix is 47

/******************************************************************************/
/* --- STRUTURES --- */

/* | Alphabet structure |
 *  word32 *charset = set of characters.
 *                    Must be sorted in ascending order, according to their ASCII code.
 *  radix = number of different characters in the alphabet.
 *			There is a bijection between the integer set {0,...,radix-1} and alphabet.
 */
typedef struct Alphabet {
    word32 * charset;
    word32   radix;
} Alphabet;

/* | Ciph_CtrMode structure |
 * int ciphType = type of block cipher = TDES (1) or AES (2)
 *
 * word32 blockSize = size in bits of the ciphered block
 *
 * byte *key = encryption key for the block cipher
 *
 * word32 keyLength = size in bits of the key
 *
 * byte *counterBlock = counter of blockSize bits.
 *      counterBlock is used as the data to encrypt, in the FPE Counter Mode.
 *      It is made up of :
 *      - a counter called T, of blockSize-7 bits. Initialized with
 *          the Device ID or portions of. Increases at each round
 *          (1 round = encrypt 1 block of blockSize bits)
 *      - an internal counter called S, of 7 bits. Used for generating
 *          the keyStreamBlock.
 *      counterBlock[0] = 7-bit counter S + MSB of T.
 *      counterBlock is in big endian format.
 */
typedef struct Ciph_CtrMode {
    int    ciphType;
    word32 blockSize;
    byte * key, * counterBlock;
    word32 keyLength;
} Ciph_CtrMode;


/* --- MACROS --- */

/* assuming (byte*)byteArray (len = 4) is in big endian format : byteArray[0] is MSB */
#define BYTESTR4_TO_WORD32(byteArray) \
    (((byteArray)[0] << 24) | ((byteArray)[1] << 16) | ((byteArray)[2] << 8 ) \
    | (byteArray)[3])

/* map the character to the index in the related alphabet */
#define MAP_IX(chr, alphabet) (CharsetPosition((chr), (alphabet))


/* --- PROTOTYPES --- */

void
Alphabet_Init(Alphabet * alphabet, word32 * charset, word32 radix);
void
Ciph_Init(Ciph_CtrMode * ciph, int ciphType, word32 blockSize,
  byte * key, word32 keyLength, byte * counterBlock);

void
Ciph_SetCtrblk(Ciph_CtrMode * ciph, byte * counterBlock);

word32
unsigned_bin_to_word32(const byte * byteStr, word32 szBin);

int
cipher_CIPH(const Ciph_CtrMode ciph,
  const byte *                 inputbuf,
  byte *                       outputbuf);

int
invalid(const Ciph_CtrMode ciph,
  const Alphabet           alphabet,
  word32                   kBaseRadixDigits,
  const word32 *           Plaintext,
  word32                   PlaintextLength);

int
increment(byte * CounterBlock,
  word32         CounterLength);

unsigned int
CharsetPosition(word32 PlaintextChr,
  const Alphabet       alphabet);

int
CreateOneBlockOfKeyStream(const Ciph_CtrMode ciph,
  word32                                     radix,
  word32                                     kBaseRadixDigits,
  word32 *                                   keyStreamBlock);

int
FPCM_Encrypt_Decrypt(int EnDeType,
  Ciph_CtrMode           ciph,
  const Alphabet         alphabet,
  word32                 kBaseRadixDigits,
  const word32 *         Plaintext,
  word32                 PlaintextLength,
  word32 *               Output);


/* Other PROTO - original lines from vfpe.h */

typedef void (* TDES_ENC )(unsigned char *, unsigned char *, unsigned char *, unsigned char, unsigned char *,
  unsigned char);
typedef unsigned char (* AES_ENC )(unsigned char *, unsigned int, unsigned char *);
typedef void (* RAND )(unsigned char *, unsigned int);

/*******************************************************************************
 * Modified by Rajani(ID TECH)
 ******************************************************************************/
// typedef unsigned int (* RTC )(void);
typedef unsigned int (* RTC_FNC)(void);
/******************************************************************************/
extern TDES_ENC TDESENC_CB;
extern AES_ENC AES_ENC_CB;
extern RAND RAND_CB;

/*******************************************************************************
 * Modified by Rajani(ID TECH)
 ******************************************************************************/
// extern RTC RTC_CB;
extern RTC_FNC RTC_CB;
/******************************************************************************/

// VFPE will replace original track data, LRC of original track data is still present
// Track length might be changed if there is no expiration date or check digit is invalid
// Counter block is 8 bytes for TDES and 16 bytes for AES
// Use KSN for input
// TDES
// memset(CounterBlock, 0, 16);
// memcpy(CounterBlock, ksn+2, 8);
// CounterBlock[0] &= 0x01;
// AES
// memset(CounterBlock, 0, 16);
// memcpy(CounterBlock+8, ksn+2, 8);
// CounterBlock[8] &= 0x01;

/* Original prototypes (input arguments)
 * unsigned char VFPE_Track1(unsigned char encDec, unsigned char* track, unsigned char *len, unsigned char encType, unsigned char* key, unsigned char keylen, unsigned char *CounterBlock);
 * unsigned char VFPE_Track2(unsigned char encDec, unsigned char* track, unsigned char *len, unsigned char encType, unsigned char* key, unsigned char keylen, unsigned char *CounterBlock);
 */
unsigned char
VFPE_Track1(unsigned char encDec, unsigned char * track, unsigned char * len, Ciph_CtrMode ciph);
unsigned char
VFPE_Track2(unsigned char encDec, unsigned char * track, unsigned char * len, Ciph_CtrMode ciph);

// Make sure track data buffer has at least 128 bytes free after because encryption blocks will be added to it
// Track length will be changed to include mask data and encryption data
// P2PE will replace original track data with masked data and LRC is destroyed
unsigned char
P2PE_Track1(unsigned char * track, unsigned int * len, unsigned char * key);
// After mask data is 3 bytes for length of PAN, name, and discretionary data blocks
// After the blocks length is the encryption blocks
unsigned char
P2PE_Track2(unsigned char * track, unsigned int * len, unsigned char * key);
// After mask data is 2 bytes for length of PAN and discretionary data blocks
// After the blocks length is the encryption blocks

/* End of Other PROTO - original lines from vfpe.h */

#endif /* VFPE_H_ */
