xref: /openbsd-src/libexec/login_yubikey/yubikey.c (revision 6cc4e57d498809f6ec364f1c10349bb4b55a87d8)
1 /* $OpenBSD: yubikey.c,v 1.6 2017/09/16 08:07:15 anton Exp $ */
2 
3 /*
4  * Written by Simon Josefsson <simon@josefsson.org>.
5  * Copyright (c) 2006, 2007, 2008, 2009 Yubico AB
6  * Copyright (c) 2010 Daniel Hartmeier <daniel@benzedrine.cx>
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions are
11  * met:
12  *
13  * * Redistributions of source code must retain the above copyright
14  *   notice, this list of conditions and the following disclaimer.
15  *
16  * * Redistributions in binary form must reproduce the above
17  *   copyright notice, this list of conditions and the following
18  *   disclaimer in the documentation and/or other materials provided
19  *   with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  */
34 
35 #include <ctype.h>
36 #include <stdlib.h>
37 #include <wchar.h>
38 #include <locale.h>
39 #include <errno.h>
40 
41 #include "yubikey.h"
42 #include "keymaps.h"
43 
44 static const uint8_t RC[] = {
45 	0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36
46 };
47 
48 static const uint8_t rijndael_sbox[] = {
49 	0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5,
50 	0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
51 	0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0,
52 	0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
53 	0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC,
54 	0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
55 	0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A,
56 	0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
57 	0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0,
58 	0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
59 	0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B,
60 	0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
61 	0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85,
62 	0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
63 	0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5,
64 	0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
65 	0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17,
66 	0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
67 	0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88,
68 	0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
69 	0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C,
70 	0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
71 	0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9,
72 	0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
73 	0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6,
74 	0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
75 	0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E,
76 	0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
77 	0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94,
78 	0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
79 	0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68,
80 	0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
81 };
82 
83 static const uint8_t rijndael_inv_sbox[] = {
84 	0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38,
85 	0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
86 	0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87,
87 	0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
88 	0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D,
89 	0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
90 	0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2,
91 	0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
92 	0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16,
93 	0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
94 	0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA,
95 	0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
96 	0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A,
97 	0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
98 	0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02,
99 	0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
100 	0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA,
101 	0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
102 	0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85,
103 	0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
104 	0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89,
105 	0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
106 	0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20,
107 	0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
108 	0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31,
109 	0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
110 	0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D,
111 	0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
112 	0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0,
113 	0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
114 	0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26,
115 	0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
116 };
117 
118 static inline uint8_t
xtime(uint8_t b)119 xtime(uint8_t b)
120 {
121 	return (b & 0x80) ? ((b << 1) ^ 0x1b) : (b << 1);
122 }
123 
124 #define NUMBER_OF_ROUNDS 10
125 
126 void
yubikey_aes_decrypt(uint8_t * state,const uint8_t * key)127 yubikey_aes_decrypt(uint8_t *state, const uint8_t *key)
128 {
129 	uint8_t i, j, round_key[0x10];
130 	uint8_t a02x, a13x;
131 	uint8_t a02xx, a13xx;
132 	uint8_t k1, k2;
133 
134 	memcpy(round_key, key, sizeof(round_key));
135 	for (i = 0; i < NUMBER_OF_ROUNDS; i++) {
136 		round_key[0] ^= RC[i];
137 
138 		round_key[0] ^= rijndael_sbox[round_key[13]];
139 		round_key[1] ^= rijndael_sbox[round_key[14]];
140 		round_key[2] ^= rijndael_sbox[round_key[15]];
141 		round_key[3] ^= rijndael_sbox[round_key[12]];
142 
143 		for (j = 4; j < 16; j++)
144 			round_key[j] ^= round_key[j - 4];
145 	}
146 	for (i = 0; i < 0x10; i++)
147 		state[i] ^= round_key[i];
148 
149 	for (i = 1; i <= NUMBER_OF_ROUNDS; i++) {
150 		/* inv_byte_sub_shift_row(); */
151 
152 		/* First row: 0 shift, 0 4 8 12 */
153 		state[0] = rijndael_inv_sbox[state[0]];
154 		state[4] = rijndael_inv_sbox[state[4]];
155 		state[8] = rijndael_inv_sbox[state[8]];
156 		state[12] = rijndael_inv_sbox[state[12]];
157 
158 		/* Second row: -1 shift, 1 5 9 13 */
159 		j = state[13];
160 		state[13] = rijndael_inv_sbox[state[9]];
161 		state[9] = rijndael_inv_sbox[state[5]];
162 		state[5] = rijndael_inv_sbox[state[1]];
163 		state[1] = rijndael_inv_sbox[j];
164 
165 		/* Third row: -2 shift, 2 6 10 14 */
166 		j = state[2];
167 		state[2] = rijndael_inv_sbox[state[10]];
168 		state[10] = rijndael_inv_sbox[j];
169 		j = state[6];
170 		state[6] = rijndael_inv_sbox[state[14]];
171 		state[14] = rijndael_inv_sbox[j];
172 
173 		/* Fourth row: -3 shift, 3 7 11 15 */
174 		j = state[3];
175 		state[3] = rijndael_inv_sbox[state[7]];
176 		state[7] = rijndael_inv_sbox[state[11]];
177 		state[11] = rijndael_inv_sbox[state[15]];
178 		state[15] = rijndael_inv_sbox[j];
179 
180 		/* get_inv_round_key(i); */
181 
182 		for (j = 15; j > 3; j--)
183 			round_key[j] ^= round_key[j - 4];
184 
185 		round_key[0] ^= (RC[NUMBER_OF_ROUNDS - i] ^
186 		    rijndael_sbox[round_key[13]]);
187 
188 		round_key[1] ^= rijndael_sbox[round_key[14]];
189 		round_key[2] ^= rijndael_sbox[round_key[15]];
190 		round_key[3] ^= rijndael_sbox[round_key[12]];
191 
192 		for (j = 0; j < 16; j++)
193 			state[j] ^= round_key[j];
194 		if (i != NUMBER_OF_ROUNDS) {
195 			/* inv_mix_column(); */
196 
197 			for (j = 0; j < 16; j += 4) {
198 				k1 = state[j] ^ state[j + 2];
199 				a02x = xtime(k1);
200 				k2 = state[j + 1] ^ state[j + 3];
201 				a13x = xtime(k2);
202 
203 				k1 ^= (k2 ^ xtime(state[j + 1] ^ state[j + 2]));
204 				k2 = k1;
205 
206 				a02xx = xtime(a02x);
207 				a13xx = xtime(a13x);
208 
209 
210 				k1 ^= (xtime(a02xx ^ a13xx) ^ a02xx);
211 				k2 ^= (xtime(a02xx ^ a13xx) ^ a13xx);
212 
213 				state[j] ^= (k1 ^ a02x);
214 				state[j + 1] ^= k2;
215 				state[j + 2] ^= (k1 ^ a13x);
216 				state[j + 3] ^= (k2 ^ a02x ^ a13x);
217 			}
218 		}
219 
220 	}
221 }
222 
223 uint16_t
yubikey_crc16(const uint8_t * buf,size_t buf_size)224 yubikey_crc16(const uint8_t *buf, size_t buf_size)
225 {
226 	uint16_t m_crc = 0xffff;
227 
228 	while (buf_size--) {
229 		int i, j;
230 
231 		m_crc ^= (uint8_t)*buf++ & 0xFF;
232 		for (i = 0; i < 8; i++) {
233 			j = m_crc & 1;
234 			m_crc >>= 1;
235 			if (j)
236 				m_crc ^= 0x8408;
237 		}
238 	}
239 	return m_crc;
240 }
241 
242 static const char hex_trans[] = "0123456789abcdef";
243 
244 void
yubikey_hex_encode(char * dst,const char * src,size_t srcSize)245 yubikey_hex_encode(char *dst, const char *src, size_t srcSize)
246 {
247 	while (srcSize--) {
248 		*dst++ = hex_trans[(*src >> 4) & 0xf];
249 		*dst++ = hex_trans[*src++ & 0xf];
250 	}
251 	*dst = '\0';
252 }
253 
254 void
yubikey_hex_decode(char * dst,const char * src,size_t dstSize)255 yubikey_hex_decode(char *dst, const char *src, size_t dstSize)
256 {
257 	char b;
258 	int flag = 0;
259 	char *p1;
260 
261 	for (; *src && dstSize > 0; src++) {
262 		p1 = strchr(hex_trans, tolower((unsigned char)*src));
263 		if (p1 == NULL)
264 			b = 0;
265 		else
266 			b = (char)(p1 - hex_trans);
267 		if ((flag = !flag))
268 			*dst = b;
269 		else {
270 			*dst = (*dst << 4) | b;
271 			dst++;
272 			dstSize--;
273 		}
274 	}
275 	while (dstSize--)
276 		*dst++ = 0;
277 }
278 
279 static const char modhex_trans[] = "cbdefghijklnrtuv";
280 
281 void
yubikey_modhex_decode(char * dst,const char * src,size_t dstSize)282 yubikey_modhex_decode(char *dst, const char *src, size_t dstSize)
283 {
284 	char b;
285 	int flag = 0;
286 	char *p1;
287 
288 	for (; *src && dstSize > 0; src++) {
289 		p1 = strchr(modhex_trans, tolower((unsigned char)*src));
290 		if (p1 == NULL)
291 			b = 0;
292 		else
293 			b = (char)(p1 - modhex_trans);
294 
295 		if ((flag = !flag))
296 			*dst = b;
297 		else {
298 			*dst = (*dst << 4) | b;
299 			dst++;
300 			dstSize--;
301 		}
302 	}
303 	while (dstSize--)
304 		*dst++ = 0;
305 }
306 
307 uint8_t
yubikey_keymap_decode(wchar_t * wpassword,char * token,int index)308 yubikey_keymap_decode(wchar_t *wpassword, char *token, int index)
309 {
310 	int c, j, found;
311 	for (j=0; j<YUBIKEY_TOKEN_SIZE; j++) {
312 		found = 0;
313 		for (c=0; c<16; c++) {
314 			if (wpassword[j] == keymaps[index][c]) {
315 				token[j] = modhex_trans[c];
316 				found++;
317 				break;
318 			}
319 		}
320 		if (found == 0)
321 			return 1;
322 	}
323 	return 0;
324 }
325 
326 int
yubikey_parse(const uint8_t * password,const uint8_t key[YUBIKEY_KEY_SIZE],yubikey_token_t out,int index)327 yubikey_parse(const uint8_t *password, const uint8_t key[YUBIKEY_KEY_SIZE],
328     yubikey_token_t out, int index)
329 {
330 	wchar_t *wpassword, *pp;
331 	char token[YUBIKEY_TOKEN_SIZE + 1], *lc_ctype;
332 	size_t len;
333 	int rc = 0;
334 
335 	if (index < 0 || index >= YUBIKEY_KEYMAP_COUNT)
336 		return -1;
337 
338 	len = strlen(password);
339 	pp = wpassword = reallocarray(NULL, len + 1, sizeof(wchar_t));
340 	if (pp == NULL)
341 		return ENOMEM;
342 
343 	memset(out, 0, sizeof(*out));
344 	memset(token, 0, YUBIKEY_TOKEN_SIZE + 1);
345 
346 	lc_ctype = getenv("LC_CTYPE");
347 	setlocale(LC_CTYPE, lc_ctype ? lc_ctype : "C.UTF-8");
348 	len = mbstowcs(wpassword, password, len);
349 	if (len == (size_t)-1) {
350 		rc = errno;
351 		goto ret;
352 	}
353 	setlocale(LC_CTYPE, "C");
354 
355 	if (len > YUBIKEY_TOKEN_SIZE)
356 		pp = pp + len - YUBIKEY_TOKEN_SIZE;
357 	if (len < YUBIKEY_TOKEN_SIZE) {
358 		rc = EMSGSIZE;
359 		goto ret;
360 	}
361 
362 	if (yubikey_keymap_decode(pp, token, index)) {
363 		rc = EINVAL;
364 		goto ret;
365 	}
366 	yubikey_modhex_decode((void *)out, token, sizeof(*out));
367 	yubikey_aes_decrypt((void *)out, key);
368 
369 ret:
370 	freezero(wpassword, (len + 1) * sizeof(wchar_t));
371 	return rc;
372 }
373