#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <math.h>
#include <stdint.h>


typedef union uwb
{
	unsigned w;
	unsigned char b[4];
}
WBunion;

typedef unsigned Digest[4];

unsigned f0(unsigned abcd[])
{
	return (abcd[1] &abcd[2]) | (~abcd[1] &abcd[3]);
}

unsigned f1(unsigned abcd[])
{
	return (abcd[3] &abcd[1]) | (~abcd[3] &abcd[2]);
}

unsigned f2(unsigned abcd[])
{
	return abcd[1] ^ abcd[2] ^ abcd[3];
}

unsigned f3(unsigned abcd[])
{
	return abcd[2] ^ (abcd[1] | ~abcd[3]);
}

typedef unsigned(*DgstFctn)(unsigned a[]);

unsigned* calcKs(unsigned *k)
{
	double s, pwr;
	int i;

	pwr = pow(2, 32);
	for (i = 0; i < 64; i++)
	{
		s = fabs(sin(1 + i));
		k[i] = (unsigned)(s *pwr);
	}
	return k;
}

// ROtate v Left by amt bits
unsigned rol(unsigned v, short amt)
{
	unsigned msk1 = (1 << amt) - 1;
	return ((v >> (32 - amt)) &msk1) | ((v << amt) &~msk1);
}

unsigned* md5(const char *msg, int mlen)
{
	static Digest h0 = { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476
	};
	//static Digest h0 = { 0x01234567, 0x89ABCDEF, 0xFEDCBA98, 0x76543210 };
	static DgstFctn ff[] = { &f0, &f1, &f2, &f3
	};
	static short M[] = { 1, 5, 3, 7 };
	static short O[] = { 0, 1, 5, 0 };
	static short rot0[] = { 7, 12, 17, 22 };
	static short rot1[] = { 5, 9, 14, 20 };
	static short rot2[] = { 4, 11, 16, 23 };
	static short rot3[] = { 6, 10, 15, 21 };
	static short *rots[] = { rot0, rot1, rot2, rot3
	};
	static unsigned kspace[64];
	static unsigned * k;

	static Digest h;
	Digest abcd;
	DgstFctn fctn;
	short m, o, g;
	unsigned f;
	short * rotn;
	union
	{
		unsigned w[16];
		char b[64];
	}
	mm;
	int os = 0;
	int grp, grps, q, p;
	unsigned char *msg2;

	if (k == NULL) k = calcKs(kspace);

	for (q = 0; q < 4; q++) h[q] = h0[q];

	{
		grps = 1 + (mlen + 8) / 64;
		msg2 = malloc(64 *grps);
		memcpy(msg2, msg, mlen);
		msg2[mlen] = (unsigned char) 0x80;
		q = mlen + 1;
		while (q < 64 *grps)
		{
			msg2[q] = 0;
			q++;
		}
		{
			WBunion u;
			u.w = 8 * mlen;
			q -= 8;
			memcpy(msg2 + q, &u.w, 4);
		}
	}

	for (grp = 0; grp < grps; grp++)
	{
		memcpy(mm.b, msg2 + os, 64);
		for (q = 0; q < 4; q++) abcd[q] = h[q];
		for (p = 0; p < 4; p++)
		{
			fctn = ff[p];
			rotn = rots[p];
			m = M[p];
			o = O[p];
			for (q = 0; q < 16; q++)
			{
				g = (m *q + o) % 16;
				f = abcd[1] + rol(abcd[0] + fctn(abcd) + k[q + 16 *p] + mm.w[g], rotn[q % 4]);

				abcd[0] = abcd[3];
				abcd[3] = abcd[2];
				abcd[2] = abcd[1];
				abcd[1] = f;
			}
		}
		for (p = 0; p < 4; p++)
			h[p] += abcd[p];
		os += 64;
	}

	if (msg2)
		free(msg2);

	return h;
}

char *get_mac()
{
	FILE * fp;
	char *mac = malloc(18* sizeof(char));
	fp = fopen("/sys/class/net/wlan0/address", "r");
	fgets(mac, 17, fp);
	fclose(fp);
	return mac;
}

char *get_serial()
{
	FILE * fp;
	char *serial = malloc(21* sizeof(char));
	fp = fopen("/etc/serial", "r");
	fgets(serial, 20, fp);
	return serial;
}

int gen_serial()
{
	// hexdump -n 16 -e '4/4 "%08X" 1 "\n"' /dev/random
	if (fopen("/etc/serial", "r") == NULL)
	{
		system("/usr/bin/hexdump -n 16 -e '4/4 \"%08X\"' /dev/urandom > /etc/serial");
	}
}

int gen_ssid()
{
	char *serial;
	FILE * fp;
	char ssid[20] = "CyberChallenge-";
	serial = get_serial();
	strncat(ssid, serial, 4);
	fp = fopen("/etc/ssid", "w");
	fprintf(fp, "%s", ssid);
	fclose(fp);

}

int gen_key(char *serial, char *mac)
{
	FILE * fp;
	char md5a[33];
	char md5b[33];
	char tmp[3];
	char key[21];
	char wstr[9];
	char xstr[9];
	char ystr[9];
	char zstr[9];
	uint32_t w, x, y, z;
	long long int seed;
	int j, k;
	unsigned long int h;
	WBunion u;

	md5a[0] = '\0';
	md5b[0] = '\0';

	wstr[8] = '\0';
	xstr[8] = '\0';
	ystr[8] = '\0';
	zstr[8] = '\0';

	unsigned *d = md5(mac, strlen(mac));
	for (j = 0; j < 4; j++)
	{
		u.w = d[j];
		for (k = 0; k < 4; k++)
		{
			snprintf(tmp, 3, "%02x", u.b[k]);
			strncat(md5a, tmp, 2);
		}
	}

	unsigned *f = md5(serial, strlen(serial));
	for (j = 0; j < 4; j++)
	{
		u.w = f[j];
		for (k = 0; k < 4; k++)
		{
			snprintf(tmp, 3, "%02x", u.b[k]);
			strncat(md5b, tmp, 2);
		}
	}

	strncpy(wstr, md5a, 8);
	strncpy(xstr, md5a + 8, 8);
	strncpy(ystr, md5a + 16, 8);
	strncpy(zstr, md5a + 24, 8);

	w = strtoul(wstr, NULL, 16);
	x = strtoul(xstr, NULL, 16);
	y = strtoul(ystr, NULL, 16);
	z = strtoul(zstr, NULL, 16);

	for (int i = 0; i < 20; ++i)
	{
		uint32_t t = x;
		t ^= t << 11U;
		t ^= t >> 8U;
		x = y;
		y = z;
		z = w;
		w ^= w >> 19U;
		w ^= t;
		key[i] = md5b[t % 20];
	}
	key[20] = '\0';

	fp = fopen("/etc/wpa", "w");
	fprintf(fp, "%s", key);
	fclose(fp);
}

int main()
{
	char *mac;
	char *serial;

	gen_serial();
	gen_ssid();
	mac = get_mac();
	serial = get_serial();
	gen_key(mac, serial);

	return 0;
}