18d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/*
28d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * NDEF(NFC Data Exchange Format) routines for Wi-Fi Protected Setup
38d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *   Reference is "NFCForum-TS-NDEF_1.0 2006-07-24".
404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt * Copyright (c) 2009-2012, Masashi Honma <masashi.honma@gmail.com>
58d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *
604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt * This software may be distributed under the terms of the BSD license.
704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt * See README for more details.
88d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */
98d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "includes.h"
118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "common.h"
128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "wps/wps.h"
138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#define FLAG_MESSAGE_BEGIN (1 << 7)
158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#define FLAG_MESSAGE_END (1 << 6)
168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#define FLAG_CHUNK (1 << 5)
178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#define FLAG_SHORT_RECORD (1 << 4)
188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#define FLAG_ID_LENGTH_PRESENT (1 << 3)
19d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt#define FLAG_TNF_NFC_FORUM (0x01)
208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#define FLAG_TNF_RFC2046 (0x02)
218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstruct ndef_record {
2304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	const u8 *type;
2404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	const u8 *id;
2504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	const u8 *payload;
268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	u8 type_length;
278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	u8 id_length;
288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	u32 payload_length;
298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	u32 total_length;
308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt};
318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
321d755d025b206e22b06aeb322e25a79f98ca7777Dmitry Shmidtstatic const char wifi_handover_type[] = "application/vnd.wfa.wsc";
331d755d025b206e22b06aeb322e25a79f98ca7777Dmitry Shmidtstatic const char p2p_handover_type[] = "application/vnd.wfa.p2p";
348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
3504949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic int ndef_parse_record(const u8 *data, u32 size,
3604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			     struct ndef_record *record)
378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
3804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	const u8 *pos = data + 1;
398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (size < 2)
418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return -1;
428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	record->type_length = *pos++;
438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (data[0] & FLAG_SHORT_RECORD) {
448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (size < 3)
458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			return -1;
468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		record->payload_length = *pos++;
478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	} else {
488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (size < 6)
498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			return -1;
508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		record->payload_length = ntohl(*(u32 *)pos);
518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		pos += sizeof(u32);
528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (data[0] & FLAG_ID_LENGTH_PRESENT) {
558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if ((int) size < pos - data + 1)
568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			return -1;
578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		record->id_length = *pos++;
588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	} else
598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		record->id_length = 0;
608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	record->type = record->type_length == 0 ? NULL : pos;
628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	pos += record->type_length;
638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	record->id = record->id_length == 0 ? NULL : pos;
658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	pos += record->id_length;
668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	record->payload = record->payload_length == 0 ? NULL : pos;
688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	pos += record->payload_length;
698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	record->total_length = pos - data;
718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (record->total_length > size)
728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return -1;
738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return 0;
748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7704949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic struct wpabuf * ndef_parse_records(const struct wpabuf *buf,
788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					  int (*filter)(struct ndef_record *))
798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	struct ndef_record record;
818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int len = wpabuf_len(buf);
8204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	const u8 *data = wpabuf_head(buf);
838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	while (len > 0) {
858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (ndef_parse_record(data, len, &record) < 0) {
868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			wpa_printf(MSG_ERROR, "NDEF : Failed to parse");
878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			return NULL;
888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		}
898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (filter == NULL || filter(&record))
908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			return wpabuf_alloc_copy(record.payload,
918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt						 record.payload_length);
928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		data += record.total_length;
938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		len -= record.total_length;
948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	wpa_printf(MSG_ERROR, "NDEF : Record not found");
968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return NULL;
978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1001d755d025b206e22b06aeb322e25a79f98ca7777Dmitry Shmidtstatic struct wpabuf * ndef_build_record(u8 flags, const void *type,
1018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					 u8 type_length, void *id,
10204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					 u8 id_length,
10304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					 const struct wpabuf *payload)
1048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
1058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	struct wpabuf *record;
1068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	size_t total_len;
1078d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int short_record;
1088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	u8 local_flag;
10904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	size_t payload_length = wpabuf_len(payload);
1108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	short_record = payload_length < 256 ? 1 : 0;
1128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	total_len = 2; /* flag + type length */
1148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	/* payload length */
1158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	total_len += short_record ? sizeof(u8) : sizeof(u32);
1168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (id_length > 0)
1178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		total_len += 1;
1188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	total_len += type_length + id_length + payload_length;
1198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	record = wpabuf_alloc(total_len);
1208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (record == NULL) {
1218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpa_printf(MSG_ERROR, "NDEF : Failed to allocate "
1228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			   "record for build");
1238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return NULL;
1248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
1258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	local_flag = flags;
1278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (id_length > 0)
1288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		local_flag |= FLAG_ID_LENGTH_PRESENT;
1298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (short_record)
1308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		local_flag |= FLAG_SHORT_RECORD;
1318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	wpabuf_put_u8(record, local_flag);
1328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	wpabuf_put_u8(record, type_length);
1348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (short_record)
1368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpabuf_put_u8(record, payload_length);
1378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	else
1388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpabuf_put_be32(record, payload_length);
1398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (id_length > 0)
1418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpabuf_put_u8(record, id_length);
1428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	wpabuf_put_data(record, type, type_length);
1438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	wpabuf_put_data(record, id, id_length);
14404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpabuf_put_buf(record, payload);
1458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return record;
1468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
1478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic int wifi_filter(struct ndef_record *record)
1508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
1517d5c8f257a74ac0d12828962a492e8b84ef83923Dmitry Shmidt	if (record->type == NULL ||
1527d5c8f257a74ac0d12828962a492e8b84ef83923Dmitry Shmidt	    record->type_length != os_strlen(wifi_handover_type))
1538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return 0;
1548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (os_memcmp(record->type, wifi_handover_type,
1558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		      os_strlen(wifi_handover_type)) != 0)
1568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return 0;
1578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return 1;
1588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
1598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
16104949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstruct wpabuf * ndef_parse_wifi(const struct wpabuf *buf)
1628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
1638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return ndef_parse_records(buf, wifi_filter);
1648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
1658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
16704949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstruct wpabuf * ndef_build_wifi(const struct wpabuf *buf)
1688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
1698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END |
1708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				 FLAG_TNF_RFC2046, wifi_handover_type,
17104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				 os_strlen(wifi_handover_type), NULL, 0, buf);
1728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
173d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt
174d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt
175cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidtstatic int p2p_filter(struct ndef_record *record)
176f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt{
1777d5c8f257a74ac0d12828962a492e8b84ef83923Dmitry Shmidt	if (record->type == NULL ||
1787d5c8f257a74ac0d12828962a492e8b84ef83923Dmitry Shmidt	    record->type_length != os_strlen(p2p_handover_type))
179cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt		return 0;
180cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt	if (os_memcmp(record->type, p2p_handover_type,
181cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt		      os_strlen(p2p_handover_type)) != 0)
182cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt		return 0;
183cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt	return 1;
184f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt}
185f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt
186f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt
187cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidtstruct wpabuf * ndef_parse_p2p(const struct wpabuf *buf)
188d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt{
189cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt	return ndef_parse_records(buf, p2p_filter);
190cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt}
191d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt
192d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt
193cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidtstruct wpabuf * ndef_build_p2p(const struct wpabuf *buf)
194cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt{
195cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt	return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END |
196cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt				 FLAG_TNF_RFC2046, p2p_handover_type,
197cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt				 os_strlen(p2p_handover_type), NULL, 0, buf);
198d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt}
199