305 lines
6.9 KiB
C
305 lines
6.9 KiB
C
/*-
|
||
* Copyright (C) 2009, Romain Tartiere, Romuald Conty.
|
||
*
|
||
* This program is free software: you can redistribute it and/or modify it
|
||
* under the terms of the GNU Lesser General Public License as published by the
|
||
* Free Software Foundation, either version 3 of the License, or (at your
|
||
* option) any later version.
|
||
*
|
||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||
* more details.
|
||
*
|
||
* You should have received a copy of the GNU Lesser General Public License
|
||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||
*
|
||
* $Id$
|
||
*/
|
||
|
||
/*
|
||
* This implementation was written based on information provided by the
|
||
* following document:
|
||
*
|
||
* /dev/brain
|
||
*/
|
||
#include "config.h"
|
||
|
||
#include <errno.h>
|
||
#include <string.h>
|
||
#include <stdlib.h>
|
||
|
||
#include <freefare.h>
|
||
#include "freefare_internal.h"
|
||
|
||
#define FIRST_SECTOR 1
|
||
|
||
int aidcmp (const MadAid left, const MadAid right);
|
||
size_t count_aids (const Mad mad, const MadAid aid);
|
||
|
||
/*
|
||
* Get the number of sectors allocated in the MAD for the provided application.
|
||
*/
|
||
size_t
|
||
count_aids (const Mad mad, const MadAid aid)
|
||
{
|
||
size_t result = 0;
|
||
|
||
MifareClassicSectorNumber s_max = (mad_get_version (mad) == 1) ? 0x0f : 0x27;
|
||
|
||
/* Count application sectors */
|
||
MadAid c_aid;
|
||
for (MifareClassicSectorNumber s = FIRST_SECTOR; s <= s_max; s++) {
|
||
mad_get_aid (mad, s, &c_aid);
|
||
if (0 == aidcmp (aid, c_aid)) {
|
||
result++;
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/*
|
||
* Compare two application identifiers.
|
||
*/
|
||
inline int
|
||
aidcmp (const MadAid left, const MadAid right)
|
||
{
|
||
return ((left.function_cluster_code - right.function_cluster_code) << 8) | (left.application_code - right.application_code);
|
||
}
|
||
|
||
|
||
/*
|
||
* Card publisher functions (MAD owner).
|
||
*/
|
||
|
||
/*
|
||
* Allocates a new application into a MAD.
|
||
*/
|
||
MifareClassicSectorNumber *
|
||
mifare_application_alloc (Mad mad, MadAid aid, size_t size)
|
||
{
|
||
uint8_t sector_map[40];
|
||
MifareClassicSectorNumber sector;
|
||
MadAid sector_aid;
|
||
MifareClassicSectorNumber *res = NULL;
|
||
ssize_t s = size;
|
||
|
||
/*
|
||
* Ensure the card does not already have the application registered.
|
||
*/
|
||
MifareClassicSectorNumber *found;
|
||
if ((found = mifare_application_find (mad, aid))) {
|
||
free (found);
|
||
return NULL;
|
||
}
|
||
|
||
for (size_t i = 0; i < sizeof (sector_map); i++)
|
||
sector_map[i] = 0;
|
||
|
||
/*
|
||
* Try to minimize lost space and allocate as many large pages as possible
|
||
* when the target is a Mifare Classic 4k.
|
||
*/
|
||
MadAid free_aid = { 0x00, 0x00 };
|
||
if (mad_get_version (mad) == 2) {
|
||
sector = 32;
|
||
while ((s >= 12*16) && sector < 40) {
|
||
mad_get_aid (mad, sector, §or_aid);
|
||
if (0 == aidcmp (sector_aid, free_aid)) {
|
||
sector_map[sector] = 1;
|
||
s -= 15*16;
|
||
}
|
||
sector++;
|
||
}
|
||
}
|
||
|
||
sector = FIRST_SECTOR;
|
||
MifareClassicSectorNumber s_max = (mad_get_version (mad) == 1) ? 15 : 31;
|
||
while ((s > 0) && (sector <= s_max)) {
|
||
if (mad_sector_reserved (sector))
|
||
continue;
|
||
mad_get_aid (mad, sector, §or_aid);
|
||
if (0 == aidcmp (sector_aid, free_aid)) {
|
||
sector_map[sector] = 1;
|
||
s -= 3*16;
|
||
}
|
||
sector++;
|
||
}
|
||
|
||
/*
|
||
* Ensure the remaining free space is suficient before destroying the MAD.
|
||
*/
|
||
if (s > 0)
|
||
return NULL;
|
||
|
||
int n = 0;
|
||
for (size_t i = FIRST_SECTOR; i < sizeof (sector_map); i++)
|
||
if (sector_map[i])
|
||
n++;
|
||
|
||
if (!(res = malloc (sizeof (*res) * (n+1))))
|
||
return NULL;
|
||
|
||
n = 0;
|
||
for (size_t i = FIRST_SECTOR; i < sizeof (sector_map); i++)
|
||
if (sector_map[i]) {
|
||
res[n] = i;
|
||
mad_set_aid (mad, i, aid);
|
||
n++;
|
||
}
|
||
|
||
res[n] = 0;
|
||
|
||
/* Return the list of allocated sectors */
|
||
return res;
|
||
}
|
||
|
||
/*
|
||
* Remove an application from a MAD.
|
||
*/
|
||
void
|
||
mifare_application_free (Mad mad, MadAid aid)
|
||
{
|
||
MifareClassicSectorNumber *sectors = mifare_application_find (mad, aid);
|
||
MifareClassicSectorNumber *p = sectors;
|
||
MadAid free_aid = { 0x00, 0x00 };
|
||
while (*p) {
|
||
mad_set_aid (mad, *p, free_aid);
|
||
p++;
|
||
}
|
||
|
||
free (sectors);
|
||
}
|
||
|
||
|
||
/*
|
||
* Application owner functions.
|
||
*/
|
||
|
||
/*
|
||
* Get all sector numbers of an application from the provided MAD.
|
||
*/
|
||
MifareClassicSectorNumber *
|
||
mifare_application_find (Mad mad, MadAid aid)
|
||
{
|
||
MifareClassicSectorNumber *res = NULL;
|
||
size_t res_count = count_aids (mad, aid);
|
||
|
||
if (res_count)
|
||
res = malloc (sizeof (*res) * (res_count + 1));
|
||
|
||
size_t r = FIRST_SECTOR, w = 0;
|
||
if (res) {
|
||
/* Fill in the result */
|
||
MadAid c_aid;
|
||
while (w < res_count) {
|
||
mad_get_aid (mad, r, &c_aid);
|
||
if (0 == aidcmp (c_aid, aid)) {
|
||
res[w++] = r;
|
||
}
|
||
r++;
|
||
}
|
||
res[w] = 0;
|
||
}
|
||
|
||
return res;
|
||
}
|
||
|
||
ssize_t
|
||
mifare_application_read (MifareTag tag, Mad mad, const MadAid aid, void *buf, size_t nbytes, const MifareClassicKey key, const MifareClassicKeyType key_type)
|
||
{
|
||
ssize_t res = 0;
|
||
|
||
MifareClassicSectorNumber *sectors = mifare_application_find (mad, aid);
|
||
MifareClassicSectorNumber *s = sectors;
|
||
|
||
if (!sectors)
|
||
return errno = EBADF, -1;
|
||
|
||
while (*s && nbytes && (res >= 0)) {
|
||
MifareClassicBlockNumber first_block = mifare_classic_sector_first_block (*s);
|
||
MifareClassicBlockNumber last_block = mifare_classic_sector_last_block (*s);
|
||
|
||
MifareClassicBlockNumber b = first_block;
|
||
MifareClassicBlock block;
|
||
|
||
if (mifare_classic_authenticate (tag, first_block, key, key_type) < 0) {
|
||
res = -1;
|
||
break;
|
||
}
|
||
|
||
while ((b < last_block) && nbytes) {
|
||
size_t n = MIN (nbytes, 16);
|
||
|
||
if (mifare_classic_read (tag, b, &block) < 0) {
|
||
res = -1;
|
||
break;
|
||
}
|
||
memcpy ((uint8_t *)buf + res, &block, n);
|
||
|
||
nbytes -= n;
|
||
res += n;
|
||
|
||
b++;
|
||
}
|
||
|
||
s++;
|
||
}
|
||
|
||
free (sectors);
|
||
return res;
|
||
}
|
||
|
||
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)
|
||
{
|
||
ssize_t res = 0;
|
||
|
||
MifareClassicSectorNumber *sectors = mifare_application_find (mad, aid);
|
||
MifareClassicSectorNumber *s = sectors;
|
||
|
||
if (!sectors)
|
||
return errno = EBADF, -1;
|
||
|
||
while (*s && nbytes && (res >= 0)) {
|
||
MifareClassicBlockNumber first_block = mifare_classic_sector_first_block (*s);
|
||
MifareClassicBlockNumber last_block = mifare_classic_sector_last_block (*s);
|
||
|
||
MifareClassicBlockNumber b = first_block;
|
||
MifareClassicBlock block;
|
||
|
||
if (mifare_classic_authenticate (tag, first_block, key, key_type) < 0) {
|
||
res = -1;
|
||
break;
|
||
}
|
||
|
||
while ((b < last_block) && nbytes) {
|
||
size_t n = MIN (nbytes, 16);
|
||
// Avoid overwriting existing data with uninitialized memory.
|
||
if (n < 16) {
|
||
if (mifare_classic_read (tag, b, &block) < 0) {
|
||
res = -1;
|
||
break;
|
||
}
|
||
}
|
||
|
||
memcpy (&block, (uint8_t *)buf + res, n);
|
||
if (mifare_classic_write (tag, b, block) < 0) {
|
||
res = -1;
|
||
break;
|
||
}
|
||
|
||
nbytes -= n;
|
||
res += n;
|
||
|
||
b++;
|
||
}
|
||
|
||
s++;
|
||
}
|
||
|
||
free (sectors);
|
||
return res;
|
||
|
||
}
|