/**
 * $Id: key.c,v 1.5 2002/11/25 09:53:03 mein Exp $
 * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
 *
 * 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 2
 * of the License, or (at your option) any later version. The Blender
 * Foundation also sells licenses for use in proprietary software under
 * the Blender License.  See http://www.blender.org/BL/ for information
 * about this.
 *
 * 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, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL/BL DUAL LICENSE BLOCK *****
 */

/* ex:ts=4 */

/**
 * $Id: key.c,v 1.5 2002/11/25 09:53:03 mein Exp $
 * Copyright (C) 2001 NaN Technologies B.V.
 * Blender Key loader library
 */

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

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "blenkey.h"	/* external interface */
#include "key_internal.h"

char *Hexify(byte *in, unsigned int length) {
	unsigned int i;
	char Tstring[3];
	char *out = malloc((2*length + 1) * sizeof(char));
	sprintf(out, "%02X", in[0]);
	for (i=1; i<length; i++) {
		sprintf(Tstring, "%02X", in[i]);
		strcat(out, Tstring);
	}
	return (out);
}

byte *DeHexify(char *in) {
	size_t len = strlen(in);
	byte *out = malloc((len/2) * sizeof(byte));
	unsigned int hexedbyte;
	unsigned int i;
	char *inp = in;
	byte *outp = out;
	for (i=0; i<(len/2); i++) {
		sscanf(inp, "%2x", &hexedbyte);
		inp += 2;
		*outp = (byte) hexedbyte;
		outp++;
	}
	/* printf("\nlen=%d, string=[%s]\n", len, Hexify(out, len/2)); */
	return (out);
}

int from_hex(char c) {
	return (c<'A') ? (c-'0') : (c-'A'+10);
}

/* 5 ReadHex helper functions ------------------------------------------
 read one Hex byte (two characters) and skip newlines if necessary */
byte ReadHexByteFp(FILE *fh, int *newlinetracker) {
	unsigned char a;
	unsigned char a1, a2;
	/* read 2 bytes hexcode of ascii data type */
	fread(&a1, 1, 1, fh);
	fread(&a2, 1, 1, fh);
	a = 16 * (from_hex(a1)) + (from_hex(a2));
	/*printf("Char[%d] = %02X\n", *newlinetracker, a); */
	*newlinetracker += 2;
	/* skip the newlines */
	if (*newlinetracker == 72) {
		fseek(fh, 1, SEEK_CUR);
		*newlinetracker = 0;
		/*printf("LastChar = %02X\n", a); */
	}
	return((byte) a);
}
byte ReadHexByteCp(char **from) {
	int a;
	/* read 2 bytes hexcode of ascii data type */
	sscanf(*from, "%2x", &a);
	/*printf("Char = %02X\n", a); */
	*from += 2;
	return((byte) a);
}
/* Generic hex2int */
int HexToInt(int a) {
	if (a == 0x20) /* space, count as 0 ;-) */
		return 0;
	else
		return(a - '0');
}
/* Note: this is only to be used for the header type */
int HexToIntFp(FILE *fh, int *newlinetracker) {
	byte a = ReadHexByteFp(fh, newlinetracker);
	if (DEBUG) printf("%02X = %d\n", a, a); /* note: no HexToInt here */
	return(a);
}
int HexToIntCp(char **from) {
	byte a = ReadHexByteCp(from);
	if (DEBUG) printf("%02X = %d\n", a, a); /* note: no HexToInt here */
	return(a);
}
/* Note: this is only to be used for the header length */
int Hex5ToInt(byte a, byte b, byte c, byte d, byte e) {
	return(HexToInt((int) a) * 10000 +
		   HexToInt((int) b) * 1000 +
		   HexToInt((int) c) * 100 +
		   HexToInt((int) d) * 10 +
		   HexToInt((int) e));
}
/* Note: this is only to be used for the header length */
int Hex5ToIntFp(FILE *fh, int *newlinetracker) {
	byte a = ReadHexByteFp(fh, newlinetracker),
		 b = ReadHexByteFp(fh, newlinetracker),
		 c = ReadHexByteFp(fh, newlinetracker),
		 d = ReadHexByteFp(fh, newlinetracker),
		 e = ReadHexByteFp(fh, newlinetracker);
	if (DEBUG) printf("\n%02X%02X%02X%02X%02X = %d\n", a, b, c, d, e,
		Hex5ToInt(a, b, c, d, e));
	return(Hex5ToInt(a, b, c, d, e));
}
int Hex5ToIntCp(char **from) {
	byte a = ReadHexByteCp(from),
		 b = ReadHexByteCp(from),
		 c = ReadHexByteCp(from),
		 d = ReadHexByteCp(from),
		 e = ReadHexByteCp(from);
	if (DEBUG) printf("\n%02X%02X%02X%02X%02X = %d\n", a, b, c, d, e,
		Hex5ToInt(a, b, c, d, e));
	return(Hex5ToInt(a, b, c, d, e));
}
/* --------------------------------------------------------------------- */

