int YubiKeyWriter::assembleConfig(YubiKeyConfig *ykConfig, YKP_CONFIG *cfg, bool *useAccessCode, unsigned char *accessCode) { // Check features support bool flagSrNoSupport = false; bool flagUpdateSupport = false; bool flagImfSupport = false; bool flagLedInvSupport = false; YubiKeyFinder *finder = YubiKeyFinder::getInstance(); if(finder->checkFeatureSupport( YubiKeyFinder::Feature_SerialNumber)) { flagSrNoSupport = true; } if(finder->checkFeatureSupport( YubiKeyFinder::Feature_Updatable)) { flagUpdateSupport = true; } if(finder->checkFeatureSupport( YubiKeyFinder::Feature_MovingFactor)) { flagImfSupport = true; } if(finder->checkFeatureSupport( YubiKeyFinder::Feature_LedInvert)) { flagLedInvSupport = true; } //Programming Mode... bool longSecretKey = false; int command = ykConfig->configSlot() == 2 ? SLOT_CONFIG2 : SLOT_CONFIG; switch(ykConfig->programmingMode()) { case YubiKeyConfig::Mode_Update: // if we're doing an update it's other commands. command = ykConfig->configSlot() == 2 ? SLOT_UPDATE2 : SLOT_UPDATE1; break; case YubiKeyConfig::Mode_Swap: // swap has it's own command command = SLOT_SWAP; break; case YubiKeyConfig::Mode_YubicoOtp: break; case YubiKeyConfig::Mode_Static: CFGFLAG(STATIC_TICKET, ykConfig->staticTicket()); CFGFLAG(SHORT_TICKET, ykConfig->shortTicket()); CFGFLAG(STRONG_PW1, ykConfig->strongPw1()); CFGFLAG(STRONG_PW2, ykConfig->strongPw2()); CFGFLAG(MAN_UPDATE, ykConfig->manUpdate()); CFGFLAG(SEND_REF, ykConfig->sendRef()); break; case YubiKeyConfig::Mode_OathHotp: TKTFLAG(OATH_HOTP, true); CFGFLAG(OATH_HOTP8, ykConfig->oathHotp8()); CFGFLAG(OATH_FIXED_MODHEX1, ykConfig->oathFixedModhex1()); CFGFLAG(OATH_FIXED_MODHEX2, ykConfig->oathFixedModhex2()); CFGFLAG(OATH_FIXED_MODHEX, ykConfig->oathFixedModhex()); //Moving Factor Seed... if(flagImfSupport && !ykp_set_oath_imf(cfg, ykConfig->oathMovingFactorSeed())) { return 0; } //For OATH-HOTP, 160 bits key is also valid longSecretKey = true; break; case YubiKeyConfig::Mode_ChalRespYubico: TKTFLAG(CHAL_RESP, true); CFGFLAG(CHAL_YUBICO, ykConfig->chalYubico()); CFGFLAG(CHAL_BTN_TRIG, ykConfig->chalBtnTrig()); break; case YubiKeyConfig::Mode_ChalRespHmac: TKTFLAG(CHAL_RESP, true); CFGFLAG(CHAL_HMAC, ykConfig->chalHmac()); CFGFLAG(HMAC_LT64, ykConfig->hmacLT64()); CFGFLAG(CHAL_BTN_TRIG, ykConfig->chalBtnTrig()); //For HMAC (not Yubico) challenge-response, 160 bits key is also valid longSecretKey = true; break; } //Configuration slot... if (!ykp_configure_command(cfg, command)) { return 0; } //Public ID... if(ykConfig->pubIdTxt().length() > 0) { qDebug() << "Pub id: " << ykConfig->pubIdTxt() << "length:" << ykConfig->pubIdTxt().length(); char pubIdStr[MAX_SIZE]; YubiKeyUtil::qstrToRaw(pubIdStr, sizeof(pubIdStr), ykConfig->pubIdTxt()); size_t pubIdStrLen = strlen(pubIdStr); unsigned char pubId[MAX_SIZE]; size_t pubIdLen = 0; int rc = YubiKeyUtil::hexModhexDecode(pubId, &pubIdLen, pubIdStr, pubIdStrLen, 0, FIXED_SIZE * 2, !ykConfig->pubIdInHex()); if (rc <= 0) { qDebug("Invalid public id: %s", pubIdStr); return 0; } ykp_set_fixed(cfg, pubId, pubIdLen); } //Private ID... if(ykConfig->pvtIdTxt().length() > 0) { qDebug() << "Pvt id: " << ykConfig->pvtIdTxt() << "length:" << ykConfig->pvtIdTxt().length(); char pvtIdStr[MAX_SIZE]; YubiKeyUtil::qstrToRaw(pvtIdStr, sizeof(pvtIdStr), ykConfig->pvtIdTxt()); size_t pvtIdStrLen = strlen(pvtIdStr); unsigned char pvtId[MAX_SIZE]; size_t pvtIdLen = 0; int rc = YubiKeyUtil::hexModhexDecode(pvtId, &pvtIdLen, pvtIdStr, pvtIdStrLen, UID_SIZE * 2, UID_SIZE * 2, false); if (rc <= 0) { qDebug("Invalid private id: %s", pvtIdStr); return 0; } ykp_set_uid(cfg, pvtId, pvtIdLen); } //Secret Key... if(ykConfig->secretKeyTxt().length() > 0) { qDebug() << "Secret key: " << ykConfig->secretKeyTxt() << "length:" << ykConfig->secretKeyTxt().length(); char secretKeyStr[MAX_SIZE]; YubiKeyUtil::qstrToRaw(secretKeyStr, sizeof(secretKeyStr), ykConfig->secretKeyTxt()); int res = 0; if(longSecretKey && strlen(secretKeyStr) == 40) { res = ykp_HMAC_key_from_hex(cfg, secretKeyStr); } else { res = ykp_AES_key_from_hex(cfg, secretKeyStr); } if (res) { qDebug() << "Bad secret key: " << secretKeyStr; return 0; } } //Configuration protection... //Current Access Code... size_t accessCodeLen = 0; if(ykConfig->currentAccessCodeTxt().length() > 0) { int rc = encodeAccessCode(ykConfig->currentAccessCodeTxt(), accessCode, &accessCodeLen); if (rc <= 0) { qDebug() << "Invalid current access code: " << ykConfig->currentAccessCodeTxt(); return 0; } } //New Access Code... unsigned char newAccessCode[MAX_SIZE]; size_t newAccessCodeLen = 0; if(ykConfig->newAccessCodeTxt().length() > 0) { int rc = encodeAccessCode(ykConfig->newAccessCodeTxt(), newAccessCode, &newAccessCodeLen); if (rc <= 0) { qDebug() << "Invalid new access code: " << ykConfig->newAccessCodeTxt(); return 0; } if(ykConfig->accMode() > 0) { int accMode = 0; if(ykConfig->accMode() == Acc_None) { accMode = YKP_ACCCODE_NONE; } else if(ykConfig->accMode() == Acc_Serial) { accMode = YKP_ACCCODE_SERIAL; } else if(ykConfig->accMode() == Acc_Random) { accMode = YKP_ACCCODE_RANDOM; } ykp_set_acccode_type(cfg, accMode); } } if(accessCodeLen > 0) { *useAccessCode = true; } if(newAccessCodeLen > 0) { //Enable/change protection ykp_set_access_code(cfg, newAccessCode, newAccessCodeLen); } else if(accessCodeLen > 0) { //Keep same protection ykp_set_access_code(cfg, accessCode, accessCodeLen); } TKTFLAG(TAB_FIRST, ykConfig->tabFirst()); TKTFLAG(APPEND_TAB1, ykConfig->appendTab1()); TKTFLAG(APPEND_TAB2, ykConfig->appendTab2()); TKTFLAG(APPEND_CR, ykConfig->appendCr()); TKTFLAG(APPEND_DELAY1, ykConfig->appendDelay1()); TKTFLAG(APPEND_DELAY2, ykConfig->appendDelay2()); //TKTFLAG(PROTECT_CFG2, ykConfig->protectCfg2()); //CFGFLAG(SEND_REF, ykConfig->sendRef()); //CFGFLAG(TICKET_FIRST, ykConfig->ticketFirst()); CFGFLAG(PACING_10MS, ykConfig->pacing10ms()); CFGFLAG(PACING_20MS, ykConfig->pacing20ms()); //CFGFLAG(ALLOW_HIDTRIG, ykConfig->allowHidtrig()); //Serial # visibility... if(flagSrNoSupport) { EXTFLAG(SERIAL_BTN_VISIBLE, ykConfig->serialBtnVisible()); EXTFLAG(SERIAL_USB_VISIBLE, ykConfig->serialUsbVisible()); EXTFLAG(SERIAL_API_VISIBLE, ykConfig->serialApiVisible()); } if(flagUpdateSupport) { EXTFLAG(ALLOW_UPDATE, ykConfig->allowUpdate()); // XXX: let update support mean these as well.. EXTFLAG(DORMANT, ykConfig->dormant()); EXTFLAG(FAST_TRIG, ykConfig->fastTrig()); EXTFLAG(USE_NUMERIC_KEYPAD, ykConfig->useNumericKeypad()); } if(flagLedInvSupport) { EXTFLAG(LED_INV, ykConfig->ledInvert()); } return 1; }
/* * Parse all arguments supplied to this program and turn it into mainly * a YKP_CONFIG (but return some other parameters as well, like * access_code, verbose etc.). * * Done in this way to be testable (see tests/test_args_to_config.c). */ int args_to_config(int argc, char **argv, YKP_CONFIG *cfg, YK_KEY *yk, const char **infname, const char **outfname, bool *autocommit, char *salt, YK_STATUS *st, bool *verbose, unsigned char *access_code, bool *use_access_code, bool *aesviahash, char *ndef_type, char *ndef, unsigned char *usb_mode, bool *zap, unsigned char *scan_bin, int *exit_code) { int c; const char *aeshash = NULL; bool new_access_code = false; bool slot_chosen = false; bool mode_chosen = false; bool option_seen = false; bool swap_seen = false; bool update_seen = false; bool ndef_seen = false; bool usb_mode_seen = false; bool scan_map_seen = false; struct config_st *ycfg; ykp_configure_version(cfg, st); ycfg = (struct config_st *) ykp_core_config(cfg); while((c = getopt(argc, argv, optstring)) != -1) { if (c == 'o') { if (strcmp(optarg, "oath-hotp") == 0 || strcmp(optarg, "chal-resp") == 0) { if (mode_chosen) { fprintf(stderr, "You may only choose mode (-ooath-hotp / " "-ochal-resp) once.\n"); *exit_code = 1; return 0; } if (option_seen) { fprintf(stderr, "Mode choosing flags (oath-hotp / chal-resp) " "must be set prior to any other options (-o).\n"); *exit_code = 1; return 0; } /* The default flags (particularly for slot 2) does not apply to * these new modes of operation found in Yubikey >= 2.1. Therefor, * we reset them here and, as a consequence of that, require the * mode choosing options to be specified before any other. */ ycfg->tktFlags = 0; ycfg->cfgFlags = 0; ycfg->extFlags = 0; mode_chosen = 1; } option_seen = true; } switch (c) { case 'u': if (slot_chosen) { fprintf(stderr, "You must use update before slot (-1 / -2).\n"); *exit_code = 1; return 0; } if (swap_seen) { fprintf(stderr, "Update (-u) and swap (-x) can't be combined.\n"); *exit_code = 1; return 0; } if (ndef_seen) { fprintf(stderr, "Update (-u) can not be combined with ndef (-n).\n"); *exit_code = 1; return 0; } update_seen = true; break; case '1': case '2': { int command; if (slot_chosen) { fprintf(stderr, "You may only choose slot (-1 / -2) once.\n"); *exit_code = 1; return 0; } if (option_seen) { fprintf(stderr, "You must choose slot before any options (-o).\n"); *exit_code = 1; return 0; } if (swap_seen) { fprintf(stderr, "You can not combine slot swap (-x) with configuring a slot.\n"); *exit_code = 1; return 0; } ykp_set_tktflag_APPEND_CR(cfg, true); if (update_seen) { ykp_set_extflag_ALLOW_UPDATE(cfg, true); if(c == '1') { command = SLOT_UPDATE1; } else if(c == '2') { command = SLOT_UPDATE2; } } else if (c == '1') { command = SLOT_CONFIG; } else if (c == '2') { command = SLOT_CONFIG2; ykp_set_cfgflag_STATIC_TICKET(cfg, true); ykp_set_cfgflag_STRONG_PW1(cfg, true); ykp_set_cfgflag_STRONG_PW2(cfg, true); ykp_set_cfgflag_MAN_UPDATE(cfg, true); } if (!ykp_configure_command(cfg, command)) return 0; slot_chosen = true; break; } case 'x': if (slot_chosen || option_seen || update_seen || ndef_seen || *zap || usb_mode_seen || scan_map_seen) { fprintf(stderr, "Slot swap (-x) can not be used with other options.\n"); *exit_code = 1; return 0; } if (!ykp_configure_command(cfg, SLOT_SWAP)) { return 0; } swap_seen = true; break; case 'z': if (swap_seen || update_seen || ndef_seen || usb_mode_seen || scan_map_seen) { fprintf(stderr, "Zap (-z) can only be used with a slot (-1 / -2).\n"); *exit_code = 1; return 0; } *zap = true; break; case 'i': *infname = optarg; break; case 's': *outfname = optarg; break; case 'a': *aesviahash = true; aeshash = optarg; break; case 'c': { size_t access_code_len = 0; int rc = hex_modhex_decode(access_code, &access_code_len, optarg, strlen(optarg), 12, 12, false); if (rc <= 0) { fprintf(stderr, "Invalid access code string: %s\n", optarg); *exit_code = 1; return 0; } if (!new_access_code) ykp_set_access_code(cfg, access_code, access_code_len); *use_access_code = true; break; } case 't': *ndef_type = 'T'; case 'n': { int command; if(!*ndef_type) { *ndef_type = 'U'; } if (swap_seen || update_seen || option_seen || *zap || usb_mode_seen || scan_map_seen) { fprintf(stderr, "Ndef (-n/-t) can only be used with a slot (-1/-2).\n"); *exit_code = 1; return 0; } if(ykp_command(cfg) == SLOT_CONFIG) { command = SLOT_NDEF; } else if(ykp_command(cfg) == SLOT_CONFIG2) { command = SLOT_NDEF2; } else { command = SLOT_NDEF; } if (!ykp_configure_command(cfg, command)) { return 0; } memcpy(ndef, optarg, strlen(optarg)); ndef_seen = true; break; } case 'm': if(slot_chosen || swap_seen || update_seen || option_seen || ndef_seen || *zap || scan_map_seen) { fprintf(stderr, "USB mode (-m) can not be combined with other options.\n"); *exit_code = 1; return 0; } if(optarg[1] != '\0') { *usb_mode = (optarg[0] - '0') << 4; optarg++; } if(optarg[1] == '\0') { int mode = optarg[0] - '0'; if(mode >= 0 && mode < MODE_MASK) { *usb_mode |= mode; usb_mode_seen = true; } } /* Only true if we've parsed a valid USB mode number */ if(!usb_mode_seen) { fprintf(stderr, "Invalid USB operation mode.\n"); *exit_code = 1; return 0; } if (!ykp_configure_command(cfg, SLOT_DEVICE_CONFIG)) return 0; break; case 'S': { size_t scanlength = strlen(SCAN_MAP); if(slot_chosen || swap_seen || update_seen || option_seen || ndef_seen || *zap || usb_mode_seen) { fprintf(stderr, "Scanmap (-S) can not be combined with other options.\n"); *exit_code = 1; return 0; } if(optarg) { size_t scanbinlen; size_t scanlen = strlen (optarg); int rc = hex_modhex_decode(scan_bin, &scanbinlen, optarg, scanlen, scanlength * 2, scanlength * 2, false); if (rc <= 0) { fprintf(stderr, "Invalid scanmap string %s\n", optarg); *exit_code = 1; return 0; } } else { memset(scan_bin, 0, scanlength); } scan_map_seen = true; } if (!ykp_configure_command(cfg, SLOT_SCAN_MAP)) return 0; break; case 'o': if (*zap) { fprintf(stderr, "No options can be given with zap (-z).\n"); *exit_code = 1; return 0; } if (strncmp(optarg, "salt=", 5) == 0) salt = strdup(optarg+5); else if (strncmp(optarg, "fixed=", 6) == 0) { if (_set_fixed(optarg + 6, cfg) != 1) { fprintf(stderr, "Invalid fixed string: %s\n", optarg + 6); *exit_code = 1; return 0; } } else if (strncmp(optarg, "uid=", 4) == 0) { const char *uid = optarg+4; size_t uidlen = strlen (uid); unsigned char uidbin[256]; size_t uidbinlen = 0; int rc = hex_modhex_decode(uidbin, &uidbinlen, uid, uidlen, 12, 12, false); if (rc <= 0) { fprintf(stderr, "Invalid uid string: %s\n", uid); *exit_code = 1; return 0; } /* for OATH-HOTP and CHAL-RESP, uid is not applicable */ if ((ycfg->tktFlags & TKTFLAG_OATH_HOTP) == TKTFLAG_OATH_HOTP || (ycfg->tktFlags & TKTFLAG_CHAL_RESP) == TKTFLAG_CHAL_RESP) { fprintf(stderr, "Option uid= not valid with -ooath-hotp or -ochal-resp.\n" ); *exit_code = 1; return 0; } ykp_set_uid(cfg, uidbin, uidbinlen); } else if (strncmp(optarg, "access=", 7) == 0) { const char *acc = optarg+7; size_t acclen = strlen (acc); unsigned char accbin[256]; size_t accbinlen = 0; int rc = hex_modhex_decode (accbin, &accbinlen, acc, acclen, 12, 12, false); if (rc <= 0) { fprintf(stderr, "Invalid access code string: %s\n", acc); *exit_code = 1; return 0; } ykp_set_access_code(cfg, accbin, accbinlen); new_access_code = true; } #define TKTFLAG(o, f) \ else if (strcmp(optarg, o) == 0) { \ if (!ykp_set_tktflag_##f(cfg, true)) { \ *exit_code = 1; \ return 0; \ } \ } else if (strcmp(optarg, "-" o) == 0) { \ if (! ykp_set_tktflag_##f(cfg, false)) { \ *exit_code = 1; \ return 0; \ } \ } TKTFLAG("tab-first", TAB_FIRST) TKTFLAG("append-tab1", APPEND_TAB1) TKTFLAG("append-tab2", APPEND_TAB2) TKTFLAG("append-delay1", APPEND_DELAY1) TKTFLAG("append-delay2", APPEND_DELAY2) TKTFLAG("append-cr", APPEND_CR) TKTFLAG("protect-cfg2", PROTECT_CFG2) TKTFLAG("oath-hotp", OATH_HOTP) TKTFLAG("chal-resp", CHAL_RESP) #undef TKTFLAG #define CFGFLAG(o, f) \ else if (strcmp(optarg, o) == 0) { \ if (! ykp_set_cfgflag_##f(cfg, true)) { \ *exit_code = 1; \ return 0; \ } \ } else if (strcmp(optarg, "-" o) == 0) { \ if (! ykp_set_cfgflag_##f(cfg, false)) { \ *exit_code = 1; \ return 0; \ } \ } CFGFLAG("send-ref", SEND_REF) CFGFLAG("ticket-first", TICKET_FIRST) CFGFLAG("pacing-10ms", PACING_10MS) CFGFLAG("pacing-20ms", PACING_20MS) CFGFLAG("allow-hidtrig", ALLOW_HIDTRIG) CFGFLAG("static-ticket", STATIC_TICKET) CFGFLAG("short-ticket", SHORT_TICKET) CFGFLAG("strong-pw1", STRONG_PW1) CFGFLAG("strong-pw2", STRONG_PW2) CFGFLAG("man-update", MAN_UPDATE) CFGFLAG("oath-hotp8", OATH_HOTP8) CFGFLAG("oath-fixed-modhex1", OATH_FIXED_MODHEX1) CFGFLAG("oath-fixed-modhex2", OATH_FIXED_MODHEX2) CFGFLAG("oath-fixed-modhex", OATH_FIXED_MODHEX) CFGFLAG("chal-yubico", CHAL_YUBICO) CFGFLAG("chal-hmac", CHAL_HMAC) CFGFLAG("hmac-lt64", HMAC_LT64) CFGFLAG("chal-btn-trig", CHAL_BTN_TRIG) #undef CFGFLAG else if (strncmp(optarg, "oath-imf=", 9) == 0) { unsigned long imf; if (!(ycfg->tktFlags & TKTFLAG_OATH_HOTP) == TKTFLAG_OATH_HOTP) { fprintf(stderr, "Option oath-imf= only valid with -ooath-hotp or -ooath-hotp8.\n" ); *exit_code = 1; return 0; } if (sscanf(optarg+9, "%lu", &imf) != 1 || /* yubikey limitations */ imf > 65535*16 || imf % 16 != 0) { fprintf(stderr, "Invalid value %s for oath-imf=.\n", optarg+9 ); *exit_code = 1; return 0; } if (! ykp_set_oath_imf(cfg, imf)) { *exit_code = 1; return 0; } } else if (strncmp(optarg, "oath-id=", 8) == 0 || strcmp(optarg, "oath-id") == 0) { if (_set_oath_id(optarg, cfg, ycfg, yk, st) != 1) { *exit_code = 1; return 0; } } #define EXTFLAG(o, f) \ else if (strcmp(optarg, o) == 0) { \ if (! ykp_set_extflag_##f(cfg, true)) { \ *exit_code = 1; \ return 0; \ } \ } else if (strcmp(optarg, "-" o) == 0) { \ if (! ykp_set_extflag_##f(cfg, false)) { \ *exit_code = 1; \ return 0; \ } \ } EXTFLAG("serial-btn-visible", SERIAL_BTN_VISIBLE) EXTFLAG("serial-usb-visible", SERIAL_USB_VISIBLE) EXTFLAG("serial-api-visible", SERIAL_API_VISIBLE) EXTFLAG("use-numeric-keypad", USE_NUMERIC_KEYPAD) EXTFLAG("fast-trig", FAST_TRIG) EXTFLAG("allow-update", ALLOW_UPDATE) EXTFLAG("dormant", DORMANT) #undef EXTFLAG else { fprintf(stderr, "Unknown option '%s'\n", optarg); fputs(usage, stderr); *exit_code = 1; return 0; } break; case 'v': *verbose = true; break; case 'y': *autocommit = true; break; case 'h': default: fputs(usage, stderr); *exit_code = 0; return 0; } } if (!slot_chosen && !ndef_seen && !swap_seen && !usb_mode_seen && !scan_map_seen) { fprintf(stderr, "A slot must be chosen with -1 or -2.\n"); *exit_code = 1; return 0; } if (update_seen) { struct config_st *core_config = (struct config_st *) ykp_core_config(cfg); if ((core_config->tktFlags & TKTFLAG_UPDATE_MASK) != core_config->tktFlags) { fprintf(stderr, "Unallowed ticket flags with update.\n"); *exit_code = 1; return 0; } if ((core_config->cfgFlags & CFGFLAG_UPDATE_MASK) != core_config->cfgFlags) { fprintf(stderr, "Unallowed cfg flags with update.\n"); *exit_code = 1; return 0; } if ((core_config->extFlags & EXTFLAG_UPDATE_MASK) != core_config->extFlags) { fprintf(stderr, "Unallowed ext flags with update.\n"); *exit_code = 1; return 0; } } if (*aesviahash) { bool long_key_valid = false; int res = 0; /* for OATH-HOTP, 160 bits key is also valid */ if ((ycfg->tktFlags & TKTFLAG_OATH_HOTP) == TKTFLAG_OATH_HOTP) long_key_valid = true; /* for HMAC (not Yubico) challenge-response, 160 bits key is also valid */ if ((ycfg->tktFlags & TKTFLAG_CHAL_RESP) == TKTFLAG_CHAL_RESP && (ycfg->cfgFlags & CFGFLAG_CHAL_HMAC) == CFGFLAG_CHAL_HMAC) { long_key_valid = true; } if (long_key_valid && strlen(aeshash) == 40) { res = ykp_HMAC_key_from_hex(cfg, aeshash); } else { res = ykp_AES_key_from_hex(cfg, aeshash); } if (res) { fprintf(stderr, "Bad %s key: %s\n", long_key_valid ? "HMAC":"AES", aeshash); fflush(stderr); return 0; } } return 1; }