2012-03-13 22:47:47 +01:00
/*-
* Copyright ( C ) 2010 , Audrey Diacre .
*
* 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/>
*/
2012-03-15 00:05:10 +01:00
# include "config.h"
2012-03-13 22:47:47 +01:00
# include <err.h>
# include <stdlib.h>
2012-03-15 00:05:10 +01:00
# include <string.h>
# include <unistd.h>
2012-03-13 22:47:47 +01:00
# include <freefare.h>
/*
* This example was written based on information provided by the
* following documents :
*
2012-05-15 14:34:17 +02:00
* AN11004 Mifare DESFire as Type 4 Tag
2012-03-13 22:47:47 +01:00
* NFC Forum Type 4 Tag Extensions for Mifare DESFire
* Rev . 1.1 - 21 August 2007
2012-05-15 14:34:17 +02:00
* Rev . 2.2 - 4 January 2012
2012-03-13 22:47:47 +01:00
*
*/
uint8_t key_data_picc [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
uint8_t key_data_app [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
2012-03-15 00:05:10 +01:00
struct {
bool interactive ;
} create_options = {
. interactive = true
} ;
2012-05-18 18:18:45 +02:00
static void
2012-03-15 00:05:10 +01:00
usage ( char * progname )
{
2012-03-15 09:56:14 +01:00
fprintf ( stderr , " This application turns Mifare DESFire targets into NFC Forum Type 4 Tags. \n " ) ;
2012-03-15 00:52:05 +01:00
fprintf ( stderr , " usage: %s [-y] [-K 11223344AABBCCDD] \n " , progname ) ;
2012-03-15 00:05:10 +01:00
fprintf ( stderr , " \n Options: \n " ) ;
fprintf ( stderr , " -y Do not ask for confirmation \n " ) ;
2012-03-15 00:52:05 +01:00
fprintf ( stderr , " -K Provide another PICC key than the default one \n " ) ;
2012-03-15 00:05:10 +01:00
}
2012-03-13 22:47:47 +01:00
int
main ( int argc , char * argv [ ] )
{
2012-03-15 00:05:10 +01:00
int ch ;
2012-03-13 22:47:47 +01:00
int error = EXIT_SUCCESS ;
nfc_device * device = NULL ;
2015-05-11 18:48:10 +02:00
FreefareTag * tags = NULL ;
2012-03-13 22:47:47 +01:00
2012-03-15 00:05:10 +01:00
while ( ( ch = getopt ( argc , argv , " hyK: " ) ) ! = - 1 ) {
switch ( ch ) {
case ' h ' :
usage ( argv [ 0 ] ) ;
exit ( EXIT_SUCCESS ) ;
break ;
case ' y ' :
create_options . interactive = false ;
break ;
case ' K ' :
if ( strlen ( optarg ) ! = 16 ) {
usage ( argv [ 0 ] ) ;
exit ( EXIT_FAILURE ) ;
}
uint64_t n = strtoull ( optarg , NULL , 16 ) ;
int i ;
for ( i = 7 ; i > = 0 ; i - - ) {
key_data_picc [ i ] = ( uint8_t ) n ;
n > > = 8 ;
}
break ;
default :
usage ( argv [ 0 ] ) ;
exit ( EXIT_FAILURE ) ;
}
}
// Remaining args, if any, are in argv[optind .. (argc-1)]
2012-03-13 22:47:47 +01:00
nfc_connstring devices [ 8 ] ;
size_t device_count ;
2012-12-23 22:30:10 +01:00
nfc_context * context ;
nfc_init ( & context ) ;
2013-03-30 18:07:34 +01:00
if ( context = = NULL )
errx ( EXIT_FAILURE , " Unable to init libnfc (malloc) " ) ;
2012-12-23 22:30:10 +01:00
device_count = nfc_list_devices ( context , devices , 8 ) ;
2012-03-13 22:47:47 +01:00
if ( device_count < = 0 )
errx ( EXIT_FAILURE , " No NFC device found. " ) ;
for ( size_t d = 0 ; d < device_count ; d + + ) {
2012-12-23 22:30:10 +01:00
device = nfc_open ( context , devices [ d ] ) ;
2015-05-12 13:19:00 +02:00
2012-03-13 22:47:47 +01:00
if ( ! device ) {
warnx ( " nfc_open() failed. " ) ;
error = EXIT_FAILURE ;
continue ;
}
tags = freefare_get_tags ( device ) ;
if ( ! tags ) {
nfc_close ( device ) ;
errx ( EXIT_FAILURE , " Error listing tags. " ) ;
}
for ( int i = 0 ; ( ! error ) & & tags [ i ] ; i + + ) {
2015-05-12 13:29:21 +02:00
if ( MIFARE_DESFIRE ! = freefare_get_tag_type ( tags [ i ] ) )
2012-03-13 22:47:47 +01:00
continue ;
char * tag_uid = freefare_get_tag_uid ( tags [ i ] ) ;
char buffer [ BUFSIZ ] ;
2012-05-15 14:34:17 +02:00
int res ;
2012-03-13 22:47:47 +01:00
2012-05-15 14:34:17 +02:00
res = mifare_desfire_connect ( tags [ i ] ) ;
if ( res < 0 ) {
warnx ( " Can't connect to Mifare DESFire target. " ) ;
error = EXIT_FAILURE ;
break ;
}
// We've to track DESFire version as NDEF mapping is different
struct mifare_desfire_version_info info ;
res = mifare_desfire_get_version ( tags [ i ] , & info ) ;
if ( res < 0 ) {
freefare_perror ( tags [ i ] , " mifare_desfire_get_version " ) ;
error = 1 ;
break ;
}
printf ( " Found %s with UID %s and software v%d.%d \n " , freefare_get_tag_friendly_name ( tags [ i ] ) , tag_uid , info . software . version_major , info . software . version_minor ) ;
2012-03-15 00:05:10 +01:00
bool create_ndef = true ;
2012-05-15 14:34:17 +02:00
int ndef_mapping ;
switch ( info . software . version_major ) {
case 0 :
ndef_mapping = 1 ;
break ;
case 1 :
ndef_mapping = 2 ;
2014-04-13 19:47:56 +02:00
break ;
2012-05-15 14:34:17 +02:00
default : // newer version? let's assume it supports latest mapping too
2014-04-13 19:47:56 +02:00
warnx ( " Software version not supported. Assuming backward compatibility with version 1. " ) ;
2012-05-15 14:34:17 +02:00
ndef_mapping = 2 ;
}
2012-03-15 00:05:10 +01:00
if ( create_options . interactive ) {
2012-05-15 14:34:17 +02:00
printf ( " Create NDEF app v%d [yN] " , ndef_mapping ) ;
2012-03-15 00:05:10 +01:00
fgets ( buffer , BUFSIZ , stdin ) ;
create_ndef = ( ( buffer [ 0 ] = = ' y ' ) | | ( buffer [ 0 ] = = ' Y ' ) ) ;
} else {
printf ( " \n " ) ;
}
if ( create_ndef ) {
2012-03-13 22:47:47 +01:00
/* Initialised Formatting Procedure. See section 6.5.1 and 8.1 of Mifare DESFire as Type 4 Tag document*/
// Send Mifare DESFire Select Application with AID equal to 000000h to select the PICC level
res = mifare_desfire_select_application ( tags [ i ] , NULL ) ;
if ( res < 0 )
errx ( EXIT_FAILURE , " Application selection failed " ) ;
MifareDESFireKey key_picc ;
MifareDESFireKey key_app ;
key_picc = mifare_desfire_des_key_new_with_version ( key_data_picc ) ;
key_app = mifare_desfire_des_key_new_with_version ( key_data_app ) ;
// Authentication with PICC master key MAY be needed to issue ChangeKeySettings command
res = mifare_desfire_authenticate ( tags [ i ] , 0 , key_picc ) ;
if ( res < 0 )
errx ( EXIT_FAILURE , " Authentication with PICC master key failed " ) ;
2012-05-15 23:22:25 +02:00
MifareDESFireAID aid ;
if ( ndef_mapping = = 1 ) {
2012-05-15 23:22:35 +02:00
uint8_t key_settings ;
uint8_t max_keys ;
mifare_desfire_get_key_settings ( tags [ i ] , & key_settings , & max_keys ) ;
if ( ( key_settings & 0x08 ) = = 0x08 ) {
// Send Mifare DESFire ChangeKeySetting to change the PICC master key settings into :
// bit7-bit4 equal to 0000b
// bit3 equal to Xb, the configuration of the PICC master key MAY be changeable or frozen
// bit2 equal to 0b, CreateApplication and DeleteApplication commands are allowed with PICC master key authentication
// bit1 equal to 0b, GetApplicationIDs, and GetKeySettings are allowed with PICC master key authentication
// bit0 equal to Xb, PICC masterkey MAY be frozen or changeable
res = mifare_desfire_change_key_settings ( tags [ i ] , 0x09 ) ;
if ( res < 0 )
errx ( EXIT_FAILURE , " ChangeKeySettings failed " ) ;
}
2012-05-15 23:22:25 +02:00
// Mifare DESFire Create Application with AID equal to EEEE10h, key settings equal to 0x09, NumOfKeys equal to 01h
aid = mifare_desfire_aid_new ( 0xEEEE10 ) ;
res = mifare_desfire_create_application ( tags [ i ] , aid , 0x09 , 1 ) ;
if ( res < 0 )
errx ( EXIT_FAILURE , " Application creation failed. Try mifare-desfire-format before running %s. " , argv [ 0 ] ) ;
// Mifare DESFire SelectApplication (Select previously creates application)
res = mifare_desfire_select_application ( tags [ i ] , aid ) ;
if ( res < 0 )
errx ( EXIT_FAILURE , " Application selection failed " ) ;
free ( aid ) ;
// Authentication with NDEF Tag Application master key (Authentication with key 0)
res = mifare_desfire_authenticate ( tags [ i ] , 0 , key_app ) ;
if ( res < 0 )
errx ( EXIT_FAILURE , " Authentication with NDEF Tag Application master key failed " ) ;
// Mifare DESFire ChangeKeySetting with key settings equal to 00001001b
res = mifare_desfire_change_key_settings ( tags [ i ] , 0x09 ) ;
if ( res < 0 )
errx ( EXIT_FAILURE , " ChangeKeySettings failed " ) ;
// Mifare DESFire CreateStdDataFile with FileNo equal to 03h (CC File DESFire FID), ComSet equal to 00h,
// AccesRights equal to E000h, File Size bigger equal to 00000Fh
res = mifare_desfire_create_std_data_file ( tags [ i ] , 0x03 , MDCM_PLAIN , 0xE000 , 0x00000F ) ;
2012-03-13 22:47:47 +01:00
if ( res < 0 )
errx ( EXIT_FAILURE , " CreateStdDataFile failed " ) ;
2012-05-15 23:22:25 +02:00
// Mifare DESFire WriteData to write the content of the CC File with CClEN equal to 000Fh,
// Mapping Version equal to 10h,MLe equal to 003Bh, MLc equal to 0034h, and NDEF File Control TLV
// equal to T =04h, L=06h, V=E1 04 (NDEF ISO FID=E104h) 0E E0 (NDEF File size =3808 Bytes) 00 (free read access)
// 00 free write access
uint8_t capability_container_file_content [ 15 ] = {
0x00 , 0x0F , // CCLEN: Size of this capability container.CCLEN values are between 000Fh and FFFEh
0x10 , // Mapping version
0x00 , 0x3B , // MLe: Maximum data size that can be read using a single ReadBinary command. MLe = 000Fh-FFFFh
0x00 , 0x34 , // MLc: Maximum data size that can be sent using a single UpdateBinary command. MLc = 0001h-FFFFh
0x04 , 0x06 , // T & L of NDEF File Control TLV, followed by 6 bytes of V:
0xE1 , 0x04 , // File Identifier of NDEF File
0x0E , 0xE0 , // Maximum NDEF File size of 3808 bytes
0x00 , // free read access
0x00 // free write acces
} ;
res = mifare_desfire_write_data ( tags [ i ] , 0x03 , 0 , sizeof ( capability_container_file_content ) , capability_container_file_content ) ;
if ( res > 0 ) {
// Mifare DESFire CreateStdDataFile with FileNo equal to 04h (NDEF FileDESFire FID), CmmSet equal to 00h, AccessRigths
// equal to EEE0h, FileSize equal to 000EE0h (3808 Bytes)
res = mifare_desfire_create_std_data_file ( tags [ i ] , 0x04 , MDCM_PLAIN , 0xEEE0 , 0x000EE0 ) ;
if ( res < 0 )
errx ( EXIT_FAILURE , " CreateStdDataFile failed " ) ;
} else {
errx ( EXIT_FAILURE , " Write CC file content failed " ) ;
}
}
else if ( ndef_mapping = = 2 ) {
// Mifare DESFire Create Application with AID equal to 000001h, key settings equal to 0x0F, NumOfKeys equal to 01h,
// 2 bytes File Identifiers supported, File-ID equal to E110h
aid = mifare_desfire_aid_new ( 0x000001 ) ;
uint8_t app [ ] = { 0xd2 , 0x76 , 0x00 , 0x00 , 0x85 , 0x01 , 0x01 } ;
res = mifare_desfire_create_application_iso ( tags [ i ] , aid , 0x0F , 0x21 , 0 , 0xE110 , app , sizeof ( app ) ) ;
if ( res < 0 )
errx ( EXIT_FAILURE , " Application creation failed. Try mifare-desfire-format before running %s. " , argv [ 0 ] ) ;
// Mifare DESFire SelectApplication (Select previously creates application)
res = mifare_desfire_select_application ( tags [ i ] , aid ) ;
if ( res < 0 )
errx ( EXIT_FAILURE , " Application selection failed " ) ;
free ( aid ) ;
// Authentication with NDEF Tag Application master key (Authentication with key 0)
res = mifare_desfire_authenticate ( tags [ i ] , 0 , key_app ) ;
if ( res < 0 )
errx ( EXIT_FAILURE , " Authentication with NDEF Tag Application master key failed " ) ;
// Mifare DESFire CreateStdDataFile with FileNo equal to 01h (DESFire FID), ComSet equal to 00h,
// AccesRights equal to E000h, File Size bigger equal to 00000Fh, ISO File ID equal to E103h
res = mifare_desfire_create_std_data_file_iso ( tags [ i ] , 0x01 , MDCM_PLAIN , 0xE000 , 0x00000F , 0xE103 ) ;
if ( res < 0 )
errx ( EXIT_FAILURE , " CreateStdDataFileIso failed " ) ;
// Mifare DESFire WriteData to write the content of the CC File with CClEN equal to 000Fh,
// Mapping Version equal to 20h,MLe equal to 003Bh, MLc equal to 0034h, and NDEF File Control TLV
// equal to T =04h, L=06h, V=E1 04 (NDEF ISO FID=E104h) 0xNNNN (NDEF File size = 0x0800/0x1000/0x1E00 bytes)
// 00 (free read access) 00 free write access
uint8_t capability_container_file_content [ 15 ] = {
0x00 , 0x0F , // CCLEN: Size of this capability container.CCLEN values are between 000Fh and FFFEh
0x20 , // Mapping version
0x00 , 0x3B , // MLe: Maximum data size that can be read using a single ReadBinary command. MLe = 000Fh-FFFFh
0x00 , 0x34 , // MLc: Maximum data size that can be sent using a single UpdateBinary command. MLc = 0001h-FFFFh
0x04 , 0x06 , // T & L of NDEF File Control TLV, followed by 6 bytes of V:
0xE1 , 0x04 , // File Identifier of NDEF File
2012-05-15 23:22:35 +02:00
0x04 , 0x00 , // Maximum NDEF File size of 1024 bytes
2012-05-15 23:22:25 +02:00
0x00 , // free read access
0x00 // free write acces
} ;
2012-05-15 23:22:35 +02:00
uint16_t ndefmaxsize = 0x0800 ;
2012-05-17 09:40:29 +02:00
uint16_t announcedsize = 1 < < ( info . software . storage_size > > 1 ) ;
2012-05-15 23:22:35 +02:00
if ( announcedsize > = 0x1000 )
ndefmaxsize = 0x1000 ;
if ( announcedsize > = 0x1E00 )
ndefmaxsize = 0x1E00 ;
capability_container_file_content [ 11 ] = ndefmaxsize > > 8 ;
capability_container_file_content [ 12 ] = ndefmaxsize & 0xFF ;
2012-05-15 23:22:25 +02:00
res = mifare_desfire_write_data ( tags [ i ] , 0x01 , 0 , sizeof ( capability_container_file_content ) , capability_container_file_content ) ;
if ( res > 0 ) {
// Mifare DESFire CreateStdDataFile with FileNo equal to 02h (DESFire FID), CmmSet equal to 00h, AccessRigths
2012-05-15 23:22:35 +02:00
// equal to EEE0h, FileSize equal to ndefmaxsize (0x000800, 0x001000 or 0x001E00)
res = mifare_desfire_create_std_data_file_iso ( tags [ i ] , 0x02 , MDCM_PLAIN , 0xEEE0 , ndefmaxsize , 0xE104 ) ;
2012-05-15 23:22:25 +02:00
if ( res < 0 )
errx ( EXIT_FAILURE , " CreateStdDataFileIso failed " ) ;
} else {
errx ( EXIT_FAILURE , " Write CC file content failed " ) ;
}
2012-03-13 22:47:47 +01:00
}
mifare_desfire_key_free ( key_picc ) ;
mifare_desfire_key_free ( key_app ) ;
}
2012-05-15 14:34:17 +02:00
mifare_desfire_disconnect ( tags [ i ] ) ;
2012-03-13 22:47:47 +01:00
free ( tag_uid ) ;
}
freefare_free_tags ( tags ) ;
nfc_close ( device ) ;
}
2012-12-23 22:30:10 +01:00
nfc_exit ( context ) ;
2012-03-13 22:47:47 +01:00
exit ( error ) ;
}