1/*
2 * EAP-IKEv2 common routines
3 * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * Alternatively, this software may be distributed under the terms of BSD
10 * license.
11 *
12 * See README and COPYING for more details.
13 */
14
15#include "includes.h"
16
17#include "common.h"
18#include "eap_defs.h"
19#include "eap_common.h"
20#include "ikev2_common.h"
21#include "eap_ikev2_common.h"
22
23
24int eap_ikev2_derive_keymat(int prf, struct ikev2_keys *keys,
25			    const u8 *i_nonce, size_t i_nonce_len,
26			    const u8 *r_nonce, size_t r_nonce_len,
27			    u8 *keymat)
28{
29	u8 *nonces;
30	size_t nlen;
31
32	/* KEYMAT = prf+(SK_d, Ni | Nr) */
33	if (keys->SK_d == NULL || i_nonce == NULL || r_nonce == NULL)
34		return -1;
35
36	nlen = i_nonce_len + r_nonce_len;
37	nonces = os_malloc(nlen);
38	if (nonces == NULL)
39		return -1;
40	os_memcpy(nonces, i_nonce, i_nonce_len);
41	os_memcpy(nonces + i_nonce_len, r_nonce, r_nonce_len);
42
43	if (ikev2_prf_plus(prf, keys->SK_d, keys->SK_d_len, nonces, nlen,
44			   keymat, EAP_MSK_LEN + EAP_EMSK_LEN)) {
45		os_free(nonces);
46		return -1;
47	}
48	os_free(nonces);
49
50	wpa_hexdump_key(MSG_DEBUG, "EAP-IKEV2: KEYMAT",
51			keymat, EAP_MSK_LEN + EAP_EMSK_LEN);
52
53	return 0;
54}
55
56
57struct wpabuf * eap_ikev2_build_frag_ack(u8 id, u8 code)
58{
59	struct wpabuf *msg;
60
61#ifdef CCNS_PL
62	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, 1, code, id);
63	if (msg == NULL) {
64		wpa_printf(MSG_ERROR, "EAP-IKEV2: Failed to allocate memory "
65			   "for fragment ack");
66		return NULL;
67	}
68	wpabuf_put_u8(msg, 0); /* Flags */
69#else /* CCNS_PL */
70	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, 0, code, id);
71	if (msg == NULL) {
72		wpa_printf(MSG_ERROR, "EAP-IKEV2: Failed to allocate memory "
73			   "for fragment ack");
74		return NULL;
75	}
76#endif /* CCNS_PL */
77
78	wpa_printf(MSG_DEBUG, "EAP-IKEV2: Send fragment ack");
79
80	return msg;
81}
82
83
84int eap_ikev2_validate_icv(int integ_alg, struct ikev2_keys *keys,
85			   int initiator, const struct wpabuf *msg,
86			   const u8 *pos, const u8 *end)
87{
88	const struct ikev2_integ_alg *integ;
89	size_t icv_len;
90	u8 icv[IKEV2_MAX_HASH_LEN];
91	const u8 *SK_a = initiator ? keys->SK_ai : keys->SK_ar;
92
93	integ = ikev2_get_integ(integ_alg);
94	if (integ == NULL) {
95		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG "
96			   "transform / cannot validate ICV");
97		return -1;
98	}
99	icv_len = integ->hash_len;
100
101	if (end - pos < (int) icv_len) {
102		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Not enough room in the "
103			   "message for Integrity Checksum Data");
104		return -1;
105	}
106
107	if (SK_a == NULL) {
108		wpa_printf(MSG_DEBUG, "EAP-IKEV2: No SK_a for ICV validation");
109		return -1;
110	}
111
112	if (ikev2_integ_hash(integ_alg, SK_a, keys->SK_integ_len,
113			     wpabuf_head(msg),
114			     wpabuf_len(msg) - icv_len, icv) < 0) {
115		wpa_printf(MSG_INFO, "EAP-IKEV2: Could not calculate ICV");
116		return -1;
117	}
118
119	if (os_memcmp(icv, end - icv_len, icv_len) != 0) {
120		wpa_printf(MSG_INFO, "EAP-IKEV2: Invalid ICV");
121		wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Calculated ICV",
122			    icv, icv_len);
123		wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Received ICV",
124			    end - icv_len, icv_len);
125		return -1;
126	}
127
128	wpa_printf(MSG_DEBUG, "EAP-IKEV2: Valid Integrity Checksum Data in "
129		   "the received message");
130
131	return icv_len;
132}
133