]> err.no Git - yubikey-personalization.old/blob - ykchalresp.c
Merge tag 'v1.6.4'
[yubikey-personalization.old] / ykchalresp.c
1 /* -*- mode:C; c-file-style: "bsd" -*- */
2 /*
3  * Copyright (c) 2011-2012 Yubico AB.
4  * All rights reserved.
5  *
6  * Author : Fredrik Thulin <fredrik@yubico.com>
7  *
8  * Some basic code copied from ykpersonalize.c.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions are
12  * met:
13  *
14  *     * Redistributions of source code must retain the above copyright
15  *       notice, this list of conditions and the following disclaimer.
16  *
17  *     * Redistributions in binary form must reproduce the above
18  *       copyright notice, this list of conditions and the following
19  *       disclaimer in the documentation and/or other materials provided
20  *       with the distribution.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34
35 #include <stdio.h>
36 #include <unistd.h>
37
38 #include <ykpers.h>
39 #include <yubikey.h>
40 #include <ykdef.h>
41
42 const char *usage =
43         "Usage: ykchalresp [options] challenge\n"
44         "\n"
45         "Options :\n"
46         "\n"
47         "\t-1        Send challenge to slot 1. This is the default.\n"
48         "\t-2        Send challenge to slot 2.\n"
49         "\t-H        Send a 64 byte HMAC challenge. This is the default.\n"
50         "\t-Y        Send a 6 byte Yubico challenge.\n"
51         "\t-N        Abort if Yubikey requires button press.\n"
52         "\t-x        Challenge is hex encoded.\n"
53         "\n"
54         "\t-v        verbose\n"
55         "\t-h        help (this text)\n"
56         "\n"
57         "\n"
58         ;
59 const char *optstring = "12xvhHYN";
60
61 static void report_yk_error()
62 {
63         if (ykp_errno)
64                 fprintf(stderr, "Yubikey personalization error: %s\n",
65                         ykp_strerror(ykp_errno));
66         if (yk_errno) {
67                 if (yk_errno == YK_EUSBERR) {
68                         fprintf(stderr, "USB error: %s\n",
69                                 yk_usb_strerror());
70                 } else {
71                         fprintf(stderr, "Yubikey core error: %s\n",
72                                 yk_strerror(yk_errno));
73                 }
74         }
75 }
76
77 int parse_args(int argc, char **argv,
78                int *slot, bool *verbose,
79                unsigned char **challenge, unsigned int *challenge_len,
80                bool *hmac, bool *may_block,
81                int *exit_code)
82 {
83         int c;
84         bool hex_encoded = false;
85
86         while((c = getopt(argc, argv, optstring)) != -1) {
87                 switch (c) {
88                 case '1':
89                         *slot = 1;
90                         break;
91                 case '2':
92                         *slot = 2;
93                         break;
94                 case 'H':
95                         *hmac = true;
96                         break;
97                 case 'N':
98                         *may_block = false;
99                         break;
100                 case 'Y':
101                         *hmac = false;
102                         break;
103                 case 'x':
104                         hex_encoded = true;
105                         break;
106                 case 'v':
107                         *verbose = true;
108                         break;
109                 case 'h':
110                 default:
111                         fputs(usage, stderr);
112                         *exit_code = 0;
113                         return 0;
114                 }
115         }
116
117         if (optind >= argc) {
118                 /* No challenge */
119                 fputs(usage, stderr);
120                 return 0;
121         }
122
123         if (hex_encoded) {
124                 static unsigned char decoded[SHA1_MAX_BLOCK_SIZE];
125                 int decoded_len;
126
127                 int strl = strlen(argv[optind]);
128
129                 if (strl > sizeof(decoded) * 2) {
130                         fprintf(stderr, "Hex-encoded challenge too long (max %lu chars)\n",
131                                 sizeof(decoded) * 2);
132                         return 0;
133                 }
134
135                 if (strl % 2 != 0) {
136                         fprintf(stderr, "Odd number of characters in hex-encoded challenge\n");
137                         return 0;
138                 }
139
140                 memset(decoded, 0, sizeof(decoded));
141
142                 if (yubikey_hex_p(argv[optind])) {
143                         yubikey_hex_decode((char *)decoded, argv[optind], sizeof(decoded));
144                 } else {
145                         fprintf(stderr, "Bad hex-encoded string '%s'\n", argv[optind]);
146                         return 0;
147                 }
148                 *challenge = (unsigned char *) &decoded;
149                 *challenge_len = strl / 2;
150         } else {
151                 *challenge = (unsigned char *) argv[optind];
152                 *challenge_len = strlen(argv[optind]);
153         }
154
155         return 1;
156 }
157
158 int check_firmware(YK_KEY *yk, bool verbose)
159 {
160         YK_STATUS *st = ykds_alloc();
161
162         if (!yk_get_status(yk, st)) {
163                 free(st);
164                 return 0;
165         }
166
167         if (verbose) {
168                 printf("Firmware version %d.%d.%d\n",
169                        ykds_version_major(st),
170                        ykds_version_minor(st),
171                        ykds_version_build(st));
172                 fflush(stdout);
173         }
174
175         if (ykds_version_major(st) < 2 ||
176             (ykds_version_major(st) == 2
177              && ykds_version_minor(st) < 2)) {
178                 fprintf(stderr, "Challenge-response not supported before YubiKey 2.2.\n");
179                 free(st);
180                 return 0;
181         }
182
183         free(st);
184         return 1;
185 }
186
187 int challenge_response(YK_KEY *yk, int slot,
188                        unsigned char *challenge, unsigned int len,
189                        bool hmac, bool may_block, bool verbose)
190 {
191         unsigned char response[64];
192         unsigned char output_buf[(SHA1_MAX_BLOCK_SIZE * 2) + 1];
193         int yk_cmd;
194         unsigned int flags = 0;
195         unsigned int response_len = 0;
196         unsigned int expect_bytes = 0;
197
198         memset(response, 0, sizeof(response));
199
200         if (may_block)
201                 flags |= YK_FLAG_MAYBLOCK;
202
203         if (verbose) {
204                 fprintf(stderr, "Sending %i bytes %s challenge to slot %i\n", len, (hmac == true)?"HMAC":"Yubico", slot);
205                 //_yk_hexdump(challenge, len);
206         }
207
208         switch(slot) {
209         case 1:
210                 yk_cmd = (hmac == true) ? SLOT_CHAL_HMAC1 : SLOT_CHAL_OTP1;
211                 break;
212         case 2:
213                 yk_cmd = (hmac == true) ? SLOT_CHAL_HMAC2 : SLOT_CHAL_OTP2;
214                 break;
215         }
216
217         if (!yk_write_to_key(yk, yk_cmd, challenge, len))
218                 return 0;
219
220         if (verbose) {
221                 fprintf(stderr, "Reading response...\n");
222         }
223
224         /* HMAC responses are 160 bits, Yubico 128 */
225         expect_bytes = (hmac == true) ? 20 : 16;
226
227         if (! yk_read_response_from_key(yk, slot, flags,
228                                         &response, sizeof(response),
229                                         expect_bytes,
230                                         &response_len))
231                 return 0;
232
233         if (hmac && response_len > 20)
234                 response_len = 20;
235         if (! hmac && response_len > 16)
236                 response_len = 16;
237
238         memset(output_buf, 0, sizeof(output_buf));
239         if (hmac) {
240                 yubikey_hex_encode((char *)output_buf, (char *)response, response_len);
241         } else {
242                 yubikey_modhex_encode((char *)output_buf, (char *)response, response_len);
243         }
244         printf("%s\n", output_buf);
245
246         return 1;
247 }
248
249 int main(int argc, char **argv)
250 {
251         YK_KEY *yk = 0;
252         bool error = true;
253         int exit_code = 0;
254
255         /* Options */
256         bool verbose = false;
257         bool hex_encoded = false;
258         bool hmac = true;
259         bool may_block = true;
260         unsigned char *challenge;
261         unsigned int challenge_len;
262         int slot = 1;
263
264         ykp_errno = 0;
265         yk_errno = 0;
266
267         if (! parse_args(argc, argv,
268                          &slot, &verbose,
269                          &challenge, &challenge_len,
270                          &hmac, &may_block,
271                          &exit_code))
272                 goto err;
273
274         if (!yk_init()) {
275                 exit_code = 1;
276                 goto err;
277         }
278
279         if (!(yk = yk_open_first_key())) {
280                 exit_code = 1;
281                 goto err;
282         }
283
284         if (! check_firmware(yk, verbose)) {
285                 exit_code = 1;
286                 goto err;
287         }
288
289         if (! challenge_response(yk, slot,
290                                  challenge, challenge_len,
291                                  hmac, may_block, verbose)) {
292                 exit_code = 1;
293                 goto err;
294         }
295
296         exit_code = 0;
297         error = false;
298
299 err:
300         if (error || exit_code != 0) {
301                 report_yk_error();
302         }
303
304         if (yk && !yk_close_key(yk)) {
305                 report_yk_error();
306                 exit_code = 2;
307         }
308
309         if (!yk_release()) {
310                 report_yk_error();
311                 exit_code = 2;
312         }
313
314         exit(exit_code);
315 }