1189251Ssam/*
2189251Ssam * Wi-Fi Protected Setup - attribute building
3189251Ssam * Copyright (c) 2008, 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"
18214734Srpaulo#include "crypto/aes_wrap.h"
19214734Srpaulo#include "crypto/crypto.h"
20214734Srpaulo#include "crypto/dh_group5.h"
21214734Srpaulo#include "crypto/sha256.h"
22189251Ssam#include "wps_i.h"
23189251Ssam
24189251Ssam
25189251Ssamint wps_build_public_key(struct wps_data *wps, struct wpabuf *msg)
26189251Ssam{
27189251Ssam	struct wpabuf *pubkey;
28189251Ssam
29189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * Public Key");
30214734Srpaulo	wpabuf_free(wps->dh_privkey);
31214734Srpaulo	if (wps->dev_pw_id != DEV_PW_DEFAULT && wps->wps->dh_privkey) {
32214734Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Using pre-configured DH keys");
33214734Srpaulo		wps->dh_privkey = wpabuf_dup(wps->wps->dh_privkey);
34214734Srpaulo		wps->dh_ctx = wps->wps->dh_ctx;
35214734Srpaulo		wps->wps->dh_ctx = NULL;
36214734Srpaulo		pubkey = wpabuf_dup(wps->wps->dh_pubkey);
37214734Srpaulo	} else {
38214734Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Generate new DH keys");
39214734Srpaulo		wps->dh_privkey = NULL;
40214734Srpaulo		dh5_free(wps->dh_ctx);
41214734Srpaulo		wps->dh_ctx = dh5_init(&wps->dh_privkey, &pubkey);
42214734Srpaulo		pubkey = wpabuf_zeropad(pubkey, 192);
43214734Srpaulo	}
44214734Srpaulo	if (wps->dh_ctx == NULL || wps->dh_privkey == NULL || pubkey == NULL) {
45189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Failed to initialize "
46189251Ssam			   "Diffie-Hellman handshake");
47214734Srpaulo		wpabuf_free(pubkey);
48189251Ssam		return -1;
49189251Ssam	}
50189251Ssam
51189251Ssam	wpabuf_put_be16(msg, ATTR_PUBLIC_KEY);
52189251Ssam	wpabuf_put_be16(msg, wpabuf_len(pubkey));
53189251Ssam	wpabuf_put_buf(msg, pubkey);
54189251Ssam
55189251Ssam	if (wps->registrar) {
56189251Ssam		wpabuf_free(wps->dh_pubkey_r);
57189251Ssam		wps->dh_pubkey_r = pubkey;
58189251Ssam	} else {
59189251Ssam		wpabuf_free(wps->dh_pubkey_e);
60189251Ssam		wps->dh_pubkey_e = pubkey;
61189251Ssam	}
62189251Ssam
63189251Ssam	return 0;
64189251Ssam}
65189251Ssam
66189251Ssam
67189251Ssamint wps_build_req_type(struct wpabuf *msg, enum wps_request_type type)
68189251Ssam{
69189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * Request Type");
70189251Ssam	wpabuf_put_be16(msg, ATTR_REQUEST_TYPE);
71189251Ssam	wpabuf_put_be16(msg, 1);
72189251Ssam	wpabuf_put_u8(msg, type);
73189251Ssam	return 0;
74189251Ssam}
75189251Ssam
76189251Ssam
77214734Srpauloint wps_build_resp_type(struct wpabuf *msg, enum wps_response_type type)
78214734Srpaulo{
79214734Srpaulo	wpa_printf(MSG_DEBUG, "WPS:  * Response Type (%d)", type);
80214734Srpaulo	wpabuf_put_be16(msg, ATTR_RESPONSE_TYPE);
81214734Srpaulo	wpabuf_put_be16(msg, 1);
82214734Srpaulo	wpabuf_put_u8(msg, type);
83214734Srpaulo	return 0;
84214734Srpaulo}
85214734Srpaulo
86214734Srpaulo
87189251Ssamint wps_build_config_methods(struct wpabuf *msg, u16 methods)
88189251Ssam{
89189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * Config Methods (%x)", methods);
90189251Ssam	wpabuf_put_be16(msg, ATTR_CONFIG_METHODS);
91189251Ssam	wpabuf_put_be16(msg, 2);
92189251Ssam	wpabuf_put_be16(msg, methods);
93189251Ssam	return 0;
94189251Ssam}
95189251Ssam
96189251Ssam
97189251Ssamint wps_build_uuid_e(struct wpabuf *msg, const u8 *uuid)
98189251Ssam{
99189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * UUID-E");
100189251Ssam	wpabuf_put_be16(msg, ATTR_UUID_E);
101189251Ssam	wpabuf_put_be16(msg, WPS_UUID_LEN);
102189251Ssam	wpabuf_put_data(msg, uuid, WPS_UUID_LEN);
103189251Ssam	return 0;
104189251Ssam}
105189251Ssam
106189251Ssam
107189251Ssamint wps_build_dev_password_id(struct wpabuf *msg, u16 id)
108189251Ssam{
109189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * Device Password ID (%d)", id);
110189251Ssam	wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
111189251Ssam	wpabuf_put_be16(msg, 2);
112189251Ssam	wpabuf_put_be16(msg, id);
113189251Ssam	return 0;
114189251Ssam}
115189251Ssam
116189251Ssam
117189251Ssamint wps_build_config_error(struct wpabuf *msg, u16 err)
118189251Ssam{
119189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * Configuration Error (%d)", err);
120189251Ssam	wpabuf_put_be16(msg, ATTR_CONFIG_ERROR);
121189251Ssam	wpabuf_put_be16(msg, 2);
122189251Ssam	wpabuf_put_be16(msg, err);
123189251Ssam	return 0;
124189251Ssam}
125189251Ssam
126189251Ssam
127189251Ssamint wps_build_authenticator(struct wps_data *wps, struct wpabuf *msg)
128189251Ssam{
129189251Ssam	u8 hash[SHA256_MAC_LEN];
130189251Ssam	const u8 *addr[2];
131189251Ssam	size_t len[2];
132189251Ssam
133189251Ssam	if (wps->last_msg == NULL) {
134189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Last message not available for "
135189251Ssam			   "building authenticator");
136189251Ssam		return -1;
137189251Ssam	}
138189251Ssam
139189251Ssam	/* Authenticator = HMAC-SHA256_AuthKey(M_prev || M_curr*)
140189251Ssam	 * (M_curr* is M_curr without the Authenticator attribute)
141189251Ssam	 */
142189251Ssam	addr[0] = wpabuf_head(wps->last_msg);
143189251Ssam	len[0] = wpabuf_len(wps->last_msg);
144189251Ssam	addr[1] = wpabuf_head(msg);
145189251Ssam	len[1] = wpabuf_len(msg);
146189251Ssam	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len, hash);
147189251Ssam
148189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * Authenticator");
149189251Ssam	wpabuf_put_be16(msg, ATTR_AUTHENTICATOR);
150189251Ssam	wpabuf_put_be16(msg, WPS_AUTHENTICATOR_LEN);
151189251Ssam	wpabuf_put_data(msg, hash, WPS_AUTHENTICATOR_LEN);
152189251Ssam
153189251Ssam	return 0;
154189251Ssam}
155189251Ssam
156189251Ssam
157189251Ssamint wps_build_version(struct wpabuf *msg)
158189251Ssam{
159189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * Version");
160189251Ssam	wpabuf_put_be16(msg, ATTR_VERSION);
161189251Ssam	wpabuf_put_be16(msg, 1);
162189251Ssam	wpabuf_put_u8(msg, WPS_VERSION);
163189251Ssam	return 0;
164189251Ssam}
165189251Ssam
166189251Ssam
167189251Ssamint wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type)
168189251Ssam{
169189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * Message Type (%d)", msg_type);
170189251Ssam	wpabuf_put_be16(msg, ATTR_MSG_TYPE);
171189251Ssam	wpabuf_put_be16(msg, 1);
172189251Ssam	wpabuf_put_u8(msg, msg_type);
173189251Ssam	return 0;
174189251Ssam}
175189251Ssam
176189251Ssam
177189251Ssamint wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg)
178189251Ssam{
179189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * Enrollee Nonce");
180189251Ssam	wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE);
181189251Ssam	wpabuf_put_be16(msg, WPS_NONCE_LEN);
182189251Ssam	wpabuf_put_data(msg, wps->nonce_e, WPS_NONCE_LEN);
183189251Ssam	return 0;
184189251Ssam}
185189251Ssam
186189251Ssam
187189251Ssamint wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg)
188189251Ssam{
189189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * Registrar Nonce");
190189251Ssam	wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
191189251Ssam	wpabuf_put_be16(msg, WPS_NONCE_LEN);
192189251Ssam	wpabuf_put_data(msg, wps->nonce_r, WPS_NONCE_LEN);
193189251Ssam	return 0;
194189251Ssam}
195189251Ssam
196189251Ssam
197189251Ssamint wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg)
198189251Ssam{
199189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type Flags");
200189251Ssam	wpabuf_put_be16(msg, ATTR_AUTH_TYPE_FLAGS);
201189251Ssam	wpabuf_put_be16(msg, 2);
202189251Ssam	wpabuf_put_be16(msg, WPS_AUTH_TYPES);
203189251Ssam	return 0;
204189251Ssam}
205189251Ssam
206189251Ssam
207189251Ssamint wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg)
208189251Ssam{
209189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type Flags");
210189251Ssam	wpabuf_put_be16(msg, ATTR_ENCR_TYPE_FLAGS);
211189251Ssam	wpabuf_put_be16(msg, 2);
212189251Ssam	wpabuf_put_be16(msg, WPS_ENCR_TYPES);
213189251Ssam	return 0;
214189251Ssam}
215189251Ssam
216189251Ssam
217189251Ssamint wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg)
218189251Ssam{
219189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * Connection Type Flags");
220189251Ssam	wpabuf_put_be16(msg, ATTR_CONN_TYPE_FLAGS);
221189251Ssam	wpabuf_put_be16(msg, 1);
222189251Ssam	wpabuf_put_u8(msg, WPS_CONN_ESS);
223189251Ssam	return 0;
224189251Ssam}
225189251Ssam
226189251Ssam
227189251Ssamint wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg)
228189251Ssam{
229189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * Association State");
230189251Ssam	wpabuf_put_be16(msg, ATTR_ASSOC_STATE);
231189251Ssam	wpabuf_put_be16(msg, 2);
232189251Ssam	wpabuf_put_be16(msg, WPS_ASSOC_NOT_ASSOC);
233189251Ssam	return 0;
234189251Ssam}
235189251Ssam
236189251Ssam
237189251Ssamint wps_build_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg)
238189251Ssam{
239189251Ssam	u8 hash[SHA256_MAC_LEN];
240189251Ssam
241189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * Key Wrap Authenticator");
242189251Ssam	hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, wpabuf_head(msg),
243189251Ssam		    wpabuf_len(msg), hash);
244189251Ssam
245189251Ssam	wpabuf_put_be16(msg, ATTR_KEY_WRAP_AUTH);
246189251Ssam	wpabuf_put_be16(msg, WPS_KWA_LEN);
247189251Ssam	wpabuf_put_data(msg, hash, WPS_KWA_LEN);
248189251Ssam	return 0;
249189251Ssam}
250189251Ssam
251189251Ssam
252189251Ssamint wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
253189251Ssam			    struct wpabuf *plain)
254189251Ssam{
255189251Ssam	size_t pad_len;
256189251Ssam	const size_t block_size = 16;
257189251Ssam	u8 *iv, *data;
258189251Ssam
259189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * Encrypted Settings");
260189251Ssam
261189251Ssam	/* PKCS#5 v2.0 pad */
262189251Ssam	pad_len = block_size - wpabuf_len(plain) % block_size;
263189251Ssam	os_memset(wpabuf_put(plain, pad_len), pad_len, pad_len);
264189251Ssam
265189251Ssam	wpabuf_put_be16(msg, ATTR_ENCR_SETTINGS);
266189251Ssam	wpabuf_put_be16(msg, block_size + wpabuf_len(plain));
267189251Ssam
268189251Ssam	iv = wpabuf_put(msg, block_size);
269189251Ssam	if (os_get_random(iv, block_size) < 0)
270189251Ssam		return -1;
271189251Ssam
272189251Ssam	data = wpabuf_put(msg, 0);
273189251Ssam	wpabuf_put_buf(msg, plain);
274189251Ssam	if (aes_128_cbc_encrypt(wps->keywrapkey, iv, data, wpabuf_len(plain)))
275189251Ssam		return -1;
276189251Ssam
277189251Ssam	return 0;
278189251Ssam}
279214734Srpaulo
280214734Srpaulo
281214734Srpaulo#ifdef CONFIG_WPS_OOB
282214734Srpauloint wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps)
283214734Srpaulo{
284214734Srpaulo	size_t hash_len;
285214734Srpaulo	const u8 *addr[1];
286214734Srpaulo	u8 pubkey_hash[WPS_HASH_LEN];
287214734Srpaulo	u8 dev_password_bin[WPS_OOB_DEVICE_PASSWORD_LEN];
288214734Srpaulo
289214734Srpaulo	wpa_printf(MSG_DEBUG, "WPS:  * OOB Device Password");
290214734Srpaulo
291214734Srpaulo	addr[0] = wpabuf_head(wps->dh_pubkey);
292214734Srpaulo	hash_len = wpabuf_len(wps->dh_pubkey);
293214734Srpaulo	sha256_vector(1, addr, &hash_len, pubkey_hash);
294214734Srpaulo
295214734Srpaulo	if (os_get_random((u8 *) &wps->oob_dev_pw_id, sizeof(u16)) < 0) {
296214734Srpaulo		wpa_printf(MSG_ERROR, "WPS: device password id "
297214734Srpaulo			   "generation error");
298214734Srpaulo		return -1;
299214734Srpaulo	}
300214734Srpaulo	wps->oob_dev_pw_id |= 0x0010;
301214734Srpaulo
302214734Srpaulo	if (os_get_random(dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN) < 0) {
303214734Srpaulo		wpa_printf(MSG_ERROR, "WPS: OOB device password "
304214734Srpaulo			   "generation error");
305214734Srpaulo		return -1;
306214734Srpaulo	}
307214734Srpaulo
308214734Srpaulo	wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD);
309214734Srpaulo	wpabuf_put_be16(msg, WPS_OOB_DEVICE_PASSWORD_ATTR_LEN);
310214734Srpaulo	wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
311214734Srpaulo	wpabuf_put_be16(msg, wps->oob_dev_pw_id);
312214734Srpaulo	wpabuf_put_data(msg, dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN);
313214734Srpaulo
314214734Srpaulo	wpa_snprintf_hex_uppercase(
315214734Srpaulo		wpabuf_put(wps->oob_conf.dev_password,
316214734Srpaulo			   wpabuf_size(wps->oob_conf.dev_password)),
317214734Srpaulo		wpabuf_size(wps->oob_conf.dev_password),
318214734Srpaulo		dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN);
319214734Srpaulo
320214734Srpaulo	return 0;
321214734Srpaulo}
322214734Srpaulo#endif /* CONFIG_WPS_OOB */
323