/* return the biggest */
byte checkfunc0(byte a, byte b) {
	if (a > b) return a;
	else return b;
}
/* return |a-b| */
byte checkfunc1(byte a, byte b) {
	if (a > b) return a - b;
	else return b - a;
}
/* return the sum mod 256 */
byte checkfunc2(byte a, byte b) {
	return ((a + b) % 256);
}
/* return the multiplication mod 256 */
byte checkfunc3(byte a, byte b) {
	return ((a * b) % 256);
}
/* return a/b or 0 */
byte checkfunc4(byte a, byte b) {
	if (b != 0) return (a / b);
	else return 0;
}

char *scan_ascii(FILE *fh, UserStruct *User) {
	long ascii_size;
	char *ascii_data = NULL;
	char *mdhex = NULL;
	byte md[RIPEMD160_DIGEST_LENGTH];
	char string[1024];
	char dosnewlines = 0;
	int lines = 0;
	int oldftell;

	/* NOTE: fscanf is notorious for its buffer overflows. This must be
	 fixed some day, consider this a proof-of-concept version. */

	fscanf(fh, "%1000[^\n]", string);
	sscanf(string, "%*s %s %s %lu %d %d %d",
		   User->email,
		   User->shopid,
		   &User->reldate,
		   &User->keytype,
		   &User->keylevel,
		   &User->keyformat);

	if (User->keyformat <= BLENKEYFORMAT) {
		if (DEBUG) printf(
			"Email:[%s] ShopID:[%s] RelDate:[%lu] KeyType[%d] KeyLevel[%d]\n",
			User->email, User->shopid, User->reldate, User->keytype,
			User->keylevel);

		/* read /n/n
		 check if we're reading dow newlines...
		*/
		oldftell = ftell(fh);
		getc(fh);
		if ((ftell(fh) - oldftell) == 2) {
			/* yes ! */
			dosnewlines = 1;
		}
		getc(fh);

		fscanf(fh, "%1000[^\n]", string);
		strncpy(User->name, string, sizeof(User->name) - 1);

		if (DEBUG) printf("Name:[%s]\n", User->name);

		getc(fh);

		/* 4 lines read uptil now... */
		lines = 4;

		while (getc(fh) != EOF) {
			fscanf(fh, "%1000[^\n]", string);
			lines++;
			/* if (DEBUG) printf("%s\n", string); */
			if (strcmp(string, BLENKEYSEPERATOR) == 0) {
				getc(fh);
				break;
			}
		}
	
		/* fh now points at the start of the datablock */
		ascii_size = ftell(fh);

		if (dosnewlines) {
			/* if we were reading on dos
			 ftell will also count the ^M 's in the file; 
			 substract them
			*/
			ascii_size -= lines;
		}

		ascii_data = malloc((ascii_size+1) * sizeof(char));
		fseek(fh, 0, SEEK_SET);
		fread(ascii_data, sizeof(char), ascii_size, fh);
		ascii_data[ascii_size] = '\0';

		if (DEBUG)
			printf("asciiblock is %ld bytes long:\n[%s]\n", ascii_size, ascii_data);

		/* calculate the hash checksum */
		RIPEMD160(ascii_data, ascii_size, md);
		free(ascii_data);
		mdhex = Hexify(md, RIPEMD160_DIGEST_LENGTH);
	}

	return(mdhex);
}

