/* just read the username from the client key exchange. */ int _gnutls_proc_psk_client_kx (gnutls_session_t session, uint8_t * data, size_t _data_size) { ssize_t data_size = _data_size; int ret; gnutls_datum_t username, psk_key; gnutls_psk_server_credentials_t cred; psk_auth_info_t info; cred = (gnutls_psk_server_credentials_t) _gnutls_get_cred (session, GNUTLS_CRD_PSK, NULL); if (cred == NULL) { gnutls_assert (); return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } if ((ret = _gnutls_auth_info_set (session, GNUTLS_CRD_PSK, sizeof (psk_auth_info_st), 1)) < 0) { gnutls_assert (); return ret; } DECR_LEN (data_size, 2); username.size = _gnutls_read_uint16 (&data[0]); DECR_LEN (data_size, username.size); username.data = &data[2]; /* copy the username to the auth info structures */ info = _gnutls_get_auth_info (session); if (username.size > MAX_USERNAME_SIZE) { gnutls_assert (); return GNUTLS_E_ILLEGAL_SRP_USERNAME; } memcpy (info->username, username.data, username.size); info->username[username.size] = 0; ret = _gnutls_psk_pwd_find_entry(session, info->username, &psk_key); if (ret < 0) return gnutls_assert_val(ret); ret = _gnutls_set_psk_session_key (session, &psk_key, NULL); if (ret < 0) { gnutls_assert (); goto error; } ret = 0; error: _gnutls_free_datum(&psk_key); return ret; }
/* Process the client key exchange message */ static int _gnutls_proc_rsa_psk_client_kx(gnutls_session_t session, uint8_t * data, size_t _data_size) { gnutls_datum_t username; psk_auth_info_t info; gnutls_datum_t plaintext; gnutls_datum_t ciphertext; gnutls_datum_t pwd_psk = { NULL, 0 }; int ret, dsize; int randomize_key = 0; ssize_t data_size = _data_size; gnutls_psk_server_credentials_t cred; gnutls_datum_t premaster_secret = { NULL, 0 }; cred = (gnutls_psk_server_credentials_t) _gnutls_get_cred(session, GNUTLS_CRD_PSK); if (cred == NULL) { gnutls_assert(); return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } ret = _gnutls_auth_info_init(session, GNUTLS_CRD_PSK, sizeof(psk_auth_info_st), 1); if (ret < 0) { gnutls_assert(); return ret; } /*** 1. Extract user psk_identity ***/ DECR_LEN(data_size, 2); username.size = _gnutls_read_uint16(&data[0]); DECR_LEN(data_size, username.size); username.data = &data[2]; /* copy the username to the auth info structures */ info = _gnutls_get_auth_info(session, GNUTLS_CRD_PSK); if (info == NULL) { gnutls_assert(); return GNUTLS_E_INTERNAL_ERROR; } if (username.size > MAX_USERNAME_SIZE) { gnutls_assert(); return GNUTLS_E_ILLEGAL_SRP_USERNAME; } memcpy(info->username, username.data, username.size); info->username[username.size] = 0; /* Adjust data so it points to EncryptedPreMasterSecret */ data += username.size + 2; /*** 2. Decrypt and extract EncryptedPreMasterSecret ***/ DECR_LEN(data_size, 2); ciphertext.data = &data[2]; dsize = _gnutls_read_uint16(data); if (dsize != data_size) { gnutls_assert(); return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } ciphertext.size = dsize; ret = gnutls_privkey_decrypt_data(session->internals.selected_key, 0, &ciphertext, &plaintext); if (ret < 0 || plaintext.size != GNUTLS_MASTER_SIZE) { /* In case decryption fails then don't inform * the peer. Just use a random key. (in order to avoid * attack against pkcs-1 formatting). */ gnutls_assert(); _gnutls_debug_log ("auth_rsa_psk: Possible PKCS #1 format attack\n"); if (ret >= 0) { gnutls_free(plaintext.data); } randomize_key = 1; } else { /* If the secret was properly formatted, then * check the version number. */ if (_gnutls_get_adv_version_major(session) != plaintext.data[0] || (session->internals.allow_wrong_pms == 0 && _gnutls_get_adv_version_minor(session) != plaintext.data[1])) { /* No error is returned here, if the version number check * fails. We proceed normally. * That is to defend against the attack described in the paper * "Attacking RSA-based sessions in SSL/TLS" by Vlastimil Klima, * Ondej Pokorny and Tomas Rosa. */ gnutls_assert(); _gnutls_debug_log ("auth_rsa: Possible PKCS #1 version check format attack\n"); } } if (randomize_key != 0) { premaster_secret.size = GNUTLS_MASTER_SIZE; premaster_secret.data = gnutls_malloc(premaster_secret.size); if (premaster_secret.data == NULL) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } /* we do not need strong random numbers here. */ ret = gnutls_rnd(GNUTLS_RND_NONCE, premaster_secret.data, premaster_secret.size); if (ret < 0) { gnutls_assert(); goto cleanup; } } else { premaster_secret.data = plaintext.data; premaster_secret.size = plaintext.size; } /* This is here to avoid the version check attack * discussed above. */ premaster_secret.data[0] = _gnutls_get_adv_version_major(session); premaster_secret.data[1] = _gnutls_get_adv_version_minor(session); /* find the key of this username */ ret = _gnutls_psk_pwd_find_entry(session, info->username, &pwd_psk); if (ret < 0) { gnutls_assert(); goto cleanup; } ret = set_rsa_psk_session_key(session, &pwd_psk, &premaster_secret); if (ret < 0) { gnutls_assert(); goto cleanup; } ret = 0; cleanup: _gnutls_free_key_datum(&pwd_psk); _gnutls_free_temp_key_datum(&premaster_secret); return ret; }
static int server_recv_params(gnutls_session_t session, const unsigned char *data, size_t len, const gnutls_psk_server_credentials_t pskcred) { int ret; const mac_entry_st *prf; gnutls_datum_t full_client_hello; uint8_t binder_value[MAX_HASH_SIZE]; uint16_t psk_index, i; gnutls_datum_t binder_recvd = { NULL, 0 }; gnutls_datum_t key = {NULL, 0}; psk_ext_parser_st psk_parser; psk_ext_iter_st psk_iter; struct psk_st psk; psk_auth_info_t info; tls13_ticket_st ticket_data; /* These values should be set properly when session ticket is accepted. */ uint32_t ticket_age = UINT32_MAX; struct timespec ticket_creation_time = { 0, 0 }; bool resuming; ret = _gnutls13_psk_ext_parser_init(&psk_parser, data, len); if (ret < 0) { /* No PSKs advertised by client */ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) return 0; return gnutls_assert_val(ret); } _gnutls13_psk_ext_iter_init(&psk_iter, &psk_parser); for (psk_index = 0; ; psk_index++) { ret = _gnutls13_psk_ext_iter_next_identity(&psk_iter, &psk); if (ret < 0) { /* We couldn't find any usable PSK */ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) return 0; return gnutls_assert_val(ret); } /* This will unpack the session ticket if it is well * formed and has the expected name */ if (!(session->internals.flags & GNUTLS_NO_TICKETS) && (ret = _gnutls13_unpack_session_ticket(session, &psk.identity, &ticket_data)) == 0) { prf = ticket_data.prf; session->internals.resumption_requested = 1; /* Check whether ticket is stale or not */ ticket_age = psk.ob_ticket_age - ticket_data.age_add; if (ticket_age / 1000 > ticket_data.lifetime) { gnutls_assert(); tls13_ticket_deinit(&ticket_data); continue; } ret = compute_psk_from_ticket(&ticket_data, &key); if (ret < 0) { gnutls_assert(); tls13_ticket_deinit(&ticket_data); continue; } memcpy(&ticket_creation_time, &ticket_data.creation_time, sizeof(struct timespec)); tls13_ticket_deinit(&ticket_data); resuming = 1; break; } else if (pskcred && psk.ob_ticket_age == 0 && psk.identity.size > 0 && psk.identity.size <= MAX_USERNAME_SIZE) { /* _gnutls_psk_pwd_find_entry() expects 0-terminated identities */ char identity_str[MAX_USERNAME_SIZE + 1]; prf = pskcred->binder_algo; memcpy(identity_str, psk.identity.data, psk.identity.size); identity_str[psk.identity.size] = 0; /* this fails only on configuration errors; as such we always * return its error code in that case */ ret = _gnutls_psk_pwd_find_entry(session, identity_str, &key); if (ret < 0) return gnutls_assert_val(ret); resuming = 0; break; } } _gnutls13_psk_ext_iter_init(&psk_iter, &psk_parser); for (i = 0; i <= psk_index; i++) { ret = _gnutls13_psk_ext_iter_next_binder(&psk_iter, &binder_recvd); if (ret < 0) { gnutls_assert(); /* We couldn't extract binder */ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; goto fail; } } /* Get full ClientHello */ if (!_gnutls_ext_get_full_client_hello(session, &full_client_hello)) { ret = GNUTLS_E_INTERNAL_ERROR; gnutls_assert(); goto fail; } /* Compute the binder value for this PSK */ ret = compute_psk_binder(session, prf, psk_parser.binders_len+2, 0, 0, &key, &full_client_hello, resuming, binder_value); if (ret < 0) { gnutls_assert(); goto fail; } if (_gnutls_mac_get_algo_len(prf) != binder_recvd.size || safe_memcmp(binder_value, binder_recvd.data, binder_recvd.size)) { gnutls_assert(); ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; goto fail; } if (session->internals.hsk_flags & HSK_PSK_KE_MODE_DHE_PSK) _gnutls_handshake_log("EXT[%p]: selected DHE-PSK mode\n", session); else { reset_cand_groups(session); _gnutls_handshake_log("EXT[%p]: selected PSK mode\n", session); } /* save the username in psk_auth_info to make it available * using gnutls_psk_server_get_username() */ if (!resuming) { assert(psk.identity.size < sizeof(info->username)); ret = _gnutls_auth_info_init(session, GNUTLS_CRD_PSK, sizeof(psk_auth_info_st), 1); if (ret < 0) { gnutls_assert(); goto fail; } info = _gnutls_get_auth_info(session, GNUTLS_CRD_PSK); assert(info != NULL); memcpy(info->username, psk.identity.data, psk.identity.size); info->username[psk.identity.size] = 0; _gnutls_handshake_log("EXT[%p]: selected PSK identity: %s (%d)\n", session, info->username, psk_index); } else { if (session->internals.hsk_flags & HSK_EARLY_DATA_ACCEPTED) { if (session->internals.anti_replay) { ret = _gnutls_anti_replay_check(session->internals.anti_replay, ticket_age, &ticket_creation_time, &binder_recvd); if (ret < 0) { session->internals.hsk_flags &= ~HSK_EARLY_DATA_ACCEPTED; _gnutls_handshake_log("EXT[%p]: replay detected; rejecting early data\n", session); } } else { _gnutls_handshake_log("EXT[%p]: anti-replay is not enabled; rejecting early data\n", session); session->internals.hsk_flags &= ~HSK_EARLY_DATA_ACCEPTED; } } session->internals.resumed = RESUME_TRUE; _gnutls_handshake_log("EXT[%p]: selected resumption PSK identity (%d)\n", session, psk_index); } session->internals.hsk_flags |= HSK_PSK_SELECTED; /* Reference the selected pre-shared key */ session->key.binders[0].psk.data = key.data; session->key.binders[0].psk.size = key.size; session->key.binders[0].idx = psk_index; session->key.binders[0].prf = prf; session->key.binders[0].resumption = resuming; ret = _gnutls_generate_early_secrets_for_psk(session); if (ret < 0) { gnutls_assert(); goto fail; } return 0; fail: gnutls_free(key.data); return ret; }
static int proc_ecdhe_psk_client_kx(gnutls_session_t session, uint8_t * data, size_t _data_size) { int ret; gnutls_psk_server_credentials_t cred; gnutls_datum_t psk_key; psk_auth_info_t info; gnutls_datum_t username; ssize_t data_size = _data_size; cred = (gnutls_psk_server_credentials_t) _gnutls_get_cred(session, GNUTLS_CRD_PSK, NULL); if (cred == NULL) { gnutls_assert(); return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } if ((ret = _gnutls_auth_info_set(session, GNUTLS_CRD_PSK, sizeof(psk_auth_info_st), 1)) < 0) { gnutls_assert(); return ret; } DECR_LEN(data_size, 2); username.size = _gnutls_read_uint16(&data[0]); DECR_LEN(data_size, username.size); username.data = &data[2]; /* copy the username to the auth info structures */ info = _gnutls_get_auth_info(session); if (username.size > MAX_USERNAME_SIZE) { gnutls_assert(); return GNUTLS_E_ILLEGAL_SRP_USERNAME; } memcpy(info->username, username.data, username.size); info->username[username.size] = 0; /* Adjust the data */ data += username.size + 2; /* should never fail. It will always return a key even if it is * a random one */ ret = _gnutls_psk_pwd_find_entry(session, info->username, &psk_key); if (ret < 0) return gnutls_assert_val(ret); ret = _gnutls_proc_ecdh_common_client_kx(session, data, data_size, _gnutls_session_ecc_curve_get (session), &psk_key); _gnutls_free_datum(&psk_key); return ret; }
static int proc_dhe_psk_client_kx(gnutls_session_t session, uint8_t * data, size_t _data_size) { int ret; bigint_t p, g; gnutls_dh_params_t dh_params; const bigint_t *mpis; gnutls_datum_t psk_key; gnutls_psk_server_credentials_t cred; psk_auth_info_t info; gnutls_datum_t username; ssize_t data_size = _data_size; cred = (gnutls_psk_server_credentials_t) _gnutls_get_cred(session, GNUTLS_CRD_PSK, NULL); if (cred == NULL) { gnutls_assert(); return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } if ((ret = _gnutls_auth_info_set(session, GNUTLS_CRD_PSK, sizeof(psk_auth_info_st), 1)) < 0) { gnutls_assert(); return ret; } dh_params = _gnutls_get_dh_params(cred->dh_params, cred->params_func, session); mpis = _gnutls_dh_params_to_mpi(dh_params); if (mpis == NULL) { gnutls_assert(); return GNUTLS_E_NO_TEMPORARY_DH_PARAMS; } p = mpis[0]; g = mpis[1]; DECR_LEN(data_size, 2); username.size = _gnutls_read_uint16(&data[0]); DECR_LEN(data_size, username.size); username.data = &data[2]; /* copy the username to the auth info structures */ info = _gnutls_get_auth_info(session); if (username.size > MAX_USERNAME_SIZE) { gnutls_assert(); return GNUTLS_E_ILLEGAL_SRP_USERNAME; } memcpy(info->username, username.data, username.size); info->username[username.size] = 0; /* Adjust the data */ data += username.size + 2; ret = _gnutls_psk_pwd_find_entry(session, info->username, &psk_key); if (ret < 0) return gnutls_assert_val(ret); ret = _gnutls_proc_dh_common_client_kx(session, data, data_size, g, p, &psk_key); _gnutls_free_datum(&psk_key); return ret; }
/* Set the PSK premaster secret. */ int _gnutls_set_psk_session_key (gnutls_session_t session, gnutls_datum_t * dh_secret) { gnutls_datum_t pwd_psk = { NULL, 0 }; gnutls_datum_t *ppsk; size_t dh_secret_size; int ret; if (session->security_parameters.entity == GNUTLS_CLIENT) { gnutls_psk_client_credentials_t cred; cred = (gnutls_psk_client_credentials_t) _gnutls_get_cred (session->key, GNUTLS_CRD_PSK, NULL); if (cred == NULL) { gnutls_assert (); return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } ppsk = &cred->key; } else { /* SERVER side */ psk_auth_info_t info; info = _gnutls_get_auth_info (session); /* find the key of this username */ ret = _gnutls_psk_pwd_find_entry (session, info->username, &pwd_psk); if (ret < 0) { gnutls_assert (); return ret; } ppsk = &pwd_psk; } if (dh_secret == NULL) dh_secret_size = ppsk->size; else dh_secret_size = dh_secret->size; /* set the session key */ session->key->key.size = 4 + dh_secret_size + ppsk->size; session->key->key.data = gnutls_malloc (session->key->key.size); if (session->key->key.data == NULL) { gnutls_assert (); ret = GNUTLS_E_MEMORY_ERROR; goto error; } /* format of the premaster secret: * (uint16_t) psk_size * psk_size bytes of zeros * (uint16_t) psk_size * the psk */ _gnutls_write_uint16 (dh_secret_size, session->key->key.data); if (dh_secret == NULL) memset (&session->key->key.data[2], 0, dh_secret_size); else memcpy (&session->key->key.data[2], dh_secret->data, dh_secret->size); _gnutls_write_datum16 (&session->key->key.data[dh_secret_size + 2], *ppsk); ret = 0; error: _gnutls_free_datum (&pwd_psk); return ret; }