1189251Ssam/*
2189251Ssam * EAP-IKEv2 common routines
3189251Ssam * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
4189251Ssam *
5189251Ssam * This program is free software; you can redistribute it and/or modify
6189251Ssam * it under the terms of the GNU General Public License version 2 as
7189251Ssam * published by the Free Software Foundation.
8189251Ssam *
9189251Ssam * Alternatively, this software may be distributed under the terms of BSD
10189251Ssam * license.
11189251Ssam *
12189251Ssam * See README and COPYING for more details.
13189251Ssam */
14189251Ssam
15189251Ssam#include "includes.h"
16189251Ssam
17189251Ssam#include "common.h"
18189251Ssam#include "eap_defs.h"
19189251Ssam#include "eap_common.h"
20189251Ssam#include "ikev2_common.h"
21189251Ssam#include "eap_ikev2_common.h"
22189251Ssam
23189251Ssam
24189251Ssamint eap_ikev2_derive_keymat(int prf, struct ikev2_keys *keys,
25189251Ssam			    const u8 *i_nonce, size_t i_nonce_len,
26189251Ssam			    const u8 *r_nonce, size_t r_nonce_len,
27189251Ssam			    u8 *keymat)
28189251Ssam{
29189251Ssam	u8 *nonces;
30189251Ssam	size_t nlen;
31189251Ssam
32189251Ssam	/* KEYMAT = prf+(SK_d, Ni | Nr) */
33189251Ssam	if (keys->SK_d == NULL || i_nonce == NULL || r_nonce == NULL)
34189251Ssam		return -1;
35189251Ssam
36189251Ssam	nlen = i_nonce_len + r_nonce_len;
37189251Ssam	nonces = os_malloc(nlen);
38189251Ssam	if (nonces == NULL)
39189251Ssam		return -1;
40189251Ssam	os_memcpy(nonces, i_nonce, i_nonce_len);
41189251Ssam	os_memcpy(nonces + i_nonce_len, r_nonce, r_nonce_len);
42189251Ssam
43189251Ssam	if (ikev2_prf_plus(prf, keys->SK_d, keys->SK_d_len, nonces, nlen,
44189251Ssam			   keymat, EAP_MSK_LEN + EAP_EMSK_LEN)) {
45189251Ssam		os_free(nonces);
46189251Ssam		return -1;
47189251Ssam	}
48189251Ssam	os_free(nonces);
49189251Ssam
50189251Ssam	wpa_hexdump_key(MSG_DEBUG, "EAP-IKEV2: KEYMAT",
51189251Ssam			keymat, EAP_MSK_LEN + EAP_EMSK_LEN);
52189251Ssam
53189251Ssam	return 0;
54189251Ssam}
55189251Ssam
56189251Ssam
57189251Ssamstruct wpabuf * eap_ikev2_build_frag_ack(u8 id, u8 code)
58189251Ssam{
59189251Ssam	struct wpabuf *msg;
60189251Ssam
61189251Ssam#ifdef CCNS_PL
62189251Ssam	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, 1, code, id);
63189251Ssam	if (msg == NULL) {
64189251Ssam		wpa_printf(MSG_ERROR, "EAP-IKEV2: Failed to allocate memory "
65189251Ssam			   "for fragment ack");
66189251Ssam		return NULL;
67189251Ssam	}
68189251Ssam	wpabuf_put_u8(msg, 0); /* Flags */
69189251Ssam#else /* CCNS_PL */
70189251Ssam	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, 0, code, id);
71189251Ssam	if (msg == NULL) {
72189251Ssam		wpa_printf(MSG_ERROR, "EAP-IKEV2: Failed to allocate memory "
73189251Ssam			   "for fragment ack");
74189251Ssam		return NULL;
75189251Ssam	}
76189251Ssam#endif /* CCNS_PL */
77189251Ssam
78189251Ssam	wpa_printf(MSG_DEBUG, "EAP-IKEV2: Send fragment ack");
79189251Ssam
80189251Ssam	return msg;
81189251Ssam}
82189251Ssam
83189251Ssam
84189251Ssamint eap_ikev2_validate_icv(int integ_alg, struct ikev2_keys *keys,
85189251Ssam			   int initiator, const struct wpabuf *msg,
86189251Ssam			   const u8 *pos, const u8 *end)
87189251Ssam{
88189251Ssam	const struct ikev2_integ_alg *integ;
89189251Ssam	size_t icv_len;
90189251Ssam	u8 icv[IKEV2_MAX_HASH_LEN];
91189251Ssam	const u8 *SK_a = initiator ? keys->SK_ai : keys->SK_ar;
92189251Ssam
93189251Ssam	integ = ikev2_get_integ(integ_alg);
94189251Ssam	if (integ == NULL) {
95189251Ssam		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG "
96189251Ssam			   "transform / cannot validate ICV");
97189251Ssam		return -1;
98189251Ssam	}
99189251Ssam	icv_len = integ->hash_len;
100189251Ssam
101189251Ssam	if (end - pos < (int) icv_len) {
102189251Ssam		wpa_printf(MSG_DEBUG, "EAP-IKEV2: Not enough room in the "
103189251Ssam			   "message for Integrity Checksum Data");
104189251Ssam		return -1;
105189251Ssam	}
106189251Ssam
107189251Ssam	if (SK_a == NULL) {
108189251Ssam		wpa_printf(MSG_DEBUG, "EAP-IKEV2: No SK_a for ICV validation");
109189251Ssam		return -1;
110189251Ssam	}
111189251Ssam
112189251Ssam	if (ikev2_integ_hash(integ_alg, SK_a, keys->SK_integ_len,
113189251Ssam			     wpabuf_head(msg),
114189251Ssam			     wpabuf_len(msg) - icv_len, icv) < 0) {
115189251Ssam		wpa_printf(MSG_INFO, "EAP-IKEV2: Could not calculate ICV");
116189251Ssam		return -1;
117189251Ssam	}
118189251Ssam
119189251Ssam	if (os_memcmp(icv, end - icv_len, icv_len) != 0) {
120189251Ssam		wpa_printf(MSG_INFO, "EAP-IKEV2: Invalid ICV");
121189251Ssam		wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Calculated ICV",
122189251Ssam			    icv, icv_len);
123189251Ssam		wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Received ICV",
124189251Ssam			    end - icv_len, icv_len);
125189251Ssam		return -1;
126189251Ssam	}
127189251Ssam
128189251Ssam	wpa_printf(MSG_DEBUG, "EAP-IKEV2: Valid Integrity Checksum Data in "
129189251Ssam		   "the received message");
130189251Ssam
131189251Ssam	return icv_len;
132189251Ssam}
133