#include <stdio.h>
#include <string.h>

#include "wpapcap2john.h"

bool Process(FILE *, const char *InFName);
bool GetNextPacket(FILE *in);
bool ProcessPacket();
void HandleBeacon();
void Handle4Way(bool bIsQOS);
void DumpKey(int idx, int one_three, bool bIsQOS);

uint32 start_t, start_u, cur_t, cur_u;
pcaprec_hdr_t pkt_hdr;
uint8 packet[65535];
static bool bROT;
WPA4way_t wpa[1000];
int nwpa=0;
char itoa64[65] = 	"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

int main(int argc, char **argv) {
	if (argc != 2) return !!fprintf(stderr, "Usage wpacpap2john cpap_filename\n");
	FILE *in = fopen(argv[1], "rb");
	if (in) {
		Process(in, argv[1]);
		fclose(in);
	} else 
		fprintf(stderr, "Error, file %s not found\n", argv[1]);
	return 0;
}

bool Process(FILE *in, const char *InFName) {
	pcap_hdr_t main_hdr;
	if (fread(&main_hdr, 1, sizeof(pcap_hdr_t), in) != sizeof(pcap_hdr_t)) {
		fprintf(stderr, "Error, could not read enough bytes to get a common 'main' pcap header\n");
		return false;
	}
	if (main_hdr.magic_number ==  0xa1b2c3d4) bROT = false;
	else if (main_hdr.magic_number ==  0xd4c3b2a1) bROT = true;
	else { fprintf(stderr, "Error, Invalid pcap magic number, (not a pcap file!)\n");  return false; }
	if(bROT) {
		main_hdr.magic_number = swap(main_hdr.magic_number);
		main_hdr.version_major = swap(main_hdr.version_major);
		main_hdr.version_minor = swap(main_hdr.version_minor);
		main_hdr.sigfigs = swap(main_hdr.sigfigs);
		main_hdr.snaplen = swap(main_hdr.snaplen);
		main_hdr.network = swap(main_hdr.network);
	}
	if (main_hdr.network != 105) {
		fprintf(stderr, "This program will only read 802.11 wireless traffic\n");
		return false;
	}

	while (GetNextPacket(in)) {
		if (!ProcessPacket())
			return true;
	}
	return true;
}

bool GetNextPacket(FILE *in) {
	if (fread(&pkt_hdr, 1, sizeof(pkt_hdr), in) != sizeof(pkt_hdr)) return false;
	if(bROT) {
		pkt_hdr.ts_sec = swap(pkt_hdr.ts_sec);
		pkt_hdr.ts_usec = swap(pkt_hdr.ts_usec);
		pkt_hdr.incl_len = swap(pkt_hdr.incl_len);
		pkt_hdr.orig_len = swap(pkt_hdr.orig_len);
	}
	if (!start_t) {
		start_t = pkt_hdr.ts_sec;
		start_u = pkt_hdr.ts_usec;
	}
	cur_t = pkt_hdr.ts_sec-start_t;
	if (start_u > pkt_hdr.ts_usec) {
		--cur_t;
		cur_u = 1000000-(start_u-pkt_hdr.ts_usec);
	} else
		cur_u = pkt_hdr.ts_usec-start_u;

	fread(packet, 1, pkt_hdr.incl_len, in);
	return true;
}

// Ok, this function is the main packet processor.  NOTE, when we are done 
// reading packets (i.e. we have done what we want), we return false, and
// the program will exit gracefully.  It is not an error, it is just an
// indication we have completed (or that the data we want is not here).
bool ProcessPacket() {
	// our data is in packet[] with pkt_hdr being the pcap packet header for this packet.
	ether_frame_hdr_t *pkt = (ether_frame_hdr_t*)packet;
	ether_frame_ctl_t *ctl = (ether_frame_ctl_t *)&pkt->frame_ctl;

	if (ctl->type == 0 && ctl->subtype == 8) { // beacon  Type 0 is management, subtype 8 is beacon
		HandleBeacon();
		return true;
	}
	// if not beacon, then only look data, looking for EAPOL 'type'
	if (ctl->type == 2) { // type 2 is data
		bool bQOS = (ctl->subtype & 8) != 0;
		if ( (ctl->toDS ^ ctl->fromDS) != 1)// eapol will ONLY be direct toDS or direct fromDS.
			return true;
		uint8 *p = packet;
		// Ok, find out if this is a EAPOL packet or not.

		p += sizeof(ether_frame_hdr_s);
		if (bQOS)
			p += 2;
		// p now points to the start of the LLC (logical link control) structure.
		// this is 8 bytes long, and the last 2 bytes are the 'type' field.  What
		// we are looking for is 802.11X authentication packets. These are 0x888e
		// in value.  We are running from an LE system, so should look for 0x8e88
		p += 6;
		if (*((uint16*)p) == 0x8e88)
			Handle4Way(bQOS);	// this packet was a eapol packet.
	}

	return true;
}

