[BACK]Return to x99token.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / x99token

File: [local] / src / usr.bin / x99token / x99token.c (download)

Revision 1.13, Wed May 3 09:51:39 2017 UTC (7 years ago) by mestre
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, OPENBSD_7_2_BASE, OPENBSD_7_2, OPENBSD_7_1_BASE, OPENBSD_7_1, OPENBSD_7_0_BASE, OPENBSD_7_0, OPENBSD_6_9_BASE, OPENBSD_6_9, OPENBSD_6_8_BASE, OPENBSD_6_8, OPENBSD_6_7_BASE, OPENBSD_6_7, OPENBSD_6_6_BASE, OPENBSD_6_6, OPENBSD_6_5_BASE, OPENBSD_6_5, OPENBSD_6_4_BASE, OPENBSD_6_4, OPENBSD_6_3_BASE, OPENBSD_6_3, OPENBSD_6_2_BASE, OPENBSD_6_2, HEAD
Changes since 1.12: +3 -3 lines

Use the safe idiom of cleaning sensitive data from memory with explicit_bzero,
instead of relying on other methods, after readpassphrase. Some programs on
this diff won't benefit that much since it happens near the terminal path, but
someone might copy the unsafe idiom to another program and place it where it
may leak sensitive data.

Discussed aeons ago with tb@, OK deraadt@ and beck@

/*	$OpenBSD: x99token.c,v 1.13 2017/05/03 09:51:39 mestre Exp $	*/

/*
 * X9.9 calculator
 * This software is provided AS IS with no express or implied warranty
 * October 1995, Paul Borman <prb@krystal.com>
 *
 * Donated to the Public Domain by Paul Borman
 */

#include <sys/stat.h>

#include <ctype.h>
#include <err.h>
#include <pwd.h>
#include <readpassphrase.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <openssl/des.h>

#define	KEYFILE		".keyfile.des"
#define	HEXDIGITS	"0123456789abcdef"
#define	DECDIGITS	"0123456789012345"

void predict(DES_key_schedule, const char *, int);

char *digits = HEXDIGITS;
extern char *__progname;

int
main(int argc, char **argv)
{
	int i;
	char buf[256];
	DES_key_schedule ks;
	DES_cblock key;
	char _keyfile[PATH_MAX];
	char *keyfile = 0;
	FILE *fp;
	int init = 0;
	int hex = 1;
	int cnt = 1;
	unsigned int pin;
	struct passwd *pwd;

	if (pledge("stdio rpath wpath cpath getpw tty", NULL) == -1)
		err(1, "pledge");

	while ((i = getopt(argc, argv, "dk:in:")) != -1) {
		switch (i) {
		case 'k':
			keyfile = optarg;
			break;
		case 'i':
			init = 1;
			break;
		case 'd':
			hex = 0;
			break;
		case 'n':
			cnt = atoi(optarg);
			if (cnt <= 0)
				err(1, "invalid count: %s", optarg);
			break;
		default:
			fprintf(stderr,
			    "usage: %s [-d] [-k keyfile] [-n count]\n"
			    "       %s -i [-k keyfile]\n",
			    __progname, __progname);
			exit(1);
		}
	}

	if (!keyfile) {
		if ((pwd = getpwuid(getuid())) == NULL) {
			fprintf(stderr, "Say, just who are you, anyhow?\n");
			exit(1);
		}
		snprintf(_keyfile, sizeof(_keyfile), "%s/%s", pwd->pw_dir,
		    KEYFILE);
		keyfile = _keyfile;
	}

	if (init)
		readpassphrase("Enter Key: ", buf, sizeof(buf), 0);
	else if ((fp = fopen(keyfile, "r")) == NULL)
		err(1, "unable to open %s", keyfile);
	else {
		if (fgets(buf, sizeof(buf), fp) == NULL) {
			fprintf(stderr, "No key in %s\n", keyfile);
			exit(1);
		}
		fclose(fp);
	}

	memset(key, 0, sizeof(key));
	if (init && buf[3] == ' ') {
		char *b = buf;
		/* Assume octal input */
		for (i = 0; i < 8; ++i) {
			if (!*b)
				fprintf(stderr, "%s: invalid key\n", buf);
			while (isdigit((unsigned char)*b))
				key[i] = key[i] << 3 | (*b++ - '0');
			while (*b && !isdigit((unsigned char)*b))
				++b;
		}
	} else {
		for (i = 0; i < 16; ++i) {
			int d;

			if (islower((unsigned char)buf[i]))
				buf[i] = toupper((unsigned char)buf[i]);
			if (buf[i] >= '0' && buf[i] <= '9')
				d = buf[i] - '0';
			else if (buf[i] >= 'A' && buf[i] <= 'F')
				d = buf[i] - 'A' + 10;
			else {
				fprintf(stderr, "invalid key: %s\n", buf);
				exit(1);
			}
			key[i>>1] |= d << ((i & 1) ? 0 : 4);
		}
	}

	/* XXX - should warn on non-space or non-digit */
	readpassphrase("Enter Pin: ", buf, sizeof(buf), 0);
	for (i = 0, pin = 0; buf[i] && buf[i] != '\n'; ++i)
		if (isdigit((unsigned char)buf[i]))
			pin = pin * 16 + buf[i] - '0' + 1;

	if ((pin & 0xffff0000) == 0)
		pin |= pin << 16;

	for (i = 0; i < 8; ++i)
		key[0] ^= (pin >> ((i * 7) % 26)) & 0x7f;

	if (init) {
		umask(S_IRWXG | S_IRWXO);
		unlink(keyfile);
		if ((fp = fopen(keyfile, "w")) == NULL)
			err(1, "could not open %s for writing", keyfile);
		for (i = 0; i < 8; ++i) {
			fprintf(fp, "%c", digits[(key[i]>>4)&0xf]);
			fprintf(fp, "%c", digits[(key[i]>>0)&0xf]);
		}
		fputc('\n', fp);
		fclose(fp);
		exit(0);
	}

	DES_fixup_key_parity(&key);
	DES_key_sched(&key, &ks);

	buf[0] = '\0';
	readpassphrase("Enter challenge: ", buf, sizeof(buf), RPP_ECHO_ON);
	if (buf[0] == '\0')
		exit(0);

	for (i = 0; i < 8; ++i)
		if (buf[i] == '\n')
			buf[i] = '\0';

	if (!hex)
		digits = DECDIGITS;

	predict(ks, buf, cnt);

	explicit_bzero(&ks, sizeof(ks));
	explicit_bzero(buf, sizeof(buf));

	exit(0);
}

void
predict(DES_key_schedule ks, const char *chal, int cnt)
{
	int i;
	DES_cblock cb;

	memcpy(&cb, chal, sizeof(cb));
	while (cnt-- > 0) {
		printf("%.8s: ", (char *)cb);
		DES_ecb_encrypt(&cb, &cb, &ks, DES_ENCRYPT);
		for (i = 0; i < 4; ++i) {
			printf("%c", digits[(cb[i]>>4) & 0xf]);
			printf("%c", digits[(cb[i]>>0) & 0xf]);
		}
		putchar('\n');
		for (i = 0; i < 8; ++i) {
			if ((cb[i] &= 0xf) > 9)
				cb[i] -= 10;
			cb[i] |= 0x30;
		}
	}
	memset(&cb, 0, sizeof(cb));
}