1e28a4053SRui Paulo /* 2e28a4053SRui Paulo * NDEF(NFC Data Exchange Format) routines for Wi-Fi Protected Setup 3e28a4053SRui Paulo * Reference is "NFCForum-TS-NDEF_1.0 2006-07-24". 4f05cddf9SRui Paulo * Copyright (c) 2009-2012, Masashi Honma <masashi.honma@gmail.com> 5e28a4053SRui Paulo * 6f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license. 7f05cddf9SRui Paulo * See README for more details. 8e28a4053SRui Paulo */ 9e28a4053SRui Paulo 10e28a4053SRui Paulo #include "includes.h" 11e28a4053SRui Paulo #include "common.h" 12e28a4053SRui Paulo #include "wps/wps.h" 13e28a4053SRui Paulo 14e28a4053SRui Paulo #define FLAG_MESSAGE_BEGIN (1 << 7) 15e28a4053SRui Paulo #define FLAG_MESSAGE_END (1 << 6) 16e28a4053SRui Paulo #define FLAG_CHUNK (1 << 5) 17e28a4053SRui Paulo #define FLAG_SHORT_RECORD (1 << 4) 18e28a4053SRui Paulo #define FLAG_ID_LENGTH_PRESENT (1 << 3) 19f05cddf9SRui Paulo #define FLAG_TNF_NFC_FORUM (0x01) 20e28a4053SRui Paulo #define FLAG_TNF_RFC2046 (0x02) 21e28a4053SRui Paulo 22e28a4053SRui Paulo struct ndef_record { 23f05cddf9SRui Paulo const u8 *type; 24f05cddf9SRui Paulo const u8 *id; 25f05cddf9SRui Paulo const u8 *payload; 26e28a4053SRui Paulo u8 type_length; 27e28a4053SRui Paulo u8 id_length; 28e28a4053SRui Paulo u32 payload_length; 29e28a4053SRui Paulo u32 total_length; 30e28a4053SRui Paulo }; 31e28a4053SRui Paulo 32*325151a3SRui Paulo static const char wifi_handover_type[] = "application/vnd.wfa.wsc"; 33*325151a3SRui Paulo static const char p2p_handover_type[] = "application/vnd.wfa.p2p"; 34e28a4053SRui Paulo 35f05cddf9SRui Paulo static int ndef_parse_record(const u8 *data, u32 size, 36f05cddf9SRui Paulo struct ndef_record *record) 37e28a4053SRui Paulo { 38f05cddf9SRui Paulo const u8 *pos = data + 1; 39e28a4053SRui Paulo 40e28a4053SRui Paulo if (size < 2) 41e28a4053SRui Paulo return -1; 42e28a4053SRui Paulo record->type_length = *pos++; 43e28a4053SRui Paulo if (data[0] & FLAG_SHORT_RECORD) { 44e28a4053SRui Paulo if (size < 3) 45e28a4053SRui Paulo return -1; 46e28a4053SRui Paulo record->payload_length = *pos++; 47e28a4053SRui Paulo } else { 48*325151a3SRui Paulo u32 len; 49*325151a3SRui Paulo 50e28a4053SRui Paulo if (size < 6) 51e28a4053SRui Paulo return -1; 52*325151a3SRui Paulo len = WPA_GET_BE32(pos); 53*325151a3SRui Paulo if (len > size - 6 || len > 20000) 54*325151a3SRui Paulo return -1; 55*325151a3SRui Paulo record->payload_length = len; 56e28a4053SRui Paulo pos += sizeof(u32); 57e28a4053SRui Paulo } 58e28a4053SRui Paulo 59e28a4053SRui Paulo if (data[0] & FLAG_ID_LENGTH_PRESENT) { 60e28a4053SRui Paulo if ((int) size < pos - data + 1) 61e28a4053SRui Paulo return -1; 62e28a4053SRui Paulo record->id_length = *pos++; 63e28a4053SRui Paulo } else 64e28a4053SRui Paulo record->id_length = 0; 65e28a4053SRui Paulo 66e28a4053SRui Paulo record->type = record->type_length == 0 ? NULL : pos; 67e28a4053SRui Paulo pos += record->type_length; 68e28a4053SRui Paulo 69e28a4053SRui Paulo record->id = record->id_length == 0 ? NULL : pos; 70e28a4053SRui Paulo pos += record->id_length; 71e28a4053SRui Paulo 72e28a4053SRui Paulo record->payload = record->payload_length == 0 ? NULL : pos; 73e28a4053SRui Paulo pos += record->payload_length; 74e28a4053SRui Paulo 75e28a4053SRui Paulo record->total_length = pos - data; 76*325151a3SRui Paulo if (record->total_length > size || 77*325151a3SRui Paulo record->total_length < record->payload_length) 78e28a4053SRui Paulo return -1; 79e28a4053SRui Paulo return 0; 80e28a4053SRui Paulo } 81e28a4053SRui Paulo 82e28a4053SRui Paulo 83f05cddf9SRui Paulo static struct wpabuf * ndef_parse_records(const struct wpabuf *buf, 84e28a4053SRui Paulo int (*filter)(struct ndef_record *)) 85e28a4053SRui Paulo { 86e28a4053SRui Paulo struct ndef_record record; 87e28a4053SRui Paulo int len = wpabuf_len(buf); 88f05cddf9SRui Paulo const u8 *data = wpabuf_head(buf); 89e28a4053SRui Paulo 90e28a4053SRui Paulo while (len > 0) { 91e28a4053SRui Paulo if (ndef_parse_record(data, len, &record) < 0) { 92e28a4053SRui Paulo wpa_printf(MSG_ERROR, "NDEF : Failed to parse"); 93e28a4053SRui Paulo return NULL; 94e28a4053SRui Paulo } 95e28a4053SRui Paulo if (filter == NULL || filter(&record)) 96e28a4053SRui Paulo return wpabuf_alloc_copy(record.payload, 97e28a4053SRui Paulo record.payload_length); 98e28a4053SRui Paulo data += record.total_length; 99e28a4053SRui Paulo len -= record.total_length; 100e28a4053SRui Paulo } 101e28a4053SRui Paulo wpa_printf(MSG_ERROR, "NDEF : Record not found"); 102e28a4053SRui Paulo return NULL; 103e28a4053SRui Paulo } 104e28a4053SRui Paulo 105e28a4053SRui Paulo 106*325151a3SRui Paulo static struct wpabuf * ndef_build_record(u8 flags, const void *type, 107e28a4053SRui Paulo u8 type_length, void *id, 108f05cddf9SRui Paulo u8 id_length, 109f05cddf9SRui Paulo const struct wpabuf *payload) 110e28a4053SRui Paulo { 111e28a4053SRui Paulo struct wpabuf *record; 112e28a4053SRui Paulo size_t total_len; 113e28a4053SRui Paulo int short_record; 114e28a4053SRui Paulo u8 local_flag; 115f05cddf9SRui Paulo size_t payload_length = wpabuf_len(payload); 116e28a4053SRui Paulo 117e28a4053SRui Paulo short_record = payload_length < 256 ? 1 : 0; 118e28a4053SRui Paulo 119e28a4053SRui Paulo total_len = 2; /* flag + type length */ 120e28a4053SRui Paulo /* payload length */ 121e28a4053SRui Paulo total_len += short_record ? sizeof(u8) : sizeof(u32); 122e28a4053SRui Paulo if (id_length > 0) 123e28a4053SRui Paulo total_len += 1; 124e28a4053SRui Paulo total_len += type_length + id_length + payload_length; 125e28a4053SRui Paulo record = wpabuf_alloc(total_len); 126e28a4053SRui Paulo if (record == NULL) { 127e28a4053SRui Paulo wpa_printf(MSG_ERROR, "NDEF : Failed to allocate " 128e28a4053SRui Paulo "record for build"); 129e28a4053SRui Paulo return NULL; 130e28a4053SRui Paulo } 131e28a4053SRui Paulo 132e28a4053SRui Paulo local_flag = flags; 133e28a4053SRui Paulo if (id_length > 0) 134e28a4053SRui Paulo local_flag |= FLAG_ID_LENGTH_PRESENT; 135e28a4053SRui Paulo if (short_record) 136e28a4053SRui Paulo local_flag |= FLAG_SHORT_RECORD; 137e28a4053SRui Paulo wpabuf_put_u8(record, local_flag); 138e28a4053SRui Paulo 139e28a4053SRui Paulo wpabuf_put_u8(record, type_length); 140e28a4053SRui Paulo 141e28a4053SRui Paulo if (short_record) 142e28a4053SRui Paulo wpabuf_put_u8(record, payload_length); 143e28a4053SRui Paulo else 144e28a4053SRui Paulo wpabuf_put_be32(record, payload_length); 145e28a4053SRui Paulo 146e28a4053SRui Paulo if (id_length > 0) 147e28a4053SRui Paulo wpabuf_put_u8(record, id_length); 148e28a4053SRui Paulo wpabuf_put_data(record, type, type_length); 149e28a4053SRui Paulo wpabuf_put_data(record, id, id_length); 150f05cddf9SRui Paulo wpabuf_put_buf(record, payload); 151e28a4053SRui Paulo return record; 152e28a4053SRui Paulo } 153e28a4053SRui Paulo 154e28a4053SRui Paulo 155e28a4053SRui Paulo static int wifi_filter(struct ndef_record *record) 156e28a4053SRui Paulo { 1575b9c547cSRui Paulo if (record->type == NULL || 1585b9c547cSRui Paulo record->type_length != os_strlen(wifi_handover_type)) 159e28a4053SRui Paulo return 0; 160e28a4053SRui Paulo if (os_memcmp(record->type, wifi_handover_type, 161e28a4053SRui Paulo os_strlen(wifi_handover_type)) != 0) 162e28a4053SRui Paulo return 0; 163e28a4053SRui Paulo return 1; 164e28a4053SRui Paulo } 165e28a4053SRui Paulo 166e28a4053SRui Paulo 167f05cddf9SRui Paulo struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf) 168e28a4053SRui Paulo { 169e28a4053SRui Paulo return ndef_parse_records(buf, wifi_filter); 170e28a4053SRui Paulo } 171e28a4053SRui Paulo 172e28a4053SRui Paulo 173f05cddf9SRui Paulo struct wpabuf * ndef_build_wifi(const struct wpabuf *buf) 174e28a4053SRui Paulo { 175e28a4053SRui Paulo return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END | 176e28a4053SRui Paulo FLAG_TNF_RFC2046, wifi_handover_type, 177f05cddf9SRui Paulo os_strlen(wifi_handover_type), NULL, 0, buf); 178f05cddf9SRui Paulo } 179f05cddf9SRui Paulo 180f05cddf9SRui Paulo 1815b9c547cSRui Paulo static int p2p_filter(struct ndef_record *record) 182f05cddf9SRui Paulo { 1835b9c547cSRui Paulo if (record->type == NULL || 1845b9c547cSRui Paulo record->type_length != os_strlen(p2p_handover_type)) 1855b9c547cSRui Paulo return 0; 1865b9c547cSRui Paulo if (os_memcmp(record->type, p2p_handover_type, 1875b9c547cSRui Paulo os_strlen(p2p_handover_type)) != 0) 1885b9c547cSRui Paulo return 0; 1895b9c547cSRui Paulo return 1; 190f05cddf9SRui Paulo } 191f05cddf9SRui Paulo 1925b9c547cSRui Paulo 1935b9c547cSRui Paulo struct wpabuf * ndef_parse_p2p(const struct wpabuf *buf) 1945b9c547cSRui Paulo { 1955b9c547cSRui Paulo return ndef_parse_records(buf, p2p_filter); 196f05cddf9SRui Paulo } 197f05cddf9SRui Paulo 198f05cddf9SRui Paulo 1995b9c547cSRui Paulo struct wpabuf * ndef_build_p2p(const struct wpabuf *buf) 2005b9c547cSRui Paulo { 2015b9c547cSRui Paulo return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END | 2025b9c547cSRui Paulo FLAG_TNF_RFC2046, p2p_handover_type, 2035b9c547cSRui Paulo os_strlen(p2p_handover_type), NULL, 0, buf); 204e28a4053SRui Paulo } 205