Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement session recreate when using YubiKey5 for Yubihsm2 authentication in PKCS11 #268

Open
YoussKam opened this issue Jun 15, 2022 · 3 comments
Assignees
Labels
enhancement New feature or request

Comments

@YoussKam
Copy link

Hi,

We have implement the authentication of the Yubihsm2 using the Yubikey-auth in PKCS11.
The problem is that after 30 seconds of inactivity, the session is closed/pause. We don't know have to recreate the session is this case.

Without the Yubikey, this is working properly with the original code (lib/yubihsm.c).


yh_rc yh_send_secure_msg(yh_session *session, yh_cmd cmd, const uint8_t *data,
size_t data_len, yh_cmd *response_cmd,
uint8_t *response, size_t *response_len) {

size_t saved_len = *response_len;

yh_rc yrc = send_encrypted_msg(&session->s, cmd, data, data_len, response_cmd,
response, response_len);

if ((yrc == YHR_DEVICE_INVALID_SESSION ||
yrc == YHR_DEVICE_AUTHENTICATION_FAILED) &&
session->authkey_id) {
DBG_INFO("Recreating session");
yrc = yh_create_session(session->s.parent, session->authkey_id,
session->key_enc, SCP_KEY_LEN, session->key_mac,
SCP_KEY_LEN, true, &session);
if (yrc != YHR_SUCCESS) {
return yrc;
}

*response_len = saved_len;
yrc = send_encrypted_msg(&session->s, cmd, data, data_len, response_cmd,
                         response, response_len);

}

return yrc;
}

Our code for the Yubikey session open is the following (pkcs11/yubihsm_pkcs11.c):


