]> err.no Git - yubikey-personalization.old/blob - ykpers-args.c
Merge tag 'v1.6.4'
[yubikey-personalization.old] / ykpers-args.c
1 /* -*- mode:C; c-file-style: "bsd" -*- */
2 /*
3  * Copyright (c) 2008-2012 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 #include <stdlib.h>
33 #include <stdio.h>
34 #include <stddef.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <errno.h>
38
39 #include <ykpers.h>
40 #include <yubikey.h> /* To get yubikey_modhex_encode and yubikey_hex_encode */
41 #include <ykdef.h>
42
43 #define YUBICO_OATH_VENDOR_ID_HEX       0xe1    /* UB as hex */
44 #define YUBICO_HOTP_EVENT_TOKEN_TYPE    0x63    /* HE as hex */
45
46 const char *usage =
47 "Usage: ykpersonalize [options]\n"
48 "-1        change the first configuration.  This is the default and\n"
49 "          is normally used for true OTP generation.\n"
50 "          In this configuration, TKTFLAG_APPEND_CR is set by default.\n"
51 "-2        change the second configuration.  This is for Yubikey II only\n"
52 "          and is then normally used for static key generation.\n"
53 "          In this configuration, TKTFLAG_APPEND_CR, CFGFLAG_STATIC_TICKET,\n"
54 "          CFGFLAG_STRONG_PW1, CFGFLAG_STRONG_PW2 and CFGFLAG_MAN_UPDATE\n"
55 "          are set by default.\n"
56 "-sFILE    save configuration to FILE instead of key.\n"
57 "          (if FILE is -, send to stdout)\n"
58 "-iFILE    read configuration from FILE.\n"
59 "          (if FILE is -, read from stdin)\n"
60 "-aXXX..   The AES secret key as a 32 (or 40 for OATH-HOTP/HMAC CHAL-RESP)\n"
61 "          char hex value (not modhex)\n"
62 "-cXXX..   A 12 char hex value (not modhex) to use as access code for programming\n"
63 "          (this does NOT SET the access code, that's done with -oaccess=)\n"
64 "-oOPTION  change configuration option.  Possible OPTION arguments are:\n"
65 "          salt=ssssssss       Salt to be used when deriving key from a\n"
66 "                              password.  If none is given, a unique random\n"
67 "                              one will be generated.\n"
68 "          fixed=xxxxxxxxxxx   The public identity of key, in MODHEX.\n"
69 "                              This is 0-16 characters long.\n"
70 "          uid=xxxxxx          The uid part of the generated ticket, in HEX.\n"
71 "                              MUST be 12 characters long.\n"
72 "          access=xxxxxxxxxxx  New access code to set, in HEX.\n"
73 "                              MUST be 12 characters long.\n"
74 "          oath-imf=IMF        OATH Initial Moving Factor to use.\n"
75 "          oath-id[=h:OOTT...] OATH Token Identifier (none for serial-based)\n"
76 "\n"
77 "          Ticket flags for all firmware versions:\n"
78 "          [-]tab-first           set/clear TAB_FIRST\n"
79 "          [-]append-tab1         set/clear APPEND_TAB1\n"
80 "          [-]append-tab2         set/clear APPEND_TAB2\n"
81 "          [-]append-delay1       set/clear APPEND_DELAY1\n"
82 "          [-]append-delay2       set/clear APPEND_DELAY2\n"
83 "          [-]append-cr           set/clear APPEND_CR\n"
84 "\n"
85 "          Ticket flags for firmware version 2.0 and above:\n"
86 "          [-]protect-cfg2        set/clear PROTECT_CFG2\n"
87 "\n"
88 "          Ticket flags for firmware version 2.1 and above:\n"
89 "          [-]oath-hotp           set/clear OATH_HOTP\n"
90 "\n"
91 "          Ticket flags for firmware version 2.2 and above:\n"
92 "          [-]chal-resp           set/clear CHAL_RESP\n"
93 "\n"
94 "          Configuration flags for all firmware versions:\n"
95 "          [-]send-ref            set/clear SEND_REF\n"
96 "          [-]pacing-10ms         set/clear PACING_10MS\n"
97 "          [-]pacing-20ms         set/clear PACING_20MS\n"
98 "          [-]static-ticket       set/clear STATIC_TICKET\n"
99 "\n"
100 "          Configuration flags for firmware version 1.x only:\n"
101 "          [-]ticket-first        set/clear TICKET_FIRST\n"
102 "          [-]allow-hidtrig       set/clear ALLOW_HIDTRIG\n"
103 "\n"
104 "          Configuration flags for firmware version 2.0 and above:\n"
105 "          [-]short-ticket        set/clear SHORT_TICKET\n"
106 "          [-]strong-pw1          set/clear STRONG_PW1\n"
107 "          [-]strong-pw2          set/clear STRONG_PW2\n"
108 "          [-]man-update          set/clear MAN_UPDATE\n"
109 "\n"
110 "          Configuration flags for firmware version 2.1 and above:\n"
111 "          [-]oath-hotp8          set/clear OATH_HOTP8\n"
112 "          [-]oath-fixed-modhex1  set/clear OATH_FIXED_MODHEX1\n"
113 "          [-]oath-fixed-modhex2  set/clear OATH_FIXED_MODHEX2\n"
114 "          [-]oath-fixed-modhex   set/clear OATH_MODHEX\n"
115 "\n"
116 "          Configuration flags for firmware version 2.2 and above:\n"
117 "          [-]chal-yubico         set/clear CHAL_YUBICO\n"
118 "          [-]chal-hmac           set/clear CHAL_HMAC\n"
119 "          [-]hmac-lt64           set/clear HMAC_LT64\n"
120 "          [-]chal-btn-trig       set/clear CHAL_BTN_TRIG\n"
121 "\n"
122 "          Extended flags for firmware version 2.2 and above:\n"
123 "          [-]serial-btn-visible  set/clear SERIAL_BTN_VISIBLE\n"
124 "          [-]serial-usb-visible  set/clear SERIAL_USB_VISIBLE\n"
125 "          [-]serial-api-visible  set/clear SERIAL_API_VISIBLE\n"
126 "\n"
127 "-y        always commit (do not prompt)\n"
128 "\n"
129 "-v        verbose\n"
130 "-h        help (this text)\n"
131 ;
132 const char *optstring = "12a:c:hi:o:s:vy";
133
134 static int hex_modhex_decode(unsigned char *result, size_t *resultlen,
135                              const char *str, size_t strl,
136                              size_t minsize, size_t maxsize,
137                              bool primarily_modhex)
138 {
139         if (strl >= 2) {
140                 if (strncmp(str, "m:", 2) == 0
141                     || strncmp(str, "M:", 2) == 0) {
142                         str += 2;
143                         strl -= 2;
144                         primarily_modhex = true;
145                 } else if (strncmp(str, "h:", 2) == 0
146                            || strncmp(str, "H:", 2) == 0) {
147                         str += 2;
148                         strl -= 2;
149                         primarily_modhex = false;
150                 }
151         }
152
153         if ((strl % 2 != 0) || (strl < minsize) || (strl > maxsize)) {
154                 return -1;
155         }
156
157         *resultlen = strl / 2;
158         if (primarily_modhex) {
159                 if (yubikey_modhex_p(str)) {
160                         yubikey_modhex_decode((char *)result, str, strl);
161                         return 1;
162                 }
163         } else {
164                 if (yubikey_hex_p(str)) {
165                         yubikey_hex_decode((char *)result, str, strl);
166                         return 1;
167                 }
168         }
169
170         return 0;
171 }
172
173 void report_yk_error()
174 {
175         if (ykp_errno)
176                 fprintf(stderr, "Yubikey personalization error: %s\n",
177                         ykp_strerror(ykp_errno));
178         if (yk_errno) {
179                 if (yk_errno == YK_EUSBERR) {
180                         fprintf(stderr, "USB error: %s\n",
181                                 yk_usb_strerror());
182                 } else {
183                         fprintf(stderr, "Yubikey core error: %s\n",
184                                 yk_strerror(yk_errno));
185                 }
186         }
187 }
188
189 /*
190  * Parse all arguments supplied to this program and turn it into mainly
191  * a YKP_CONFIG (but return some other parameters as well, like
192  * access_code, verbose etc.).
193  *
194  * Done in this way to be testable (see tests/test_args_to_config.c).
195  */
196 int args_to_config(int argc, char **argv, YKP_CONFIG *cfg, YK_KEY *yk,
197                    const char **infname, const char **outfname,
198                    bool *autocommit, char *salt,
199                    YK_STATUS *st, bool *verbose,
200                    unsigned char *access_code, bool *use_access_code,
201                    bool *aesviahash,
202                    int *exit_code)
203 {
204         int c;
205         const char *aeshash = NULL;
206         bool new_access_code = false;
207         bool slot_chosen = false;
208         bool mode_chosen = false;
209         bool option_seen = false;
210
211         struct config_st *ycfg = (struct config_st *) ykp_core_config(cfg);
212
213         while((c = getopt(argc, argv, optstring)) != -1) {
214                 if (c == 'o') {
215                         if (strcmp(optarg, "oath-hotp") == 0 ||
216                             strcmp(optarg, "chal-resp") == 0) {
217                                 if (mode_chosen) {
218                                         fprintf(stderr, "You may only choose mode (-ooath-hotp / "
219                                                 "-ochal-resp) once.\n");
220                                         *exit_code = 1;
221                                         return 0;
222                                 }
223
224                                 if (option_seen) {
225                                         fprintf(stderr, "Mode choosing flags (oath-hotp / chal-resp) "
226                                                 "must be set prior to any other options (-o).\n");
227                                         *exit_code = 1;
228                                         return 0;
229                                 }
230
231                                 /* The default flags (particularly for slot 2) does not apply to
232                                  * these new modes of operation found in Yubikey >= 2.1. Therefor,
233                                  * we reset them here and, as a consequence of that, require the
234                                  * mode choosing options to be specified before any other.
235                                  */
236                                 ycfg->tktFlags = 0;
237                                 ycfg->cfgFlags = 0;
238                                 ycfg->extFlags = 0;
239
240                                 mode_chosen = 1;
241                         }
242
243                         option_seen = true;
244                 }
245
246                 switch (c) {
247                 case '1':
248                         if (slot_chosen) {
249                                 fprintf(stderr, "You may only choose slot (-1 / -2) once.\n");
250                                 *exit_code = 1;
251                                 return 0;
252                         }
253                         if (option_seen) {
254                                 fprintf(stderr, "You must choose slot before any options (-o).\n");
255                                 *exit_code = 1;
256                                 return 0;
257                         }
258                         if (!ykp_configure_for(cfg, 1, st))
259                                 return 0;
260                         slot_chosen = true;
261                         break;
262                 case '2':
263                         if (slot_chosen) {
264                                 fprintf(stderr, "You may only choose slot (-1 / -2) once.\n");
265                                 *exit_code = 1;
266                                 return 0;
267                         }
268                         if (option_seen) {
269                                 fprintf(stderr, "You must choose slot before any options (-o).\n");
270                                 *exit_code = 1;
271                                 return 0;
272                         }
273                         if (!ykp_configure_for(cfg, 2, st))
274                                 return 0;
275                         slot_chosen = true;
276                         break;
277                 case 'i':
278                         *infname = optarg;
279                         break;
280                 case 's':
281                         *outfname = optarg;
282                         break;
283                 case 'a':
284                         *aesviahash = true;
285                         aeshash = optarg;
286                         break;
287                 case 'c': {
288                         size_t access_code_len = 0;
289                         int rc = hex_modhex_decode(access_code, &access_code_len,
290                                                    optarg, strlen(optarg),
291                                                    12, 12, false);
292                         if (rc <= 0) {
293                                 fprintf(stderr,
294                                         "Invalid access code string: %s\n",
295                                         optarg);
296                                 *exit_code = 1;
297                                 return 0;
298                         }
299                         if (!new_access_code)
300                                 ykp_set_access_code(cfg,
301                                                     access_code,
302                                                     access_code_len);
303                         *use_access_code = true;
304                         break;
305                 }
306                 case 'o':
307                         if (strncmp(optarg, "salt=", 5) == 0)
308                                 salt = strdup(optarg+5);
309                         else if (strncmp(optarg, "fixed=", 6) == 0) {
310                                 if (_set_fixed(optarg + 6, cfg) != 1) {
311                                         fprintf(stderr,
312                                                 "Invalid fixed string: %s\n",
313                                                 optarg + 6);
314                                         *exit_code = 1;
315                                         return 0;
316                                 }
317                         }
318                         else if (strncmp(optarg, "uid=", 4) == 0) {
319                                 const char *uid = optarg+4;
320                                 size_t uidlen = strlen (uid);
321                                 unsigned char uidbin[256];
322                                 size_t uidbinlen = 0;
323                                 int rc = hex_modhex_decode(uidbin, &uidbinlen,
324                                                            uid, uidlen,
325                                                            12, 12, false);
326                                 if (rc <= 0) {
327                                         fprintf(stderr,
328                                                 "Invalid uid string: %s\n",
329                                                 uid);
330                                         *exit_code = 1;
331                                         return 0;
332                                 }
333                                 /* for OATH-HOTP and CHAL-RESP, uid is not applicable */
334                                 if ((ycfg->tktFlags & TKTFLAG_OATH_HOTP) == TKTFLAG_OATH_HOTP ||
335                                     (ycfg->tktFlags & TKTFLAG_CHAL_RESP) == TKTFLAG_CHAL_RESP) {
336                                         fprintf(stderr,
337                                                 "Option uid= not valid with -ooath-hotp or -ochal-resp.\n"
338                                                 );
339                                         *exit_code = 1;
340                                         return 0;
341                                 }
342                                 ykp_set_uid(cfg, uidbin, uidbinlen);
343                         }
344                         else if (strncmp(optarg, "access=", 7) == 0) {
345                                 const char *acc = optarg+7;
346                                 size_t acclen = strlen (acc);
347                                 unsigned char accbin[256];
348                                 size_t accbinlen = 0;
349                                 int rc = hex_modhex_decode (accbin, &accbinlen,
350                                                             acc, acclen,
351                                                             12, 12, false);
352                                 if (rc <= 0) {
353                                         fprintf(stderr,
354                                                 "Invalid access code string: %s\n",
355                                                 acc);
356                                         *exit_code = 1;
357                                         return 0;
358                                 }
359                                 ykp_set_access_code(cfg, accbin, accbinlen);
360                                 new_access_code = true;
361                         }
362 #define TKTFLAG(o, f)                                                   \
363                         else if (strcmp(optarg, o) == 0) {              \
364                                 if (!ykp_set_tktflag_##f(cfg, true)) {  \
365                                         *exit_code = 1;                 \
366                                         return 0;               \
367                                 }                                       \
368                         } else if (strcmp(optarg, "-" o) == 0) {        \
369                                 if (! ykp_set_tktflag_##f(cfg, false)) { \
370                                         *exit_code = 1;                 \
371                                         return 0;               \
372                                 }                                       \
373                         }
374                         TKTFLAG("tab-first", TAB_FIRST)
375                         TKTFLAG("append-tab1", APPEND_TAB1)
376                         TKTFLAG("append-tab2", APPEND_TAB2)
377                         TKTFLAG("append-delay1", APPEND_DELAY1)
378                         TKTFLAG("append-delay2", APPEND_DELAY2)
379                         TKTFLAG("append-cr", APPEND_CR)
380                         TKTFLAG("protect-cfg2", PROTECT_CFG2)
381                         TKTFLAG("oath-hotp", OATH_HOTP)
382                         TKTFLAG("chal-resp", CHAL_RESP)
383 #undef TKTFLAG
384
385 #define CFGFLAG(o, f)                                                   \
386                         else if (strcmp(optarg, o) == 0) {              \
387                                 if (! ykp_set_cfgflag_##f(cfg, true)) { \
388                                         *exit_code = 1;                 \
389                                         return 0;                       \
390                                 }                                       \
391                         } else if (strcmp(optarg, "-" o) == 0) {        \
392                                 if (! ykp_set_cfgflag_##f(cfg, false)) { \
393                                         *exit_code = 1;                 \
394                                         return 0;                       \
395                                 }                                       \
396                         }
397                         CFGFLAG("send-ref", SEND_REF)
398                         CFGFLAG("ticket-first", TICKET_FIRST)
399                         CFGFLAG("pacing-10ms", PACING_10MS)
400                         CFGFLAG("pacing-20ms", PACING_20MS)
401                         CFGFLAG("allow-hidtrig", ALLOW_HIDTRIG)
402                         CFGFLAG("static-ticket", STATIC_TICKET)
403                         CFGFLAG("short-ticket", SHORT_TICKET)
404                         CFGFLAG("strong-pw1", STRONG_PW1)
405                         CFGFLAG("strong-pw2", STRONG_PW2)
406                         CFGFLAG("man-update", MAN_UPDATE)
407                         CFGFLAG("oath-hotp8", OATH_HOTP8)
408                         CFGFLAG("oath-fixed-modhex1", OATH_FIXED_MODHEX1)
409                         CFGFLAG("oath-fixed-modhex2", OATH_FIXED_MODHEX2)
410                         CFGFLAG("oath-fixed-modhex", OATH_FIXED_MODHEX)
411                         CFGFLAG("chal-yubico", CHAL_YUBICO)
412                         CFGFLAG("chal-hmac", CHAL_HMAC)
413                         CFGFLAG("hmac-lt64", HMAC_LT64)
414                         CFGFLAG("chal-btn-trig", CHAL_BTN_TRIG)
415 #undef CFGFLAG
416                         else if (strncmp(optarg, "oath-imf=", 9) == 0) {
417                                 unsigned long imf;
418
419                                 if (!(ycfg->tktFlags & TKTFLAG_OATH_HOTP) == TKTFLAG_OATH_HOTP) {
420                                         fprintf(stderr,
421                                                 "Option oath-imf= only valid with -ooath-hotp or -ooath-hotp8.\n"
422                                                 );
423                                         *exit_code = 1;
424                                         return 0;
425                                 }
426
427                                 if (sscanf(optarg+9, "%lu", &imf) != 1 ||
428                                     /* yubikey limitations */
429                                     imf > 65535*16 || imf % 16 != 0) {
430                                         fprintf(stderr,
431                                                 "Invalid value %s for oath-imf=.\n", optarg+9
432                                                 );
433                                         *exit_code = 1;
434                                         return 0;
435                                 }
436                                 if (! ykp_set_oath_imf(cfg, imf)) {
437                                         *exit_code = 1;
438                                         return 0;
439                                 }
440                         }
441                         else if (strncmp(optarg, "oath-id=", 8) == 0 || strcmp(optarg, "oath-id") == 0) {
442                                 if (_set_oath_id(optarg, cfg, ycfg, yk, st) != 1) {
443                                         *exit_code = 1;
444                                         return 0;
445                                 }
446                         }
447
448 #define EXTFLAG(o, f)                                                   \
449                         else if (strcmp(optarg, o) == 0) {              \
450                                 if (! ykp_set_extflag_##f(cfg, true)) { \
451                                         *exit_code = 1;                 \
452                                         return 0;                       \
453                                 }                                       \
454                         } else if (strcmp(optarg, "-" o) == 0) {        \
455                                 if (! ykp_set_extflag_##f(cfg, false)) { \
456                                         *exit_code = 1;                 \
457                                         return 0;                       \
458                                 }                                       \
459                         }
460                         EXTFLAG("serial-btn-visible", SERIAL_BTN_VISIBLE)
461                         EXTFLAG("serial-usb-visible", SERIAL_USB_VISIBLE)
462                         EXTFLAG("serial-api-visible", SERIAL_API_VISIBLE)
463 #undef EXTFLAG
464                         else {
465                                 fprintf(stderr, "Unknown option '%s'\n",
466                                         optarg);
467                                 fputs(usage, stderr);
468                                 *exit_code = 1;
469                                 return 0;
470                         }
471                         break;
472                 case 'v':
473                         *verbose = true;
474                         break;
475                 case 'y':
476                         *autocommit = true;
477                         break;
478                 case 'h':
479                 default:
480                         fputs(usage, stderr);
481                         *exit_code = 0;
482                         return 0;
483                 }
484         }
485
486         if (*aesviahash) {
487                 bool long_key_valid = false;
488                 int res = 0;
489
490                 /* for OATH-HOTP, 160 bits key is also valid */
491                 if ((ycfg->tktFlags & TKTFLAG_OATH_HOTP) == TKTFLAG_OATH_HOTP)
492                         long_key_valid = true;
493
494                 /* for HMAC (not Yubico) challenge-response, 160 bits key is also valid */
495                 if ((ycfg->tktFlags & TKTFLAG_CHAL_RESP) == TKTFLAG_CHAL_RESP &&
496                     (ycfg->cfgFlags & CFGFLAG_CHAL_HMAC) == CFGFLAG_CHAL_HMAC) {
497                         long_key_valid = true;
498                 }
499
500                 if (long_key_valid && strlen(aeshash) == 40) {
501                         res = ykp_HMAC_key_from_hex(cfg, aeshash);
502                 } else {
503                         res = ykp_AES_key_from_hex(cfg, aeshash);
504                 }
505
506                 if (res) {
507                         fprintf(stderr, "Bad %s key: %s\n", long_key_valid ? "HMAC":"AES", aeshash);
508                         fflush(stderr);
509                         return 0;
510                 }
511         }
512
513         return 1;
514 }
515
516 int _set_fixed(char *optarg, YKP_CONFIG *cfg) {
517         const char *fixed = optarg;
518         size_t fixedlen = strlen (fixed);
519         unsigned char fixedbin[256];
520         size_t fixedbinlen = 0;
521         int rc = hex_modhex_decode(fixedbin, &fixedbinlen,
522                                    fixed, fixedlen,
523                                    0, 16, true);
524         if (rc <= 0)
525                 return 0;
526
527         ykp_set_fixed(cfg, fixedbin, fixedbinlen);
528         return 1;
529 }
530
531
532 /* re-format decimal 12345678 into 'hex' 0x12 0x34 0x56 0x78 */
533 int _format_decimal_as_hex(uint8_t *dst, size_t dst_len, uint8_t *src)
534 {
535         uint8_t *end;
536
537         end = dst + dst_len;
538         while (src[0] && src[1]) {
539                 if (dst >= end)
540                         return 0;
541                 *dst = ((src[0] - '0') * 0x10) + src[1] - '0';
542                 dst++;
543                 src += 2;
544         }
545
546         return 1;
547 }
548
549 /* For details, see YubiKey Manual 2010-09-16 section 5.3.4 - OATH-HOTP Token Identifier */
550 int _format_oath_id(uint8_t *dst, size_t dst_len, uint8_t vendor, uint8_t type, uint32_t mui)
551 {
552         uint8_t buf[8 + 1];
553
554         if (mui > 99999999)
555                 return 0;
556
557         /* two bytes vendor and token type, and eight bytes MUI */
558         if (dst_len < 2 + 8)
559                 return 0;
560
561         /* Make the YubiKey output the MUI number in decimal */
562         snprintf(buf, sizeof(buf), "%08i", mui);
563
564         dst[0] = vendor;
565         dst[1] = type;
566
567         if (_format_decimal_as_hex(dst + 2, dst_len - 2, buf) != 1)
568                 return 0;
569
570         return 1;
571 }
572
573 int _set_oath_id(char *optarg, YKP_CONFIG *cfg, struct config_st *ycfg, YK_KEY *yk, YK_STATUS *st) {
574         /* For details, see YubiKey Manual 2010-09-16 section 5.3.4 - OATH-HOTP Token Identifier */
575         if (!(ycfg->tktFlags & TKTFLAG_OATH_HOTP) == TKTFLAG_OATH_HOTP) {
576                 fprintf(stderr,
577                         "Option oath-id= only valid with -ooath-hotp or -ooath-hotp8.\n"
578                         );
579                 return 0;
580         }
581         if (! ykp_set_cfgflag_OATH_FIXED_MODHEX2(cfg, true))
582                 return 0;
583         if (! ykp_set_extflag_SERIAL_API_VISIBLE(cfg, true))
584                 return 0;
585
586         if (strlen(optarg) > 7) {
587                 if (_set_fixed(optarg + 8, cfg) != 1) {
588                         fprintf(stderr,
589                                 "Invalid OATH token identifier %s supplied with oath-id=.\n", optarg + 8
590                                 );
591                         return 0;
592                 }
593         } else {
594                 /* No Token Id supplied, try to create one automatically based on
595                  * the serial number of the YubiKey.
596                  */
597                 unsigned int serial;
598                 uint8_t oath_id[12] = {0};
599                 if (ykds_version_major(st) > 2 ||
600                     (ykds_version_major(st) == 2 &&
601                      ykds_version_minor(st) >= 2)) {
602                         if (! yk_get_serial(yk, 0, 0, &serial)) {
603                                 fprintf(stderr,
604                                         "YubiKey refuses reading serial number. "
605                                         "Can't use -ooath-id.\n"
606                                         );
607                                 return 0;
608                         }
609                 } else {
610                         fprintf(stderr,
611                                 "YubiKey %d.%d.%d does not support reading serial number. "
612                                 "Can't use -ooath-id.\n",
613                                 ykds_version_major(st),
614                                 ykds_version_minor(st),
615                                 ykds_version_build(st)
616                                 );
617                         return 0;
618                 }
619
620                 if (_format_oath_id(oath_id, sizeof(oath_id), YUBICO_OATH_VENDOR_ID_HEX,
621                                     YUBICO_HOTP_EVENT_TOKEN_TYPE, serial) != 1) {
622                         fprintf(stderr, "Failed formatting OATH token identifier.\n");
623                         return 0;
624                 }
625
626                 if (ykp_set_fixed(cfg, oath_id, 6) != 1) {
627                         fprintf(stderr,
628                                 "Failed setting OATH token identifier.\n"
629                                 );
630                         return 0;
631                 }
632         }
633
634         return 1;
635 }