listen.pyΒΆ
Source:
#!/usr/bin/env python
# -*- coding: latin-1 -*-
# -----------------------------------------------------------------------------
# Copyright 2015 Stephen Tiedemann <stephen.tiedemann@gmail.com>
#
# Licensed under the EUPL, Version 1.1 or - as soon they
# will be approved by the European Commission - subsequent
# versions of the EUPL (the "Licence");
# You may not use this work except in compliance with the
# Licence.
# You may obtain a copy of the Licence at:
#
# https://joinup.ec.europa.eu/software/page/eupl
#
# Unless required by applicable law or agreed to in
# writing, software distributed under the Licence is
# distributed on an "AS IS" basis,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
# express or implied.
# See the Licence for the specific language governing
# permissions and limitations under the Licence.
# -----------------------------------------------------------------------------
"""Listen as Target for activation requests from a remote Initiator.
**Usage:** ::
listen.py tt2 [options] [--uid UID]
listen.py tt3 [options] [--idm <idm>] [--pmm <pmm>] [--sys <sys>]
listen.py tt4 [options] [--uid <uid>]
listen.py dep [options] [--id3 <id3>] [--gbt <gbt>] [--hce]
listen.py -h | --help
As the Target selected with the first positional argument listen.py
waits '--time T' seconds for activation by a remote device and prints
the local target configuration if not timed out. The listen period is
repeated after '--wait T' seconds if the '--repeat' flag is given.
Without the '--repeat' flag, the exit status is 0 when activated and 1
if timed out, with the '--repeat' flag it is 0 for termination by
keyboard interrupt (Ctrl-C). For argument errors and unsupported
targets listen.py exits with 2. If a local device is not found or was
removed listen.py exits with 3.
**Options:**
-h, --help show this help message and exit
-t, --time T listen time in seconds [default: 2.5]
-w, --wait T time between repetations [default: 1.0]
-r, --repeat repeat forever (cancel with Ctrl-C)
-d, --debug output debug log messages to stderr
-v, --verbose print and log more information
--device PATH local device search path [default: usb]
--bitrate BR set bitrate (default is 106 for A/B and 212 for F)
--uid UID tt2/tt4 identifier [default: 08010203]
--idm IDM tt3 identifier [default: 02FE010203040506]
--pmm PMM tt3 parameters [default: FFFFFFFFFFFFFFFF]
--sys SYS tt3 system code [default: 12FC]
--id3 ID3 dep nfcid3 [default: 01FE0102030405060708]
--gbt GBT dep general bytes [default: 46666D010111]
--hce announce dep and tt4 support for Type A
**Examples:** ::
listen.py tt2 --uid 08ABCDEF # listen as Type 2 Tag with this UID
listen.py tt3 --bitrate 424 # listen as Type 3 Tag at 424 kbps
listen.py tt3 --sys 0003 # use the Suica system code for FeliCa
listen.py dep --gbt '' # send ATR response without general bytes
listen.py dep --hce # offer NFC-DEP Protocol and Type 4A Tag
"""
from __future__ import print_function
import os
import re
import sys
import time
import errno
import argparse
import logging
from binascii import hexlify
import nfc
import nfc.clf
def main(args):
if args['--debug']:
loglevel = logging.DEBUG - (1 if args['--verbose'] else 0)
logging.getLogger("nfc.clf").setLevel(loglevel)
logging.getLogger().setLevel(loglevel)
try:
try: waittime = float(args['--wait'])
except ValueError: assert 0, "the '--wait T' argument must be a number"
assert waittime >= 0, "the '--wait T' argument must be positive"
try: timeout = float(args['--time'])
except ValueError: assert 0, "the '--time T' argument must be a number"
assert timeout >= 0, "the '--time T' argument must be positive"
except AssertionError as error:
print(str(error), file=sys.stderr); return 2
try:
clf = nfc.ContactlessFrontend(args['--device'])
except IOError as error:
print("no device found on path %r" % args['--device'], file=sys.stderr)
return 3
try:
while True:
try:
target = None
if args['tt2']: target = listen_tta(timeout, clf, args)
if args['tt3']: target = listen_ttf(timeout, clf, args)
if args['tt4']: target = listen_tta(timeout, clf, args)
if args['dep']: target = listen_dep(timeout, clf, args)
if target:
print("{0} {1}".format(time.strftime("%X"), target))
except nfc.clf.CommunicationError as error:
if args['--verbose']: logging.error("%r", error)
except AssertionError as error:
print(str(error), file=sys.stderr); return 2
if args['--repeat']: time.sleep(waittime)
else: return (0 if target else 1)
except nfc.clf.UnsupportedTargetError as error:
logging.error("%r", error)
return 2
except IOError as error:
if error.errno != errno.EIO: logging.error("%r", error)
else: logging.error("lost connection to local device")
return 3
except KeyboardInterrupt:
pass
finally:
clf.close()
def listen_tta(timeout, clf, args):
try: bitrate = (int(args['--bitrate']) if args['--bitrate'] else 106)
except ValueError: assert 0, "the '--bitrate' argument must be an integer"
assert bitrate >= 0, "the '--bitrate' argument must be a positive integer"
try: uid = bytearray.fromhex(args['--uid'])
except ValueError: assert 0, "the '--uid' argument must be hexadecimal"
assert len(uid) in (4,7,10), "the '--uid' must be 4, 7, or 10 bytes"
target = nfc.clf.LocalTarget(str(bitrate) + 'A')
target.sens_res = bytearray("\x01\x01")
target.sdd_res = uid
target.sel_res = bytearray("\x00" if args['tt2'] else "\x20")
target = clf.listen(target, timeout)
if target and target.tt2_cmd:
logging.debug("rcvd TT2_CMD %s", hexlify(target.tt2_cmd))
# Verify that we can send a response.
if target.tt2_cmd == "\x30\x00":
data = bytearray.fromhex("046FD536 11127A00 79C80000 E110060F")
elif target.tt2_cmd[0] == 0x30:
data = bytearray(16)
else:
logging.warning("communication not verified")
return target
try:
clf.exchange(data, timeout=1)
return target
except nfc.clf.CommunicationError:
logging.error("communication failure after activation")
if target and target.tt4_cmd:
logging.debug("rcvd TT4_CMD %s", hexlify(target.tt4_cmd))
logging.warning("communication not verified")
return target
def listen_ttf(timeout, clf, args):
try: bitrate = (int(args['--bitrate']) if args['--bitrate'] else 212)
except ValueError: assert 0, "the '--bitrate' argument must be an integer"
assert bitrate >= 0, "the '--bitrate' argument must be a positive integer"
try: idm = bytearray.fromhex(args['--idm'][0:16])
except ValueError: assert 0, "the '--idm' argument must be hexadecimal"
idm += os.urandom(8-len(idm))
try: pmm = bytearray.fromhex(args['--pmm'][0:16])
except ValueError: assert 0, "the '--pmm' argument must be hexadecimal"
pmm += (8-len(pmm)) * "\xFF"
try: sys = bytearray.fromhex(args['--sys'][0:4])
except ValueError: assert 0, "the '--sys' argument must be hexadecimal"
sys += (2-len(sys)) * "\xFF"
target = nfc.clf.LocalTarget(str(bitrate) + 'F')
target.sensf_res = "\x01" + idm + pmm + sys
target = clf.listen(target, timeout)
if target and target.tt3_cmd:
if target.tt3_cmd[0] == 0x06:
response = chr(29) + "\7" + idm + "\0\0\1" + bytearray(16)
clf.exchange(response, timeout=0)
elif target.tt3_cmd[0] == 0x0C:
response = chr(13) + "\x0D" + idm + "\x01" + sys
else:
logging.warning("communication not verified")
return target
try:
clf.exchange(response, timeout=1)
return target
except nfc.clf.CommunicationError:
logging.error("communication failure after activation")
def listen_dep(timeout, clf, args):
try: id3 = bytearray.fromhex(args['--id3'][0:20])
except ValueError: assert 0, "the '--id3' argument must be hexadecimal"
id3 += os.urandom(10-len(id3))
try: gbt = bytearray.fromhex(args['--gbt'])
except ValueError: assert 0, "the '--gbt' argument must be hexadecimal"
target = nfc.clf.LocalTarget()
target.sensf_res = bytearray.fromhex("01") + id3[0:8] + bytearray(10)
target.sens_res = bytearray.fromhex("0101")
target.sdd_res = bytearray.fromhex("08") + id3[-3:]
target.sel_res = bytearray.fromhex("60" if args['--hce'] else "40")
target.atr_res = "\xD5\x01"+id3+"\0\0\0\x08"+("\x32" if gbt else "\0")+gbt
target = clf.listen(target, timeout)
if target and target.dep_req:
logging.debug("rcvd DEP_REQ %s", hexlify(target.dep_req))
# Verify that we can indeed send a response. Note that we do
# not handle a DID, but nobody is sending them anyway. Further
# note that target.dep_req is without the frame length byte
# but exchange() works on frames and so it has to be added.
if target.dep_req.startswith("\xD4\x06\x80"):
# older phones start with attention
dep_res = bytearray.fromhex("04 D5 07 80")
elif target.dep_req.startswith("\xD4\x06\x00"):
# newer phones send information packet
dep_res = bytearray.fromhex("06 D5 07 00 00 00")
else:
logging.warning("communication not verified")
return target
logging.debug("send DEP_RES %s", hexlify(buffer(dep_res, 1)))
try:
data = clf.exchange(dep_res, timeout=1)
assert data and data[0]==len(data)
except (nfc.clf.CommunicationError, AssertionError):
logging.error("communication failure after activation")
return None
logging.debug("rcvd DEP_REQ %s", hexlify(buffer(data, 1)))
mode = "passive" if target.sens_res or target.sensf_res else "active"
logging.debug("activated in %s communication mode", mode)
return target
if __name__ == '__main__':
logging.basicConfig(format='%(relativeCreated)d ms [%(name)s] %(message)s')
try:
from docopt import docopt
except ImportError:
sys.exit("the 'docopt' module is needed to execute this program")
# remove restructured text formatting before input to docopt
usage = re.sub(r'(?<=\n)\*\*(\w+:)\*\*.*\n', r'\1', __doc__)
sys.exit(main(docopt(usage)))