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".
48d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp>
58d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *
68d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * This program is free software; you can redistribute it and/or modify
78d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * it under the terms of the GNU General Public License version 2 as
88d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * published by the Free Software Foundation.
98d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *
108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Alternatively, this software may be distributed under the terms of BSD
118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * license.
128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *
138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * See README and COPYING for more details.
148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */
158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "includes.h"
178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "common.h"
188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "wps/wps.h"
198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "wps/wps_i.h"
208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#define FLAG_MESSAGE_BEGIN (1 << 7)
228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#define FLAG_MESSAGE_END (1 << 6)
238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#define FLAG_CHUNK (1 << 5)
248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#define FLAG_SHORT_RECORD (1 << 4)
258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#define FLAG_ID_LENGTH_PRESENT (1 << 3)
268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#define FLAG_TNF_RFC2046 (0x02)
278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstruct ndef_record {
298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	u8 *type;
308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	u8 *id;
318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	u8 *payload;
328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	u8 type_length;
338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	u8 id_length;
348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	u32 payload_length;
358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	u32 total_length;
368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt};
378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic char wifi_handover_type[] = "application/vnd.wfa.wsc";
398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic int ndef_parse_record(u8 *data, u32 size, struct ndef_record *record)
418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	u8 *pos = data + 1;
438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (size < 2)
458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return -1;
468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	record->type_length = *pos++;
478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (data[0] & FLAG_SHORT_RECORD) {
488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (size < 3)
498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			return -1;
508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		record->payload_length = *pos++;
518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	} else {
528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (size < 6)
538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			return -1;
548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		record->payload_length = ntohl(*(u32 *)pos);
558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		pos += sizeof(u32);
568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (data[0] & FLAG_ID_LENGTH_PRESENT) {
598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if ((int) size < pos - data + 1)
608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			return -1;
618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		record->id_length = *pos++;
628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	} else
638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		record->id_length = 0;
648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	record->type = record->type_length == 0 ? NULL : pos;
668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	pos += record->type_length;
678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	record->id = record->id_length == 0 ? NULL : pos;
698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	pos += record->id_length;
708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	record->payload = record->payload_length == 0 ? NULL : pos;
728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	pos += record->payload_length;
738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	record->total_length = pos - data;
758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (record->total_length > size)
768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return -1;
778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return 0;
788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic struct wpabuf * ndef_parse_records(struct wpabuf *buf,
828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					  int (*filter)(struct ndef_record *))
838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	struct ndef_record record;
858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int len = wpabuf_len(buf);
868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	u8 *data = wpabuf_mhead(buf);
878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	while (len > 0) {
898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (ndef_parse_record(data, len, &record) < 0) {
908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			wpa_printf(MSG_ERROR, "NDEF : Failed to parse");
918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			return NULL;
928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		}
938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (filter == NULL || filter(&record))
948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			return wpabuf_alloc_copy(record.payload,
958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt						 record.payload_length);
968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		data += record.total_length;
978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		len -= record.total_length;
988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	wpa_printf(MSG_ERROR, "NDEF : Record not found");
1008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return NULL;
1018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
1028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic struct wpabuf * ndef_build_record(u8 flags, void *type,
1058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					 u8 type_length, void *id,
1068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					 u8 id_length, void *payload,
1078d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					 u32 payload_length)
1088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
1098d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	struct wpabuf *record;
1108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	size_t total_len;
1118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int short_record;
1128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	u8 local_flag;
1138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	short_record = payload_length < 256 ? 1 : 0;
1158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	total_len = 2; /* flag + type length */
1178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	/* payload length */
1188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	total_len += short_record ? sizeof(u8) : sizeof(u32);
1198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (id_length > 0)
1208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		total_len += 1;
1218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	total_len += type_length + id_length + payload_length;
1228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	record = wpabuf_alloc(total_len);
1238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (record == NULL) {
1248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpa_printf(MSG_ERROR, "NDEF : Failed to allocate "
1258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			   "record for build");
1268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return NULL;
1278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
1288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	local_flag = flags;
1308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (id_length > 0)
1318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		local_flag |= FLAG_ID_LENGTH_PRESENT;
1328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (short_record)
1338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		local_flag |= FLAG_SHORT_RECORD;
1348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	wpabuf_put_u8(record, local_flag);
1358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	wpabuf_put_u8(record, type_length);
1378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (short_record)
1398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpabuf_put_u8(record, payload_length);
1408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	else
1418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpabuf_put_be32(record, payload_length);
1428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (id_length > 0)
1448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpabuf_put_u8(record, id_length);
1458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	wpabuf_put_data(record, type, type_length);
1468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	wpabuf_put_data(record, id, id_length);
1478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	wpabuf_put_data(record, payload, payload_length);
1488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return record;
1498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
1508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic int wifi_filter(struct ndef_record *record)
1538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
1548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (record->type_length != os_strlen(wifi_handover_type))
1558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return 0;
1568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (os_memcmp(record->type, wifi_handover_type,
1578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		      os_strlen(wifi_handover_type)) != 0)
1588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return 0;
1598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return 1;
1608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
1618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstruct wpabuf * ndef_parse_wifi(struct wpabuf *buf)
1648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
1658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return ndef_parse_records(buf, wifi_filter);
1668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
1678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstruct wpabuf * ndef_build_wifi(struct wpabuf *buf)
1708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
1718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END |
1728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				 FLAG_TNF_RFC2046, wifi_handover_type,
1738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				 os_strlen(wifi_handover_type), NULL, 0,
1748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				 wpabuf_mhead(buf), wpabuf_len(buf));
1758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
176