2010-09-28 18:15:59 +02:00
/*-
2010-11-17 15:27:11 +01:00
* Public platform independent Near Field Communication ( NFC ) library examples
*
* Copyright ( C ) 2010 , Roel Verdult , Romuald Conty
2011-03-12 16:56:57 +01:00
* Copyright ( C ) 2011 , Romain Tartière , Romuald Conty
2010-11-17 15:27:11 +01:00
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
* 1 ) Redistributions of source code must retain the above copyright notice ,
* this list of conditions and the following disclaimer .
* 2 ) Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
2010-09-28 18:15:59 +02:00
*
2010-11-17 15:27:11 +01:00
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS "
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR
* CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS
* INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN
* CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE )
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE .
*
* Note that this license only applies on the examples , NFC library itself is under LGPL
2010-09-28 18:15:59 +02:00
*
*/
/**
2010-10-08 17:37:21 +02:00
* @ file nfc - emulate - forum - tag4 . c
2010-10-14 13:53:27 +02:00
* @ brief Emulates a NFC Forum Tag Type 4 with a NDEF message
2010-09-28 18:15:59 +02:00
*/
2010-10-06 23:04:52 +02:00
// Notes & differences with nfc-emulate-tag:
// - This example only works with PN532 because it relies on
// its internal handling of ISO14443-4 specificities.
// - Thanks to this internal handling & injection of WTX frames,
// this example works on readers very strict on timing
2010-09-28 18:15:59 +02:00
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif // HAVE_CONFIG_H
2011-03-11 20:01:28 +01:00
# include <sys/types.h>
# include <sys/stat.h>
2011-03-09 19:17:05 +01:00
# include <errno.h>
2011-03-09 14:31:12 +01:00
# include <signal.h>
2010-09-28 18:15:59 +02:00
# include <stdio.h>
# include <stdlib.h>
# include <stddef.h>
# include <stdint.h>
# include <string.h>
# include <nfc/nfc.h>
2011-03-13 15:31:06 +01:00
# include <nfc/nfc-emulation.h>
2010-09-28 18:15:59 +02:00
# include "nfc-utils.h"
static nfc_device_t * pnd ;
static bool quiet_output = false ;
# define SYMBOL_PARAM_fISO14443_4_PICC 0x20
2011-03-09 19:17:05 +01:00
typedef enum { NONE , CC_FILE , NDEF_FILE } file ;
2011-03-13 15:40:36 +01:00
struct nfcforum_tag4_ndef_data {
2011-03-09 19:17:05 +01:00
uint8_t * ndef_file ;
size_t ndef_file_len ;
2011-03-13 15:40:36 +01:00
} ;
struct nfcforum_tag4_state_machine_data {
2011-03-09 19:17:05 +01:00
file current_file ;
} ;
uint8_t nfcforum_capability_container [ ] = {
0x00 , 0x0F , /* CCLEN 15 bytes */
0x10 , /* Mapping version 1.0 */
2011-04-22 19:32:07 +02:00
0x00 , 0x54 , /* MLe Maximum R-ADPU data size */
// Notes:
// - I (Romuald) don't know why Nokia 6212 Classic refuses the NDEF message if MLe is more than 0xFD (any suggests are welcome);
// - ARYGON devices doesn't support extended frame sending, consequently these devices can't sent more than 0xFE bytes as APDU, so 0xFB APDU data bytes.
// - I (Romuald) don't know why ARYGON device doesn't ACK when MLe > 0x54 (ARYGON frame lenght = 0xC2 (192 bytes))
0x00 , 0xFF , /* MLc Maximum C-ADPU data size */
2011-03-09 19:17:05 +01:00
0x04 , /* T field of the NDEF File-Control TLV */
0x06 , /* L field of the NDEF File-Control TLV */
/* V field of the NDEF File-Control TLV */
0xE1 , 0x04 , /* File identifier */
2011-03-12 16:01:04 +01:00
0xFF , 0xFE , /* Maximum NDEF Size */
2011-03-09 19:17:05 +01:00
0x00 , /* NDEF file read access condition */
2011-03-12 16:01:04 +01:00
0x00 , /* NDEF file write access condition */
2011-03-09 19:17:05 +01:00
} ;
/* C-ADPU offsets */
# define CLA 0
# define INS 1
# define P1 2
# define P2 3
# define LC 4
# define DATA 5
# define ISO144434A_RATS 0xE0
int
nfcforum_tag4_io ( struct nfc_emulator * emulator , const byte_t * data_in , const size_t data_in_len , byte_t * data_out , const size_t data_out_len )
{
int res = 0 ;
2011-03-13 15:40:36 +01:00
struct nfcforum_tag4_ndef_data * ndef_data = ( struct nfcforum_tag4_ndef_data * ) ( emulator - > user_data ) ;
struct nfcforum_tag4_state_machine_data * state_machine_data = ( struct nfcforum_tag4_state_machine_data * ) ( emulator - > state_machine - > data ) ;
2011-03-09 19:17:05 +01:00
2011-06-27 11:14:19 +02:00
if ( data_in_len = = 0 ) {
// No input data, nothing to do
return res ;
}
2011-03-09 19:17:05 +01:00
// Show transmitted command
if ( ! quiet_output ) {
printf ( " In: " ) ;
print_hex ( data_in , data_in_len ) ;
}
if ( data_in_len > = 4 ) {
if ( data_in [ CLA ] ! = 0x00 )
return - ENOTSUP ;
# define ISO7816_SELECT 0xA4
# define ISO7816_READ_BINARY 0xB0
# define ISO7816_UPDATE_BINARY 0xD6
switch ( data_in [ INS ] ) {
case ISO7816_SELECT :
switch ( data_in [ P1 ] ) {
case 0x00 : /* Select by ID */
if ( ( data_in [ P2 ] | 0x0C ) ! = 0x0C )
return - ENOTSUP ;
const uint8_t ndef_capability_container [ ] = { 0xE1 , 0x03 } ;
const uint8_t ndef_file [ ] = { 0xE1 , 0x04 } ;
if ( ( data_in [ LC ] = = sizeof ( ndef_capability_container ) ) & & ( 0 = = memcmp ( ndef_capability_container , data_in + DATA , data_in [ LC ] ) ) ) {
memcpy ( data_out , " \x90 \x00 " , res = 2 ) ;
2011-03-13 15:40:36 +01:00
state_machine_data - > current_file = CC_FILE ;
2011-03-09 19:17:05 +01:00
} else if ( ( data_in [ LC ] = = sizeof ( ndef_file ) ) & & ( 0 = = memcmp ( ndef_file , data_in + DATA , data_in [ LC ] ) ) ) {
memcpy ( data_out , " \x90 \x00 " , res = 2 ) ;
2011-03-13 15:40:36 +01:00
state_machine_data - > current_file = NDEF_FILE ;
2011-03-09 19:17:05 +01:00
} else {
memcpy ( data_out , " \x6a \x00 " , res = 2 ) ;
2011-03-13 15:40:36 +01:00
state_machine_data - > current_file = NONE ;
2011-03-09 19:17:05 +01:00
}
break ;
case 0x04 : /* Select by name */
if ( data_in [ P2 ] ! = 0x00 )
return - ENOTSUP ;
const uint8_t ndef_tag_application_name [ ] = { 0xD2 , 0x76 , 0x00 , 0x00 , 0x85 , 0x01 , 0x00 } ;
if ( ( data_in [ LC ] = = sizeof ( ndef_tag_application_name ) ) & & ( 0 = = memcmp ( ndef_tag_application_name , data_in + DATA , data_in [ LC ] ) ) )
memcpy ( data_out , " \x90 \x00 " , res = 2 ) ;
else
memcpy ( data_out , " \x6a \x82 " , res = 2 ) ;
break ;
default :
return - ENOTSUP ;
}
break ;
case ISO7816_READ_BINARY :
2011-03-13 15:31:06 +01:00
if ( ( size_t ) ( data_in [ LC ] + 2 ) > data_out_len ) {
2011-03-09 19:17:05 +01:00
return - ENOSPC ;
}
2011-03-13 15:40:36 +01:00
switch ( state_machine_data - > current_file ) {
2011-03-09 19:17:05 +01:00
case NONE :
memcpy ( data_out , " \x6a \x82 " , res = 2 ) ;
break ;
case CC_FILE :
memcpy ( data_out , nfcforum_capability_container + ( data_in [ P1 ] < < 8 ) + data_in [ P2 ] , data_in [ LC ] ) ;
memcpy ( data_out + data_in [ LC ] , " \x90 \x00 " , 2 ) ;
res = data_in [ LC ] + 2 ;
break ;
case NDEF_FILE :
2011-03-13 15:40:36 +01:00
memcpy ( data_out , ndef_data - > ndef_file + ( data_in [ P1 ] < < 8 ) + data_in [ P2 ] , data_in [ LC ] ) ;
2011-03-09 19:17:05 +01:00
memcpy ( data_out + data_in [ LC ] , " \x90 \x00 " , 2 ) ;
res = data_in [ LC ] + 2 ;
break ;
}
break ;
case ISO7816_UPDATE_BINARY :
2011-03-13 15:40:36 +01:00
memcpy ( ndef_data - > ndef_file + ( data_in [ P1 ] < < 8 ) + data_in [ P2 ] , data_in + DATA , data_in [ LC ] ) ;
2011-03-12 16:01:04 +01:00
if ( ( data_in [ P1 ] < < 8 ) + data_in [ P2 ] = = 0 ) {
2011-03-13 15:40:36 +01:00
ndef_data - > ndef_file_len = ( ndef_data - > ndef_file [ 0 ] < < 8 ) + ndef_data - > ndef_file [ 1 ] + 2 ;
2011-03-12 16:01:04 +01:00
}
memcpy ( data_out , " \x90 \x00 " , res = 2 ) ;
2011-03-09 19:17:05 +01:00
break ;
default : // Unknown
if ( ! quiet_output ) {
printf ( " Unknown frame, emulated target abort. \n " ) ;
}
res = - ENOTSUP ;
}
} else {
res = - ENOTSUP ;
}
// Show transmitted command
if ( ! quiet_output ) {
2011-06-27 11:14:19 +02:00
if ( res < 0 ) {
ERR ( " %s (%d) " , strerror ( - res ) , - res ) ;
} else {
printf ( " Out: " ) ;
2011-03-09 19:17:05 +01:00
print_hex ( data_out , res ) ;
2011-06-27 11:14:19 +02:00
}
2011-03-09 19:17:05 +01:00
}
return res ;
}
2011-03-09 14:31:12 +01:00
void stop_emulation ( int sig )
{
( void ) sig ;
if ( pnd )
nfc_abort_command ( pnd ) ;
else
exit ( EXIT_FAILURE ) ;
}
2011-03-11 20:01:28 +01:00
size_t
2011-03-13 15:40:36 +01:00
ndef_message_load ( char * filename , struct nfcforum_tag4_ndef_data * tag_data )
2010-09-28 18:15:59 +02:00
{
2011-03-11 20:01:28 +01:00
struct stat sb ;
if ( stat ( filename , & sb ) < 0 )
return 0 ;
2010-09-28 18:15:59 +02:00
2011-03-11 20:01:28 +01:00
/* Check file size */
if ( sb . st_size > 0xFFFF ) {
errx ( EXIT_FAILURE , " file size too large '%s' " , filename ) ;
2010-09-28 18:15:59 +02:00
}
2011-03-11 20:01:28 +01:00
tag_data - > ndef_file_len = sb . st_size + 2 ;
tag_data - > ndef_file [ 0 ] = ( uint8_t ) ( sb . st_size > > 8 ) ;
tag_data - > ndef_file [ 1 ] = ( uint8_t ) ( sb . st_size ) ;
FILE * F ;
if ( ! ( F = fopen ( filename , " r " ) ) )
err ( EXIT_FAILURE , " fopen (%s, \" r \" ) " , filename ) ;
if ( 1 ! = fread ( tag_data - > ndef_file + 2 , sb . st_size , 1 , F ) )
err ( EXIT_FAILURE , " Can't read from %s " , filename ) ;
fclose ( F ) ;
return sb . st_size ;
}
size_t
2011-03-13 15:40:36 +01:00
ndef_message_save ( char * filename , struct nfcforum_tag4_ndef_data * tag_data )
2011-03-11 20:01:28 +01:00
{
FILE * F ;
if ( ! ( F = fopen ( filename , " w " ) ) )
2011-03-12 16:01:04 +01:00
err ( EXIT_FAILURE , " fopen (%s, w) " , filename ) ;
2011-03-11 20:01:28 +01:00
if ( 1 ! = fwrite ( tag_data - > ndef_file + 2 , tag_data - > ndef_file_len - 2 , 1 , F ) ) {
2011-03-12 16:01:04 +01:00
err ( EXIT_FAILURE , " fwrite (%lu) " , tag_data - > ndef_file_len - 2 ) ;
2011-03-11 20:01:28 +01:00
}
fclose ( F ) ;
return tag_data - > ndef_file_len - 2 ;
}
2010-10-04 14:46:03 +02:00
2011-03-12 16:01:04 +01:00
void
usage ( char * progname )
{
fprintf ( stderr , " usage: %s [infile [outfile]] \n " , progname ) ;
}
2011-03-11 20:01:28 +01:00
int
main ( int argc , char * argv [ ] )
{
2010-10-04 14:46:03 +02:00
nfc_target_t nt = {
2011-02-28 10:47:31 +01:00
. nm = {
. nmt = NMT_ISO14443A ,
. nbr = NBR_UNDEFINED , // Will be updated by nfc_target_init()
} ,
. nti = {
. nai = {
. abtAtqa = { 0x00 , 0x04 } ,
. abtUid = { 0x08 , 0x00 , 0xb0 , 0x0b } ,
. szUidLen = 4 ,
2011-03-09 14:31:12 +01:00
. btSak = 0x20 ,
2011-03-09 19:17:05 +01:00
. abtAts = { 0x75 , 0x33 , 0x92 , 0x03 } , /* Not used by PN532 */
2011-03-09 14:31:12 +01:00
. szAtsLen = 4 ,
2011-02-28 10:47:31 +01:00
} ,
} ,
2010-10-04 14:46:03 +02:00
} ;
2011-03-12 16:01:04 +01:00
uint8_t ndef_file [ 0xfffe ] = {
2011-03-09 19:17:05 +01:00
0x00 , 33 ,
0xd1 , 0x02 , 0x1c , 0x53 , 0x70 , 0x91 , 0x01 , 0x09 , 0x54 , 0x02 ,
0x65 , 0x6e , 0x4c , 0x69 , 0x62 , 0x6e , 0x66 , 0x63 , 0x51 , 0x01 ,
0x0b , 0x55 , 0x03 , 0x6c , 0x69 , 0x62 , 0x6e , 0x66 , 0x63 , 0x2e ,
0x6f , 0x72 , 0x67
} ;
2010-09-28 18:15:59 +02:00
2011-03-13 15:40:36 +01:00
struct nfcforum_tag4_ndef_data nfcforum_tag4_data = {
2011-03-09 19:17:05 +01:00
. ndef_file = ndef_file ,
2011-03-12 16:01:04 +01:00
. ndef_file_len = ndef_file [ 1 ] + 2 ,
2011-03-13 15:40:36 +01:00
} ;
struct nfcforum_tag4_state_machine_data state_machine_data = {
2011-03-09 19:17:05 +01:00
. current_file = NONE ,
} ;
2011-03-13 15:31:06 +01:00
struct nfc_emulation_state_machine state_machine = {
. io = nfcforum_tag4_io ,
2011-03-13 15:40:36 +01:00
. data = & state_machine_data ,
2011-03-13 15:31:06 +01:00
} ;
2011-03-09 19:17:05 +01:00
struct nfc_emulator emulator = {
. target = & nt ,
2011-03-13 15:31:06 +01:00
. state_machine = & state_machine ,
. user_data = & nfcforum_tag4_data ,
2011-03-09 19:17:05 +01:00
} ;
2010-09-30 18:02:02 +02:00
2011-03-12 16:01:04 +01:00
if ( argc > 3 ) {
usage ( argv [ 0 ] ) ;
exit ( EXIT_FAILURE ) ;
}
2011-03-11 20:01:28 +01:00
// If some file is provided load it
2011-03-12 16:01:04 +01:00
if ( argc > = 2 ) {
2011-03-11 20:01:28 +01:00
if ( ! ndef_message_load ( argv [ 1 ] , & nfcforum_tag4_data ) ) {
err ( EXIT_FAILURE , " Can't load NDEF file '%s' " , argv [ 1 ] ) ;
}
}
// Try to open the NFC reader
pnd = nfc_connect ( NULL ) ;
if ( pnd = = NULL ) {
ERR ( " Unable to connect to NFC device " ) ;
exit ( EXIT_FAILURE ) ;
}
signal ( SIGINT , stop_emulation ) ;
printf ( " Connected to NFC device: %s \n " , pnd - > acName ) ;
printf ( " Emulating NDEF tag now, please touch it with a second NFC device \n " ) ;
2011-05-24 19:10:13 +02:00
if ( 0 ! = nfc_emulate_target ( pnd , & emulator ) ) { // contains already nfc_target_init() call
nfc_perror ( pnd , " nfc_emulate_target " ) ;
}
2010-09-28 18:15:59 +02:00
nfc_disconnect ( pnd ) ;
2011-03-11 20:01:28 +01:00
2011-03-12 16:01:04 +01:00
if ( argc = = 3 ) {
if ( ! ( ndef_message_save ( argv [ 2 ] , & nfcforum_tag4_data ) ) ) {
err ( EXIT_FAILURE , " Can't save NDEF file '%s' " , argv [ 2 ] ) ;
2011-03-11 20:01:28 +01:00
}
}
2010-09-28 18:15:59 +02:00
exit ( EXIT_SUCCESS ) ;
}