/* Read the factory programmed serial number from a YubiKey.
 * The possibility to retreive the serial number might be disabled
 * using configuration, so it should not be considered a fatal error
 * to not be able to read the serial number using this function.
 *
 * Serial number reading might also be configured to require user
 * interaction (YubiKey button press) on startup, in which case flags
 * might have to have YK_FLAG_MAYBLOCK set - haven't tried that.
 *
 * The slot parameter is here for future purposes only.
 */
int yk_get_serial(YK_KEY *yk, uint8_t slot, unsigned int flags, unsigned int *serial)
{
	unsigned char buf[FEATURE_RPT_SIZE * 2];
	unsigned int response_len = 0;
	unsigned int expect_bytes = 0;

	memset(buf, 0, sizeof(buf));

	if (!yk_write_to_key(yk, SLOT_DEVICE_SERIAL, &buf, 0))
		return 0;

	expect_bytes = 4;

	if (! yk_read_response_from_key(yk, slot, flags,
					&buf, sizeof(buf),
					expect_bytes,
					&response_len))
		return 0;

	/* Serial number is stored in big endian byte order, despite
	 * everything else in the YubiKey being little endian - for
	 * some good reason I don't remember.
	 */
	*serial =
		(buf[0] << 24) +
		(buf[1] << 16) +
		(buf[2] << 8) +
		(buf[3]);

	return 1;
}
int yk_get_capabilities(YK_KEY *yk, uint8_t slot, unsigned int flags,
		unsigned char *capabilities, unsigned int *len)
{
	unsigned int response_len = 0;

	if (!yk_write_to_key(yk, SLOT_YK4_CAPABILITIES, capabilities, 0))
		return 0;

	if (! yk_read_response_from_key(yk, slot, flags,
					capabilities, *len, 0, /* we have no idea how much data we'll get */
					&response_len))
		return 0;

	/* the first data of the capabilities string is the length */
	response_len = capabilities[0];
	response_len++;

	/* validate the length we got back from the hardware */
	if (response_len > *len) {
		yk_errno = YK_EWRONGSIZ;
		return 0;
	}

	*len = response_len;
	return 1;
}
Example #3
0
int challenge_response(YK_KEY *yk, int slot,
		       char *challenge, unsigned int len,
		       bool hmac, unsigned int flags, bool verbose,
		       char *response, int res_size, unsigned int *res_len)
{
	int yk_cmd;
	unsigned int response_len = 0;
	unsigned int expect_bytes = 0;

	if (res_size < sizeof(64 + 16))
	  return 0;

	memset(response, 0, res_size);

	if (verbose) {
		fprintf(stderr, "Sending %i bytes %s challenge to slot %i\n", len, (hmac == true)?"HMAC":"Yubico", slot);
		//_yk_hexdump(challenge, len);
	}

	switch(slot) {
	case 1:
		yk_cmd = (hmac == true) ? SLOT_CHAL_HMAC1 : SLOT_CHAL_OTP1;
		break;
	case 2:
		yk_cmd = (hmac == true) ? SLOT_CHAL_HMAC2 : SLOT_CHAL_OTP2;
		break;
	default:
		return 0;
	}

	if (!yk_write_to_key(yk, yk_cmd, challenge, len))
		return 0;

	if (verbose) {
		fprintf(stderr, "Reading response...\n");
	}

	/* HMAC responses are 160 bits, Yubico 128 */
	expect_bytes = (hmac == true) ? 20 : 16;

	if (! yk_read_response_from_key(yk, slot, flags,
					response, res_size,
					expect_bytes,
					&response_len))
		return 0;

	if (hmac && response_len > 20)
		response_len = 20;
	if (! hmac && response_len > 16)
		response_len = 16;

	*res_len = response_len;

	return 1;
}
static int _yk_write(YK_KEY *yk, uint8_t yk_cmd, unsigned char *buf, size_t len)
{
	YK_STATUS stat;
	int seq;

	/* Get current sequence # from status block */

	if (!yk_get_status(yk, &stat /*, 0*/))
		return 0;

	seq = stat.pgmSeq;

	/* Write to Yubikey */
	if (!yk_write_to_key(yk, yk_cmd, buf, len))
		return 0;

	/* When the Yubikey clears the SLOT_WRITE_FLAG, it has processed the last write.
	 * This wait can't be done in yk_write_to_key since some users of that function
	 * want to get the bytes in the status message, but when writing configuration
	 * we don't expect any data back.
	 */
	if(!yk_wait_for_key_status(yk, yk_cmd, 0, WAIT_FOR_WRITE_FLAG, false, SLOT_WRITE_FLAG, NULL))
		return 0;

	/* Verify update */

	if (!yk_get_status(yk, &stat /*, 0*/))
		return 0;

	yk_errno = YK_EWRITEERR;

	/* when both configurations from a YubiKey is erased it will return
	 * pgmSeq 0, if one is still configured after an erase pgmSeq is
	 * counted up as usual. */
	if((stat.touchLevel & (CONFIG1_VALID | CONFIG2_VALID)) == 0 && stat.pgmSeq == 0) {
		return 1;
	}
	return stat.pgmSeq != seq;
}
/*
 * This function is for doing HMAC-SHA1 or Yubico challenge-response with a key.
 */
