13ff40c12SJohn Marino /*
23ff40c12SJohn Marino * NDEF(NFC Data Exchange Format) routines for Wi-Fi Protected Setup
33ff40c12SJohn Marino * Reference is "NFCForum-TS-NDEF_1.0 2006-07-24".
43ff40c12SJohn Marino * Copyright (c) 2009-2012, Masashi Honma <masashi.honma@gmail.com>
53ff40c12SJohn Marino *
63ff40c12SJohn Marino * This software may be distributed under the terms of the BSD license.
73ff40c12SJohn Marino * See README for more details.
83ff40c12SJohn Marino */
93ff40c12SJohn Marino
103ff40c12SJohn Marino #include "includes.h"
113ff40c12SJohn Marino #include "common.h"
123ff40c12SJohn Marino #include "wps/wps.h"
133ff40c12SJohn Marino
143ff40c12SJohn Marino #define FLAG_MESSAGE_BEGIN (1 << 7)
153ff40c12SJohn Marino #define FLAG_MESSAGE_END (1 << 6)
163ff40c12SJohn Marino #define FLAG_CHUNK (1 << 5)
173ff40c12SJohn Marino #define FLAG_SHORT_RECORD (1 << 4)
183ff40c12SJohn Marino #define FLAG_ID_LENGTH_PRESENT (1 << 3)
193ff40c12SJohn Marino #define FLAG_TNF_NFC_FORUM (0x01)
203ff40c12SJohn Marino #define FLAG_TNF_RFC2046 (0x02)
213ff40c12SJohn Marino
223ff40c12SJohn Marino struct ndef_record {
233ff40c12SJohn Marino const u8 *type;
243ff40c12SJohn Marino const u8 *id;
253ff40c12SJohn Marino const u8 *payload;
263ff40c12SJohn Marino u8 type_length;
273ff40c12SJohn Marino u8 id_length;
283ff40c12SJohn Marino u32 payload_length;
293ff40c12SJohn Marino u32 total_length;
303ff40c12SJohn Marino };
313ff40c12SJohn Marino
32*a1157835SDaniel Fojt static const char wifi_handover_type[] = "application/vnd.wfa.wsc";
33*a1157835SDaniel Fojt static const char p2p_handover_type[] = "application/vnd.wfa.p2p";
343ff40c12SJohn Marino
ndef_parse_record(const u8 * data,u32 size,struct ndef_record * record)353ff40c12SJohn Marino static int ndef_parse_record(const u8 *data, u32 size,
363ff40c12SJohn Marino struct ndef_record *record)
373ff40c12SJohn Marino {
383ff40c12SJohn Marino const u8 *pos = data + 1;
393ff40c12SJohn Marino
403ff40c12SJohn Marino if (size < 2)
413ff40c12SJohn Marino return -1;
423ff40c12SJohn Marino record->type_length = *pos++;
433ff40c12SJohn Marino if (data[0] & FLAG_SHORT_RECORD) {
443ff40c12SJohn Marino if (size < 3)
453ff40c12SJohn Marino return -1;
463ff40c12SJohn Marino record->payload_length = *pos++;
473ff40c12SJohn Marino } else {
48*a1157835SDaniel Fojt u32 len;
49*a1157835SDaniel Fojt
503ff40c12SJohn Marino if (size < 6)
513ff40c12SJohn Marino return -1;
52*a1157835SDaniel Fojt len = WPA_GET_BE32(pos);
53*a1157835SDaniel Fojt if (len > size - 6 || len > 20000)
54*a1157835SDaniel Fojt return -1;
55*a1157835SDaniel Fojt record->payload_length = len;
563ff40c12SJohn Marino pos += sizeof(u32);
573ff40c12SJohn Marino }
583ff40c12SJohn Marino
593ff40c12SJohn Marino if (data[0] & FLAG_ID_LENGTH_PRESENT) {
603ff40c12SJohn Marino if ((int) size < pos - data + 1)
613ff40c12SJohn Marino return -1;
623ff40c12SJohn Marino record->id_length = *pos++;
633ff40c12SJohn Marino } else
643ff40c12SJohn Marino record->id_length = 0;
653ff40c12SJohn Marino
663ff40c12SJohn Marino record->type = record->type_length == 0 ? NULL : pos;
673ff40c12SJohn Marino pos += record->type_length;
683ff40c12SJohn Marino
693ff40c12SJohn Marino record->id = record->id_length == 0 ? NULL : pos;
703ff40c12SJohn Marino pos += record->id_length;
713ff40c12SJohn Marino
723ff40c12SJohn Marino record->payload = record->payload_length == 0 ? NULL : pos;
733ff40c12SJohn Marino pos += record->payload_length;
743ff40c12SJohn Marino
753ff40c12SJohn Marino record->total_length = pos - data;
76*a1157835SDaniel Fojt if (record->total_length > size ||
77*a1157835SDaniel Fojt record->total_length < record->payload_length)
783ff40c12SJohn Marino return -1;
793ff40c12SJohn Marino return 0;
803ff40c12SJohn Marino }
813ff40c12SJohn Marino
823ff40c12SJohn Marino
ndef_parse_records(const struct wpabuf * buf,int (* filter)(struct ndef_record *))833ff40c12SJohn Marino static struct wpabuf * ndef_parse_records(const struct wpabuf *buf,
843ff40c12SJohn Marino int (*filter)(struct ndef_record *))
853ff40c12SJohn Marino {
863ff40c12SJohn Marino struct ndef_record record;
873ff40c12SJohn Marino int len = wpabuf_len(buf);
883ff40c12SJohn Marino const u8 *data = wpabuf_head(buf);
893ff40c12SJohn Marino
903ff40c12SJohn Marino while (len > 0) {
913ff40c12SJohn Marino if (ndef_parse_record(data, len, &record) < 0) {
923ff40c12SJohn Marino wpa_printf(MSG_ERROR, "NDEF : Failed to parse");
933ff40c12SJohn Marino return NULL;
943ff40c12SJohn Marino }
953ff40c12SJohn Marino if (filter == NULL || filter(&record))
963ff40c12SJohn Marino return wpabuf_alloc_copy(record.payload,
973ff40c12SJohn Marino record.payload_length);
983ff40c12SJohn Marino data += record.total_length;
993ff40c12SJohn Marino len -= record.total_length;
1003ff40c12SJohn Marino }
1013ff40c12SJohn Marino wpa_printf(MSG_ERROR, "NDEF : Record not found");
1023ff40c12SJohn Marino return NULL;
1033ff40c12SJohn Marino }
1043ff40c12SJohn Marino
1053ff40c12SJohn Marino
ndef_build_record(u8 flags,const void * type,u8 type_length,void * id,u8 id_length,const struct wpabuf * payload)106*a1157835SDaniel Fojt static struct wpabuf * ndef_build_record(u8 flags, const void *type,
1073ff40c12SJohn Marino u8 type_length, void *id,
1083ff40c12SJohn Marino u8 id_length,
1093ff40c12SJohn Marino const struct wpabuf *payload)
1103ff40c12SJohn Marino {
1113ff40c12SJohn Marino struct wpabuf *record;
1123ff40c12SJohn Marino size_t total_len;
1133ff40c12SJohn Marino int short_record;
1143ff40c12SJohn Marino u8 local_flag;
1153ff40c12SJohn Marino size_t payload_length = wpabuf_len(payload);
1163ff40c12SJohn Marino
1173ff40c12SJohn Marino short_record = payload_length < 256 ? 1 : 0;
1183ff40c12SJohn Marino
1193ff40c12SJohn Marino total_len = 2; /* flag + type length */
1203ff40c12SJohn Marino /* payload length */
1213ff40c12SJohn Marino total_len += short_record ? sizeof(u8) : sizeof(u32);
1223ff40c12SJohn Marino if (id_length > 0)
1233ff40c12SJohn Marino total_len += 1;
1243ff40c12SJohn Marino total_len += type_length + id_length + payload_length;
1253ff40c12SJohn Marino record = wpabuf_alloc(total_len);
1263ff40c12SJohn Marino if (record == NULL) {
1273ff40c12SJohn Marino wpa_printf(MSG_ERROR, "NDEF : Failed to allocate "
1283ff40c12SJohn Marino "record for build");
1293ff40c12SJohn Marino return NULL;
1303ff40c12SJohn Marino }
1313ff40c12SJohn Marino
1323ff40c12SJohn Marino local_flag = flags;
1333ff40c12SJohn Marino if (id_length > 0)
1343ff40c12SJohn Marino local_flag |= FLAG_ID_LENGTH_PRESENT;
1353ff40c12SJohn Marino if (short_record)
1363ff40c12SJohn Marino local_flag |= FLAG_SHORT_RECORD;
1373ff40c12SJohn Marino wpabuf_put_u8(record, local_flag);
1383ff40c12SJohn Marino
1393ff40c12SJohn Marino wpabuf_put_u8(record, type_length);
1403ff40c12SJohn Marino
1413ff40c12SJohn Marino if (short_record)
1423ff40c12SJohn Marino wpabuf_put_u8(record, payload_length);
1433ff40c12SJohn Marino else
1443ff40c12SJohn Marino wpabuf_put_be32(record, payload_length);
1453ff40c12SJohn Marino
1463ff40c12SJohn Marino if (id_length > 0)
1473ff40c12SJohn Marino wpabuf_put_u8(record, id_length);
1483ff40c12SJohn Marino wpabuf_put_data(record, type, type_length);
1493ff40c12SJohn Marino wpabuf_put_data(record, id, id_length);
1503ff40c12SJohn Marino wpabuf_put_buf(record, payload);
1513ff40c12SJohn Marino return record;
1523ff40c12SJohn Marino }
1533ff40c12SJohn Marino
1543ff40c12SJohn Marino
wifi_filter(struct ndef_record * record)1553ff40c12SJohn Marino static int wifi_filter(struct ndef_record *record)
1563ff40c12SJohn Marino {
157*a1157835SDaniel Fojt if (record->type == NULL ||
158*a1157835SDaniel Fojt record->type_length != os_strlen(wifi_handover_type))
1593ff40c12SJohn Marino return 0;
1603ff40c12SJohn Marino if (os_memcmp(record->type, wifi_handover_type,
1613ff40c12SJohn Marino os_strlen(wifi_handover_type)) != 0)
1623ff40c12SJohn Marino return 0;
1633ff40c12SJohn Marino return 1;
1643ff40c12SJohn Marino }
1653ff40c12SJohn Marino
1663ff40c12SJohn Marino
ndef_parse_wifi(const struct wpabuf * buf)1673ff40c12SJohn Marino struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf)
1683ff40c12SJohn Marino {
1693ff40c12SJohn Marino return ndef_parse_records(buf, wifi_filter);
1703ff40c12SJohn Marino }
1713ff40c12SJohn Marino
1723ff40c12SJohn Marino
ndef_build_wifi(const struct wpabuf * buf)1733ff40c12SJohn Marino struct wpabuf * ndef_build_wifi(const struct wpabuf *buf)
1743ff40c12SJohn Marino {
1753ff40c12SJohn Marino return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END |
1763ff40c12SJohn Marino FLAG_TNF_RFC2046, wifi_handover_type,
1773ff40c12SJohn Marino os_strlen(wifi_handover_type), NULL, 0, buf);
1783ff40c12SJohn Marino }
1793ff40c12SJohn Marino
1803ff40c12SJohn Marino
p2p_filter(struct ndef_record * record)1813ff40c12SJohn Marino static int p2p_filter(struct ndef_record *record)
1823ff40c12SJohn Marino {
183*a1157835SDaniel Fojt if (record->type == NULL ||
184*a1157835SDaniel Fojt record->type_length != os_strlen(p2p_handover_type))
1853ff40c12SJohn Marino return 0;
1863ff40c12SJohn Marino if (os_memcmp(record->type, p2p_handover_type,
1873ff40c12SJohn Marino os_strlen(p2p_handover_type)) != 0)
1883ff40c12SJohn Marino return 0;
1893ff40c12SJohn Marino return 1;
1903ff40c12SJohn Marino }
1913ff40c12SJohn Marino
1923ff40c12SJohn Marino
ndef_parse_p2p(const struct wpabuf * buf)1933ff40c12SJohn Marino struct wpabuf * ndef_parse_p2p(const struct wpabuf *buf)
1943ff40c12SJohn Marino {
1953ff40c12SJohn Marino return ndef_parse_records(buf, p2p_filter);
1963ff40c12SJohn Marino }
1973ff40c12SJohn Marino
1983ff40c12SJohn Marino
ndef_build_p2p(const struct wpabuf * buf)1993ff40c12SJohn Marino struct wpabuf * ndef_build_p2p(const struct wpabuf *buf)
2003ff40c12SJohn Marino {
2013ff40c12SJohn Marino return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END |
2023ff40c12SJohn Marino FLAG_TNF_RFC2046, p2p_handover_type,
2033ff40c12SJohn Marino os_strlen(p2p_handover_type), NULL, 0, buf);
2043ff40c12SJohn Marino }
205