nfc-relay-picc: Add new example to relay ISO14443-4 communications, with this one it can communicate over network... Enjoy! (Thanks to generous contributor)

This commit is contained in:
Romuald Conty 2010-10-14 12:27:51 +00:00
parent 5a5b5d681b
commit 2f267f5463
3 changed files with 525 additions and 0 deletions

View file

@ -10,6 +10,7 @@ bin_PROGRAMS = \
nfc-mfultralight \
nfc-poll \
nfc-relay \
nfc-relay-picc \
pn53x-diagnose \
pn53x-sam \
pn53x-tamashell
@ -46,6 +47,10 @@ nfc_relay_SOURCES = nfc-relay.c
nfc_relay_LDADD = $(top_builddir)/libnfc/libnfc.la \
libnfcutils.la
nfc_relay_picc_SOURCES = nfc-relay-picc.c
nfc_relay_picc_LDADD = $(top_builddir)/libnfc/libnfc.la \
libnfcutils.la
nfc_emulate_forum_tag4_SOURCES = nfc-emulate-forum-tag4.c
nfc_emulate_forum_tag4_LDADD = $(top_builddir)/libnfc/libnfc.la \
libnfcutils.la
@ -91,6 +96,7 @@ dist_man_MANS = \
nfc-mfultralight.1 \
nfc-poll.1 \
nfc-relay.1 \
nfc-relay-picc.1 \
pn53x-diagnose.1 \
pn53x-sam.1 \
pn53x-tamashell.1

74
examples/nfc-relay-picc.1 Normal file
View file

@ -0,0 +1,74 @@
.TH NFC-RELAY-PICC 1 "October 12, 2010"
.SH NAME
nfc-relay-picc \- Relay demonstration tool for ISO14443-4
.SH SYNOPSIS
.B nfc-relay-picc
.SH DESCRIPTION
.B nfc-relay-picc
This tool requires two NFC devices. One device (configured as target) will
emulate an ISO/IEC 14443-4 type A tag, while the second device (configured as
initiator) will act as a reader. The genuine tag can be placed on the second
device (initiator) and the tag emulator (target) can be placed close to the
original reader. All communication is now relayed and shown in the screen on
real-time.
tag <---> initiator (relay) <---> target (relay) <---> original reader
.SH OPTIONS
\fB-h\fP
Help
List options
\fB-q\fP
Quiet mode
Suppress printing of relayed data (improves timing)
\fB-t\fP
Target mode only (to be used on reader side)
Commands are sent to file descriptor 4
Responses are read from file descriptor 3
\fB-i\fP
Initiator mode only (to be used on tag side)
Commands are read from file descriptor 3
Responses are sent to file descriptor 4
\fB-n\fP \fIN\fP
Adds a waiting time of \fIN\fP seconds (integer) in the loop
.SH EXAMPLES
Basic usage:
\fBnfc-relay-picc\fP
Remote relay over TCP/IP:
\fBsocat\fP
TCP-LISTEN:port,reuseaddr
"EXEC:\fBnfc-relay-picc -i\fP,fdin=3,fdout=4"
\fBsocat\fP
TCP:remotehost:port
"EXEC:\fBnfc-relay-picc -t\fP,fdin=3,fdout=4"
.SH NOTES
There are some differences with \fBnfc-relay\fP:
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.
.SH BUGS
Please report any bugs on the
.B libnfc
forum at
.BR http://www.libnfc.org/community/ "."
.SH LICENCE
.B libnfc
and
.B libnfc-examples
are covered by the GNU Lesser General Public License (LGPL), version 3.
.PP
This manual page is licensed under the terms of the GNU GPL (version 2 or later).

445
examples/nfc-relay-picc.c Normal file
View file