void to_bssid(char ssid[18], uint8 *p) {
	sprintf(ssid, "%02X:%02X:%02X:%02X:%02X:%02X",p[0],p[1],p[2],p[3],p[4],p[5]);
}

void HandleBeacon() {
	// addr1 should be broadcast
	// addr2 is source addr (should be same as BSSID
	// addr3 is BSSID (routers MAC)
	ether_frame_hdr_t *pkt = (ether_frame_hdr_t*)packet;
	int i;

	ether_beacon_data_t *pDat = (ether_beacon_data_t*)&packet[sizeof(ether_frame_hdr_t)];
	ether_beacon_tag_t *tag = pDat->tags;
	// ok, walk the tags
	uint8 *pFinal = &packet[pkt_hdr.incl_len];
	char ssid[36];
	memset(ssid, 0, sizeof(ssid));
	while (((uint8*)tag) < pFinal) {
		if (tag->tagtype == 0) { // essid
			memcpy(ssid, tag->tag, tag->taglen);
		}
		char *x = (char*)tag;
		x += tag->taglen + 2;
		tag = (ether_beacon_tag_t *)x;
	}
	char essid[18];
	to_bssid(essid, pkt->addr3);
	for (i = 0; i < nwpa; ++i) {
		if (!strcmp(essid, wpa[i].essid) && !strcmp(ssid, wpa[i].ssid))
			return;
	}
	if (nwpa==999) return;
	strcpy(wpa[nwpa].ssid, ssid);
	strcpy(wpa[nwpa].essid, essid);
	++nwpa;
}
void Handle4Way(bool bIsQOS) {
	ether_frame_hdr_t *pkt = (ether_frame_hdr_t*)packet;
	ether_frame_ctl_t *ctl = (ether_frame_ctl_t *)&pkt->frame_ctl;
	int i, ess=-1;
	uint8 orig_2[512];

	// ok, first thing, find the beacon.  If we can NOT find the beacon, then
	// do not proceed.  Also, if we find the becon, we may determine that
	// we already HAVE fully cracked this

	char essid[18];
	to_bssid(essid, pkt->addr3);
	for (i = 0; i < nwpa; ++i) {
		if (!strcmp(essid, wpa[i].essid)) {
			ess=i;
			break;
		}
	}
	if (ess==-1) return;
	if (wpa[ess].fully_cracked)
		return;  // no reason to go on.

	memcpy(orig_2, packet, pkt_hdr.orig_len);

	uint8 *p = (uint8*)&packet[sizeof(ether_frame_hdr_t)];
	// Ok, after pkt,  uint16 QOS control (should be 00 00)
	if (bIsQOS)
		p += 2;
	// we are now at Logical-Link Control. (8 bytes long).
	// LLC check not needed here any more.  We do it in the packet cracker section, b4
	// calling this function.  We just need to skip the 8 byte LLC.
	//if (memcmp(p, "\xaa\xaa\x3\0\0\0\x88\x8e", 8)) return; // not a 4way
	p += 8;
	// p now points to the 802.1X Authentication structure.
	ether_auto_802_1x_t *auth = (ether_auto_802_1x_t*)p;
	auth->length = swap(auth->length);
	*(uint16*)&(auth->key_info) = swap(*(uint16*)&(auth->key_info));
	auth->key_len  = swap(auth->key_len);
	auth->replay_cnt  = swap(auth->replay_cnt);
	auth->wpa_keydatlen  = swap(auth->wpa_keydatlen);

	int msg = 0;
	if (!auth->key_info.KeyACK) {
		// msg 2 or 4
		if (auth->key_info.Secure) {
			msg = 4;
			// is this useful?
			return;
		}
		else
			msg = 2;
	} else {
		if (auth->key_info.Install)
			msg = 3;
		else
			msg = 1;
	}

	// Ok, we look for a 1 followed immediately by a 2 which have exact same replay_cnt, we have 
	// a 'likely' key. Or we want a 2 followed by a 3 that are 1 replay count apart)  which means
	// we DO have a key.  The 3 is not returned unless the 2 (which came from the client), IS
	// valid. So, we get the anonce from either the 1 or the 3 packet.

	// for our first run, we output ALL valid keys found in the file. That way, I can validate that
	// any keys which were produced by aircrack-ng are 'valid' or not.  aircrack-ng WILL generate some
	// invalid keys.  Also, I want to flag "unknown" keys as just that, unk.  These are 1-2's which
	// do not have valid 3 4's.  They 'may' be valid, but may also be a client with the wrong password.

	if (msg == 1) {
		delete wpa[ess].packet1;
		wpa[ess].packet1 = new uint8 [pkt_hdr.orig_len];
		memcpy(wpa[ess].packet1, packet, pkt_hdr.orig_len);
		delete wpa[ess].packet2;  wpa[ess].packet2 = NULL;
		delete wpa[ess].orig_2;  wpa[ess].orig_2 = NULL;
		delete wpa[ess].packet3;  wpa[ess].packet3 = NULL;
		delete wpa[ess].packet4;  wpa[ess].packet4 = NULL;
	}
	if (msg == 2) {
		// see if we have a msg1 that 'matches'.
		delete wpa[ess].packet3;  wpa[ess].packet3 = NULL;
		delete wpa[ess].packet4;  wpa[ess].packet4 = NULL;
		wpa[ess].packet2 = new uint8 [pkt_hdr.orig_len];
		wpa[ess].orig_2 = new uint8 [pkt_hdr.orig_len];
		memcpy(wpa[ess].packet2, packet, pkt_hdr.orig_len);
		memcpy(wpa[ess].orig_2, orig_2, pkt_hdr.orig_len);
		wpa[ess].eapol_sz = pkt_hdr.orig_len-8-sizeof(ether_frame_hdr_t);
		if (bIsQOS) wpa[ess].eapol_sz -= 2;
		if (wpa[ess].packet1) {
			ether_auto_802_1x_t *auth2 = auth;
			p = (uint8*)wpa[ess].packet1;
			if (bIsQOS)
				p += 2;
			p += 8;
			p += sizeof(ether_frame_hdr_t);
			ether_auto_802_1x_t *auth1 = (ether_auto_802_1x_t*)p;
			if (auth1->replay_cnt == auth2->replay_cnt) {
				fprintf (stderr, "Key1/Key2 hit (hopful hit), for SSID:%s\n", wpa[ess].ssid);
				DumpKey(ess, 1, bIsQOS);
			}
			// we no longer want to know about this packet 1.
			delete wpa[ess].packet1;  wpa[ess].packet1 = NULL;
		}
	}
	if (msg == 3) {
		// see if we have a msg2 that 'matches',  which is 1 less than our replay count.
		delete wpa[ess].packet1;  wpa[ess].packet1 = NULL;
		delete wpa[ess].packet4;  wpa[ess].packet4 = NULL;
		wpa[ess].packet3 = new uint8 [pkt_hdr.orig_len];
		memcpy(wpa[ess].packet3, packet, pkt_hdr.orig_len);
		if (wpa[ess].packet2) {
			ether_auto_802_1x_t *auth3 = auth;
			p = (uint8*)wpa[ess].packet2;
			if (bIsQOS)
				p += 2;
			p += 8;
			p += sizeof(ether_frame_hdr_t);
			ether_auto_802_1x_t *auth2 = (ether_auto_802_1x_t*)p;
			if (auth2->replay_cnt+1 == auth3->replay_cnt) {
				fprintf (stderr, "Key2/Key3 hit (SURE hit), for SSID:%s\n", wpa[ess].ssid);
				DumpKey(ess, 3, bIsQOS);
			}
		}
		// clear this, so we do not hit the same 3 packet and output exact same 2/3 combo.
		delete wpa[ess].packet3;  wpa[ess].packet3 = NULL;
		delete wpa[ess].packet2;  wpa[ess].packet2 = NULL;
		delete wpa[ess].orig_2;  wpa[ess].orig_2 = NULL;
	}
	if (msg == 4) {
		delete wpa[ess].packet1;  wpa[ess].packet1 = NULL;
		delete wpa[ess].packet2;  wpa[ess].packet2 = NULL;
		delete wpa[ess].orig_2;  wpa[ess].orig_2 = NULL;
		delete wpa[ess].packet3;  wpa[ess].packet3 = NULL;
		delete wpa[ess].packet4;  wpa[ess].packet4 = NULL;
	}
}

