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
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}
171d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt
172d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt
173f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidtstruct wpabuf * ndef_build_wifi_hc(int begin)
174f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt{
175f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt	struct wpabuf *hc, *carrier;
176f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt
177f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt	carrier = wpabuf_alloc(2 + os_strlen(wifi_handover_type));
178f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt	if (carrier == NULL)
179f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt		return NULL;
180f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt	wpabuf_put_u8(carrier, 0x02); /* Carrier Type Format */
181f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt	wpabuf_put_u8(carrier, os_strlen(wifi_handover_type));
182f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt	wpabuf_put_str(carrier, wifi_handover_type);
183f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt
184f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt	hc = ndef_build_record((begin ? FLAG_MESSAGE_BEGIN : 0) |
185f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt			       FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "Hc", 2,
186f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt			       "0", 1, carrier);
187f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt	wpabuf_free(carrier);
188f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt
189f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt	return hc;
190f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt}
191f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt
192f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt
193d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidtstruct wpabuf * ndef_build_wifi_hr(void)
194d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt{
195d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	struct wpabuf *rn, *cr, *ac_payload, *ac, *hr_payload, *hr;
196f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt	struct wpabuf *hc;
197d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt
198d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	rn = wpabuf_alloc(2);
199d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	if (rn == NULL)
200d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt		return NULL;
201d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	wpabuf_put_be16(rn, os_random() & 0xffff);
202d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt
203d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	cr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "cr", 2,
204d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt			       NULL, 0, rn);
205d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	wpabuf_free(rn);
206d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt
207d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	if (cr == NULL)
208d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt		return NULL;
209d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt
210d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	ac_payload = wpabuf_alloc(4);
211d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	if (ac_payload == NULL) {
212d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt		wpabuf_free(cr);
213d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt		return NULL;
214d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	}
215d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	wpabuf_put_u8(ac_payload, 0x01); /* Carrier Flags: CRS=1 "active" */
216d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	wpabuf_put_u8(ac_payload, 0x01); /* Carrier Data Reference Length */
217d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	wpabuf_put_u8(ac_payload, '0'); /* Carrier Data Reference: "0" */
218d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	wpabuf_put_u8(ac_payload, 0); /* Aux Data Reference Count */
219d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt
220d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	ac = ndef_build_record(FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "ac", 2,
221d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt			       NULL, 0, ac_payload);
222d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	wpabuf_free(ac_payload);
223d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	if (ac == NULL) {
224d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt		wpabuf_free(cr);
225d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt		return NULL;
226d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	}
227d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt
228d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	hr_payload = wpabuf_alloc(1 + wpabuf_len(cr) + wpabuf_len(ac));
229d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	if (hr_payload == NULL) {
230d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt		wpabuf_free(cr);
231d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt		wpabuf_free(ac);
232d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt		return NULL;
233d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	}
234d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt
235d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	wpabuf_put_u8(hr_payload, 0x12); /* Connection Handover Version 1.2 */
236d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	wpabuf_put_buf(hr_payload, cr);
237d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	wpabuf_put_buf(hr_payload, ac);
238d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	wpabuf_free(cr);
239d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	wpabuf_free(ac);
240d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt
241d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	hr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "Hr", 2,
242d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt			       NULL, 0, hr_payload);
243d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	wpabuf_free(hr_payload);
244d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	if (hr == NULL)
245d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt		return NULL;
246d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt
247f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt	hc = ndef_build_wifi_hc(0);
248d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	if (hc == NULL) {
249d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt		wpabuf_free(hr);
250d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt		return NULL;
251d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	}
252d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt
253d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt	return wpabuf_concat(hr, hc);
254d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt}
255