/*
 * Copyright (c) 1996-2007, OpenFWTK Development Group
 * All rights reserved. See LICENSE.
 */

static	char	RcsId[] = "";

#include	<stdio.h>
#include	<ctype.h>
#include	<sys/types.h>
#include	<fcntl.h>

#include	"global.h"
#include	"md5.h"

/*
	This is the client side MD5 authenticator. it's derived from
an SNK-based "softsnk" which is non-exportable since it uses DES.
The SNK version stores the user's secret key codes in an encrypted
file on the laptop (encrypted with DES and an encrypted checksum to
verify that the user has unlocked it correctly). Softsnk does an
SNK-style challenge/response once the file has been unlocked and the
key decoded correctly.
	The MD5 version asks the user for a password, which is
hashed into an MD5 value used as a seed for an MD5 checksum.
There is an option that can be used to spit out the octets of
the checksum seed, which is stored in the authentication database.
	*WARNING* -- this is example code; it uses getpass() to
read the user's passphrase. On some systems, getpass() truncates
at 8 chars, which is truly lame. If you have a system that does
such a thing, you should make sure to replace/enhance your getpass()
routine if you plan to use this code in production.

*/

extern	char		*getpass();

typedef	unsigned char	md5_key_schedule[8];
typedef	unsigned char	md5_cblock[8];

char			kbuf[512];
char			challbuf[512];


/*
given a decoded key, do a SNK-style challenge/response
using MD5
*/
static	int
md5reply(chall,dflg,kbloc)
char		*chall;
int		dflg;
md5_cblock	kbloc;
{
	MD5_CTX			keysched;
	unsigned char		digest[16];
	char			buf[12];
	char			cbuf[12];
	int			i;
	int			j;
	unsigned long		kval = 0;

	/* zeroize the entire buffer */
	for(i = 0; i < 9; i++)
		buf[i] = '\0';
	strncpy(buf,chall,8);

	/* begin MD5 specific code */
	MD5Init(&keysched);
	/* set the "key" (initializer) into MD5 */
	MD5Update(&keysched,kbloc,8);

	/* push response through the rotating knives */
	MD5Update(&keysched,buf,8);
	/* wrap up MD5 into digest */
	MD5Final(digest,&keysched);
	/* emulate DES by pulling 8 bytes into cbuf */
	bcopy(digest,cbuf,8);
	/* done MD5 specific code */

	/* pull some bits out of the ciphertext into a long */
	for(i=0; i<4; i++)
		for(j = 0; j < 8; j++)
			kval = (kval << 1) | ((cbuf[i] >> (7 - j)) & 1);

	snprintf(buf,sizeof(buf),"%08lx",kval);
	if(!dflg) {
		printf("Response: %s\n",buf);
		return(0);
	}
	for(i=0; buf[i]; i++)
		if(buf[i] == 'a' || buf[i] == 'b' || buf[i] == 'c')
			buf[i] = '2';
		else
			if(buf[i] == 'd' || buf[i] == 'e' || buf[i] == 'f')
				buf[i] = '3';
	printf("Response: %s\n",buf);
	return(0);
}


main(ac,av)
int	ac;
char	*av[];
{
	int		x;
	char		*challenge = (char *)0;
	char		*key = (char *)0;
	int		iflg = 0;
	int		dflg = 1;
	md5_cblock	kbloc;

	for(x = 1; x < ac; x++) {
		if(av[x][0] == '-') {
			switch(av[x][1]) {
			case 'h':
				dflg = 0;
				break;
			case 'c':
				challenge = av[x+1];
				break;
			case 'k':
				key = av[x+1];
				break;
			case 'I':
				iflg = 1;
				break;
			default:
				exit(usage());
			}
		}
	}


	/*
	this uses getpass() which is truly lame, since it truncates
	to 8 chars. if you have a decent library with a good getpass
	or getpass compatible routine, it is suggested you replace
	it here.
	*/
	/* if not given key, ask for it */
	if(key == (char *)0) {
		char		*pp;

		if((pp = getpass("Enter key:")) == (char *)0) {
			fprintf(stderr,"Invalid password\n");
			exit(1);
		}
		if(iflg) {
			char	b1[512];
			strlcpy(b1,pp,512);
			if((pp = getpass("Repeat key:")) == (char *)0) {
				fprintf(stderr,"Invalid password\n");
				exit(1);
			}
			if(strcmp(pp,b1)) {
				fprintf(stderr,"Passwords do not match\n");
				exit(1);
			}
		}
		strlcpy(kbuf,pp,512);
		key = kbuf;
	}


	/* decode user's DES key - MD5 version hashes it into a kblock
	instead of decrypting the kblock from a file */
	if(decode_key(key,kbloc))
		exit(1);


	/* initialize password mode */
	if(iflg)
		exit(initkey(key,kbloc));


	/* dropping into challenge/password mode */
	if(!iflg && challenge == (char *)0) {
		fprintf(stderr,"Challenge: ");
		fflush(stderr);
		if((challenge = gets(challbuf)) == (char *)0)
			exit(1);
	}
	md5reply(challenge,dflg,kbloc);
	exit(0);
}


decode_key(key,kbloc)
char		*key;
md5_cblock	kbloc;
{
	MD5_CTX			keysched;
	unsigned char		digest[16];
	int	x;

	MD5Init(&keysched);
	MD5Update(&keysched,key,strlen(key));
	MD5Final(digest,&keysched);
	bcopy(digest,kbloc,8);
	return(0);
}



initkey(key,kbloc)
char		*key;
md5_cblock	kbloc;
{
	int		x;

	printf("Key \"%s\" hashkey = {\n",key);
	for(x = 0; x < 8; x++)
		printf("\t%3.3o\n",kbloc[x]);
	printf("}\n");
}


usage()
{
	fprintf(stderr,"usage: softmd5: [-c challenge] [-k key] [-I](initialize)\n");
	return(1);
}