// These 2 functions output data properly for JtR, in base-64 format. These
// were taken from hccap2john.c source, and modified for this project.
static void code_block(unsigned char *in, unsigned char b)
{
	putchar(itoa64[in[0] >> 2]);
	putchar(itoa64[((in[0] & 0x03) << 4) | (in[1] >> 4)]);
	if (b) {
		putchar(itoa64[((in[1] & 0x0f) << 2) | (in[2] >> 6)]);
		putchar(itoa64[in[2] & 0x3f]);
	} else
		putchar(itoa64[((in[1] & 0x0f) << 2)]);
}

void DumpKey(int ess, int one_three, bool bIsQOS) {
	fprintf (stderr, "Dumping key %d at time:  %d.%d\n", one_three, cur_t, cur_u);
	printf ("$WPAPSK$%s#", wpa[ess].ssid);
	ether_auto_802_1x_t *auth13, *auth2;
	if (!wpa[ess].packet2) { printf ("ERROR, msg2 null\n"); return; }
	uint8 *p = (uint8*)wpa[ess].packet2;
	uint8 *pkt2 = p;
	if (bIsQOS)
		p += 2;
	p += 8;
	p += sizeof(ether_frame_hdr_t);
	auth2 = (ether_auto_802_1x_t*)p;
	uint8 *p13;
	if (one_three==1) {
		if (!wpa[ess].packet1) { printf ("ERROR, msg1 null\n"); return; }
		p = wpa[ess].packet1;
	} else  {
		if (!wpa[ess].packet3) { printf ("ERROR, msg3 null\n"); return; }
		p = wpa[ess].packet3;
	}
	p13 = p;
	if (bIsQOS)
		p += 2;
	p += 8;
	p += sizeof(ether_frame_hdr_t);
	auth13 = (ether_auto_802_1x_t*)p;

	hccap_t	hccap;
	memset(&hccap, 0, sizeof(hccap_t));
	hccap.keyver = auth2->key_info.KeyDescr;
	memcpy(hccap.mac1, ((ether_frame_hdr_s*)pkt2)->addr1, 6);
	memcpy(hccap.mac2, ((ether_frame_hdr_s*)(p13))->addr1, 6);
	memcpy(hccap.nonce1, auth2->wpa_nonce,32);
	memcpy(hccap.nonce2, auth13->wpa_nonce,32);
	memcpy(hccap.keymic, auth2->wpa_keymic, 16);
	p = wpa[ess].orig_2;
	if (bIsQOS)
		p += 2;
	p += 8;
	p += sizeof(ether_frame_hdr_t);
	auth2 = (ether_auto_802_1x_t*)p;
	memset(auth2->wpa_keymic, 0, 16);
	memcpy(hccap.eapol, auth2, wpa[ess].eapol_sz);
	hccap.eapol_size = wpa[ess].eapol_sz;

	int i;
	uint8 *w = (uint8 *)&hccap;
	for (i = 36; i + 3 < sizeof(hccap_t); i += 3)
		code_block(&w[i], 1);
	code_block(&w[i], 0);
	printf("\n");
	fprintf(stderr, "keyver=%d\n\n",hccap.keyver);
}