1e5b75505Sopenharmony_ci/*
2e5b75505Sopenharmony_ci * IKEv2 initiator (RFC 4306) for EAP-IKEV2
3e5b75505Sopenharmony_ci * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
4e5b75505Sopenharmony_ci *
5e5b75505Sopenharmony_ci * This software may be distributed under the terms of the BSD license.
6e5b75505Sopenharmony_ci * See README for more details.
7e5b75505Sopenharmony_ci */
8e5b75505Sopenharmony_ci
9e5b75505Sopenharmony_ci#include "includes.h"
10e5b75505Sopenharmony_ci
11e5b75505Sopenharmony_ci#include "common.h"
12e5b75505Sopenharmony_ci#include "crypto/dh_groups.h"
13e5b75505Sopenharmony_ci#include "crypto/random.h"
14e5b75505Sopenharmony_ci#include "ikev2.h"
15e5b75505Sopenharmony_ci
16e5b75505Sopenharmony_ci
17e5b75505Sopenharmony_cistatic int ikev2_process_idr(struct ikev2_initiator_data *data,
18e5b75505Sopenharmony_ci			     const u8 *idr, size_t idr_len);
19e5b75505Sopenharmony_ci
20e5b75505Sopenharmony_ci
21e5b75505Sopenharmony_civoid ikev2_initiator_deinit(struct ikev2_initiator_data *data)
22e5b75505Sopenharmony_ci{
23e5b75505Sopenharmony_ci	ikev2_free_keys(&data->keys);
24e5b75505Sopenharmony_ci	wpabuf_free(data->r_dh_public);
25e5b75505Sopenharmony_ci	wpabuf_free(data->i_dh_private);
26e5b75505Sopenharmony_ci	os_free(data->IDi);
27e5b75505Sopenharmony_ci	os_free(data->IDr);
28e5b75505Sopenharmony_ci	os_free(data->shared_secret);
29e5b75505Sopenharmony_ci	wpabuf_free(data->i_sign_msg);
30e5b75505Sopenharmony_ci	wpabuf_free(data->r_sign_msg);
31e5b75505Sopenharmony_ci	os_free(data->key_pad);
32e5b75505Sopenharmony_ci}
33e5b75505Sopenharmony_ci
34e5b75505Sopenharmony_ci
35e5b75505Sopenharmony_cistatic int ikev2_derive_keys(struct ikev2_initiator_data *data)
36e5b75505Sopenharmony_ci{
37e5b75505Sopenharmony_ci	u8 *buf, *pos, *pad, skeyseed[IKEV2_MAX_HASH_LEN];
38e5b75505Sopenharmony_ci	size_t buf_len, pad_len;
39e5b75505Sopenharmony_ci	struct wpabuf *shared;
40e5b75505Sopenharmony_ci	const struct ikev2_integ_alg *integ;
41e5b75505Sopenharmony_ci	const struct ikev2_prf_alg *prf;
42e5b75505Sopenharmony_ci	const struct ikev2_encr_alg *encr;
43e5b75505Sopenharmony_ci	int ret;
44e5b75505Sopenharmony_ci	const u8 *addr[2];
45e5b75505Sopenharmony_ci	size_t len[2];
46e5b75505Sopenharmony_ci
47e5b75505Sopenharmony_ci	/* RFC 4306, Sect. 2.14 */
48e5b75505Sopenharmony_ci
49e5b75505Sopenharmony_ci	integ = ikev2_get_integ(data->proposal.integ);
50e5b75505Sopenharmony_ci	prf = ikev2_get_prf(data->proposal.prf);
51e5b75505Sopenharmony_ci	encr = ikev2_get_encr(data->proposal.encr);
52e5b75505Sopenharmony_ci	if (integ == NULL || prf == NULL || encr == NULL) {
53e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Unsupported proposal");
54e5b75505Sopenharmony_ci		return -1;
55e5b75505Sopenharmony_ci	}
56e5b75505Sopenharmony_ci
57e5b75505Sopenharmony_ci	shared = dh_derive_shared(data->r_dh_public, data->i_dh_private,
58e5b75505Sopenharmony_ci				  data->dh);
59e5b75505Sopenharmony_ci	if (shared == NULL)
60e5b75505Sopenharmony_ci		return -1;
61e5b75505Sopenharmony_ci
62e5b75505Sopenharmony_ci	/* Construct Ni | Nr | SPIi | SPIr */
63e5b75505Sopenharmony_ci
64e5b75505Sopenharmony_ci	buf_len = data->i_nonce_len + data->r_nonce_len + 2 * IKEV2_SPI_LEN;
65e5b75505Sopenharmony_ci	buf = os_malloc(buf_len);
66e5b75505Sopenharmony_ci	if (buf == NULL) {
67e5b75505Sopenharmony_ci		wpabuf_free(shared);
68e5b75505Sopenharmony_ci		return -1;
69e5b75505Sopenharmony_ci	}
70e5b75505Sopenharmony_ci
71e5b75505Sopenharmony_ci	pos = buf;
72e5b75505Sopenharmony_ci	os_memcpy(pos, data->i_nonce, data->i_nonce_len);
73e5b75505Sopenharmony_ci	pos += data->i_nonce_len;
74e5b75505Sopenharmony_ci	os_memcpy(pos, data->r_nonce, data->r_nonce_len);
75e5b75505Sopenharmony_ci	pos += data->r_nonce_len;
76e5b75505Sopenharmony_ci	os_memcpy(pos, data->i_spi, IKEV2_SPI_LEN);
77e5b75505Sopenharmony_ci	pos += IKEV2_SPI_LEN;
78e5b75505Sopenharmony_ci	os_memcpy(pos, data->r_spi, IKEV2_SPI_LEN);
79e5b75505Sopenharmony_ci
80e5b75505Sopenharmony_ci	/* SKEYSEED = prf(Ni | Nr, g^ir) */
81e5b75505Sopenharmony_ci
82e5b75505Sopenharmony_ci	/* Use zero-padding per RFC 4306, Sect. 2.14 */
83e5b75505Sopenharmony_ci	pad_len = data->dh->prime_len - wpabuf_len(shared);
84e5b75505Sopenharmony_ci	pad = os_zalloc(pad_len ? pad_len : 1);
85e5b75505Sopenharmony_ci	if (pad == NULL) {
86e5b75505Sopenharmony_ci		wpabuf_free(shared);
87e5b75505Sopenharmony_ci		os_free(buf);
88e5b75505Sopenharmony_ci		return -1;
89e5b75505Sopenharmony_ci	}
90e5b75505Sopenharmony_ci	addr[0] = pad;
91e5b75505Sopenharmony_ci	len[0] = pad_len;
92e5b75505Sopenharmony_ci	addr[1] = wpabuf_head(shared);
93e5b75505Sopenharmony_ci	len[1] = wpabuf_len(shared);
94e5b75505Sopenharmony_ci	if (ikev2_prf_hash(prf->id, buf, data->i_nonce_len + data->r_nonce_len,
95e5b75505Sopenharmony_ci			   2, addr, len, skeyseed) < 0) {
96e5b75505Sopenharmony_ci		wpabuf_free(shared);
97e5b75505Sopenharmony_ci		os_free(buf);
98e5b75505Sopenharmony_ci		os_free(pad);
99e5b75505Sopenharmony_ci		return -1;
100e5b75505Sopenharmony_ci	}
101e5b75505Sopenharmony_ci	os_free(pad);
102e5b75505Sopenharmony_ci	wpabuf_free(shared);
103e5b75505Sopenharmony_ci
104e5b75505Sopenharmony_ci	/* DH parameters are not needed anymore, so free them */
105e5b75505Sopenharmony_ci	wpabuf_free(data->r_dh_public);
106e5b75505Sopenharmony_ci	data->r_dh_public = NULL;
107e5b75505Sopenharmony_ci	wpabuf_free(data->i_dh_private);
108e5b75505Sopenharmony_ci	data->i_dh_private = NULL;
109e5b75505Sopenharmony_ci
110e5b75505Sopenharmony_ci	wpa_hexdump_key(MSG_DEBUG, "IKEV2: SKEYSEED",
111e5b75505Sopenharmony_ci			skeyseed, prf->hash_len);
112e5b75505Sopenharmony_ci
113e5b75505Sopenharmony_ci	ret = ikev2_derive_sk_keys(prf, integ, encr, skeyseed, buf, buf_len,
114e5b75505Sopenharmony_ci				   &data->keys);
115e5b75505Sopenharmony_ci	os_free(buf);
116e5b75505Sopenharmony_ci	return ret;
117e5b75505Sopenharmony_ci}
118e5b75505Sopenharmony_ci
119e5b75505Sopenharmony_ci
120e5b75505Sopenharmony_cistatic int ikev2_parse_transform(struct ikev2_initiator_data *data,
121e5b75505Sopenharmony_ci				 struct ikev2_proposal_data *prop,
122e5b75505Sopenharmony_ci				 const u8 *pos, const u8 *end)
123e5b75505Sopenharmony_ci{
124e5b75505Sopenharmony_ci	int transform_len;
125e5b75505Sopenharmony_ci	const struct ikev2_transform *t;
126e5b75505Sopenharmony_ci	u16 transform_id;
127e5b75505Sopenharmony_ci	const u8 *tend;
128e5b75505Sopenharmony_ci
129e5b75505Sopenharmony_ci	if (end - pos < (int) sizeof(*t)) {
130e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Too short transform");
131e5b75505Sopenharmony_ci		return -1;
132e5b75505Sopenharmony_ci	}
133e5b75505Sopenharmony_ci
134e5b75505Sopenharmony_ci	t = (const struct ikev2_transform *) pos;
135e5b75505Sopenharmony_ci	transform_len = WPA_GET_BE16(t->transform_length);
136e5b75505Sopenharmony_ci	if (transform_len < (int) sizeof(*t) || transform_len > end - pos) {
137e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Invalid transform length %d",
138e5b75505Sopenharmony_ci			   transform_len);
139e5b75505Sopenharmony_ci		return -1;
140e5b75505Sopenharmony_ci	}
141e5b75505Sopenharmony_ci	tend = pos + transform_len;
142e5b75505Sopenharmony_ci
143e5b75505Sopenharmony_ci	transform_id = WPA_GET_BE16(t->transform_id);
144e5b75505Sopenharmony_ci
145e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "IKEV2:   Transform:");
146e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "IKEV2:     Type: %d  Transform Length: %d  "
147e5b75505Sopenharmony_ci		   "Transform Type: %d  Transform ID: %d",
148e5b75505Sopenharmony_ci		   t->type, transform_len, t->transform_type, transform_id);
149e5b75505Sopenharmony_ci
150e5b75505Sopenharmony_ci	if (t->type != 0 && t->type != 3) {
151e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Unexpected Transform type");
152e5b75505Sopenharmony_ci		return -1;
153e5b75505Sopenharmony_ci	}
154e5b75505Sopenharmony_ci
155e5b75505Sopenharmony_ci	pos = (const u8 *) (t + 1);
156e5b75505Sopenharmony_ci	if (pos < tend) {
157e5b75505Sopenharmony_ci		wpa_hexdump(MSG_DEBUG, "IKEV2:     Transform Attributes",
158e5b75505Sopenharmony_ci			    pos, tend - pos);
159e5b75505Sopenharmony_ci	}
160e5b75505Sopenharmony_ci
161e5b75505Sopenharmony_ci	switch (t->transform_type) {
162e5b75505Sopenharmony_ci	case IKEV2_TRANSFORM_ENCR:
163e5b75505Sopenharmony_ci		if (ikev2_get_encr(transform_id) &&
164e5b75505Sopenharmony_ci		    transform_id == data->proposal.encr) {
165e5b75505Sopenharmony_ci			if (transform_id == ENCR_AES_CBC) {
166e5b75505Sopenharmony_ci				if (tend - pos != 4) {
167e5b75505Sopenharmony_ci					wpa_printf(MSG_DEBUG, "IKEV2: No "
168e5b75505Sopenharmony_ci						   "Transform Attr for AES");
169e5b75505Sopenharmony_ci					break;
170e5b75505Sopenharmony_ci				}
171e5b75505Sopenharmony_ci				if (WPA_GET_BE16(pos) != 0x800e) {
172e5b75505Sopenharmony_ci					wpa_printf(MSG_DEBUG, "IKEV2: Not a "
173e5b75505Sopenharmony_ci						   "Key Size attribute for "
174e5b75505Sopenharmony_ci						   "AES");
175e5b75505Sopenharmony_ci					break;
176e5b75505Sopenharmony_ci				}
177e5b75505Sopenharmony_ci				if (WPA_GET_BE16(pos + 2) != 128) {
178e5b75505Sopenharmony_ci					wpa_printf(MSG_DEBUG, "IKEV2: "
179e5b75505Sopenharmony_ci						   "Unsupported AES key size "
180e5b75505Sopenharmony_ci						   "%d bits",
181e5b75505Sopenharmony_ci						   WPA_GET_BE16(pos + 2));
182e5b75505Sopenharmony_ci					break;
183e5b75505Sopenharmony_ci				}
184e5b75505Sopenharmony_ci			}
185e5b75505Sopenharmony_ci			prop->encr = transform_id;
186e5b75505Sopenharmony_ci		}
187e5b75505Sopenharmony_ci		break;
188e5b75505Sopenharmony_ci	case IKEV2_TRANSFORM_PRF:
189e5b75505Sopenharmony_ci		if (ikev2_get_prf(transform_id) &&
190e5b75505Sopenharmony_ci		    transform_id == data->proposal.prf)
191e5b75505Sopenharmony_ci			prop->prf = transform_id;
192e5b75505Sopenharmony_ci		break;
193e5b75505Sopenharmony_ci	case IKEV2_TRANSFORM_INTEG:
194e5b75505Sopenharmony_ci		if (ikev2_get_integ(transform_id) &&
195e5b75505Sopenharmony_ci		    transform_id == data->proposal.integ)
196e5b75505Sopenharmony_ci			prop->integ = transform_id;
197e5b75505Sopenharmony_ci		break;
198e5b75505Sopenharmony_ci	case IKEV2_TRANSFORM_DH:
199e5b75505Sopenharmony_ci		if (dh_groups_get(transform_id) &&
200e5b75505Sopenharmony_ci		    transform_id == data->proposal.dh)
201e5b75505Sopenharmony_ci			prop->dh = transform_id;
202e5b75505Sopenharmony_ci		break;
203e5b75505Sopenharmony_ci	}
204e5b75505Sopenharmony_ci
205e5b75505Sopenharmony_ci	return transform_len;
206e5b75505Sopenharmony_ci}
207e5b75505Sopenharmony_ci
208e5b75505Sopenharmony_ci
209e5b75505Sopenharmony_cistatic int ikev2_parse_proposal(struct ikev2_initiator_data *data,
210e5b75505Sopenharmony_ci				struct ikev2_proposal_data *prop,
211e5b75505Sopenharmony_ci				const u8 *pos, const u8 *end)
212e5b75505Sopenharmony_ci{
213e5b75505Sopenharmony_ci	const u8 *pend, *ppos;
214e5b75505Sopenharmony_ci	int proposal_len, i;
215e5b75505Sopenharmony_ci	const struct ikev2_proposal *p;
216e5b75505Sopenharmony_ci
217e5b75505Sopenharmony_ci	if (end - pos < (int) sizeof(*p)) {
218e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Too short proposal");
219e5b75505Sopenharmony_ci		return -1;
220e5b75505Sopenharmony_ci	}
221e5b75505Sopenharmony_ci
222e5b75505Sopenharmony_ci	p = (const struct ikev2_proposal *) pos;
223e5b75505Sopenharmony_ci	proposal_len = WPA_GET_BE16(p->proposal_length);
224e5b75505Sopenharmony_ci	if (proposal_len < (int) sizeof(*p) || proposal_len > end - pos) {
225e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Invalid proposal length %d",
226e5b75505Sopenharmony_ci			   proposal_len);
227e5b75505Sopenharmony_ci		return -1;
228e5b75505Sopenharmony_ci	}
229e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "IKEV2: SAi1 Proposal # %d",
230e5b75505Sopenharmony_ci		   p->proposal_num);
231e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "IKEV2:   Type: %d  Proposal Length: %d "
232e5b75505Sopenharmony_ci		   " Protocol ID: %d",
233e5b75505Sopenharmony_ci		   p->type, proposal_len, p->protocol_id);
234e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "IKEV2:   SPI Size: %d  Transforms: %d",
235e5b75505Sopenharmony_ci		   p->spi_size, p->num_transforms);
236e5b75505Sopenharmony_ci
237e5b75505Sopenharmony_ci	if (p->type != 0 && p->type != 2) {
238e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Unexpected Proposal type");
239e5b75505Sopenharmony_ci		return -1;
240e5b75505Sopenharmony_ci	}
241e5b75505Sopenharmony_ci
242e5b75505Sopenharmony_ci	if (p->protocol_id != IKEV2_PROTOCOL_IKE) {
243e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "IKEV2: Unexpected Protocol ID "
244e5b75505Sopenharmony_ci			   "(only IKE allowed for EAP-IKEv2)");
245e5b75505Sopenharmony_ci		return -1;
246e5b75505Sopenharmony_ci	}
247e5b75505Sopenharmony_ci
248e5b75505Sopenharmony_ci	if (p->proposal_num != prop->proposal_num) {
249e5b75505Sopenharmony_ci		if (p->proposal_num == prop->proposal_num + 1)
250e5b75505Sopenharmony_ci			prop->proposal_num = p->proposal_num;
251e5b75505Sopenharmony_ci		else {
252e5b75505Sopenharmony_ci			wpa_printf(MSG_INFO, "IKEV2: Unexpected Proposal #");
253e5b75505Sopenharmony_ci			return -1;
254e5b75505Sopenharmony_ci		}
255e5b75505Sopenharmony_ci	}
256e5b75505Sopenharmony_ci
257e5b75505Sopenharmony_ci	ppos = (const u8 *) (p + 1);
258e5b75505Sopenharmony_ci	pend = pos + proposal_len;
259e5b75505Sopenharmony_ci	if (p->spi_size > pend - ppos) {
260e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Not enough room for SPI "
261e5b75505Sopenharmony_ci			   "in proposal");
262e5b75505Sopenharmony_ci		return -1;
263e5b75505Sopenharmony_ci	}
264e5b75505Sopenharmony_ci	if (p->spi_size) {
265e5b75505Sopenharmony_ci		wpa_hexdump(MSG_DEBUG, "IKEV2:    SPI",
266e5b75505Sopenharmony_ci			    ppos, p->spi_size);
267e5b75505Sopenharmony_ci		ppos += p->spi_size;
268e5b75505Sopenharmony_ci	}
269e5b75505Sopenharmony_ci
270e5b75505Sopenharmony_ci	/*
271e5b75505Sopenharmony_ci	 * For initial IKE_SA negotiation, SPI Size MUST be zero; for
272e5b75505Sopenharmony_ci	 * subsequent negotiations, it must be 8 for IKE. We only support
273e5b75505Sopenharmony_ci	 * initial case for now.
274e5b75505Sopenharmony_ci	 */
275e5b75505Sopenharmony_ci	if (p->spi_size != 0) {
276e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Unexpected SPI Size");
277e5b75505Sopenharmony_ci		return -1;
278e5b75505Sopenharmony_ci	}
279e5b75505Sopenharmony_ci
280e5b75505Sopenharmony_ci	if (p->num_transforms == 0) {
281e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: At least one transform required");
282e5b75505Sopenharmony_ci		return -1;
283e5b75505Sopenharmony_ci	}
284e5b75505Sopenharmony_ci
285e5b75505Sopenharmony_ci	for (i = 0; i < (int) p->num_transforms; i++) {
286e5b75505Sopenharmony_ci		int tlen = ikev2_parse_transform(data, prop, ppos, pend);
287e5b75505Sopenharmony_ci		if (tlen < 0)
288e5b75505Sopenharmony_ci			return -1;
289e5b75505Sopenharmony_ci		ppos += tlen;
290e5b75505Sopenharmony_ci	}
291e5b75505Sopenharmony_ci
292e5b75505Sopenharmony_ci	if (ppos != pend) {
293e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Unexpected data after "
294e5b75505Sopenharmony_ci			   "transforms");
295e5b75505Sopenharmony_ci		return -1;
296e5b75505Sopenharmony_ci	}
297e5b75505Sopenharmony_ci
298e5b75505Sopenharmony_ci	return proposal_len;
299e5b75505Sopenharmony_ci}
300e5b75505Sopenharmony_ci
301e5b75505Sopenharmony_ci
302e5b75505Sopenharmony_cistatic int ikev2_process_sar1(struct ikev2_initiator_data *data,
303e5b75505Sopenharmony_ci			      const u8 *sar1, size_t sar1_len)
304e5b75505Sopenharmony_ci{
305e5b75505Sopenharmony_ci	struct ikev2_proposal_data prop;
306e5b75505Sopenharmony_ci	const u8 *pos, *end;
307e5b75505Sopenharmony_ci	int found = 0;
308e5b75505Sopenharmony_ci
309e5b75505Sopenharmony_ci	/* Security Association Payloads: <Proposals> */
310e5b75505Sopenharmony_ci
311e5b75505Sopenharmony_ci	if (sar1 == NULL) {
312e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: SAr1 not received");
313e5b75505Sopenharmony_ci		return -1;
314e5b75505Sopenharmony_ci	}
315e5b75505Sopenharmony_ci
316e5b75505Sopenharmony_ci	os_memset(&prop, 0, sizeof(prop));
317e5b75505Sopenharmony_ci	prop.proposal_num = 1;
318e5b75505Sopenharmony_ci
319e5b75505Sopenharmony_ci	pos = sar1;
320e5b75505Sopenharmony_ci	end = sar1 + sar1_len;
321e5b75505Sopenharmony_ci
322e5b75505Sopenharmony_ci	while (pos < end) {
323e5b75505Sopenharmony_ci		int plen;
324e5b75505Sopenharmony_ci
325e5b75505Sopenharmony_ci		prop.integ = -1;
326e5b75505Sopenharmony_ci		prop.prf = -1;
327e5b75505Sopenharmony_ci		prop.encr = -1;
328e5b75505Sopenharmony_ci		prop.dh = -1;
329e5b75505Sopenharmony_ci		plen = ikev2_parse_proposal(data, &prop, pos, end);
330e5b75505Sopenharmony_ci		if (plen < 0)
331e5b75505Sopenharmony_ci			return -1;
332e5b75505Sopenharmony_ci
333e5b75505Sopenharmony_ci		if (!found && prop.integ != -1 && prop.prf != -1 &&
334e5b75505Sopenharmony_ci		    prop.encr != -1 && prop.dh != -1) {
335e5b75505Sopenharmony_ci			found = 1;
336e5b75505Sopenharmony_ci		}
337e5b75505Sopenharmony_ci
338e5b75505Sopenharmony_ci		pos += plen;
339e5b75505Sopenharmony_ci
340e5b75505Sopenharmony_ci		/* Only one proposal expected in SAr */
341e5b75505Sopenharmony_ci		break;
342e5b75505Sopenharmony_ci	}
343e5b75505Sopenharmony_ci
344e5b75505Sopenharmony_ci	if (pos != end) {
345e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Unexpected data after proposal");
346e5b75505Sopenharmony_ci		return -1;
347e5b75505Sopenharmony_ci	}
348e5b75505Sopenharmony_ci
349e5b75505Sopenharmony_ci	if (!found) {
350e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: No acceptable proposal found");
351e5b75505Sopenharmony_ci		return -1;
352e5b75505Sopenharmony_ci	}
353e5b75505Sopenharmony_ci
354e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "IKEV2: Accepted proposal #%d: ENCR:%d PRF:%d "
355e5b75505Sopenharmony_ci		   "INTEG:%d D-H:%d", data->proposal.proposal_num,
356e5b75505Sopenharmony_ci		   data->proposal.encr, data->proposal.prf,
357e5b75505Sopenharmony_ci		   data->proposal.integ, data->proposal.dh);
358e5b75505Sopenharmony_ci
359e5b75505Sopenharmony_ci	return 0;
360e5b75505Sopenharmony_ci}
361e5b75505Sopenharmony_ci
362e5b75505Sopenharmony_ci
363e5b75505Sopenharmony_cistatic int ikev2_process_ker(struct ikev2_initiator_data *data,
364e5b75505Sopenharmony_ci			     const u8 *ker, size_t ker_len)
365e5b75505Sopenharmony_ci{
366e5b75505Sopenharmony_ci	u16 group;
367e5b75505Sopenharmony_ci
368e5b75505Sopenharmony_ci	/*
369e5b75505Sopenharmony_ci	 * Key Exchange Payload:
370e5b75505Sopenharmony_ci	 * DH Group # (16 bits)
371e5b75505Sopenharmony_ci	 * RESERVED (16 bits)
372e5b75505Sopenharmony_ci	 * Key Exchange Data (Diffie-Hellman public value)
373e5b75505Sopenharmony_ci	 */
374e5b75505Sopenharmony_ci
375e5b75505Sopenharmony_ci	if (ker == NULL) {
376e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: KEr not received");
377e5b75505Sopenharmony_ci		return -1;
378e5b75505Sopenharmony_ci	}
379e5b75505Sopenharmony_ci
380e5b75505Sopenharmony_ci	if (ker_len < 4 + 96) {
381e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Too show Key Exchange Payload");
382e5b75505Sopenharmony_ci		return -1;
383e5b75505Sopenharmony_ci	}
384e5b75505Sopenharmony_ci
385e5b75505Sopenharmony_ci	group = WPA_GET_BE16(ker);
386e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "IKEV2: KEr DH Group #%u", group);
387e5b75505Sopenharmony_ci
388e5b75505Sopenharmony_ci	if (group != data->proposal.dh) {
389e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "IKEV2: KEr DH Group #%u does not match "
390e5b75505Sopenharmony_ci			   "with the selected proposal (%u)",
391e5b75505Sopenharmony_ci			   group, data->proposal.dh);
392e5b75505Sopenharmony_ci		return -1;
393e5b75505Sopenharmony_ci	}
394e5b75505Sopenharmony_ci
395e5b75505Sopenharmony_ci	if (data->dh == NULL) {
396e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Unsupported DH group");
397e5b75505Sopenharmony_ci		return -1;
398e5b75505Sopenharmony_ci	}
399e5b75505Sopenharmony_ci
400e5b75505Sopenharmony_ci	/* RFC 4306, Section 3.4:
401e5b75505Sopenharmony_ci	 * The length of DH public value MUST be equal to the length of the
402e5b75505Sopenharmony_ci	 * prime modulus.
403e5b75505Sopenharmony_ci	 */
404e5b75505Sopenharmony_ci	if (ker_len - 4 != data->dh->prime_len) {
405e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Invalid DH public value length "
406e5b75505Sopenharmony_ci			   "%ld (expected %ld)",
407e5b75505Sopenharmony_ci			   (long) (ker_len - 4), (long) data->dh->prime_len);
408e5b75505Sopenharmony_ci		return -1;
409e5b75505Sopenharmony_ci	}
410e5b75505Sopenharmony_ci
411e5b75505Sopenharmony_ci	wpabuf_free(data->r_dh_public);
412e5b75505Sopenharmony_ci	data->r_dh_public = wpabuf_alloc_copy(ker + 4, ker_len - 4);
413e5b75505Sopenharmony_ci	if (data->r_dh_public == NULL)
414e5b75505Sopenharmony_ci		return -1;
415e5b75505Sopenharmony_ci
416e5b75505Sopenharmony_ci	wpa_hexdump_buf(MSG_DEBUG, "IKEV2: KEr Diffie-Hellman Public Value",
417e5b75505Sopenharmony_ci			data->r_dh_public);
418e5b75505Sopenharmony_ci
419e5b75505Sopenharmony_ci	return 0;
420e5b75505Sopenharmony_ci}
421e5b75505Sopenharmony_ci
422e5b75505Sopenharmony_ci
423e5b75505Sopenharmony_cistatic int ikev2_process_nr(struct ikev2_initiator_data *data,
424e5b75505Sopenharmony_ci			    const u8 *nr, size_t nr_len)
425e5b75505Sopenharmony_ci{
426e5b75505Sopenharmony_ci	if (nr == NULL) {
427e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Nr not received");
428e5b75505Sopenharmony_ci		return -1;
429e5b75505Sopenharmony_ci	}
430e5b75505Sopenharmony_ci
431e5b75505Sopenharmony_ci	if (nr_len < IKEV2_NONCE_MIN_LEN || nr_len > IKEV2_NONCE_MAX_LEN) {
432e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Invalid Nr length %ld",
433e5b75505Sopenharmony_ci			   (long) nr_len);
434e5b75505Sopenharmony_ci		return -1;
435e5b75505Sopenharmony_ci	}
436e5b75505Sopenharmony_ci
437e5b75505Sopenharmony_ci	data->r_nonce_len = nr_len;
438e5b75505Sopenharmony_ci	os_memcpy(data->r_nonce, nr, nr_len);
439e5b75505Sopenharmony_ci	wpa_hexdump(MSG_MSGDUMP, "IKEV2: Nr",
440e5b75505Sopenharmony_ci		    data->r_nonce, data->r_nonce_len);
441e5b75505Sopenharmony_ci
442e5b75505Sopenharmony_ci	return 0;
443e5b75505Sopenharmony_ci}
444e5b75505Sopenharmony_ci
445e5b75505Sopenharmony_ci
446e5b75505Sopenharmony_cistatic int ikev2_process_sa_init_encr(struct ikev2_initiator_data *data,
447e5b75505Sopenharmony_ci				      const struct ikev2_hdr *hdr,
448e5b75505Sopenharmony_ci				      const u8 *encrypted,
449e5b75505Sopenharmony_ci				      size_t encrypted_len, u8 next_payload)
450e5b75505Sopenharmony_ci{
451e5b75505Sopenharmony_ci	u8 *decrypted;
452e5b75505Sopenharmony_ci	size_t decrypted_len;
453e5b75505Sopenharmony_ci	struct ikev2_payloads pl;
454e5b75505Sopenharmony_ci	int ret = 0;
455e5b75505Sopenharmony_ci
456e5b75505Sopenharmony_ci	decrypted = ikev2_decrypt_payload(data->proposal.encr,
457e5b75505Sopenharmony_ci					  data->proposal.integ, &data->keys, 0,
458e5b75505Sopenharmony_ci					  hdr, encrypted, encrypted_len,
459e5b75505Sopenharmony_ci					  &decrypted_len);
460e5b75505Sopenharmony_ci	if (decrypted == NULL)
461e5b75505Sopenharmony_ci		return -1;
462e5b75505Sopenharmony_ci
463e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "IKEV2: Processing decrypted payloads");
464e5b75505Sopenharmony_ci
465e5b75505Sopenharmony_ci	if (ikev2_parse_payloads(&pl, next_payload, decrypted,
466e5b75505Sopenharmony_ci				 decrypted + decrypted_len) < 0) {
467e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Failed to parse decrypted "
468e5b75505Sopenharmony_ci			   "payloads");
469e5b75505Sopenharmony_ci		return -1;
470e5b75505Sopenharmony_ci	}
471e5b75505Sopenharmony_ci
472e5b75505Sopenharmony_ci	if (pl.idr)
473e5b75505Sopenharmony_ci		ret = ikev2_process_idr(data, pl.idr, pl.idr_len);
474e5b75505Sopenharmony_ci
475e5b75505Sopenharmony_ci	os_free(decrypted);
476e5b75505Sopenharmony_ci
477e5b75505Sopenharmony_ci	return ret;
478e5b75505Sopenharmony_ci}
479e5b75505Sopenharmony_ci
480e5b75505Sopenharmony_ci
481e5b75505Sopenharmony_cistatic int ikev2_process_sa_init(struct ikev2_initiator_data *data,
482e5b75505Sopenharmony_ci				 const struct ikev2_hdr *hdr,
483e5b75505Sopenharmony_ci				 struct ikev2_payloads *pl)
484e5b75505Sopenharmony_ci{
485e5b75505Sopenharmony_ci	if (ikev2_process_sar1(data, pl->sa, pl->sa_len) < 0 ||
486e5b75505Sopenharmony_ci	    ikev2_process_ker(data, pl->ke, pl->ke_len) < 0 ||
487e5b75505Sopenharmony_ci	    ikev2_process_nr(data, pl->nonce, pl->nonce_len) < 0)
488e5b75505Sopenharmony_ci		return -1;
489e5b75505Sopenharmony_ci
490e5b75505Sopenharmony_ci	os_memcpy(data->r_spi, hdr->r_spi, IKEV2_SPI_LEN);
491e5b75505Sopenharmony_ci
492e5b75505Sopenharmony_ci	if (ikev2_derive_keys(data) < 0)
493e5b75505Sopenharmony_ci		return -1;
494e5b75505Sopenharmony_ci
495e5b75505Sopenharmony_ci	if (pl->encrypted) {
496e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "IKEV2: Encrypted payload in SA_INIT - "
497e5b75505Sopenharmony_ci			   "try to get IDr from it");
498e5b75505Sopenharmony_ci		if (ikev2_process_sa_init_encr(data, hdr, pl->encrypted,
499e5b75505Sopenharmony_ci					       pl->encrypted_len,
500e5b75505Sopenharmony_ci					       pl->encr_next_payload) < 0) {
501e5b75505Sopenharmony_ci			wpa_printf(MSG_INFO, "IKEV2: Failed to process "
502e5b75505Sopenharmony_ci				   "encrypted payload");
503e5b75505Sopenharmony_ci			return -1;
504e5b75505Sopenharmony_ci		}
505e5b75505Sopenharmony_ci	}
506e5b75505Sopenharmony_ci
507e5b75505Sopenharmony_ci	data->state = SA_AUTH;
508e5b75505Sopenharmony_ci
509e5b75505Sopenharmony_ci	return 0;
510e5b75505Sopenharmony_ci}
511e5b75505Sopenharmony_ci
512e5b75505Sopenharmony_ci
513e5b75505Sopenharmony_cistatic int ikev2_process_idr(struct ikev2_initiator_data *data,
514e5b75505Sopenharmony_ci			     const u8 *idr, size_t idr_len)
515e5b75505Sopenharmony_ci{
516e5b75505Sopenharmony_ci	u8 id_type;
517e5b75505Sopenharmony_ci
518e5b75505Sopenharmony_ci	if (idr == NULL) {
519e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: No IDr received");
520e5b75505Sopenharmony_ci		return -1;
521e5b75505Sopenharmony_ci	}
522e5b75505Sopenharmony_ci
523e5b75505Sopenharmony_ci	if (idr_len < 4) {
524e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Too short IDr payload");
525e5b75505Sopenharmony_ci		return -1;
526e5b75505Sopenharmony_ci	}
527e5b75505Sopenharmony_ci
528e5b75505Sopenharmony_ci	id_type = idr[0];
529e5b75505Sopenharmony_ci	idr += 4;
530e5b75505Sopenharmony_ci	idr_len -= 4;
531e5b75505Sopenharmony_ci
532e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "IKEV2: IDr ID Type %d", id_type);
533e5b75505Sopenharmony_ci	wpa_hexdump_ascii(MSG_DEBUG, "IKEV2: IDr", idr, idr_len);
534e5b75505Sopenharmony_ci	if (data->IDr) {
535e5b75505Sopenharmony_ci		if (id_type != data->IDr_type || idr_len != data->IDr_len ||
536e5b75505Sopenharmony_ci		    os_memcmp(idr, data->IDr, idr_len) != 0) {
537e5b75505Sopenharmony_ci			wpa_printf(MSG_INFO, "IKEV2: IDr differs from the one "
538e5b75505Sopenharmony_ci				   "received earlier");
539e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG, "IKEV2: Previous IDr ID Type %d",
540e5b75505Sopenharmony_ci				   id_type);
541e5b75505Sopenharmony_ci			wpa_hexdump_ascii(MSG_DEBUG, "Previous IKEV2: IDr",
542e5b75505Sopenharmony_ci					  data->IDr, data->IDr_len);
543e5b75505Sopenharmony_ci			return -1;
544e5b75505Sopenharmony_ci		}
545e5b75505Sopenharmony_ci		os_free(data->IDr);
546e5b75505Sopenharmony_ci	}
547e5b75505Sopenharmony_ci	data->IDr = os_memdup(idr, idr_len);
548e5b75505Sopenharmony_ci	if (data->IDr == NULL)
549e5b75505Sopenharmony_ci		return -1;
550e5b75505Sopenharmony_ci	data->IDr_len = idr_len;
551e5b75505Sopenharmony_ci	data->IDr_type = id_type;
552e5b75505Sopenharmony_ci
553e5b75505Sopenharmony_ci	return 0;
554e5b75505Sopenharmony_ci}
555e5b75505Sopenharmony_ci
556e5b75505Sopenharmony_ci
557e5b75505Sopenharmony_cistatic int ikev2_process_cert(struct ikev2_initiator_data *data,
558e5b75505Sopenharmony_ci			      const u8 *cert, size_t cert_len)
559e5b75505Sopenharmony_ci{
560e5b75505Sopenharmony_ci	u8 cert_encoding;
561e5b75505Sopenharmony_ci
562e5b75505Sopenharmony_ci	if (cert == NULL) {
563e5b75505Sopenharmony_ci		if (data->peer_auth == PEER_AUTH_CERT) {
564e5b75505Sopenharmony_ci			wpa_printf(MSG_INFO, "IKEV2: No Certificate received");
565e5b75505Sopenharmony_ci			return -1;
566e5b75505Sopenharmony_ci		}
567e5b75505Sopenharmony_ci		return 0;
568e5b75505Sopenharmony_ci	}
569e5b75505Sopenharmony_ci
570e5b75505Sopenharmony_ci	if (cert_len < 1) {
571e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: No Cert Encoding field");
572e5b75505Sopenharmony_ci		return -1;
573e5b75505Sopenharmony_ci	}
574e5b75505Sopenharmony_ci
575e5b75505Sopenharmony_ci	cert_encoding = cert[0];
576e5b75505Sopenharmony_ci	cert++;
577e5b75505Sopenharmony_ci	cert_len--;
578e5b75505Sopenharmony_ci
579e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "IKEV2: Cert Encoding %d", cert_encoding);
580e5b75505Sopenharmony_ci	wpa_hexdump(MSG_MSGDUMP, "IKEV2: Certificate Data", cert, cert_len);
581e5b75505Sopenharmony_ci
582e5b75505Sopenharmony_ci	/* TODO: validate certificate */
583e5b75505Sopenharmony_ci
584e5b75505Sopenharmony_ci	return 0;
585e5b75505Sopenharmony_ci}
586e5b75505Sopenharmony_ci
587e5b75505Sopenharmony_ci
588e5b75505Sopenharmony_cistatic int ikev2_process_auth_cert(struct ikev2_initiator_data *data,
589e5b75505Sopenharmony_ci				   u8 method, const u8 *auth, size_t auth_len)
590e5b75505Sopenharmony_ci{
591e5b75505Sopenharmony_ci	if (method != AUTH_RSA_SIGN) {
592e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication "
593e5b75505Sopenharmony_ci			   "method %d", method);
594e5b75505Sopenharmony_ci		return -1;
595e5b75505Sopenharmony_ci	}
596e5b75505Sopenharmony_ci
597e5b75505Sopenharmony_ci	/* TODO: validate AUTH */
598e5b75505Sopenharmony_ci	return 0;
599e5b75505Sopenharmony_ci}
600e5b75505Sopenharmony_ci
601e5b75505Sopenharmony_ci
602e5b75505Sopenharmony_cistatic int ikev2_process_auth_secret(struct ikev2_initiator_data *data,
603e5b75505Sopenharmony_ci				     u8 method, const u8 *auth,
604e5b75505Sopenharmony_ci				     size_t auth_len)
605e5b75505Sopenharmony_ci{
606e5b75505Sopenharmony_ci	u8 auth_data[IKEV2_MAX_HASH_LEN];
607e5b75505Sopenharmony_ci	const struct ikev2_prf_alg *prf;
608e5b75505Sopenharmony_ci
609e5b75505Sopenharmony_ci	if (method != AUTH_SHARED_KEY_MIC) {
610e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication "
611e5b75505Sopenharmony_ci			   "method %d", method);
612e5b75505Sopenharmony_ci		return -1;
613e5b75505Sopenharmony_ci	}
614e5b75505Sopenharmony_ci
615e5b75505Sopenharmony_ci	/* msg | Ni | prf(SK_pr,IDr') */
616e5b75505Sopenharmony_ci	if (ikev2_derive_auth_data(data->proposal.prf, data->r_sign_msg,
617e5b75505Sopenharmony_ci				   data->IDr, data->IDr_len, data->IDr_type,
618e5b75505Sopenharmony_ci				   &data->keys, 0, data->shared_secret,
619e5b75505Sopenharmony_ci				   data->shared_secret_len,
620e5b75505Sopenharmony_ci				   data->i_nonce, data->i_nonce_len,
621e5b75505Sopenharmony_ci				   data->key_pad, data->key_pad_len,
622e5b75505Sopenharmony_ci				   auth_data) < 0) {
623e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data");
624e5b75505Sopenharmony_ci		return -1;
625e5b75505Sopenharmony_ci	}
626e5b75505Sopenharmony_ci
627e5b75505Sopenharmony_ci	wpabuf_free(data->r_sign_msg);
628e5b75505Sopenharmony_ci	data->r_sign_msg = NULL;
629e5b75505Sopenharmony_ci
630e5b75505Sopenharmony_ci	prf = ikev2_get_prf(data->proposal.prf);
631e5b75505Sopenharmony_ci	if (prf == NULL)
632e5b75505Sopenharmony_ci		return -1;
633e5b75505Sopenharmony_ci
634e5b75505Sopenharmony_ci	if (auth_len != prf->hash_len ||
635e5b75505Sopenharmony_ci	    os_memcmp_const(auth, auth_data, auth_len) != 0) {
636e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Invalid Authentication Data");
637e5b75505Sopenharmony_ci		wpa_hexdump(MSG_DEBUG, "IKEV2: Received Authentication Data",
638e5b75505Sopenharmony_ci			    auth, auth_len);
639e5b75505Sopenharmony_ci		wpa_hexdump(MSG_DEBUG, "IKEV2: Expected Authentication Data",
640e5b75505Sopenharmony_ci			    auth_data, prf->hash_len);
641e5b75505Sopenharmony_ci		return -1;
642e5b75505Sopenharmony_ci	}
643e5b75505Sopenharmony_ci
644e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "IKEV2: Peer authenticated successfully "
645e5b75505Sopenharmony_ci		   "using shared keys");
646e5b75505Sopenharmony_ci
647e5b75505Sopenharmony_ci	return 0;
648e5b75505Sopenharmony_ci}
649e5b75505Sopenharmony_ci
650e5b75505Sopenharmony_ci
651e5b75505Sopenharmony_cistatic int ikev2_process_auth(struct ikev2_initiator_data *data,
652e5b75505Sopenharmony_ci			      const u8 *auth, size_t auth_len)
653e5b75505Sopenharmony_ci{
654e5b75505Sopenharmony_ci	u8 auth_method;
655e5b75505Sopenharmony_ci
656e5b75505Sopenharmony_ci	if (auth == NULL) {
657e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: No Authentication Payload");
658e5b75505Sopenharmony_ci		return -1;
659e5b75505Sopenharmony_ci	}
660e5b75505Sopenharmony_ci
661e5b75505Sopenharmony_ci	if (auth_len < 4) {
662e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Too short Authentication "
663e5b75505Sopenharmony_ci			   "Payload");
664e5b75505Sopenharmony_ci		return -1;
665e5b75505Sopenharmony_ci	}
666e5b75505Sopenharmony_ci
667e5b75505Sopenharmony_ci	auth_method = auth[0];
668e5b75505Sopenharmony_ci	auth += 4;
669e5b75505Sopenharmony_ci	auth_len -= 4;
670e5b75505Sopenharmony_ci
671e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "IKEV2: Auth Method %d", auth_method);
672e5b75505Sopenharmony_ci	wpa_hexdump(MSG_MSGDUMP, "IKEV2: Authentication Data", auth, auth_len);
673e5b75505Sopenharmony_ci
674e5b75505Sopenharmony_ci	switch (data->peer_auth) {
675e5b75505Sopenharmony_ci	case PEER_AUTH_CERT:
676e5b75505Sopenharmony_ci		return ikev2_process_auth_cert(data, auth_method, auth,
677e5b75505Sopenharmony_ci					       auth_len);
678e5b75505Sopenharmony_ci	case PEER_AUTH_SECRET:
679e5b75505Sopenharmony_ci		return ikev2_process_auth_secret(data, auth_method, auth,
680e5b75505Sopenharmony_ci						 auth_len);
681e5b75505Sopenharmony_ci	}
682e5b75505Sopenharmony_ci
683e5b75505Sopenharmony_ci	return -1;
684e5b75505Sopenharmony_ci}
685e5b75505Sopenharmony_ci
686e5b75505Sopenharmony_ci
687e5b75505Sopenharmony_cistatic int ikev2_process_sa_auth_decrypted(struct ikev2_initiator_data *data,
688e5b75505Sopenharmony_ci					   u8 next_payload,
689e5b75505Sopenharmony_ci					   u8 *payload, size_t payload_len)
690e5b75505Sopenharmony_ci{
691e5b75505Sopenharmony_ci	struct ikev2_payloads pl;
692e5b75505Sopenharmony_ci
693e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "IKEV2: Processing decrypted payloads");
694e5b75505Sopenharmony_ci
695e5b75505Sopenharmony_ci	if (ikev2_parse_payloads(&pl, next_payload, payload, payload +
696e5b75505Sopenharmony_ci				 payload_len) < 0) {
697e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Failed to parse decrypted "
698e5b75505Sopenharmony_ci			   "payloads");
699e5b75505Sopenharmony_ci		return -1;
700e5b75505Sopenharmony_ci	}
701e5b75505Sopenharmony_ci
702e5b75505Sopenharmony_ci	if (ikev2_process_idr(data, pl.idr, pl.idr_len) < 0 ||
703e5b75505Sopenharmony_ci	    ikev2_process_cert(data, pl.cert, pl.cert_len) < 0 ||
704e5b75505Sopenharmony_ci	    ikev2_process_auth(data, pl.auth, pl.auth_len) < 0)
705e5b75505Sopenharmony_ci		return -1;
706e5b75505Sopenharmony_ci
707e5b75505Sopenharmony_ci	return 0;
708e5b75505Sopenharmony_ci}
709e5b75505Sopenharmony_ci
710e5b75505Sopenharmony_ci
711e5b75505Sopenharmony_cistatic int ikev2_process_sa_auth(struct ikev2_initiator_data *data,
712e5b75505Sopenharmony_ci				 const struct ikev2_hdr *hdr,
713e5b75505Sopenharmony_ci				 struct ikev2_payloads *pl)
714e5b75505Sopenharmony_ci{
715e5b75505Sopenharmony_ci	u8 *decrypted;
716e5b75505Sopenharmony_ci	size_t decrypted_len;
717e5b75505Sopenharmony_ci	int ret;
718e5b75505Sopenharmony_ci
719e5b75505Sopenharmony_ci	decrypted = ikev2_decrypt_payload(data->proposal.encr,
720e5b75505Sopenharmony_ci					  data->proposal.integ,
721e5b75505Sopenharmony_ci					  &data->keys, 0, hdr, pl->encrypted,
722e5b75505Sopenharmony_ci					  pl->encrypted_len, &decrypted_len);
723e5b75505Sopenharmony_ci	if (decrypted == NULL)
724e5b75505Sopenharmony_ci		return -1;
725e5b75505Sopenharmony_ci
726e5b75505Sopenharmony_ci	ret = ikev2_process_sa_auth_decrypted(data, pl->encr_next_payload,
727e5b75505Sopenharmony_ci					      decrypted, decrypted_len);
728e5b75505Sopenharmony_ci	os_free(decrypted);
729e5b75505Sopenharmony_ci
730e5b75505Sopenharmony_ci	if (ret == 0 && !data->unknown_user) {
731e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "IKEV2: Authentication completed");
732e5b75505Sopenharmony_ci		data->state = IKEV2_DONE;
733e5b75505Sopenharmony_ci	}
734e5b75505Sopenharmony_ci
735e5b75505Sopenharmony_ci	return ret;
736e5b75505Sopenharmony_ci}
737e5b75505Sopenharmony_ci
738e5b75505Sopenharmony_ci
739e5b75505Sopenharmony_cistatic int ikev2_validate_rx_state(struct ikev2_initiator_data *data,
740e5b75505Sopenharmony_ci				   u8 exchange_type, u32 message_id)
741e5b75505Sopenharmony_ci{
742e5b75505Sopenharmony_ci	switch (data->state) {
743e5b75505Sopenharmony_ci	case SA_INIT:
744e5b75505Sopenharmony_ci		/* Expect to receive IKE_SA_INIT: HDR, SAr, KEr, Nr, [CERTREQ],
745e5b75505Sopenharmony_ci		 * [SK{IDr}] */
746e5b75505Sopenharmony_ci		if (exchange_type != IKE_SA_INIT) {
747e5b75505Sopenharmony_ci			wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type "
748e5b75505Sopenharmony_ci				   "%u in SA_INIT state", exchange_type);
749e5b75505Sopenharmony_ci			return -1;
750e5b75505Sopenharmony_ci		}
751e5b75505Sopenharmony_ci		if (message_id != 0) {
752e5b75505Sopenharmony_ci			wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u "
753e5b75505Sopenharmony_ci				   "in SA_INIT state", message_id);
754e5b75505Sopenharmony_ci			return -1;
755e5b75505Sopenharmony_ci		}
756e5b75505Sopenharmony_ci		break;
757e5b75505Sopenharmony_ci	case SA_AUTH:
758e5b75505Sopenharmony_ci		/* Expect to receive IKE_SA_AUTH:
759e5b75505Sopenharmony_ci		 * HDR, SK {IDr, [CERT,] [CERTREQ,] [NFID,] AUTH}
760e5b75505Sopenharmony_ci		 */
761e5b75505Sopenharmony_ci		if (exchange_type != IKE_SA_AUTH) {
762e5b75505Sopenharmony_ci			wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type "
763e5b75505Sopenharmony_ci				   "%u in SA_AUTH state", exchange_type);
764e5b75505Sopenharmony_ci			return -1;
765e5b75505Sopenharmony_ci		}
766e5b75505Sopenharmony_ci		if (message_id != 1) {
767e5b75505Sopenharmony_ci			wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u "
768e5b75505Sopenharmony_ci				   "in SA_AUTH state", message_id);
769e5b75505Sopenharmony_ci			return -1;
770e5b75505Sopenharmony_ci		}
771e5b75505Sopenharmony_ci		break;
772e5b75505Sopenharmony_ci	case CHILD_SA:
773e5b75505Sopenharmony_ci		if (exchange_type != CREATE_CHILD_SA) {
774e5b75505Sopenharmony_ci			wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type "
775e5b75505Sopenharmony_ci				   "%u in CHILD_SA state", exchange_type);
776e5b75505Sopenharmony_ci			return -1;
777e5b75505Sopenharmony_ci		}
778e5b75505Sopenharmony_ci		if (message_id != 2) {
779e5b75505Sopenharmony_ci			wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u "
780e5b75505Sopenharmony_ci				   "in CHILD_SA state", message_id);
781e5b75505Sopenharmony_ci			return -1;
782e5b75505Sopenharmony_ci		}
783e5b75505Sopenharmony_ci		break;
784e5b75505Sopenharmony_ci	case IKEV2_DONE:
785e5b75505Sopenharmony_ci		return -1;
786e5b75505Sopenharmony_ci	}
787e5b75505Sopenharmony_ci
788e5b75505Sopenharmony_ci	return 0;
789e5b75505Sopenharmony_ci}
790e5b75505Sopenharmony_ci
791e5b75505Sopenharmony_ci
792e5b75505Sopenharmony_ciint ikev2_initiator_process(struct ikev2_initiator_data *data,
793e5b75505Sopenharmony_ci			    const struct wpabuf *buf)
794e5b75505Sopenharmony_ci{
795e5b75505Sopenharmony_ci	const struct ikev2_hdr *hdr;
796e5b75505Sopenharmony_ci	u32 length, message_id;
797e5b75505Sopenharmony_ci	const u8 *pos, *end;
798e5b75505Sopenharmony_ci	struct ikev2_payloads pl;
799e5b75505Sopenharmony_ci
800e5b75505Sopenharmony_ci	wpa_printf(MSG_MSGDUMP, "IKEV2: Received message (len %lu)",
801e5b75505Sopenharmony_ci		   (unsigned long) wpabuf_len(buf));
802e5b75505Sopenharmony_ci
803e5b75505Sopenharmony_ci	if (wpabuf_len(buf) < sizeof(*hdr)) {
804e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Too short frame to include HDR");
805e5b75505Sopenharmony_ci		return -1;
806e5b75505Sopenharmony_ci	}
807e5b75505Sopenharmony_ci
808e5b75505Sopenharmony_ci	hdr = (const struct ikev2_hdr *) wpabuf_head(buf);
809e5b75505Sopenharmony_ci	end = wpabuf_head_u8(buf) + wpabuf_len(buf);
810e5b75505Sopenharmony_ci	message_id = WPA_GET_BE32(hdr->message_id);
811e5b75505Sopenharmony_ci	length = WPA_GET_BE32(hdr->length);
812e5b75505Sopenharmony_ci
813e5b75505Sopenharmony_ci	wpa_hexdump(MSG_DEBUG, "IKEV2:   IKE_SA Initiator's SPI",
814e5b75505Sopenharmony_ci		    hdr->i_spi, IKEV2_SPI_LEN);
815e5b75505Sopenharmony_ci	wpa_hexdump(MSG_DEBUG, "IKEV2:   IKE_SA Initiator's SPI",
816e5b75505Sopenharmony_ci		    hdr->r_spi, IKEV2_SPI_LEN);
817e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "IKEV2:   Next Payload: %u  Version: 0x%x  "
818e5b75505Sopenharmony_ci		   "Exchange Type: %u",
819e5b75505Sopenharmony_ci		   hdr->next_payload, hdr->version, hdr->exchange_type);
820e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "IKEV2:   Message ID: %u  Length: %u",
821e5b75505Sopenharmony_ci		   message_id, length);
822e5b75505Sopenharmony_ci
823e5b75505Sopenharmony_ci	if (hdr->version != IKEV2_VERSION) {
824e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Unsupported HDR version 0x%x "
825e5b75505Sopenharmony_ci			   "(expected 0x%x)", hdr->version, IKEV2_VERSION);
826e5b75505Sopenharmony_ci		return -1;
827e5b75505Sopenharmony_ci	}
828e5b75505Sopenharmony_ci
829e5b75505Sopenharmony_ci	if (length != wpabuf_len(buf)) {
830e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Invalid length (HDR: %lu != "
831e5b75505Sopenharmony_ci			   "RX: %lu)", (unsigned long) length,
832e5b75505Sopenharmony_ci			   (unsigned long) wpabuf_len(buf));
833e5b75505Sopenharmony_ci		return -1;
834e5b75505Sopenharmony_ci	}
835e5b75505Sopenharmony_ci
836e5b75505Sopenharmony_ci	if (ikev2_validate_rx_state(data, hdr->exchange_type, message_id) < 0)
837e5b75505Sopenharmony_ci		return -1;
838e5b75505Sopenharmony_ci
839e5b75505Sopenharmony_ci	if ((hdr->flags & (IKEV2_HDR_INITIATOR | IKEV2_HDR_RESPONSE)) !=
840e5b75505Sopenharmony_ci	    IKEV2_HDR_RESPONSE) {
841e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Unexpected Flags value 0x%x",
842e5b75505Sopenharmony_ci			   hdr->flags);
843e5b75505Sopenharmony_ci		return -1;
844e5b75505Sopenharmony_ci	}
845e5b75505Sopenharmony_ci
846e5b75505Sopenharmony_ci	if (data->state != SA_INIT) {
847e5b75505Sopenharmony_ci		if (os_memcmp(data->i_spi, hdr->i_spi, IKEV2_SPI_LEN) != 0) {
848e5b75505Sopenharmony_ci			wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA "
849e5b75505Sopenharmony_ci				   "Initiator's SPI");
850e5b75505Sopenharmony_ci			return -1;
851e5b75505Sopenharmony_ci		}
852e5b75505Sopenharmony_ci		if (os_memcmp(data->r_spi, hdr->r_spi, IKEV2_SPI_LEN) != 0) {
853e5b75505Sopenharmony_ci			wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA "
854e5b75505Sopenharmony_ci				   "Responder's SPI");
855e5b75505Sopenharmony_ci			return -1;
856e5b75505Sopenharmony_ci		}
857e5b75505Sopenharmony_ci	}
858e5b75505Sopenharmony_ci
859e5b75505Sopenharmony_ci	pos = (const u8 *) (hdr + 1);
860e5b75505Sopenharmony_ci	if (ikev2_parse_payloads(&pl, hdr->next_payload, pos, end) < 0)
861e5b75505Sopenharmony_ci		return -1;
862e5b75505Sopenharmony_ci
863e5b75505Sopenharmony_ci	switch (data->state) {
864e5b75505Sopenharmony_ci	case SA_INIT:
865e5b75505Sopenharmony_ci		if (ikev2_process_sa_init(data, hdr, &pl) < 0)
866e5b75505Sopenharmony_ci			return -1;
867e5b75505Sopenharmony_ci		wpabuf_free(data->r_sign_msg);
868e5b75505Sopenharmony_ci		data->r_sign_msg = wpabuf_dup(buf);
869e5b75505Sopenharmony_ci		break;
870e5b75505Sopenharmony_ci	case SA_AUTH:
871e5b75505Sopenharmony_ci		if (ikev2_process_sa_auth(data, hdr, &pl) < 0)
872e5b75505Sopenharmony_ci			return -1;
873e5b75505Sopenharmony_ci		break;
874e5b75505Sopenharmony_ci	case CHILD_SA:
875e5b75505Sopenharmony_ci	case IKEV2_DONE:
876e5b75505Sopenharmony_ci		break;
877e5b75505Sopenharmony_ci	}
878e5b75505Sopenharmony_ci
879e5b75505Sopenharmony_ci	return 0;
880e5b75505Sopenharmony_ci}
881e5b75505Sopenharmony_ci
882e5b75505Sopenharmony_ci
883e5b75505Sopenharmony_cistatic void ikev2_build_hdr(struct ikev2_initiator_data *data,
884e5b75505Sopenharmony_ci			    struct wpabuf *msg, u8 exchange_type,
885e5b75505Sopenharmony_ci			    u8 next_payload, u32 message_id)
886e5b75505Sopenharmony_ci{
887e5b75505Sopenharmony_ci	struct ikev2_hdr *hdr;
888e5b75505Sopenharmony_ci
889e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "IKEV2: Adding HDR");
890e5b75505Sopenharmony_ci
891e5b75505Sopenharmony_ci	/* HDR - RFC 4306, Sect. 3.1 */
892e5b75505Sopenharmony_ci	hdr = wpabuf_put(msg, sizeof(*hdr));
893e5b75505Sopenharmony_ci	os_memcpy(hdr->i_spi, data->i_spi, IKEV2_SPI_LEN);
894e5b75505Sopenharmony_ci	os_memcpy(hdr->r_spi, data->r_spi, IKEV2_SPI_LEN);
895e5b75505Sopenharmony_ci	hdr->next_payload = next_payload;
896e5b75505Sopenharmony_ci	hdr->version = IKEV2_VERSION;
897e5b75505Sopenharmony_ci	hdr->exchange_type = exchange_type;
898e5b75505Sopenharmony_ci	hdr->flags = IKEV2_HDR_INITIATOR;
899e5b75505Sopenharmony_ci	WPA_PUT_BE32(hdr->message_id, message_id);
900e5b75505Sopenharmony_ci}
901e5b75505Sopenharmony_ci
902e5b75505Sopenharmony_ci
903e5b75505Sopenharmony_cistatic int ikev2_build_sai(struct ikev2_initiator_data *data,
904e5b75505Sopenharmony_ci			    struct wpabuf *msg, u8 next_payload)
905e5b75505Sopenharmony_ci{
906e5b75505Sopenharmony_ci	struct ikev2_payload_hdr *phdr;
907e5b75505Sopenharmony_ci	size_t plen;
908e5b75505Sopenharmony_ci	struct ikev2_proposal *p;
909e5b75505Sopenharmony_ci	struct ikev2_transform *t;
910e5b75505Sopenharmony_ci
911e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "IKEV2: Adding SAi payload");
912e5b75505Sopenharmony_ci
913e5b75505Sopenharmony_ci	/* SAi1 - RFC 4306, Sect. 2.7 and 3.3 */
914e5b75505Sopenharmony_ci	phdr = wpabuf_put(msg, sizeof(*phdr));
915e5b75505Sopenharmony_ci	phdr->next_payload = next_payload;
916e5b75505Sopenharmony_ci	phdr->flags = 0;
917e5b75505Sopenharmony_ci
918e5b75505Sopenharmony_ci	/* TODO: support for multiple proposals */
919e5b75505Sopenharmony_ci	p = wpabuf_put(msg, sizeof(*p));
920e5b75505Sopenharmony_ci	p->proposal_num = data->proposal.proposal_num;
921e5b75505Sopenharmony_ci	p->protocol_id = IKEV2_PROTOCOL_IKE;
922e5b75505Sopenharmony_ci	p->num_transforms = 4;
923e5b75505Sopenharmony_ci
924e5b75505Sopenharmony_ci	t = wpabuf_put(msg, sizeof(*t));
925e5b75505Sopenharmony_ci	t->type = 3;
926e5b75505Sopenharmony_ci	t->transform_type = IKEV2_TRANSFORM_ENCR;
927e5b75505Sopenharmony_ci	WPA_PUT_BE16(t->transform_id, data->proposal.encr);
928e5b75505Sopenharmony_ci	if (data->proposal.encr == ENCR_AES_CBC) {
929e5b75505Sopenharmony_ci		/* Transform Attribute: Key Len = 128 bits */
930e5b75505Sopenharmony_ci		wpabuf_put_be16(msg, 0x800e); /* AF=1, AttrType=14 */
931e5b75505Sopenharmony_ci		wpabuf_put_be16(msg, 128); /* 128-bit key */
932e5b75505Sopenharmony_ci	}
933e5b75505Sopenharmony_ci	plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) t;
934e5b75505Sopenharmony_ci	WPA_PUT_BE16(t->transform_length, plen);
935e5b75505Sopenharmony_ci
936e5b75505Sopenharmony_ci	t = wpabuf_put(msg, sizeof(*t));
937e5b75505Sopenharmony_ci	t->type = 3;
938e5b75505Sopenharmony_ci	WPA_PUT_BE16(t->transform_length, sizeof(*t));
939e5b75505Sopenharmony_ci	t->transform_type = IKEV2_TRANSFORM_PRF;
940e5b75505Sopenharmony_ci	WPA_PUT_BE16(t->transform_id, data->proposal.prf);
941e5b75505Sopenharmony_ci
942e5b75505Sopenharmony_ci	t = wpabuf_put(msg, sizeof(*t));
943e5b75505Sopenharmony_ci	t->type = 3;
944e5b75505Sopenharmony_ci	WPA_PUT_BE16(t->transform_length, sizeof(*t));
945e5b75505Sopenharmony_ci	t->transform_type = IKEV2_TRANSFORM_INTEG;
946e5b75505Sopenharmony_ci	WPA_PUT_BE16(t->transform_id, data->proposal.integ);
947e5b75505Sopenharmony_ci
948e5b75505Sopenharmony_ci	t = wpabuf_put(msg, sizeof(*t));
949e5b75505Sopenharmony_ci	WPA_PUT_BE16(t->transform_length, sizeof(*t));
950e5b75505Sopenharmony_ci	t->transform_type = IKEV2_TRANSFORM_DH;
951e5b75505Sopenharmony_ci	WPA_PUT_BE16(t->transform_id, data->proposal.dh);
952e5b75505Sopenharmony_ci
953e5b75505Sopenharmony_ci	plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) p;
954e5b75505Sopenharmony_ci	WPA_PUT_BE16(p->proposal_length, plen);
955e5b75505Sopenharmony_ci
956e5b75505Sopenharmony_ci	plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
957e5b75505Sopenharmony_ci	WPA_PUT_BE16(phdr->payload_length, plen);
958e5b75505Sopenharmony_ci
959e5b75505Sopenharmony_ci	return 0;
960e5b75505Sopenharmony_ci}
961e5b75505Sopenharmony_ci
962e5b75505Sopenharmony_ci
963e5b75505Sopenharmony_cistatic int ikev2_build_kei(struct ikev2_initiator_data *data,
964e5b75505Sopenharmony_ci			   struct wpabuf *msg, u8 next_payload)
965e5b75505Sopenharmony_ci{
966e5b75505Sopenharmony_ci	struct ikev2_payload_hdr *phdr;
967e5b75505Sopenharmony_ci	size_t plen;
968e5b75505Sopenharmony_ci	struct wpabuf *pv;
969e5b75505Sopenharmony_ci
970e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "IKEV2: Adding KEi payload");
971e5b75505Sopenharmony_ci
972e5b75505Sopenharmony_ci	data->dh = dh_groups_get(data->proposal.dh);
973e5b75505Sopenharmony_ci	pv = dh_init(data->dh, &data->i_dh_private);
974e5b75505Sopenharmony_ci	if (pv == NULL) {
975e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "IKEV2: Failed to initialize DH");
976e5b75505Sopenharmony_ci		return -1;
977e5b75505Sopenharmony_ci	}
978e5b75505Sopenharmony_ci
979e5b75505Sopenharmony_ci	/* KEi - RFC 4306, Sect. 3.4 */
980e5b75505Sopenharmony_ci	phdr = wpabuf_put(msg, sizeof(*phdr));
981e5b75505Sopenharmony_ci	phdr->next_payload = next_payload;
982e5b75505Sopenharmony_ci	phdr->flags = 0;
983e5b75505Sopenharmony_ci
984e5b75505Sopenharmony_ci	wpabuf_put_be16(msg, data->proposal.dh); /* DH Group # */
985e5b75505Sopenharmony_ci	wpabuf_put(msg, 2); /* RESERVED */
986e5b75505Sopenharmony_ci	/*
987e5b75505Sopenharmony_ci	 * RFC 4306, Sect. 3.4: possible zero padding for public value to
988e5b75505Sopenharmony_ci	 * match the length of the prime.
989e5b75505Sopenharmony_ci	 */
990e5b75505Sopenharmony_ci	wpabuf_put(msg, data->dh->prime_len - wpabuf_len(pv));
991e5b75505Sopenharmony_ci	wpabuf_put_buf(msg, pv);
992e5b75505Sopenharmony_ci	wpabuf_free(pv);
993e5b75505Sopenharmony_ci
994e5b75505Sopenharmony_ci	plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
995e5b75505Sopenharmony_ci	WPA_PUT_BE16(phdr->payload_length, plen);
996e5b75505Sopenharmony_ci	return 0;
997e5b75505Sopenharmony_ci}
998e5b75505Sopenharmony_ci
999e5b75505Sopenharmony_ci
1000e5b75505Sopenharmony_cistatic int ikev2_build_ni(struct ikev2_initiator_data *data,
1001e5b75505Sopenharmony_ci			  struct wpabuf *msg, u8 next_payload)
1002e5b75505Sopenharmony_ci{
1003e5b75505Sopenharmony_ci	struct ikev2_payload_hdr *phdr;
1004e5b75505Sopenharmony_ci	size_t plen;
1005e5b75505Sopenharmony_ci
1006e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "IKEV2: Adding Ni payload");
1007e5b75505Sopenharmony_ci
1008e5b75505Sopenharmony_ci	/* Ni - RFC 4306, Sect. 3.9 */
1009e5b75505Sopenharmony_ci	phdr = wpabuf_put(msg, sizeof(*phdr));
1010e5b75505Sopenharmony_ci	phdr->next_payload = next_payload;
1011e5b75505Sopenharmony_ci	phdr->flags = 0;
1012e5b75505Sopenharmony_ci	wpabuf_put_data(msg, data->i_nonce, data->i_nonce_len);
1013e5b75505Sopenharmony_ci	plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
1014e5b75505Sopenharmony_ci	WPA_PUT_BE16(phdr->payload_length, plen);
1015e5b75505Sopenharmony_ci	return 0;
1016e5b75505Sopenharmony_ci}
1017e5b75505Sopenharmony_ci
1018e5b75505Sopenharmony_ci
1019e5b75505Sopenharmony_cistatic int ikev2_build_idi(struct ikev2_initiator_data *data,
1020e5b75505Sopenharmony_ci			   struct wpabuf *msg, u8 next_payload)
1021e5b75505Sopenharmony_ci{
1022e5b75505Sopenharmony_ci	struct ikev2_payload_hdr *phdr;
1023e5b75505Sopenharmony_ci	size_t plen;
1024e5b75505Sopenharmony_ci
1025e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "IKEV2: Adding IDi payload");
1026e5b75505Sopenharmony_ci
1027e5b75505Sopenharmony_ci	if (data->IDi == NULL) {
1028e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: No IDi available");
1029e5b75505Sopenharmony_ci		return -1;
1030e5b75505Sopenharmony_ci	}
1031e5b75505Sopenharmony_ci
1032e5b75505Sopenharmony_ci	/* IDi - RFC 4306, Sect. 3.5 */
1033e5b75505Sopenharmony_ci	phdr = wpabuf_put(msg, sizeof(*phdr));
1034e5b75505Sopenharmony_ci	phdr->next_payload = next_payload;
1035e5b75505Sopenharmony_ci	phdr->flags = 0;
1036e5b75505Sopenharmony_ci	wpabuf_put_u8(msg, ID_KEY_ID);
1037e5b75505Sopenharmony_ci	wpabuf_put(msg, 3); /* RESERVED */
1038e5b75505Sopenharmony_ci	wpabuf_put_data(msg, data->IDi, data->IDi_len);
1039e5b75505Sopenharmony_ci	plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
1040e5b75505Sopenharmony_ci	WPA_PUT_BE16(phdr->payload_length, plen);
1041e5b75505Sopenharmony_ci	return 0;
1042e5b75505Sopenharmony_ci}
1043e5b75505Sopenharmony_ci
1044e5b75505Sopenharmony_ci
1045e5b75505Sopenharmony_cistatic int ikev2_build_auth(struct ikev2_initiator_data *data,
1046e5b75505Sopenharmony_ci			    struct wpabuf *msg, u8 next_payload)
1047e5b75505Sopenharmony_ci{
1048e5b75505Sopenharmony_ci	struct ikev2_payload_hdr *phdr;
1049e5b75505Sopenharmony_ci	size_t plen;
1050e5b75505Sopenharmony_ci	const struct ikev2_prf_alg *prf;
1051e5b75505Sopenharmony_ci
1052e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "IKEV2: Adding AUTH payload");
1053e5b75505Sopenharmony_ci
1054e5b75505Sopenharmony_ci	prf = ikev2_get_prf(data->proposal.prf);
1055e5b75505Sopenharmony_ci	if (prf == NULL)
1056e5b75505Sopenharmony_ci		return -1;
1057e5b75505Sopenharmony_ci
1058e5b75505Sopenharmony_ci	/* Authentication - RFC 4306, Sect. 3.8 */
1059e5b75505Sopenharmony_ci	phdr = wpabuf_put(msg, sizeof(*phdr));
1060e5b75505Sopenharmony_ci	phdr->next_payload = next_payload;
1061e5b75505Sopenharmony_ci	phdr->flags = 0;
1062e5b75505Sopenharmony_ci	wpabuf_put_u8(msg, AUTH_SHARED_KEY_MIC);
1063e5b75505Sopenharmony_ci	wpabuf_put(msg, 3); /* RESERVED */
1064e5b75505Sopenharmony_ci
1065e5b75505Sopenharmony_ci	/* msg | Nr | prf(SK_pi,IDi') */
1066e5b75505Sopenharmony_ci	if (ikev2_derive_auth_data(data->proposal.prf, data->i_sign_msg,
1067e5b75505Sopenharmony_ci				   data->IDi, data->IDi_len, ID_KEY_ID,
1068e5b75505Sopenharmony_ci				   &data->keys, 1, data->shared_secret,
1069e5b75505Sopenharmony_ci				   data->shared_secret_len,
1070e5b75505Sopenharmony_ci				   data->r_nonce, data->r_nonce_len,
1071e5b75505Sopenharmony_ci				   data->key_pad, data->key_pad_len,
1072e5b75505Sopenharmony_ci				   wpabuf_put(msg, prf->hash_len)) < 0) {
1073e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data");
1074e5b75505Sopenharmony_ci		return -1;
1075e5b75505Sopenharmony_ci	}
1076e5b75505Sopenharmony_ci	wpabuf_free(data->i_sign_msg);
1077e5b75505Sopenharmony_ci	data->i_sign_msg = NULL;
1078e5b75505Sopenharmony_ci
1079e5b75505Sopenharmony_ci	plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
1080e5b75505Sopenharmony_ci	WPA_PUT_BE16(phdr->payload_length, plen);
1081e5b75505Sopenharmony_ci	return 0;
1082e5b75505Sopenharmony_ci}
1083e5b75505Sopenharmony_ci
1084e5b75505Sopenharmony_ci
1085e5b75505Sopenharmony_cistatic struct wpabuf * ikev2_build_sa_init(struct ikev2_initiator_data *data)
1086e5b75505Sopenharmony_ci{
1087e5b75505Sopenharmony_ci	struct wpabuf *msg;
1088e5b75505Sopenharmony_ci
1089e5b75505Sopenharmony_ci	/* build IKE_SA_INIT: HDR, SAi, KEi, Ni */
1090e5b75505Sopenharmony_ci
1091e5b75505Sopenharmony_ci	if (os_get_random(data->i_spi, IKEV2_SPI_LEN))
1092e5b75505Sopenharmony_ci		return NULL;
1093e5b75505Sopenharmony_ci	wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Initiator's SPI",
1094e5b75505Sopenharmony_ci		    data->i_spi, IKEV2_SPI_LEN);
1095e5b75505Sopenharmony_ci
1096e5b75505Sopenharmony_ci	data->i_nonce_len = IKEV2_NONCE_MIN_LEN;
1097e5b75505Sopenharmony_ci	if (random_get_bytes(data->i_nonce, data->i_nonce_len))
1098e5b75505Sopenharmony_ci		return NULL;
1099e5b75505Sopenharmony_ci	wpa_hexdump(MSG_DEBUG, "IKEV2: Ni", data->i_nonce, data->i_nonce_len);
1100e5b75505Sopenharmony_ci
1101e5b75505Sopenharmony_ci	msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + 1000);
1102e5b75505Sopenharmony_ci	if (msg == NULL)
1103e5b75505Sopenharmony_ci		return NULL;
1104e5b75505Sopenharmony_ci
1105e5b75505Sopenharmony_ci	ikev2_build_hdr(data, msg, IKE_SA_INIT, IKEV2_PAYLOAD_SA, 0);
1106e5b75505Sopenharmony_ci	if (ikev2_build_sai(data, msg, IKEV2_PAYLOAD_KEY_EXCHANGE) ||
1107e5b75505Sopenharmony_ci	    ikev2_build_kei(data, msg, IKEV2_PAYLOAD_NONCE) ||
1108e5b75505Sopenharmony_ci	    ikev2_build_ni(data, msg, IKEV2_PAYLOAD_NO_NEXT_PAYLOAD)) {
1109e5b75505Sopenharmony_ci		wpabuf_free(msg);
1110e5b75505Sopenharmony_ci		return NULL;
1111e5b75505Sopenharmony_ci	}
1112e5b75505Sopenharmony_ci
1113e5b75505Sopenharmony_ci	ikev2_update_hdr(msg);
1114e5b75505Sopenharmony_ci
1115e5b75505Sopenharmony_ci	wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (SA_INIT)", msg);
1116e5b75505Sopenharmony_ci
1117e5b75505Sopenharmony_ci	wpabuf_free(data->i_sign_msg);
1118e5b75505Sopenharmony_ci	data->i_sign_msg = wpabuf_dup(msg);
1119e5b75505Sopenharmony_ci
1120e5b75505Sopenharmony_ci	return msg;
1121e5b75505Sopenharmony_ci}
1122e5b75505Sopenharmony_ci
1123e5b75505Sopenharmony_ci
1124e5b75505Sopenharmony_cistatic struct wpabuf * ikev2_build_sa_auth(struct ikev2_initiator_data *data)
1125e5b75505Sopenharmony_ci{
1126e5b75505Sopenharmony_ci	struct wpabuf *msg, *plain;
1127e5b75505Sopenharmony_ci	const u8 *secret;
1128e5b75505Sopenharmony_ci	size_t secret_len;
1129e5b75505Sopenharmony_ci
1130e5b75505Sopenharmony_ci	secret = data->get_shared_secret(data->cb_ctx, data->IDr,
1131e5b75505Sopenharmony_ci					 data->IDr_len, &secret_len);
1132e5b75505Sopenharmony_ci	if (secret == NULL) {
1133e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IKEV2: Could not get shared secret - "
1134e5b75505Sopenharmony_ci			   "use fake value");
1135e5b75505Sopenharmony_ci		/* RFC 5106, Sect. 7:
1136e5b75505Sopenharmony_ci		 * Use a random key to fake AUTH generation in order to prevent
1137e5b75505Sopenharmony_ci		 * probing of user identities.
1138e5b75505Sopenharmony_ci		 */
1139e5b75505Sopenharmony_ci		data->unknown_user = 1;
1140e5b75505Sopenharmony_ci		os_free(data->shared_secret);
1141e5b75505Sopenharmony_ci		data->shared_secret = os_malloc(16);
1142e5b75505Sopenharmony_ci		if (data->shared_secret == NULL)
1143e5b75505Sopenharmony_ci			return NULL;
1144e5b75505Sopenharmony_ci		data->shared_secret_len = 16;
1145e5b75505Sopenharmony_ci		if (random_get_bytes(data->shared_secret, 16))
1146e5b75505Sopenharmony_ci			return NULL;
1147e5b75505Sopenharmony_ci	} else {
1148e5b75505Sopenharmony_ci		os_free(data->shared_secret);
1149e5b75505Sopenharmony_ci		data->shared_secret = os_memdup(secret, secret_len);
1150e5b75505Sopenharmony_ci		if (data->shared_secret == NULL)
1151e5b75505Sopenharmony_ci			return NULL;
1152e5b75505Sopenharmony_ci		data->shared_secret_len = secret_len;
1153e5b75505Sopenharmony_ci	}
1154e5b75505Sopenharmony_ci
1155e5b75505Sopenharmony_ci	/* build IKE_SA_AUTH: HDR, SK {IDi, [CERT,] [CERTREQ,] AUTH} */
1156e5b75505Sopenharmony_ci
1157e5b75505Sopenharmony_ci	msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + data->IDr_len + 1000);
1158e5b75505Sopenharmony_ci	if (msg == NULL)
1159e5b75505Sopenharmony_ci		return NULL;
1160e5b75505Sopenharmony_ci	ikev2_build_hdr(data, msg, IKE_SA_AUTH, IKEV2_PAYLOAD_ENCRYPTED, 1);
1161e5b75505Sopenharmony_ci
1162e5b75505Sopenharmony_ci	plain = wpabuf_alloc(data->IDr_len + 1000);
1163e5b75505Sopenharmony_ci	if (plain == NULL) {
1164e5b75505Sopenharmony_ci		wpabuf_free(msg);
1165e5b75505Sopenharmony_ci		return NULL;
1166e5b75505Sopenharmony_ci	}
1167e5b75505Sopenharmony_ci
1168e5b75505Sopenharmony_ci	if (ikev2_build_idi(data, plain, IKEV2_PAYLOAD_AUTHENTICATION) ||
1169e5b75505Sopenharmony_ci	    ikev2_build_auth(data, plain, IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) ||
1170e5b75505Sopenharmony_ci	    ikev2_build_encrypted(data->proposal.encr, data->proposal.integ,
1171e5b75505Sopenharmony_ci				  &data->keys, 1, msg, plain,
1172e5b75505Sopenharmony_ci				  IKEV2_PAYLOAD_IDi)) {
1173e5b75505Sopenharmony_ci		wpabuf_free(plain);
1174e5b75505Sopenharmony_ci		wpabuf_free(msg);
1175e5b75505Sopenharmony_ci		return NULL;
1176e5b75505Sopenharmony_ci	}
1177e5b75505Sopenharmony_ci	wpabuf_free(plain);
1178e5b75505Sopenharmony_ci
1179e5b75505Sopenharmony_ci	wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (SA_AUTH)", msg);
1180e5b75505Sopenharmony_ci
1181e5b75505Sopenharmony_ci	return msg;
1182e5b75505Sopenharmony_ci}
1183e5b75505Sopenharmony_ci
1184e5b75505Sopenharmony_ci
1185e5b75505Sopenharmony_cistruct wpabuf * ikev2_initiator_build(struct ikev2_initiator_data *data)
1186e5b75505Sopenharmony_ci{
1187e5b75505Sopenharmony_ci	switch (data->state) {
1188e5b75505Sopenharmony_ci	case SA_INIT:
1189e5b75505Sopenharmony_ci		return ikev2_build_sa_init(data);
1190e5b75505Sopenharmony_ci	case SA_AUTH:
1191e5b75505Sopenharmony_ci		return ikev2_build_sa_auth(data);
1192e5b75505Sopenharmony_ci	case CHILD_SA:
1193e5b75505Sopenharmony_ci		return NULL;
1194e5b75505Sopenharmony_ci	case IKEV2_DONE:
1195e5b75505Sopenharmony_ci		return NULL;
1196e5b75505Sopenharmony_ci	}
1197e5b75505Sopenharmony_ci	return NULL;
1198e5b75505Sopenharmony_ci}
1199