char *ReadHexCryptedData(FILE *fh, int *newlinetracker) {
	int HexCryptedDataLength = Hex5ToIntFp(fh, newlinetracker);
	int DataType = HexToIntFp(fh, newlinetracker);
	char *HexCryptedData = malloc((HexCryptedDataLength+1) * sizeof(char));
	int i;

	if (DataType != 1) {
		/* printf("Error: unexpected datatype for HexCryptedData\n"); */
		free(HexCryptedData);
		HexCryptedData = 0;
	} else {
		for (i=0; i<(HexCryptedDataLength/2); i++) {
			sprintf(HexCryptedData+2*i, "%02X", ReadHexByteFp(fh, newlinetracker));
		}
	}

	return(HexCryptedData);
}

char *ReadHexCryptedKey(FILE *fh, int *newlinetracker) {
	int HexCryptedKeyLength = Hex5ToIntFp(fh, newlinetracker);
	int DataType = HexToIntFp(fh, newlinetracker);
	char *HexCryptedKey = malloc((HexCryptedKeyLength+1) * sizeof(char));
	int i;

	if (DataType != 2) {
		/* printf("Error: unexpected datatype for HexCryptedKey\n"); */
		free(HexCryptedKey);
		HexCryptedKey = 0;
	} else {
		for (i=0; i<(HexCryptedKeyLength/2); i++) {
			sprintf(HexCryptedKey+2*i, "%02X", ReadHexByteFp(fh, newlinetracker));
		}
	}

	return(HexCryptedKey);
}

/* NOTE: CHANGE THIS INTO A KEY OF OUR OWN */
void LoadRSApubKey(RSA *Pub) {
    static unsigned char n[] =
"\xD1\x12\x0C\x6A\x34\x0A\xCF\x4C\x6B\x34\xA9\x3C\xDD\x1A\x2A\x68"
"\x34\xE5\xB4\xA2\x08\xE8\x9F\xCE\x76\xEF\x4B\x92\x9B\x99\xB4\x57"
"\x72\x95\x78\x1D\x9E\x21\x1B\xF9\x5C\x1B\x0E\xC9\xD0\x89\x75\x28"
"\x08\x13\x6A\xD8\xA9\xC2\xA4\x31\x91\x53\x5A\xB9\x26\x71\x8C\x05";
    static unsigned char e[] =
"\x01\x00\x01";
/*
	static unsigned char e[] = "\x11";
    static unsigned char n[] =
"\x00\xAA\x36\xAB\xCE\x88\xAC\xFD\xFF\x55\x52\x3C\x7F\xC4\x52\x3F"
"\x90\xEF\xA0\x0D\xF3\x77\x4A\x25\x9F\x2E\x62\xB4\xC5\xD9\x9C\xB5"
"\xAD\xB3\x00\xA0\x28\x5E\x53\x01\x93\x0E\x0C\x70\xFB\x68\x76\x93"
"\x9C\xE6\x16\xCE\x62\x4A\x11\xE0\x08\x6D\x34\x1E\xBC\xAC\xA0\xA1"
"\xF5";
*/

	Pub->e = BN_bin2bn(e, sizeof(e)-1, Pub->e);
	Pub->n = BN_bin2bn(n, sizeof(n)-1, Pub->n);
}

byte *RSADecryptKey(char *HexCryptedKey) {
	byte *CryptedKey = NULL;
	byte *Key = NULL;
	int KeyLen;
	int CryptedKeyLen = strlen(HexCryptedKey)/2;
	RSA *Pub = NULL;

	/* Load RSA public key */
	Pub = RSA_new();
	if (Pub == NULL) {
		/* printf("Error in RSA_new\n"); */
	} else {
		LoadRSApubKey(Pub);

		Key = malloc(RSA_size(Pub) * sizeof(byte));

		CryptedKey = DeHexify(HexCryptedKey);

		KeyLen = RSA_public_decrypt(CryptedKeyLen, CryptedKey, Key, Pub,
									RSA_PKCS1_PADDING);
		if (DEBUG)
			printf("CryptedKeyLen = %d, KeyLen = %d\n", CryptedKeyLen, KeyLen);
		if (KeyLen == -1) {
#ifndef NDEBUG
			printf("Error in RSA_public_decrypt: %s\n", ERR_error_string(ERR_get_error(), NULL));
#endif
			free(Key);
			Key = NULL;
		}
	}

	return (Key);
}

char *DeCryptDatablock(byte *CryptKey, int keylen, char *HexCryptedData) {
	RC4_KEY key;
	byte *CryptedData = DeHexify(HexCryptedData);
	unsigned int datalen = strlen(HexCryptedData)/2;
	char *KeyDataString = malloc(datalen * sizeof(char));

	RC4_set_key(&key, keylen, CryptKey);
	RC4(&key, datalen,  CryptedData, KeyDataString);
	free(CryptedData);

	return(KeyDataString);
}

