1e5b75505Sopenharmony_ci/*
2e5b75505Sopenharmony_ci * NDEF(NFC Data Exchange Format) routines for Wi-Fi Protected Setup
3e5b75505Sopenharmony_ci *   Reference is "NFCForum-TS-NDEF_1.0 2006-07-24".
4e5b75505Sopenharmony_ci * Copyright (c) 2009-2012, Masashi Honma <masashi.honma@gmail.com>
5e5b75505Sopenharmony_ci *
6e5b75505Sopenharmony_ci * This software may be distributed under the terms of the BSD license.
7e5b75505Sopenharmony_ci * See README for more details.
8e5b75505Sopenharmony_ci */
9e5b75505Sopenharmony_ci
10e5b75505Sopenharmony_ci#include "includes.h"
11e5b75505Sopenharmony_ci#include "common.h"
12e5b75505Sopenharmony_ci#include "wps/wps.h"
13e5b75505Sopenharmony_ci
14e5b75505Sopenharmony_ci#define FLAG_MESSAGE_BEGIN (1 << 7)
15e5b75505Sopenharmony_ci#define FLAG_MESSAGE_END (1 << 6)
16e5b75505Sopenharmony_ci#define FLAG_CHUNK (1 << 5)
17e5b75505Sopenharmony_ci#define FLAG_SHORT_RECORD (1 << 4)
18e5b75505Sopenharmony_ci#define FLAG_ID_LENGTH_PRESENT (1 << 3)
19e5b75505Sopenharmony_ci#define FLAG_TNF_NFC_FORUM (0x01)
20e5b75505Sopenharmony_ci#define FLAG_TNF_RFC2046 (0x02)
21e5b75505Sopenharmony_ci
22e5b75505Sopenharmony_cistruct ndef_record {
23e5b75505Sopenharmony_ci	const u8 *type;
24e5b75505Sopenharmony_ci	const u8 *id;
25e5b75505Sopenharmony_ci	const u8 *payload;
26e5b75505Sopenharmony_ci	u8 type_length;
27e5b75505Sopenharmony_ci	u8 id_length;
28e5b75505Sopenharmony_ci	u32 payload_length;
29e5b75505Sopenharmony_ci	u32 total_length;
30e5b75505Sopenharmony_ci};
31e5b75505Sopenharmony_ci
32e5b75505Sopenharmony_cistatic const char wifi_handover_type[] = "application/vnd.wfa.wsc";
33e5b75505Sopenharmony_cistatic const char p2p_handover_type[] = "application/vnd.wfa.p2p";
34e5b75505Sopenharmony_ci
35e5b75505Sopenharmony_cistatic int ndef_parse_record(const u8 *data, u32 size,
36e5b75505Sopenharmony_ci			     struct ndef_record *record)
37e5b75505Sopenharmony_ci{
38e5b75505Sopenharmony_ci	const u8 *pos = data + 1;
39e5b75505Sopenharmony_ci
40e5b75505Sopenharmony_ci	if (size < 2)
41e5b75505Sopenharmony_ci		return -1;
42e5b75505Sopenharmony_ci	record->type_length = *pos++;
43e5b75505Sopenharmony_ci	if (data[0] & FLAG_SHORT_RECORD) {
44e5b75505Sopenharmony_ci		if (size < 3)
45e5b75505Sopenharmony_ci			return -1;
46e5b75505Sopenharmony_ci		record->payload_length = *pos++;
47e5b75505Sopenharmony_ci	} else {
48e5b75505Sopenharmony_ci		u32 len;
49e5b75505Sopenharmony_ci
50e5b75505Sopenharmony_ci		if (size < 6)
51e5b75505Sopenharmony_ci			return -1;
52e5b75505Sopenharmony_ci		len = WPA_GET_BE32(pos);
53e5b75505Sopenharmony_ci		if (len > size - 6 || len > 20000)
54e5b75505Sopenharmony_ci			return -1;
55e5b75505Sopenharmony_ci		record->payload_length = len;
56e5b75505Sopenharmony_ci		pos += sizeof(u32);
57e5b75505Sopenharmony_ci	}
58e5b75505Sopenharmony_ci
59e5b75505Sopenharmony_ci	if (data[0] & FLAG_ID_LENGTH_PRESENT) {
60e5b75505Sopenharmony_ci		if ((int) size < pos - data + 1)
61e5b75505Sopenharmony_ci			return -1;
62e5b75505Sopenharmony_ci		record->id_length = *pos++;
63e5b75505Sopenharmony_ci	} else
64e5b75505Sopenharmony_ci		record->id_length = 0;
65e5b75505Sopenharmony_ci
66e5b75505Sopenharmony_ci	record->type = record->type_length == 0 ? NULL : pos;
67e5b75505Sopenharmony_ci	pos += record->type_length;
68e5b75505Sopenharmony_ci
69e5b75505Sopenharmony_ci	record->id = record->id_length == 0 ? NULL : pos;
70e5b75505Sopenharmony_ci	pos += record->id_length;
71e5b75505Sopenharmony_ci
72e5b75505Sopenharmony_ci	record->payload = record->payload_length == 0 ? NULL : pos;
73e5b75505Sopenharmony_ci	pos += record->payload_length;
74e5b75505Sopenharmony_ci
75e5b75505Sopenharmony_ci	record->total_length = pos - data;
76e5b75505Sopenharmony_ci	if (record->total_length > size ||
77e5b75505Sopenharmony_ci	    record->total_length < record->payload_length)
78e5b75505Sopenharmony_ci		return -1;
79e5b75505Sopenharmony_ci	return 0;
80e5b75505Sopenharmony_ci}
81e5b75505Sopenharmony_ci
82e5b75505Sopenharmony_ci
83e5b75505Sopenharmony_cistatic struct wpabuf * ndef_parse_records(const struct wpabuf *buf,
84e5b75505Sopenharmony_ci					  int (*filter)(struct ndef_record *))
85e5b75505Sopenharmony_ci{
86e5b75505Sopenharmony_ci	struct ndef_record record;
87e5b75505Sopenharmony_ci	int len = wpabuf_len(buf);
88e5b75505Sopenharmony_ci	const u8 *data = wpabuf_head(buf);
89e5b75505Sopenharmony_ci
90e5b75505Sopenharmony_ci	while (len > 0) {
91e5b75505Sopenharmony_ci		if (ndef_parse_record(data, len, &record) < 0) {
92e5b75505Sopenharmony_ci			wpa_printf(MSG_ERROR, "NDEF : Failed to parse");
93e5b75505Sopenharmony_ci			return NULL;
94e5b75505Sopenharmony_ci		}
95e5b75505Sopenharmony_ci		if (filter == NULL || filter(&record))
96e5b75505Sopenharmony_ci			return wpabuf_alloc_copy(record.payload,
97e5b75505Sopenharmony_ci						 record.payload_length);
98e5b75505Sopenharmony_ci		data += record.total_length;
99e5b75505Sopenharmony_ci		len -= record.total_length;
100e5b75505Sopenharmony_ci	}
101e5b75505Sopenharmony_ci	wpa_printf(MSG_ERROR, "NDEF : Record not found");
102e5b75505Sopenharmony_ci	return NULL;
103e5b75505Sopenharmony_ci}
104e5b75505Sopenharmony_ci
105e5b75505Sopenharmony_ci
106e5b75505Sopenharmony_cistatic struct wpabuf * ndef_build_record(u8 flags, const void *type,
107e5b75505Sopenharmony_ci					 u8 type_length, void *id,
108e5b75505Sopenharmony_ci					 u8 id_length,
109e5b75505Sopenharmony_ci					 const struct wpabuf *payload)
110e5b75505Sopenharmony_ci{
111e5b75505Sopenharmony_ci	struct wpabuf *record;
112e5b75505Sopenharmony_ci	size_t total_len;
113e5b75505Sopenharmony_ci	int short_record;
114e5b75505Sopenharmony_ci	u8 local_flag;
115e5b75505Sopenharmony_ci	size_t payload_length = wpabuf_len(payload);
116e5b75505Sopenharmony_ci
117e5b75505Sopenharmony_ci	short_record = payload_length < 256 ? 1 : 0;
118e5b75505Sopenharmony_ci
119e5b75505Sopenharmony_ci	total_len = 2; /* flag + type length */
120e5b75505Sopenharmony_ci	/* payload length */
121e5b75505Sopenharmony_ci	total_len += short_record ? sizeof(u8) : sizeof(u32);
122e5b75505Sopenharmony_ci	if (id_length > 0)
123e5b75505Sopenharmony_ci		total_len += 1;
124e5b75505Sopenharmony_ci	total_len += type_length + id_length + payload_length;
125e5b75505Sopenharmony_ci	record = wpabuf_alloc(total_len);
126e5b75505Sopenharmony_ci	if (record == NULL) {
127e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "NDEF : Failed to allocate "
128e5b75505Sopenharmony_ci			   "record for build");
129e5b75505Sopenharmony_ci		return NULL;
130e5b75505Sopenharmony_ci	}
131e5b75505Sopenharmony_ci
132e5b75505Sopenharmony_ci	local_flag = flags;
133e5b75505Sopenharmony_ci	if (id_length > 0)
134e5b75505Sopenharmony_ci		local_flag |= FLAG_ID_LENGTH_PRESENT;
135e5b75505Sopenharmony_ci	if (short_record)
136e5b75505Sopenharmony_ci		local_flag |= FLAG_SHORT_RECORD;
137e5b75505Sopenharmony_ci	wpabuf_put_u8(record, local_flag);
138e5b75505Sopenharmony_ci
139e5b75505Sopenharmony_ci	wpabuf_put_u8(record, type_length);
140e5b75505Sopenharmony_ci
141e5b75505Sopenharmony_ci	if (short_record)
142e5b75505Sopenharmony_ci		wpabuf_put_u8(record, payload_length);
143e5b75505Sopenharmony_ci	else
144e5b75505Sopenharmony_ci		wpabuf_put_be32(record, payload_length);
145e5b75505Sopenharmony_ci
146e5b75505Sopenharmony_ci	if (id_length > 0)
147e5b75505Sopenharmony_ci		wpabuf_put_u8(record, id_length);
148e5b75505Sopenharmony_ci	wpabuf_put_data(record, type, type_length);
149e5b75505Sopenharmony_ci	wpabuf_put_data(record, id, id_length);
150e5b75505Sopenharmony_ci	wpabuf_put_buf(record, payload);
151e5b75505Sopenharmony_ci	return record;
152e5b75505Sopenharmony_ci}
153e5b75505Sopenharmony_ci
154e5b75505Sopenharmony_ci
155e5b75505Sopenharmony_cistatic int wifi_filter(struct ndef_record *record)
156e5b75505Sopenharmony_ci{
157e5b75505Sopenharmony_ci	if (record->type == NULL ||
158e5b75505Sopenharmony_ci	    record->type_length != os_strlen(wifi_handover_type))
159e5b75505Sopenharmony_ci		return 0;
160e5b75505Sopenharmony_ci	if (os_memcmp(record->type, wifi_handover_type,
161e5b75505Sopenharmony_ci		      os_strlen(wifi_handover_type)) != 0)
162e5b75505Sopenharmony_ci		return 0;
163e5b75505Sopenharmony_ci	return 1;
164e5b75505Sopenharmony_ci}
165e5b75505Sopenharmony_ci
166e5b75505Sopenharmony_ci
167e5b75505Sopenharmony_cistruct wpabuf * ndef_parse_wifi(const struct wpabuf *buf)
168e5b75505Sopenharmony_ci{
169e5b75505Sopenharmony_ci	return ndef_parse_records(buf, wifi_filter);
170e5b75505Sopenharmony_ci}
171e5b75505Sopenharmony_ci
172e5b75505Sopenharmony_ci
173e5b75505Sopenharmony_cistruct wpabuf * ndef_build_wifi(const struct wpabuf *buf)
174e5b75505Sopenharmony_ci{
175e5b75505Sopenharmony_ci	return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END |
176e5b75505Sopenharmony_ci				 FLAG_TNF_RFC2046, wifi_handover_type,
177e5b75505Sopenharmony_ci				 os_strlen(wifi_handover_type), NULL, 0, buf);
178e5b75505Sopenharmony_ci}
179e5b75505Sopenharmony_ci
180e5b75505Sopenharmony_ci
181e5b75505Sopenharmony_cistatic int p2p_filter(struct ndef_record *record)
182e5b75505Sopenharmony_ci{
183e5b75505Sopenharmony_ci	if (record->type == NULL ||
184e5b75505Sopenharmony_ci	    record->type_length != os_strlen(p2p_handover_type))
185e5b75505Sopenharmony_ci		return 0;
186e5b75505Sopenharmony_ci	if (os_memcmp(record->type, p2p_handover_type,
187e5b75505Sopenharmony_ci		      os_strlen(p2p_handover_type)) != 0)
188e5b75505Sopenharmony_ci		return 0;
189e5b75505Sopenharmony_ci	return 1;
190e5b75505Sopenharmony_ci}
191e5b75505Sopenharmony_ci
192e5b75505Sopenharmony_ci
193e5b75505Sopenharmony_cistruct wpabuf * ndef_parse_p2p(const struct wpabuf *buf)
194e5b75505Sopenharmony_ci{
195e5b75505Sopenharmony_ci	return ndef_parse_records(buf, p2p_filter);
196e5b75505Sopenharmony_ci}
197e5b75505Sopenharmony_ci
198e5b75505Sopenharmony_ci
199e5b75505Sopenharmony_cistruct wpabuf * ndef_build_p2p(const struct wpabuf *buf)
200e5b75505Sopenharmony_ci{
201e5b75505Sopenharmony_ci	return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END |
202e5b75505Sopenharmony_ci				 FLAG_TNF_RFC2046, p2p_handover_type,
203e5b75505Sopenharmony_ci				 os_strlen(p2p_handover_type), NULL, 0, buf);
204e5b75505Sopenharmony_ci}
205