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, &sector_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, &sector_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