char *get_from_datablock(char **DataPtr, char *TypeString) {
	int tstringsize = Hex5ToIntCp(DataPtr);
	int tstringtype = HexToIntCp(DataPtr);
	char *HexString = NULL;

	if (atoi(TypeString) != tstringtype) {
		/* printf("Unexpected type %d, expected %s\n", tstringtype, TypeString); */
	} else {
		HexString = malloc((tstringsize+1) * sizeof(char));

		strncpy(HexString, *DataPtr, tstringsize);
		*DataPtr += tstringsize;
		HexString[tstringsize] = '\0';
	}

	return(HexString);	
}

int ReadKeyFile(char *filename, UserStruct *User,
				char **Priv, char **Pub, byte **Byte, char **Python) {
	FILE *rawkeyfile;
	char *HexAsciiHash = NULL, *HexCryptedData = NULL, *HexCryptedKey =
		NULL;
	int newlinetracker = 0; /* line position, counts from 0-71 */
	byte *CryptKey = NULL;
	char *KeyDataString = NULL;
	char *KeyDataPtr = NULL;
	char *HexByte = NULL;
	char *mdhex = NULL;
	int ret_val = 1;

	if ((rawkeyfile = fopen(filename, "rb")) == NULL) {
		/* printf("error, cannot read %s\n", filename); */
	} else {
		/* Scan and interpret the ASCII part */
		HexAsciiHash = scan_ascii(rawkeyfile, User);
		if (DEBUG) printf("\nHexHash: %s\n", HexAsciiHash);

		/* Read the HexCryptedData */
		HexCryptedData = ReadHexCryptedData(rawkeyfile, &newlinetracker);
		if (DEBUG) printf("\nHexCryptedData: %s\n", HexCryptedData);

		/* Read the HexCryptedKey */
		HexCryptedKey = ReadHexCryptedKey(rawkeyfile, &newlinetracker);
		if (DEBUG) printf("\nHexCryptedKey: %s\n", HexCryptedKey);

		/* close keyfile */
		fclose(rawkeyfile);

		if (HexAsciiHash && HexCryptedKey && HexCryptedData) {
			/* Decrypt HexCryptedKey */
			CryptKey = RSADecryptKey(HexCryptedKey);

			if (CryptKey) {
				/* Decrypt HexCryptedData */
				KeyDataString = DeCryptDatablock(CryptKey, 16, HexCryptedData);
				free(CryptKey);
				CryptKey = NULL;

				if (KeyDataString) {
					if (DEBUG) printf("\nKeyDataString: %s\n", KeyDataString);

					/* Extract data from KeyDataString */
					KeyDataPtr = KeyDataString;
					mdhex   = get_from_datablock(&KeyDataPtr, "01");
					*Priv   = get_from_datablock(&KeyDataPtr, "02");
					*Pub    = get_from_datablock(&KeyDataPtr, "03");
					HexByte = get_from_datablock(&KeyDataPtr, "04");
					if (HexByte) {
						*Byte = DeHexify(HexByte);
						free(HexByte);
						HexByte = NULL;

						*Python = get_from_datablock(&KeyDataPtr, "05");

						/* Check ascii hash */
						if (strcmp(mdhex, HexAsciiHash) != 0) {
							/* printf("Ascii part checksums do not match !\n");
							 printf("found: %s\n", mdhex);
							 printf("check: %s\n", HexAsciiHash);
							*/
							ret_val = 2;
						} else {
							if (DEBUG) printf("\nThe ascii part checksum matches\n");
							/* everything ok ! */
							ret_val = 0;
						}
						free(mdhex);
						mdhex = NULL;
					}

					free(KeyDataString);
					KeyDataString = NULL;
				}
			}
		}

		/* cleanup */

		if (HexAsciiHash) {
			free(HexAsciiHash);
			HexAsciiHash = NULL;
		}

		if (HexCryptedKey) {
			free(HexCryptedKey);
			HexCryptedKey = NULL;
		}

		if (HexCryptedData) {
			free(HexCryptedData);
			HexCryptedData = NULL;
		}
	}

	return (ret_val);
}

