/* 
   babeld-lor
    Copyright (C) 2017  Rodrigo Garcia

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

#include "lorauth.h"
#include "decrypt.h"
#include "babeld.h"

/**
 * \brief
 * Opens LORAUTH_TOKENS_DIR and LORAUTH_TOKENS_FILE to get the ciphered token
 * to be sent in the next update, according to `seqno' and `id' passed as 
 * arguments, if the token is found it is stored on `dest', if it is not
 * , does not change `dest'
 * \return
 * Returns 0 if found, -1 if not found.
 *
 * \docs
 * See: docs-lorauth/babel-integration.es.md
 */
int 
lorauth_token(unsigned char *dest, unsigned char id[8], 
	      unsigned short seqno)
{
    char *file_abs_path = calloc(1, 150);
    strcat(file_abs_path, LORAUTH_TOKENS_DIR);
    strcat(file_abs_path, "/");
    strcat(file_abs_path, LORAUTH_TOKENS_FILE);
    
    FILE *fp = fopen(file_abs_path, "rt");
    int c = 0;
    if (fp)
      {
	// include here LORAUTH_CIPHER_LEN + 1
	char *line = (char *)malloc(513);
	size_t len = 0;
      
	unsigned int index = lorauth_token_index(id, seqno);
	// getting respective index token.
	while (getline(&line, &len, fp) != -1)
	  {
	    if(c == index)
	      {
		strcpy((char *)dest, line);
		break;
	      }
	    c++;
	  }

	if(fclose(fp)){
	  printf(" Error fclose ");
	  return -1;
	}
	
	free(file_abs_path);
	free(line);

	if(c!=index)
	  {
	    printf(" c %d index: %d\n", c, index);
	    return -1;
	  }

	return 0;
      }
    else{
      fprintf(stderr, "Couldn't read %s file\n", file_abs_path);
      free(file_abs_path);
      if(fclose(fp)){
	printf(" Error fclose ");
      }
      return -1;
    }
}

/**
 \brief
 Returns the index using:
  (((seqno*7)+1) xor (((router_id>>32)>>3) & (router_id&0xFFFFFFFF))) % 100
 */
int
lorauth_token_index(unsigned char id[8], unsigned short seqno)
{
    unsigned int index = 0;

    unsigned char idh[] = {id[0],id[1],id[2],id[3]};
    unsigned char idl[] = {id[4],id[5],id[6],id[7]};

    unsigned long long H=0;
    unsigned long long L=0;

    int i=0;
    for(i=0; i<4; i++){
      H |= (unsigned long long) (idh[i]<<8*(3-i));
      L |= (unsigned long long) (idl[i]<<8*(3-i));
    }
    index = seqno*7;
    index += 1;
    index ^= (H>>3) & L;
    index %= 100;

    return index;
}

/**
 \brief
 * Checks if the cipher text is a valid authentication token
 * and it is following the lorauth specs (see doc-lorauth/)
 \returns 
 * 0 if it is a valid ciphertext
 * -1 if the ciphertext is invalid or can't be decrypted using
 * the given public key.
 */
int 
check_lorauth_token(unsigned char id[8], 
		    unsigned char prefix[16],
		    unsigned short seqno,
		    unsigned short clen,
		    unsigned char *cipher)
{
    if(clen != strlen((char *)cipher))
      {
	printf("\tcipher size (%d) different than clen %d",
	       strlen((char*)cipher), clen);
	return -1;
      }

    /* if(clen < 513){ */
    /*   /\* Seems that rsa_decrypt expects a message which ends in \n (10)*\/ */
    /*   printf("\tcorrecting\t"); */
    /*   strcat((char*)cipher, "\n"); */
    /* } */
    /* printf("%s\n",cipher); */
    /* int i; */
    /* for(i=0; i<clen; i++) */
    /*   printf("%d ",(unsigned char)cipher[i]); */
    /* printf("-- %d \n",clen); */

    //printf("rsA_result decrypted: %d\t",strlen((char *)rsa_result));
    int rc = rsa_decrypt(&rsa_context, &rsa_entropy, &rsa_ctr_drbg,
			 (char *)cipher, (unsigned char *)&rsa_result);

    if(rc!=0)
      {
	printf("\tFailed to decrypt ciphertext\n");
	return -1;
      }

    /* looking for the given prefix in the decrypted token
       for now it looks a contiguous space, but the prefix may be
       contained in an arbitrary order in the ciphertext later on.
    */
    // ipv4
    /*TODO: Add automatic check, by parsing by "." in decrypted string.
    the prefix is received like  (0 0 0 0 0 0 0 0 0 0 255 255 80 0 1 0)
    */
    if(
       prefix[12] != (rsa_result[0]-48)*10 + (rsa_result[1]-48) || 
       prefix[13] != rsa_result[3]-48 ||
       prefix[14] != rsa_result[5]-48 ||
       prefix[15] != rsa_result[7]-48)
      {
	printf("\tprefix not found in decrypted token (contiguous check)\n");
	printf("\trsa_result: %s \nprefix: ", rsa_result);
	int i;
	for(i=0; i<16; i++)
	  printf("%d ", (unsigned char)prefix[i]);
	
	return -1;
      }
    //TODO: implement ipv6 check
    //...
    unsigned int index = lorauth_token_index(id, seqno);
    size_t dc = strlen((char *)rsa_result);
    
    // only the last two (ascii) chars for now
    int ircv = (rsa_result[dc-2]-48)*10 + (rsa_result[dc-1]-48);
    if( ircv != index )
      {
	printf("\tnot correct token index received (%d), expected %d\n", 
	       ircv, index);
	printf("\trsa_result: %s \n", rsa_result);
	return -1;
      }
    return 0;
}

/* Some utils */
const char* reduced_lorauth_token(const unsigned char *token){
     static char red[8];
     if(strlen((char*)token)<4)
      {
	strcpy(red, "none.");
	return red;
      }
    else
      {
	strncpy(red, (char*)token, 3);
	strcpy(red+3, "..");
	strncpy(red+5, (char*)token+(LORAUTH_CIPHER_LEN-2), 2);
	return red;
      }
}

void clean_cipher(unsigned char *buffered_cipher){
    memset(buffered_cipher, 0, 514);
}
