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#include "wps/wps_i.h"
148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#define FLAG_MESSAGE_BEGIN (1 << 7)
168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#define FLAG_MESSAGE_END (1 << 6)
178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#define FLAG_CHUNK (1 << 5)
188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#define FLAG_SHORT_RECORD (1 << 4)
198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#define FLAG_ID_LENGTH_PRESENT (1 << 3)
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
328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic char wifi_handover_type[] = "application/vnd.wfa.wsc";
338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
3404949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic int ndef_parse_record(const u8 *data, u32 size,
3504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			     struct ndef_record *record)
368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
3704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	const u8 *pos = data + 1;
388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (size < 2)
408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return -1;
418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	record->type_length = *pos++;
428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (data[0] & FLAG_SHORT_RECORD) {
438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (size < 3)
448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			return -1;
458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		record->payload_length = *pos++;
468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	} else {
478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (size < 6)
488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			return -1;
498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		record->payload_length = ntohl(*(u32 *)pos);
508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		pos += sizeof(u32);
518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (data[0] & FLAG_ID_LENGTH_PRESENT) {
548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if ((int) size < pos - data + 1)
558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			return -1;
568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		record->id_length = *pos++;
578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	} else
588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		record->id_length = 0;
598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	record->type = record->type_length == 0 ? NULL : pos;
618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	pos += record->type_length;
628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	record->id = record->id_length == 0 ? NULL : pos;
648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	pos += record->id_length;
658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	record->payload = record->payload_length == 0 ? NULL : pos;
678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	pos += record->payload_length;
688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	record->total_length = pos - data;
708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (record->total_length > size)
718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return -1;
728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return 0;
738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7604949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic struct wpabuf * ndef_parse_records(const struct wpabuf *buf,
778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					  int (*filter)(struct ndef_record *))
788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	struct ndef_record record;
808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int len = wpabuf_len(buf);
8104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	const u8 *data = wpabuf_head(buf);
828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	while (len > 0) {
848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (ndef_parse_record(data, len, &record) < 0) {
858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			wpa_printf(MSG_ERROR, "NDEF : Failed to parse");
868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			return NULL;
878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		}
888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (filter == NULL || filter(&record))
898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			return wpabuf_alloc_copy(record.payload,
908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt						 record.payload_length);
918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		data += record.total_length;
928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		len -= record.total_length;
938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	wpa_printf(MSG_ERROR, "NDEF : Record not found");
958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return NULL;
968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic struct wpabuf * ndef_build_record(u8 flags, void *type,
1008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					 u8 type_length, void *id,
10104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					 u8 id_length,
10204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					 const struct wpabuf *payload)
1038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
1048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	struct wpabuf *record;
1058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	size_t total_len;
1068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int short_record;
1078d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	u8 local_flag;
10804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	size_t payload_length = wpabuf_len(payload);
1098d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	short_record = payload_length < 256 ? 1 : 0;
1118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	total_len = 2; /* flag + type length */
1138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	/* payload length */
1148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	total_len += short_record ? sizeof(u8) : sizeof(u32);
1158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (id_length > 0)
1168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		total_len += 1;
1178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	total_len += type_length + id_length + payload_length;
1188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	record = wpabuf_alloc(total_len);
1198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (record == NULL) {
1208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpa_printf(MSG_ERROR, "NDEF : Failed to allocate "
1218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			   "record for build");
1228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return NULL;
1238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
1248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	local_flag = flags;
1268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (id_length > 0)
1278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		local_flag |= FLAG_ID_LENGTH_PRESENT;
1288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (short_record)
1298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		local_flag |= FLAG_SHORT_RECORD;
1308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	wpabuf_put_u8(record, local_flag);
1318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	wpabuf_put_u8(record, type_length);
1338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (short_record)
1358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpabuf_put_u8(record, payload_length);
1368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	else
1378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpabuf_put_be32(record, payload_length);
1388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (id_length > 0)
1408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpabuf_put_u8(record, id_length);
1418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	wpabuf_put_data(record, type, type_length);
1428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	wpabuf_put_data(record, id, id_length);
14304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpabuf_put_buf(record, payload);
1448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return record;
1458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
1468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic int wifi_filter(struct ndef_record *record)
1498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
1508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (record->type_length != os_strlen(wifi_handover_type))
1518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return 0;
1528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (os_memcmp(record->type, wifi_handover_type,
1538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		      os_strlen(wifi_handover_type)) != 0)
1548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return 0;
1558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return 1;
1568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
1578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
15904949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstruct wpabuf * ndef_parse_wifi(const struct wpabuf *buf)
1608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
1618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return ndef_parse_records(buf, wifi_filter);
1628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
1638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
16504949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstruct wpabuf * ndef_build_wifi(const struct wpabuf *buf)
1668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
1678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END |
1688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				 FLAG_TNF_RFC2046, wifi_handover_type,
16904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				 os_strlen(wifi_handover_type), NULL, 0, buf);
1708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
171