/* Generate an AES (128 bits) or HMAC (despite the function name) (160 bits)
 * key from user entered input.
 *
 * Use user provided salt, or use salt from an available random device.
 * If no random device is available we fall back to using 2048 bits of
 * system time data, together with the user input, as salt.
 */
int ykp_AES_key_from_passphrase(YKP_CONFIG *cfg, const char *passphrase,
				const char *salt)
{
	if (cfg) {
		char *random_places[] = {
			"/dev/srandom",
			"/dev/urandom",
			"/dev/random",
			0
		};
		char **random_place;
		uint8_t _salt[8];
		size_t _salt_len = 0;
		unsigned char buf[sizeof(cfg->ykcore_config.key) + 4];
		int rc;
		int key_bytes = _get_supported_key_length(cfg);

		assert (key_bytes <= sizeof(buf));

		if (salt) {
			_salt_len = strlen(salt);
			if (_salt_len > 8)
				_salt_len = 8;
			memcpy(_salt, salt, _salt_len);
		} else {
			for (random_place = random_places;
			     *random_place;
			     random_place++) {
				FILE *random_file = fopen(*random_place, "r");
				if (random_file) {
					size_t read_bytes = 0;

					while (read_bytes < sizeof(_salt)) {
						size_t n = fread(&_salt[read_bytes],
								 1, sizeof (_salt) - read_bytes,
								 random_file);
						read_bytes += n;
					}

					fclose(random_file);

					_salt_len = sizeof(_salt);

					break; /* from for loop */
				}
			}
		}
		if (_salt_len == 0) {
			/* There was no randomness files, so create a cheap
			   salt from time */
#                       include <ykpbkdf2.h>

			time_t t = time(NULL);
			uint8_t output[256]; /* 2048 bits is a lot! */

			yk_hmac_sha1.prf_fn(passphrase, strlen(passphrase),
					    (char *)&t, sizeof(t),
					    output, sizeof(output));
			memcpy(_salt, output, sizeof(_salt));
			_salt_len = sizeof(_salt);
		}

		rc = yk_pbkdf2(passphrase,
			       _salt, _salt_len,
			       1024,
			       buf, key_bytes,
			       &yk_hmac_sha1);

		if (rc) {
			memcpy(cfg->ykcore_config.key, buf, sizeof(cfg->ykcore_config.key));

			if (key_bytes == 20) {
				memcpy(cfg->ykcore_config.uid, buf + sizeof(cfg->ykcore_config.key), 4);
			}
		}

		memset (buf, 0, sizeof(buf));
		return rc;
	}
	return 0;
}
int ykp_write_config(const YKP_CONFIG *cfg,
		     int (*writer)(const char *buf, size_t count,
				   void *userdata),
		     void *userdata)
{
	if (cfg) {
		char buffer[256];
		struct map_st *p;
		unsigned char t_flags;
		bool key_bits_in_uid = false;

		/* for OATH-HOTP and HMAC-SHA1 challenge response, there is four bytes
		 *  additional key data in the uid field
		 */
		key_bits_in_uid = (_get_supported_key_length(cfg) == 20);

		/* fixed: */
		writer(str_fixed, strlen(str_fixed), userdata);
		writer(str_key_value_separator,
		       strlen(str_key_value_separator),
		       userdata);
		/* XXX print OATH-HOTP fixed differently based on oath-fixed-modhex etc. */
		writer(str_modhex_prefix,
		       strlen(str_key_value_separator),
		       userdata);
		yubikey_modhex_encode(buffer, (char *)cfg->ykcore_config.fixed, cfg->ykcore_config.fixedSize);
		writer(buffer, strlen(buffer), userdata);
		writer("\n", 1, userdata);

		/* uid: */
		writer(str_uid, strlen(str_uid), userdata);
		writer(str_key_value_separator,
		       strlen(str_key_value_separator),
		       userdata);
		if (key_bits_in_uid) {
			writer("n/a", 3, userdata);
		} else {
			writer(str_hex_prefix,
			       strlen(str_key_value_separator),
			       userdata);
			yubikey_hex_encode(buffer, (char *)cfg->ykcore_config.uid, UID_SIZE);
			writer(buffer, strlen(buffer), userdata);
		}
		writer("\n", 1, userdata);

		/* key: */
		writer(str_key, strlen(str_key), userdata);
		writer(str_key_value_separator,
		       strlen(str_key_value_separator),
		       userdata);
		writer(str_hex_prefix,
		       strlen(str_key_value_separator),
		       userdata);
		yubikey_hex_encode(buffer, (char *)cfg->ykcore_config.key, KEY_SIZE);
		if (key_bits_in_uid) {
			yubikey_hex_encode(buffer + KEY_SIZE * 2, (char *)cfg->ykcore_config.uid, 4);
		}
		writer(buffer, strlen(buffer), userdata);
		writer("\n", 1, userdata);

		/* acc_code: */
		writer(str_acc_code, strlen(str_acc_code), userdata);
		writer(str_key_value_separator,
		       strlen(str_key_value_separator),
		       userdata);
		writer(str_hex_prefix,
		       strlen(str_key_value_separator),
		       userdata);
		yubikey_hex_encode(buffer, (char *)cfg->ykcore_config.accCode, ACC_CODE_SIZE);
		writer(buffer, strlen(buffer), userdata);
		writer("\n", 1, userdata);

		/* ticket_flags: */
		buffer[0] = '\0';
		for (p = ticket_flags_map; p->flag; p++) {
			if ((cfg->ykcore_config.tktFlags & p->flag) == p->flag
			    && p->vcheck(cfg)) {
				if (*buffer) {
					strcat(buffer, str_flags_separator);
					strcat(buffer, p->flag_text);
				} else {
					strcpy(buffer, p->flag_text);
				}
			}
		}
		writer(str_ticket_flags, strlen(str_ticket_flags), userdata);
		writer(str_key_value_separator,
		       strlen(str_key_value_separator),
		       userdata);
		writer(buffer, strlen(buffer), userdata);
		writer("\n", 1, userdata);

		/* config_flags: */
		buffer[0] = '\0';
		t_flags = cfg->ykcore_config.cfgFlags;
		for (p = config_flags_map; p->flag; p++) {
			if ((t_flags & p->flag) == p->flag
			    && p->vcheck(cfg)
			    && (cfg->ykcore_config.tktFlags & p->tkt_context) == p->tkt_context) {
				if (*buffer) {
					strcat(buffer, str_flags_separator);
					strcat(buffer, p->flag_text);
				} else {
					strcpy(buffer, p->flag_text);
				}
				/* make sure we don't show more than one cfgFlag per value -
				   some cfgflags share value in different contexts
				*/
				t_flags -= p->flag;
			}
		}
		writer(str_config_flags, strlen(str_config_flags), userdata);
		writer(str_key_value_separator,
		       strlen(str_key_value_separator),
		       userdata);
		writer(buffer, strlen(buffer), userdata);
		writer("\n", 1, userdata);

		/* extended_flags: */
		buffer[0] = '\0';
		for (p = extended_flags_map; p->flag; p++) {
			if ((cfg->ykcore_config.extFlags & p->flag) == p->flag
			    && p->vcheck(cfg)) {
				if (*buffer) {
					strcat(buffer, str_flags_separator);
					strcat(buffer, p->flag_text);
				} else {
					strcpy(buffer, p->flag_text);
				}
			}
		}
		writer(str_extended_flags, strlen(str_extended_flags), userdata);
		writer(str_key_value_separator,
		       strlen(str_key_value_separator),
		       userdata);
		writer(buffer, strlen(buffer), userdata);
		writer("\n", 1, userdata);

		return 1;
	}
	return 0;
}
static int _ykp_legacy_export_config(const YKP_CONFIG *cfg, char *buf, size_t len) {
	if (cfg) {
		char buffer[256];
		struct map_st *p;
		unsigned char t_flags;
		bool key_bits_in_uid = false;
		YK_CONFIG ycfg = cfg->ykcore_config;
		int mode = MODE_OTP_YUBICO;

		int pos = 0;

		if((ycfg.tktFlags & TKTFLAG_OATH_HOTP) == TKTFLAG_OATH_HOTP){
			if((ycfg.cfgFlags & CFGFLAG_CHAL_HMAC) == CFGFLAG_CHAL_HMAC) {
				mode = MODE_CHAL_HMAC;
			} else if((ycfg.cfgFlags & CFGFLAG_CHAL_YUBICO) == CFGFLAG_CHAL_YUBICO) {
				mode = MODE_CHAL_YUBICO;
			} else {
				mode = MODE_OATH_HOTP;
			}
		}
		else if((ycfg.cfgFlags & CFGFLAG_STATIC_TICKET) == CFGFLAG_STATIC_TICKET) {
			mode = MODE_STATIC_TICKET;
		}

		/* for OATH-HOTP and HMAC-SHA1 challenge response, there is four bytes
		 *  additional key data in the uid field
		 */
		key_bits_in_uid = (_get_supported_key_length(cfg) == 20);

		/* fixed: or OATH id: */
		if ((ycfg.tktFlags & TKTFLAG_OATH_HOTP) == TKTFLAG_OATH_HOTP &&
		    ycfg.fixedSize) {
			/* First byte (vendor id) */
			if ((ycfg.cfgFlags & CFGFLAG_OATH_FIXED_MODHEX1) == CFGFLAG_OATH_FIXED_MODHEX1 ||
			    (ycfg.cfgFlags & CFGFLAG_OATH_FIXED_MODHEX2) == CFGFLAG_OATH_FIXED_MODHEX2 ||
			    (ycfg.cfgFlags & CFGFLAG_OATH_FIXED_MODHEX) == CFGFLAG_OATH_FIXED_MODHEX) {
				yubikey_modhex_encode(buffer, (const char *)ycfg.fixed, 1);
			} else {
				yubikey_hex_encode(buffer, (const char *)ycfg.fixed, 1);
			}
			/* Second byte (token type) */
			if ((ycfg.cfgFlags & CFGFLAG_OATH_FIXED_MODHEX2) == CFGFLAG_OATH_FIXED_MODHEX2 ||
			    (ycfg.cfgFlags & CFGFLAG_OATH_FIXED_MODHEX) == CFGFLAG_OATH_FIXED_MODHEX) {
				yubikey_modhex_encode(buffer + 2, (const char *)ycfg.fixed + 1, 1);
			} else {
				yubikey_hex_encode(buffer + 2, (const char *)ycfg.fixed + 1, 1);
			}
			/* bytes 3-12 - MUI */
			if ((ycfg.cfgFlags & CFGFLAG_OATH_FIXED_MODHEX) == CFGFLAG_OATH_FIXED_MODHEX) {
				yubikey_modhex_encode(buffer + 4, (const char *)ycfg.fixed + 2, 8);
			} else {
				yubikey_hex_encode(buffer + 4, (const char *)ycfg.fixed + 2, 8);
			}
			buffer[12] = 0;
			pos += snprintf(buf, len - (size_t)pos, "%s%s%s\n", str_oath_id, str_key_value_separator, buffer);
		} else {
			yubikey_modhex_encode(buffer, (const char *)ycfg.fixed, ycfg.fixedSize);
			pos += snprintf(buf, len - (size_t)pos, "%s%s%s%s\n", str_fixed, str_key_value_separator, str_modhex_prefix, buffer);
		}

		/* uid: */
		if (key_bits_in_uid) {
			strncpy(buffer, "n/a", 3);
		} else {
			yubikey_hex_encode(buffer, (const char *)ycfg.uid, UID_SIZE);
		}
		pos += snprintf(buf + pos, len - (size_t)pos, "%s%s%s\n", str_uid, str_key_value_separator, buffer);

		/* key: */
		yubikey_hex_encode(buffer, (const char *)ycfg.key, KEY_SIZE);
		if (key_bits_in_uid) {
			yubikey_hex_encode(buffer + KEY_SIZE * 2, (const char *)ycfg.uid, 4);
		}
		pos += snprintf(buf + pos, len - (size_t)pos, "%s%s%s%s\n", str_key, str_key_value_separator, str_hex_prefix, buffer);

		/* acc_code: */
		yubikey_hex_encode(buffer, (const char*)ycfg.accCode, ACC_CODE_SIZE);
		pos += snprintf(buf + pos, len - (size_t)pos, "%s%s%s%s\n", str_acc_code, str_key_value_separator, str_hex_prefix, buffer);

		/* OATH IMF: */
		if ((ycfg.tktFlags & TKTFLAG_OATH_HOTP) == TKTFLAG_OATH_HOTP &&
		    capability_has_oath_imf(cfg)) {
			pos += snprintf(buf + pos, len - (size_t)pos, "%s%s%s%lx\n", str_oath_imf, str_key_value_separator, str_hex_prefix, ykp_get_oath_imf(cfg));
		}

		/* ticket_flags: */
		buffer[0] = '\0';
		for (p = _ticket_flags_map; p->flag; p++) {
			if ((ycfg.tktFlags & p->flag) == p->flag
			    && p->capability(cfg)
			    && (mode & p->mode) == mode) {
				if (*buffer) {
					strcat(buffer, str_flags_separator);
					strcat(buffer, p->flag_text);
				} else {
					strcpy(buffer, p->flag_text);
				}
			}
		}
		pos += snprintf(buf + pos, len - (size_t)pos, "%s%s%s\n", str_ticket_flags, str_key_value_separator, buffer);

		/* config_flags: */
		buffer[0] = '\0';
		t_flags = ycfg.cfgFlags;
		for (p = _config_flags_map; p->flag; p++) {
			if ((t_flags & p->flag) == p->flag
			    && p->capability(cfg)
			    && (mode & p->mode) == mode) {
				if (*buffer) {
					strcat(buffer, str_flags_separator);
					strcat(buffer, p->flag_text);
				} else {
					strcpy(buffer, p->flag_text);
				}
				/* make sure we don't show more than one cfgFlag per value -
				   some cfgflags share value in different contexts
				*/
				t_flags -= p->flag;
			}
		}
		pos += snprintf(buf + pos, len - (size_t)pos, "%s%s%s\n", str_config_flags, str_key_value_separator, buffer);

		/* extended_flags: */
		buffer[0] = '\0';
		for (p = _extended_flags_map; p->flag; p++) {
			if ((ycfg.extFlags & p->flag) == p->flag
			    && p->capability(cfg)
			    && (mode & p->mode) == mode) {
				if (*buffer) {
					strcat(buffer, str_flags_separator);
					strcat(buffer, p->flag_text);
				} else {
					strcpy(buffer, p->flag_text);
				}
			}
		}
		pos += snprintf(buf + pos, len - (size_t)pos, "%s%s%s\n", str_extended_flags, str_key_value_separator, buffer);

		return pos;
	}
	return 0;
}