All pastes #948090 Raw Edit

FiSH encryption module for irssi

public cpp v1 · immutable
·published 2008-03-18 21:50 UTC
rendered paste body
// FiSH encryption module for irssi, v0.99#include "FiSH.h"// encrypt a message and store in bf_dest (using key for target)static int FiSH_encrypt(char *msg_ptr, const char *target, char *bf_dest){	unsigned char contactName[100]="", theKey[150]="", ini_outgoing[10]="";	if(msg_ptr==NULL || *msg_ptr=='\0' || bf_dest==NULL || target==NULL || *target=='\0') return 0;	GetPrivateProfileString("FiSH", "process_outgoing", "1", ini_outgoing, sizeof(ini_outgoing), iniPath);	if(*ini_outgoing=='0' || *ini_outgoing=='n' || *ini_outgoing=='N') return 0;	if(strlen(target) >= sizeof(contactName)) return 0;	// buffer too small	strcpy(contactName, target);	FixContactName(contactName);	// replace '[' and ']' with '~' in contact name	GetPrivateProfileString(contactName, "key", "", theKey, sizeof(theKey), iniPath);	if(strlen(theKey) < 4) return 0;		// don't process, key not found in ini	if(strncmp(theKey, "+OK ", 4)==0)	{	// key is encrypted, lets decrypt		decrypt_string((char *)iniKey, theKey+4, theKey, strlen(theKey+4));		if(*theKey=='\0')		{	// don't process, decrypted key is bad			ZeroMemory(theKey, sizeof(theKey));			return 0;		}	}	encrypt_string(theKey, msg_ptr, bf_dest, strlen(msg_ptr));	ZeroMemory(theKey, sizeof(theKey));	return 1;}// decrypt a base64 cipher text (using key for target)static int FiSH_decrypt(char *msg_ptr, char *msg_bak, const char *target){	unsigned char contactName[100], theKey[150]="", bf_dest[1000]="";	unsigned char ini_incoming[10]="", myMark[20]="", mark_pos[20]="";	unsigned int msg_len, i, mark_broken_block=0;	if(msg_ptr==NULL || *msg_ptr=='\0' || msg_bak==NULL || target==NULL || *target=='\0') return 0;	GetPrivateProfileString("FiSH", "process_incoming", "1", ini_incoming, sizeof(ini_incoming), iniPath);	if(*ini_incoming=='0' || *ini_incoming=='n' || *ini_incoming=='N') return 0;	if(strncmp(msg_ptr, "+OK ", 4)==0) msg_ptr += 4;	else if(strncmp(msg_ptr, "mcps ", 5)==0) msg_ptr += 5;	else return 0;		// don't process, blowcrypt-prefix not found	strcpy(contactName, target);	FixContactName(contactName);	// replace '[' and ']' with '~' in contact name	GetPrivateProfileString(contactName, "key", "", theKey, sizeof(theKey), iniPath);	if(*theKey=='\0' || strlen(theKey)<4) return 0;	// don't process, key not found in ini	if(strncmp(theKey, "+OK ", 4)==0)	{	// key is encrypted, lets decrypt		decrypt_string((char *)iniKey, theKey+4, theKey, strlen(theKey+4));		if(*theKey=='\0')		{	// don't process, decrypted key is bad			ZeroMemory(theKey, sizeof(theKey));			return 0;		}	}	// Verify base64 string	msg_len=strlen(msg_ptr);	if((strspn(msg_ptr, B64) != msg_len) || (msg_len < 12)) return 0;	// usually a received message does not exceed 512 chars, but we want to prevent evil buffer overflow	if(msg_len >= (int)(sizeof(bf_dest)*1.5)) msg_ptr[(int)(sizeof(bf_dest)*1.5)-20]='\0';	// block-align blowcrypt strings if truncated by IRC server (each block is 12 chars long)	// such a truncated block is destroyed and not needed anymore	if(msg_len != (msg_len/12)*12)	{		msg_len=(msg_len/12)*12;		msg_ptr[msg_len]='\0';		GetPrivateProfileString("FiSH", "mark_broken_block", " \002&\002", myMark, sizeof(myMark), iniPath);		if(*myMark=='\0' || *myMark=='0' || *myMark=='n' || *myMark=='N') mark_broken_block=0;		else mark_broken_block=1;	}	decrypt_string(theKey, msg_ptr, bf_dest, msg_len);	ZeroMemory(theKey, sizeof(theKey));	i=0;	while(bf_dest[i] != 0x0A && bf_dest[i] != 0x0D && bf_dest[i] != '\0') i++;	bf_dest[i]='\0';	// in case of wrong key, decrypted message might have control characters -> cut message	if(*bf_dest=='\0') return 0;	// don't process, decrypted msg is bad	// append broken-block-mark?	if(mark_broken_block) strcat(bf_dest, myMark);	// append crypt-mark?	GetPrivateProfileString(contactName, "mark_encrypted", "1", myMark, sizeof(myMark), iniPath);	if(*myMark!='0' && *myMark!='n' && *myMark!='N')	{		GetPrivateProfileString("FiSH", "mark_encrypted", "", myMark, sizeof(myMark), iniPath);	// global setting		if(*myMark != '\0')		{			GetPrivateProfileString("FiSH", "mark_position", "0", mark_pos, sizeof(mark_pos), iniPath);			if(*mark_pos=='0') strcat(bf_dest, myMark);		//append mark at the end			else			{	// prefix mark				i=strlen(myMark);				memmove(bf_dest+i, bf_dest, strlen(bf_dest)+1);				strncpy(bf_dest, myMark, i);			}		}	}	strcpy(msg_bak, bf_dest);	// copy decrypted message back (overwriting the base64 cipher text)	ZeroMemory(bf_dest, sizeof(bf_dest));	return 1;}static void decrypt_incoming(SERVER_REC *server, char *msg, const char *nick, const char *addr, const char *target){	unsigned char contactName[100]="", *msg_bak;	if(msg==NULL || target==NULL || nick==NULL) return;	else if(strncmp(msg, "+OK ", 4)!=0 && strncmp(msg, "mcps ", 5)!=0 && strcmp(nick, "-psyBNC")!=0) return;	// don't process, blowcrypt-prefix not found	msg_bak=(unsigned char *)msg;	// this is needed, because of psyBNC	//channel?	if(*target=='#' || *target=='&') strcpy(contactName, target);	else if(strcmp(nick, "-psyBNC")==0)	{		msg=strstr(msg, " :(")+3;	// points to nick!ident@host in psybnc log		if(msg==(char *)3) return;		ExtractRnick(contactName, msg);		msg=strchr(msg, ' ')+1;		if(msg==(char *)1) return;		if((strncmp(msg, "+OK ", 4)!=0) && (strncmp(msg, "mcps ", 5)!=0)) return;	}	else strcpy(contactName, nick);	if(FiSH_decrypt(msg, msg, contactName))	{		signal_stop();		signal_emit(signal_get_emitted(), 5, server, msg_bak, nick, addr, target);	}}static void encrypt_outgoing(SERVER_REC *server, char *msg, char *target, char *orig_target){	unsigned char bf_dest[800]="";	unsigned char contactName[100]="", plain_prefix[20]="";	unsigned char myMark[20]="", mark_pos[20]="";	unsigned int i;	// generally cut a message to a size of 512 byte, as everything above will never arrive complete anyway	if(strlen(msg) > 512) msg[512]='\0';	// plain-prefix in msg found?	GetPrivateProfileString("FiSH", "plain_prefix", "+p ", plain_prefix, sizeof(plain_prefix), iniPath);	if(*plain_prefix != '\0')	{		i=strlen(plain_prefix);		if(strncasecmp(msg, plain_prefix, i)==0)		{			irc_send_cmdv(server, "PRIVMSG %s :%s", target, msg+i);			return;		}	}	if(FiSH_encrypt(msg, target, bf_dest)==0)	{	// send plain-text (no key found)		irc_send_cmdv(server, "PRIVMSG %s :%s", target, msg);		return;	}	bf_dest[512]='\0';	irc_send_cmdv(server, "PRIVMSG %s :%s %s\n", target, "+OK", bf_dest);	strcpy(bf_dest, msg);	// append crypt-mark?	GetPrivateProfileString(contactName, "mark_encrypted", "1", myMark, sizeof(myMark), iniPath);	if(*myMark!='0' && *myMark!='n' && *myMark!='N')	{		GetPrivateProfileString("FiSH", "mark_encrypted", "", myMark, sizeof(myMark), iniPath);	// global setting		if(*myMark != '\0')		{			GetPrivateProfileString("FiSH", "mark_position", "0", mark_pos, sizeof(mark_pos), iniPath);			if(*mark_pos=='0') strcat(bf_dest, myMark);		//append mark at the end			else			{	// prefix mark				i=strlen(myMark);				memmove(bf_dest+i, bf_dest, strlen(bf_dest)+1);				strncpy(bf_dest, myMark, i);			}		}	}	signal_continue(4, server, bf_dest, target, orig_target);	ZeroMemory(bf_dest, sizeof(bf_dest));	return;}static void notice_incoming(SERVER_REC *server, char *msg, char *nick, char *address, char *target){	// decrypt NOTICE messages (and forward DH1080 key-exchange)	unsigned char *contactPtr;	if(strncmp(msg, "DH1024_", 7)==0)	{		irc_send_cmdv(server, "NOTICE %s :Received \002old DH1024\002 public key from you! Please update to latest version: http://fish.sekure.us\n", nick);		printtext(server, NULL, MSGLEVEL_CRAP, "\002FiSH:\002 Received \002old DH1024\002 public key from %s! Telling him to update...", nick);		return;	}	if(strncmp(msg, "DH1080_", 7)==0)	{		DH1080_received(server, msg, nick, address, target);		return;	}	if(*target=='#' || *target=='&') contactPtr=target;	else contactPtr=nick;	if(FiSH_decrypt(msg, msg, contactPtr))	{		signal_stop();		signal_emit(signal_get_emitted(), 5, server, msg, nick, address, target);	}}static void decrypt_action(SERVER_REC *server, char *msg, char *nick, char *address, char *target){	if(FiSH_decrypt(msg, msg, target))	{		signal_stop();		signal_emit(signal_get_emitted(), 5, server, msg, nick, address, target);	}}static void decrypt_topic(SERVER_REC *server, char *channel, char *topic, char *nick, char *address){	if(FiSH_decrypt(topic, topic, channel))	{		signal_stop();		signal_emit(signal_get_emitted(), 5, server, channel, topic, nick, address);	}}static void topic_changed(CHANNEL_REC *chan_rec){	FiSH_decrypt(chan_rec->topic, chan_rec->topic, chan_rec->name);}static void raw_handler(SERVER_REC *server, char *data){	char channel[100], *ptr, *ptr_bak;	int len;	if(data==NULL || *data=='\0') return;	ptr=strchr(data, ' ');	// point to command	if(ptr==NULL) return;	ptr++;	if(strncmp(ptr, "332 ", 4)!=0) return;	// 332 = TOPIC	ptr_bak=ptr;	ptr=strchr(ptr, '#');	// point to #channel	if(ptr==NULL)	{		ptr=strchr(ptr_bak, '&');	// point to &channel		if(ptr_bak==NULL) return;	}	len=strchr(ptr, ' ')-ptr;	if(len >= sizeof(channel)-2) return;	// channel string too long, something went wrong	strncpy(channel, ptr, len);	channel[len]='\0';	ptr=strchr(ptr, ':');	// point to topic msg start	if(ptr==NULL) return;	ptr++;	FiSH_decrypt(ptr, ptr, channel);}static void encrypt_notice(const char *data, SERVER_REC *server, WI_ITEM_REC *item){	// New command: /notice+ <nick/#channel> <notice message>	unsigned char bf_dest[1000]="";	const char *target;	unsigned char *msg;	void *free_arg=NULL;	if(data==NULL || (strlen(data) < 3)) goto notice_error;	if(!cmd_get_params(data, &free_arg, 2, &target, &msg)) goto notice_error;	if (target==NULL || *target=='\0' || msg==NULL || *msg=='\0') goto notice_error;	// generally refuse a notice size of more than 512 byte, as everything above will never arrive complete anyway	if(strlen(msg) >= 512)	{		printtext(server, target, MSGLEVEL_CRAP, "\002FiSH:\002 /notice+ error: message argument exceeds buffer size!");		goto notice_error;	}	if(FiSH_encrypt(msg, target, bf_dest)==0)	{		printtext(server, target, MSGLEVEL_CRAP, "\002FiSH:\002 /notice+ error: Encryption disabled or no key found for %s.", target);		goto notice_error;	}	bf_dest[512]='\0';	irc_send_cmdv(server, "NOTICE %s :%s %s\n", target, "+OK", bf_dest);	signal_emit("message irc own_notice", 3, server, msg, target);	cmd_params_free(free_arg);	return;notice_error:	if(free_arg) cmd_params_free(free_arg);	printtext(server, item!=NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP,			"\002FiSH:\002 Usage: /notice+ <nick/#channel> <notice message>");}// set encrypted topic for current channel, irssi syntax: /topic+ <your topic>static void command_crypt_TOPIC(const char *data, SERVER_REC *server, WI_ITEM_REC *item){	unsigned char bf_dest[1000]="";	const char *target;	if(data==0 || *data=='\0') goto topic_error;	if(item!=NULL) target=window_item_get_target(item);	else goto topic_error;	if(*target!='#' && *target!='&')	{		printtext(server, target, MSGLEVEL_CRAP, "\002FiSH:\002 Please change to the channel window where you want to set the topic!");		goto topic_error;	}	// generally refuse a topic size of more than 512 byte, as everything above will never arrive complete anyway	if(strlen(data) >= 512)	{		printtext(server, target, MSGLEVEL_CRAP, "\002FiSH:\002 /topic+ error: topic length exceeds buffer size!");		goto topic_error;	}	// encrypt a message (using key for target)	if(FiSH_encrypt((char *)data, target, bf_dest)==0)	{		printtext(server, target, MSGLEVEL_CRAP, "\002FiSH:\002 /topic+ error: Encryption disabled or no key found for %s.", target);		goto topic_error;	}	bf_dest[512]='\0';	irc_send_cmdv(server, "TOPIC %s :%s %s\n", target, "+OK", bf_dest);	return;topic_error:	printtext(server, item!=NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP,			"\002FiSH:\002 Usage: /topic+ <your new topic>");}static void command_helpfish(const char *arg, SERVER_REC *server, WI_ITEM_REC *item){	printtext(NULL, NULL, MSGLEVEL_CRAP,		"\n\002FiSH HELP:\002 For more information read FiSH-irssi.txt :)\n\n"		" /topic+ <your new topic>\n"		" /notice+ <nick/#channel> <notice message>\n"		" /key [<nick/#channel>]\n"		" /setkey [<nick/#channel>] <sekure_key>\n"		" /delkey <nick/#channel>\n"		" /keyx [<nick>] (DH1080 KeyXchange)\n"		" /setinipw <sekure_blow.ini_password>\n"		" /unsetinipw\n");}static void command_setinipw(const char *iniPW, SERVER_REC *server, WI_ITEM_REC *item){	unsigned int i=0, pw_len, re_enc=0;	unsigned char B64digest[50], SHA256digest[35];	unsigned char bfKey[512], new_iniKey[100], old_iniKey[100], *fptr, *ok_ptr, line_buf[1000], iniPath_new[300];	FILE *h_ini, *h_ini_new;	pw_len=strlen(iniPW);	if(pw_len < 1 || pw_len > sizeof(new_iniKey))	{		printtext(server, item!=NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP,				"\002FiSH:\002 No parameters. Usage: /setinipw <sekure_blow.ini_password>");		return;	}	strfcpy(new_iniKey, (char *)iniPW);	ZeroMemory(iniPW, pw_len);	pw_len=strlen(new_iniKey);	if(pw_len < 8)	{		printtext(server, item!=NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP,				"\002FiSH:\002 Password too short, at least 8 characters needed! Usage: /setinipw <sekure_blow.ini_password>");		return;	}	SHA256_memory(new_iniKey, pw_len, SHA256digest);	ZeroMemory(new_iniKey, sizeof(new_iniKey));	for(i=0;i<40872;i++) SHA256_memory(SHA256digest, 32, SHA256digest);	htob64(SHA256digest, B64digest, 32);	strcpy(old_iniKey, iniKey);	if(unsetiniFlag) strcpy(iniKey, default_iniKey);	// unsetinipw	else strcpy(iniKey, B64digest);	// this is used for encrypting blow.ini	for(i=0;i<30752;i++) SHA256_memory(SHA256digest, 32, SHA256digest);	htob64(SHA256digest, B64digest, 32);	// this is used to verify the entered password	ZeroMemory(SHA256digest, sizeof(SHA256digest));	// re-encrypt blow.ini with new password	strcpy(iniPath_new, iniPath);	strcat(iniPath_new, "_new");	h_ini_new=fopen(iniPath_new, "w");	h_ini=fopen(iniPath,"r");	if(h_ini && h_ini_new)	{		while (!feof(h_ini))		{			fptr=fgets(line_buf, sizeof(line_buf)-2, h_ini);			if(fptr)			{				ok_ptr=strstr(line_buf, "+OK ");				if(ok_ptr)				{					re_enc=1;					strtok(ok_ptr+4, " \n\r");					decrypt_string(old_iniKey, ok_ptr+4, bfKey, strlen(ok_ptr+4));					ZeroMemory(ok_ptr+4, strlen(ok_ptr+4)+1);					encrypt_string(iniKey, bfKey, ok_ptr+4, strlen(bfKey));					strcat(line_buf, "\n");				}				if(fprintf(h_ini_new, "%s", line_buf) < 0)				{					ZeroMemory(B64digest, sizeof(B64digest));					ZeroMemory(bfKey, sizeof(bfKey));					ZeroMemory(line_buf, sizeof(line_buf));					ZeroMemory(old_iniKey, sizeof(old_iniKey));					fclose(h_ini);					fclose(h_ini_new);					remove(iniPath_new);					printtext(server, item!=NULL ? window_item_get_target(item) : NULL,	MSGLEVEL_CRAP,							"\002FiSH ERROR:\002 Unable to write new blow.ini, probably out of disc space.");					return;				}			}		}		ZeroMemory(bfKey, sizeof(bfKey));		ZeroMemory(line_buf, sizeof(line_buf));		ZeroMemory(old_iniKey, sizeof(old_iniKey));		fclose(h_ini);		fclose(h_ini_new);		remove(iniPath);		rename(iniPath_new, iniPath);	}	else return;	if(WritePrivateProfileString("FiSH", "ini_password_Hash", B64digest, iniPath) == -1)	{		ZeroMemory(B64digest, sizeof(B64digest));		printtext(server, item!=NULL ? window_item_get_target(item) : NULL,	MSGLEVEL_CRAP,				"\002FiSH ERROR:\002 Unable to write to blow.ini, probably out of space or permission denied.");		return;	}	ZeroMemory(B64digest, sizeof(B64digest));	if(re_enc) printtext(server, item!=NULL ? window_item_get_target(item) : NULL,					MSGLEVEL_CRAP, "\002FiSH: Re-encrypted blow.ini\002 with new password.");	if(!unsetiniFlag) printtext(server, item!=NULL ? window_item_get_target(item) : NULL,						MSGLEVEL_CRAP, "\002FiSH:\002 blow.ini password hash saved.");}// Change back to default blow.ini password, irssi syntax: /unsetinipwstatic void command_unsetinipw(const char *arg, SERVER_REC *server, WI_ITEM_REC *item){	unsetiniFlag=1;	command_setinipw("Some_boogie_dummy_key", server, item);	unsetiniFlag=0;	if(WritePrivateProfileString("FiSH", "ini_password_Hash", "\0", iniPath) == -1)	{		printtext(server, item!=NULL ? window_item_get_target(item) : NULL,	MSGLEVEL_CRAP,			"\002FiSH ERROR:\002 Unable to write to blow.ini, probably out of space or permission denied.");		return;	}	printtext(server, item!=NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP,		"\002FiSH:\002 Changed back to default blow.ini password, you won't have to enter it on start-up anymore!");}static void command_setkey(const char *data, SERVER_REC *server, WI_ITEM_REC *item){	unsigned char contactName[100]="", encryptedKey[150]="";	const char *target, *key;	void *free_arg;	if (data==NULL || *data=='\0')	{		printtext(server, item!=NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP,			"\002FiSH:\002 No parameters. Usage: /setkey [<nick/#channel>] <sekure_key>");		return;	}	if (!cmd_get_params(data, &free_arg, 2, &target, &key)) return;	if (*target=='\0')	{		printtext(server, item!=NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP,				"\002FiSH:\002 No parameters. Usage: /setkey [<nick/#channel>] <sekure_key>");		cmd_params_free(free_arg);		return;	}	if (*key=='\0') {		// one paramter given - it's the key		key = target;		if (item != NULL) target = window_item_get_target(item);		else		{			printtext(NULL, NULL, MSGLEVEL_CRAP,					"\002FiSH:\002 Please define nick/#channel. Usage: /setkey [<nick/#channel>] <sekure_key>");			cmd_params_free(free_arg);			return;		}	}	strcpy(contactName, target);	FixContactName(contactName);	encrypt_key((char *)key, encryptedKey);	if(WritePrivateProfileString(contactName, "key", encryptedKey, iniPath) == -1)	{		ZeroMemory(encryptedKey, sizeof(encryptedKey));		printtext(server, item!=NULL ? window_item_get_target(item) : NULL,	MSGLEVEL_CRAP,				"\002FiSH ERROR:\002 Unable to write to blow.ini, probably out of space or permission denied.");		cmd_params_free(free_arg);		return;	}	ZeroMemory(encryptedKey, sizeof(encryptedKey));	printtext(server, item!=NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP,			"\002FiSH:\002 Key for %s successfully set!", target);	cmd_params_free(free_arg);}static void command_delkey(const char *target, SERVER_REC *server, WI_ITEM_REC *item){	unsigned char contactName[100]="";	if (target==NULL || *target=='\0')	{		printtext(server, item!=NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP,				"\002FiSH:\002 No parameters. Usage: /delkey <nick/#channel>");		return;	}	strfcpy(contactName, (char *)target);	FixContactName(contactName);	if(WritePrivateProfileString(contactName, "key", "\0", iniPath) == -1)	{		printtext(server, item!=NULL ? window_item_get_target(item) : NULL,	MSGLEVEL_CRAP,			"\002FiSH ERROR:\002 Unable to write to blow.ini, probably out of space or permission denied.");		return;	}	printtext(server, item!=NULL ? window_item_get_target(item) : NULL,	MSGLEVEL_CRAP,		"\002FiSH:\002 Key for %s successfully removed!", target);}static void command_key(const char *target, SERVER_REC *server, WI_ITEM_REC *item){	unsigned char contactName[100]="", theKey[150]="";	if(target==NULL || *target=='\0')	{		if (item!=NULL) target=window_item_get_target(item);		else		{			printtext(NULL, NULL, MSGLEVEL_CRAP, "\002FiSH:\002 Please define nick/#channel. Usage: /key <nick/#channel>");			return;		}	}	strfcpy(contactName, (char *)target);	FixContactName(contactName);	GetPrivateProfileString(contactName, "key", "", theKey, sizeof(theKey), iniPath);	if(*theKey=='\0' || strlen(theKey)<4)	{	// don't process, key not found in ini		printtext(server, item!=NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP,			"\002FiSH:\002 Key for %s not found!", target);		return;	}	if(strncmp(theKey, "+OK ", 4)==0)	{       // key is encrypted, lets decrypt		decrypt_string((char *)iniKey, theKey+4, theKey, strlen(theKey+4));		if(*theKey=='\0')		{	// don't process, decrypted key is bad			ZeroMemory(theKey, sizeof(theKey));			printtext(server, target, MSGLEVEL_CRAP, "\002FiSH:\002 Key for %s invalid!", target);			return;		}	}	printtext(server, target, MSGLEVEL_CRAP, "\002FiSH:\002 Key for %s: %s", target, theKey);	ZeroMemory(theKey, sizeof(theKey));}static void command_keyx(const char *target, SERVER_REC *server, WI_ITEM_REC *item){	if(target==NULL || *target=='\0')	{		if(item!=NULL) target = window_item_get_target(item);		else		{			printtext(NULL, NULL, MSGLEVEL_CRAP, "\002FiSH:\002 Please define nick/#channel. Usage: /keyx <nick/#channel>");			return;		}	}	if(*target=='#' || *target=='&')	{		printtext(server, target, MSGLEVEL_CRAP, "\002FiSH:\002 KeyXchange does not work for channels!");		return;	}	DH1080_gen(g_myPrivKey, g_myPubKey);	irc_send_cmdv(server, "NOTICE %s :%s %s", target, "DH1080_INIT", g_myPubKey);	printtext(server, item!=NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP,			"\002FiSH:\002 Sent my DH1080 public key to %s, waiting for reply ...", target);}static void DH1080_received(SERVER_REC *server, char *msg, char *nick, char *address, char *target){	unsigned int i;	char hisPubKey[300], contactName[100]="", encryptedKey[150]="";	if(*target=='&' || *target=='#' || *nick=='#' || *nick=='&') return;	// no KeyXchange for channels...	i=strlen(msg);	if(i<191 || i>195) return;	if(strncmp(msg, "DH1080_INIT ", 12)==0)	{		strcpy(hisPubKey, msg+12);		if(strspn(hisPubKey, B64ABC) != strlen(hisPubKey)) return;		if(query_find(server, nick)==NULL)		{	// query window not found, lets create one			keyx_query_created=1;			irc_query_create(server->tag, nick, TRUE);			keyx_query_created=0;		}		printtext(server, nick, MSGLEVEL_CRAP, "\002FiSH:\002 Received DH1080 public key from %s, sending mine...", nick);		DH1080_gen(g_myPrivKey, g_myPubKey);		irc_send_cmdv(server, "NOTICE %s :%s %s", nick, "DH1080_FINISH", g_myPubKey);	}	else if(strncmp(msg, "DH1080_FINISH ", 14)==0) strcpy(hisPubKey, msg+14);	else return;	if(DH1080_comp(g_myPrivKey, hisPubKey)==0) return;	signal_stop();	strcpy(contactName, nick);	FixContactName(contactName);	encrypt_key(hisPubKey, encryptedKey);	ZeroMemory(hisPubKey, sizeof(hisPubKey));	if(WritePrivateProfileString(contactName, "key", encryptedKey, iniPath) == -1)	{		ZeroMemory(encryptedKey, sizeof(encryptedKey));		printtext(server, nick,	MSGLEVEL_CRAP, "\002FiSH ERROR:\002 Unable to write to blow.ini, probably out of space or permission denied.");		return;	}	ZeroMemory(encryptedKey, sizeof(encryptedKey));	printtext(server, nick, MSGLEVEL_CRAP, "\002FiSH:\002 Key for %s successfully set!", nick);}static void do_auto_keyx(QUERY_REC *query, int automatic){	// perform auto-keyXchange only for known people	unsigned char theKey[150]="", ini_auto_keyx[10], contactName[100]="";	if(keyx_query_created) return;	// query was created by FiSH	GetPrivateProfileString("FiSH", "auto_keyxchange", "1", ini_auto_keyx, sizeof(ini_auto_keyx), iniPath);	if(*ini_auto_keyx=='0' || *ini_auto_keyx=='N' || *ini_auto_keyx=='n') return;	strcpy(contactName, query->name);	FixContactName(contactName);	GetPrivateProfileString(contactName, "key", "", theKey, sizeof(theKey), iniPath);	if(strlen(theKey) > 4)	{		ZeroMemory(theKey, sizeof(theKey));		command_keyx(query->name, query->server, NULL);	}}static void query_nick_changed(QUERY_REC *query, char *orignick){	// copy key for old nick to use with the new one	unsigned char theKey[150]="", ini_nicktracker[10], contactName[100]="";	GetPrivateProfileString("FiSH", "nicktracker", "1", ini_nicktracker, sizeof(ini_nicktracker), iniPath);	if(*ini_nicktracker=='0' || *ini_nicktracker=='N' || *ini_nicktracker=='n') return;	if(orignick==NULL || strcasecmp(orignick, query->name)==0) return;	// same nick, different case?	strcpy(contactName, orignick);	FixContactName(contactName);	GetPrivateProfileString(contactName, "key", "", theKey, sizeof(theKey), iniPath);	if(strlen(theKey) < 4) return;	// see if there is a key for the old nick	strcpy(contactName, query->name);	FixContactName(contactName);	if(WritePrivateProfileString(contactName, "key", theKey, iniPath) == -1)	{		ZeroMemory(theKey, sizeof(theKey));		printtext(NULL, NULL, MSGLEVEL_CRAP, "\002FiSH ERROR:\002 Unable to write to blow.ini, probably out of space or permission denied.");		return;	}	ZeroMemory(theKey, sizeof(theKey));}static void void_send(SERVER_REC *server, const char *target, const char *msg, int target_type){}static void server_register_fish(SERVER_REC *server){	MODULE_SERVER_REC *rec;	if(server==NULL) return;	rec = g_new0(MODULE_SERVER_REC, 1);	MODULE_DATA_SET(server, rec);	rec->orig_send_message = server->send_message;	server->send_message = void_send;}static void server_unregister_fish(SERVER_REC *server){	MODULE_SERVER_REC *rec;	if(server==NULL) return;	rec = MODULE_DATA(server);	if(rec==NULL) return;	server->send_message = rec->orig_send_message;	MODULE_DATA_UNSET(server);	g_free(rec);}void fish_init(void){	unsigned char iniPasswordHash[50], SHA256digest[35], B64digest[50], *iniPass_ptr;	unsigned int i;	g_slist_foreach(servers, (GFunc) server_register_fish, NULL);	strcpy(iniPath, get_irssi_config());	// path to irssi config file	strcpy(tempPath, iniPath);	strcpy(strrchr(iniPath, '/'), blow_ini);	strcpy(strrchr(tempPath, '/'), "/temp_FiSH.$$$");	GetPrivateProfileString("FiSH", "ini_Password_hash", "0", iniPasswordHash, sizeof(iniPasswordHash), iniPath);	if(strlen(iniPasswordHash) == 43)	{		iniPass_ptr = getpass(" --> Please enter your blow.ini password: ");		strcpy(iniKey, iniPass_ptr);		ZeroMemory(iniPass_ptr, strlen(iniPass_ptr));		irssi_redraw();		// getpass() screws irssi GUI, lets redraw!		SHA256_memory(iniKey, strlen(iniKey), SHA256digest);		for(i=0;i<40872;i++) SHA256_memory(SHA256digest, 32, SHA256digest);		htob64(SHA256digest, B64digest, 32);		strcpy(iniKey, B64digest);      // this is used for encrypting blow.ini		for(i=0;i<30752;i++) SHA256_memory(SHA256digest, 32, SHA256digest);		htob64(SHA256digest, B64digest, 32);	// this is used to verify the entered password		if(strcmp(B64digest, iniPasswordHash) != 0)		{			printtext(NULL, NULL, MSGLEVEL_CRAP, "\002FiSH:\002 Wrong blow.ini password entered, try again...");			printtext(NULL, NULL, MSGLEVEL_CRAP, "\002FiSH module NOT loaded.\002");			return;		}		printtext(NULL, NULL, MSGLEVEL_CRAP, "\002FiSH:\002 Correct blow.ini password entered, lets go!");	}	else	{		strcpy(iniKey, default_iniKey);		printtext(NULL, NULL, MSGLEVEL_CRAP, "\002FiSH:\002 Using default password to decrypt blow.ini... Try /setinipw to set a custom password.");	}	signal_add_first("message private", (SIGNAL_FUNC) decrypt_incoming);	signal_add_first("message public", (SIGNAL_FUNC) decrypt_incoming);	signal_add_first("message irc notice", (SIGNAL_FUNC) notice_incoming);	signal_add_first("message irc action", (SIGNAL_FUNC) decrypt_action);	signal_add_first("message own_private", (SIGNAL_FUNC) encrypt_outgoing);	signal_add_first("message own_public", (SIGNAL_FUNC) encrypt_outgoing);	signal_add_first("channel topic changed", (SIGNAL_FUNC) topic_changed);	signal_add_first("message topic", (SIGNAL_FUNC) decrypt_topic);	signal_add_first("server incoming", (SIGNAL_FUNC) raw_handler);	signal_add("server connected", (SIGNAL_FUNC) server_register_fish);	signal_add("server disconnected", (SIGNAL_FUNC) server_unregister_fish);	signal_add("query created", (SIGNAL_FUNC) do_auto_keyx);	signal_add("query nick changed", (SIGNAL_FUNC) query_nick_changed);	command_bind("topic+", NULL, (SIGNAL_FUNC) command_crypt_TOPIC);	command_bind("notice+", NULL, (SIGNAL_FUNC) encrypt_notice);	command_bind("notfish", NULL, (SIGNAL_FUNC) encrypt_notice);	command_bind("setkey", NULL, (SIGNAL_FUNC) command_setkey);	command_bind("delkey", NULL, (SIGNAL_FUNC) command_delkey);	command_bind("key", NULL, (SIGNAL_FUNC) command_key);	command_bind("keyx", NULL, (SIGNAL_FUNC) command_keyx);	command_bind("setinipw", NULL, (SIGNAL_FUNC) command_setinipw);	command_bind("unsetinipw", NULL, (SIGNAL_FUNC) command_unsetinipw);	command_bind("fishhelp", NULL, (SIGNAL_FUNC) command_helpfish);	command_bind("helpfish", NULL, (SIGNAL_FUNC) command_helpfish);	printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE,		"FiSH v0.99 - encryption module for irssi loaded! URL: http://fish.sekure.us\n"		"Try /helpfish or /fishhelp for a short command overview");	module_register("fish", "core");}void fish_deinit(void){	g_slist_foreach(servers, (GFunc) server_unregister_fish, NULL);	signal_remove("message private", (SIGNAL_FUNC) decrypt_incoming);	signal_remove("message public", (SIGNAL_FUNC) decrypt_incoming);	signal_remove("message irc notice", (SIGNAL_FUNC) notice_incoming);	signal_remove("message irc action", (SIGNAL_FUNC) decrypt_action);	signal_remove("message own_private", (SIGNAL_FUNC) encrypt_outgoing);	signal_remove("message own_public", (SIGNAL_FUNC) encrypt_outgoing);	signal_remove("channel topic changed", (SIGNAL_FUNC) topic_changed);	signal_remove("message topic", (SIGNAL_FUNC) decrypt_topic);	signal_remove("server incoming", (SIGNAL_FUNC) raw_handler);	signal_remove("server connected", (SIGNAL_FUNC) server_register_fish);	signal_remove("server disconnected", (SIGNAL_FUNC) server_unregister_fish);	signal_remove("query created", (SIGNAL_FUNC) do_auto_keyx);	signal_remove("query nick changed", (SIGNAL_FUNC) query_nick_changed);	command_unbind("topic+", (SIGNAL_FUNC) command_crypt_TOPIC);	command_unbind("notice+", (SIGNAL_FUNC) encrypt_notice);	command_unbind("notfish", (SIGNAL_FUNC) encrypt_notice);	command_unbind("setkey", (SIGNAL_FUNC) command_setkey);	command_unbind("delkey", (SIGNAL_FUNC) command_delkey);	command_unbind("key", (SIGNAL_FUNC) command_key);	command_unbind("keyx", (SIGNAL_FUNC) command_keyx);	command_unbind("setinipw", (SIGNAL_FUNC) command_setinipw);	command_unbind("unsetinipw", (SIGNAL_FUNC) command_unsetinipw);	command_unbind("fishhelp", (SIGNAL_FUNC) command_helpfish);	command_unbind("helpfish", (SIGNAL_FUNC) command_helpfish);}// :someone!ident@host.net PRIVMSG leetguy :Some Text -> Result: Rnick="someone"int ExtractRnick(char *Rnick, char *incoming_msg)		// needs direct pointer to "nick@host" or ":nick@host"{	int k=0;	if(*incoming_msg == ':') incoming_msg++;	while(*incoming_msg!='!' && *incoming_msg!='\0') {		Rnick[k]=*incoming_msg;		incoming_msg++;		k++;	}	Rnick[k]='\0';	if (*Rnick != '\0') return TRUE;	else return FALSE;}// replace '[' and ']' from nick/channel with '~' (else problems with .ini files)void FixContactName(char *contactName){	while(*contactName != '\0')	{		if((*contactName == '[') || (*contactName == ']')) *contactName='~';		contactName++;	}}void memXOR(unsigned char *s1, const unsigned char *s2, int n){	while(n--) *s1++ ^= *s2++;}char *strfcpy(unsigned char *dest, char *buffer)		// removes leading and trailing blanks from string{	int i=0, k=strlen(buffer);	while(buffer[i]==' ') i++;	while(buffer[k-1]==' ') k--;	buffer[k]=0;	strcpy(dest, buffer+i);	return dest;}