1214501Srpaulo/* 2214501Srpaulo * NDEF(NFC Data Exchange Format) routines for Wi-Fi Protected Setup 3214501Srpaulo * Reference is "NFCForum-TS-NDEF_1.0 2006-07-24". 4252726Srpaulo * Copyright (c) 2009-2012, Masashi Honma <masashi.honma@gmail.com> 5214501Srpaulo * 6252726Srpaulo * This software may be distributed under the terms of the BSD license. 7252726Srpaulo * See README for more details. 8214501Srpaulo */ 9214501Srpaulo 10214501Srpaulo#include "includes.h" 11214501Srpaulo#include "common.h" 12214501Srpaulo#include "wps/wps.h" 13214501Srpaulo 14214501Srpaulo#define FLAG_MESSAGE_BEGIN (1 << 7) 15214501Srpaulo#define FLAG_MESSAGE_END (1 << 6) 16214501Srpaulo#define FLAG_CHUNK (1 << 5) 17214501Srpaulo#define FLAG_SHORT_RECORD (1 << 4) 18214501Srpaulo#define FLAG_ID_LENGTH_PRESENT (1 << 3) 19252726Srpaulo#define FLAG_TNF_NFC_FORUM (0x01) 20214501Srpaulo#define FLAG_TNF_RFC2046 (0x02) 21214501Srpaulo 22214501Srpaulostruct ndef_record { 23252726Srpaulo const u8 *type; 24252726Srpaulo const u8 *id; 25252726Srpaulo const u8 *payload; 26214501Srpaulo u8 type_length; 27214501Srpaulo u8 id_length; 28214501Srpaulo u32 payload_length; 29214501Srpaulo u32 total_length; 30214501Srpaulo}; 31214501Srpaulo 32214501Srpaulostatic char wifi_handover_type[] = "application/vnd.wfa.wsc"; 33214501Srpaulo 34252726Srpaulostatic int ndef_parse_record(const u8 *data, u32 size, 35252726Srpaulo struct ndef_record *record) 36214501Srpaulo{ 37252726Srpaulo const u8 *pos = data + 1; 38214501Srpaulo 39214501Srpaulo if (size < 2) 40214501Srpaulo return -1; 41214501Srpaulo record->type_length = *pos++; 42214501Srpaulo if (data[0] & FLAG_SHORT_RECORD) { 43214501Srpaulo if (size < 3) 44214501Srpaulo return -1; 45214501Srpaulo record->payload_length = *pos++; 46214501Srpaulo } else { 47214501Srpaulo if (size < 6) 48214501Srpaulo return -1; 49214501Srpaulo record->payload_length = ntohl(*(u32 *)pos); 50214501Srpaulo pos += sizeof(u32); 51214501Srpaulo } 52214501Srpaulo 53214501Srpaulo if (data[0] & FLAG_ID_LENGTH_PRESENT) { 54214501Srpaulo if ((int) size < pos - data + 1) 55214501Srpaulo return -1; 56214501Srpaulo record->id_length = *pos++; 57214501Srpaulo } else 58214501Srpaulo record->id_length = 0; 59214501Srpaulo 60214501Srpaulo record->type = record->type_length == 0 ? NULL : pos; 61214501Srpaulo pos += record->type_length; 62214501Srpaulo 63214501Srpaulo record->id = record->id_length == 0 ? NULL : pos; 64214501Srpaulo pos += record->id_length; 65214501Srpaulo 66214501Srpaulo record->payload = record->payload_length == 0 ? NULL : pos; 67214501Srpaulo pos += record->payload_length; 68214501Srpaulo 69214501Srpaulo record->total_length = pos - data; 70214501Srpaulo if (record->total_length > size) 71214501Srpaulo return -1; 72214501Srpaulo return 0; 73214501Srpaulo} 74214501Srpaulo 75214501Srpaulo 76252726Srpaulostatic struct wpabuf * ndef_parse_records(const struct wpabuf *buf, 77214501Srpaulo int (*filter)(struct ndef_record *)) 78214501Srpaulo{ 79214501Srpaulo struct ndef_record record; 80214501Srpaulo int len = wpabuf_len(buf); 81252726Srpaulo const u8 *data = wpabuf_head(buf); 82214501Srpaulo 83214501Srpaulo while (len > 0) { 84214501Srpaulo if (ndef_parse_record(data, len, &record) < 0) { 85214501Srpaulo wpa_printf(MSG_ERROR, "NDEF : Failed to parse"); 86214501Srpaulo return NULL; 87214501Srpaulo } 88214501Srpaulo if (filter == NULL || filter(&record)) 89214501Srpaulo return wpabuf_alloc_copy(record.payload, 90214501Srpaulo record.payload_length); 91214501Srpaulo data += record.total_length; 92214501Srpaulo len -= record.total_length; 93214501Srpaulo } 94214501Srpaulo wpa_printf(MSG_ERROR, "NDEF : Record not found"); 95214501Srpaulo return NULL; 96214501Srpaulo} 97214501Srpaulo 98214501Srpaulo 99214501Srpaulostatic struct wpabuf * ndef_build_record(u8 flags, void *type, 100214501Srpaulo u8 type_length, void *id, 101252726Srpaulo u8 id_length, 102252726Srpaulo const struct wpabuf *payload) 103214501Srpaulo{ 104214501Srpaulo struct wpabuf *record; 105214501Srpaulo size_t total_len; 106214501Srpaulo int short_record; 107214501Srpaulo u8 local_flag; 108252726Srpaulo size_t payload_length = wpabuf_len(payload); 109214501Srpaulo 110214501Srpaulo short_record = payload_length < 256 ? 1 : 0; 111214501Srpaulo 112214501Srpaulo total_len = 2; /* flag + type length */ 113214501Srpaulo /* payload length */ 114214501Srpaulo total_len += short_record ? sizeof(u8) : sizeof(u32); 115214501Srpaulo if (id_length > 0) 116214501Srpaulo total_len += 1; 117214501Srpaulo total_len += type_length + id_length + payload_length; 118214501Srpaulo record = wpabuf_alloc(total_len); 119214501Srpaulo if (record == NULL) { 120214501Srpaulo wpa_printf(MSG_ERROR, "NDEF : Failed to allocate " 121214501Srpaulo "record for build"); 122214501Srpaulo return NULL; 123214501Srpaulo } 124214501Srpaulo 125214501Srpaulo local_flag = flags; 126214501Srpaulo if (id_length > 0) 127214501Srpaulo local_flag |= FLAG_ID_LENGTH_PRESENT; 128214501Srpaulo if (short_record) 129214501Srpaulo local_flag |= FLAG_SHORT_RECORD; 130214501Srpaulo wpabuf_put_u8(record, local_flag); 131214501Srpaulo 132214501Srpaulo wpabuf_put_u8(record, type_length); 133214501Srpaulo 134214501Srpaulo if (short_record) 135214501Srpaulo wpabuf_put_u8(record, payload_length); 136214501Srpaulo else 137214501Srpaulo wpabuf_put_be32(record, payload_length); 138214501Srpaulo 139214501Srpaulo if (id_length > 0) 140214501Srpaulo wpabuf_put_u8(record, id_length); 141214501Srpaulo wpabuf_put_data(record, type, type_length); 142214501Srpaulo wpabuf_put_data(record, id, id_length); 143252726Srpaulo wpabuf_put_buf(record, payload); 144214501Srpaulo return record; 145214501Srpaulo} 146214501Srpaulo 147214501Srpaulo 148214501Srpaulostatic int wifi_filter(struct ndef_record *record) 149214501Srpaulo{ 150214501Srpaulo if (record->type_length != os_strlen(wifi_handover_type)) 151214501Srpaulo return 0; 152214501Srpaulo if (os_memcmp(record->type, wifi_handover_type, 153214501Srpaulo os_strlen(wifi_handover_type)) != 0) 154214501Srpaulo return 0; 155214501Srpaulo return 1; 156214501Srpaulo} 157214501Srpaulo 158214501Srpaulo 159252726Srpaulostruct wpabuf * ndef_parse_wifi(const struct wpabuf *buf) 160214501Srpaulo{ 161214501Srpaulo return ndef_parse_records(buf, wifi_filter); 162214501Srpaulo} 163214501Srpaulo 164214501Srpaulo 165252726Srpaulostruct wpabuf * ndef_build_wifi(const struct wpabuf *buf) 166214501Srpaulo{ 167214501Srpaulo return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END | 168214501Srpaulo FLAG_TNF_RFC2046, wifi_handover_type, 169252726Srpaulo os_strlen(wifi_handover_type), NULL, 0, buf); 170214501Srpaulo} 171252726Srpaulo 172252726Srpaulo 173252726Srpaulostruct wpabuf * ndef_build_wifi_hr(void) 174252726Srpaulo{ 175252726Srpaulo struct wpabuf *rn, *cr, *ac_payload, *ac, *hr_payload, *hr; 176252726Srpaulo struct wpabuf *carrier, *hc; 177252726Srpaulo 178252726Srpaulo rn = wpabuf_alloc(2); 179252726Srpaulo if (rn == NULL) 180252726Srpaulo return NULL; 181252726Srpaulo wpabuf_put_be16(rn, os_random() & 0xffff); 182252726Srpaulo 183252726Srpaulo cr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "cr", 2, 184252726Srpaulo NULL, 0, rn); 185252726Srpaulo wpabuf_free(rn); 186252726Srpaulo 187252726Srpaulo if (cr == NULL) 188252726Srpaulo return NULL; 189252726Srpaulo 190252726Srpaulo ac_payload = wpabuf_alloc(4); 191252726Srpaulo if (ac_payload == NULL) { 192252726Srpaulo wpabuf_free(cr); 193252726Srpaulo return NULL; 194252726Srpaulo } 195252726Srpaulo wpabuf_put_u8(ac_payload, 0x01); /* Carrier Flags: CRS=1 "active" */ 196252726Srpaulo wpabuf_put_u8(ac_payload, 0x01); /* Carrier Data Reference Length */ 197252726Srpaulo wpabuf_put_u8(ac_payload, '0'); /* Carrier Data Reference: "0" */ 198252726Srpaulo wpabuf_put_u8(ac_payload, 0); /* Aux Data Reference Count */ 199252726Srpaulo 200252726Srpaulo ac = ndef_build_record(FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "ac", 2, 201252726Srpaulo NULL, 0, ac_payload); 202252726Srpaulo wpabuf_free(ac_payload); 203252726Srpaulo if (ac == NULL) { 204252726Srpaulo wpabuf_free(cr); 205252726Srpaulo return NULL; 206252726Srpaulo } 207252726Srpaulo 208252726Srpaulo hr_payload = wpabuf_alloc(1 + wpabuf_len(cr) + wpabuf_len(ac)); 209252726Srpaulo if (hr_payload == NULL) { 210252726Srpaulo wpabuf_free(cr); 211252726Srpaulo wpabuf_free(ac); 212252726Srpaulo return NULL; 213252726Srpaulo } 214252726Srpaulo 215252726Srpaulo wpabuf_put_u8(hr_payload, 0x12); /* Connection Handover Version 1.2 */ 216252726Srpaulo wpabuf_put_buf(hr_payload, cr); 217252726Srpaulo wpabuf_put_buf(hr_payload, ac); 218252726Srpaulo wpabuf_free(cr); 219252726Srpaulo wpabuf_free(ac); 220252726Srpaulo 221252726Srpaulo hr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "Hr", 2, 222252726Srpaulo NULL, 0, hr_payload); 223252726Srpaulo wpabuf_free(hr_payload); 224252726Srpaulo if (hr == NULL) 225252726Srpaulo return NULL; 226252726Srpaulo 227252726Srpaulo carrier = wpabuf_alloc(2 + os_strlen(wifi_handover_type)); 228252726Srpaulo if (carrier == NULL) { 229252726Srpaulo wpabuf_free(hr); 230252726Srpaulo return NULL; 231252726Srpaulo } 232252726Srpaulo wpabuf_put_u8(carrier, 0x02); /* Carrier Type Format */ 233252726Srpaulo wpabuf_put_u8(carrier, os_strlen(wifi_handover_type)); 234252726Srpaulo wpabuf_put_str(carrier, wifi_handover_type); 235252726Srpaulo 236252726Srpaulo hc = ndef_build_record(FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "Hc", 2, 237252726Srpaulo "0", 1, carrier); 238252726Srpaulo wpabuf_free(carrier); 239252726Srpaulo if (hc == NULL) { 240252726Srpaulo wpabuf_free(hr); 241252726Srpaulo return NULL; 242252726Srpaulo } 243252726Srpaulo 244252726Srpaulo return wpabuf_concat(hr, hc); 245252726Srpaulo} 246