static struct wpabuf * eap_ikev2_build_msg(struct eap_ikev2_data *data, u8 id) { struct wpabuf *req; u8 flags; size_t send_len, plen, icv_len = 0; wpa_printf(MSG_DEBUG, "EAP-IKEV2: Generating Request"); flags = 0; send_len = wpabuf_len(data->out_buf) - data->out_used; if (1 + send_len > data->fragment_size) { send_len = data->fragment_size - 1; flags |= IKEV2_FLAGS_MORE_FRAGMENTS; if (data->out_used == 0) { flags |= IKEV2_FLAGS_LENGTH_INCLUDED; send_len -= 4; } } plen = 1 + send_len; if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) plen += 4; if (data->keys_ready) { const struct ikev2_integ_alg *integ; wpa_printf(MSG_DEBUG, "EAP-IKEV2: Add Integrity Checksum " "Data"); flags |= IKEV2_FLAGS_ICV_INCLUDED; integ = ikev2_get_integ(data->ikev2.proposal.integ); if (integ == NULL) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG " "transform / cannot generate ICV"); return NULL; } icv_len = integ->hash_len; plen += icv_len; } req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, plen, EAP_CODE_REQUEST, id); if (req == NULL) return NULL; wpabuf_put_u8(req, flags); /* Flags */ if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) wpabuf_put_be32(req, wpabuf_len(data->out_buf)); wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used, send_len); data->out_used += send_len; if (flags & IKEV2_FLAGS_ICV_INCLUDED) { const u8 *msg = wpabuf_head(req); size_t len = wpabuf_len(req); ikev2_integ_hash(data->ikev2.proposal.integ, data->ikev2.keys.SK_ai, data->ikev2.keys.SK_integ_len, msg, len, wpabuf_put(req, icv_len)); } if (data->out_used == wpabuf_len(data->out_buf)) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes " "(message sent completely)", (unsigned long) send_len); wpabuf_free(data->out_buf); data->out_buf = NULL; data->out_used = 0; } else { wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes " "(%lu more to send)", (unsigned long) send_len, (unsigned long) wpabuf_len(data->out_buf) - data->out_used); eap_ikev2_state(data, WAIT_FRAG_ACK); } return req; }
int ikev2_build_encrypted(int encr_id, int integ_id, struct ikev2_keys *keys, int initiator, struct wpabuf *msg, struct wpabuf *plain, u8 next_payload) { struct ikev2_payload_hdr *phdr; size_t plen; size_t iv_len, pad_len; u8 *icv, *iv; const struct ikev2_integ_alg *integ_alg; const struct ikev2_encr_alg *encr_alg; const u8 *SK_e = initiator ? keys->SK_ei : keys->SK_er; const u8 *SK_a = initiator ? keys->SK_ai : keys->SK_ar; wpa_printf(MSG_DEBUG, "IKEV2: Adding Encrypted payload"); /* Encr - RFC 4306, Sect. 3.14 */ encr_alg = ikev2_get_encr(encr_id); if (encr_alg == NULL) { wpa_printf(MSG_INFO, "IKEV2: Unsupported encryption type"); return -1; } iv_len = encr_alg->block_size; integ_alg = ikev2_get_integ(integ_id); if (integ_alg == NULL) { wpa_printf(MSG_INFO, "IKEV2: Unsupported intergrity type"); return -1; } if (SK_e == NULL) { wpa_printf(MSG_INFO, "IKEV2: No SK_e available"); return -1; } if (SK_a == NULL) { wpa_printf(MSG_INFO, "IKEV2: No SK_a available"); return -1; } phdr = wpabuf_put(msg, sizeof(*phdr)); phdr->next_payload = next_payload; phdr->flags = 0; iv = wpabuf_put(msg, iv_len); if (random_get_bytes(iv, iv_len)) { wpa_printf(MSG_INFO, "IKEV2: Could not generate IV"); return -1; } pad_len = iv_len - (wpabuf_len(plain) + 1) % iv_len; if (pad_len == iv_len) pad_len = 0; wpabuf_put(plain, pad_len); wpabuf_put_u8(plain, pad_len); if (ikev2_encr_encrypt(encr_alg->id, SK_e, keys->SK_encr_len, iv, wpabuf_head(plain), wpabuf_mhead(plain), wpabuf_len(plain)) < 0) return -1; wpabuf_put_buf(msg, plain); /* Need to update all headers (Length fields) prior to hash func */ icv = wpabuf_put(msg, integ_alg->hash_len); plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; WPA_PUT_BE16(phdr->payload_length, plen); ikev2_update_hdr(msg); return ikev2_integ_hash(integ_id, SK_a, keys->SK_integ_len, wpabuf_head(msg), wpabuf_len(msg) - integ_alg->hash_len, icv); return 0; }
static struct wpabuf * eap_ikev2_build_msg(struct eap_ikev2_data *data, struct eap_method_ret *ret, u8 id) { struct wpabuf *resp; u8 flags; size_t send_len, plen, icv_len = 0; ret->ignore = FALSE; wpa_printf(MSG_DEBUG, "EAP-IKEV2: Generating Response"); ret->allowNotifications = TRUE; flags = 0; send_len = wpabuf_len(data->out_buf) - data->out_used; if (1 + send_len > data->fragment_size) { send_len = data->fragment_size - 1; flags |= IKEV2_FLAGS_MORE_FRAGMENTS; if (data->out_used == 0) { flags |= IKEV2_FLAGS_LENGTH_INCLUDED; send_len -= 4; } } #ifdef CCNS_PL /* Some issues figuring out the length of the message if Message Length * field not included?! */ if (!(flags & IKEV2_FLAGS_LENGTH_INCLUDED)) flags |= IKEV2_FLAGS_LENGTH_INCLUDED; #endif /* CCNS_PL */ plen = 1 + send_len; if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) plen += 4; if (data->keys_ready) { const struct ikev2_integ_alg *integ; wpa_printf(MSG_DEBUG, "EAP-IKEV2: Add Integrity Checksum " "Data"); flags |= IKEV2_FLAGS_ICV_INCLUDED; integ = ikev2_get_integ(data->ikev2.proposal.integ); if (integ == NULL) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG " "transform / cannot generate ICV"); return NULL; } icv_len = integ->hash_len; plen += icv_len; } resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, plen, EAP_CODE_RESPONSE, id); if (resp == NULL) return NULL; wpabuf_put_u8(resp, flags); /* Flags */ if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) wpabuf_put_be32(resp, wpabuf_len(data->out_buf)); wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used, send_len); data->out_used += send_len; if (flags & IKEV2_FLAGS_ICV_INCLUDED) { const u8 *msg = wpabuf_head(resp); size_t len = wpabuf_len(resp); ikev2_integ_hash(data->ikev2.proposal.integ, data->ikev2.keys.SK_ar, data->ikev2.keys.SK_integ_len, msg, len, wpabuf_put(resp, icv_len)); } ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_FAIL; if (data->out_used == wpabuf_len(data->out_buf)) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes " "(message sent completely)", (unsigned long) send_len); wpabuf_free(data->out_buf); data->out_buf = NULL; data->out_used = 0; switch (data->ikev2.state) { case SA_AUTH: /* SA_INIT was sent out, so message have to be * integrity protected from now on. */ data->keys_ready = 1; break; case IKEV2_DONE: ret->methodState = METHOD_DONE; if (data->state == FAIL) break; ret->decision = DECISION_COND_SUCC; wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication " "completed successfully"); if (eap_ikev2_peer_keymat(data)) break; eap_ikev2_state(data, DONE); break; case IKEV2_FAILED: wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication " "failed"); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; break; default: break; } } else { wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes " "(%lu more to send)", (unsigned long) send_len, (unsigned long) wpabuf_len(data->out_buf) - data->out_used); eap_ikev2_state(data, WAIT_FRAG_ACK); } return resp; }
u8 * ikev2_decrypt_payload(int encr_id, int integ_id, struct ikev2_keys *keys, int initiator, const struct ikev2_hdr *hdr, const u8 *encrypted, size_t encrypted_len, size_t *res_len) { size_t iv_len; const u8 *pos, *end, *iv, *integ; u8 hash[IKEV2_MAX_HASH_LEN], *decrypted; size_t decrypted_len, pad_len; const struct ikev2_integ_alg *integ_alg; const struct ikev2_encr_alg *encr_alg; const u8 *SK_e = initiator ? keys->SK_ei : keys->SK_er; const u8 *SK_a = initiator ? keys->SK_ai : keys->SK_ar; if (encrypted == NULL) { wpa_printf(MSG_INFO, "IKEV2: No Encrypted payload in SA_AUTH"); return NULL; } encr_alg = ikev2_get_encr(encr_id); if (encr_alg == NULL) { wpa_printf(MSG_INFO, "IKEV2: Unsupported encryption type"); return NULL; } iv_len = encr_alg->block_size; integ_alg = ikev2_get_integ(integ_id); if (integ_alg == NULL) { wpa_printf(MSG_INFO, "IKEV2: Unsupported intergrity type"); return NULL; } if (encrypted_len < iv_len + 1 + integ_alg->hash_len) { wpa_printf(MSG_INFO, "IKEV2: No room for IV or Integrity " "Checksum"); return NULL; } iv = encrypted; pos = iv + iv_len; end = encrypted + encrypted_len; integ = end - integ_alg->hash_len; if (SK_a == NULL) { wpa_printf(MSG_INFO, "IKEV2: No SK_a available"); return NULL; } if (ikev2_integ_hash(integ_id, SK_a, keys->SK_integ_len, (const u8 *) hdr, integ - (const u8 *) hdr, hash) < 0) { wpa_printf(MSG_INFO, "IKEV2: Failed to calculate integrity " "hash"); return NULL; } if (os_memcmp_const(integ, hash, integ_alg->hash_len) != 0) { wpa_printf(MSG_INFO, "IKEV2: Incorrect Integrity Checksum " "Data"); return NULL; } if (SK_e == NULL) { wpa_printf(MSG_INFO, "IKEV2: No SK_e available"); return NULL; } decrypted_len = integ - pos; decrypted = os_malloc(decrypted_len); if (decrypted == NULL) return NULL; if (ikev2_encr_decrypt(encr_alg->id, SK_e, keys->SK_encr_len, iv, pos, decrypted, decrypted_len) < 0) { os_free(decrypted); return NULL; } pad_len = decrypted[decrypted_len - 1]; if (decrypted_len < pad_len + 1) { wpa_printf(MSG_INFO, "IKEV2: Invalid padding in encrypted " "payload"); os_free(decrypted); return NULL; } decrypted_len -= pad_len + 1; *res_len = decrypted_len; return decrypted; }