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 32289549Srpaulostatic const char wifi_handover_type[] = "application/vnd.wfa.wsc"; 33289549Srpaulostatic const char p2p_handover_type[] = "application/vnd.wfa.p2p"; 34214501Srpaulo 35252726Srpaulostatic int ndef_parse_record(const u8 *data, u32 size, 36252726Srpaulo struct ndef_record *record) 37214501Srpaulo{ 38252726Srpaulo const u8 *pos = data + 1; 39214501Srpaulo 40214501Srpaulo if (size < 2) 41214501Srpaulo return -1; 42214501Srpaulo record->type_length = *pos++; 43214501Srpaulo if (data[0] & FLAG_SHORT_RECORD) { 44214501Srpaulo if (size < 3) 45214501Srpaulo return -1; 46214501Srpaulo record->payload_length = *pos++; 47214501Srpaulo } else { 48289549Srpaulo u32 len; 49289549Srpaulo 50214501Srpaulo if (size < 6) 51214501Srpaulo return -1; 52289549Srpaulo len = WPA_GET_BE32(pos); 53289549Srpaulo if (len > size - 6 || len > 20000) 54289549Srpaulo return -1; 55289549Srpaulo record->payload_length = len; 56214501Srpaulo pos += sizeof(u32); 57214501Srpaulo } 58214501Srpaulo 59214501Srpaulo if (data[0] & FLAG_ID_LENGTH_PRESENT) { 60214501Srpaulo if ((int) size < pos - data + 1) 61214501Srpaulo return -1; 62214501Srpaulo record->id_length = *pos++; 63214501Srpaulo } else 64214501Srpaulo record->id_length = 0; 65214501Srpaulo 66214501Srpaulo record->type = record->type_length == 0 ? NULL : pos; 67214501Srpaulo pos += record->type_length; 68214501Srpaulo 69214501Srpaulo record->id = record->id_length == 0 ? NULL : pos; 70214501Srpaulo pos += record->id_length; 71214501Srpaulo 72214501Srpaulo record->payload = record->payload_length == 0 ? NULL : pos; 73214501Srpaulo pos += record->payload_length; 74214501Srpaulo 75214501Srpaulo record->total_length = pos - data; 76289549Srpaulo if (record->total_length > size || 77289549Srpaulo record->total_length < record->payload_length) 78214501Srpaulo return -1; 79214501Srpaulo return 0; 80214501Srpaulo} 81214501Srpaulo 82214501Srpaulo 83252726Srpaulostatic struct wpabuf * ndef_parse_records(const struct wpabuf *buf, 84214501Srpaulo int (*filter)(struct ndef_record *)) 85214501Srpaulo{ 86214501Srpaulo struct ndef_record record; 87214501Srpaulo int len = wpabuf_len(buf); 88252726Srpaulo const u8 *data = wpabuf_head(buf); 89214501Srpaulo 90214501Srpaulo while (len > 0) { 91214501Srpaulo if (ndef_parse_record(data, len, &record) < 0) { 92214501Srpaulo wpa_printf(MSG_ERROR, "NDEF : Failed to parse"); 93214501Srpaulo return NULL; 94214501Srpaulo } 95214501Srpaulo if (filter == NULL || filter(&record)) 96214501Srpaulo return wpabuf_alloc_copy(record.payload, 97214501Srpaulo record.payload_length); 98214501Srpaulo data += record.total_length; 99214501Srpaulo len -= record.total_length; 100214501Srpaulo } 101214501Srpaulo wpa_printf(MSG_ERROR, "NDEF : Record not found"); 102214501Srpaulo return NULL; 103214501Srpaulo} 104214501Srpaulo 105214501Srpaulo 106289549Srpaulostatic struct wpabuf * ndef_build_record(u8 flags, const void *type, 107214501Srpaulo u8 type_length, void *id, 108252726Srpaulo u8 id_length, 109252726Srpaulo const struct wpabuf *payload) 110214501Srpaulo{ 111214501Srpaulo struct wpabuf *record; 112214501Srpaulo size_t total_len; 113214501Srpaulo int short_record; 114214501Srpaulo u8 local_flag; 115252726Srpaulo size_t payload_length = wpabuf_len(payload); 116214501Srpaulo 117214501Srpaulo short_record = payload_length < 256 ? 1 : 0; 118214501Srpaulo 119214501Srpaulo total_len = 2; /* flag + type length */ 120214501Srpaulo /* payload length */ 121214501Srpaulo total_len += short_record ? sizeof(u8) : sizeof(u32); 122214501Srpaulo if (id_length > 0) 123214501Srpaulo total_len += 1; 124214501Srpaulo total_len += type_length + id_length + payload_length; 125214501Srpaulo record = wpabuf_alloc(total_len); 126214501Srpaulo if (record == NULL) { 127214501Srpaulo wpa_printf(MSG_ERROR, "NDEF : Failed to allocate " 128214501Srpaulo "record for build"); 129214501Srpaulo return NULL; 130214501Srpaulo } 131214501Srpaulo 132214501Srpaulo local_flag = flags; 133214501Srpaulo if (id_length > 0) 134214501Srpaulo local_flag |= FLAG_ID_LENGTH_PRESENT; 135214501Srpaulo if (short_record) 136214501Srpaulo local_flag |= FLAG_SHORT_RECORD; 137214501Srpaulo wpabuf_put_u8(record, local_flag); 138214501Srpaulo 139214501Srpaulo wpabuf_put_u8(record, type_length); 140214501Srpaulo 141214501Srpaulo if (short_record) 142214501Srpaulo wpabuf_put_u8(record, payload_length); 143214501Srpaulo else 144214501Srpaulo wpabuf_put_be32(record, payload_length); 145214501Srpaulo 146214501Srpaulo if (id_length > 0) 147214501Srpaulo wpabuf_put_u8(record, id_length); 148214501Srpaulo wpabuf_put_data(record, type, type_length); 149214501Srpaulo wpabuf_put_data(record, id, id_length); 150252726Srpaulo wpabuf_put_buf(record, payload); 151214501Srpaulo return record; 152214501Srpaulo} 153214501Srpaulo 154214501Srpaulo 155214501Srpaulostatic int wifi_filter(struct ndef_record *record) 156214501Srpaulo{ 157281806Srpaulo if (record->type == NULL || 158281806Srpaulo record->type_length != os_strlen(wifi_handover_type)) 159214501Srpaulo return 0; 160214501Srpaulo if (os_memcmp(record->type, wifi_handover_type, 161214501Srpaulo os_strlen(wifi_handover_type)) != 0) 162214501Srpaulo return 0; 163214501Srpaulo return 1; 164214501Srpaulo} 165214501Srpaulo 166214501Srpaulo 167252726Srpaulostruct wpabuf * ndef_parse_wifi(const struct wpabuf *buf) 168214501Srpaulo{ 169214501Srpaulo return ndef_parse_records(buf, wifi_filter); 170214501Srpaulo} 171214501Srpaulo 172214501Srpaulo 173252726Srpaulostruct wpabuf * ndef_build_wifi(const struct wpabuf *buf) 174214501Srpaulo{ 175214501Srpaulo return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END | 176214501Srpaulo FLAG_TNF_RFC2046, wifi_handover_type, 177252726Srpaulo os_strlen(wifi_handover_type), NULL, 0, buf); 178214501Srpaulo} 179252726Srpaulo 180252726Srpaulo 181281806Srpaulostatic int p2p_filter(struct ndef_record *record) 182252726Srpaulo{ 183281806Srpaulo if (record->type == NULL || 184281806Srpaulo record->type_length != os_strlen(p2p_handover_type)) 185281806Srpaulo return 0; 186281806Srpaulo if (os_memcmp(record->type, p2p_handover_type, 187281806Srpaulo os_strlen(p2p_handover_type)) != 0) 188281806Srpaulo return 0; 189281806Srpaulo return 1; 190281806Srpaulo} 191252726Srpaulo 192252726Srpaulo 193281806Srpaulostruct wpabuf * ndef_parse_p2p(const struct wpabuf *buf) 194281806Srpaulo{ 195281806Srpaulo return ndef_parse_records(buf, p2p_filter); 196281806Srpaulo} 197252726Srpaulo 198252726Srpaulo 199281806Srpaulostruct wpabuf * ndef_build_p2p(const struct wpabuf *buf) 200281806Srpaulo{ 201281806Srpaulo return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END | 202281806Srpaulo FLAG_TNF_RFC2046, p2p_handover_type, 203281806Srpaulo os_strlen(p2p_handover_type), NULL, 0, buf); 204252726Srpaulo} 205