#define _GNU_SOURCE
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <time.h>
#include <sys/stat.h>
#if defined(__APPLE__) || defined(__OpenBSD__)
#include <libgen.h>
#else
#include <stdio_ext.h>
#endif

#include "common.h"

#define WKPSIZE	2622

#define WKPESSID1	0x4c0
#define WKPESSID2	0x520
#define WKPESSID_LEN	0x540

#define WKPMAC_AP	0x514
#define WKPNONCE_AP	0x54c

#define WKPMAC_STA	0x51a
#define WKPNONCE_STA	0x56c

#define WKPKEYVER	0x544
#define WKPEAPOL_SIZE	0x548
#define WKPEAPOLDATA	0x58c
#define WKPKEYMIC	0x68c

/*===========================================================================*/
/* globale Variablen */

static long int hcxcount = 0;
static long int wkpcount = 0;

static char *hcxoutname = NULL;
static char *essidoutname = NULL;
/*===========================================================================*/
static uint8_t geteapkey(uint8_t *eapdata)
{
eap_t *eap;
uint16_t keyinfo;
int eapkey = 0;

eap = (eap_t*)(uint8_t*)(eapdata);
keyinfo = (((eap->keyinfo & 0xff) << 8) | (eap->keyinfo >> 8));
if (keyinfo & WPA_KEY_INFO_ACK)
	{
	if(keyinfo & WPA_KEY_INFO_INSTALL)
		{
		/* handshake 3 */
		eapkey = 3;
		}
	else
		{
		/* handshake 1 */
		eapkey = 1;
		}
	}
else
	{
	if(keyinfo & WPA_KEY_INFO_SECURE)
		{
		/* handshake 4 */
		eapkey = 4;
		}
	else
		{
		/* handshake 2 */
		eapkey = 2;
		}
	}
return eapkey;
}
/*===========================================================================*/
static bool writeessid(uint8_t *essid, uint8_t essidlen)
{
FILE *fhessid;

char essidstring[34] = { 0 };

memcpy(&essidstring, essid, essidlen);
if(essidoutname != NULL)
	{
	if((fhessid = fopen(essidoutname, "a")) == NULL)
		{
		fprintf(stderr, "error opening essid file %s\n", essidoutname);
		return false;
		}
	fprintf(fhessid, "%s\n", essidstring);
	fclose(fhessid);
	}

return true;
}
/*===========================================================================*/
static bool writehccapx(uint8_t *wkpdata)
{
FILE *fhhcx;
hcx_t hcxrecord;
int mp;
uint8_t wkpessidlen;
uint8_t wkpeapolsize;

wkpessidlen = wkpdata[WKPESSID_LEN];
wkpeapolsize = wkpdata[WKPEAPOL_SIZE];
mp = geteapkey(&wkpdata[WKPEAPOLDATA]);

if(mp == 2)
	hcxrecord.message_pair = MESSAGE_PAIR_M12E2NR;
if(mp == 3)
	hcxrecord.message_pair = MESSAGE_PAIR_M32E3NR;
if(mp == 4)
	hcxrecord.message_pair = MESSAGE_PAIR_M14E4NR;

memset(&hcxrecord, 0, HCX_SIZE);
hcxrecord.signature = HCCAPX_SIGNATURE;
hcxrecord.version = HCCAPX_VERSION;
hcxrecord.message_pair = mp;
hcxrecord.essid_len = wkpessidlen;
memcpy(hcxrecord.essid, &wkpdata[WKPESSID2], wkpessidlen);

hcxrecord.keyver = wkpdata[WKPKEYVER];
memcpy(hcxrecord.mac_ap.addr, &wkpdata[WKPMAC_AP], 6);
memcpy(hcxrecord.nonce_ap, &wkpdata[WKPNONCE_AP], 32);
memcpy(hcxrecord.mac_sta.addr, &wkpdata[WKPMAC_STA], 6);
memcpy(hcxrecord.nonce_sta, &wkpdata[WKPNONCE_STA], 32);
hcxrecord.eapol_len = wkpeapolsize;
memcpy(hcxrecord.eapol, &wkpdata[WKPEAPOLDATA], 256);
memcpy(hcxrecord.keymic,  &wkpdata[WKPKEYMIC], 16);
memset(&hcxrecord.eapol[0x51], 0, 16);

if(hcxoutname != NULL)
	{
	if((fhhcx = fopen(hcxoutname, "ab")) == NULL)
		{
		fprintf(stderr, "error opening essid file %s\n", hcxoutname);
		return false;
		}
	fwrite(&hcxrecord, 1 * HCX_SIZE, 1, fhhcx);
	fclose(fhhcx);
	}

hcxcount++;
return true;
}
/*===========================================================================*/
static bool processdata(char *wkpiname)
{
struct stat statinfo;
int wkpsize;
wkpcount = 0;
uint8_t wkpessidlen = 0;

FILE *fhwkp = NULL;

const char *wkpmagic = "CPWE";

uint8_t wkpdata[WKPSIZE];

if(wkpiname == NULL)
	return false;

if(stat(wkpiname, &statinfo) != 0)
	{
	fprintf(stderr, "can't stat %s\n", wkpiname);
	return false;
	}

if((statinfo.st_size % WKPSIZE) != 0)
	{
	fprintf(stderr, "file corrupt\n");
	return false;
	}

if((fhwkp = fopen(wkpiname, "rb")) == NULL)
	{
	fprintf(stderr, "error opening file %s\n", wkpiname);
	return false;
	}

wkpsize = fread(wkpdata, 1, WKPSIZE, fhwkp);
if(wkpsize != WKPSIZE)
	{
	fprintf(stderr, "error reading file %s\n", wkpiname);
	fclose (fhwkp);
	return false;
	}

if(memcmp(wkpmagic, &wkpdata, 4) != 0)
	{
	fprintf(stderr, "wrong magic number %s\n", wkpiname);
	fclose(fhwkp);
	return false;
	}

if(wkpsize != WKPSIZE)
	{
	fprintf(stderr, "wrong filesize %s\n", wkpiname);
	fclose(fhwkp);
	return false;
	}

if(memcmp(&wkpdata[WKPESSID1], &wkpdata[WKPESSID1], 32) != 0)
	{
	fprintf(stderr, "error processing ESSID %s\n", wkpiname);
	fclose(fhwkp);
	return false;
	}

wkpessidlen = wkpdata[WKPESSID_LEN];

if((wkpessidlen == 0) || (wkpessidlen > 32))
	{
	fprintf(stderr, "wrong ESSID len %s\n", wkpiname);
	fclose(fhwkp);
	return false;
	}

fclose(fhwkp);
wkpcount++;

writehccapx(wkpdata);

if(essidoutname != NULL)
	writeessid(&wkpdata[WKPESSID2], wkpessidlen);

return true;
}
/*===========================================================================*/
__attribute__ ((noreturn))
static void usage(char *eigenname)
{
printf("%s %s (C) %s ZeroBeat\n"
	"usage: %s <options> [input.wkp] [input.wkp] ...\n"
	"       %s <options> *.wkp\n"
	"\n"
	"options:\n"
	"-o <file> : output hccapx file\n"
	"-e <file> : output essidlist\n"
	"\n", eigenname, VERSION_TAG, VERSION_YEAR, eigenname, eigenname);
exit(EXIT_FAILURE);
}
/*===========================================================================*/
int main(int argc, char *argv[])
{
int index;
int auswahl;

setbuf(stdout, NULL);
while ((auswahl = getopt(argc, argv, "o:e:hv")) != -1)
	{
	switch (auswahl)
		{
		case 'o':
		hcxoutname = optarg;
		break;

		case 'e':
		essidoutname = optarg;
		break;

		default:
		usage(basename(argv[0]));
		}
	}

for (index = optind; index < argc; index++)
	{
	if(hcxoutname != NULL)
		{
		if(processdata(argv[index]) == false)
			{
			fprintf(stderr, "error processing records from %s\n", argv[index]);
			exit(EXIT_FAILURE);

			}
		printf("%ld record(s) read from %s\n", wkpcount, argv[index]);
		}
	}
if(hcxcount > 0)
	printf("%ld record(s) written to %s\n", hcxcount, hcxoutname);

return EXIT_SUCCESS;
}