CK_DEFINE_FUNCTION(CK_RV, C_Login)
(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType, CK_UTF8CHAR_PTR pPin,
CK_ULONG ulPinLen) {

DIN;

if (g_yh_initialized == false) {
DBG_ERR("libyubihsm is not initialized or already finalized");
return CKR_CRYPTOKI_NOT_INITIALIZED;
}

if (userType != CKU_USER) {
DBG_ERR("Inalid user type, only regular user allowed");
return CKR_USER_TYPE_INVALID;
}

CK_UTF8CHAR prefix = *pPin;
if (prefix == '@' || prefix =='#') {
pPin++;
ulPinLen--;
}

if (ulPinLen < YUBIHSM_PKCS11_MIN_PIN_LEN ||
ulPinLen > YUBIHSM_PKCS11_MAX_PIN_LEN) {
DBG_ERR("Wrong PIN length, must be [%d, %d] got %lu",
YUBIHSM_PKCS11_MIN_PIN_LEN, YUBIHSM_PKCS11_MAX_PIN_LEN, ulPinLen);
return CKR_ARGUMENTS_BAD;
}

uint16_t key_id = 0;
size_t key_id_len = sizeof(key_id);
char tmpPin[5] = {0};
memcpy(tmpPin, pPin, 4);

if (hex_decode((const char *) tmpPin, (uint8_t *) &key_id, &key_id_len) ==
false ||
key_id_len != sizeof(key_id)) {
DBG_ERR(
"PIN contains invalid characters, first four digits must be [0-9A-Fa-f]");
return CKR_PIN_INCORRECT;
}

key_id = ntohs(key_id);

pPin += 4;
ulPinLen -= 4;

yubihsm_pkcs11_session *session = 0;
CK_RV rv = get_session(&g_ctx, hSession, &session, SESSION_NOT_AUTHENTICATED);
if (rv != CKR_OK) {
DBG_ERR("Invalid session ID: %lu", hSession);
return rv;
}

yh_rc yrc = YHR_SUCCESS;

if (prefix == '@') { // Asymmetric authentication

uint8_t sk_oce[YH_EC_P256_PRIVKEY_LEN] = {0},
        pk_oce[YH_EC_P256_PUBKEY_LEN] = {0},
        pk_sd[YH_EC_P256_PUBKEY_LEN] = {0};
size_t pk_sd_len = sizeof(pk_sd);
yrc = yh_util_derive_ec_p256_key(pPin, ulPinLen, sk_oce, sizeof(sk_oce),
                                 pk_oce, sizeof(pk_oce));
if (yrc != YHR_SUCCESS) {
  DBG_ERR("Failed to derive asymmetric key: %s", yh_strerror(yrc));
  rv = CKR_FUNCTION_FAILED;
  goto c_l_out;
}

yrc = yh_util_get_device_pubkey(session->slot->connector, pk_sd, &pk_sd_len,
                                NULL);
if (yrc != YHR_SUCCESS) {
  DBG_ERR("Failed to get device public key: %s", yh_strerror(yrc));
  rv = CKR_FUNCTION_FAILED;
  goto c_l_out;
}

if (pk_sd_len != YH_EC_P256_PUBKEY_LEN) {
  DBG_ERR("Invalid device public key");
  rv = CKR_FUNCTION_FAILED;
  goto c_l_out;
}

int hits = 0;
for (ListItem *item = g_ctx.device_pubkeys.head; item != NULL;
     item = item->next) {
  if (!memcmp(item->data, pk_sd, YH_EC_P256_PUBKEY_LEN)) {
    hits++;
  }
}

if (g_ctx.device_pubkeys.length > 0 && hits == 0) {
  DBG_ERR("Failed to validate device public key");
  rv = CKR_FUNCTION_FAILED;
  goto c_l_out;
}

yrc = yh_create_session_asym(session->slot->connector, key_id, sk_oce,
                             sizeof(sk_oce), pk_sd, pk_sd_len,
                             &session->slot->device_session);
if (yrc != YHR_SUCCESS) {
  DBG_ERR("Failed to create asymmetric session: %s", yh_strerror(yrc));
  if (yrc == YHR_SESSION_AUTHENTICATION_FAILED) {
    rv = CKR_PIN_INCORRECT;
  } else {
    rv = CKR_FUNCTION_FAILED;
  }
  goto c_l_out;
}

} else if (prefix == '#') { // Yubico Key auhtentication

int verbose = 1;
ykhsmauth_state *state = NULL;
ykhsmauth_rc ykhsmauthrc = ykhsmauth_init(&state, verbose);

if (ykhsmauthrc != YKHSMAUTHR_SUCCESS) {
  fprintf(stderr, "Failed to initialize libykhsmauth\n");
  rv = CKR_FUNCTION_FAILED;
  goto c_l_out;
}

ykhsmauthrc = ykhsmauth_connect(state, NULL);
if (ykhsmauthrc != YKHSMAUTHR_SUCCESS) {
  fprintf(stderr, "Failed to connect to the YubiKey: %s\n",
          ykhsmauth_strerror(ykhsmauthrc));
  return -1;
}

uint8_t host_challenge[YH_EC_P256_PUBKEY_LEN] = {0};
size_t host_challenge_len = sizeof(host_challenge);
const char *label = getenv("YK_AUTHKEY_LABEL");
if (label == NULL)
{
  fprintf(stderr, "PKCS11 YubiKey Authentication Key label YK_AUTHKEY_LABEL must be defined");
  rv = CKR_FUNCTION_FAILED;
  goto c_l_out;
}

ykhsmauthrc = ykhsmauth_get_challenge(state, label, host_challenge,
                                      &host_challenge_len);
if (ykhsmauthrc != YKHSMAUTHR_SUCCESS) {
  fprintf(stderr, "Failed to get host challenge from the YubiKey: %s\n",
          ykhsmauth_strerror(ykhsmauthrc));
  ykhsmauth_disconnect(state);
  rv = CKR_FUNCTION_FAILED;
  goto c_l_out;
}

uint8_t card_pubkey[YH_EC_P256_PUBKEY_LEN] = {0};
size_t card_pubkey_len = 0;

if (host_challenge_len == YH_EC_P256_PUBKEY_LEN) {
  card_pubkey_len = sizeof(card_pubkey);
  yrc = yh_util_get_device_pubkey(session->slot->connector, card_pubkey,
                                  &card_pubkey_len, NULL);
  if (yrc != YHR_SUCCESS) {
    fprintf(stderr, "Failed to retrieve device pubkey: %s\n",
            yh_strerror(yrc));
    ykhsmauth_disconnect(state);
    rv = CKR_FUNCTION_FAILED;
    goto c_l_out;
  }

  if (card_pubkey_len != YH_EC_P256_PUBKEY_LEN) {
    fprintf(stderr, "Invalid device pubkey\n");
    ykhsmauth_disconnect(state);
    rv = CKR_FUNCTION_FAILED;
    goto c_l_out;
  }
}

uint8_t card_cryptogram[YH_KEY_LEN] = {0};
size_t card_cryptogram_len = sizeof(card_cryptogram);
uint8_t *yh_context = 0;

yrc = yh_begin_create_session(session->slot->connector, key_id, &yh_context,
                              host_challenge, &host_challenge_len,
                              card_cryptogram, &card_cryptogram_len,
                              &session->slot->device_session);
if (yrc != YHR_SUCCESS) {
  fprintf(stderr, "Failed to create session: %s\n", yh_strerror(yrc));
  ykhsmauth_disconnect(state);
  rv = CKR_FUNCTION_FAILED;
  goto c_l_out;
}

uint8_t key_s_enc[YH_KEY_LEN] = {0};
uint8_t key_s_mac[YH_KEY_LEN] = {0};
uint8_t key_s_rmac[YH_KEY_LEN] = {0};
uint8_t retries = 0;

ykhsmauthrc =
  ykhsmauth_calculate_ex(state, label, yh_context, 2 * host_challenge_len,
                         card_pubkey, card_pubkey_len, card_cryptogram,
                         card_cryptogram_len, pPin, ulPinLen, key_s_enc,
                         sizeof(key_s_enc), key_s_mac, sizeof(key_s_mac),
                         key_s_rmac, sizeof(key_s_rmac), &retries);

insecure_memzero(pPin, ulPinLen);

ykhsmauth_disconnect(state);
if (ykhsmauthrc != YKHSMAUTHR_SUCCESS) {
  fprintf(stderr, "Failed to get session keys from the YubiKey: %s",
          ykhsmauth_strerror(ykhsmauthrc));
  if (ykhsmauthrc == YKHSMAUTHR_WRONG_PW) {
    fprintf(stderr, ", %d attempts remaining", retries);
  }
  fprintf(stderr, "\n");

  return -1;
}

yrc =
  yh_finish_create_session(session->slot->device_session, key_s_enc,
                           sizeof(key_s_enc), key_s_mac, sizeof(key_s_mac),
                           key_s_rmac, sizeof(key_s_rmac), card_cryptogram,
                           card_cryptogram_len);
if (yrc != YHR_SUCCESS) {
  fprintf(stderr, "Failed to create session: %s\n", yh_strerror(yrc));
  rv = CKR_FUNCTION_FAILED;
  goto c_l_out;
}

uint8_t session_id = 0;
yrc = yh_get_session_id(session->slot->device_session, &session_id);
if (yrc != YHR_SUCCESS) {
  fprintf(stderr, "Failed to create session: %s\n", yh_strerror(yrc));
  rv = CKR_FUNCTION_FAILED;
  goto c_l_out;
}


fprintf(stderr, "**************** 01 Session::: id: %d, connector: %s, authkey_id: %d, key_enc: %02x%02x%02x%02x, key_mac: %02x%02x%02x%02x\n", 
session->id, session->slot->connector_name, session->slot->device_session->authkey_id, 
session->slot->device_session->key_enc[0], session->slot->device_session->key_enc[1], session->slot->device_session->key_enc[2], session->slot->device_session->key_enc[3], 
session->slot->device_session->key_mac[0], session->slot->device_session->key_mac[1], session->slot->device_session->key_mac[2], session->slot->device_session->key_mac[3]);


fprintf(stderr, "Created session %d\n", session_id);

} else { // Symmetric authentication
yrc =
yh_create_session_derived(session->slot->connector, key_id, pPin,
ulPinLen, true, &session->slot->device_session);
if (yrc != YHR_SUCCESS) {
DBG_ERR("Failed to create session: %s", yh_strerror(yrc));
if (yrc == YHR_CRYPTOGRAM_MISMATCH) {
rv = CKR_PIN_INCORRECT;
} else {
rv = CKR_FUNCTION_FAILED;
}
goto c_l_out;
}
}

list_iterate(&session->slot->pkcs11_sessions, login_sessions);

DOUT;

c_l_out:

release_session(&g_ctx, session);

return rv;
}

