/************************************************************************
 *   IRC - Internet Relay Chat, modules/m_dkey.c
 *
 *   Copyright (C) 2000-2003 TR-IRCD Development
 *
 *   Copyright (C) 1990 Jarkko Oikarinen and
 *                      University of Oulu, Co Center
 *
 *   See file AUTHORS in IRC package for additional names of
 *   the programmers.
 *
 *   This program is free softwmare; 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, or (at your option)
 *   any later version.
 *
 *   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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "struct.h"
#include "common.h"
#include "sys.h"
#include "h.h"
#include "dh.h"
#include "msg.h"

#define DKEY_GOTIN  0x01
#define DKEY_GOTOUT 0x02

#define DKEY_DONE(x) (((x) & (DKEY_GOTIN|DKEY_GOTOUT)) == (DKEY_GOTIN|DKEY_GOTOUT))

static struct Message _msgtab[] = {
    {MSG_DKEY, 0, MAXPARA, M_SLOW, 0L,
     m_dkey, m_ignore, m_ignore, m_dkey, m_ignore}
};

#ifndef STATIC_MODULES

char *_version = "$Revision: 1.3 $";

void _modinit(void)
{
    mod_add_cmd(_msgtab);
}

void _moddeinit(void)
{
    mod_del_cmd(_msgtab);
}
#else
void m_dkey_init(void)
{
    mod_add_cmd(_msgtab);
}
#endif

int m_dkey(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
#ifdef HAVE_ENCRYPTION_ON
    if (!(IsNegoServer(sptr) && parc > 1)) {
	if (IsPerson(sptr))
	    return 0;
	return exit_client(sptr, sptr, "Not negotiating now");
    }
    if (irc_strcmp(parv[1], "START") == 0) {
    char keybuf[1024];

	if (parc != 2)
	    return exit_client(sptr, sptr, "DKEY START failure");

	if (sptr->serv->sin != NULL && sptr->serv->sout != NULL)
	    return exit_client(sptr, sptr, "DKEY START duplicate?!");

	sptr->serv->sin = dh_start_session();
	sptr->serv->sout = dh_start_session();
	sendto_gnotice("Initiating diffie-hellman key exchange with %s", sptr->name);

	dh_get_s_public(keybuf, 1024, sptr->serv->sin);
	sendto_one(sptr, "%s PUB I %s", MSG_DKEY, keybuf);

	dh_get_s_public(keybuf, 1024, sptr->serv->sout);
	sendto_one(sptr, "%s PUB O %s", MSG_DKEY, keybuf);
	return 0;
    }

    if (irc_strcmp(parv[1], "PUB") == 0) {
    char keybuf[1024];

	if (parc != 4 || !sptr->serv->sin || !sptr->serv->sout)
	    return exit_client(sptr, sptr, "DKEY PUB failure");

	if (irc_strcmp(parv[2], "O") == 0) {	/* their out is my in! */
	    if (!dh_generate_shared(sptr->serv->sin, parv[3]))
		return exit_client(sptr, sptr, "DKEY PUB O invalid");
	    sptr->serv->dkey_flags |= DKEY_GOTOUT;
	} else if (irc_strcmp(parv[2], "I") == 0) {	/* their out is my in! */
	    if (!dh_generate_shared(sptr->serv->sout, parv[3]))
		return exit_client(sptr, sptr, "DKEY PUB I invalid");
	    sptr->serv->dkey_flags |= DKEY_GOTIN;
	} else
	    return exit_client(sptr, sptr, "DKEY PUB bad option");

	if (DKEY_DONE(sptr->serv->dkey_flags)) {
    int keylen;
	    sendto_one(sptr, "%s DONE", MSG_DKEY);
	    SetRC4OUT(sptr);
            keylen = 1024;
            if(!dh_get_s_shared(keybuf, &keylen, sptr->serv->sin))
            	return exit_client(sptr, sptr, "Could not setup encrypted session");
            sptr->serv->rc4_in = rc4_initstate(keybuf, keylen);

            keylen = 1024;
            if(!dh_get_s_shared(keybuf, &keylen, sptr->serv->sout))
            	return exit_client(sptr, sptr, "Could not setup encrypted session");
            sptr->serv->rc4_out = rc4_initstate(keybuf, keylen);
	    dh_end_session(sptr->serv->sin);
	    dh_end_session(sptr->serv->sout);

	    sptr->serv->sin = sptr->serv->sout = NULL;
	    return 0;
	}
	return 0;
    }

    if (irc_strcmp(parv[1], "DONE") == 0) {
	if (!((sptr->serv->sin == NULL && sptr->serv->sout == NULL)
	      && (sptr->serv->rc4_in != NULL && sptr->serv->rc4_out != NULL)))
	    return exit_client(sptr, sptr, "DKEY DONE when not done!");
	SetRC4IN(sptr);
	sendto_gnotice("Diffie-Hellman exchange with %s complete, connection encrypted.", sptr->name);
	sendto_one(sptr, "%s EXIT", MSG_DKEY);
	return RC4_NEXT_BUFFER;
    }

    if (irc_strcmp(parv[1], "EXIT") == 0) {
	if (!(IsRC4IN(sptr) && IsRC4OUT(sptr)))
	    return exit_client(sptr, sptr, "DKEY EXIT when not in proper stage");
	ClearNegoServer(sptr);
	return continue_server_estab(sptr);
    }
#endif
    return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1