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