@ -0,0 +1,445 @@
/*-
* Public platform independent Near Field Communication (NFC) library
*
* Copyright (C) 2010, 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/>
*/
/**
* @file nfc-relay-picc.c
* @brief Relay example using two PN532 devices.
*/
// Notes & differences with nfc-relay:
// - 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
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif /* HAVE_CONFIG_H */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <signal.h>
//#include <stddef.h>
#include <nfc/nfc.h>
#include <nfc/nfc-messages.h>
#include "nfc-utils.h"
#ifndef _WIN32
// Needed by sleep() under Unix
# include <unistd.h>
# define sleep sleep
# define SUSP_TIME 1 // secs.
#else
// Needed by Sleep() under Windows
# include <winbase.h>
# define sleep Sleep
# define SUSP_TIME 1000 // msecs.
#endif
#define MAX_FRAME_LEN 264
#define MAX_DEVICE_COUNT 2
static byte_t abtCapdu[MAX_FRAME_LEN];
static size_t szCapduLen;
static byte_t abtRapdu[MAX_FRAME_LEN];
static size_t szRapduLen;
static nfc_device_t *pndInitiator;
static nfc_device_t *pndTarget;
static bool quitting = false;
static bool quiet_output = false;
static bool initiator_only_mode = false;
static bool target_only_mode = false;
static int waiting_time = 0;
FILE * fd3;
FILE * fd4;
#define SYMBOL_PARAM_fISO14443_4_PICC 0x20
void
intr_hdlr (void)
{
printf ("\nQuitting...\n");
printf ("Please send a last command to the emulator to quit properly.\n");
quitting = true;
return;
}
void
print_usage (char *argv[])
{
printf ("Usage: %s [OPTIONS]\n", argv[0]);
printf ("Options:\n");
printf ("\t-h\tHelp. Print this message.\n");
printf ("\t-q\tQuiet mode. Suppress printing of relayed data (improves timing).\n");
printf ("\t-t\tTarget mode only (the one on reader side). Data expected from FD3 to FD4.\n");
printf ("\t-i\tInitiator mode only (the one on tag side). Data expected from FD3 to FD4.\n");
printf ("\t-n N\tAdds a waiting time of N seconds (integer) in the relay to mimic long distance.\n");
}
bool print_hex_fd4 (const byte_t * pbtData, const size_t szBytes, const char * pchPrefix)
{
size_t szPos;
if (szBytes > MAX_FRAME_LEN) {
return EXIT_FAILURE;
}
if (fprintf (fd4, "#%s %04x: ", pchPrefix, szBytes)<0) {
return EXIT_FAILURE;
}
for (szPos = 0; szPos < szBytes; szPos++) {
if (fprintf (fd4, "%02x ", pbtData[szPos])<0) {
return EXIT_FAILURE;
}
}
if (fprintf (fd4, "\n")<0) {
return EXIT_FAILURE;
}
fflush(fd4);
return EXIT_SUCCESS;
}
bool scan_hex_fd3 (byte_t *pbtData, size_t *pszBytes, const char * pchPrefix)
{
size_t szPos;
unsigned int uiBytes;
unsigned int uiData;
char pchScan[256];
int c;
// Look for our next sync marker
while ( (c=fgetc(fd3)) != '#') {
if (c == EOF) {
return EXIT_FAILURE;
}
}
strncpy(pchScan, pchPrefix, 250);
strcat(pchScan, " %04x:");
if (fscanf (fd3, pchScan, &uiBytes)<1) {
return EXIT_FAILURE;
}
*pszBytes=uiBytes;
if (*pszBytes > MAX_FRAME_LEN) {
return EXIT_FAILURE;
}
for (szPos = 0; szPos < *pszBytes; szPos++) {
if (fscanf (fd3, "%02x", &uiData)<1) {
return EXIT_FAILURE;
}
pbtData[szPos]=uiData;
}
return EXIT_SUCCESS;
}
int
main (int argc, char *argv[])
{
int arg;
size_t szFound;
nfc_device_desc_t *pnddDevices;
const char *acLibnfcVersion = nfc_version ();
nfc_target_t ntRealTarget;
// Get commandline options
for (arg = 1; arg < argc; arg++) {
if (0 == strcmp (argv[arg], "-h")) {
print_usage (argv);
return EXIT_SUCCESS;
} else if (0 == strcmp (argv[arg], "-q")) {
INFO ("%s", "Quiet mode.");
quiet_output = true;
} else if (0 == strcmp (argv[arg], "-t")) {
INFO ("%s", "Target mode only.");
initiator_only_mode = false;
target_only_mode = true;
} else if (0 == strcmp (argv[arg], "-i")) {
INFO ("%s", "Initiator mode only.");
initiator_only_mode = true;
target_only_mode = false;
} else if (0 == strcmp (argv[arg], "-n")) {
if (++arg==argc || (sscanf(argv[arg], "%i", &waiting_time)<1)) {
ERR ("Missing or wrong waiting time value: %s.", argv[arg]);
print_usage (argv);
return EXIT_FAILURE;
}
INFO ("Waiting time: %i secs.", waiting_time);
} else {
ERR ("%s is not supported option.", argv[arg]);
print_usage (argv);
return EXIT_FAILURE;
}
}
// Display libnfc version
printf ("%s use libnfc %s\n", argv[0], acLibnfcVersion);
#ifdef WIN32
signal (SIGINT, (void (__cdecl *) (int)) intr_hdlr);
#else
signal (SIGINT, (void (*)()) intr_hdlr);
#endif
// Allocate memory to put the result of available devices listing
if (!(pnddDevices = malloc (MAX_DEVICE_COUNT * sizeof (*pnddDevices)))) {
fprintf (stderr, "malloc() failed\n");
return EXIT_FAILURE;
}
// List available devices
nfc_list_devices (pnddDevices, MAX_DEVICE_COUNT, &szFound);
if (initiator_only_mode || target_only_mode) {
if (szFound < 1) {
ERR ("No device found");
return EXIT_FAILURE;
}
fd3 = fdopen(3, "r");
fd4 = fdopen(4, "w");
}
else {
if (szFound < 2) {
ERR ("%zd device found but two connected devices are needed to relay NFC.", szFound);
return EXIT_FAILURE;
}
}
if (!target_only_mode) {
// Try to open the NFC reader used as initiator
// Little hack to allow using initiator no matter if
// there is already a target used locally or not on the same machine:
// if there is more than one readers connected we connect to the second reader
// (we hope they're always detected in the same order)
if (szFound == 1) {
pndInitiator = nfc_connect (&(pnddDevices[0]));
} else {
pndInitiator = nfc_connect (&(pnddDevices[1]));
}
printf ("Connected to the NFC reader device: %s\n", pndInitiator->acName);
// Try to find a ISO 14443-4A tag
nfc_modulation_t nm = {
.nmt = NMT_ISO14443A,
.nbr = NBR_106,
};
if (!nfc_initiator_select_passive_target (pndInitiator, nm, NULL, 0, &ntRealTarget)) {
printf ("Error: no tag was found\n");
nfc_disconnect (pndInitiator);
exit (EXIT_FAILURE);
}
printf("Found tag:\n");
print_nfc_iso14443a_info (ntRealTarget.nti.nai);
if (initiator_only_mode) {
if (print_hex_fd4(ntRealTarget.nti.nai.abtUid, ntRealTarget.nti.nai.szUidLen, "UID") != EXIT_SUCCESS) {
fprintf (stderr, "Error while printing UID to FD4\n");
nfc_disconnect (pndInitiator);
exit(EXIT_FAILURE);
}
if (print_hex_fd4(ntRealTarget.nti.nai.abtAtqa, 2, "ATQA") != EXIT_SUCCESS) {
fprintf (stderr, "Error while printing ATQA to FD4\n");
nfc_disconnect (pndInitiator);
exit(EXIT_FAILURE);
}
if (print_hex_fd4(&(ntRealTarget.nti.nai.btSak), 1, "SAK") != EXIT_SUCCESS) {
fprintf (stderr, "Error while printing SAK to FD4\n");
nfc_disconnect (pndInitiator);
exit(EXIT_FAILURE);
}
if (print_hex_fd4(ntRealTarget.nti.nai.abtAts, ntRealTarget.nti.nai.szAtsLen, "ATS") != EXIT_SUCCESS) {
fprintf (stderr, "Error while printing ATS to FD4\n");
nfc_disconnect (pndInitiator);
exit(EXIT_FAILURE);
}
}
}
if (initiator_only_mode) {
printf ("Hint: tag <---> *INITIATOR* (relay) <-FD3/FD4-> target (relay) <---> original reader\n\n");
} else if (target_only_mode) {
printf ("Hint: tag <---> initiator (relay) <-FD3/FD4-> *TARGET* (relay) <---> original reader\n\n");
} else {
printf ("Hint: tag <---> initiator (relay) <---> target (relay) <---> original reader\n\n");
}
if (!initiator_only_mode) {
nfc_target_t ntEmulatedTarget = {
.nm.nmt = NMT_ISO14443A,
.nm.nbr = NBR_106,
};
if (target_only_mode) {
size_t foo;
if (scan_hex_fd3(ntEmulatedTarget.nti.nai.abtUid, &(ntEmulatedTarget.nti.nai.szUidLen), "UID") != EXIT_SUCCESS) {
fprintf (stderr, "Error while scanning UID from FD3\n");
nfc_disconnect (pndInitiator);
exit(EXIT_FAILURE);
}
if (scan_hex_fd3(ntEmulatedTarget.nti.nai.abtAtqa, &foo, "ATQA") != EXIT_SUCCESS) {
fprintf (stderr, "Error while scanning ATQA from FD3\n");
nfc_disconnect (pndInitiator);
exit(EXIT_FAILURE);
}
if (scan_hex_fd3(&(ntEmulatedTarget.nti.nai.btSak), &foo, "SAK") != EXIT_SUCCESS) {
fprintf (stderr, "Error while scanning SAK from FD3\n");
nfc_disconnect (pndInitiator);
exit(EXIT_FAILURE);
}
if (scan_hex_fd3(ntEmulatedTarget.nti.nai.abtAts, &(ntEmulatedTarget.nti.nai.szAtsLen), "ATS") != EXIT_SUCCESS) {
fprintf (stderr, "Error while scanning ATS from FD3\n");
nfc_disconnect (pndInitiator);
exit(EXIT_FAILURE);
}
} else {
ntEmulatedTarget.nti = ntRealTarget.nti;
}
// We can only emulate a short UID, so fix length & ATQA bit:
ntEmulatedTarget.nti.nai.szUidLen = 4;
ntEmulatedTarget.nti.nai.abtAtqa[1] &= (0xFF-0x40);
// First byte of UID is always automatically replaced by 0x08 in this mode anyway
ntEmulatedTarget.nti.nai.abtUid[0] = 0x08;
// ATS is always automatically replaced by PN532, we've no control on it:
// ATS = (05) 75 33 92 03
// (TL) T0 TA TB TC
// | | | +-- CID supported, NAD supported
// | | +----- FWI=9 SFGI=2 => FWT=154ms, SFGT=1.21ms
// | +-------- DR=2,4 DS=2,4 => supports 106, 212 & 424bps in both directions
// +----------- TA,TB,TC, FSCI=5 => FSC=64
// It seems hazardous to tell we support NAD if the tag doesn't support NAD but I don't know how to disable it
// PC/SC pseudo-ATR = 3B 80 80 01 01
ntEmulatedTarget.nti.nai.abtAts[0] = 0x75;
ntEmulatedTarget.nti.nai.abtAts[1] = 0x33;
ntEmulatedTarget.nti.nai.abtAts[2] = 0x92;
ntEmulatedTarget.nti.nai.abtAts[3] = 0x03;
ntEmulatedTarget.nti.nai.szAtsLen = 4;
//FIXME we could actually emulate also the historical bytes of the tag once libnfc API supports it...
printf("We will emulate:\n");
print_nfc_iso14443a_info (ntEmulatedTarget.nti.nai);
// Try to open the NFC emulator device
pndTarget = nfc_connect (&(pnddDevices[0]));
if (pndTarget == NULL) {
printf ("Error connecting NFC emulator device\n");
if (!target_only_mode) {
nfc_disconnect (pndInitiator);
}
return EXIT_FAILURE;
}
printf ("Connected to the NFC emulator device: %s\n", pndTarget->acName);
if (!nfc_target_init (pndTarget, NTM_ISO14443_4_PICC_ONLY, ntEmulatedTarget, abtCapdu, &szCapduLen)) {
ERR ("%s", "Initialization of NFC emulator failed");
if (!target_only_mode) {
nfc_disconnect (pndInitiator);
}
nfc_disconnect (pndTarget);
exit(EXIT_FAILURE);
}
printf ("%s\n", "Done, relaying frames now!");
}
while (!quitting) {
bool ret;
if (!initiator_only_mode) {
// Receive external reader command through target
if (!nfc_target_receive_bytes(pndTarget,abtCapdu,&szCapduLen)) {
nfc_perror (pndTarget, "nfc_target_receive_bytes");
if (!target_only_mode) {
nfc_disconnect (pndInitiator);
}
nfc_disconnect (pndTarget);
exit(EXIT_FAILURE);
}
if (target_only_mode) {
if (print_hex_fd4(abtCapdu, szCapduLen, "C-APDU") != EXIT_SUCCESS) {
fprintf (stderr, "Error while printing C-APDU to FD4\n");
nfc_disconnect (pndTarget);
exit(EXIT_FAILURE);
}
}
} else {
if (scan_hex_fd3(abtCapdu, &szCapduLen, "C-APDU") != EXIT_SUCCESS) {
fprintf (stderr, "Error while scanning C-APDU from FD3\n");
nfc_disconnect (pndInitiator);
exit(EXIT_FAILURE);
}
}
// Show transmitted response
if (!quiet_output) {
printf ("Forwarding C-APDU: ");
print_hex (abtCapdu, szCapduLen);
}
if (!target_only_mode) {
// Forward the frame to the original tag
ret = nfc_initiator_transceive_bytes
(pndInitiator, abtCapdu, szCapduLen, abtRapdu, &szRapduLen);
} else {
if (scan_hex_fd3(abtRapdu, &szRapduLen, "R-APDU") != EXIT_SUCCESS) {
fprintf (stderr, "Error while scanning R-APDU from FD3\n");
nfc_disconnect (pndTarget);
exit(EXIT_FAILURE);
}
ret = true;
}
if (ret) {
// Redirect the answer back to the external reader
if (waiting_time > 0) {
if (!quiet_output) {
printf ("Waiting %is to simulate longer relay...\n", waiting_time);
}
sleep(waiting_time * SUSP_TIME);
}
// Show transmitted response
if (!quiet_output) {
printf ("Forwarding R-APDU: ");
print_hex (abtRapdu, szRapduLen);
}
if (!initiator_only_mode) {
// Transmit the response bytes
if (!nfc_target_send_bytes(pndTarget, abtRapdu, szRapduLen)) {
nfc_perror (pndTarget, "nfc_target_send_bytes");
if (!target_only_mode) {
nfc_disconnect (pndInitiator);
}
if (!initiator_only_mode) {
nfc_disconnect (pndTarget);
}
exit(EXIT_FAILURE);
}
} else {
if (print_hex_fd4(abtRapdu, szRapduLen, "R-APDU") != EXIT_SUCCESS) {
fprintf (stderr, "Error while printing R-APDU to FD4\n");
nfc_disconnect (pndInitiator);
exit(EXIT_FAILURE);
}
}
}
}
if (!target_only_mode) {
nfc_disconnect (pndInitiator);
}
if (!initiator_only_mode) {
nfc_disconnect (pndTarget);
}
exit (EXIT_SUCCESS);
}