But parameters given in PKCS#11 are not available in the lib/yubihsm.c.
So how to recreate the session after 30secs of inactivity.

Thanks in advance,
Youssiph

@qpernil
Copy link
Contributor

qpernil commented Aug 23, 2022

This is nice work, and a feature that has been considered but not yet implemented. The situation is rather complicated because session recreation is performed in yh_send_secure_msg, and currently depends on having long-term SCP03 keys key_enc and key_mac available in the library. yubihsm-auth is specifically designed so that no long-term secrets are leaked outside the YubiKey and YubiHSM. Hence you need to store all the parameters for all the yubihsmauth calls in the yh_session, and essentially duplicate the functionality you have already implemented there as well. So that would be the reader name, slot name and password for that slot. You could skip the reader name as you are doing now, but that may bite you if someone has two yubihsm-auth-capable YubiKeys attached. It would also require the YubiKey to be kept attached for the duration of the HSM session (at least if it becomes idle at any point).

@qpernil
Copy link
Contributor

qpernil commented Aug 23, 2022

As an alternative you could consider keepalives, for example sending some simple message every few seconds. yubihsm-shell uses that technique, sending an 1-byte ECHO command every 10 seconds. The timeout is 30 seconds, so 25 seconds would also work. See timer_handler in main.c

@qpernil
Copy link
Contributor

qpernil commented Aug 29, 2022

I'd also point out that keeping the password for a yubihsm-auth slot in memory kind of negates the security achieved by using it in the first place, so please consider if this is really the right path to take.

@qpernil qpernil added the enhancement New feature or request label Jan 14, 2023
@qpernil qpernil self-assigned this Jul 3, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Development

No branches or pull requests

2 participants