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