int yk_challenge_response(YK_KEY *yk, uint8_t yk_cmd, int may_block,
		unsigned int challenge_len, const unsigned char *challenge,
		unsigned int response_len, unsigned char *response)
{
	unsigned int flags = 0;
	unsigned int bytes_read = 0;
	unsigned int expect_bytes = 0;

	switch(yk_cmd) {
	case SLOT_CHAL_HMAC1:
	case SLOT_CHAL_HMAC2:
		expect_bytes = 20;
		break;
	case SLOT_CHAL_OTP1:
	case SLOT_CHAL_OTP2:
		expect_bytes = 16;
		break;
	default:
		yk_errno = YK_EINVALIDCMD;
		return 0;
	}

	if (may_block)
		flags |= YK_FLAG_MAYBLOCK;

	if (! yk_write_to_key(yk, yk_cmd, challenge, challenge_len)) {
		return 0;
	}

	if (! yk_read_response_from_key(yk, yk_cmd, flags,
				response, response_len,
				expect_bytes,
				&bytes_read)) {
		return 0;
	}
	return 1;
}
Example #6
0
/* Returns 0 on error and length of response on success 
 * slot = 1 or 2 (slot on yubikey)
 * challenge = challenge data (must be 32 bytes)
 * response = 64 byte buffer
 */
int yubi_hmac_challenge_response(unsigned char slot, unsigned char *challenge,
    unsigned char *response)
{
  YK_KEY *yk = NULL;
  bool error = true;
  int exit_code = 0;
  int yk_cmd;
  unsigned int response_len = 0;

  if (!yk_init()) {
    printf("\nykchalresp.c:%d ykp_errno: %d yk_errno: %d\n", __LINE__, ykp_errno, yk_errno);
    exit_code = 2;
    goto err;
  }

  ykp_errno = 0;
  yk_errno = 0;

  if (!(yk = yk_open_first_key())) {
    printf("\nykchalresp.c:%d ykp_errno: %d yk_errno: %d\n", __LINE__, ykp_errno, yk_errno);
    exit_code = 1;
    goto err;
  }

  memset(response, 0, 64);

  switch(slot) {
  case 1:
    yk_cmd = SLOT_CHAL_HMAC1;
    break;
  case 2:
    yk_cmd = SLOT_CHAL_HMAC2;
    break;
  default:
    goto err;
  }

  while (! (
      yk_write_to_key(yk, yk_cmd, challenge, 32)
      && yk_read_response_from_key(yk, slot, YK_FLAG_MAYBLOCK,
          response, 64, 20, &response_len)
    ) ) {
    if (yk_errno == 4) {
      yk_errno = 0;
      sleep(1);
      continue;
    } else {
      printf("\nykchalresp.c:%d ykp_errno: %d yk_errno: %d\n", __LINE__, ykp_errno, yk_errno);
      exit_code = 4;
      goto err;
    }
  }

  if (response_len > 20) {
    memset(&response[20], 0, 44);
    response_len = 20;
  }

  exit_code = 0;
  error = false;

err:
  if (error || exit_code != 0) {
    report_yk_error();
#ifdef DEBUG
    SDMCKT_debug_tracking(__FILE__,__LINE__);
#endif
  }

  if (yk && !yk_close_key(yk)) {
    report_yk_error();
#ifdef DEBUG
    SDMCKT_debug_tracking(__FILE__,__LINE__);
#endif
  }

  if (!yk_release()) {
    report_yk_error();
#ifdef DEBUG
    SDMCKT_debug_tracking(__FILE__,__LINE__);
#endif
  }

  if (response_len == 0) {
    memset(response, 0, 64);
#ifdef DEBUG
    SDMCKT_debug_tracking(__FILE__,__LINE__);
#endif
  }

  return response_len;
}
int yk_write_config(YK_KEY *yk, YK_CONFIG *cfg, int confnum,
		    unsigned char *acc_code)
{
	unsigned char buf[sizeof(YK_CONFIG) + ACC_CODE_SIZE];
	YK_STATUS stat;
	int seq;
	uint8_t slot;

	/* Get current sequence # from status block */

	if (!yk_get_status(yk, &stat /*, 0*/))
		return 0;

	seq = stat.pgmSeq;

	/* Update checksum and insert config block in buffer if present */

	memset(buf, 0, sizeof(buf));

	if (cfg) {
		cfg->crc = ~yubikey_crc16 ((unsigned char *) cfg,
					   sizeof(YK_CONFIG) - sizeof(cfg->crc));
		cfg->crc = yk_endian_swap_16(cfg->crc);
		memcpy(buf, cfg, sizeof(YK_CONFIG));
	}

	/* Append current access code if present */

	if (acc_code)
		memcpy(buf + sizeof(YK_CONFIG), acc_code, ACC_CODE_SIZE);

	/* Write to Yubikey */

	switch(confnum) {
	case 1:
		slot = SLOT_CONFIG;
		break;
	case 2:
		slot = SLOT_CONFIG2;
		break;
	}

	if (!yk_write_to_key(yk, slot, buf, sizeof(buf)))
		return 0;

	/* When the Yubikey clears the SLOT_WRITE_FLAG, it has processed the last write.
	 * This wait can't be done in yk_write_to_key since some users of that function
	 * want to get the bytes in the status message, but when writing configuration
	 * we don't expect any data back.
	 */
	yk_wait_for_key_status(yk, slot, 0, WAIT_FOR_WRITE_FLAG, false, SLOT_WRITE_FLAG, NULL);

	/* Verify update */

	if (!yk_get_status(yk, &stat /*, 0*/))
		return 0;

	yk_errno = YK_EWRITEERR;
	if (cfg) {
		return stat.pgmSeq != seq;
	}

	return stat.pgmSeq == 0;

}