]> err.no Git - yubikey-personalization/blob - ykpers-args.c
Merge tag 'v1.15.1'
[yubikey-personalization] / ykpers-args.c
1 /* -*- mode:C; c-file-style: "bsd" -*- */
2 /*
3  * Copyright (c) 2008-2014 Yubico AB
4  * Copyright (c) 2010 Tollef Fog Heen <tfheen@err.no>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are
9  * met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *
14  *     * Redistributions in binary form must reproduce the above
15  *       copyright notice, this list of conditions and the following
16  *       disclaimer in the documentation and/or other materials provided
17  *       with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <stddef.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <errno.h>
39
40 #include <ykpers.h>
41 #include <yubikey.h> /* To get yubikey_modhex_encode and yubikey_hex_encode */
42 #include <ykdef.h>
43 #include <ykpers-version.h>
44 #include "ykpers-args.h"
45
46 #define YUBICO_OATH_VENDOR_ID_HEX       0xe1    /* UB as hex */
47 #define YUBICO_HOTP_EVENT_TOKEN_TYPE    0x63    /* HE as hex */
48
49 const char *usage =
50 "Usage: ykpersonalize [options]\n"
51 "-u        update configuration without overwriting.  This is only available\n"
52 "          in YubiKey 2.3 and later.  EXTFLAG_ALLOW_UPDATE will be set by\n"
53 "          default\n"
54 "-1        change the first configuration.  This is the default and\n"
55 "          is normally used for true OTP generation.\n"
56 "          In this configuration, TKTFLAG_APPEND_CR is set by default.\n"
57 "-2        change the second configuration.  This is for Yubikey II only\n"
58 "          and is then normally used for static key generation.\n"
59 "          In this configuration, TKTFLAG_APPEND_CR, CFGFLAG_STATIC_TICKET,\n"
60 "          CFGFLAG_STRONG_PW1, CFGFLAG_STRONG_PW2 and CFGFLAG_MAN_UPDATE\n"
61 "          are set by default.\n"
62 "-x        swap the configuration in slot 1 and 2.  This is for YubiKey 2.3\n"
63 "          and newer only\n"
64 "-z        delete the configuration in slot 1 or 2.\n"
65 "-sFILE    save configuration to FILE instead of key.\n"
66 "          (if FILE is -, send to stdout)\n"
67 "-iFILE    read configuration from FILE.\n"
68 "          (if FILE is -, read from stdin)\n"
69 "-fformat  set the data format for -s and -i valid values are ycfg or legacy.\n"
70 "-a[XXX..] The AES secret key as a 32 (or 40 for OATH-HOTP/HMAC CHAL-RESP)\n"
71 "          char hex value (not modhex) (none to prompt for key on stdin)\n"
72 "          If -a is not used a random key will be generated.\n"
73 "-cXXX..   A 12 char hex value (not modhex) to use as access code for programming\n"
74 "          (this does NOT SET the access code, that's done with -oaccess=)\n"
75 "-nXXX..   Write NDEF URI to YubiKey NEO, must be used with -1 or -2\n"
76 "-tXXX..   Write NDEF text to YubiKey NEO, must be used with -1 or -2\n"
77 "-mMODE    Set the USB device configuration of the YubiKey NEO.\n"
78 "          See the manpage for details\n"
79 "-S0605..  Set the scanmap to use with the YubiKey NEO. Must be 45 unique bytes,\n"
80 "          in hex.  Use with no argument to reset to the default.\n"
81 "-oOPTION  change configuration option.  Possible OPTION arguments are:\n"
82 "          fixed=xxxxxxxxxxx   The public identity of key, in MODHEX.\n"
83 "                              This is 0-16 characters long.\n"
84 "          uid=xxxxxx          The uid part of the generated ticket, in HEX.\n"
85 "                              MUST be 12 characters long.\n"
86 "          access=xxxxxxxxxxx  New access code to set, in HEX.\n"
87 "                              MUST be 12 characters long.\n"
88 "          oath-imf=IMF        OATH Initial Moving Factor to use.\n"
89 "          oath-id[=h:OOTT...] OATH Token Identifier (none for serial-based)\n"
90 "\n"
91 "          Ticket flags for all firmware versions:\n"
92 "          [-]tab-first           set/clear TAB_FIRST\n"
93 "          [-]append-tab1         set/clear APPEND_TAB1\n"
94 "          [-]append-tab2         set/clear APPEND_TAB2\n"
95 "          [-]append-delay1       set/clear APPEND_DELAY1\n"
96 "          [-]append-delay2       set/clear APPEND_DELAY2\n"
97 "          [-]append-cr           set/clear APPEND_CR\n"
98 "\n"
99 "          Ticket flags for firmware version 2.0 and above:\n"
100 "          [-]protect-cfg2        set/clear PROTECT_CFG2\n"
101 "\n"
102 "          Ticket flags for firmware version 2.1 and above:\n"
103 "          [-]oath-hotp           set/clear OATH_HOTP\n"
104 "\n"
105 "          Ticket flags for firmware version 2.2 and above:\n"
106 "          [-]chal-resp           set/clear CHAL_RESP\n"
107 "\n"
108 "          Configuration flags for all firmware versions:\n"
109 "          [-]send-ref            set/clear SEND_REF\n"
110 "          [-]pacing-10ms         set/clear PACING_10MS\n"
111 "          [-]pacing-20ms         set/clear PACING_20MS\n"
112 "          [-]static-ticket       set/clear STATIC_TICKET\n"
113 "\n"
114 "          Configuration flags for firmware version 1.x only:\n"
115 "          [-]ticket-first        set/clear TICKET_FIRST\n"
116 "          [-]allow-hidtrig       set/clear ALLOW_HIDTRIG\n"
117 "\n"
118 "          Configuration flags for firmware version 2.0 and above:\n"
119 "          [-]short-ticket        set/clear SHORT_TICKET\n"
120 "          [-]strong-pw1          set/clear STRONG_PW1\n"
121 "          [-]strong-pw2          set/clear STRONG_PW2\n"
122 "          [-]man-update          set/clear MAN_UPDATE\n"
123 "\n"
124 "          Configuration flags for firmware version 2.1 and above:\n"
125 "          [-]oath-hotp8          set/clear OATH_HOTP8\n"
126 "          [-]oath-fixed-modhex1  set/clear OATH_FIXED_MODHEX1\n"
127 "          [-]oath-fixed-modhex2  set/clear OATH_FIXED_MODHEX2\n"
128 "          [-]oath-fixed-modhex   set/clear OATH_MODHEX\n"
129 "\n"
130 "          Configuration flags for firmware version 2.2 and above:\n"
131 "          [-]chal-yubico         set/clear CHAL_YUBICO\n"
132 "          [-]chal-hmac           set/clear CHAL_HMAC\n"
133 "          [-]hmac-lt64           set/clear HMAC_LT64\n"
134 "          [-]chal-btn-trig       set/clear CHAL_BTN_TRIG\n"
135 "\n"
136 "          Extended flags for firmware version 2.2 and above:\n"
137 "          [-]serial-btn-visible  set/clear SERIAL_BTN_VISIBLE\n"
138 "          [-]serial-usb-visible  set/clear SERIAL_USB_VISIBLE\n"
139 "          [-]serial-api-visible  set/clear SERIAL_API_VISIBLE\n"
140 "\n"
141 "          Extended flags for firmware version 2.3 and above:\n"
142 "          [-]use-numeric-keypad  set/clear USE_NUMERIC_KEYPAD\n"
143 "          [-]fast-trig           set/clear FAST_TRIG\n"
144 "          [-]allow-update        set/clear ALLOW_UPDATE\n"
145 "          [-]dormant             set/clear DORMANT\n"
146 "\n"
147 "          Extended flags for firmware version 2.4/3.1 and above:\n"
148 "          [-]led-inv             set/clear LED_INV\n"
149 "\n"
150 "-y        always commit (do not prompt)\n"
151 "\n"
152 "-d        dry-run (don't write anything to key)\n"
153 "\n"
154 "-v        verbose\n"
155 "-V        tool version\n"
156 "-h        help (this text)\n"
157 ;
158 const char *optstring = "u12xza::c:n:t:hi:o:s:f:dvym:S::V";
159
160 static int _set_fixed(char *opt, YKP_CONFIG *cfg);
161 static int _format_decimal_as_hex(uint8_t *dst, size_t dst_len, uint8_t *src);
162 static int _format_oath_id(uint8_t *dst, size_t dst_len, uint8_t vendor, uint8_t type, uint32_t mui);
163 static int _set_oath_id(char *opt, YKP_CONFIG *cfg, YK_KEY *yk, YK_STATUS *st);
164
165 static int hex_modhex_decode(unsigned char *result, size_t *resultlen,
166                              const char *str, size_t strl,
167                              size_t minsize, size_t maxsize,
168                              bool primarily_modhex)
169 {
170         if (strl >= 2) {
171                 if (strncmp(str, "m:", 2) == 0
172                     || strncmp(str, "M:", 2) == 0) {
173                         str += 2;
174                         strl -= 2;
175                         primarily_modhex = true;
176                 } else if (strncmp(str, "h:", 2) == 0
177                            || strncmp(str, "H:", 2) == 0) {
178                         str += 2;
179                         strl -= 2;
180                         primarily_modhex = false;
181                 }
182         }
183
184         if ((strl % 2 != 0) || (strl < minsize) || (strl > maxsize)) {
185                 return -1;
186         }
187
188         *resultlen = strl / 2;
189         if (primarily_modhex) {
190                 if (yubikey_modhex_p(str)) {
191                         yubikey_modhex_decode((char *)result, str, strl);
192                         return 1;
193                 }
194         } else {
195                 if (yubikey_hex_p(str)) {
196                         yubikey_hex_decode((char *)result, str, strl);
197                         return 1;
198                 }
199         }
200
201         return 0;
202 }
203
204 void report_yk_error(void)
205 {
206         if (ykp_errno)
207                 fprintf(stderr, "Yubikey personalization error: %s\n",
208                         ykp_strerror(ykp_errno));
209         if (yk_errno) {
210                 if (yk_errno == YK_EUSBERR) {
211                         fprintf(stderr, "USB error: %s\n",
212                                 yk_usb_strerror());
213                 } else {
214                         fprintf(stderr, "Yubikey core error: %s\n",
215                                 yk_strerror(yk_errno));
216                 }
217         }
218 }
219
220 /*
221  * Parse all arguments supplied to this program and turn it into mainly
222  * a YKP_CONFIG (but return some other parameters as well, like
223  * access_code, verbose etc.).
224  *
225  * Done in this way to be testable (see tests/test_args_to_config.c).
226  */
227 int args_to_config(int argc, char **argv, YKP_CONFIG *cfg, YK_KEY *yk,
228                    const char **infname, const char **outfname,
229                    int *data_format, bool *autocommit,
230                    YK_STATUS *st, bool *verbose, bool *dry_run,
231                    unsigned char *access_code, bool *use_access_code,
232                    char *keylocation, char *ndef_type, char *ndef,
233                    unsigned char *usb_mode, bool *zap,
234                    unsigned char *scan_bin, unsigned char *cr_timeout,
235                    unsigned char *autoeject_timeout, int *num_modes_seen,
236                    int *exit_code)
237 {
238         int c;
239         const char *aeshash = NULL;
240         bool new_access_code = false;
241         bool slot_chosen = false;
242         bool mode_chosen = false;
243         bool option_seen = false;
244         bool swap_seen = false;
245         bool update_seen = false;
246         bool ndef_seen = false;
247         bool usb_mode_seen = false;
248         bool scan_map_seen = false;
249
250         ykp_configure_version(cfg, st);
251
252         while((c = getopt(argc, argv, optstring)) != -1) {
253                 if (c == 'o') {
254                         if (strcmp(optarg, "oath-hotp") == 0 ||
255                             strcmp(optarg, "chal-resp") == 0) {
256                                 if (mode_chosen) {
257                                         fprintf(stderr, "You may only choose mode (-ooath-hotp / "
258                                                 "-ochal-resp) once.\n");
259                                         *exit_code = 1;
260                                         return 0;
261                                 }
262
263                                 if (option_seen) {
264                                         fprintf(stderr, "Mode choosing flags (oath-hotp / chal-resp) "
265                                                 "must be set prior to any other options (-o).\n");
266                                         *exit_code = 1;
267                                         return 0;
268                                 }
269
270                                 /* The default flags (particularly for slot 2) does not apply to
271                                  * these new modes of operation found in Yubikey >= 2.1. Therefor,
272                                  * we reset them here and, as a consequence of that, require the
273                                  * mode choosing options to be specified before any other.
274                                  */
275                                 ykp_clear_config(cfg);
276
277                                 mode_chosen = 1;
278                         }
279
280                         option_seen = true;
281                 }
282
283                 switch (c) {
284                 case 'u':
285                         if (slot_chosen) {
286                                 fprintf(stderr, "You must use update before slot (-1 / -2).\n");
287                                 *exit_code = 1;
288                                 return 0;
289                         }
290                         if (swap_seen) {
291                                 fprintf(stderr, "Update (-u) and swap (-x) can't be combined.\n");
292                                 *exit_code = 1;
293                                 return 0;
294                         }
295                         if (ndef_seen) {
296                                 fprintf(stderr, "Update (-u) can not be combined with ndef (-n).\n");
297                                 *exit_code = 1;
298                                 return 0;
299                         }
300                         update_seen = true;
301                         break;
302                 case '1':
303                 case '2': {
304                                 int command;
305                                 if (slot_chosen) {
306                                         fprintf(stderr, "You may only choose slot (-1 / -2) once.\n");
307                                         *exit_code = 1;
308                                         return 0;
309                                 }
310                                 if (option_seen) {
311                                         fprintf(stderr, "You must choose slot before any options (-o).\n");
312                                         *exit_code = 1;
313                                         return 0;
314                                 }
315                                 if (swap_seen) {
316                                         fprintf(stderr, "You can not combine slot swap (-x) with configuring a slot.\n");
317                                         *exit_code = 1;
318                                         return 0;
319                                 }
320                                 ykp_set_tktflag_APPEND_CR(cfg, true);
321                                 if (update_seen) {
322                                         ykp_set_extflag_ALLOW_UPDATE(cfg, true);
323                                         if(c == '1') {
324                                                 command = SLOT_UPDATE1;
325                                         } else if(c == '2') {
326                                                 command = SLOT_UPDATE2;
327                                         }
328                                 } else if (c == '1') {
329                                         command = SLOT_CONFIG;
330                                 } else if (c == '2') {
331                                         command = SLOT_CONFIG2;
332                                         ykp_set_cfgflag_STATIC_TICKET(cfg, true);
333                                         ykp_set_cfgflag_STRONG_PW1(cfg, true);
334                                         ykp_set_cfgflag_STRONG_PW2(cfg, true);
335                                         ykp_set_cfgflag_MAN_UPDATE(cfg, true);
336
337                                 }
338                                 if (!ykp_configure_command(cfg, command))
339                                         return 0;
340                                 slot_chosen = true;
341                                 break;
342                         }
343                 case 'x':
344                         if (slot_chosen || option_seen || update_seen || ndef_seen || *zap || usb_mode_seen || scan_map_seen) {
345                                 fprintf(stderr, "Slot swap (-x) can not be used with other options.\n");
346                                 *exit_code = 1;
347                                 return 0;
348                         }
349
350                         if (!ykp_configure_command(cfg, SLOT_SWAP)) {
351                                 return 0;
352                         }
353                         swap_seen = true;
354                         break;
355                 case 'z':
356                         if (swap_seen || update_seen || ndef_seen || usb_mode_seen || scan_map_seen) {
357                                 fprintf(stderr, "Zap (-z) can only be used with a slot (-1 / -2).\n");
358                                 *exit_code = 1;
359                                 return 0;
360                         }
361                         *zap = true;
362                         break;
363                 case 'i':
364                         *infname = optarg;
365                         break;
366                 case 's':
367                         *outfname = optarg;
368                         break;
369                 case 'f':
370                         if(strcmp(optarg, "ycfg") == 0) {
371                                 *data_format = YKP_FORMAT_YCFG;
372                         } else if(strcmp(optarg, "legacy") == 0) {
373                                 *data_format = YKP_FORMAT_LEGACY;
374                         } else {
375                                 fprintf(stderr, "The only valid formats to -f is ycfg and legacy.\n");
376                                 *exit_code = 1;
377                                 return 0;
378                         }
379                         break;
380                 case 'a':
381                         if(optarg) {
382                                 aeshash = optarg;
383                                 *keylocation = 1;
384                         } else {
385                                 *keylocation = 2;
386                         }
387                         break;
388                 case 'c': {
389                         size_t access_code_len = 0;
390                         int rc = hex_modhex_decode(access_code, &access_code_len,
391                                                    optarg, strlen(optarg),
392                                                    12, 12, false);
393                         if (rc <= 0) {
394                                 fprintf(stderr,
395                                         "Invalid access code string: %s\n",
396                                         optarg);
397                                 *exit_code = 1;
398                                 return 0;
399                         }
400                         if (!new_access_code)
401                                 ykp_set_access_code(cfg,
402                                                     access_code,
403                                                     access_code_len);
404                         *use_access_code = true;
405                         break;
406                 }
407                 case 't':
408                         *ndef_type = 'T';
409                 case 'n': {
410                                   int command;
411                                   if(!*ndef_type) {
412                                           *ndef_type = 'U';
413                                   }
414                                   if (swap_seen || update_seen || option_seen || *zap || usb_mode_seen || scan_map_seen) {
415                                           fprintf(stderr, "Ndef (-n/-t) can only be used with a slot (-1/-2).\n");
416                                           *exit_code = 1;
417                                           return 0;
418                                   }
419                                   if(ykp_command(cfg) == SLOT_CONFIG) {
420                                           command = SLOT_NDEF;
421                                   } else if(ykp_command(cfg) == SLOT_CONFIG2) {
422                                           command = SLOT_NDEF2;
423                                   } else {
424                                           command = SLOT_NDEF;
425                                   }
426                                   if (!ykp_configure_command(cfg, command)) {
427                                           return 0;
428                                   }
429                                   memcpy(ndef, optarg, strlen(optarg));
430                                   ndef_seen = true;
431                                   break;
432                           }
433                 case 'm':
434                         if(slot_chosen || swap_seen || update_seen || option_seen || ndef_seen || *zap || scan_map_seen) {
435                                 fprintf(stderr, "USB mode (-m) can not be combined with other options.\n");
436                                 *exit_code = 1;
437                                 return 0;
438                         }
439                         unsigned char mode, crtime, autotime;
440                         int matched = sscanf(optarg, "%hhx:%hhd:%hhd", &mode, &crtime, &autotime);
441                         if(matched > 0) {
442                                 *usb_mode = mode;
443                                 if(matched > 1) {
444                                         *cr_timeout = crtime;
445                                         if(matched > 2) {
446                                                 *autoeject_timeout = autotime;
447                                         }
448                                 }
449                                 usb_mode_seen = true;
450                                 *num_modes_seen = matched;
451                         } else {
452                                 fprintf(stderr, "Invalid USB operation mode.\n");
453                                 *exit_code = 1;
454                                 return 0;
455                         }
456                         if (!ykp_configure_command(cfg, SLOT_DEVICE_CONFIG))
457                                 return 0;
458
459                         break;
460                 case 'S':
461                         {
462                                 size_t scanlength = strlen(SCAN_MAP);
463                                 if(slot_chosen || swap_seen || update_seen || option_seen || ndef_seen || *zap || usb_mode_seen) {
464                                         fprintf(stderr, "Scanmap (-S) can not be combined with other options.\n");
465                                         *exit_code = 1;
466                                         return 0;
467                                 }
468                                 if(optarg) {
469                                         size_t scanbinlen;
470                                         size_t scanlen = strlen (optarg);
471                                         int rc = hex_modhex_decode(scan_bin, &scanbinlen,
472                                                         optarg, scanlen,
473                                                         scanlength * 2, scanlength * 2,
474                                                         false);
475
476                                         if (rc <= 0) {
477                                                 fprintf(stderr,
478                                                                 "Invalid scanmap string %s\n",
479                                                                 optarg);
480                                                 *exit_code = 1;
481                                                 return 0;
482                                         }
483                                 } else {
484                                         memset(scan_bin, 0, scanlength);
485                                 }
486                                 scan_map_seen = true;
487                         }
488                         if (!ykp_configure_command(cfg, SLOT_SCAN_MAP))
489                                 return 0;
490                         break;
491                 case 'o':
492                         if (*zap) {
493                                 fprintf(stderr, "No options can be given with zap (-z).\n");
494                                 *exit_code = 1;
495                                 return 0;
496                         }
497                         if (strncmp(optarg, "fixed=", 6) == 0) {
498                                 if (_set_fixed(optarg + 6, cfg) != 1) {
499                                         fprintf(stderr,
500                                                 "Invalid fixed string: %s\n",
501                                                 optarg + 6);
502                                         *exit_code = 1;
503                                         return 0;
504                                 }
505                         }
506                         else if (strncmp(optarg, "uid=", 4) == 0) {
507                                 const char *uid = optarg+4;
508                                 size_t uidlen = strlen (uid);
509                                 unsigned char uidbin[256];
510                                 size_t uidbinlen = 0;
511                                 int rc = hex_modhex_decode(uidbin, &uidbinlen,
512                                                            uid, uidlen,
513                                                            12, 12, false);
514                                 if (rc <= 0) {
515                                         fprintf(stderr,
516                                                 "Invalid uid string: %s\n",
517                                                 uid);
518                                         *exit_code = 1;
519                                         return 0;
520                                 }
521                                 /* for OATH-HOTP and CHAL-RESP, uid is not applicable */
522                                 if (ykp_get_tktflag_OATH_HOTP(cfg) || ykp_get_tktflag_CHAL_RESP(cfg)) {
523                                         fprintf(stderr,
524                                                 "Option uid= not valid with -ooath-hotp or -ochal-resp.\n"
525                                                 );
526                                         *exit_code = 1;
527                                         return 0;
528                                 }
529                                 ykp_set_uid(cfg, uidbin, uidbinlen);
530                         }
531                         else if (strncmp(optarg, "access=", 7) == 0) {
532                                 const char *acc = optarg+7;
533                                 size_t acclen = strlen (acc);
534                                 unsigned char accbin[256];
535                                 size_t accbinlen = 0;
536                                 int rc = hex_modhex_decode (accbin, &accbinlen,
537                                                             acc, acclen,
538                                                             12, 12, false);
539                                 if (rc <= 0) {
540                                         fprintf(stderr,
541                                                 "Invalid access code string: %s\n",
542                                                 acc);
543                                         *exit_code = 1;
544                                         return 0;
545                                 }
546                                 ykp_set_access_code(cfg, accbin, accbinlen);
547                                 new_access_code = true;
548                         }
549 #define TKTFLAG(o, f)                                                   \
550                         else if (strcmp(optarg, o) == 0) {              \
551                                 if (!ykp_set_tktflag_##f(cfg, true)) {  \
552                                         *exit_code = 1;                 \
553                                         return 0;               \
554                                 }                                       \
555                         } else if (strcmp(optarg, "-" o) == 0) {        \
556                                 if (! ykp_set_tktflag_##f(cfg, false)) { \
557                                         *exit_code = 1;                 \
558                                         return 0;               \
559                                 }                                       \
560                         }
561                         TKTFLAG("tab-first", TAB_FIRST)
562                         TKTFLAG("append-tab1", APPEND_TAB1)
563                         TKTFLAG("append-tab2", APPEND_TAB2)
564                         TKTFLAG("append-delay1", APPEND_DELAY1)
565                         TKTFLAG("append-delay2", APPEND_DELAY2)
566                         TKTFLAG("append-cr", APPEND_CR)
567                         TKTFLAG("protect-cfg2", PROTECT_CFG2)
568                         TKTFLAG("oath-hotp", OATH_HOTP)
569                         TKTFLAG("chal-resp", CHAL_RESP)
570 #undef TKTFLAG
571
572 #define CFGFLAG(o, f)                                                   \
573                         else if (strcmp(optarg, o) == 0) {              \
574                                 if (! ykp_set_cfgflag_##f(cfg, true)) { \
575                                         *exit_code = 1;                 \
576                                         return 0;                       \
577                                 }                                       \
578                         } else if (strcmp(optarg, "-" o) == 0) {        \
579                                 if (! ykp_set_cfgflag_##f(cfg, false)) { \
580                                         *exit_code = 1;                 \
581                                         return 0;                       \
582                                 }                                       \
583                         }
584                         CFGFLAG("send-ref", SEND_REF)
585                         CFGFLAG("ticket-first", TICKET_FIRST)
586                         CFGFLAG("pacing-10ms", PACING_10MS)
587                         CFGFLAG("pacing-20ms", PACING_20MS)
588                         CFGFLAG("allow-hidtrig", ALLOW_HIDTRIG)
589                         CFGFLAG("static-ticket", STATIC_TICKET)
590                         CFGFLAG("short-ticket", SHORT_TICKET)
591                         CFGFLAG("strong-pw1", STRONG_PW1)
592                         CFGFLAG("strong-pw2", STRONG_PW2)
593                         CFGFLAG("man-update", MAN_UPDATE)
594                         CFGFLAG("oath-hotp8", OATH_HOTP8)
595                         CFGFLAG("oath-fixed-modhex1", OATH_FIXED_MODHEX1)
596                         CFGFLAG("oath-fixed-modhex2", OATH_FIXED_MODHEX2)
597                         CFGFLAG("oath-fixed-modhex", OATH_FIXED_MODHEX)
598                         CFGFLAG("chal-yubico", CHAL_YUBICO)
599                         CFGFLAG("chal-hmac", CHAL_HMAC)
600                         CFGFLAG("hmac-lt64", HMAC_LT64)
601                         CFGFLAG("chal-btn-trig", CHAL_BTN_TRIG)
602 #undef CFGFLAG
603                         else if (strncmp(optarg, "oath-imf=", 9) == 0) {
604                                 unsigned long imf;
605
606                                 if (!ykp_get_tktflag_OATH_HOTP(cfg)) {
607                                         fprintf(stderr,
608                                                 "Option oath-imf= only valid with -ooath-hotp or -ooath-hotp8.\n"
609                                                 );
610                                         *exit_code = 1;
611                                         return 0;
612                                 }
613
614                                 if (sscanf(optarg+9, "%lu", &imf) != 1 ||
615                                     /* yubikey limitations */
616                                     imf > 65535*16 || imf % 16 != 0) {
617                                         fprintf(stderr,
618                                                 "Invalid value %s for oath-imf=.\n", optarg+9
619                                                 );
620                                         *exit_code = 1;
621                                         return 0;
622                                 }
623                                 if (! ykp_set_oath_imf(cfg, imf)) {
624                                         *exit_code = 1;
625                                         return 0;
626                                 }
627                         }
628                         else if (strncmp(optarg, "oath-id=", 8) == 0 || strcmp(optarg, "oath-id") == 0) {
629                                 if (_set_oath_id(optarg, cfg, yk, st) != 1) {
630                                         *exit_code = 1;
631                                         return 0;
632                                 }
633                         }
634
635 #define EXTFLAG(o, f)                                                   \
636                         else if (strcmp(optarg, o) == 0) {              \
637                                 if (! ykp_set_extflag_##f(cfg, true)) { \
638                                         *exit_code = 1;                 \
639                                         return 0;                       \
640                                 }                                       \
641                         } else if (strcmp(optarg, "-" o) == 0) {        \
642                                 if (! ykp_set_extflag_##f(cfg, false)) { \
643                                         *exit_code = 1;                 \
644                                         return 0;                       \
645                                 }                                       \
646                         }
647                         EXTFLAG("serial-btn-visible", SERIAL_BTN_VISIBLE)
648                         EXTFLAG("serial-usb-visible", SERIAL_USB_VISIBLE)
649                         EXTFLAG("serial-api-visible", SERIAL_API_VISIBLE)
650                         EXTFLAG("use-numeric-keypad", USE_NUMERIC_KEYPAD)
651                         EXTFLAG("fast-trig", FAST_TRIG)
652                         EXTFLAG("allow-update", ALLOW_UPDATE)
653                         EXTFLAG("dormant", DORMANT)
654                         EXTFLAG("led-inv", LED_INV)
655 #undef EXTFLAG
656                         else {
657                                 fprintf(stderr, "Unknown option '%s'\n",
658                                         optarg);
659                                 fputs(usage, stderr);
660                                 *exit_code = 1;
661                                 return 0;
662                         }
663                         break;
664                 case 'd':
665                         *dry_run = true;
666                         break;
667                 case 'v':
668                         *verbose = true;
669                         break;
670                 case 'y':
671                         *autocommit = true;
672                         break;
673                 case 'V':
674                         fputs(YKPERS_VERSION_STRING "\n", stderr);
675                         *exit_code = 0;
676                         return 0;
677                 case 'h':
678                 default:
679                         fputs(usage, stderr);
680                         *exit_code = 0;
681                         return 0;
682                 }
683         }
684
685         if (!slot_chosen && !ndef_seen && !swap_seen && !usb_mode_seen && !scan_map_seen) {
686                 fprintf(stderr, "A slot must be chosen with -1 or -2.\n");
687                 *exit_code = 1;
688                 return 0;
689         }
690
691         if (update_seen) {
692                 struct config_st *core_config = (struct config_st *) ykp_core_config(cfg);
693                 if ((core_config->tktFlags & TKTFLAG_UPDATE_MASK) != core_config->tktFlags) {
694                         fprintf(stderr, "Unallowed ticket flags with update.\n");
695                         *exit_code = 1;
696                         return 0;
697                 }
698                 if ((core_config->cfgFlags & CFGFLAG_UPDATE_MASK) != core_config->cfgFlags) {
699                         fprintf(stderr, "Unallowed cfg flags with update.\n");
700                         *exit_code = 1;
701                         return 0;
702                 }
703                 if ((core_config->extFlags & EXTFLAG_UPDATE_MASK) != core_config->extFlags) {
704                         fprintf(stderr, "Unallowed ext flags with update.\n");
705                         *exit_code = 1;
706                         return 0;
707                 }
708         }
709
710         if (*keylocation == 1) {
711                 bool long_key_valid = ykp_get_supported_key_length(cfg) == 20 ? true : false;
712                 int res = 0;
713
714                 if (long_key_valid && strlen(aeshash) == 40) {
715                         res = ykp_HMAC_key_from_hex(cfg, aeshash);
716                 } else {
717                         res = ykp_AES_key_from_hex(cfg, aeshash);
718                 }
719
720                 if (res) {
721                         fprintf(stderr, "Bad %s key: %s\n", long_key_valid ? "HMAC":"AES", aeshash);
722                         fflush(stderr);
723                         return 0;
724                 }
725         }
726
727         return 1;
728 }
729
730 static int _set_fixed(char *opt, YKP_CONFIG *cfg) {
731         const char *fixed = opt;
732         size_t fixedlen = strlen (fixed);
733         unsigned char fixedbin[256];
734         size_t fixedbinlen = 0;
735         int rc = hex_modhex_decode(fixedbin, &fixedbinlen,
736                                    fixed, fixedlen,
737                                    0, 32, true);
738         if (rc <= 0)
739                 return 0;
740
741         ykp_set_fixed(cfg, fixedbin, fixedbinlen);
742         return 1;
743 }
744
745
746 /* re-format decimal 12345678 into 'hex' 0x12 0x34 0x56 0x78 */
747 static int _format_decimal_as_hex(uint8_t *dst, size_t dst_len, uint8_t *src)
748 {
749         uint8_t *end;
750
751         end = dst + dst_len;
752         while (src[0] && src[1]) {
753                 if (dst >= end)
754                         return 0;
755                 *dst = ((src[0] - '0') * 0x10) + src[1] - '0';
756                 dst++;
757                 src += 2;
758         }
759
760         return 1;
761 }
762
763 /* For details, see YubiKey Manual 2010-09-16 section 5.3.4 - OATH-HOTP Token Identifier */
764 static int _format_oath_id(uint8_t *dst, size_t dst_len, uint8_t vendor, uint8_t type, uint32_t mui)
765 {
766         uint8_t buf[8 + 1];
767
768         if (mui > 99999999)
769                 return 0;
770
771         /* two bytes vendor and token type, and eight bytes MUI */
772         if (dst_len < 2 + 8)
773                 return 0;
774
775         /* Make the YubiKey output the MUI number in decimal */
776         snprintf(buf, sizeof(buf), "%08i", mui);
777
778         dst[0] = vendor;
779         dst[1] = type;
780
781         if (_format_decimal_as_hex(dst + 2, dst_len - 2, buf) != 1)
782                 return 0;
783
784         return 1;
785 }
786
787 static int _set_oath_id(char *opt, YKP_CONFIG *cfg, YK_KEY *yk, YK_STATUS *st) {
788         /* For details, see YubiKey Manual 2010-09-16 section 5.3.4 - OATH-HOTP Token Identifier */
789         if (!ykp_get_tktflag_OATH_HOTP(cfg)) {
790                 fprintf(stderr,
791                         "Option oath-id= only valid with -ooath-hotp or -ooath-hotp8.\n"
792                         );
793                 return 0;
794         }
795         if (! ykp_set_cfgflag_OATH_FIXED_MODHEX2(cfg, true))
796                 return 0;
797         if (! ykp_set_extflag_SERIAL_API_VISIBLE(cfg, true))
798                 return 0;
799
800         if (strlen(opt) > 7) {
801                 if (_set_fixed(opt + 8, cfg) != 1) {
802                         fprintf(stderr,
803                                 "Invalid OATH token identifier %s supplied with oath-id=.\n", opt + 8
804                                 );
805                         return 0;
806                 }
807         } else {
808                 /* No Token Id supplied, try to create one automatically based on
809                  * the serial number of the YubiKey.
810                  */
811                 unsigned int serial;
812                 uint8_t oath_id[12] = {0};
813                 if (ykds_version_major(st) > 2 ||
814                     (ykds_version_major(st) == 2 &&
815                      ykds_version_minor(st) >= 2)) {
816                         if (! yk_get_serial(yk, 0, 0, &serial)) {
817                                 fprintf(stderr,
818                                         "YubiKey refuses reading serial number. "
819                                         "Can't use -ooath-id.\n"
820                                         );
821                                 return 0;
822                         }
823                 } else {
824                         fprintf(stderr,
825                                 "YubiKey %d.%d.%d does not support reading serial number. "
826                                 "Can't use -ooath-id.\n",
827                                 ykds_version_major(st),
828                                 ykds_version_minor(st),
829                                 ykds_version_build(st)
830                                 );
831                         return 0;
832                 }
833
834                 if (_format_oath_id(oath_id, sizeof(oath_id), YUBICO_OATH_VENDOR_ID_HEX,
835                                     YUBICO_HOTP_EVENT_TOKEN_TYPE, serial) != 1) {
836                         fprintf(stderr, "Failed formatting OATH token identifier.\n");
837                         return 0;
838                 }
839
840                 if (ykp_set_fixed(cfg, oath_id, 6) != 1) {
841                         fprintf(stderr,
842                                 "Failed setting OATH token identifier.\n"
843                                 );
844                         return 0;
845                 }
846         }
847
848         return 1;
849 }