1 /*-
2 * Copyright (C) 2009, Romain Tartiere, Romuald Conty.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by the
6 * Free Software Foundation, either version 3 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>
16 *
17 * $Id$
18 */
19
20 /*
21 * This implementation was written based on information provided by the
22 * following document:
23 *
24 * /dev/brain
25 */
26 #include "config.h"
27
28 #include <errno.h>
29 #include <string.h>
30 #include <stdlib.h>
31
32 #include <freefare.h>
33 #include "freefare_internal.h"
34
35 #define FIRST_SECTOR 1
36
37 int aidcmp (const MadAid left, const MadAid right);
38 size_t count_aids (const Mad mad, const MadAid aid);
39
40 /*
41 * Get the number of sectors allocated in the MAD for the provided application.
42 */
43 size_t
count_aids(const Mad mad,const MadAid aid)44 count_aids (const Mad mad, const MadAid aid)
45 {
46 size_t result = 0;
47
48 MifareClassicSectorNumber s_max = (mad_get_version (mad) == 1) ? 0x0f : 0x27;
49
50 /* Count application sectors */
51 MadAid c_aid;
52 for (MifareClassicSectorNumber s = FIRST_SECTOR; s <= s_max; s++) {
53 mad_get_aid (mad, s, &c_aid);
54 if (0 == aidcmp (aid, c_aid)) {
55 result++;
56 }
57 }
58
59 return result;
60 }
61
62 /*
63 * Compare two application identifiers.
64 */
65 inline int
aidcmp(const MadAid left,const MadAid right)66 aidcmp (const MadAid left, const MadAid right)
67 {
68 return ((left.function_cluster_code - right.function_cluster_code) << 8) | (left.application_code - right.application_code);
69 }
70
71
72 /*
73 * Card publisher functions (MAD owner).
74 */
75
76 /*
77 * Allocates a new application into a MAD.
78 */
79 MifareClassicSectorNumber *
mifare_application_alloc(Mad mad,MadAid aid,size_t size)80 mifare_application_alloc (Mad mad, MadAid aid, size_t size)
81 {
82 uint8_t sector_map[40];
83 MifareClassicSectorNumber sector;
84 MadAid sector_aid;
85 MifareClassicSectorNumber *res = NULL;
86 ssize_t s = size;
87
88 /*
89 * Ensure the card does not already have the application registered.
90 */
91 MifareClassicSectorNumber *found;
92 if ((found = mifare_application_find (mad, aid))) {
93 free (found);
94 return NULL;
95 }
96
97 for (size_t i = 0; i < sizeof (sector_map); i++)
98 sector_map[i] = 0;
99
100 /*
101 * Try to minimize lost space and allocate as many large pages as possible
102 * when the target is a Mifare Classic 4k.
103 */
104 MadAid free_aid = { 0x00, 0x00 };
105 if (mad_get_version (mad) == 2) {
106 sector = 32;
107 while ((s >= 12*16) && sector < 40) {
108 mad_get_aid (mad, sector, §or_aid);
109 if (0 == aidcmp (sector_aid, free_aid)) {
110 sector_map[sector] = 1;
111 s -= 15*16;
112 }
113 sector++;
114 }
115 }
116
117 sector = FIRST_SECTOR;
118 MifareClassicSectorNumber s_max = (mad_get_version (mad) == 1) ? 15 : 31;
119 while ((s > 0) && (sector <= s_max)) {
120 if (mad_sector_reserved (sector))
121 continue;
122 mad_get_aid (mad, sector, §or_aid);
123 if (0 == aidcmp (sector_aid, free_aid)) {
124 sector_map[sector] = 1;
125 s -= 3*16;
126 }
127 sector++;
128 }
129
130 /*
131 * Ensure the remaining free space is suficient before destroying the MAD.
132 */
133 if (s > 0)
134 return NULL;
135
136 int n = 0;
137 for (size_t i = FIRST_SECTOR; i < sizeof (sector_map); i++)
138 if (sector_map[i])
139 n++;
140
141 if (!(res = malloc (sizeof (*res) * (n+1))))
142 return NULL;
143
144 n = 0;
145 for (size_t i = FIRST_SECTOR; i < sizeof (sector_map); i++)
146 if (sector_map[i]) {
147 res[n] = i;
148 mad_set_aid (mad, i, aid);
149 n++;
150 }
151
152 res[n] = 0;
153
154 /* Return the list of allocated sectors */
155 return res;
156 }
157
158 /*
159 * Remove an application from a MAD.
160 */
161 void
mifare_application_free(Mad mad,MadAid aid)162 mifare_application_free (Mad mad, MadAid aid)
163 {
164 MifareClassicSectorNumber *sectors = mifare_application_find (mad, aid);
165 MifareClassicSectorNumber *p = sectors;
166 MadAid free_aid = { 0x00, 0x00 };
167 while (*p) {
168 mad_set_aid (mad, *p, free_aid);
169 p++;
170 }
171
172 free (sectors);
173 }
174
175
176 /*
177 * Application owner functions.
178 */
179
180 /*
181 * Get all sector numbers of an application from the provided MAD.
182 */
183 MifareClassicSectorNumber *
mifare_application_find(Mad mad,MadAid aid)184 mifare_application_find (Mad mad, MadAid aid)
185 {
186 MifareClassicSectorNumber *res = NULL;
187 size_t res_count = count_aids (mad, aid);
188
189 if (res_count)
190 res = malloc (sizeof (*res) * (res_count + 1));
191
192 size_t r = FIRST_SECTOR, w = 0;
193 if (res) {
194 /* Fill in the result */
195 MadAid c_aid;
196 while (w < res_count) {
197 mad_get_aid (mad, r, &c_aid);
198 if (0 == aidcmp (c_aid, aid)) {
199 res[w++] = r;
200 }
201 r++;
202 }
203 res[w] = 0;
204 }
205
206 return res;
207 }
208
209 ssize_t
mifare_application_read(MifareTag tag,Mad mad,const MadAid aid,void * buf,size_t nbytes,const MifareClassicKey key,const MifareClassicKeyType key_type)210 mifare_application_read (MifareTag tag, Mad mad, const MadAid aid, void *buf, size_t nbytes, const MifareClassicKey key, const MifareClassicKeyType key_type)
211 {
212 ssize_t res = 0;
213
214 MifareClassicSectorNumber *sectors = mifare_application_find (mad, aid);
215 MifareClassicSectorNumber *s = sectors;
216
217 if (!sectors)
218 return errno = EBADF, -1;
219
220 while (*s && nbytes && (res >= 0)) {
221 MifareClassicBlockNumber first_block = mifare_classic_sector_first_block (*s);
222 MifareClassicBlockNumber last_block = mifare_classic_sector_last_block (*s);
223
224 MifareClassicBlockNumber b = first_block;
225 MifareClassicBlock block;
226
227 if (mifare_classic_authenticate (tag, first_block, key, key_type) < 0) {
228 res = -1;
229 break;
230 }
231
232 while ((b < last_block) && nbytes) {
233 size_t n = MIN (nbytes, 16);
234
235 if (mifare_classic_read (tag, b, &block) < 0) {
236 res = -1;
237 break;
238 }
239 memcpy ((uint8_t *)buf + res, &block, n);
240
241 nbytes -= n;
242 res += n;
243
244 b++;
245 }
246
247 s++;
248 }
249
250 free (sectors);
251 return res;
252 }
253
254 ssize_t
mifare_application_write(MifareTag tag,Mad mad,const MadAid aid,const void * buf,size_t nbytes,const MifareClassicKey key,const MifareClassicKeyType key_type)255 mifare_application_write (MifareTag tag, Mad mad, const MadAid aid, const void *buf, size_t nbytes, const MifareClassicKey key, const MifareClassicKeyType key_type)
256 {
257 ssize_t res = 0;
258
259 MifareClassicSectorNumber *sectors = mifare_application_find (mad, aid);
260 MifareClassicSectorNumber *s = sectors;
261
262 if (!sectors)
263 return errno = EBADF, -1;
264
265 while (*s && nbytes && (res >= 0)) {
266 MifareClassicBlockNumber first_block = mifare_classic_sector_first_block (*s);
267 MifareClassicBlockNumber last_block = mifare_classic_sector_last_block (*s);
268
269 MifareClassicBlockNumber b = first_block;
270 MifareClassicBlock block;
271
272 if (mifare_classic_authenticate (tag, first_block, key, key_type) < 0) {
273 res = -1;
274 break;
275 }
276
277 while ((b < last_block) && nbytes) {
278 size_t n = MIN (nbytes, 16);
279 // Avoid overwriting existing data with uninitialized memory.
280 if (n < 16) {
281 if (mifare_classic_read (tag, b, &block) < 0) {
282 res = -1;
283 break;
284 }
285 }
286
287 memcpy (&block, (uint8_t *)buf + res, n);
288 if (mifare_classic_write (tag, b, block) < 0) {
289 res = -1;
290 break;
291 }
292
293 nbytes -= n;
294 res += n;
295
296 b++;
297 }
298
299 s++;
300 }
301
302 free (sectors);
303 return res;
304
305 }
306