xref: /openbsd/usr.bin/x99token/x99token.c (revision 4cfece93)
1 /*	$OpenBSD: x99token.c,v 1.13 2017/05/03 09:51:39 mestre Exp $	*/
2 
3 /*
4  * X9.9 calculator
5  * This software is provided AS IS with no express or implied warranty
6  * October 1995, Paul Borman <prb@krystal.com>
7  *
8  * Donated to the Public Domain by Paul Borman
9  */
10 
11 #include <sys/stat.h>
12 
13 #include <ctype.h>
14 #include <err.h>
15 #include <pwd.h>
16 #include <readpassphrase.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include <limits.h>
22 #include <openssl/des.h>
23 
24 #define	KEYFILE		".keyfile.des"
25 #define	HEXDIGITS	"0123456789abcdef"
26 #define	DECDIGITS	"0123456789012345"
27 
28 void predict(DES_key_schedule, const char *, int);
29 
30 char *digits = HEXDIGITS;
31 extern char *__progname;
32 
33 int
34 main(int argc, char **argv)
35 {
36 	int i;
37 	char buf[256];
38 	DES_key_schedule ks;
39 	DES_cblock key;
40 	char _keyfile[PATH_MAX];
41 	char *keyfile = 0;
42 	FILE *fp;
43 	int init = 0;
44 	int hex = 1;
45 	int cnt = 1;
46 	unsigned int pin;
47 	struct passwd *pwd;
48 
49 	if (pledge("stdio rpath wpath cpath getpw tty", NULL) == -1)
50 		err(1, "pledge");
51 
52 	while ((i = getopt(argc, argv, "dk:in:")) != -1) {
53 		switch (i) {
54 		case 'k':
55 			keyfile = optarg;
56 			break;
57 		case 'i':
58 			init = 1;
59 			break;
60 		case 'd':
61 			hex = 0;
62 			break;
63 		case 'n':
64 			cnt = atoi(optarg);
65 			if (cnt <= 0)
66 				err(1, "invalid count: %s", optarg);
67 			break;
68 		default:
69 			fprintf(stderr,
70 			    "usage: %s [-d] [-k keyfile] [-n count]\n"
71 			    "       %s -i [-k keyfile]\n",
72 			    __progname, __progname);
73 			exit(1);
74 		}
75 	}
76 
77 	if (!keyfile) {
78 		if ((pwd = getpwuid(getuid())) == NULL) {
79 			fprintf(stderr, "Say, just who are you, anyhow?\n");
80 			exit(1);
81 		}
82 		snprintf(_keyfile, sizeof(_keyfile), "%s/%s", pwd->pw_dir,
83 		    KEYFILE);
84 		keyfile = _keyfile;
85 	}
86 
87 	if (init)
88 		readpassphrase("Enter Key: ", buf, sizeof(buf), 0);
89 	else if ((fp = fopen(keyfile, "r")) == NULL)
90 		err(1, "unable to open %s", keyfile);
91 	else {
92 		if (fgets(buf, sizeof(buf), fp) == NULL) {
93 			fprintf(stderr, "No key in %s\n", keyfile);
94 			exit(1);
95 		}
96 		fclose(fp);
97 	}
98 
99 	memset(key, 0, sizeof(key));
100 	if (init && buf[3] == ' ') {
101 		char *b = buf;
102 		/* Assume octal input */
103 		for (i = 0; i < 8; ++i) {
104 			if (!*b)
105 				fprintf(stderr, "%s: invalid key\n", buf);
106 			while (isdigit((unsigned char)*b))
107 				key[i] = key[i] << 3 | (*b++ - '0');
108 			while (*b && !isdigit((unsigned char)*b))
109 				++b;
110 		}
111 	} else {
112 		for (i = 0; i < 16; ++i) {
113 			int d;
114 
115 			if (islower((unsigned char)buf[i]))
116 				buf[i] = toupper((unsigned char)buf[i]);
117 			if (buf[i] >= '0' && buf[i] <= '9')
118 				d = buf[i] - '0';
119 			else if (buf[i] >= 'A' && buf[i] <= 'F')
120 				d = buf[i] - 'A' + 10;
121 			else {
122 				fprintf(stderr, "invalid key: %s\n", buf);
123 				exit(1);
124 			}
125 			key[i>>1] |= d << ((i & 1) ? 0 : 4);
126 		}
127 	}
128 
129 	/* XXX - should warn on non-space or non-digit */
130 	readpassphrase("Enter Pin: ", buf, sizeof(buf), 0);
131 	for (i = 0, pin = 0; buf[i] && buf[i] != '\n'; ++i)
132 		if (isdigit((unsigned char)buf[i]))
133 			pin = pin * 16 + buf[i] - '0' + 1;
134 
135 	if ((pin & 0xffff0000) == 0)
136 		pin |= pin << 16;
137 
138 	for (i = 0; i < 8; ++i)
139 		key[0] ^= (pin >> ((i * 7) % 26)) & 0x7f;
140 
141 	if (init) {
142 		umask(S_IRWXG | S_IRWXO);
143 		unlink(keyfile);
144 		if ((fp = fopen(keyfile, "w")) == NULL)
145 			err(1, "could not open %s for writing", keyfile);
146 		for (i = 0; i < 8; ++i) {
147 			fprintf(fp, "%c", digits[(key[i]>>4)&0xf]);
148 			fprintf(fp, "%c", digits[(key[i]>>0)&0xf]);
149 		}
150 		fputc('\n', fp);
151 		fclose(fp);
152 		exit(0);
153 	}
154 
155 	DES_fixup_key_parity(&key);
156 	DES_key_sched(&key, &ks);
157 
158 	buf[0] = '\0';
159 	readpassphrase("Enter challenge: ", buf, sizeof(buf), RPP_ECHO_ON);
160 	if (buf[0] == '\0')
161 		exit(0);
162 
163 	for (i = 0; i < 8; ++i)
164 		if (buf[i] == '\n')
165 			buf[i] = '\0';
166 
167 	if (!hex)
168 		digits = DECDIGITS;
169 
170 	predict(ks, buf, cnt);
171 
172 	explicit_bzero(&ks, sizeof(ks));
173 	explicit_bzero(buf, sizeof(buf));
174 
175 	exit(0);
176 }
177 
178 void
179 predict(DES_key_schedule ks, const char *chal, int cnt)
180 {
181 	int i;
182 	DES_cblock cb;
183 
184 	memcpy(&cb, chal, sizeof(cb));
185 	while (cnt-- > 0) {
186 		printf("%.8s: ", (char *)cb);
187 		DES_ecb_encrypt(&cb, &cb, &ks, DES_ENCRYPT);
188 		for (i = 0; i < 4; ++i) {
189 			printf("%c", digits[(cb[i]>>4) & 0xf]);
190 			printf("%c", digits[(cb[i]>>0) & 0xf]);
191 		}
192 		putchar('\n');
193 		for (i = 0; i < 8; ++i) {
194 			if ((cb[i] &= 0xf) > 9)
195 				cb[i] -= 10;
196 			cb[i] |= 0x30;
197 		}
198 	}
199 	memset(&cb, 0, sizeof(cb));
200 }
201