1 /* -*- mode:C; c-file-style: "bsd" -*- */
3 * Copyright (c) 2008-2012 Yubico AB
4 * Copyright (c) 2010 Tollef Fog Heen <tfheen@err.no>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
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.
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.
40 #include <yubikey.h> /* To get yubikey_modhex_encode and yubikey_hex_encode */
43 #define YUBICO_OATH_VENDOR_ID_HEX 0xe1 /* UB as hex */
44 #define YUBICO_HOTP_EVENT_TOKEN_TYPE 0x63 /* HE as hex */
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"
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"
85 " Ticket flags for firmware version 2.0 and above:\n"
86 " [-]protect-cfg2 set/clear PROTECT_CFG2\n"
88 " Ticket flags for firmware version 2.1 and above:\n"
89 " [-]oath-hotp set/clear OATH_HOTP\n"
91 " Ticket flags for firmware version 2.2 and above:\n"
92 " [-]chal-resp set/clear CHAL_RESP\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"
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"
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"
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"
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"
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"
127 "-y always commit (do not prompt)\n"
130 "-h help (this text)\n"
132 const char *optstring = "12a:c:hi:o:s:vy";
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)
140 if (strncmp(str, "m:", 2) == 0
141 || strncmp(str, "M:", 2) == 0) {
144 primarily_modhex = true;
145 } else if (strncmp(str, "h:", 2) == 0
146 || strncmp(str, "H:", 2) == 0) {
149 primarily_modhex = false;
153 if ((strl % 2 != 0) || (strl < minsize) || (strl > maxsize)) {
157 *resultlen = strl / 2;
158 if (primarily_modhex) {
159 if (yubikey_modhex_p(str)) {
160 yubikey_modhex_decode((char *)result, str, strl);
164 if (yubikey_hex_p(str)) {
165 yubikey_hex_decode((char *)result, str, strl);
173 void report_yk_error()
176 fprintf(stderr, "Yubikey personalization error: %s\n",
177 ykp_strerror(ykp_errno));
179 if (yk_errno == YK_EUSBERR) {
180 fprintf(stderr, "USB error: %s\n",
183 fprintf(stderr, "Yubikey core error: %s\n",
184 yk_strerror(yk_errno));
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.).
194 * Done in this way to be testable (see tests/test_args_to_config.c).
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,
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;
211 struct config_st *ycfg = (struct config_st *) ykp_core_config(cfg);
213 while((c = getopt(argc, argv, optstring)) != -1) {
215 if (strcmp(optarg, "oath-hotp") == 0 ||
216 strcmp(optarg, "chal-resp") == 0) {
218 fprintf(stderr, "You may only choose mode (-ooath-hotp / "
219 "-ochal-resp) once.\n");
225 fprintf(stderr, "Mode choosing flags (oath-hotp / chal-resp) "
226 "must be set prior to any other options (-o).\n");
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.
249 fprintf(stderr, "You may only choose slot (-1 / -2) once.\n");
254 fprintf(stderr, "You must choose slot before any options (-o).\n");
258 if (!ykp_configure_for(cfg, 1, st))
264 fprintf(stderr, "You may only choose slot (-1 / -2) once.\n");
269 fprintf(stderr, "You must choose slot before any options (-o).\n");
273 if (!ykp_configure_for(cfg, 2, st))
288 size_t access_code_len = 0;
289 int rc = hex_modhex_decode(access_code, &access_code_len,
290 optarg, strlen(optarg),
294 "Invalid access code string: %s\n",
299 if (!new_access_code)
300 ykp_set_access_code(cfg,
303 *use_access_code = true;
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) {
312 "Invalid fixed string: %s\n",
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,
328 "Invalid uid string: %s\n",
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) {
337 "Option uid= not valid with -ooath-hotp or -ochal-resp.\n"
342 ykp_set_uid(cfg, uidbin, uidbinlen);
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,
354 "Invalid access code string: %s\n",
359 ykp_set_access_code(cfg, accbin, accbinlen);
360 new_access_code = true;
362 #define TKTFLAG(o, f) \
363 else if (strcmp(optarg, o) == 0) { \
364 if (!ykp_set_tktflag_##f(cfg, true)) { \
368 } else if (strcmp(optarg, "-" o) == 0) { \
369 if (! ykp_set_tktflag_##f(cfg, false)) { \
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)
385 #define CFGFLAG(o, f) \
386 else if (strcmp(optarg, o) == 0) { \
387 if (! ykp_set_cfgflag_##f(cfg, true)) { \
391 } else if (strcmp(optarg, "-" o) == 0) { \
392 if (! ykp_set_cfgflag_##f(cfg, false)) { \
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)
416 else if (strncmp(optarg, "oath-imf=", 9) == 0) {
419 if (!(ycfg->tktFlags & TKTFLAG_OATH_HOTP) == TKTFLAG_OATH_HOTP) {
421 "Option oath-imf= only valid with -ooath-hotp or -ooath-hotp8.\n"
427 if (sscanf(optarg+9, "%lu", &imf) != 1 ||
428 /* yubikey limitations */
429 imf > 65535*16 || imf % 16 != 0) {
431 "Invalid value %s for oath-imf=.\n", optarg+9
436 if (! ykp_set_oath_imf(cfg, imf)) {
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) {
448 #define EXTFLAG(o, f) \
449 else if (strcmp(optarg, o) == 0) { \
450 if (! ykp_set_extflag_##f(cfg, true)) { \
454 } else if (strcmp(optarg, "-" o) == 0) { \
455 if (! ykp_set_extflag_##f(cfg, false)) { \
460 EXTFLAG("serial-btn-visible", SERIAL_BTN_VISIBLE)
461 EXTFLAG("serial-usb-visible", SERIAL_USB_VISIBLE)
462 EXTFLAG("serial-api-visible", SERIAL_API_VISIBLE)
465 fprintf(stderr, "Unknown option '%s'\n",
467 fputs(usage, stderr);
480 fputs(usage, stderr);
487 bool long_key_valid = false;
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;
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;
500 if (long_key_valid && strlen(aeshash) == 40) {
501 res = ykp_HMAC_key_from_hex(cfg, aeshash);
503 res = ykp_AES_key_from_hex(cfg, aeshash);
507 fprintf(stderr, "Bad %s key: %s\n", long_key_valid ? "HMAC":"AES", aeshash);
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,
527 ykp_set_fixed(cfg, fixedbin, fixedbinlen);
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)
538 while (src[0] && src[1]) {
541 *dst = ((src[0] - '0') * 0x10) + src[1] - '0';
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)
557 /* two bytes vendor and token type, and eight bytes MUI */
561 /* Make the YubiKey output the MUI number in decimal */
562 snprintf(buf, sizeof(buf), "%08i", mui);
567 if (_format_decimal_as_hex(dst + 2, dst_len - 2, buf) != 1)
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) {
577 "Option oath-id= only valid with -ooath-hotp or -ooath-hotp8.\n"
581 if (! ykp_set_cfgflag_OATH_FIXED_MODHEX2(cfg, true))
583 if (! ykp_set_extflag_SERIAL_API_VISIBLE(cfg, true))
586 if (strlen(optarg) > 7) {
587 if (_set_fixed(optarg + 8, cfg) != 1) {
589 "Invalid OATH token identifier %s supplied with oath-id=.\n", optarg + 8
594 /* No Token Id supplied, try to create one automatically based on
595 * the serial number of the YubiKey.
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)) {
604 "YubiKey refuses reading serial number. "
605 "Can't use -ooath-id.\n"
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)
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");
626 if (ykp_set_fixed(cfg, oath_id, 6) != 1) {
628 "Failed setting OATH token identifier.\n"