From 447c38477e1a178823bc6ddbdbe560ae582bb622 Mon Sep 17 00:00:00 2001 From: Adam Laurie Date: Mon, 31 Oct 2011 10:22:22 +0000 Subject: [PATCH] first commit --- CHANGES | 229 ++ Makefile | 45 + README.TXT | 39 + RFIDIOt.py | 2226 +++++++++++++++++ RFIDIOtconfig.opts | 7 + RFIDIOtconfig.py | 185 ++ bruteforce.py | 77 + cardselect.py | 53 + copytag.py | 92 + delete-smartcafe.gpsh | 12 + demotag.py | 35 + eeprom.py | 42 + fdxbnum.py | 175 ++ formatmifare1kvalue.py | 64 + froschtest.py | 79 + hidprox.py | 88 + hitag2brute.py | 68 + hitag2reset.py | 86 + iso3166.py | 556 ++++ isotype.py | 81 + java/Makefile | 111 + java/jcop_delete_atr_hist.gpsh | 16 + java/jcop_set_atr_hist.cap | Bin 0 -> 3381 bytes java/jcop_set_atr_hist.gpsh | 17 + java/src/jcop_set_atr_hist/ATRGlobal.java | 30 + .../src/jcop_set_atr_hist/JCOPSetATRHist.java | 68 + jcop_mifare_access.cap | Bin 0 -> 3841 bytes jcop_mifare_access.gpsh | 20 + jcopmifare.py | 181 ++ jcopsetatrhist.py | 128 + jcoptool.py | 490 ++++ lfxtype.py | 50 + loginall.py | 49 + mifare.pdf | Bin 0 -> 328329 bytes mifarekeys.py | 127 + mrpkey.py | 1580 ++++++++++++ multiselect.py | 57 + pn532.py | 140 ++ pn532emulate.py | 171 ++ pn532mitm.py | 412 +++ pynfc.py | 269 ++ q5reset.py | 87 + readlfx.py | 174 ++ readmifare1k.py | 104 + readmifaresimple.py | 279 +++ readmifareultra.py | 140 ++ readtag.py | 52 + sod.py | 45 + testacg.sh | 3 + testlahf.sh | 4 + transit.py | 133 + unique.py | 152 ++ upload2cosmo.gpsh | 14 + upload2jcop.gpsh | 15 + upload2nokia.gpsh | 15 + upload2smartcafe.gpsh | 13 + writelfx.py | 178 ++ writemifare1k.py | 72 + 58 files changed, 9635 insertions(+) create mode 100644 CHANGES create mode 100644 Makefile create mode 100644 README.TXT create mode 100755 RFIDIOt.py create mode 100644 RFIDIOtconfig.opts create mode 100755 RFIDIOtconfig.py create mode 100755 bruteforce.py create mode 100755 cardselect.py create mode 100755 copytag.py create mode 100644 delete-smartcafe.gpsh create mode 100755 demotag.py create mode 100755 eeprom.py create mode 100755 fdxbnum.py create mode 100755 formatmifare1kvalue.py create mode 100755 froschtest.py create mode 100755 hidprox.py create mode 100755 hitag2brute.py create mode 100755 hitag2reset.py create mode 100755 iso3166.py create mode 100755 isotype.py create mode 100644 java/Makefile create mode 100644 java/jcop_delete_atr_hist.gpsh create mode 100644 java/jcop_set_atr_hist.cap create mode 100644 java/jcop_set_atr_hist.gpsh create mode 100644 java/src/jcop_set_atr_hist/ATRGlobal.java create mode 100644 java/src/jcop_set_atr_hist/JCOPSetATRHist.java create mode 100644 jcop_mifare_access.cap create mode 100644 jcop_mifare_access.gpsh create mode 100755 jcopmifare.py create mode 100755 jcopsetatrhist.py create mode 100755 jcoptool.py create mode 100755 lfxtype.py create mode 100755 loginall.py create mode 100644 mifare.pdf create mode 100755 mifarekeys.py create mode 100755 mrpkey.py create mode 100755 multiselect.py create mode 100755 pn532.py create mode 100755 pn532emulate.py create mode 100755 pn532mitm.py create mode 100755 pynfc.py create mode 100755 q5reset.py create mode 100755 readlfx.py create mode 100755 readmifare1k.py create mode 100755 readmifaresimple.py create mode 100755 readmifareultra.py create mode 100755 readtag.py create mode 100755 sod.py create mode 100755 testacg.sh create mode 100755 testlahf.sh create mode 100755 transit.py create mode 100755 unique.py create mode 100644 upload2cosmo.gpsh create mode 100644 upload2jcop.gpsh create mode 100644 upload2nokia.gpsh create mode 100644 upload2smartcafe.gpsh create mode 100755 writelfx.py create mode 100755 writemifare1k.py diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000..2f02dc1 --- /dev/null +++ b/CHANGES @@ -0,0 +1,229 @@ +v0.1: 2006-04-28 +First release. + +v0.1a: 2006-05-16 +Add 'Value' commands: + readvalueblock + writevalueblock + MIFAREvb (set variables from value block) + +v0.1b: 2006-05-29 +Make readblock non MIFARE specific +Add readMIFAREblock command +Add reset() call to all test programs (to switch off constant read) +Add readlfx program to support ACG LFX reader + +v0.1c: 2006-05-30 +Add LFXTags hash (125 kHz tag types) +Add lfxtype.py (command line tag identifier) + +v0.d: 2006-06-01 +Add access control block user data byte to MIFARE structure +Add LFX tag type detection to cardselect.py + +v0.e: 2006-09-29 +Add ICAO 9303 structures for Machine Readable Passports +New test program: mrpkey.py for ICAO 9303 +Move reader config to RFIDIOtconfig.py +Add EM4x05 ID decode + +v0.f: 2006-10-24 +Emergency release - 0.e lost in webserver disk crash +Contains work in progress!!! +Rename EM4x05 decode to more generic FDX-B +Add EM4x02 'Unique' ID decode/encode +Add passport file reads and image display + +v0.g: 2006-10-27 +Tidy up! +Update all version numbers + +v.0.h: +Add Hitag2 reading (readlfx) +Add fdx-b cloning to Q5 / Hitag2 (fdxbnum) +Add q5unfuck (does what it say on the tin!) +mrpkey - display all data in GUI +fdxbnum - add ability to write raw 16 digit HEX (to clone non-compliant tags) + +v0.i: 2006-12-10 +Fix login error for sector 0 + +v0.j: +Fix iso_7816_fail to allow non-passports to fail properly +Add support for Frosch Hitag reader/writer +Start to rationalise routines to always return True or False +fdxbnum - add Frosch support for Hitag2 +fdxbnum - wait for blank tag in WRITE mode + +v0.k: +was release j - forgot to update version number! + +v0.m: +add facility to set Q5 native ID in 'q5reset.py' +fix 'EM4x02' ID mode in 'unique.py' (was reversed) +allow forcing of tag type in 'readlfx.py' +add 'readtag.py' - read data blocks with no login +add 'copytag.py' - copy data blocks to matching blank +add 'isotype.py' - determine HF tag types +add CHECK for Machine Readable Document in 'mrpkey.py' +fix bruteforce for non complete document numbers in 'mrpkey.py' +fix bruteforce length of field in 'mrpkey.py' +add offsets for usa & netherlands to 'mrpkey.py' +add global overrides for line/speed etc. in RFIDIOtconfig.py [AL & Philippe Biondi] + +v0.n: +add CLONE mode to 'unique.py' +make 'mrpkey.py' more intelligent about reading passport contents: + read all data groups + extract image from CBEFF block in EF.DG2 + extract public key certificate from EF.SOD (requires openssl installation) + add asn.1 field length encoding rules +add 'sod.py' tool for brute force finding of certificates in EF_SOD.BIN (requires openssl installation) + +v0.p +add PCSC support (http://pcsclite.alioth.debian.org/ and http://pyscard.sourceforge.net/) [hints/tips/inspiration Henryk Plötz] +fix cardselect.py and multiselect.py to check for presence of card +fix 'waitfor/do nothing' in RFIDIOt.py [Philippe Biondi] +cleaner check digit calc in mrpkey.py [Philippe Biondi] +change -r to -R (reader type) to allow -r to be used for PCSC compatibility +add speed/framesize reporting to mrpkey.py +increase MAX read chunk size to 118 in mrpkey.py (needs fixing to go up to device supported size ISO_FRAMESIZE) +fix bit allignment issue in FDXBID encoding/decoding [Matsche] +add global uid variable +add locked block reporting to readmifare +add readmifaresimple.py + +v0.q +fix asn1 field length calculation in mrpkey.py +add human readable config block for Q5 in readlfx.py +add Manchester encoding to RFIDIOt.py and unique.py +add serial port opening and baud rate checking for ACG / Frosch in RFIDIOt.py +add Q5 emulation detection in lfxtype.py + +v0.r +add SCM Microsystems reader support +add -d (debug) option +switch to T=1 protocol for PC/SC +add auto-detect of PC/SC reader types +fix minor reporting issues in readmifaresimple.py +fix setting of tag type 'ALL' on ACG readers (different for LF or HF) +added a bunch of PCSC ATR card types +add reading of previously stored files to mrpkey.py +fix CBEFF processing in mrpkey.py +fix bruteforcing of first character in mrpkey.py [Petter Bjorklund] +add ID Card processing to mrpkey.py [vonJeek ] + +v0.s +fix -L issue in RFIDIOtconfig (readernum must be 0) +add human readable dump to readmifaresimple.py (ReadablePrint() in RFIDIOt) +fix logic in tag selection in unique.py (would not use hitag2) +add hitag2 login (password mode) to RFIDIOt.py +add hitag2bruteforce program hitag2brute.py +start migrating definitions into smaller files to aid sharing with other apps (e.g. iso3166.py) +add Windows distribution [Zac Franken] + +v0.t - October 2008 +add WRITE function to mrpkey.py for vonJeek JCOP emulator (http://freeworld.thc.org/thc-epassport/) +add Makefile and vonJeek.gpsh for installing vonJeek epassport.cap to JCOP +add VONJEEK declarations for vonJeek emulator to RFIDIOt.py +set mrpkey file types to binary for windows compatibility [Jeroen van Beek / vonJeek ] +use windows compatible command execution for external commands in mrpkey [Jeroen van Beek / vonJeek ] +add WRITE function to mrpkey.py for JMRTD JCOP emulator (http://jmrtd.org/) +allow mrpkey to skip objects (e.g. fingerprint (EF.DG3) protected by active authentication) +add mifarekeys.py - calculate 3DES MifarePWD for Access to Mifare memory/functions on Dual Interface JCOP cards + as per Philips Application Note AN02105, http://www.nxp.com/acrobat_download/other/identification/067512.pdf +add some country codes to iso3166.py [Jeroen van Beek / vonJeek ] +add -g 'No GUI' option to RFIDIOtconfig.py + +v0.u - November 2008 +add testlahf.sh script for testing LAHF units +fix -R reader type override in RFIDIOtconfig.py +add RFIDIOtconfig.py checking for global overrides in one of the following locations (in search order): + + $(RFIDIOtconfig_opts) + ./RFIDIOtconfig.opts + /etc/RFIDIOtconfig.opts + + options should be specified on the first line as if typed on the command line, e.g. + + -s 9600 -l /dev/ttyUSB0 + + command line options will take precedence over this file. + +add -n (No Init) command to RFIDIOtconfig.py - allow modules to run without hardware +add display of checksum-corrected MRZ to mrpkey.py +add jcop_mifare_access.cap - mifare access applet for JCOP +add jcop_mifare_access.gpsh and target in Makefile for installation of jcop_mifare_access.cap +add jcopmifare.py test program for JCOP mifare emulation +add display of biometric features on FACE in mrpkey.py + +v0.v - January 2009 +fix ATS position & length in RFIDIOT.py +add jcopsetatrhist.py - sets ATR Historical Bytes (ATS) on JCOP cards +add jcop_set_atr_hist.cap - java applet for setting ATR/ATS +add JAVA source for jcop_set_atr_hist.cap +move iso_7816 routines into RFIDIOt (from mrpkey.py) +fix exit status of all test programs and RFIDIOt (should be True on error) + +v0.w +fix ACG reset/info sequence in RFIDIOt.py +fix facial image display bug in mrpkey.py where conversion is required [Andreas Schmidt] +fix RANDOM_UID setting in jcop_mifare_access.cap/jcopmifare.py (you will need a secret key from NXP) +add jcoptool.py - JCOP toolkit (work in progress) +mrpkey.py changes: + fix binary mode when reading files under Windows (for WRITE to card) + fix computation of composite checksum digit + support reading non-BAC passports + specify a dummy MRZ or simply the keyword 'PLAIN' for Plain Access if there is no Basic Access Control + support writing non-BAC passports (only for vonJeek cards) + new commands SETBAC and UNSETBAC to toggle the BAC mode on vonJeek cards + extract & display signature image stored in DG7, if any + fix bug in Jpeg 2000 handling & add Jpeg 2000 support for DG7 + better error handling if PCSC daemon is down or no reader is found + support clone mode by specifying PLAIN/MRZ and WRITE: first read then write + support shortened MRZ (as in mrp0wn) + strip AA & EAC by default when writing, set STRIP_INDEX=False to disable stripping +change Makefile to match vonJeek gpshell files (upload2jcop.gpsh & upload2nokia.gpsh) + +v0.x +add support for ACS readers and Alcatel-Lucent Tikitag/Touchatag [props to pytey for http://hackerati.com/post/57314994/rfid-on-the-cheap-hacking-tikitag] +rationalise PCSC subtypes +add hidprox.py for reading HID ProxCards (only tested with OmniKey 5325) +add pn532.py - definitions for nxp pn532 chip +add pn532emulate.py - run nxp pn532 in emulator mode +add pn532mitm.py - relay traffic between reader and emulator, and log APDU and responses + +v0.y +fix support for ACS PCSC-2 devices (e.g. ACR 122U) +add writelfx.py - test write LF devices +fix 3DES key setting for ID cards in mrpkey.py +allow missing files to be skipped if running in files mode in mrpkey.py + +v0.z +add xorcheck.py - search for valid final byte of rolling LRC [input from Henryk Plötz] +add transit.py - program Q5 with FDI Matalec 'TRANSIT 500' or 'TRANSIT 999' standard UID [input from Proxmark Community] + +v1.0a +make mrpkey.py slightly easier to add new document types to +add COPY and RESET functions to readmifaresimple.py +add automatic keytype and default key checking to readmifaresimple.py +fix MIFARE KeyA and KeyB handling on all supported readers +add readmifareultra.py - read Mifare UltraLight tags [Keith Howell] +add support for libnfc devices [Nick von Dadelszen] (work in progress) + +v1.0b +allow global overrides to be specified in ENV variable, e.g. + export RFIDIOtconfig="-s 9600 -l /dev/ttyUSB1 -R RFIDIOt.rfidiot.READER_ACG" +use latest pynfc.py (ver 0.2) for libnfc devices +add device listing to libnfc support (-N) +add more keys to readmifaresimple.py [nethemba] +strip leading character from ACG LFX UID +add cosmo card (alternative to jcop) upload files for gpshell +fix init for pn532mitm.py +make sure all programs exit with status (also removes annoying PCSC error on close) +wait for passport in mrpkey.py [Adam Urban] +add lifecycle data to jcoptool.py + +v1.0c +fix reading unknown block size in readblock() +detect more ACS readers [Keith Howell] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a9ebb90 --- /dev/null +++ b/Makefile @@ -0,0 +1,45 @@ +# Makefile for uploading vonJeek epassport emulator and Mifare acccess applet +# http://freeworld.thc.org/thc-epassport/ +# +# gpshell can be found here: +# http://sourceforge.net/project/showfiles.php?group_id=143343&package_id=159897 +# +# blank JCOP cards can be got here: +# (note vonJeek applet requires 72K card) +# http://www.rfidiot.org/ +# +# This makefile by Adam Laurie, 2008 + +# GPShell... +GPSHELL= "gpshell" +GPSHELL_VONJEEK_SCRIPT="upload2jcop.gpsh" +GPSHELL_VONJEEK_NOKIA_SCRIPT="upload2nokia.gpsh" +GPSHELL_MIFARE_SCRIPT="jcop_mifare_access.gpsh" +GPSHELL_ATR_SCRIPT="jcop_set_atr_hist.gpsh" +GPSHELL_ATR_UNINSTALL_SCRIPT="jcop_delete_atr_hist.gpsh" +GPSHELL_NOKIA_MIFARE_SCRIPT="nokia_jcop_mifare_access.gpsh" + +# install passport applet +install-passport: + # first clean the script of nasty windows s + tr -d '\r' < $(GPSHELL_VONJEEK_SCRIPT) > /tmp/$(GPSHELL_VONJEEK_SCRIPT) + $(GPSHELL) /tmp/$(GPSHELL_VONJEEK_SCRIPT) + +# install passport applet to Nokia +# phone must have been unlocked with the unlock midlet: +# Nokia NFC Unlock Service MIDlet - http://www.forum.nokia.com +install-passport-nokia: + tr -d '\r' < $(GPSHELL_VONJEEK_NOKIA_SCRIPT) > /tmp/$(GPSHELL_VONJEEK_NOKIA_SCRIPT) + $(GPSHELL) /tmp/$(GPSHELL_VONJEEK_NOKIA_SCRIPT) + +# install mifare access applet +install-mifare: + $(GPSHELL) $(GPSHELL_MIFARE_SCRIPT) + +# install ATR History applet +install-atr: + cd java && $(GPSHELL) $(GPSHELL_ATR_SCRIPT) + +# delete ATR History applet +uninstall-atr: + cd java && $(GPSHELL) $(GPSHELL_ATR_UNINSTALL_SCRIPT) diff --git a/README.TXT b/README.TXT new file mode 100644 index 0000000..3584c14 --- /dev/null +++ b/README.TXT @@ -0,0 +1,39 @@ +/* RFIDIOt.py - RFID IO tools for python + * + * Adam Laurie + * http://rfidiot.org/ + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This code 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. + */ + +Copyright (c) 2006-2011 Adam Laurie + +q: What is RFIDIOt? +a: A collection of tools and libraries for exploring RFID technology, written +in python. + +q: Why RFIDIOt? +a: I like silly puns. Also, I'm coming at this from an idiot's point of view: +I know nothing about RFID tags, and even less about python. As such, I felt a +complete idiot when I started. :) + +q: How can I contribute? +a: Send me patches, info, new tools, coffee, money, drugs and/or loose women. + +q: What hardware is supported? +a: So far this works with the ACG serial readers. I use the CF Card model, +but it should also work with the USB version by changing the serial port to +/dev/ttyUSB0. You can find more details here: + + http://www.acg.de + +q: So what exactly is here? +a: Please see http://www.rfidiot.org/documentation.html diff --git a/RFIDIOt.py b/RFIDIOt.py new file mode 100755 index 0000000..c318cc3 --- /dev/null +++ b/RFIDIOt.py @@ -0,0 +1,2226 @@ +# RFIDIOt.py - RFID IO tools for python +# -*- coding: iso-8859-15 -*- +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2006,7,8,9 All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + +# use Psyco compiler to speed things up if available +try: + import psyco + psyco.profile(0.01) + psyco.full() +except ImportError: + pass + + + +import os +import sys +import random +import string +import time +from Crypto.Hash import SHA +from Crypto.Cipher import DES3 +from Crypto.Cipher import DES +from operator import * +import pynfc +import signal + + +try: + import smartcard, smartcard.CardRequest + IOCTL_SMARTCARD_VENDOR_IFD_EXCHANGE = smartcard.scard.SCARD_CTL_CODE(1) +except: + print '*** Warning - no pyscard installed or pcscd not running' + +MASK_CCITT = 0x1021 # CRC-CCITT mask (ISO 3309, used in X25, HDLC) +MASK_11785 = 0x8408 +MASK_CRC16 = 0xA001 # CRC16 mask (used in ARC files) + +DEBUG= False +#DEBUG= True +NoInit= False +NFCReader= None + +class rfidiot: + "RFIDIOt - RFID I/O tools - http://rfidiot.org" + # local imports + from iso3166 import ISO3166CountryCodesAlpha + from iso3166 import ISO3166CountryCodes + # + # open reader port + # + def __init__(self,readernum,reader,port,baud,to,debug,noinit,nfcreader): + global NoInit + global DEBUG + self.readertype= reader + self.readersubtype= reader + readernum= int(readernum) + DEBUG= debug + NoInit= noinit + NFCReader= nfcreader + if not NoInit: + if self.readertype == self.READER_PCSC: + try: + self.pcsc_protocol= smartcard.scard.SCARD_PROTOCOL_T1 + except: + print 'Could not find PCSC daemon, try with option -n if you don\'t have a reader' + os._exit(True) + # select the reader specified + try: + self.pcsc= smartcard.System.readers() + except: + print 'Could not find PCSC daemon, try with option -n if you don\'t have a reader' + os._exit(True) + if readernum >= len(self.pcsc): + print 'There is no such reader #%i, PCSC sees only %i reader(s)' % (readernum, len(self.pcsc)) + os._exit(True) + try: + self.readername= self.pcsc[readernum].name + self.pcsc_connection= self.pcsc[readernum].createConnection() + # debug option will show APDU traffic + if DEBUG: + from smartcard.CardConnectionObserver import ConsoleCardConnectionObserver + observer=ConsoleCardConnectionObserver() + self.pcsc_connection.addObserver( observer ) + except: + print 'Could not create connection to %s' % self.readername + os._exit(True) + # determine PCSC subtype + if string.find(self.readername,'OMNIKEY') == 0: + self.readersubtype= self.READER_OMNIKEY + else: + if string.find(self.readername,'SDI010') == 0: + self.readersubtype= self.READER_SCM + else: + if string.find(self.readername,'ACS ACR 38U') == 0 or string.find(self.readername,'ACS ACR38U') == 0: + self.readersubtype= self.READER_ACS + self.pcsc_protocol= smartcard.scard.SCARD_PROTOCOL_T0 + self.hcard = None + elif string.find(self.readername,'ACS ACR122U PICC') == 0: + self.readersubtype= self.READER_ACS + self.pcsc_protocol= smartcard.scard.SCARD_PROTOCOL_T1 + self.hcard = None + else: + # default to Omnikey for now + self.readersubtype= self.READER_OMNIKEY + if DEBUG: + print 'Reader Subtype:',self.readersubtype + # create a connection + try: + self.pcsc_connection.connect() + except: + # card may be something like a HID PROX which only returns ATR and does not allow connect + hresult, hcontext = smartcard.scard.SCardEstablishContext( smartcard.scard.SCARD_SCOPE_USER ) + if hresult != 0: + raise error, 'Failed to establish context: ' + smartcard.scard.SCardGetErrorMessage(hresult) + hresult, readers = smartcard.scard.SCardListReaders( hcontext, [] ) + readerstates= [ (readers[readernum], smartcard.scard.SCARD_STATE_UNAWARE ) ] + hresult, newstates = smartcard.scard.SCardGetStatusChange( hcontext, 0, readerstates ) + if self.readersubtype == self.READER_ACS and self.pcsc_protocol == smartcard.scard.SCARD_PROTOCOL_T1: + # SCARD_SHARE_SHARED if there is a PICC otherwise SCARD_SHARE_DIRECT + hresult, hcard, dwActiveProtocol = smartcard.scard.SCardConnect( + hcontext, readers[readernum], smartcard.scard.SCARD_SHARE_DIRECT, smartcard.scard.SCARD_PROTOCOL_T0 ) + self.hcard = hcard + # Let's test if we can really use SCardControl, e.g. by sending a get_firmware_version APDU + apdu = [ 0xFF, 0x00, 0x48, 0x00, 0x00 ] + hresult, response = smartcard.scard.SCardControl( self.hcard, IOCTL_SMARTCARD_VENDOR_IFD_EXCHANGE, apdu ) + if hresult != smartcard.scard.SCARD_S_SUCCESS: + print 'Failed to control: ' + smartcard.scard.SCardGetErrorMessage(hresult) + if hresult == smartcard.scard.SCARD_E_NOT_TRANSACTED: + print 'Did you set DRIVER_OPTION_CCID_EXCHANGE_AUTHORIZED in ifdDriverOptions in libccid_Info.plist?' + os._exit(True) + self.pcsc_atr= self.ListToHex(newstates[0][2]) + pass + if self.readersubtype == self.READER_ACS: + self.acs_set_retry(to) + #libnfc device + elif self.readertype == self.READER_LIBNFC: + self.nfc = pynfc.NFC() + self.readername = self.nfc.LIBNFC_READER + elif self.readertype == self.READER_NONE: + self.readername = 'none' + else: + # frosch always starts at 9600 baud - need to add code to test for selected rate and + # switch if required. + try: + import serial + self.ser = serial.Serial(port, baud, timeout=to) + self.ser.readline() + self.ser.flushInput() + self.ser.flushOutput() + except: + print 'Could not open serial port %s' % port + os._exit(True) + # + # variables + # + # VERSION: RFIDIOt.py version number + # errorcode: 1 letter errorcode returned by the reader + # + # MIFAREdata: ASCII HEX representation of data block after successful read + # MIFAREbinary: data block converted back to binary + # MIFAREBLOCKLEN: constant ASCII HEX block length + # MIFAREVALUELEN: constant ASCII HEX value length + # MIFAREserialnumber: Unique ID (UID) of card + # MIFAREkeyA: KEYA from key block (will always be 000000000000) + # MIFAREkeyB: KEYB from key block + # MIFAREaccessconditions: access conditions field from Key Block + # MIFAREC1: Access conditions bitfield C1 + # MIFAREC2: Access conditions bitfield C2 + # MIFAREC3: Access conditions bitfield C3 + # MIFAREblock0AC: Block 0 Access Conditions + # MIFAREblock1AC: Block 1 Access Conditions + # MIFAREblock2AC: Block 2 Access Conditions + # MIFAREblock3AC: Block 3 Access Conditions + # MIFAREACKB: Human readable Key Block Access Conditions + # MIFAREACDB: Human readable Data Block Access ConditionsA + # + # MRPmrzu: Machine Readable Passport - Machine Readable Zone - Upper + # MRPmrzl Machine Readable Passport - Machine Readable Zone - Lower + VERSION= '1.0c-beta' + # Reader types + READER_ACG= 0x01 + READER_FROSCH= 0x02 + READER_DEMOTAG= 0x03 + READER_PCSC= 0x04 + READER_OMNIKEY= 0x05 + READER_SCM= 0x06 + READER_ACS= 0x07 + READER_LIBNFC = 0x08 + READER_NONE = 0x09 + # TAG related globals + errorcode= '' + binary= '' + data= '' + sel_res= '' + sens_res= '' + tagtype= '' + speed= '' + framesize= '' + uid= '' + MIFAREdata='' + MIFAREbinary='' + MIFAREBLOCKLEN=32 + MIFAREVALUELEN=8 + MIFAREserialnumber= '' + MIFAREcheckbyte= '' + MIFAREmanufacturerdata= '' + MIFAREkeyA= '' + MIFAREkeyB= '' + MIFAREaccessconditions= '' + MIFAREaccessconditionsuserbyte= ' ' + MIFAREC1= 0 + MIFAREC2= 0 + MIFAREC3= 0 + MIFAREblock0AC= '' + MIFAREblock1AC= '' + MIFAREblock2AC= '' + MIFAREblock3AC= '' + # PCSC uses 'External Authentication', whereby keys are stored in the reader and then presented to the card. They only need + # to be set up once per session, so login will store them in a global dictionary. + # PCSC_Keys= { key : keynum } where keynum is 0 - 31 as per OmniKey docs + PCSC_Keys= {} + # Static Globals + MIFAREACKB= {'000':'Write KeyA: KEYA, Read Access bits: KEYA, Write Access bits: NONE, Read KeyB: KEYA, Write KeyB: KEYA (KEYB readable)',\ + '010':'Write KeyA: NONE, Read Access bits: KEYA, Write Access bits: NONE, Read KeyB: KEYA, Write KeyB: NONE (KEYB readable)',\ + '100':'Write KeyA: KEYB, Read Access bits: KEYA/B, Write Access bits: NONE, Read KeyB: NONE, Write KeyB: KEYB',\ + '110':'Write KeyA: NONE, Read Access bits: KEYA/B, Write Access bits: NONE, Read KeyB: NONE, Write KeyB: NONE',\ + '001':'Write KeyA: KEYA, Read Access bits: KEYA, Write Access bits: KEYA, Read KeyB: KEYA, Write KeyB: KEYA (KEYB readable, transport configuration)',\ + '011':'Write KeyA: KEYB, Read Access bits: KEYA/B, Write Access bits: KEYB, Read KeyB: NONE, Write KeyB: KEYB',\ + '101':'Write KeyA: NONE, Read Access bits: KEYA/B, Write Access bits: KEYB, Read KeyB: NONE, Write KeyB: NONE',\ + '111':'Write KeyA: NONE, Read Access bits: KEYA/B, Write Access bits: NONE, Read KeyB: NONE, Write KeyB: NONE'} + MIFAREACDB= {'000':'Read: KEYA/B, Write: KEYA/B, Increment: KEYA/B, Decrement/Transfer/Restore: KEYA/B (transport configuration)',\ + '010':'Read: KEYA/B, Write: NONE, Increment: NONE, Decrement/Transfer/Restore: NONE',\ + '100':'Read: KEYA/B, Write: KEYB, Increment: NONE, Decrement/Transfer/Restore: NONE',\ + '110':'Read: KEYA/B, Write: KEYB, Increment: KEYB, Decrement/Transfer/Restore: KEYA/B',\ + '001':'Read: KEYA/B, Write: NONE, Increment: NONE, Decrement/Transfer/Restore: KEYA/B',\ + '011':'Read: KEYB, Write: KEYB, Increment: NONE, Decrement/Transfer/Restore: NONE',\ + '101':'Read: KEYB, Write: NONE, Increment: NONE, Decrement/Transfer/Restore: NONE',\ + '111':'Read: NONE, Write: NONE, Increment: NONE, Decrement/Transfer/Restore: NONE'} + LFXTags= {'U':'EM 4x02 (Unique)',\ + 'Z':'EM 4x05 (ISO FDX-B)',\ + 'T':'EM 4x50',\ + 'h':'Hitag 1 / Hitag S',\ + 'H':'Hitag 2',\ + 'Q':'Q5',\ + 'R':'TI-RFID Systems',\ + 'N':'No TAG present!'} + # number of data blocks for each tag type (including block 0) + LFXTagBlocks= {'U':0,\ + 'Z':0,\ + 'T':34,\ + 'h':64,\ + 'H':8,\ + 'Q':8,\ + 'R':18,\ + 'N':0} + ALL= 'all' + EM4x02= 'U' + EM4x05= 'Z' + Q5= 'Q' + HITAG1= 'h' + HITAG2= 'H' + HITAG2_TRANSPORT_RWD='4D494B52' + HITAG2_TRANSPORT_TAG='AA4854' + HITAG2_TRANSPORT_HIGH='4F4E' + HITAG2_PUBLIC_A= '02' + HITAG2_PUBLIC_B= '00' + HITAG2_PUBLIC_C= '04' + HITAG2_PASSWORD= '06' + HITAG2_CRYPTO= '0e' + ACG_FAIL= 'N' + # Mifare transort keys + MIFARE_TK= { 'AA' : 'A0A1A2A3A4A5',\ + 'BB' : 'B0B1B2B3B4B5',\ + 'FF' : 'FFFFFFFFFFFF'} + ISOTags= {'a':'ISO 14443 Type A ',\ + 'b':'ISO 14443 Type B ',\ + 'd':'ICODE UID ',\ + 'e':'ICODE EPC ',\ + 'i':'ICODE ',\ + 's':'SR176 ',\ + 'v':'ISO 15693 '} + ISOTagsA= {'t':'All Supported Tags'} + ISO15693= 'v' + # Manufacturer codes (Listed in ISO/IEC 7816-6) + ISO7816Manufacturer= { '00':'Not Specified',\ + '01':'Motorola',\ + '02':'ST Microelectronics',\ + '03':'Hitachi, Ltd',\ + '04':'Philips Semiconductors (NXP)',\ + '05':'Infineon Technologies AG',\ + '06':'Cylinc',\ + '07':'Texas Instrument',\ + '08':'Fujitsu Limited',\ + '09':'Matsushita Electronics Corporation',\ + '0a':'NEC',\ + '0b':'Oki Electric Industry Co. Ltd',\ + '0c':'Toshiba Corp.',\ + '0d':'Mitsubishi Electric Corp.',\ + '0e':'Samsung Electronics Co. Ltd',\ + '0f':'Hyundai Electronics Industries Co. Ltd',\ + '10':'LG-Semiconductors Co. Ltd',\ + '12':'HID Corporation',\ + '16':'EM Microelectronic-Marin SA', + } + ISOAPDU= {'ERASE BINARY':'0E', + 'VERIFY':'20', + # Global Platform + 'INITIALIZE_UPDATE':'50', + # GP end + 'MANAGE_CHANNEL':'70', + 'EXTERNAL_AUTHENTICATE':'82', + 'GET_CHALLENGE':'84', + 'INTERNAL_AUTHENTICATE':'88', + 'SELECT_FILE':'A4', + #vonjeek start + 'VONJEEK_SELECT_FILE':'A5', + 'VONJEEK_UPDATE_BINARY':'A6', + 'VONJEEK_SET_MRZ':'A7', + 'VONJEEK_SET_BAC':'A8', + 'VONJEEK_SET_DATASET':'AA', + #vonjeek end + # special for JCOP + 'MIFARE_ACCESS':'AA', + 'ATR_HIST':'AB', + 'SET_RANDOM_UID':'AC', + # JCOP end + 'READ_BINARY':'B0', + 'READ_RECORD(S)':'B2', + 'GET_RESPONSE':'C0', + 'ENVELOPE':'C2', + 'GET_DATA':'CA', + 'WRITE_BINARY':'D0', + 'WRITE_RECORD':'D2', + 'UPDATE_BINARY':'D6', + 'PUT_DATA':'DA', + 'UPDATE_DATA':'DC', + 'CREATE_FILE':'E0', + 'APPEND_RECORD':'E2', + # Global Platform + 'GET_STATUS':'F2', + # GP end + 'READ_BALANCE':'4C', + 'INIT_LOAD': '40', + 'LOAD_CMD':'42', + 'WRITE_MEMORY':'7A', + 'READ_MEMORY':'78', + } + # some control parameters + ISO_7816_SELECT_BY_NAME= '04' + ISO_7816_SELECT_BY_EF= '02' + ISO_7816_OPTION_FIRST_OR_ONLY= '00' + ISO_7816_OPTION_NEXT_OCCURRENCE= '02' + + # well known AIDs + AID_CARD_MANAGER= 'A000000003000000' + AID_MRTD= 'A0000002471001' + AID_JAVA_LANG= 'A0000000620001' + AID_JAVACARD_FRAMEWORK= 'A0000000620101' + AID_JAVACARD_SECURITY= 'A0000000620102' + AID_JAVARCARDX_CRYPTO= 'A0000000620201' + AID_FIPS_140_2= 'A000000167413001' + AID_JAVACARD_BIOMETRY= 'A0000001320001' + AID_SECURITY_DOMAIN= 'A0000000035350' + AID_PKCS_15= 'A000000063' + AID_JCOP_IDENTIFY= 'A000000167413000FF' + + # Global Platform + CLA_GLOBAL_PLATFORM= '80' + GP_MAC_KEY= '404142434445464748494A4B4C4D4E4F' + GP_ENC_KEY= '404142434445464748494A4B4C4D4E4F' + GP_KEK_KEY= '404142434445464748494A4B4C4D4E4F' + GP_NO_ENCRYPTION= '00' + GP_C_MAC= '01' + GP_C_MAC_DECRYPTION= '02' + GP_SCP02= '02' + GP_REG_DATA= 'E3' + GP_REG_AID= '4F' + GP_REG_LCS= '9F70' + GP_REG_PRIV= 'C5' + GP_FILTER_ISD= '80' + GP_FILTER_ASSD= '40' + GP_FILTER_ELF= '20' + + ISO_OK= '9000' + ISO_SECURE= '6982' + ISO_NOINFO= '6200' + + ISO_SPEED= {'00':'106kBaud',\ + '02':'212kBaud',\ + '04':'424kBaud',\ + '08':'848kBaud'} + ISO_FRAMESIZE= { '00':'16',\ + '01':'24',\ + '02':'32',\ + '03':'40',\ + '04':'48',\ + '05':'64',\ + '06':'96',\ + '07':'128',\ + '08':'256'} + ISO7816ErrorCodes= { + '61':'SW2 indicates the number of response bytes still available', + '6200':'No information given', + '6281':'Part of returned data may be corrupted', + '6282':'End of file/record reached before reading Le bytes', + '6283':'Selected file invalidated', + '6284':'FCI not formatted according to ISO7816-4 section 5.1.5', + '6300':'No information given', + '6301':'ACR: PN532 does not respond', + '6327':'ACR: Contacless Response invalid checksum', + '637F':'ACR: PN532 invalid Contactless Command', + '6381':'File filled up by the last write', + '6382':'Card Key not supported', + '6383':'Reader Key not supported', + '6384':'Plain transmission not supported', + '6385':'Secured Transmission not supported', + '6386':'Volatile memory not available', + '6387':'Non Volatile memory not available', + '6388':'Key number not valid', + '6389':'Key length is not correct', + '63C':'Counter provided by X (valued from 0 to 15) (exact meaning depending on the command)', + '64':'State of non-volatile memory unchanged (SW2=00, other values are RFU)', + '6400':'Card Execution error', + '6500':'No information given', + '6581':'Memory failure', + '66':'Reserved for security-related issues (not defined in this part of ISO/IEC 7816)', + '6700':'Wrong length', + '6800':'No information given', + '6881':'Logical channel not supported', + '6882':'Secure messaging not supported', + '6900':'No information given', + '6981':'Command incompatible with file structure', + '6982':'Security status not satisfied', + '6983':'Authentication method blocked', + '6984':'Referenced data invalidated', + '6985':'Conditions of use not satisfied', + '6986':'Command not allowed (no current EF)', + '6987':'Expected SM data objects missing', + '6988':'SM data objects incorrect', + '6A00':'No information given', + '6A80':'Incorrect parameters in the data field', + '6A81':'Function not supported', + '6A82':'File not found', + '6A83':'Record not found', + '6A84':'Not enough memory space in the file', + '6A85':'Lc inconsistent with TLV structure', + '6A86':'Incorrect parameters P1-P2', + '6A87':'Lc inconsistent with P1-P2', + '6A88':'Referenced data not found', + '6B00':'Wrong parameter(s) P1-P2', + '6C':'Wrong length Le: SW2 indicates the exact length', + '6D00':'Instruction code not supported or invalid', + '6E00':'Class not supported', + '6F00':'No precise diagnosis', + '9000':'No further qualification', + 'ABCD':'RFIDIOt: Reader does not support this command', + 'F':'Read error or Security status not satisfied', + 'FFFB':'Mifare (JCOP) Block Out Of Range', + 'FFFF':'Unspecified Mifare (JCOP) Error', + 'N':'No precise diagnosis', + 'PC00':'No TAG present!', + 'PC01':'PCSC Communications Error', + 'PN00': 'PN531 Communications Error', + 'R':'Block out of range', + 'X':'Authentication failed', + } + DES_IV='\0\0\0\0\0\0\0\0' + DES_PAD= [chr(0x80),chr(0),chr(0),chr(0),chr(0),chr(0),chr(0),chr(0)] + DES_PAD_HEX= '8000000000000000' + KENC= '\0\0\0\1' + KMAC= '\0\0\0\2' + DO87= '870901' + DO8E= '8E08' + DO97= '9701' + DO99= '99029000' + # + # frosch command set + # + # + # Reader Key Init Mode (update internal secret key) + FR_RWD_Key_Init_Mode= chr(0x4B) + # Reader Key Init Mode Reset (exit key init mode) + FR_RWD_KI_Reset= chr(0x52) + # READER Key Init Mode Read EEPROM + FR_RWD_KI_Read_EE_Data= chr(0x58) + # Reader Stop + FR_RWD_Stop_Cmd= chr(0xA6) + # Reader Reset + FR_RWD_HF_Reset= chr(0x68) + # Reader Version + FR_RWD_Get_Version= chr(0x56) + # Hitag1 Get Serial Number + FR_HT1_Get_Snr= chr(0x47) + # Hitag1 Get Serial Number & set tag into Advanced Protocol Mode + FR_HT1_Get_Snr_Adv= chr(0xA2) + # Hitag1 Select Last Seen + FR_HT1_Select_Last= chr(0x53) + # Hitag1 Select Serial Number + FR_HT1_Select_Snr= chr(0x53) + # Hitag1 Read Page + FR_HT1_Read_Page= chr(0x50) + # Hitag2 Get Serial Number (password mode) + FR_HT2_Get_Snr_PWD= chr(0x80) + chr(0x00) + # Hitag2 Get Serial Number Reset (to reset for normal access when in public modes) + FR_HT2_Get_Snr_Reset= chr(0x80) + # Hitag2 Halt Selected + FR_HT2_Halt_Selected= chr(0x81) + # Hitag2 read page + FR_HT2_Read_Page= chr(0x82) + # Hitag2 Read Miro (Unique / Public Mode A) + FR_HT2_Read_Miro= chr(0x4d) + # Hitag2 Read Public B (FDX-B) + FR_HT2_Read_PublicB= chr(0x9e) + # Hitag2 Write Page + FR_HT2_Write_Page= chr(0x84) + # + # frosch errors + # + FROSCH_Errors= { '00':'No Error',\ + '02':'Error',\ + '07':'No Error',\ + 'eb':'Antenna Overload',\ + 'f1':'EEPROM Read Protected',\ + 'f2':'EEPROM Write Protected',\ + 'f3':'EEPROM Wrong - Old Data',\ + 'f4':'EEPROM Error',\ + 'f5':'CryptoBlock not INIT',\ + 'f6':'Acknowledgement Error',\ + 'f9':'Authentication Error',\ + 'fa':'Incorrect Password TAG',\ + 'fb':'Incorrect Password RWD',\ + 'fc':'Timeout',\ + 'fd':'No TAG present!',\ + 'ff':'Serial port fail or wrong mode'} + # + # frosch constants + # + FR_BAUD_RATE= { 9600:chr(0x01),\ + 14400:chr(0x02),\ + 19200:chr(0x03),\ + 38400:chr(0x04),\ + 57600:chr(0x05),\ + 115200:chr(0x06)} + FR_NO_ERROR= chr(0x00) + FR_PLAIN= chr(0x00) + FR_CRYPTO= chr(0x01) + FR_TIMEOUT= 'fc' + FR_COMMAND_MODE= 0x00 + FR_KEY_INIT_MODE= 0x01 + # + # frosch statics + # + FR_BCC_Mode= FR_COMMAND_MODE + # + # DemoTag command set + # + DT_SET_UID= 'u' + # + # DemoTag Errors + # + DT_ERROR= '?' + # + # PCSC APDUs + # + # these are basically standard APDUs but with fields filled in and using OmniKey terminology + # should really unify them all, but for now... + # COMMAND : [Class, Ins, P1, P2, DATA, LEN] + PCSC_APDU= { + 'ACS_14443_A' : ['d4','40','01'], + 'ACS_14443_B' : ['d4','42','02'], + 'ACS_14443_0' : ['d5','86','80', '05'], + 'ACS_DISABLE_AUTO_POLL' : ['ff','00','51','3f','00'], + 'ACS_DIRECT_TRANSMIT' : ['ff','00','00','00'], + 'ACS_GET_SAM_SERIAL' : ['80','14','00','00','08'], + 'ACS_GET_SAM_ID' : ['80','14','04','00','06'], + 'ACS_GET_READER_FIRMWARE' : ['ff','00','48','00','00'], + 'ACS_GET_RESPONSE' : ['ff','c0','00','00'], + 'ACS_GET_STATUS' : ['d4','04'], + 'ACS_IN_LIST_PASSIVE_TARGET' : ['d4','4a'], + 'ACS_LED_GREEN' : ['ff','00','40','0e','04','00','00','00','00'], + 'ACS_LED_ORANGE' : ['ff','00','40','0f','04','00','00','00','00'], + 'ACS_LED_RED' : ['ff','00','40','0d','04','00','00','00','00'], + 'ACS_MIFARE_LOGIN' : ['d4','40','01'], + 'ACS_READ_MIFARE' : ['d4','40','01','30'], + 'ACS_POLL_MIFARE' : ['d4','4a','01','00'], + 'ACS_POWER_OFF' : ['d4','32','01','00'], + 'ACS_POWER_ON' : ['d4','32','01','01'], + 'ACS_RATS_14443_4_OFF' : ['d4','12','24'], + 'ACS_RATS_14443_4_ON' : ['d4','12','34'], + 'ACS_SET_PARAMETERS' : ['d4','12'], + 'ACS_SET_RETRY' : ['d4','32','05','00','00','00'], + 'AUTHENTICATE' : ['ff', ISOAPDU['INTERNAL_AUTHENTICATE']], + 'GUID' : ['ff', ISOAPDU['GET_DATA'], '00', '00', '00'], + 'ACS_GET_ATS' : ['ff', ISOAPDU['GET_DATA'], '01', '00', '00'], + 'LOAD_KEY' : ['ff', ISOAPDU['EXTERNAL_AUTHENTICATE']], + 'READ_BLOCK' : ['ff', ISOAPDU['READ_BINARY']], + 'UPDATE_BLOCK' : ['ff', ISOAPDU['UPDATE_BINARY']], + 'VERIFY' : ['ff', ISOAPDU['VERIFY']], + 'WRITE_BLOCK' : ['ff', ISOAPDU['WRITE_BINARY']], + } + # PCSC Errors + PCSC_NO_CARD= 'PC00' + PCSC_COMMS_ERROR= 'PC01' + PCSC_VOLATILE= '00' + PCSC_NON_VOLATILE= '20' + # PCSC Contactless Storage Cards + PCSC_CSC= '804F' + # PCSC Workgroup RID + PCSC_RID= 'A000000306' + # PCSC Storage Standard Byte + PCSC_SS= { '00':'No information given',\ + '01':'ISO 14443 A, part 1',\ + '02':'ISO 14443 A, part 2',\ + '03':'ISO 14443 A, part 3',\ + '04':'RFU',\ + '05':'ISO 14443 B, part 1',\ + '06':'ISO 14443 B, part 2',\ + '07':'ISO 14443 B, part 3',\ + '08':'RFU',\ + '09':'ISO 15693, part 1',\ + '0A':'ISO 15693, part 2',\ + '0B':'ISO 15693, part 3',\ + '0C':'ISO 15693, part 4',\ + '0D':'Contact (7816-10) I2 C',\ + '0E':'Contact (7816-10) Extended I2 C',\ + '0F':'Contact (7816-10) 2WBP',\ + '10':'Contact (7816-10) 3WBP',\ + 'FF':'RFU'} + # PCSC card names + PCSC_NAME= { '0000':'No name given',\ + '0001':'Mifare Standard 1K',\ + '0002':'Mifare Standard 4K',\ + '0003':'Mifare Ultra light',\ + '0004':'SLE55R_XXXX',\ + '0006':'SR176',\ + '0007':'SRI X4K',\ + '0008':'AT88RF020',\ + '0009':'AT88SC0204CRF',\ + '000A':'AT88SC0808CRF',\ + '000B':'AT88SC1616CRF',\ + '000C':'AT88SC3216CRF',\ + '000D':'AT88SC6416CRF',\ + '000E':'SRF55V10P',\ + '000F':'SRF55V02P',\ + '0010':'SRF55V10S',\ + '0011':'SRF55V02S',\ + '0012':'TAG_IT',\ + '0013':'LRI512',\ + '0014':'ICODESLI',\ + '0015':'TEMPSENS',\ + '0016':'I.CODE1',\ + '0017':'PicoPass 2K',\ + '0018':'PicoPass 2KS',\ + '0019':'PicoPass 16K',\ + '001A':'PicoPass 16Ks',\ + '001B':'PicoPass 16K(8x2)',\ + '001C':'PicoPass 16KS(8x2)',\ + '001D':'PicoPass 32KS(16+16)',\ + '001E':'PicoPass 32KS(16+8x2)',\ + '001F':'PicoPass 32KS(8x2+16)',\ + '0020':'PicoPass 32KS(8x2+8x2)',\ + '0021':'LRI64',\ + '0022':'I.CODE UID',\ + '0023':'I.CODE EPC',\ + '0024':'LRI12',\ + '0025':'LRI128',\ + '0026':'Mifare Mini'} + # ACS Constants + ACS_TAG_FOUND= 'D54B' + ACS_DATA_OK= 'D541' + ACS_NO_SAM= '3B00' + ACS_TAG_MIFARE_ULTRA= 'MIFARE Ultralight' + ACS_TAG_MIFARE_1K= 'MIFARE 1K' + ACS_TAG_MIFARE_MINI= 'MIFARE MINI' + ACS_TAG_MIFARE_4K= 'MIFARE 4K' + ACS_TAG_MIFARE_DESFIRE= 'MIFARE DESFIRE' + ACS_TAG_JCOP30= 'JCOP30' + ACS_TAG_JCOP40= 'JCOP40' + ACS_TAG_MIFARE_OYSTER= 'London Transport Oyster' + ACS_TAG_GEMPLUS_MPCOS= 'Gemplus MPCOS' + + ACS_TAG_TYPES= { + '00':ACS_TAG_MIFARE_ULTRA, + '08':ACS_TAG_MIFARE_1K, + '09':ACS_TAG_MIFARE_MINI, + '18':ACS_TAG_MIFARE_4K, + '20':ACS_TAG_MIFARE_DESFIRE, + '28':ACS_TAG_JCOP30, + '38':ACS_TAG_JCOP40, + '88':ACS_TAG_MIFARE_OYSTER, + '98':ACS_TAG_GEMPLUS_MPCOS, + } + # HID constants + HID_PROX_H10301= '3B0601' + HID_PROX_H10302= '3B0702' + HID_PROX_H10304= '3B0704' + HID_PROX_H10320= '3B0514' + HID_PROX_CORP1K= '3B0764' + HID_PROX_TYPES= { + HID_PROX_H10301:'HID Prox H10301 - 26 bit (FAC + CN)', + HID_PROX_H10302:'HID Prox H10302 - 37 bit (CN)', + HID_PROX_H10304:'HID Prox H10304 - 37 bit (FAC + CN)', + HID_PROX_H10320:'HID Prox H10320 - 32 bit clock/data card', + HID_PROX_CORP1K:'HID Prox Corp 1000 - 35 bit (CIC + CN)', + } + # + # local/informational functions + # + def info(self,caller): + if len(caller) > 0: + print caller + ' (using RFIDIOt v' + self.VERSION + ')' + if not NoInit: + self.reset() + self.version() + if len(caller) > 0: + print ' Reader:', + if self.readertype == self.READER_ACG: + print 'ACG ' + self.readername, + print ' (serial no: ' + self.id() + ')' + if self.readertype == self.READER_FROSCH: + print 'Frosch ' + self.ToBinary(self.data[:16]) + ' / ' + self.ToBinary(self.data[16:32]), + print ' (serial no: ' + self.data[32:54] + ')' + if self.readertype == self.READER_PCSC: + print 'PCSC ' + self.readername + if self.readersubtype == self.READER_ACS and self.pcsc_protocol == smartcard.scard.SCARD_PROTOCOL_T0: + # get ATR to see if we have a SAM + self.select() + if not self.pcsc_atr[:4] == self.ACS_NO_SAM: + if self.acs_get_firmware_revision(): + print ' (Firmware: %s, ' % self.ToBinary(self.data), + else: + print "\ncan't get firmware revision!" + os._exit(True) + if self.acs_get_sam_serial(): + print 'SAM Serial: %s, ' % self.data, + else: + print "\ncan't get SAM Serial Number!" + os._exit(True) + if self.acs_get_sam_id(): + print 'SAM ID: %s)' % self.ToBinary(self.data) + else: + print "\ncan't get SAM Serial Number!" + os._exit(True) + elif self.readersubtype == self.READER_ACS and self.pcsc_protocol == smartcard.scard.SCARD_PROTOCOL_T1: + if self.acs_get_firmware_revision(): + print ' (Firmware: %s)' % self.ToBinary(self.data) + else: + print "\ncan't get firmware revision!" + os._exit(True) + if self.readertype == self.READER_LIBNFC: + print 'LibNFC', self.readername + print + # + # reader functions + # + def reset(self): + if self.readertype == self.READER_ACG: + # send a select to stop just in case it's in multi-select mode + self.ser.write('s') + self.ser.readline() + self.ser.flushInput() + self.ser.flushOutput() + # now send a reset and read response + self.ser.write('x') + self.ser.readline() + # now send a select and read remaining lines + self.ser.write('s') + self.ser.readline() + self.ser.flushInput() + self.ser.flushOutput() + return True + if self.readertype == self.READER_FROSCH: + if self.frosch(self.FR_RWD_HF_Reset,''): + return True + else: + print self.FROSCH_Errors[self.errorcode] + os._exit(True) + if self.readertype == self.READER_PCSC: + if self.readersubtype == self.READER_ACS: + self.acs_power_off() + self.acs_power_on() + self.data= 'A PCSC Reader (need to add reset function!)' + if self.readertype == self.READER_LIBNFC: + self.nfc.powerOff() + self.nfc.powerOn() + def version(self): + if self.readertype == self.READER_ACG: + self.ser.write('v') + try: + self.data= self.ser.readline()[:-2] + self.readername= self.data + except: + print '\nReader not responding - check baud rate' + os._exit(True) + # check for garbage data (wrong baud rate) + if not self.data or self.data[0] < ' ' or self.data[0] > '~': + print '\nGarbage received from reader - check baud rate' + os._exit(True) + return True + if self.readertype == self.READER_FROSCH: + if self.frosch(self.FR_RWD_Get_Version,''): + return True + else: + print self.FROSCH_Errors[self.errorcode] + os._exit(True) + def id(self): + return self.readEEPROM(0)[:2] + self.readEEPROM(1)[:2] + self.readEEPROM(2)[:2] + self.readEEPROM(3)[:2] + def station(self): + return self.readEEPROM(0x0a)[:2] + def PCON(self): + return self.readEEPROM(0x0b)[:2] + def PCON2(self): + return self.readEEPROM(0x13)[:2] + def PCON3(self): + return self.readEEPROM(0x1b)[:2] + def BAUD(self): + return self.readEEPROM(0x0c)[:2] + def CGT(self): + return self.readEEPROM(0x0d)[:2] + def opmode(self): + return self.readEEPROM(0x0e)[:2] + def SST(self): + return self.readEEPROM(0x0f)[:2] + def ROT(self): + return self.readEEPROM(0x14)[:2] + def RRT(self): + return self.readEEPROM(0x15)[:2] + def AFI(self): + return self.readEEPROM(0x16)[:2] + def STOa(self): + return self.readEEPROM(0x17)[:2] + def STOb(self): + return self.readEEPROM(0x18)[:2] + def STOs(self): + return self.readEEPROM(0x19)[:2] + def readEEPROM(self,byte): + self.ser.write('rp%02x' % byte) + return self.ser.readline()[:2] + def writeEEPROM(self,byte,value): + self.ser.write('wp%02x%02x' % (byte,value)) + self.errorcode= self.ser.readline()[:-2] + if eval(self.errorcode) == value: + return True + return False + def settagtype(self,type): + if self.readertype == self.READER_ACG: + # ACG HF reader uses 't' for 'all', LF uses 'a' + if type == self.ALL: + if string.find(self.readername,'LFX') == 0: + type= 'a' + else: + type= 't' + self.ser.write('o' + type) + self.errorcode= self.ser.readline()[:-2] + if self.errorcode == 'O' + string.upper(type): + self.tagtype= type + return True + if self.readertype == self.READER_FROSCH: + if type == self.EM4x02: + return self.frosch(self.FR_HT2_Read_Miro,'') + if type == self.EM4x05: + return self.frosch(self.FR_HT2_Read_PublicB,'') + return False + # + # card functions + # + def pcsc_listreaders(self): + n= 0 + print 'PCSC devices:' + #for reader in self.pcsc.listReader(): + for reader in self.pcsc: + print ' No: %d\t\t%s' % (n,reader) + n += 1 + def libnfc_listreaders(self): + self.nfc.listreaders(NFCReader) + def waitfortag(self,message): + print message + # we need a way to interrupt infinite loop + if self.readersubtype == self.READER_OMNIKEY or self.readersubtype == self.READER_SCM: + wait=True + while wait: + try: + self.pcsc_connection.connect() + self.select() + wait=False + except: + sys.stdin.flush() + time.sleep(0.5) + else: + while not self.select(): + # do nothing + time.sleep(0.1) + return True + def select(self): + self.uid= '' + # return True or False and set tag type and data + if self.readertype == self.READER_ACG: + self.ser.write('s') + self.data= self.ser.readline()[:-2] + self.tagtype= self.data[:1] + if self.tagtype == self.ACG_FAIL: + self.errorcode= self.PCSC_NO_CARD + return False + # strip leading tag type from LFX response + if self.readername.find("LFX") == 0: + self.uid= self.data[1:] + else: + self.uid= self.data + return True + if self.readertype == self.READER_FROSCH: + if self.frosch(self.FR_HT2_Get_Snr_PWD,''): + # select returns an extra byte on the serial number, so strip it + self.data= self.data[:len(self.data) - 2] + self.tagtype= self.HITAG2 + self.uid= self.data + return True + if self.frosch(self.FR_HT1_Get_Snr,''): + # select returns an extra byte on the serial number, so strip it + # and preserve for after select command + serialno= self.data[:len(self.data) - 2] + if self.frosch(self.FR_HT1_Select_Last,''): + self.tagtype= self.HITAG1 + self.data= self.uid= serialno + return True + return False + if self.readertype == self.READER_PCSC: + try: + # start a new connection in case TAG has been switched + self.pcsc_connection.disconnect() + self.pcsc_connection.connect() + time.sleep(0.6) + self.pcsc_atr= self.ListToHex(self.pcsc_connection.getATR()) + atslen= 2 * int(self.pcsc_atr[3],16) + self.pcsc_ats= self.pcsc_atr[8:8 + atslen] + if self.readersubtype == self.READER_ACS: + self.acs_select_tag() + else: + self.pcsc_send_apdu(self.PCSC_APDU['GUID']) + except smartcard.Exceptions.NoCardException: + self.errorcode= self.PCSC_NO_CARD + return False + except: + self.errorcode= self.PCSC_COMMS_ERROR + return False + if self.errorcode == self.ISO_OK: + self.uid= self.data + if not self.readersubtype == self.READER_ACS: + self.tagtype= self.PCSCGetTagType(self.pcsc_atr) + # pcsc returns ISO15693 tags LSByte first, so reverse + if string.find(self.tagtype,'ISO 15693') >= 0: + self.data= self.uid= self.HexByteReverse(self.data) + return True + else: + return False + if self.readertype == self.READER_LIBNFC: + try: + if DEBUG: + print 'reading card using LIBNFC' + result = self.nfc.readISO14443A() + if result: + self.atr = result.atr + self.uid = result.uid + if DEBUG: + print 'ATR: ' + self.atr + print 'UID: ' + self.uid + return True + else: + if DEBUG: + print 'Error selecting card' + return False + except ValueError: + self.errorcode = 'Error reading card using LIBNFC' + e + return False + def h2publicselect(self): + "select Hitag2 from Public Mode A/B/C" + if self.readertype == self.READER_FROSCH: + if (self.frosch(self.FR_HT2_Get_Snr_Reset,self.FR_PLAIN + 'M')): + self.tagtype= self.HITAG2 + self.data= self.data[:8] + return True + return False + def h2login(self,password): + "login to hitag2 in password mode" + if not self.readertype == self.READER_ACG: + print 'Reader type not supported for hitag2login!' + return False + self.ser.write('l'+password) + ret= self.ser.readline()[:-2] + if ret == self.ACG_FAIL: + self.errorcode= ret + return False + return True + def hsselect(self,speed): + if self.readertype == self.READER_PCSC or self.readertype == self.READER_LIBNFC: + # low level takes care of this, so normal select only + if self.select(): + #fixme - find true speed/framesize + self.speed= '04' + self.framesize= '08' + return True + else: + return False + "high speed select - 106 (speed= 01), 212 (speed= 02), 424 (speed= 04) or 848 (speed= 08) kBaud" + self.ser.write('h'+speed) + ret= self.ser.readline()[:-2] + if ret == self.ACG_FAIL: + self.errorcode= ret + return False + sizebaud= ret[-2:] + self.speed= '%02d' % int(sizebaud[-1:]) + self.framesize= '%02d' % int(sizebaud[:-1]) + self.data= ret[:-2] + return True + # ACS specific commands + # + # note there are 2 different types of ACS command: + # + # standard APDU for reader - acs_send_reader_apdu + # pseudo APDU for contact or contactless card - acs_send_apdu + # + # contact and contacless commands are wrapped and passed to the NXP PN532 for processing + def acs_send_apdu(self,apdu): + "ACS send APDU to contacless card" + myapdu= self.HexArraysToArray(apdu) + # determine if this is for direct transmission to the card + if myapdu[0] == 'd4': + # build pseudo command for ACS contactless interface + lc= '%02x' % len(myapdu) + apduout= self.HexArrayToList(self.PCSC_APDU['ACS_DIRECT_TRANSMIT']+[lc]+myapdu) + else: + if myapdu[0] == 'ff' or myapdu[0] == '80': + apduout= self.HexArrayToList(myapdu) + else: + # build pseudo command for ACS 14443-A + lc= '%02x' % (len(myapdu) + len(self.PCSC_APDU['ACS_14443_A'])) + apduout= self.HexArrayToList(self.PCSC_APDU['ACS_DIRECT_TRANSMIT']+[lc]+self.PCSC_APDU['ACS_14443_A']+myapdu) + result, sw1, sw2= self.acs_transmit_apdu(apduout) + self.errorcode= '%02X%02X' % (sw1,sw2) + if self.errorcode == self.ISO_OK: + self.data= self.ListToHex(result) + if not myapdu[0] == 'ff' and not myapdu[0] == '80' and not myapdu[0] == 'd4': + # this is a 14443-A command, so needs further processing + # last 4 data bytes is status of wrapped command + if self.data[-4:] == self.ISO_OK and len(self.data) > 6: + # strip first 6 hex characters (ACS specific) + self.data= self.data[6:] + # strip last 4 to remove errorcode + self.data= self.data[:-4] + return True + else: + self.errorcode= self.data[-4:] + # strip ACS status and errorcode in case there is some data expected despite error + self.data= self.data[6:-4] + return False + return True + self.data= '' + return False + def acs_transmit_apdu(self,apdu): + "ACS send APDU and retrieve additional DATA if required" + if self.hcard is None: + result, sw1, sw2= self.pcsc_connection.transmit(apdu,protocol= self.pcsc_protocol) + if sw1 == 0x61: + # response bytes waiting + apduout= self.HexArrayToList(self.PCSC_APDU['ACS_GET_RESPONSE']+[('%02x' % sw2)]) + result, sw1, sw2= self.pcsc_connection.transmit(apduout,protocol= self.pcsc_protocol) + return result, sw1, sw2 + else: + hresult, response = smartcard.scard.SCardControl( self.hcard, IOCTL_SMARTCARD_VENDOR_IFD_EXCHANGE, apdu ) + if hresult != smartcard.scard.SCARD_S_SUCCESS: + return '',0x63,0x00 + #print 'Failed to control: ' + smartcard.scard.SCardGetErrorMessage(hresult) + #os._exit(True) + # evil hacky bodge as ACS returns only one byte for this APDU (ACS_DISABLE_AUTO_POLL) + # and we ignore failure of we're running on firmware V1 as it doesn't support this command + if apdu == [0xff,0x00,0x51,0x3f,0x00]: + #print 'here!!!' + if response == [0x3f] or self.hcard is not None: + return '',0x90,0x00 + else: + return '',0x63,0x00 + result = response[:-2] + sw1 = response[-2] + sw2 = response[-1] + return result, sw1, sw2 + + def acs_send_reader_apdu(self,apdu): + "ACS send APDU to reader" + myapdu= self.HexArraysToArray(apdu) + apduout= self.HexArrayToList(myapdu) + result, sw1, sw2= self.acs_transmit_apdu(apduout) + self.data= self.ListToHex(result) + self.errorcode= '%02X%02X' % (sw1,sw2) + return True + def acs_send_direct_apdu(self,apdu): + "ACS send APDU direct to TAG" + myapdu= self.HexArraysToArray(apdu) + # build pseudo command for ACS 14443-A via NXP PN532 + lc= '%02x' % (len(myapdu) + len(self.PCSC_APDU['ACS_14443_A'])) + apduout= self.HexArrayToList(self.PCSC_APDU['ACS_DIRECT_TRANSMIT']+[lc]+self.PCSC_APDU['ACS_14443_A']+myapdu) + result, sw1, sw2= self.acs_transmit_apdu(apduout) + self.errorcode= '%02X%02X' % (sw1,sw2) + if self.errorcode == self.ISO_OK: + self.data= self.ListToHex(result) + # strip direct wrapper response and header to get TAG response and DATA + if self.data[-4:] == self.ISO_OK and len(self.data) > 6: + self.data= self.data[6:] + self.data= self.data[:-4] + return True + else: + self.errorcode= self.data[-4:] + # strip ACS status and errorcode in case there is some data expected despite error + self.data= self.data[6:-4] + return False + return True + else: + self.data= '' + return False + def acs_rats(self,control): + "ACS RATS on/off" + if control: + return self.acs_send_apdu(self.PCSC_APDU['ACS_RATS_14443_4_ON']) + else: + return self.acs_send_apdu(self.PCSC_APDU['ACS_RATS_14443_4_OFF']) + def acs_mifare_login(self,block,key,keytype): + "ACS Mifare Login" + if keytype == 'BB': + keytype= '61' + else: + keytype= '60' + loginblock= '%02x' % block + if self.tagtype == self.ACS_TAG_MIFARE_1K or self.tagtype == self.ACS_TAG_MIFARE_4K: + status= self.acs_send_apdu(self.PCSC_APDU['ACS_MIFARE_LOGIN']+[keytype]+[loginblock]+[key]+[self.uid]) + else: + self.errorcode= self.ISO_NOINFO + return False + if not status or not self.data[:4] == self.ACS_DATA_OK or not self.data[4:6] == '00': + self.errorcode= self.ISO_NOINFO + return False + self.errorcode= self.ISO_OK + return True + def acs_read_block(self,block): + "ACS READ Block" + readblock= '%02x' % block + read= False + if self.tagtype == self.ACS_TAG_MIFARE_ULTRA or self.tagtype == self.ACS_TAG_MIFARE_1K or self.tagtype == self.ACS_TAG_MIFARE_4K: + status= self.acs_send_apdu(self.PCSC_APDU['ACS_READ_MIFARE']+[readblock]) + read= True + if read: + if not status or len(self.data) < 8 or not self.data[:4] == self.ACS_DATA_OK: + self.errorcode= self.ISO_NOINFO + return False + # MIFARE ultralight returns 4 blocks although only asking for one, so truncate + if self.tagtype == self.ACS_TAG_MIFARE_ULTRA: + self.data= self.data[6:14] + else: + self.data= self.data[6:] + self.errorcode= self.ISO_OK + return True + print "Can't read %s blocks" % self.ACS_TAG_TYPES[self.tagtype] + os._exit(True) + def acs_get_sam_serial(self): + "ACS get SAM serial" + return self.acs_send_apdu(self.PCSC_APDU['ACS_GET_SAM_SERIAL']) + def acs_get_sam_id(self): + "ACS get SAM id" + return self.acs_send_apdu(self.PCSC_APDU['ACS_GET_SAM_ID']) + def acs_set_retry(self,time): + "ACS set retry" + # 'time' currently ignored due to lack of documentation - hard wired to '1' + return self.acs_send_apdu(self.PCSC_APDU['ACS_SET_RETRY']) + def acs_select_tag(self): + "ACS select TAG" + # power antenna off and on to reset ISO14443-4 tags + self.reset() + self.acs_send_apdu(self.PCSC_APDU['ACS_POLL_MIFARE']) + if not self.data[:4] == self.ACS_TAG_FOUND: + # this shouldn't happen as the command should return number of tags to be 0 instead + return False + tags= int(self.data[4:6]) + if tags == 0: + self.errorcode= self.PCSC_NO_CARD + return False + target= self.data[6:8] + self.sens_res= self.data[8:12] + self.sel_res= self.data[12:14] + length= int(self.data[14:16]) + if length == 0: + self.errorcode= self.PCSC_NO_CARD + return False + uid= self.data[16:16+length*2] + try: + self.tagtype= self.ACS_TAG_TYPES[self.sel_res] + except: + print 'unrecognised TAG type:', self.sel_res + print 'full ACS return:', self.data + self.tagtype= 'Unrecognised' + self.data= uid + return True + def acs_get_firmware_revision(self): + "ACS Get Firmware Revision" + self.acs_send_reader_apdu(self.PCSC_APDU['ACS_GET_READER_FIRMWARE']) + # 'special' APDU that doesn't return in the usual way. sw1,sw2 contains some of the data + if len(self.data) > 0: + self.data += self.errorcode + self.errorcode= self.ISO_OK + return True + self.data= '' + self.errorcode= self.ISO_NOINFO + return False + def acs_power_on(self): + "ACS Antenna Power On" + return self.acs_send_apdu(self.PCSC_APDU['ACS_POWER_ON']) + def acs_power_off(self): + "ACS Antenna Power Off" + return self.acs_send_apdu(self.PCSC_APDU['ACS_POWER_OFF']) + # Global Platform specific commands + def gp_external_authenticate(self,host_cryptogram,mac_key): + "Global Platform external authenticate" + cla= '84' + ins= 'EXTERNAL_AUTHENTICATE' + p1= '00' # security level 0 - plaintext + #p1= '01' # security level 1 - C-MAC + p2= '00' + data= self.ToHex(host_cryptogram) + lc= '10' # needs to include MAC that will be added after mac generation + mac= self.ToHex(self.DESMAC(self.ToBinary(cla+'82'+p1+p2+lc+data),mac_key,'')) + data += mac + return self.send_apdu('','','','',cla,ins,p1,p2,lc,data,'') + def gp_generate_session_key_01(self,hostchallenge,cardchallenge): + "Global Platform generate session key from host and card challenges (SCP01)" + derivation= cardchallenge[8:16] + derivation += hostchallenge[0:8] + derivation += cardchallenge[0:8] + derivation += hostchallenge[8:16] + return(derivation) + def gp_get_data(self,object): + "Global Platform get data" + cla= self.CLA_GLOBAL_PLATFORM + ins= 'GET_DATA' + p1= object[0:2] + p2= object[2:4] + le= '00' + return self.send_apdu('','','','',cla,ins,p1,p2,'','',le) + def gp_get_status(self,subset,control,aid): + "Global Platform get status" + cla= self.CLA_GLOBAL_PLATFORM + ins= 'GET_STATUS' + p1= subset + p2= control + data= '4F00' + aid + lc= '%02x' % (len(data) / 2) + le= '00' + return self.send_apdu('','','','',cla,ins,p1,p2,lc,data,le) + def gp_initialize_update(self,challenge): + "Global Platform initialize update" + cla= self.CLA_GLOBAL_PLATFORM + ins= 'INITIALIZE_UPDATE' + p1= '00' + p2= '00' + data= challenge + lc= '%02x' % (len(data) / 2) + le= '00' + return self.send_apdu('','','','',cla,ins,p1,p2,lc,data,le) + def gp_initialize_update_response_scp02(self,data): + "return broken down Initialize Update response (SCP02) - Key Diversification (10), Key Info (2), Sequence Counter (2), Card Challenge (6), Card Cryptogram (8)" + return data[0:20],data[20:24],data[24:28],data[28:40],data[40:56] + # ISO 7816 commands + def iso_7816_external_authenticate(self,response,key): + "7816 external authenticate" + ins= 'EXTERNAL_AUTHENTICATE' + lc= le= '%02x' % (len(response) / 2) + if self.send_apdu('','','','','',ins,'','',lc,response,le): + if self.MACVerify(self.data,key): + return True + return False + def iso_7816_fail(self,code): + "print 7816 failure code and exit" + if code == self.ACG_FAIL: + print "Application not implemented!" + os._exit(True) + print "Failed - reason code " + code + " (" + self.ISO7816ErrorCodes[code] + ")" + print + os._exit(True) + def iso_7816_get_challenge(self,length): + "get random challenge - challenge will be in .data" + ins= 'GET_CHALLENGE' + le= '%02x' % length + if DEBUG: + print "DEBUG: requesting %d byte challenge" % length + return self.send_apdu('','','','','',ins,'','','','',le) + def iso_7816_read_binary(self,bytes,offset): + "7816 read binary - data read will be in .data" + ins= 'READ_BINARY' + hexoffset= '%04x' % offset + p1= hexoffset[0:2] + p2= hexoffset[2:4] + le= '%02x' % bytes + return self.send_apdu('','','','','',ins,p1,p2,'','',le) + def iso_7816_select_file(self,file,control,options): + "7816 select file" + ins= 'SELECT_FILE' + lc= '%02x' % (len(file) / 2) + p1= control + p2= options + data= file + return self.send_apdu('','','','','',ins,p1,p2,lc,data,'') + def pcsc_send_apdu(self,apdu): + # build and transmit PCSC apdu (list as appropriate, e.g. [cla,ins,p1,p2,lc,data,le...]) + apdustring= '' + if self.readersubtype == self.READER_ACS: + return self.acs_send_apdu(apdu) + # apdu is a list which may contain long fields such as 'data', so first concatonate into + # one long string, then break up into 2 char hex fields + for d in apdu: + apdustring += d + apduout= self.HexToList(apdustring) + result, sw1, sw2= self.pcsc_connection.transmit(apduout,protocol= self.pcsc_protocol) + self.errorcode= '%02X%02X' % (sw1,sw2) + self.data= self.ListToHex(result) + # SCM readers need a little time to get over the excertion +# if self.readersubtype == self.READER_SCM: +# time.sleep(.1) + if self.errorcode == self.ISO_OK: + return True + return False + def send_apdu(self,option,pcb,cid,nad,cla,ins,p1,p2,lc,data,le): + "send iso-7816-4 apdu" + if not option: + option= '1f' + #option= '00' + if not pcb: + pcb= '02' + if not cla: + cla= '00' + if not p1: + p1= '00' + if not p2: + p2= '00' + if self.readertype == self.READER_PCSC: + return self.pcsc_send_apdu(cla+self.ISOAPDU[ins]+p1+p2+lc+data+le) + if self.readertype == self.READER_LIBNFC: + result = self.nfc.sendAPDU(cla+self.ISOAPDU[ins]+p1+p2+lc+data+le) + self.data = result[0:-4] + self.errorcode = result[len(result)-4:len(result)] + if self.errorcode == self.ISO_OK: + return True + return False + dlength= 5 + command= pcb+cla+self.ISOAPDU[ins]+p1+p2+lc+data+le + dlength += len(data) / 2 + dlength += len(lc) / 2 + dlength += len(le) / 2 + if DEBUG: + print 'sending: ' + 't' + '%02x' % dlength + option + command + self.ser.write('t' + '%02x' % dlength + option + command) + # need check for 'le' length as well + ret= self.ser.readline()[:-2] + if DEBUG: + print 'received:',ret + self.errorcode= ret[len(ret) - 4:len(ret)] + # copy data if more than just an error code (JCOP sometimes returns an error with data) + if len(ret) > 8: + self.data= ret[4:len(ret) - 4] + else: + self.data= '' + if self.errorcode == self.ISO_OK: + return True + return False +# return ret[4:len(ret) - 4] +# if not len(ret) / 2 == int(ret[0:2],16) + 1: +# return False +# return ret[4:int(le,16) * 2 + 4] + def login_iclass(self,page,keynum): + "login to an iclass page with a key stored on the reader" + if not self.readersubtype == self.READER_OMNIKEY: + self.errorcode= 'ABCD' + return False + ins= 'EXTERNAL_AUTHENTICATE' + p1= '00' + p2= '%02x' % keynum + lc= '08' + data= '0000000000000000' + if not self.send_apdu('','','','','80',ins,p1,p2,lc,data,''): + return False + return True + def login(self,sector,keytype,key): + "login to specified sector - returns True if successful, False if failed. If failure is due to an error, 'errorcode' will be set." + keytype= string.upper(keytype) + # use transport key if none specified + if not key: + key= self.MIFARE_TK[keytype] + if self.readertype == self.READER_ACG: + if keytype == 'FF': + keytype= 'AA' + if not sector == '': + if DEBUG: + print 'sending:', 'l' + ('%02x' % sector) + keytype + key + self.ser.write('l' + ('%02x' % sector) + keytype + key) + else: + if DEBUG: + print 'sending:','l' + keytype + key + self.ser.write('l' + keytype + key) + if key == '': + self.ser.write('\r') + self.errorcode= self.ser.readline()[0] + if DEBUG: + print 'received:', self.errorcode + if self.errorcode == 'L': + self.errorcode= '' + return True + return False + if self.readertype == self.READER_FROSCH: + return self.frosch(self.FR_HTS_TagAuthent,'') + if self.readertype == self.READER_PCSC: + if self.readersubtype == self.READER_ACS: + if self.acs_mifare_login(sector,key,keytype): + return True + else: + return False + # PCSC requires key to be loaded to reader, then login with key + if not self.PCSC_Keys.has_key(key): + # send key to reader and store in global PCSC_KEYS if not already sent + apdu= [] + apdu += self.PCSC_APDU['LOAD_KEY'] + if self.readersubtype == self.READER_OMNIKEY: + keynum= len(self.PCSC_Keys) + apdu += self.PCSC_NON_VOLATILE # load key to non-volatile reader memory + else: + apdu += self.PCSC_VOLATILE # load key to volatile reader memory + keynum= len(self.PCSC_Keys) + 96 # SCM Mifare keys live at hex 60+ + if keytype == 'BB': + keynumoffset= 1 + else: + keynumoffset= 0 + apdu.append('%02x' % (keynum + keynumoffset)) # p2 - key number + apdu.append('%02x' % (len(key) / 2)) # lc + apdu.append(key) # data + if not self.pcsc_send_apdu(apdu): + return False + if self.readersubtype == self.READER_OMNIKEY: + # readers with non-volatile memory only need the key once + self.PCSC_Keys[key]= keynum + else: + #use stored key if already sent + keynum= self.PCSC_Keys[key] + # now try to authenticate + return self.authenticate(sector,keytype, keynum) + def authenticate(self,sector,keytype, keynum): + keytype= string.upper(keytype) + apdu= [] + apdu += self.PCSC_APDU['AUTHENTICATE'] + block= '%04x' % sector + apdu.append(block[0:2]) # p1 sector msb + apdu.append(block[2:4]) # p1 sector lsb + if keytype == 'AA' or keytype == 'FF': + apdu.append('60') # keytype + elif keytype == 'BB': + apdu.append('61') # keytype + else: + apdu.append(keytype) + apdu.append('%02x' % keynum) # key number + ret= self.pcsc_send_apdu(apdu) + if ret == False: + # let PCSC get over it! + time.sleep(0.5) + return ret + def verify(self,keytype,key): + keytype= string.upper(keytype) + apdu= [] + apdu += self.PCSC_APDU['VERIFY'] + if keytype == 'AA' or keytype == 'FF': + apdu.append('60') # keytype + elif keytype == 'BB': + apdu.append('61') # keytype + apdu.append('00') + apdu.append('%02x' % (len(key) / 2)) + apdu.append(key) + ret= self.pcsc_send_apdu(apdu) + if ret == False: + # let PCSC get over it! + time.sleep(0.5) + return ret + def readblock(self,block): + if self.readertype == self.READER_FROSCH: + if self.tagtype == self.HITAG1: + return(self.frosch(self.FR_HT1_Read_Page,self.FR_PLAIN + chr(block))) + if self.tagtype == self.HITAG2: + return(self.frosch(self.FR_HT2_Read_Page,chr(block))) + if self.readertype == self.READER_ACG: + self.ser.write('r%02x' % block) + self.data= self.ser.readline()[:-2] + self.binary= '' + if len(self.data) == 1: + self.errorcode= self.data + self.data= '' + return False + count= 0 + while count * 2 < len(self.data): + self.binary += chr(int(self.data[count * 2:(count * 2) + 2],16)) + count += 1 + return True + if self.readertype == self.READER_PCSC: + if self.readersubtype == self.READER_ACS: + return self.acs_read_block(block) + apdu= [] + apdu += self.PCSC_APDU['READ_BLOCK'] + hexblock= '%04x' % block + apdu.append(hexblock[0:2]) # p1 + apdu.append(hexblock[2:4]) # p2 + # try reading with block length of 1 to provoke size error + apdu.append('01') # le + ret= self.pcsc_send_apdu(apdu) + # if failure is due to wrong block size, use block size returned by card instead + if self.errorcode.upper()[0:2] == '6C': + apdu[-1]= self.errorcode[2:4] + return self.pcsc_send_apdu(apdu) + else: + return ret + def readMIFAREblock(self,block): + if self.readblock(block): + self.MIFAREdata= self.data + else: + return False + count= 0 + while count * 2 < len(self.MIFAREdata): + self.MIFAREbinary += chr(int(self.MIFAREdata[count * 2:(count * 2) + 2],16)) + count += 1 + return True + def readvalueblock(self,block): + self.ser.write('rv%02x' % block) + self.MIFAREdata= self.ser.readline()[:-2] + if len(self.MIFAREdata) != self.MIFAREVALUELEN: + self.errorcode= self.MIFAREdata + self.MIFAREdata= '' + return False + count= 0 + while count * 2 < len(self.MIFAREdata): + self.MIFAREbinary += chr(int(self.MIFAREdata[count * 2:(count * 2) + 2],16)) + count += 1 + return True + def writeblock(self,block,data): + if self.readertype == self.READER_FROSCH: + #if self.tagtype == self.HITAG1: + # return(self.frosch(self.FR_HT1_Read_Page,self.FR_PLAIN + chr(block))) + if self.tagtype == self.HITAG2: + return(self.frosch(self.FR_HT2_Write_Page,chr(block) + self.ToBinary(data))) + if self.readertype == self.READER_ACG: + self.ser.write('w%02x%s' % (block,data)) + x= self.ser.readline()[:-2] + if x == string.upper(data): + self.errorcode= '' + return True + self.errorcode= x + return False + if self.readertype == self.READER_PCSC: + apdu= [] + apdu += self.PCSC_APDU['UPDATE_BLOCK'] + hexblock= '%04x' % block + apdu.append(hexblock[0:2]) # p1 + apdu.append(hexblock[2:4]) # p2 + apdu.append('%02x' % (len(data) / 2)) # le + apdu.append(data) + return self.pcsc_send_apdu(apdu) + def writevalueblock(self,block,data): + self.ser.write('wv%02x%s' % (block,data)) + x= self.ser.readline()[:-2] + if x == string.upper(data): + self.errorcode= '' + return True + self.errorcode= x + return False + def frosch(self,command,data): + "send frosch commands with check digit" + command += data + commandlen= len(command) + bcc= self.frosch_bcc_out(command,commandlen + 1) + # send length + command + checkdigit + if DEBUG: + print 'Sending: ', + self.HexPrint(chr(commandlen + 1) + command + chr(bcc)) + self.ser.write(chr(commandlen + 1) + command + chr(bcc)) + ret= '' + # perform a blocking read - returned byte is number of chars still to read + ret += self.ser.read(1) + # if read times out, reset may be required for normal read mode + if len(ret) == 0: + if command == self.FR_HT2_Read_PublicB or command == self.FR_HT2_Read_Miro: + self.frosch(self.FR_RWD_Stop_Cmd,'') + self.errorcode= self.FR_TIMEOUT + return False + # now read the rest + ret += self.ser.read(ord(ret[0])) + if DEBUG: + print 'ret: %d ' % len(ret), + self.HexPrint(ret) + # check integrity of return + bcc= self.frosch_bcc_in(ret,0) + if not bcc == ord(ret[len(ret) - 1]): + # may be reporting an error with wrong BCC set + if ret[0] == chr(0x02) and not ret[1] == chr(0x00): + self.data= '' + self.errorcode= self.ToHex(ret[1]) + return False + print 'Frosch error! Checksum error:', + self.HexPrint(ret) + print 'Expected BCC: %02x' % bcc + os._exit(True) + status= ret[1] + if status == self.FR_NO_ERROR: + self.errorcode= '' + # for consistency with ACG, data is converted to printable hex before return + self.data= self.ToHex(ret[2:len(ret) - 1]) + return True + else : + self.errorcode= self.ToHex(status) + self.data= '' + if DEBUG: + print "Frosch error:", int(self.errorcode,16) - 256 + # reader may need resetting to normal read mode + if command == self.FR_HT2_Read_PublicB or command == self.FR_HT2_Read_Miro: + self.frosch(self.FR_RWD_Stop_Cmd,'') + return False + def frosch_bcc(self,data,seed): + bcc= seed + if self.FR_BCC_Mode == self.FR_COMMAND_MODE: + for x in range(len(data)): + bcc= xor(bcc,ord(data[x])) + else: + for x in range(len(data)): + bcc += ord(data[x]) + bcc= int(bcc & 0xff) + return bcc + def frosch_bcc_in(self,data,seed): + return self.frosch_bcc(data[:len(data) - 1],seed) + def frosch_bcc_out(self,data,seed): + return self.frosch_bcc(data,seed) + def frosch_key_init_mode(self,passwd): + "enter key init mode" + status= self.frosch(self.FR_RWD_Key_Init_Mode,self.ToBinary(passwd)) + # frosch BCC calculation mode changes once we enter key init mode + if status: + self.FR_BCC_Mode= self.FR_KEY_INIT_MODE + return status + def frosch_read_ee_data(self,item): + "read RWD EEPROM" + # item defines which personalization data is to be read + # 0x00 ... Password + # 0x01 ... Key A + # 0x02 ... Key B + # 0x03 ... Logdata 0A + # 0x04 ... Logdata 0B + # 0x05 ... Logdata 1A + # 0x06 ... Logdata 1B + return self.frosch(self.FR_RWD_KI_Read_EE_Data,self.ToBinary(item)) + def demotag(self,command,data): + "send DemoTag commands" + if self.ser.write(command + data): + x= self.ser.readline()[:-2] + if x == self.DT_ERROR: + self.errorcode= x + return False + self.data= x + return True + return False + # + # data manipulation + # + def GetRandom(self,size): + data= '' + for x in range(size): + data += '%02x' % int(random.uniform(0,0xff)) + return data + def Parity(self,data,parity): + # return parity bit to make odd or even as required + myparity= 0 + for x in range(len(data)): + myparity += int(data[x],2) + myparity %= 2 + return xor(myparity,parity) + def Unique64Bit(self,data): + "convert binary ID to Unique formatted 64 bit data block" + # standard header == 9 bits of '1' + out= '111111111' + # break output into 4 bit chunks and add parity + colparity= [0,0,0,0] + for x in range(0,len(data),4): + parity= 0 + chunk= data[x:x+4] + for y in range(4): + parity += int(chunk[y],2) + colparity[y] += int(chunk[y],2) + out += chunk + '%s' % (int(parity) % 2) + # add column parity + for x in range(4): + out += '%s' % (int(colparity[x]) % 2) + # add stop bit + out += '0' + return out + def UniqueToEM(self,data): + "convert Unique ID to raw EM4x02 ID" + # swap words + tmp= '' + for x in range(5): + tmp += data[x * 2 + 1] + data[x * 2] + # reverse bits + return self.ToBinaryString(self.ToBinary(tmp))[::-1] + def EMToUnique(self,data): + "convert raw EM4x02 ID to Unique" + return self.ToHex(self.BitReverse(self.ToBinary(data))) + def HexToQ5(self,data): + "conver human readable HEX to Q5 ID" + return self.ToBinaryString(self.ToBinary(data)) + def crcccitt(self,data): + crcvalue= 0x0000 + for x in range(len(data)): + crcvalue= self.crc(crcvalue,data[x],MASK_CCITT) + return crcvalue + def crc(self, crc, data, mask=MASK_CRC16): + for char in data: + c = ord(char) + c = c << 8 + for j in xrange(8): + if (crc ^ c) & 0x8000: + crc = (crc << 1) ^ mask + else: + crc = crc << 1 + c = c << 1 + return crc & 0xffff + def crc16(self,data): + crcValue=0x0000 + crc16tab = (0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, + 0xC241, 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, + 0x0440, 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, + 0x0E40, 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, + 0xC841, 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, + 0x1A40, 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, + 0xDC41, 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, + 0xD641, 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, + 0x1040, 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, + 0x3240, 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, + 0xF441, 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, + 0xFE41, 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, + 0x3840, 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, + 0xEA41, 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, + 0x2C40, 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, + 0x2640, 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, + 0xE041, 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, + 0x6240, 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, + 0xA441, 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, + 0xAE41, 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, + 0x6840, 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, + 0xBA41, 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, + 0x7C40, 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, + 0x7640, 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, + 0xB041, 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, + 0x9241, 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, + 0x5440, 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, + 0x5E40, 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, + 0x9841, 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, + 0x4A40, 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, + 0x8C41, 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, + 0x8641, 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, + 0x4040) + for ch in data: + tmp=crcValue^(ord(ch)) + crcValue=(crcValue>> 8)^crc16tab[(tmp & 0xff)] + return crcValue + def MIFAREmfb(self,data): + "Set variables from standard MIFARE manufacturer block (block 0 sector 0)" + self.MIFAREserialnumber= data[0:8] + self.MIFAREcheckbyte= data[8:10] + self.MIFAREmanufacturerdata= data[10:32] + def MIFAREkb(self,data): + "Set variables from standard MIFARE key block (trailing sector)" + self.MIFAREkeyA= data[0:12] + self.MIFAREaccessconditions= data[12:18] + self.MIFAREaccessconditionsuserbyte= data[18:20] + self.MIFAREC1= int(data[14:16],16) >> 4 + self.MIFAREC2= int(data[16:18],16) & 0x0f + self.MIFAREC3= (int(data[16:18],16) & 0xf0) >> 4 + self.MIFAREblock0AC= str(self.MIFAREC1 & 0x01) + str(self.MIFAREC2 & 0x01) + str(self.MIFAREC3 & 0x01) + self.MIFAREblock1AC= str((self.MIFAREC1 & 0x02) >> 1) + str((self.MIFAREC2 & 0x02) >> 1) + str((self.MIFAREC3 & 0x02) >> 1) + self.MIFAREblock2AC= str((self.MIFAREC1 & 0x04) >> 2) + str((self.MIFAREC2 & 0x04) >> 2) + str((self.MIFAREC3 & 0x04) >> 2) + self.MIFAREblock3AC= str((self.MIFAREC1 & 0x08) >> 3) + str((self.MIFAREC2 & 0x08) >> 3) + str((self.MIFAREC3 & 0x08) >> 3) + self.MIFAREkeyB= data[20:32] + def MIFAREvb(self,data): + "Set variables from standard MIFARE value block" + self.MIFAREvalue= data[0:4] + self.MIFAREvalueinv= data[4:8] + self.MIFAREvalue2= data[8:12] + self.MIFAREaddr= data[12] + self.MIFAREaddrinv= data[13] + self.MIFAREaddr2= data[14] + self.MIFAREaddrinv2= data[15] + def MRPmrzl(self,data): + "Set variables from Machine Readable Zone (Lower)" + self.MRPnumber= data[0:9] + self.MRPnumbercd= data[9] + self.MRPnationality= data[10:13] + self.MRPdob= data[13:19] + self.MRPdobcd= data[19] + self.MRPsex= data[20] + self.MRPexpiry= data[21:27] + self.MRPexpirycd= data[27] + self.MRPoptional= data[28:42] + self.MRPoptionalcd= data[42] + self.MRPcompsoitecd= data[43] + def BitReverse(self,data): + "Reverse bits - MSB to LSB" + output= '' + for y in range(len(data)): + outchr= '' + for x in range(8): + outchr += str(ord(data[y]) >> x & 1) + output += str(chr(int(outchr,2))) + return output + def HexReverse(self,data): + "Reverse HEX characters" + output= '' + for y in reversed(range(len(data))): + output += data[y] + return output + def HexBitReverse(self,data): + "Convert HEX to Binary then bit reverse and convert back" + return self.ToHex(self.BitReverse(self.ToBinary(data))) + def HexByteReverse(self,data): + "Reverse order of Hex pairs" + output= '' + y= len(data) - 2 + while y >= 0: + output += data[y:y+2] + y -= 2 + return output + def NibbleReverse(self,data): + "Reverse Nibbles" + output= '' + for y in range(len(data)): + leftnibble= '' + rightnibble= '' + for x in range(4): + leftnibble += str(ord(data[y]) >> x & 1) + for x in range(4,8): + rightnibble += str(ord(data[y]) >> x & 1) + output += str(chr(int(rightnibble + leftnibble,2))) + return output + def HexNibbleReverse(self,data): + "Convert HEX to Binary then reverse nibbles and convert back" + return self.ToHex(self.NibbleReverse(self.ToBinary(data))) + def ToHex(self,data): + "convert binary data to hex printable" + string= '' + for x in range(len(data)): + string += '%02x' % ord(data[x]) + return string + def HexPrint(self,data): + print self.ToHex(data) + def ReadablePrint(self,data): + out= '' + for x in range(len(data)): + if data[x] >= ' ' and data[x] <= '~': + out += data[x] + else: + out += '.' + return out + def ListToHex(self,data): + string= '' + for d in data: + string += '%02X' % d + return string + def HexArrayToString(self,array): + # translate array of strings to single string + out= '' + for n in array: + out += n + return out + def HexArraysToArray(self,array): + # translate an array of strings to an array of 2 character strings + temp= self.HexArrayToString(array) + out= [] + n= 0 + while n < len(temp): + out.append(temp[n:n+2]) + n += 2 + return out + def HexArrayToList(self,array): + # translate array of 2 char HEX to int list + # first make sure we're dealing with a single array + source= self.HexArraysToArray(array) + out= [] + for n in source: + out.append(int(n,16)) + return out + def HexToList(self,string): + # translate string of 2 char HEX to int list + n= 0 + out= [] + while n < len(string): + out.append(int(string[n:n+2],16)) + n += 2 + return out + def ToBinary(self,string): + "convert hex string to binary characters" + output= '' + x= 0 + while x < len(string): + output += chr(int(string[x:x + 2],16)) + x += 2 + return output + def BinaryPrint(self,data): + "print binary representation" + print self.ToBinaryString(data) + def ToBinaryString(self,data): + "convert binary data to printable binary ('01101011')" + output= '' + string= self.ToHex(data) + for x in range(0,len(string),2): + for y in range(7,-1,-1): + output += '%s' % (int(string[x:x+2],16) >> y & 1) + return output + def BinaryToManchester(self,data): + "convert binary string to manchester encoded string" + output= '' + for bit in data: + if bit == '0': + output += '01' + else: + output += '10' + return output + def DESParity(self,data): + adjusted= '' + for x in range(len(data)): + y= ord(data[x]) & 0xfe + parity= 0 + for z in range(8): + parity += y >> z & 1 + adjusted += chr(y + (not parity % 2)) + return adjusted + def DESKey(self,seed,type,length): + d= seed + type + kencsha= SHA.new(d) + k= kencsha.digest() + kp= self.DESParity(k) + return(kp[:length]) + def PADBlock(self,block): + "add DES padding to data block" + # call with null string to return an 8 byte padding block + # call with an unknown sized block to return the block padded to a multiple of 8 bytes + for x in range(8 - (len(block) % 8)): + block += self.DES_PAD[x] + return block + def DES3MAC(self,message,key,ssc): + "iso 9797-1 Algorithm 3 (Full DES3)" + tdes= DES3.new(key,DES3.MODE_ECB,self.DES_IV) + if(ssc): + mac= tdes.encrypt(self.ToBinary(ssc)) + else: + mac= self.DES_IV + message += self.PADBlock('') + for y in range(len(message) / 8): + current= message[y * 8:(y * 8) + 8] + left= '' + right= '' + for x in range(len(mac)): + left += '%02x' % ord(mac[x]) + right += '%02x' % ord(current[x]) + machex= '%016x' % xor(int(left,16),int(right,16)) + mac= tdes.encrypt(self.ToBinary(machex)) + # iso 9797-1 says we should do the next two steps for "Output Transform 3" + # but they're obviously redundant for DES3 with only one key, so I don't bother! + #mac= tdes.decrypt(mac) + #mac= tdes.encrypt(mac) + return mac + def DESMAC(self,message,key,ssc): + "iso 9797-1 Algorithm 3 (Retail MAC)" + # DES for all blocks + # DES3 for last block + tdesa= DES.new(key[0:8],DES.MODE_ECB,self.DES_IV) + tdesb= DES.new(key[8:16],DES.MODE_ECB,self.DES_IV) + if(ssc): + mac= tdesa.encrypt(self.ToBinary(ssc)) + else: + mac= self.DES_IV + message += self.PADBlock('') + for y in range(len(message) / 8): + current= message[y * 8:(y * 8) + 8] + left= right= '' + for x in range(len(mac)): + left += '%02x' % ord(mac[x]) + right += '%02x' % ord(current[x]) + machex= '%016x' % xor(int(left,16),int(right,16)) + mac= tdesa.encrypt(self.ToBinary(machex)) + mac= tdesb.decrypt(mac) + return tdesa.encrypt(mac) + def MACVerify(self,message,key): + mess= self.ToBinary(message[:len(message)- 16]) + mac= self.DESMAC(mess,key,'') + if not mac == self.ToBinary(message[len(message) -16:]): + print 'MAC Error!' + print 'Expected MAC: ', message[len(message) -16:] + print 'Actual MAC: ', + self.HexPrint(mac) + return(False) + return(True) + def SSCIncrement(self,ssc): + out= int(self.ToHex(ssc),16) + 1 + return self.ToBinary("%016x" % out) + def TRANSITIDEncode(self,data): + "Encode TRANSIT ID" + # start sentinel + out= '0000000000000000' + # UID + out += self.ToBinaryString(self.ToBinary(data)) + # LRC + lrc= self.TRANSITLRC(out[16:48]) + out += self.ToBinaryString(chr(lrc)) + # end sentinel + out += self.ToBinaryString(chr(0xf2)) + return out + def TRANSITID(self,data): + "Decode TRANSIT ID" + # check for start sentinel + if(data[0:16] != '0000000000000000'): + print 'Start sentinel not found! (0000000000000000)' + return 0 + # check for end sentinel + if(int(data[56:],2) != 0xf2): + print 'End sentinel not found! (11110010)' + return 0 + lrc= self.TRANSITLRC(data[16:48]) + if(lrc != int(data[48:56],2)): + print 'LRC mismatch: %02X should be %02X!' % (int(data[48:56],2),lrc) + return 0 + out= '%08X' % int(data[16:48],2) + return out + def TRANSITIDPrint(self,data): + out= self.TRANSITID(data) + if(out != 0): + print 'UID:', out + else: + print 'Invalid ID!' + def TRANSITLRC(self,data): + "Calculate TRANSIT LRC" + i= 0 + lrc= 0x00 + # rolling XOR + while(i < 4): + lrc ^= (int(data[(i) * 8:(i+1) * 8],2)) & 0xff + i += 1 + # final byte XOR + lrc ^= 0x5a & 0xff + return lrc + def FDXBID(self,data): + "Decode FDX-B ID" + out= self.HexReverse(data) + hexout= self.ToHex(self.NibbleReverse(self.ToBinary(out))) + # Application ID + self.FDXBAPP= hexout[:4] + # Country Code + ccode= hexout[4:7] + self.FDXBCCODE= int(ccode,16) >> 2 + # Human Readable CCODE + if "%d" % self.FDXBCCODE in self.ISO3166CountryCodes: + self.FDXBCCODEHR= self.ISO3166CountryCodes["%d" % self.FDXBCCODE] + else: + self.FDXBCCODEHR= 'Undefined - see http://www.icar.org/manufacturer_codes.htm' + # National ID + natid= hexout[6:16] + self.FDXBNID= int(natid,16) &0x3fffffffff + def FDXBIDEncode(self,appid,ccode,natid): + "Encode FDX-B ID" + hexccode= "%03x" % (int(ccode,10) << 2) + glue = int(hexccode[-1:],16) & 0xc + hexccode = hexccode[:-1] + hexid= "%010x" % int(natid,10) + glue = glue | (int(hexid[:1],16) & 0x3) + hexglue = "%01x" % glue + hexid = hexid[1:] + rawid= appid + hexccode + hexglue + hexid + nibbleid= self.NibbleReverse(self.ToBinary(rawid)) + hexout= self.HexReverse(self.ToHex(nibbleid)) + return hexout + def FDXBIDPrint(self,data): + self.FDXBID(data) + print 'Application Identifier: ', self.FDXBAPP + print 'Country Code: ', + print self.FDXBCCODE, + print "(" + self.FDXBCCODEHR + ")" + print 'National ID: ', + print self.FDXBNID + def FDXBID128Bit(self,data): + "generate raw 128 bit FDX-B data from FDX-B ID" + idbin= self.ToBinaryString(self.ToBinary(data)) + # construct FDX-B encoded blocks + out= '' + # header is ten zeros and a '1' + header= '00000000001' + out += header + # break id into 8 bit chunks with a trailing '1' on each + for x in range(0,len(idbin),8): + out += idbin[x:x+8] + '1' + # add 16 CRC-CCITT error detection bits + crc= '%04x' % self.crcccitt(self.ToBinary(data)) + crcbin= self.ToBinaryString(self.ToBinary(crc)) + # crc is transmitted LSB first with trailing '1's + out += crcbin[0:8] + '1' + out += crcbin[8:16] + '1' + # add 3 sets of trailer bits (RFU) + trailer= '000000001' + for x in range(3): + out += trailer + return out + def FDXBID128BitDecode(self,data): + "convert raw 128 bit FDX-B data to FDX-B ID" + #strip off header + y= data[11:] + #strip off trailing '1' from the first 8 9-bit groups + out= '' + for x in range(0,72,9): + out += y[x:x+8] + # ignore the rest - CRC etc. + return '%016x' % int(out,2) + def PCSCGetTagType(self,atr): + "get currently selected tag type from atr" + if atr[8:12] == self.PCSC_CSC: + ss= atr[24:26] + return self.PCSC_SS[ss] + else: + return 'SMARTCARD' + def PCSCPrintATR(self,data): + "print breakdown of HEX ATR" + print ' ATR:', data + if data[0:2].upper() == '3B': + print ' 3B Initial Header' + else: + print 'ATR not recognised!' + return False + if data[2] == '8': + print ' 8 No TA1, TB1, TC1 only TD1 is following' + histlen= int(data[3],16) + print ' %s %d bytes historical data follow' % (data[3] , histlen) + if data[4] == '8': + print ' 8 No TA2, TB2, TC2 only TD2 is following' + if data[5] == '0': + print ' 0 T = 0' + if data[6] == '0': + print ' 0 No TA3, TB3, TC3, TD3 following' + if data[7] == '1': + print ' 1 T = 1' + if data[8:12] == self.PCSC_CSC: + print ' Detected STORAGECARD' + print ' Historical:', data[8:-2] + print ' 80 Status indicator may be present (COMPACT-TLV object)' + print ' 4F Application Identifier presence indicator' + applen= int(data[12:14],16) + print ' %s %d bytes follow' % (data[12:14] , applen) + print ' RID: %s ' % data[14:24], + if data[14:24].upper() == self.PCSC_RID: + print 'PC/SC Workgroup' + else: + print 'Unknown RID' + pixlen= applen - 5 + print ' PIX: %s' % data[24:24 + pixlen * 2] + ss= data[24:26] + print ' SS: %s %s' % (ss , self.PCSC_SS[ss]), + # if card is ISO15693 print manufacturer name + if 9 <= int(ss,16) <= 12: + try: + print '(%s)' % self.ISO7816Manufacturer[self.uid[2:4]] + except: + print '(Uknown Manufacturer)' + else: + print + print ' Name: %s %s' % (data[26:30] , self.PCSC_NAME[data[26:30]]) + print ' RFU: %s' % data[30:-2] + spaces= histlen * 2 + else: + print ' Detected SMARTCARD' + print ' ATS:',self.pcsc_ats,'-', + print self.ReadablePrint(self.ToBinary(self.pcsc_ats)) + # if ats starts with '00', '10' or '8X' it is an ISO-7816-4 card + atsbyte= self.pcsc_ats[0:2] + if atsbyte == '00' or atsbyte == '10' or self.pcsc_ats[0] == '8': + print ' Category: %s Format according to ISO/IEC 7816-4' % atsbyte + else: + print ' Category: %s Proprietary format' % atsbyte + spaces= len(self.pcsc_ats) + space= '' + for x in range(spaces): + space += ' ' + + print space + ' Checksum TCK: ' + data[-2:], + # calculate checksum excluding Initial Header and TCK + tck= 0 + x= 2 + while x < len(data) - 2: + tck= xor(tck,int(data[x:x+2],16)) + x += 2 + if int(data[-2:],16) == tck: + print '(OK)' + return True + else: + print '(Checksum error: %02x)' % tck + return False + + def shutdown(self): + if self.readertype == self.READER_LIBNFC: + self.nfc.powerOff() + self.nfc.deconfigure() + os._exit(False) diff --git a/RFIDIOtconfig.opts b/RFIDIOtconfig.opts new file mode 100644 index 0000000..7fec1cb --- /dev/null +++ b/RFIDIOtconfig.opts @@ -0,0 +1,7 @@ +#-s 9600 -l /dev/ttyUSB1 +# uncomment the above line to enable global overrides +# options should be all on the first line, as if added on the command line +# this file will be looked for in the following places: +# $(RFIDIOtconfig_opts) - note that this environment variable should be set to the full path and filename +# ./RFIDIOtconfig.opts +# /etc/RFIDIOtconfig.opts diff --git a/RFIDIOtconfig.py b/RFIDIOtconfig.py new file mode 100755 index 0000000..c01cf42 --- /dev/null +++ b/RFIDIOtconfig.py @@ -0,0 +1,185 @@ +#!/usr/bin/python + + +# RFIDIOtconfig.py - shared settings for local RFIDIOt +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2006,7,8,9 All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + +import RFIDIOt +import getopt +import sys +import os +import string + + +debug= False + +# help flag (-h) set? +help= False + +# nogui flag (-g) set? +nogui= False + +# noinit flag (-n) set? +noinit= False + +# options specified in this file can be overridden on the command line, or in static +# files as defined below, in the following order: +# $(RFIDIOtconfig_opts) +# ./RFIDIOtconfig.opts +# /etc/RFIDIOtconfig.opts +# +# options can also be specified in the ENV variable $(RFIDIOtconfig) +# +# note that command line options will take precedence + +# change the following sections to match your serial port +# bluetooth connections need at least 1 second timeout to establish connection + +# serial port (can be overridden with -l) + +# ignored for PCSC +#line= "/dev/ttyS0" +#line= "/dev/ttyS1" +line= "/dev/ttyUSB0" +# for Windows +#line= "COM4" + +# reader type (can be overridden with -R) +#readertype= RFIDIOt.rfidiot.READER_ACG +#readertype= RFIDIOt.rfidiot.READER_FROSCH +#readertype= RFIDIOt.rfidiot.READER_DEMOTAG +# READER_PCSC is a meta type. Actual subtype will be auto-determined. +readertype= RFIDIOt.rfidiot.READER_PCSC +#readertype= RFIDIOt.rfidiot.READER_NONE +#readertype= RFIDIOt.rfidiot.READER_LIBNFC + +# PCSC reader number (can be overridden with -r) +readernum= 1 + +# serial port speed (can be overridden with -s) +# ignored for PCSC +speed= 9600 +#speed= 57600 +#speed= 115200 +#speed= 230400 +#speed= 460800 + +# reader timeout (can be overriden with -t) +# ignored for PCSC +timeout= 1 + +# libnfc reader number (if set to 'None' first available device will be used) +# can be overridden with -f +nfcreader= None + +def printoptions(): + print '\nRFIDIOt Options:\n' + print '\t-d\t\tDebug on' + print '\t-f \tUse LibNFC device number (implies -R READER_LIBNFC)' + print '\t-g\t\tNo GUI' + print '\t-h\t\tPrint detailed help message' + print '\t-n\t\tNo Init - do not initialise hardware' + print '\t-N\t\tList available LibNFC devices' + print '\t-r \tUse PCSC device number (implies -R READER_PCSC)' + print '\t-R \tReader/writer type (see source of RFIDIOtconfig.py for types)' + print '\t-l \tLine to use for reader/writer' + print '\t-L\t\tList available PCSC devices' + print '\t-s \tSpeed of reader/writer' + print '\t-t \tTimeout for inactivity of reader/writer' + print + +# check for global overrides in local config files, in the following order: +# $(RFIDIOtconfig_opts) +# ./RFIDIOtconfig.opts +# /etc/RFIDIOtconfig.opts +# note that command line options will take precedence +extraopts= [] +OptsEnv= 'RFIDIOtconfig_opts' +if os.environ.has_key(OptsEnv): + try: + configfile= open(os.environ[OptsEnv]) + extraopts= string.split(configfile.read()) + except: + print "*** warning: config file set by ENV not found (%s) or empty!" % (os.environ[OptsEnv]) + print "*** not checking for other option files!" +else: + for path in ['.','/etc']: + try: + configfile= open(path + '/RFIDIOtconfig.opts') + extraopts= string.split(configfile.read()) + break + except: + pass +# check for global override in environment variable +OptsEnv= 'RFIDIOtconfig' +if os.environ.has_key(OptsEnv): + try: + extraopts= string.split(os.environ[OptsEnv]) + except: + print "*** warning: RFIDIOtconfig found in ENV, but no options specified!" +# ignore if commented out +if len(extraopts) > 0: + if extraopts[0][0] == '#': + extraopts= [] + +# 'args' will be set to remaining arguments (if any) +try: + opts, args = getopt.getopt(extraopts + sys.argv[1:],'df:ghnNr:R:l:Ls:t:') + + for o, a in opts: + if o == '-d': + debug= True + if o == '-f': + nfcreader= int(a) + readertype= RFIDIOt.rfidiot.READER_LIBNFC + if o == '-g': + nogui= True + if o == '-h': + help= True + printoptions() + if o == '-n': + noinit= True + if o == '-N': + readertype= RFIDIOt.rfidiot.READER_LIBNFC + card= RFIDIOt.rfidiot(readernum,readertype,line,speed,timeout,debug,noinit,nfcreader) + card.libnfc_listreaders() + os._exit(True) + if o == '-r': + readernum= a + readertype= RFIDIOt.rfidiot.READER_PCSC + if o == '-R': + readertype= eval(a) + if o == '-l': + line= a + if o == '-L': + readertype= RFIDIOt.rfidiot.READER_PCSC + readernum= 0 + card= RFIDIOt.rfidiot(readernum,readertype,line,speed,timeout,debug,noinit,nfcreader) + card.pcsc_listreaders() + os._exit(True) + if o == '-s': + speed= int(a) + if o == '-t': + timeout= int(a) + card= RFIDIOt.rfidiot(readernum,readertype,line,speed,timeout,debug,noinit,nfcreader) +except getopt.GetoptError,e: + print "RFIDIOtconfig module ERROR: %s" % e + printoptions() + args= [] diff --git a/bruteforce.py b/bruteforce.py new file mode 100755 index 0000000..3438122 --- /dev/null +++ b/bruteforce.py @@ -0,0 +1,77 @@ +#!/usr/bin/python + +# bruteforce.py - try random numbers to login to sector 0 +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2006, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + + +import RFIDIOtconfig +import random +import sys +import os + +try: + card= RFIDIOtconfig.card +except: + os._exit(True) + +args= RFIDIOtconfig.args +help= RFIDIOtconfig.help + +card.info('bruteforce v0.1h') +card.select() +print 'Card ID: ' + card.uid + +finished = 0 +tries = 0 +print ' Tries: %s\r' % tries, +sys.stdout.flush() + +while not finished: + + tries += 1 + if tries % 10 == 0: + print ' Tries: %s\r' % tries, + sys.stdout.flush() + + if len(args) == 1: + key= args[0] + if len(key) != 12: + print ' Static Key must be 12 HEX characters!' + os._exit(True) + print 'Trying static key: ' + key + else: + key = '%012x' % random.getrandbits(48) + + for type in ['AA', 'BB']: + card.select() + if card.login(0,type,key): + print '\nlogin succeeded after %d tries!' % tries + print 'key: ' + type + ' ' + key + finished = 1 + break + elif card.errorcode != 'X' and card.errorcode != '6982' and card.errorcode != '6200': + print '\nerror!' + print 'key: ' + type + ' ' + key + print 'error code: ' + card.errorcode + finished = 1 + break + if finished: + break +os._exit(False) diff --git a/cardselect.py b/cardselect.py new file mode 100755 index 0000000..e0a05a4 --- /dev/null +++ b/cardselect.py @@ -0,0 +1,53 @@ +#!/usr/bin/python + + +# cardselect.py - select card and display ID +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2006, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + + +import RFIDIOtconfig +import sys +import os + +try: + card= RFIDIOtconfig.card +except: + os._exit(True) + +args= RFIDIOtconfig.args + +card.info('cardselect v0.1l') +# force card type if specified +if len(args) == 1: + card.settagtype(args[0]) +else: + card.settagtype(card.ALL) + +if card.select(): + print ' Card ID: ' + card.uid + if card.readertype == card.READER_PCSC: + print ' ATR: ' + card.pcsc_atr +else: + if card.errorcode: + print ' '+card.ISO7816ErrorCodes[card.errorcode] + else: + print ' No card present' + os._exit(True) +os._exit(False) diff --git a/copytag.py b/copytag.py new file mode 100755 index 0000000..5c90fd9 --- /dev/null +++ b/copytag.py @@ -0,0 +1,92 @@ +#!/usr/bin/python + +# copytag.py - read all sectors from a standard tag and write them back +# to a blank +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2006, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + + +import RFIDIOtconfig +import sys +import os +import string + +try: + card= RFIDIOtconfig.card +except: + os._exit(True) + +card.info('copytag v0.1d') +card.select() +print '\nID: ' + card.uid +print ' Reading:' + +buffer= [] + +card.select() +for x in range(98): + if card.readblock(x): + print ' Block %02x: %s\r' % (x , card.data), + sys.stdout.flush() + buffer.append(card.data) + else: + if x == 0: + print 'Read error: ', card.ISO7816ErrorCodes[card.errorcode] + break + +if x > 0: + print '\nRead %d blocks' % x + raw_input('Remove source tag and hit to continue...') + targettype= card.tagtype + while 42: + card.waitfortag('Waiting for blank tag...') + print 'ID: ' + card.uid + if card.tagtype != targettype: + raw_input('Invalid tag type! Hit to continue...') + continue + if not card.readblock(0): + raw_input('Tag not readable! Hit to continue...') + continue + if len(card.data) != len(buffer[0]): + print 'Wrong blocksize! (%d / %d)' % (len(buffer[0]),len(card.data)), + raw_input(' Hit to continue...') + continue + if string.upper(raw_input('*** Warning! Data will be overwritten! Continue (y/n)?')) == 'Y': + break + else: + os._exit(False) + print ' Writing:' + for n in range(x): + print ' Block %02x: %s\r' % (n , buffer[n]), + sys.stdout.flush() + if not card.writeblock(n, buffer[n]): + print '\nWrite failed!' + print '\n Verifying:' + for n in range(x): + print ' Block %02x: %s' % (n , buffer[n]), + if not card.readblock(n) or card.data != buffer[n]: + print '\nVerify failed!' + os._exit(True) + print ' OK\r', + sys.stdout.flush() + print + os._exit(False) +else: + print 'No data!' + os._exit(True) diff --git a/delete-smartcafe.gpsh b/delete-smartcafe.gpsh new file mode 100644 index 0000000..0c87e78 --- /dev/null +++ b/delete-smartcafe.gpsh @@ -0,0 +1,12 @@ +// script to install epassport.cap to jcop card using gpshell (http://sourceforge.net/projects/globalplatform/) +mode_211 +establish_context +// edit the following line to match your PCSC reader +//card_connect -readerNumber 2 +//card_connect -reader "OMNIKEY CardMan 5x21 (USB iClass Reader) 00 01" +card_connect +select -AID A000000003000000 +open_sc -scp 2 -scpimpl 0x15 -security 1 -keyind 0 -keyver 0 -keyDerivation emvcps11 -key 404142434445464748494A4B4C4D4E4F +delete -AID A00000024710 +card_disconnect +release_context diff --git a/demotag.py b/demotag.py new file mode 100755 index 0000000..8478337 --- /dev/null +++ b/demotag.py @@ -0,0 +1,35 @@ +#!/usr/bin/python + +# demotag.py - test IAIK TUG DemoTag` +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2006, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + +import RFIDIOtconfig +import sys +import os + +try: + card= RFIDIOtconfig.card +except: + os._exit(False) + +args= RFIDIOtconfig.args + +print 'Setting ID to: ' + args[0] +print card.demotag(card.DT_SET_UID,card.ToBinary(args[0])) diff --git a/eeprom.py b/eeprom.py new file mode 100755 index 0000000..eb5b35b --- /dev/null +++ b/eeprom.py @@ -0,0 +1,42 @@ +#!/usr/bin/python + +# eeprom.py - display reader's eeprom settings +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2006, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + + +import RFIDIOtconfig +import sys +import os + +try: + card= RFIDIOtconfig.card +except: + os._exit(True) + +card.info('eeprom v0.1c') +print 'Station:\t' + card.station() +print 'Protocol:\t' + card.PCON() +print 'Protocol2:\t' + card.PCON2() +print 'Protocol3:\t' + card.PCON3() + +address= 0 +while address < 0xf0: + print 'address %02x:\t%s' % (address,card.readEEPROM(address)) + address += 1 diff --git a/fdxbnum.py b/fdxbnum.py new file mode 100755 index 0000000..9b90e80 --- /dev/null +++ b/fdxbnum.py @@ -0,0 +1,175 @@ +#!/usr/bin/python + +# fdxbnum.py - generate / decode FDX-B EM4x05 compliant IDs +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2006, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + + +import RFIDIOtconfig +import sys +import os +import string + +try: + card= RFIDIOtconfig.card +except: + os._exit(True) + +args= RFIDIOtconfig.args +help= RFIDIOtconfig.help + +card.info('fdxbnum v0.1e') + +precoded= False + +if not help and (len(args) == 1 or len(args) == 2): + print "Decode: " + if len(args[0]) == 16: + card.FDXBIDPrint(args[0]) + else: + card.FDXBIDPrint(args[0][1:]) + if len(args) == 2: + if args[1] == 'WRITE': + precoded= True + else: + print 'Unrecognised option: ' + args[1] + os._exit(True) + else: + os._exit(False) + +if not help and (len(args) >= 3 or precoded): + if precoded: + id= args[0] + else: + print "Encode: ", + id= card.FDXBIDEncode(args[0],args[1],args[2]) + print id + out= card.FDXBID128Bit(id) + print 'binary is',out + if (len(args) == 4 and args[3] == 'WRITE') or precoded: + while True: + # Q5 must be forced into Q5 mode to be sure of detection so try that first + if card.readertype == card.READER_ACG: + card.settagtype(card.Q5) + card.select() + if card.readertype == card.READER_ACG: + if not card.tagtype == card.Q5: + card.settagtype(card.ALL) + card.waitfortag('Waiting for blank tag...') + print ' Tag ID: ' + card.data + if card.tagtype == card.Q5 or card.tagtype == card.HITAG2: + x= string.upper(raw_input(' *** Warning! This will overwrite TAG! Proceed (y/n)? ')) + if x == 'N': + os._exit(False) + if x == 'Y': + break + else: + x= raw_input(' Incompatible TAG! Hit to retry...') + writetag= True + print + else: + writetag= False + # now turn it all back to 4 byte hex blocks for writing + outbin= '' + outhex= ['','','','',''] + # control block for Q5: + # carrier 32 (2 * 15 + 2) + # rf/? (don't care) - set to 00 + # data inverted + # biphase + # maxblock 4 + print ' Q5 Control Block: ', + q5control= '6000F0E8' + print q5control + for x in range(0,len(out),8): + outbin += chr(int(out[x:x + 8],2)) + for x in range(0,len(outbin),4): + print ' Q5 Data Block %02d:' % (x / 4 + 1), + outhex[x / 4 + 1]= card.ToHex(outbin[x:x+4]) + print outhex[x / 4 + 1] + # control block for Hitag2 + # Public Mode B + # default password + print + print ' Hitag2 Control Block: ', + h2control= card.HITAG2_PUBLIC_B + card.HITAG2_TRANSPORT_TAG + print h2control + for x in range(1,5,1): + print ' Hitag2 Data Block %02d:' % (x + 3), + print outhex[x] + if writetag == True: + print + print ' Writing to tag type: ' + card.LFXTags[card.tagtype] + if card.tagtype == card.Q5: + outhex[0]= q5control + offset= 0 + if card.tagtype == card.HITAG2: + outhex[0]= h2control + offset= 3 + if card.readertype == card.READER_ACG: + card.login('','',card.HITAG2_TRANSPORT_RWD) + for x in range(4 + offset,-1 + offset,-1): + print " Writing block %02x:" % x, + if not card.writeblock(x,outhex[x - offset]): + # we expect a Q5 to fail after writing the control block as it re-reads + # it before trying to verify the write and switches mode so is now no longer in Q5 mode + if x == offset: + print ' Control: ' + outhex[x - offset] + print + print ' Done!' + # now check for FDX-B ID + card.settagtype(card.EM4x05) + card.select() + print ' Card ID: ' + card.data + else: + print 'Write failed!' + if card.readertype == card.READER_FROSCH: + print card.FROSCH_Errors[card.errorcode] + os._exit(True) + else: + # hitag2 don't change mode until the next time they're selected so write + # confirmation of control block should be ok + if x == offset: + print ' Control: ' + outhex[x - offset] + print + print ' Done!' + # now check for FDX-B ID + card.reset() + card.settagtype(card.EM4x05) + card.select() + print ' Card ID: ' + card.data + else: + print outhex[x - offset] + if card.readertype == card.READER_ACG: + card.settagtype(card.ALL) + os._exit(False) +print sys.argv[0] + ' - generate / decode FDX-B EM4x05 compliant IDs' +print 'Usage: ' + sys.argv[0] + ' [OPTIONS] [WRITE] | [WRITE]' +print +print '\tIf a single 16 HEX digit ID is provided, it will be decoded according to the FDX-B standard.' +print '\tAlternatively, specifying a 4 HEX digit Application ID, 3 or 4 digit decimal country code' +print '\t(normally based on ISO-3166 country codes or ICAR.ORG manufacturer codes, range 0 - 4095)' +print '\tand a decimal National ID Number will generate a 16 HEX digit ID.' +print '\tNote: Application ID 8000 is \'Animal\', and 0000 is non-Animal.' +print '\tMaximum value for country code is 999 according to the standard, but 4 digits will work.' +print '\tMaximum value for National ID is 274877906943.' +print +print '\tIf the WRITE option is specified, a Q5 or Hitag2 will be programmed to emulate FDX-B.' +print +os._exit(True) diff --git a/formatmifare1kvalue.py b/formatmifare1kvalue.py new file mode 100755 index 0000000..10da69e --- /dev/null +++ b/formatmifare1kvalue.py @@ -0,0 +1,64 @@ +#!/usr/bin/python + +# formatmifare1kvalue.py - format value blocks on a mifare standard tag +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2006, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + + +import RFIDIOtconfig +import sys +import string +import os + +try: + card= RFIDIOtconfig.card +except: + os._exit(True) + +card.info('formatmifare1k v0.1b') +card.select() +print 'Card ID: ' + card.data +while True: + x= string.upper(raw_input('\n*** Warning! This will overwrite all data blocks! Proceed (y/n)? ')) + if x == 'N': + os._exit(False) + if x == 'Y': + break + +sector = 1 +while sector < 0x10: + for type in ['AA', 'BB', 'FF']: + card.select() + print ' sector %02x: Keytype: %s' % (sector, type), + if card.login(sector,type,''): + for block in range(3): + print '\n block %02x: ' % ((sector * 4) + block), + data= '00000000' + print 'Value: ' + data, + if card.writevalueblock((sector * 4) + block,data): + print ' OK' + elif card.errorcode: + print 'error code: ' + card.errorcode + elif type == 'FF': + print 'login failed' + print '\r', + sys.stdout.flush() + sector += 1 + print +print diff --git a/froschtest.py b/froschtest.py new file mode 100755 index 0000000..f79369f --- /dev/null +++ b/froschtest.py @@ -0,0 +1,79 @@ +#!/usr/bin/python + +# froschtest.py - test frosch HTRM112 reader` +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2006, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + +import RFIDIOtconfig +import sys +import os + +try: + card= RFIDIOtconfig.card +except: + os._exit(True) + +card.info('froschtest v0.1c') +print +print 'Trying Hitag1: ', +if card.frosch(card.FR_HT1_Get_Snr,''): + print card.data[:len(card.data) -2] + if not card.select(): + print 'Select failed: ', + print card.FROSCH_Errors[card.errorcode] + else: + for x in range(0,8): + if card.readblock(x): + print '\tBlock %02d: %s' % (x,card.data) + else: + print '\tBlock %0d read failed: ' % x, + print card.FROSCH_Errors[card.errorcode] +else: + print card.FROSCH_Errors[card.errorcode] + +print 'Trying Hitag2: ', +if card.frosch(card.FR_HT2_Get_Snr_PWD,''): + print card.data[:len(card.data) -2] + if not card.select(): + print 'Select failed: ', + print card.FROSCH_Errors[card.errorcode] + else: + for x in range(0,8): + if card.readblock(x): + print '\tBlock %02d: %s' % (x,card.data) + else: + print '\tBlock %0d read failed' % x, + print card.FROSCH_Errors[card.errorcode] +else: + print card.FROSCH_Errors[card.errorcode] + +print 'Trying Hitag2 Public A (Unique / Miro): ', +if card.frosch(card.FR_HT2_Read_Miro,''): + print card.data +else: + print card.FROSCH_Errors[card.errorcode] + +print 'Trying Hitag2 Public B (FDX-B): ', +if card.frosch(card.FR_HT2_Read_PublicB,''): + print 'Raw: ' + card.data, + print 'ID: ' + card.FDXBID128BitDecode(card.ToBinaryString(card.ToBinary(card.data))) + card.FDXBIDPrint(card.FDXBID128BitDecode(card.ToBinaryString(card.ToBinary(card.data)))) +else: + print card.FROSCH_Errors[card.errorcode] +os._exit(False) diff --git a/hidprox.py b/hidprox.py new file mode 100755 index 0000000..cfc8b27 --- /dev/null +++ b/hidprox.py @@ -0,0 +1,88 @@ +#!/usr/bin/python + + +# hidprox.py - show HID Prox card type and site/id code +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2009, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + + +import sys +import os +import string +import RFIDIOtconfig + +try: + card= RFIDIOtconfig.card +except: + os._exit(True) + + +card.info('hidprox v0.1c') + +if not card.readersubtype == card.READER_OMNIKEY: + print 'Reader type not supported!' + os._exit(True) + +try: + prox= card.pcsc_atr[:6] + type= card.HID_PROX_TYPES[prox] + print ' Card type:', type +except: + if not card.pcsc_atr: + print 'No card detected!' + else: + print 'Unrecognised card type! ATR:', card.pcsc_atr + os._exit(True) + +# H10301 - 26 bit (FAC + CN) +if prox == card.HID_PROX_H10301: + fc= card.pcsc_atr[7:10] + cn= card.pcsc_atr[11:16] + octal= '%o' % int(card.pcsc_atr[7:16]) + +# H10302 - 37 bit (CN) +if prox == card.HID_PROX_H10302: + fc= 'n/a' + cn= card.pcsc_atr[6:18] + octal= '%o' % int(card.pcsc_atr[6:18]) + +# H10304 - 37 bit (FAC + CN) +if prox == card.HID_PROX_H10304: + fc= card.pcsc_atr[7:12] + cn= card.pcsc_atr[12:18] + octal= '%o' % int(card.pcsc_atr[7:18]) + +# H10320 - 32 bit clock/data card +if prox == card.HID_PROX_H10320: + fc= 'n/a' + cn= card.pcsc_atr[6:14] + octal= '%o' % int(card.pcsc_atr[6:14]) + +# Corp 1000 - 35 bit (CIC + CN) +if prox == card.HID_PROX_CORP1K: + fc= card.pcsc_atr[6:10] + cn= card.pcsc_atr[10:18] + octal= '%o' % int(card.pcsc_atr[6:18]) + +print +print ' Facility Code:', fc +print ' Card Number:', cn +print ' Octal:', octal +print +os._exit(False) diff --git a/hitag2brute.py b/hitag2brute.py new file mode 100755 index 0000000..cc059cb --- /dev/null +++ b/hitag2brute.py @@ -0,0 +1,68 @@ +#!/usr/bin/python + + +# hitag2brute.py - Brute Force hitag2 password +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2008, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + + +import RFIDIOtconfig +import sys +import os +import time + +try: + card= RFIDIOtconfig.card +except: + os._exit(True) + +args= RFIDIOtconfig.args + +card.info('hitag2brute v0.1b') + +pwd= 0x00 + +# start at specified PWD +if len(args) == 1: + pwd= int(args[0],16) + +card.settagtype(card.ALL) + +if card.select(): + print 'Bruteforcing tag:', card.uid +else: + print 'No tag found!' + os._exit(True) + +while 42: + PWD= '%08X' % pwd + if card.h2login(PWD): + print 'Password is %s' % PWD + os._exit(False) + else: + if not pwd % 16: + print PWD + ' \r', + if not card.select(): + print 'No tag found! Last try: %s\r' % PWD, + else: + pwd= pwd + 1 + sys.stdout.flush() + if pwd == 0xffffffff: + os._exit(True) +os._exit(False) diff --git a/hitag2reset.py b/hitag2reset.py new file mode 100755 index 0000000..ae46922 --- /dev/null +++ b/hitag2reset.py @@ -0,0 +1,86 @@ +#!/usr/bin/python + +# hitag2reset.py - hitag2 tags need love too... +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2006, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + + +import RFIDIOtconfig +import sys +import os +import string + +try: + card= RFIDIOtconfig.card +except: + os._exit(True) + +args= RFIDIOtconfig.args +help= RFIDIOtconfig.help + +card.info('hitag2reset v0.1d') + +# standard config block +#CFB='06' + card.HITAG2_TRANSPORT_TAG +CFB=card.HITAG2_PASSWORD + card.HITAG2_TRANSPORT_TAG +BLK1= card.HITAG2_TRANSPORT_RWD + +if len(args) == 0 or len(args) > 2 or help: + print sys.argv[0] + ' - return a Hitag2 tag to life' + print 'Usage: ' + sys.argv[0] + ' [ ]' + print + print 'If the optional PASSWD fields are specified, the password will be set,' + print 'otherwise factory password \'%s\' will be used' % card.HITAG2_TRANSPORT_RWD + os._exit(True) + +if args[0] == 'CONTROL': + while True: + print +# if card.frosch(card.FR_HT2_Read_PublicB): +# print ' Card ID: ' + card.data + x= string.upper(raw_input(' *** Warning! This will overwrite TAG! Place card and proceed (y/n)? ')) + if x == 'N': + os._exit(False) + if x == 'Y': + break + print 'Writing...' + logins= 0 + if (card.h2publicselect()): + print 'Hitag2 ID: ' + card.data + else: + print 'No TAG, or incompatible hardware!' + os._exit(True) + if not card.writeblock(3,CFB): + print card.FROSCH_Errors[card.errorcode] + print 'Block 3 write failed!' + os._exit(True) + else: + # set new passord if specified + if len(args) > 1: + BLK1= args[1] + #if not card.writeblock(1,B1) or not card.writeblock(2,B2): + if not card.writeblock(1,BLK1): + print 'Block 1 write failed!' + print card.FROSCH_Errors[card.errorcode] + os._exit(True) + card.settagtype(card.ALL) + print 'Done!' + if card.select(): + print ' Card ID: ' + card.uid +os._exit(False) diff --git a/iso3166.py b/iso3166.py new file mode 100755 index 0000000..43e51a5 --- /dev/null +++ b/iso3166.py @@ -0,0 +1,556 @@ +# -*- coding: iso-8859-15 -*- +# ISO-3166 alpha country codes - ver 1 +ISO3166CountryCodesAlpha= { "ABW":"Aruba",\ + "AFG":"Afghanistan",\ + "AGO":"Angola",\ + "AIA":"Anguilla",\ + "ALA":"Åland Islands",\ + "ALB":"Albania",\ + "AND":"Andorra",\ + "ANT":"Netherlands Antilles",\ + "ARE":"United Arab Emirates",\ + "ARG":"Argentina",\ + "ARM":"Armenia",\ + "ASM":"American Samoa",\ + "ATA":"Antarctica",\ + "ATF":"French Southern Territories",\ + "ATG":"Antigua and Barbuda",\ + "AUS":"Australia",\ + "AUT":"Austria",\ + "AZE":"Azerbaijan",\ + "BDI":"Burundi",\ + "BDR":"Bundesdruckerei",\ + "BEL":"Belgium",\ + "BEN":"Benin",\ + "BFA":"Burkina Faso",\ + "BGD":"Bangladesh",\ + "BGR":"Bulgaria",\ + "BHR":"Bahrain",\ + "BHS":"Bahamas",\ + "BIH":"Bosnia and Herzegovina",\ + "BLR":"Belarus",\ + "BLZ":"Belize",\ + "BMU":"Bermuda",\ + "BOL":"Bolivia",\ + "BRA":"Brazil",\ + "BRB":"Barbados",\ + "BRN":"Brunei Darussalam",\ + "BTN":"Bhutan",\ + "BVT":"Bouvet Island",\ + "BWA":"Botswana",\ + "CAF":"Central African Republic",\ + "CAN":"Canada",\ + "CCK":"Cocos (Keeling) Islands",\ + "CHE":"Switzerland",\ + "CHL":"Chile",\ + "CHN":"China",\ + "CIV":"Côte d'Ivoire",\ + "CMR":"Cameroon",\ + "COD":"Congo, the Democratic Republic of the",\ + "COG":"Congo",\ + "COK":"Cook Islands",\ + "COL":"Colombia",\ + "COM":"Comoros",\ + "CPV":"Cape Verde",\ + "CRI":"Costa Rica",\ + "CUB":"Cuba",\ + "CXR":"Christmas Island",\ + "CYM":"Cayman Islands",\ + "CYP":"Cyprus",\ + "CZE":"Czech Republic",\ + "DEU":"Germany",\ + # for brain-damaged german passports + "D":"Germany",\ + "DJI":"Djibouti",\ + "DMA":"Dominica",\ + "DNK":"Denmark",\ + "DOM":"Dominican Republic",\ + "DZA":"Algeria",\ + "ECU":"Ecuador",\ + "EGY":"Egypt",\ + "ERI":"Eritrea",\ + "ESH":"Western Sahara",\ + "ESP":"Spain",\ + "EST":"Estonia",\ + "ETH":"Ethiopia",\ + "FIN":"Finland",\ + "FJI":"Fiji",\ + "FLK":"Falkland Islands (Malvinas)",\ + "FRA":"France",\ + "FRO":"Faroe Islands",\ + "FSM":"Micronesia, Federated States of",\ + "GAB":"Gabon",\ + "GBR":"United Kingdom",\ + "GEO":"Georgia",\ + "GGY":"Guernsey",\ + "GHA":"Ghana",\ + "GIB":"Gibraltar",\ + "GIN":"Guinea",\ + "GLP":"Guadeloupe",\ + "GMB":"Gambia",\ + "GNB":"Guinea-Bissau",\ + "GNQ":"Equatorial Guinea",\ + "GRC":"Greece",\ + "GRD":"Grenada",\ + "GRL":"Greenland",\ + "GTM":"Guatemala",\ + "GUF":"French Guiana",\ + "GUM":"Guam",\ + "GUY":"Guyana",\ + "HKG":"Hong Kong",\ + "HMD":"Heard Island and McDonald Islands",\ + "HND":"Honduras",\ + "HRV":"Croatia",\ + "HTI":"Haiti",\ + "HUN":"Hungary",\ + "IDN":"Indonesia",\ + "IMN":"Isle of Man",\ + "IND":"India",\ + "IOT":"British Indian Ocean Territory",\ + "IRL":"Ireland",\ + "IRN":"Iran, Islamic Republic of",\ + "IRQ":"Iraq",\ + "ISL":"Iceland",\ + "ISR":"Israel",\ + "ITA":"Italy",\ + "JAM":"Jamaica",\ + "JEY":"Jersey",\ + "JOR":"Jordan",\ + "JPN":"Japan",\ + "KAZ":"Kazakhstan",\ + "KEN":"Kenya",\ + "KGZ":"Kyrgyzstan",\ + "KHM":"Cambodia",\ + "KIR":"Kiribati",\ + "KNA":"Saint Kitts and Nevis",\ + "KOR":"Korea, Republic of",\ + "KWT":"Kuwait",\ + "LAO":"Lao People's Democratic Republic",\ + "LBN":"Lebanon",\ + "LBR":"Liberia",\ + "LBY":"Libyan Arab Jamahiriya",\ + "LCA":"Saint Lucia",\ + "LIE":"Liechtenstein",\ + "LKA":"Sri Lanka",\ + "LSO":"Lesotho",\ + "LTU":"Lithuania",\ + "LUX":"Luxembourg",\ + "LVA":"Latvia",\ + "MAC":"Macao",\ + "MAR":"Morocco",\ + "MCO":"Monaco",\ + "MDA":"Moldova, Republic of",\ + "MDG":"Madagascar",\ + "MDV":"Maldives",\ + "MEX":"Mexico",\ + "MHL":"Marshall Islands",\ + "MKD":"Macedonia, the former Yugoslav Republic of",\ + "MLI":"Mali",\ + "MLT":"Malta",\ + "MMR":"Myanmar",\ + "MNE":"Montenegro",\ + "MNG":"Mongolia",\ + "MNP":"Northern Mariana Islands",\ + "MOZ":"Mozambique",\ + "MRT":"Mauritania",\ + "MSR":"Montserrat",\ + "MTQ":"Martinique",\ + "MUS":"Mauritius",\ + "MWI":"Malawi",\ + "MYS":"Malaysia",\ + "MYT":"Mayotte",\ + "NAM":"Namibia",\ + "NCL":"New Caledonia",\ + "NER":"Niger",\ + "NFK":"Norfolk Island",\ + "NGA":"Nigeria",\ + "NIC":"Nicaragua",\ + "NIU":"Niue",\ + "NLD":"Netherlands",\ + "NOR":"Norway",\ + "NPL":"Nepal",\ + "NRU":"Nauru",\ + "NZL":"New Zealand",\ + "OMN":"Oman",\ + "PAK":"Pakistan",\ + "PAN":"Panama",\ + "PCN":"Pitcairn",\ + "PER":"Peru",\ + "PHL":"Philippines",\ + "PLW":"Palau",\ + "PNG":"Papua New Guinea",\ + "POL":"Poland",\ + "PRI":"Puerto Rico",\ + "PRK":"Korea, Democratic People's Republic of",\ + "PRT":"Portugal",\ + "PRY":"Paraguay",\ + "PSE":"Palestinian Territory, Occupied",\ + "PYF":"French Polynesia",\ + "QAT":"Qatar",\ + "REU":"Réunion",\ + "ROU":"Romania",\ + "RUS":"Russian Federation",\ + "RWA":"Rwanda",\ + "SAU":"Saudi Arabia",\ + "SDN":"Sudan",\ + "SEN":"Senegal",\ + "SGP":"Singapore",\ + "SGS":"South Georgia and the South Sandwich Islands",\ + "SHN":"Saint Helena",\ + "SJM":"Svalbard and Jan Mayen",\ + "SLB":"Solomon Islands",\ + "SLE":"Sierra Leone",\ + "SLV":"El Salvador",\ + "SMR":"San Marino",\ + "SOM":"Somalia",\ + "SPM":"Saint Pierre and Miquelon",\ + "SRB":"Serbia",\ + "STP":"Sao Tome and Principe",\ + "SUR":"Suriname",\ + "SVK":"Slovakia",\ + "SVN":"Slovenia",\ + "SWE":"Sweden",\ + "SWZ":"Swaziland",\ + "SYC":"Seychelles",\ + "SYR":"Syrian Arab Republic",\ + "TCA":"Turks and Caicos Islands",\ + "TCD":"Chad",\ + "TGO":"Togo",\ + "THA":"Thailand",\ + "TJK":"Tajikistan",\ + "TKL":"Tokelau",\ + "TKM":"Turkmenistan",\ + "TLS":"Timor-Leste",\ + "TON":"Tonga",\ + "TTO":"Trinidad and Tobago",\ + "TUN":"Tunisia",\ + "TUR":"Turkey",\ + "TUV":"Tuvalu",\ + "TWN":"Taiwan, Province of China",\ + "TZA":"Tanzania, United Republic of",\ + "UGA":"Uganda",\ + "UKR":"Ukraine",\ + "UMI":"United States Minor Outlying Islands",\ + "URY":"Uruguay",\ + "USA":"United States",\ + "UTO":"Utopia",\ + "UZB":"Uzbekistan",\ + "VAT":"Holy See (Vatican City State)",\ + "VCT":"Saint Vincent and the Grenadines",\ + "VEN":"Venezuela",\ + "VGB":"Virgin Islands, British",\ + "VIR":"Virgin Islands, U.S.",\ + "VNM":"Viet Nam",\ + "VUT":"Vanuatu",\ + "WLF":"Wallis and Futuna",\ + "WSM":"Samoa",\ + "YEM":"Yemen",\ + "ZAF":"South Africa",\ + "ZMB":"Zambia",\ + "ZWE":"Zimbabwe",\ + "UNO":"United Nations Organization",\ + "UNA":"United Nations specialized agency official",\ + "XXA":"Stateless",\ + "XXB":"Refugee",\ + "XXC":"Refugee (non-convention)",\ + "XXX":"Unspecified / Unknown",\ + } + +# combined ISO-3166 country and icar.org manufacturer codes +ISO3166CountryCodes= {'004':'Afghanistan',\ + '248':'Åland Islands',\ + '008':'Albania',\ + '012':'Algeria',\ + '016':'American Samoa',\ + '020':'Andorra',\ + '024':'Angola',\ + '660':'Anguilla',\ + '010':'Antarctica',\ + '028':'Antigua and Barbuda',\ + '032':'Argentina',\ + '051':'Armenia',\ + '533':'Aruba',\ + '036':'Australia',\ + '040':'Austria',\ + '031':'Azerbaijan',\ + '044':'Bahamas',\ + '048':'Bahrain',\ + '050':'Bangladesh',\ + '052':'Barbados',\ + '112':'Belarus',\ + '056':'Belgium',\ + '084':'Belize',\ + '204':'Benin',\ + '060':'Bermuda',\ + '064':'Bhutan',\ + '068':'Bolivia',\ + '070':'Bosnia and Herzegovina',\ + '072':'Botswana',\ + '074':'Bouvet Island',\ + '076':'Brazil',\ + '086':'British Indian Ocean Territory',\ + '096':'Brunei Darussalam',\ + '100':'Bulgaria',\ + '854':'Burkina Faso',\ + '108':'Burundi',\ + '116':'Cambodia',\ + '120':'Cameroon',\ + '124':'Canada',\ + '132':'Cape Verde',\ + '136':'Cayman Islands',\ + '140':'Central African Republic',\ + '148':'Chad',\ + '152':'Chile',\ + '156':'China',\ + '162':'Christmas Island',\ + '166':'Cocos (Keeling) Islands',\ + '170':'Colombia',\ + '174':'Comoros',\ + '178':'Congo',\ + '180':'Congo, the Democratic Republic of the',\ + '184':'Cook Islands',\ + '188':'Costa Rica',\ + '384':'Côte d\'Ivoire',\ + '191':'Croatia',\ + '192':'Cuba',\ + '196':'Cyprus',\ + '203':'Czech Republic',\ + '208':'Denmark',\ + '262':'Djibouti',\ + '212':'Dominica',\ + '214':'Dominican Republic',\ + '218':'Ecuador',\ + '818':'Egypt',\ + '222':'El Salvador',\ + '226':'Equatorial Guinea',\ + '232':'Eritrea',\ + '233':'Estonia',\ + '231':'Ethiopia',\ + '238':'Falkland Islands (Malvinas)',\ + '234':'Faroe Islands',\ + '242':'Fiji',\ + '246':'Finland',\ + '250':'France',\ + '254':'French Guiana',\ + '258':'French Polynesia',\ + '260':'French Southern Territories',\ + '266':'Gabon',\ + '270':'Gambia',\ + '268':'Georgia',\ + '276':'Germany',\ + '288':'Ghana',\ + '292':'Gibraltar',\ + '300':'Greece',\ + '304':'Greenland',\ + '308':'Grenada',\ + '312':'Guadeloupe',\ + '316':'Guam',\ + '320':'Guatemala',\ + '831':'Guernsey',\ + '324':'Guinea',\ + '624':'Guinea-Bissau',\ + '328':'Guyana',\ + '332':'Haiti',\ + '334':'Heard Island and McDonald Islands',\ + '336':'Holy See (Vatican City State)',\ + '340':'Honduras',\ + '344':'Hong Kong',\ + '348':'Hungary',\ + '352':'Iceland',\ + '356':'India',\ + '360':'Indonesia',\ + '364':'Iran, Islamic Republic of',\ + '368':'Iraq',\ + '372':'Ireland',\ + '833':'Isle of Man',\ + '376':'Israel',\ + '380':'Italy',\ + '388':'Jamaica',\ + '392':'Japan',\ + '832':'Jersey',\ + '400':'Jordan',\ + '398':'Kazakhstan',\ + '404':'Kenya',\ + '296':'Kiribati',\ + '408':'Korea, Democratic People\'s Republic of',\ + '410':'Korea, Republic of',\ + '414':'Kuwait',\ + '417':'Kyrgyzstan',\ + '418':'Lao People\'s Democratic Republic',\ + '428':'Latvia',\ + '422':'Lebanon',\ + '426':'Lesotho',\ + '430':'Liberia',\ + '434':'Libyan Arab Jamahiriya',\ + '438':'Liechtenstein',\ + '440':'Lithuania',\ + '442':'Luxembourg',\ + '446':'Macao',\ + '807':'Macedonia, the former Yugoslav Republic of',\ + '450':'Madagascar',\ + '454':'Malawi',\ + '458':'Malaysia',\ + '462':'Maldives',\ + '466':'Mali',\ + '470':'Malta',\ + '584':'Marshall Islands',\ + '474':'Martinique',\ + '478':'Mauritania',\ + '480':'Mauritius',\ + '175':'Mayotte',\ + '484':'Mexico',\ + '583':'Micronesia, Federated States of',\ + '498':'Moldova, Republic of',\ + '492':'Monaco',\ + '496':'Mongolia',\ + '499':'Montenegro',\ + '500':'Montserrat',\ + '504':'Morocco',\ + '508':'Mozambique',\ + '104':'Myanmar',\ + '516':'Namibia',\ + '520':'Nauru',\ + '524':'Nepal',\ + '528':'Netherlands',\ + '530':'Netherlands Antilles',\ + '540':'New Caledonia',\ + '554':'New Zealand',\ + '558':'Nicaragua',\ + '562':'Niger',\ + '566':'Nigeria',\ + '570':'Niue',\ + '574':'Norfolk Island',\ + '580':'Northern Mariana Islands',\ + '578':'Norway',\ + '512':'Oman',\ + '586':'Pakistan',\ + '585':'Palau',\ + '275':'Palestinian Territory, Occupied',\ + '591':'Panama',\ + '598':'Papua New Guinea',\ + '600':'Paraguay',\ + '604':'Peru',\ + '608':'Philippines',\ + '612':'Pitcairn',\ + '616':'Poland',\ + '620':'Portugal',\ + '630':'Puerto Rico',\ + '634':'Qatar',\ + '638':'Réunion',\ + '642':'Romania',\ + '643':'Russian Federation',\ + '646':'Rwanda',\ + '654':'Saint Helena',\ + '659':'Saint Kitts and Nevis',\ + '662':'Saint Lucia',\ + '666':'Saint Pierre and Miquelon',\ + '670':'Saint Vincent and the Grenadines',\ + '882':'Samoa',\ + '674':'San Marino',\ + '678':'Sao Tome and Principe',\ + '682':'Saudi Arabia',\ + '686':'Senegal',\ + '688':'Serbia',\ + '690':'Seychelles',\ + '694':'Sierra Leone',\ + '702':'Singapore',\ + '703':'Slovakia',\ + '705':'Slovenia',\ + '090':'Solomon Islands',\ + '706':'Somalia Somalia',\ + '710':'South Africa',\ + '239':'South Georgia and the South Sandwich Islands',\ + '724':'Spain',\ + '144':'Sri Lanka',\ + '736':'Sudan',\ + '740':'Suriname',\ + '744':'Svalbard and Jan Mayen',\ + '748':'Swaziland',\ + '752':'Sweden',\ + '756':'Switzerland',\ + '760':'Syrian Arab Republic',\ + '158':'Taiwan, Province of China',\ + '762':'Tajikistan',\ + '834':'Tanzania, United Republic of',\ + '764':'Thailand',\ + '626':'Timor-Leste',\ + '768':'Togo',\ + '772':'Tokelau',\ + '776':'Tonga',\ + '780':'Trinidad and Tobago',\ + '788':'Tunisia',\ + '792':'Turkey',\ + '795':'Turkmenistan',\ + '796':'Turks and Caicos Islands',\ + '798':'Tuvalu',\ + '800':'Uganda',\ + '804':'Ukraine',\ + '784':'United Arab Emirates',\ + '826':'United Kingdom',\ + '840':'United States',\ + '581':'United States Minor Outlying Islands',\ + '858':'Uruguay',\ + '860':'Uzbekistan',\ + '548':'Vanuatu',\ + '862':'Venezuela',\ + '704':'Viet Nam',\ + '092':'Virgin Islands, British',\ + '850':'Virgin Islands, U.S.',\ + '876':'Wallis and Futuna',\ + '732':'Western Sahara',\ + '887':'Yemen',\ + '894':'Zambia',\ + '716':'Zimbabwe',\ + '985':'MANUF: Destron Fearing / Digital Angel Corporation',\ + '984':'MANUF: Nedap',\ + '983':'MANUF: Texas Instruments',\ + '982':'MANUF: Allflex',\ + '981':'MANUF: Datamars',\ + '980':'MANUF: AGRIDENT BV',\ + '979':'MANUF: Earlsmere I.D.',\ + '978':'MANUF: IER SA',\ + '977':'MANUF: Avid',\ + '976':'MANUF: Gemplus',\ + '975':'MANUF: Sokymat',\ + '974':'MANUF: Impro',\ + '973':'MANUF: Fujihira',\ + '972':'MANUF: Planet ID',\ + '971':'MANUF: Alfa Laval Agri',\ + '970':'MANUF: Amphenol',\ + '969':'MANUF: Caisley',\ + '968':'MANUF: AEG',\ + '967':'MANUF: Rfdynamics',\ + '966':'MANUF: PetCode',\ + '965':'MANUF: 4D Technology Co. Ltd.',\ + '964':'MANUF: Rumitag S.L.',\ + '963':'MANUF: Korth Eletro Mecanica LTDA',\ + '962':'MANUF: DigiTag A/S',\ + '961':'MANUF: Mannings I.A.I.D.',\ + '960':'MANUF: Chevillot',\ + '959':'MANUF: Global ID Technologies',\ + '958':'MANUF: Pet ID',\ + '957':'MANUF: Innoceramics',\ + '956':'MANUF: Trovan Ltd.',\ + '955':'MANUF: Reseaumatique',\ + '954':'MANUF: Ryeflex',\ + '953':'MANUF: Cromasa',\ + '952':'MANUF: JECTA',\ + '951':'MANUF: Leader Products Pty Ltd',\ + '950':'MANUF: SPLICE do Brasil Telecomunicacoes e Eletronica S.A.',\ + '949':'MANUF: Y-Tex Corporation',\ + '948':'MANUF: H. Hauptner und Richard Herberholz GmbH & Co. KG',\ + '947':'MANUF: BELCAM. ID',\ + '946':'MANUF: Advanced Ceramics Limited',\ + '945':'MANUF: Business Inception Identification B.V.',\ + '944':'MANUF: Net & Telligent SA',\ + '943':'MANUF: E-Mark Technologie & Development',\ + '942':'MANUF: Zee Tags',\ + '941':'MANUF: Felixcan S.L.',\ + '940':'MANUF: Shearwell Data Ltd.',\ + '939':'MANUF: RealTrace',\ + '938':'MANUF: INSVET',\ + '937':'MANUF: ID & Traceback Systems AS',\ + '936':'MANUF: CROVET, S.L.',\ + '935':'MANUF: VeriLogik, Inc.',\ + '900':'MANUF: Shared (see http://www.icar.org/manufacturer_codes.htm)',\ + '1022':'UNREGISTERED MANUF: VeriChip Corporation'} diff --git a/isotype.py b/isotype.py new file mode 100755 index 0000000..b9028c1 --- /dev/null +++ b/isotype.py @@ -0,0 +1,81 @@ +#!/usr/bin/python + + +# isotype.py - determine ISO tag type +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2006, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + + +import sys +import os +import string +import RFIDIOtconfig + +try: + card= RFIDIOtconfig.card +except: + os._exit(True) + + +card.info('isotype v0.1l') + +typed= 0 +if card.readertype == card.READER_ACG: + for command, cardtype in card.ISOTags.iteritems(): + if not card.settagtype(command): + print 'Could not test for card type: ' + cardtype + continue + if card.select(): + print ' ID: ' + card.uid + print " Tag is " + cardtype + typed= 1 + if command == card.ISO15693: + print ' Manufacturer:', + try: + print card.ISO7816Manufacturer[card.uid[2:4]] + except: + print 'Unknown (%s)' % card.uid[2:4] + + for command, cardtype in card.ISOTagsA.iteritems(): + if not card.settagtype(command): + print 'Could not reset reader to ' + cardtype + '!' + os._exit(True) +if card.readertype == card.READER_PCSC: + if card.select(): + print '\n ID: ' + card.uid + print " Tag is " + card.tagtype + if string.find(card.tagtype,"ISO 15693") >= 0: + print ' Manufacturer:', + try: + print card.ISO7816Manufacturer[card.uid[2:4]] + except: + print 'Unknown (%s)' % card.uid[2:4] + typed= True + print + print + if not card.readersubtype == card.READER_ACS: + card.PCSCPrintATR(card.pcsc_atr) + else: + print card.ISO7816ErrorCodes[card.errorcode] + os._exit(True) +if not typed: + print "Could not determine type" + os._exit(True) + +os._exit(False) diff --git a/java/Makefile b/java/Makefile new file mode 100644 index 0000000..96ea493 --- /dev/null +++ b/java/Makefile @@ -0,0 +1,111 @@ +# +# Makefile for JAVA applet jcop_set_atr_hist +# +# Adam Laurie, 2009 +# http://rfidiot.org +# +# See inline comments for instructions on how to set up your environment +# + +APPLET_PACKAGE=jcop_set_atr_hist +APPLET_CLASS=JCOPSetATRHist +PACKAGE_AID=0xDC:0x44:0x20:0x06:0x06 +APPLET_AID=0xDC:0x44:0x20:0x06:0x06:0x07 +APPLET_AID_LENGTH=6 + +#JAVA_HOME=/usr/local/java/jdk +JAVA=$(JAVA_HOME)/bin/java +JAVAC=$(JAVA_HOME)/bin/javac +JAVADOC=$(JAVA_HOME)/bin/javadoc + +# Java Card Kit must be version 2.2.1 for gpshell +# and can be downloaded here: +# +# http://java.sun.com/javacard/downloads/index.jsp +# +# download 'Java Card 2.2.1 Development Kit' and set the following variable (JCZIP) +# to the zipfile name, then run 'make jckit' +# +JCZIP=java_card_kit-2_2_1-linux-dom.zip +JCPATH=./java_card_kit-2_2_1 + +# +# Global Platform API can be found here: +# +# http://www.globalplatform.org/specificationscard.asp +# +# download 'Java Card Export File for Card Specification v2.1' from the +# 'Archived Card Documentation' section. Make sure you choose v2.1 and NOT v2.1.1, +# and set the following variable (GPZIP) to the zipfile name, then run 'make gpapi' +# (GPZIP and GPDIR were correct at time of release, so should not need changing) +# +GPZIP=Java_Export_v2_1_or_v2_1_1_API.zip +GPDIR=GP_exportA00000015100 +#GPDIR=$(shell echo $(GPZIP) | cut -f1 -d'.') +GPLIB=./gp21.jar +GPEXPORTPATH=./gp21 + +JCAPI=$(JCPATH)/lib/api.jar +JCCONV=$(JCPATH)/lib/converter.jar +JCVERIFIER=$(JCPATH)/lib/offcardverifier.jar +EXPORTPATH=$(JCPATH)/api_export_files + +# +# GPShell can be got here: +# +# http://sourceforge.net/project/showfiles.php?group_id=143343&package_id=159897 +# +GPSHELL= "gpshell" +GPSHELL_SCRIPT="jcop_set_atr_hist.gpsh" +GPSHELL_UNINSTALL_SCRIPT="jcop_delete_atr_hist.gpsh" + +all: jcop_set_atr_hist.cap + +classes: build + $(JAVAC)\ + -g\ + -classpath $(JCAPI):$(GPLIB):.\ + -d build\ + -sourcepath ./src \ + -source 1.2 \ + -target 1.2 \ + src/jcop_set_atr_hist/JCOPSetATRHist.java + +jcop_set_atr_hist.cap: classes + $(JAVA)\ + -classpath $(JCCONV):$(JCVERIFIER)\ + com.sun.javacard.converter.Converter\ + -classdir build\ + -exportpath $(EXPORTPATH):$(GPEXPORTPATH):.\ + -applet $(APPLET_AID) $(APPLET_CLASS)\ + -d build\ + -out CAP\ + -v\ + -debug \ + $(APPLET_PACKAGE) $(PACKAGE_AID) 1.0 + cp build/jcop_set_atr_hist/javacard/jcop_set_atr_hist.cap . + +build: + @if ! test -e gp21.jar ; then echo "You need to set up your Global Platform environment! Read Makefile comments for help." ; exit 1 ; fi + @if ! test -e $(JCCONV); then echo "You need to set up your Java Card Kit! Read Makefile comments for help."; exit 1; fi + mkdir -p build + +install: + $(GPSHELL) $(GPSHELL_SCRIPT) + +uninstall: + $(GPSHELL) $(GPSHELL_UNINSTALL_SCRIPT) + +clean: + rm -f jcop_set_atr_hist.cap + rm -rf build + +gpapi: + @if ! test -e $(GPZIP) ; then echo "Can't find Global Platform API zipfile! Read Makefile comments for help." ; exit 1 ; fi + @unzip -x $(GPZIP) + cd $(GPDIR) && jar cvf ../gp21.jar * + mv $(GPDIR) gp21 + +jckit: + @if ! test -e $(JCZIP) ; then echo "Can't find Java Card Kit zipfile! Read Makefile comments for help." ; exit 1 ; fi + @unzip -x $(JCZIP) diff --git a/java/jcop_delete_atr_hist.gpsh b/java/jcop_delete_atr_hist.gpsh new file mode 100644 index 0000000..6a01467 --- /dev/null +++ b/java/jcop_delete_atr_hist.gpsh @@ -0,0 +1,16 @@ +// script to install applet.cap to jcop card using gpshell (http://sourceforge.net/projects/globalplatform/) +mode_211 +//enable_trace +establish_context +// edit the following line to match your PCSC reader +//card_connect -reader "OMNIKEY CardMan 5x21 00 01" +//card_connect -reader "OMNIKEY CardMan 5x21 (USB iClass Reader) 00 01" +//card_connect -reader "OMNIKEY CardMan 5x21 (OKCM0022602100142172731750393654) 00 00" +card_connect +//card_connect -reader "SDI010 USB Smart Card Reader (21120652200170) 00 01" +select -AID A000000003000000 +open_sc -security 3 -mac_key 404142434445464748494A4B4C4D4E4F -enc_key 404142434445464748494A4B4C4D4E4F -kek_key 404142434445464748494A4B4C4D4E4F // Open secure channel +delete -AID DC44200606 +get_status -element 10 +card_disconnect +release_context diff --git a/java/jcop_set_atr_hist.cap b/java/jcop_set_atr_hist.cap new file mode 100644 index 0000000000000000000000000000000000000000..c0413f3e5a4206bbb0297111f448579be70c8a2c GIT binary patch literal 3381 zcmai0drVVT82|30w+~(|6fH&MCCWovU^q4SNCA;Ykz#Q%W6DaoK(Y4Lwy5Atge@5a ziJQsH#6Os3Y|d?QTZnNpIunUH+@gD!&G^W0)6G|Oo5Q&b*msI>?M2Hy`Fd{8`OfeA zzH@%(`>v&wVYQ&Z*1u-YB6#A_0t;o!D^2r?R~8z}Oe>2Ems=|hWrZUH0A1F#LR+SG z+nm8=4>oR1OC{d)J?z9@Y&%%$S>=eruiUyP*dUE_+?|bARhe zh#rB&<~NU?Io$5}hkm~vDr#`9~}TyHoPRdMFy$~TI0zKXhiHOKr*7ytAA zL+_^AmFaaQ{iTMt9OjGNww?}cxknbFUii_#XKK~C!S}DxtEP7y`|F z35UUH7qUH?i<#GUg)K6KUoqZ&zO(w*_2+Z;U#U9O_0zYbD~3Oq)l#)<+w7Y=dN+6U z9?xx16zx5caW?wEtMQKgiNnVYeV^aBbM|Cd{?5OSr~eW8!$W%Ujf{nGVENJijuoCa zcQ5VN9LjIs9j!OD{SFtu*mh+=z2sa{fih;(>#^-M-&oa#TQknBd^ke){Cm%&80frV zoa4CnT6NQ-Q(OAF-}`&|BblXCuISyHp~IR3`XKb`>ve|8(T4ND!tK~fgyUk5O;?;rCyEM%LCA(aZz;T*M>jKR< zw_M5^HV8^$xxDjVuy;N!%&0B z6akssZYS>}W*AUWoIembLkT1BrKAX#D5dd$xE0gbE>PyiH(`j>=!c*jA5+;B z8iDAwXg2t}K;L^V9Q-l~{U(x*5@chnKAX>BFLdxue*g-kD4{0B_?e1prB9%xOYK7^ zqfv#(6qZ);b!Nei)&v*XDy82*xJ=HmD!Ee2ap9aoa=w{`J9lqIU?OQKLBc5Dy>^eo zjW>^Ahu{^%AZTPOHYF~FIB3OJ9S}AFaygXr3=S$L29*#2`mwN)5Xl%%1yOWTBA zm>p)oK@dJMX&`f;!2i`n#nwuotMY(b z>~J}JOMyvEdjVJLCgy)Yxy{4|Fw1*>yO%rUBX#`TVv$gO zeqrj8lMxyR2!!OsI!hs?Wm1IPhx`l@y`*SLaiv93#0|kK9t2lXBBi*} sdMM&H1jh}mj#5}@krH9k@p~+YyAvhLQYu4D!0Z+Zckncx^QXlB0SKr~tpET3 literal 0 HcmV?d00001 diff --git a/java/jcop_set_atr_hist.gpsh b/java/jcop_set_atr_hist.gpsh new file mode 100644 index 0000000..97a3b38 --- /dev/null +++ b/java/jcop_set_atr_hist.gpsh @@ -0,0 +1,17 @@ +// script to install applet.cap to jcop card using gpshell (http://sourceforge.net/projects/globalplatform/) +mode_211 +//enable_trace +establish_context +// edit the following line to match your PCSC reader +//card_connect -reader "OMNIKEY CardMan 5x21 (USB iClass Reader) 00 01" +//card_connect -reader "OMNIKEY CardMan 5x21 (OKCM0022602100142172731750393654) 00 00" +card_connect +//card_connect -reader "OMNIKEY CardMan 5x21 00 01" +//card_connect -reader "SDI010 USB Smart Card Reader (21120652200170) 00 01" +select -AID A000000003000000 +open_sc -security 3 -mac_key 404142434445464748494A4B4C4D4E4F -enc_key 404142434445464748494A4B4C4D4E4F -kek_key 404142434445464748494A4B4C4D4E4F // Open secure channel +delete -AID DC44200606 +install -file jcop_set_atr_hist.cap -priv 4 +get_status -element 10 +card_disconnect +release_context diff --git a/java/src/jcop_set_atr_hist/ATRGlobal.java b/java/src/jcop_set_atr_hist/ATRGlobal.java new file mode 100644 index 0000000..bbf2630 --- /dev/null +++ b/java/src/jcop_set_atr_hist/ATRGlobal.java @@ -0,0 +1,30 @@ +/** +* Adam Laurie +* http://rfidiot.org/ +* +* This code is copyright (c) Adam Laurie, 2009, All rights reserved. +* For non-commercial use only, the following terms apply - for all other +* uses, please contact the author: +* +* This code is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This code 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. +*/ + + + +package jcop_set_atr_hist; + +/* 15 byte buffer for ATR Historical Bytes (ATS) must be a global */ + +public class ATRGlobal { + public static byte[] ATR_HIST= {(byte) 0x00,(byte) 0x00,(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00,(byte) 0x00,(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00,(byte) 0x00,(byte) 0x00}; +} diff --git a/java/src/jcop_set_atr_hist/JCOPSetATRHist.java b/java/src/jcop_set_atr_hist/JCOPSetATRHist.java new file mode 100644 index 0000000..527adbb --- /dev/null +++ b/java/src/jcop_set_atr_hist/JCOPSetATRHist.java @@ -0,0 +1,68 @@ +/** +* JCOPSetATRHist.java - set ATR History bytes on JCOP cards +* +* Must be installed as "default selectable" (priv mode 0x04). +* +* Adam Laurie +* http://rfidiot.org/ +* +* This code is copyright (c) Adam Laurie, 2009, All rights reserved. +* For non-commercial use only, the following terms apply - for all other +* uses, please contact the author: +* +* This code is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This code 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. +*/ + + +package jcop_set_atr_hist; + +import javacard.framework.APDU; +import javacard.framework.ISO7816; +import javacard.framework.Applet; +import javacard.framework.ISOException; +import javacard.framework.Util; +import org.globalplatform.GPSystem; + +public class JCOPSetATRHist extends Applet +{ + public static void install(byte[] bArray, short bOffset, byte bLength) + { + new jcop_set_atr_hist.JCOPSetATRHist().register(bArray, (short) (bOffset + 1), + bArray[bOffset]); + } + + public void process(APDU apdu) + { + byte[] buffer = apdu.getBuffer(); + byte ins = buffer[ISO7816.OFFSET_INS]; + + if (selectingApplet()) + { + return; + } + + byte len = buffer[ISO7816.OFFSET_CDATA]; + // Max ATS is 15 bytes + if (len > (byte) 15) + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + + Util.arrayCopy(buffer,(short) (ISO7816.OFFSET_CDATA + 1),ATRGlobal.ATR_HIST,(short) 0x00,len); + switch (ins) + { + case (byte) 0xAB: + if( ! org.globalplatform.GPSystem.setATRHistBytes(ATRGlobal.ATR_HIST,(short) 0x00, len )) + ISOException.throwIt(ISO7816.SW_UNKNOWN); + return; + default: + ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); + } + } +} diff --git a/jcop_mifare_access.cap b/jcop_mifare_access.cap new file mode 100644 index 0000000000000000000000000000000000000000..110e31bb0293a5fb0ac9184759f4f3773d736cc7 GIT binary patch literal 3841 zcma)8dr(y86+ic}_wFvQ3$nV)%1a(uqr9w|svxh02n$GrB%Rn6mt}!PVDat}kVzWE zXdD-kQe@g0HPzHiGI7fE4~*t9oiv^NgWBrEscELuOk&0~6CGwUoiS}D(%*Lhm%Slu z@13)I?mg%Go%5Z0zH?5aOAwREi2L)2yzR8)N+yx&tD4L6Y8t8w>dPBys;ios^Xsec z&k&96x!myQLgn2@XG-rR#!sC(XTEV!adcH>ykfaIqrN$L_Q1&L=DVMGiXu-4Hw2ITb0q*XJ1V@c-ZjPwo9Qw=aZX{hN{kIOjJC1dtb`NxGN{D zKl|^?vn5|A+1@?=#?L3!t6QcnDV?LUSO4?k#EGv)t~4LE-)Ou3xBge{UysiI{>D!V zf0LUKdWX9ZjQ#exzkFc-B*V08(>va>d#5(N{aWR*$^rBCf1N)!`SIDw>)(C)%FbJk zxq@$7N{*BsR2x3q5aSs*e&_V;pI$!t?1ghv7e6|6`{Czry_EO->FQ@jN=LrDy7imP zdUey}-r~gfGe30{b^pR~?e3vxvt3^%J@uwBFqLsE<5K&0s-M62_oRy2hrXEjYyX7_ zLzmR?>o2;qf7^5A!`F(xn%VWo{sYtZ_YXdI;%4Z`+m`#(=#q`s4&S?0#u1qX!m}Wn zwb-+-@ZN_XKa+zeM!BFakH4>_$Jg#wy)AB!#~TO~bh)2)d)#VU!7i`6&8y~n+m=|csQ1}w@C8H!*Q^j&?YEFp6AwJUMMAXAEz6QFdBkE2y&v)V?RdLK-{#+Xc3L+OI0LIB_DdRMQMr&esm3Esa=WF7OWQ`iBlB3?_mB( zcH?Ss$$A?U?Si6=QGAFJY~Vtq3`NFs|J9E!E++YgDDi9xx`RGXwa?qF1=WZX^x%p_ z{nfU&zR7}&Q8d&&-u61b2gv?j_Nh!bm0TRz4RNAbHdrzxNtVqR?0G`H{elyeW(e{R1BTrv~-`(c>Gc}(u(JXMLGmQF2w?mkUI(F zpd`j4C9|l2nixOF(;)aY3;a0v%msdWQ3tu7O}TS(z;b}wKzWd``$zN$?rnH3;r_rW z;&c(0!Iw*&bk^Wyz5wOa{U4ThknCe+s=J$H$KHyj3M`je%T?8VkmT0g?d<_?5KNu7w<8Ec z6(skme%5Jdt)p%^ryHG*?_e&t``Y@+_Jb-UwDz}?ve3L>M0NMJ`Flv}X;)hUDsin8 zyC57;eL?TMU_d1)pmiIHS&*vebMr#A4SB&*jn!5jM**K$)^809GH;4)|Q!LbU@aC%JrfELD_je;35 zuIXX*!&n&hDI9^7Tn&$AdQAO17REdu1v5OI=`rp-hjgpQggdC!--R z&Q^Lz{V)}VoQ(n*9 +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2006, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# +# history +# 15/11/08 - ver 0.1a - first cut, seems to work. :) +# 13/01/09 - ver 0.1b - add RANDOM UID mode + +import RFIDIOtconfig +import sys +import os + +try: + card= RFIDIOtconfig.card +except: + os._exit(True) + +args= RFIDIOtconfig.args +Help= RFIDIOtconfig.help + +# fixed values required by JCOP applet +CLA= '00' +INS= 'MIFARE_ACCESS' +P1= '03' +WRITE= '01' +READ= '02' +RANDOM= '03' +MIFARE_AID= 'DC4420060606' + +card.info('jcopmifare v0.1d') + +if Help or len(args) < 2: + print '\nUsage:\n\n\t%s [OPTIONS] [SECTOR] [HEX DATA]' % sys.argv[0] + print + print '\tMIFARE_PWD should be the HEX 8 BYTE MifarePWD produced by mifarekeys.py, or the' + print '\tRANDOM_UID secret key.' + print + print '\tSECTOR number must be specified for READ and WRITE operations. Note that not all' + print '\tsectors are WRITEable.' + print + print '\tRANDOM will set card into RANDOM_UID mode. All future selects will return a random' + print '\tUID instead of the one stored in sector 0. This behaviour cannot be reversed.' + print + print '\tHEX DATA must be 16 BYTES worth of HEX for WRITE operations.' + print + print '\t(default NXP transport keys are both FFFFFFFFFFFF, so MifarePWD is 0B54570745FE3AE7)' + print '\t(sector 0 default is A0A1A2A3A4A5, so MifarePWD is 0FB3BBC7099ED432)' + print + print '\tExample:' + print + print '\t\t./jcopmifare.py WRITE 0B54570745FE3AE7 1 12345678123456781234567812345678' + print + print + print '\tNote that jcop_mifare_access.cap or native Mifare emulation must be active on the card.' + print + os._exit(True) + +def mifare_read(key,sector): + cla= CLA + ins= INS + p1= P1 + p2= READ + data= key + '%02X' % int(sector) + lc= '%02X' % (len(data) / 2) + le= '10' + + if card.send_apdu('','','','',cla,ins,p1,p2,lc,data,le): + return True, card.data + return False, card.errorcode + +def mifare_write(key,sector,sectordata): + cla= CLA + ins= INS + p1= P1 + p2= WRITE + data= key + sectordata + '%02X' % int(sector) + lc= '%02X' % (len(data) / 2) + + if card.send_apdu('','','','',cla,ins,p1,p2,lc,data,''): + return True, card.data + return False, card.errorcode + +def mifare_random(key): + cla= CLA + ins= INS + p1= P1 + p2= RANDOM + data= key + lc= '%02X' % (len(data) / 2) + + if card.send_apdu('','','','',cla,ins,p1,p2,lc,data,''): + return True, card.data + return False, card.errorcode + +def select_mifare_app(): + "select mifare application (AID: DC4420060606)" + ins= 'SELECT_FILE' + p1= '04' + p2= '0C' + data= MIFARE_AID + lc= '%02X' % (len(data) / 2) + card.send_apdu('','','','','',ins,p1,p2,lc,data,'') + if card.errorcode == card.ISO_OK: + return True + else: + return False + +def error_exit(message,error): + print ' %s, error number: %s' % (message,error), + try: + print card.ISO7816ErrorCodes[error] + except: + print + os._exit(True) + +if card.select(): + print ' Card ID: ' + card.uid + if card.readertype == card.READER_PCSC: + print ' ATR: ' + card.pcsc_atr +else: + print ' No card present' + +# high speed select required for ACG +if not card.hsselect('08'): + print ' Could not select card for APDU processing' + os._exit(True) + +if not select_mifare_app(): + print ' Could not select mifare applet!' + os._exit(True) + +if args[0] == 'READ': + stat, data= mifare_read(args[1],args[2]) + if not stat: + error_exit('Read failed', data) + else: + print 'Data: ', data + os._exit(False) + +if args[0] == 'WRITE': + stat, data= mifare_write(args[1],args[2],args[3]) + if not stat: + error_exit('Write failed', data) + else: + print 'Write completed' + os._exit(False) + +if args[0] == 'RANDOM': + stat, data= mifare_random(args[1]) + if not stat: + error_exit('Random_UID mode failed', data) + else: + print 'Random_UID set' + os._exit(False) + + + +print "Unrecognised command:", args[0] +os._exit(True) diff --git a/jcopsetatrhist.py b/jcopsetatrhist.py new file mode 100755 index 0000000..5044484 --- /dev/null +++ b/jcopsetatrhist.py @@ -0,0 +1,128 @@ +#!/usr/bin/python + + +# jcopsetatrhist.py - set ATR History bytes on JCOP cards +# +# The applet jcop_set_atr_hist.cap must be loaded onto the card first, +# and it must be installed as "default selectable" (priv mode 0x04). +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2008, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + +import RFIDIOtconfig +import sys +import os +import string + +try: + card= RFIDIOtconfig.card +except: + os._exit(True) + +args= RFIDIOtconfig.args +Help= RFIDIOtconfig.help + +# fixed values required by JCOP applet +CLA= '80' +P1= '00' +P2= '00' +JCOP_ATR_AID= 'DC4420060607' + +if Help or len(args) < 2: + print '\nUsage:\n\n\t%s [OPTIONS] \'SET\' ' % sys.argv[0] + print + print '\tHEX DATA is up to 15 BYTES of ASCII HEX.' + print + print '\tExample:' + print + print '\t./jcopsetatrhist.py SET 0064041101013180009000' + print + os._exit(True) + +def jcop_set_atr_hist(bytes): + cla= CLA + ins= 'ATR_HIST' + p1= P1 + p2= P2 + data= '%02X' % (len(bytes) / 2) + bytes + lc= '%02X' % (len(data) / 2) + if card.send_apdu('','','','',cla,ins,p1,p2,lc,data,''): + return True, card.data + return False, card.errorcode + +def select_atrhist_app(): + "select atr_hist application (AID: DC4420060607)" + ins= 'SELECT_FILE' + p1= '04' + p2= '0C' + data= JCOP_ATR_AID + lc= '%02X' % (len(data) / 2) + card.send_apdu('','','','','',ins,p1,p2,lc,data,'') + if card.errorcode == card.ISO_OK: + return True + else: + return False + +def error_exit(message,error): + print ' %s, error number: %s' % (message,error), + try: + print card.ISO7816ErrorCodes[error] + except: + print + os._exit(True) + +card.info('jcopsetatrhist v0.1c') + +if card.select(): + print ' Card ID: ' + card.uid + if card.readertype == card.READER_PCSC: + print ' ATR: ' + card.pcsc_atr +else: + print ' No card present' + os._exit(True) + +# high speed select required for ACG +if not card.hsselect('08'): + print ' Could not select card for APDU processing' + os._exit(True) + +if not select_atrhist_app(): + print + print " Can't select atrhist applet!" + print ' Please load jcop_set_atr_hist.cap onto JCOP card.' + print ' (Use command: gpshell java/jcop_set_atr_hist.gpsh)' + print + os._exit(True) + +if args[0] == 'SET': + stat, data= jcop_set_atr_hist(args[1]) + if not stat: + error_exit('Set hist bytes failed', data) + else: + print + print ' ATR History Bytes (ATS) set to', args[1] + print + print ' *** Remove card from reader and replace to finalise!' + print + print ' You can now delete jcop_set_atr_hist.cap from the JCOP card.' + print ' (Use command: gpshell java/jcop_delete_atr_hist.gpsh)' + print + os._exit(False) +else: + print "Unrecognised command:", args[0] + os._exit(True) diff --git a/jcoptool.py b/jcoptool.py new file mode 100755 index 0000000..14cf80c --- /dev/null +++ b/jcoptool.py @@ -0,0 +1,490 @@ +#!/usr/bin/python + + +# jcoptool.py - JCOP card toolkit +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2009, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + +import RFIDIOtconfig +import sys +import os +import string +from Crypto.Cipher import DES3 +from Crypto.Cipher import DES +from pyasn1.codec.ber import decoder + +try: + card= RFIDIOtconfig.card +except: + os._exit(True) + +args= RFIDIOtconfig.args +Help= RFIDIOtconfig.help + +# fixed values required by JCOP applet +CLA= '80' +P1= '00' +P2= '00' + +templates= { + '66':'Card Data', + '73':'Card Recognition Data', + } + +tags= { + '06':'OID', + '60':'Application tag 0 - Card Management Type and Version', + '63':'Application tag 3 - Card Identification Scheme', + '64':'Application tag 4 - Secure Channel Protocol of the Issuer Security Domain and its implementation options', + '65':'Application tag 5 - Card configuration details', + '66':'Application tag 6 - Card / chip details', + '67':'Application tag 7 - Issuer Security Domain\'s Trust Point certificate information', + '68':'Application tag 8 - Issuer Security Domain certificate information', + } + +registry_tags= { + '4F':'AID', + '9F70':'Life Cycle State', + 'C5':'Privileges', + 'C4':'Application\'s Executable Load File AID', + 'CE':'Executable Lod File Version Number', + '84':'First or only ExecutableModule AID', + 'CC':'Associated Security Domain\'s AID', + } + +card_status= { + '80':'Issuer Security Domain', + '40':'Applications and Supplementary Security Domains', + '20':'Executable Load Files', + '10':'Executable Load Files and their Executable Modules', + } + +# life cycle state must be masked as bits 4-7 (bit numbering starting at 1) are application specific +application_life_cycle_states= { + '01':'LOADED', + '03':'INSTALLED', + '07':'SELECTABLE', + '83':'LOCKED', + '87':'LOCKED', + } + +executable_life_cycle_states= { + '01':'LOADED', + } + +security_domain_life_cycle_states= { + '03':'INSTALLED', + '07':'SELECTABLE', + '0F':'PERSONALIZED', + '83':'LOCKED', + '87':'LOCKED', + '8B':'LOCKED', + '8F':'LOCKED', + } + + +card_life_cycle_states= { + '01':'OP_READY', + '07':'INITIALIZED', + '0F':'SECURED', + '7F':'CARD_LOCKED', + 'FF':'TERMINATED', + } + +targets= { + '00':'Unknown', + '01':'SmartMX', + '03':'sm412', + } + +fuse_state= { + '00':'Not Fused', + '01':'Fused', + } + +manufacturers= { + 'PH':'Philips Semiconductors', + 'NX':'NXP', + } + +privilege_byte_1= { + '80':'Security Domain', + 'C0':'DAP Verification', + 'A0':'Delegated Management', + '10':'Card Lock', + '08':'Card Terminate', + '04':'Card Reset', + '02':'CVM Management', + 'C1':'Mandated DAP Verification', + } + +def decode_jcop_identify(data, padding): + fabkey= data[0:2] + patch_id= data[2:4] + target= data[4:6] + mask_id= data[6:8] + custom_mask= data[8:16] + mask_name= data[16:28] + fuse= data[28:30] + rom_info= data[30:42] + + manufacturer= card.ToBinary(mask_name[0:4]) + manufacture_year= card.ToBinary(mask_name[4:6]) + manufacture_week= card.ToBinary(mask_name[6:10]) + manufacture_mask= ord(card.ToBinary(mask_name[10:12])) - 64 + + + print padding + 'FABKEY ID: %s' % fabkey + print padding + 'PATCH ID: %s' % patch_id + print padding + 'TARGET ID: %s' % target + ' (' + targets[target] + ')' + print padding + 'MASK ID: %s' % mask_id + ' (Mask %s)' % int(mask_id,16) + print padding + 'CUSTOM MASK: %s' % custom_mask + ' (%s)' % card.ReadablePrint(card.ToBinary(custom_mask)) + print padding + 'MASK NAME: %s' % card.ToBinary(mask_name) + print padding + 'FUSE STATE: %s' % fuse + ' (' + fuse_state[fuse] + ')' + print padding + 'ROM INFO: %s' % rom_info + ' (Checksum)' + print padding + 'COMBO NAME: %s-m%s.%s.%s-%s' % (targets[target], mask_id, fabkey, patch_id, card.ToBinary(mask_name)) + print padding + 'MANUFACTURER: %s' % manufacturers[manufacturer] + print padding + 'PRODUCED: Year %s, Week %s, Build %d' % (manufacture_year, manufacture_week, manufacture_mask) + +def decode_jcop_lifecycle(data, padding): + ic_fab= data[0:4] + ic_type= data[4:8] + os_id= data[8:12] + os_release_date= data[12:16] + os_release_level= data[16:20] + ic_fab_date= data[20:24] + ic_serial= data[24:32] + ic_batch= data[32:36] + ic_mod_fab= data[36:40] + ic_mod_pack_date= data[40:44] + icc_man= data[44:48] + ic_embed_date= data[48:52] + ic_pre_perso= data[52:56] + ic_pre_perso_date= data[56:60] + ic_pre_perso_equip= data[60:68] + ic_perso= data[68:72] + ic_perso_date= data[72:76] + ic_perso_equip= data[76:84] + + print + print padding + 'IC Fabricator %s' % ic_fab + print padding + 'IC Type %s' % ic_type + print padding + 'OS ID %s' % os_id + print padding + 'OS Release Date %s' % os_release_date + print padding + 'OS Release Level %s' % os_release_level + print padding + 'IC Fabrication Date Year %s Day %s' % (ic_fab_date[0], ic_fab_date[1:4]) + print padding + 'IC Serial Number %s' % ic_serial + print padding + 'IC Batch Number %s' % ic_batch + print padding + 'IC Module Fabricator %s' % ic_mod_fab + print padding + 'IC Module Packaging Date Year %s Day %s' % (ic_mod_pack_date[0], ic_mod_pack_date[1:4]) + print padding + 'ICC Manufacturer %s' % icc_man + print padding + 'IC Embedding Date Year %s Day %s' % (ic_embed_date[0], ic_embed_date[1:4]) + print padding + 'IC Pre-Personalizer %s' % ic_pre_perso + print padding + 'IC Pre-Personalization Date %s' % ic_pre_perso_date + print padding + 'IC Pre-Personalization Equipment %s' % ic_pre_perso_equip + print padding + 'IC Personalizer %s' % ic_perso + print padding + 'IC Personalization Date Year %s Day %s' % (ic_perso_date[0], ic_perso_date[1:4]) + print padding + 'IC Personalization Equipment %s' % ic_perso_equip + +def decode_privileges(data): + print '(', + multiple= False + try: + for mask in privilege_byte_1.keys(): + if (int(data[0:2],16) & int(mask,16)) == int(mask,16): + if multiple: + print '/', + print privilege_byte_1[mask], + multiple= True + except: + print ')', + return + print ')', + +# check privilege byte 0 to see if we're a security domain +def check_security_domain(data): + length= int(data[2:4],16) * 2 + i= 4 + while i < length + 4: + for item in registry_tags.keys(): + if data[i:i+len(item)] == item: + itemlength= int(data[i+len(item):i+len(item)+2],16) * 2 + if item == card.GP_REG_PRIV: + itemdata= data[i+len(item)+2:i+len(item)+2+itemlength] + if (int(itemdata[0:2],16) & 0x80) == 0x80: + return True + i += itemlength + len(item) + 2 + return False + +def decode_gp_registry_data(data, padding, filter): + if not data[0:2] == card.GP_REG_DATA: + return False, '' + states= application_life_cycle_states + if filter == card.GP_FILTER_ISD: + states= card_life_cycle_states + if filter == card.GP_FILTER_ASSD: + states= application_life_cycle_states + if filter == card.GP_FILTER_ELF: + states= executable_life_cycle_states + # check if this is a security domain (not set up right, so disabled!) + #if check_security_domain(data): + # states= security_domain_life_cycle_states + length= int(data[2:4],16) * 2 + i= 4 + while i < length + 4: + decoded= False + for item in registry_tags.keys(): + if data[i:i+len(item)] == item: + if not item == card.GP_REG_AID: + print ' ', + itemlength= int(data[i+len(item):i+len(item)+2],16) * 2 + itemdata= data[i+len(item)+2:i+len(item)+2+itemlength] + print padding, registry_tags[item]+':', itemdata, + if item == card.GP_REG_LCS: + if filter == card.GP_FILTER_ASSD: + # mask out application specific bits + itemdata= '%02x' % (int(itemdata,16) & 0x87) + print '( '+states[itemdata]+' )', + if item == card.GP_REG_PRIV: + decode_privileges(itemdata) + decoded= True + i += itemlength + len(item) + 2 + print + if not decoded: + return False + return True + +card.info('jcoptool v0.1c') +if Help or len(args) < 1: + print '\nUsage:\n\n\t%s [OPTIONS] [ARGS] [ENC Key] [MAC Key] [KEK Key]' % sys.argv[0] + print + print '\tWhere COMMAND/ARGS are one of the following combinations:' + print + print "\tINFO\t\t\tDisplay useful info about the JCOP card and it's contents." + print + print '\tDES keys ENC MAC and KEK are always the final 3 arguments, and should be in HEX.' + print '\tIf not specified, the default \'404142434445464748494A4B4C4D4E4F\' will be used.' + print + os._exit(True) + +command= args[0] + +if card.select(): + print + print ' Card ID: ' + card.uid + if card.readertype == card.READER_PCSC: + print ' ATS: %s (%s)' % (card.pcsc_ats,card.ReadablePrint(card.ToBinary(card.pcsc_ats))) +else: + print ' No RFID card present' + print + #os._exit(True) + +#print ' ATR: ' + card.pcsc_atr +#print + +# high speed select required for ACG +if not card.hsselect('08'): + print ' Could not select RFID card for APDU processing' + #os._exit(True) + +print +print ' JCOP Identity Data:', +# send pseudo file select command for JCOP IDENTIFY +card.iso_7816_select_file(card.AID_JCOP_IDENTIFY,'04','00') +if card.errorcode == '6A82' and len(card.data) > 0: + print card.data + print + decode_jcop_identify(card.data,' ') +else: + print ' Device does not support JCOP IDENTIFY!' + +# card life cycle data +# high speed select required for ACG +if not card.hsselect('08'): + print ' Could not select RFID card for APDU processing' +print +print ' Life Cycle data:', +if not card.gp_get_data('9F7F'): + print " Failed - ", card.ISO7816ErrorCodes[card.errorcode] +else: + print card.data + if card.data[0:4] == '9F7F': + decode_jcop_lifecycle(card.data[6:],' ') + +# select JCOP Card Manager +# high speed select required for ACG +if not card.hsselect('08'): + print ' Could not select RFID card for APDU processing' +if not card.iso_7816_select_file(card.AID_CARD_MANAGER,'04','00'): + print + print " Can't select Card Manager!", + card.iso_7816_fail(card.errorcode) + +if command == 'INFO': + # high speed select required for ACG + if not card.hsselect('08'): + print ' Could not select RFID card for APDU processing' + # get Card Recognition Data + if not card.gp_get_data('0066'): + print + print " Can't get Card Recognition Data!", + card.iso_7816_fail(card.errorcode) + pointer= 0 + item= card.data[pointer:pointer+2] + if item != '66': + print 'Unrecognised template:', item + os._exit(True) + pointer += 2 + item= card.data[pointer:pointer+2] + length= int(item,16) + + print + print ' Card Data length:',length + pointer += 2 + item= card.data[pointer:pointer+2] + if item != '73': + print 'Unrecognised template:', item + os._exit(True) + pointer += 2 + item= card.data[pointer:pointer+2] + length= int(item,16) + print ' Card Recognition Data length:',length + pointer += 2 + while pointer < len(card.data): + item= card.data[pointer:pointer+2] + try: + print ' '+tags[item]+':', + pointer += 2 + length= int(card.data[pointer:pointer + 2],16) + pointer += 2 + if tags[item] == 'OID': + decodedOID, dummy= decoder.decode(card.ToBinary(item+('%02x' % length)+card.data[pointer:pointer + length * 2])) + print decodedOID.prettyPrint() + else: + if(card.data[pointer:pointer + 2]) == '06': + decodedOID, dummy= decoder.decode(card.ToBinary(card.data[pointer:pointer + length * 2])) + print + print ' OID:', decodedOID.prettyPrint() + else: + print card.data[pointer:pointer + length * 2] + pointer += length * 2 + except: + print 'Unrecognised tag', item + os._exit(True) + # set up DES keys for encryption operations + if len(args) > 1: + enc_key= args[1] + if len(args) > 2: + mac_key= args[2] + else: + enc_key= card.GP_ENC_KEY + mac_key= card.GP_MAC_KEY + +if command == 'INSTALL': + if len(args) > 2: + enc_key= args[2] + if len(args) > 3: + mac_key= args[3] + else: + enc_key= card.GP_ENC_KEY + mac_key= card.GP_MAC_KEY + +if command == 'INFO' or command == 'INSTALL': + # authenticate to card + # initialise secure channel + print + print ' *** Warning' + print ' *** Repeated authentication failures may permanently disable device' + print + x= string.upper(raw_input(' Attempt to authenticate (y/n)? ')) + if not x == 'Y': + os._exit(True) + + # high speed select required for ACG + if not card.hsselect('08'): + print ' Could not select RFID card for APDU processing' + host_challenge= card.GetRandom(8) + if not card.gp_initialize_update(host_challenge): + print 'Can\'t Initialise Update!' + card.iso_7816_fail(card.errorcode) + card_key_diversification, card_key_info, card_sc_sequence_counter,card_challenge,card_cryptogram= card.gp_initialize_update_response_scp02(card.data) + + + secure_channel_protocol= card_key_info[2:4] + + if secure_channel_protocol == card.GP_SCP02: + # create ENC session key by encrypting derivation data with ENC key + session_pad= '000000000000000000000000' + derivation_data= '0182' + card_sc_sequence_counter + session_pad + # create encryption object with ENC key + e_enc= DES3.new(card.ToBinary(enc_key),DES3.MODE_CBC) + enc_s_key= e_enc.encrypt(card.ToBinary(derivation_data)) + # data for cryptograms + card_cryptogram_source= host_challenge + card_sc_sequence_counter + card_challenge + host_cryptogram_source= card_sc_sequence_counter + card_challenge + host_challenge + # check card cryptogram + check_cryptogram= string.upper(card.ToHex(card.DES3MAC(card.ToBinary(card_cryptogram_source), enc_s_key, ''))) + if not check_cryptogram == card_cryptogram: + print 'Key mismatch!' + print 'Card Cryptogram: ', card_cryptogram + print 'Calculated Cryptogram:', check_cryptogram + os._exit(True) + + # cryptogram checks out, so we can use session key + # create encryption object with ENC Session key + s_enc= DES3.new(enc_s_key,DES3.MODE_CBC) + + # authenticate to card + host_cryptogram= card.DES3MAC(card.ToBinary(host_cryptogram_source), enc_s_key, '') + # create encryption object with MAC key + e_enc= DES3.new(card.ToBinary(mac_key),DES3.MODE_CBC) + # create C-MAC session key + derivation_data= '0101' + card_sc_sequence_counter + session_pad + cmac_s_key= e_enc.encrypt(card.ToBinary(derivation_data)) + if not card.gp_external_authenticate(host_cryptogram,cmac_s_key): + print 'Card Authentication failed!' + card.iso_7816_fail(card.errorcode) + else: + print 'Unsupported Secure Channel Protocol:', secure_channel_protocol + os._exit(True) + + +print ' Authentication succeeded' +# get card status (list card contents) +# high speed select required for ACG +#if not card.hsselect('08'): +# print ' Could not select RFID card for APDU processing' +print +print ' Card contents:' +for filter in '80','40','20','10': + if not card.gp_get_status(filter,'02',''): + if not card.errorcode == '6A88': + print + print " Can't get Card Status!", + card.iso_7816_fail(card.errorcode) + print + print ' ', card_status[filter]+':' + if card.errorcode == '6A88': + print ' None!' + else: + if not decode_gp_registry_data(card.data,' ',filter): + print ' Can\'t decode Registry!' + print card.data + os._exit(True) +os._exit(False) diff --git a/lfxtype.py b/lfxtype.py new file mode 100755 index 0000000..ccda69d --- /dev/null +++ b/lfxtype.py @@ -0,0 +1,50 @@ +#!/usr/bin/python + + +# lfxtype.py - select card and display tag type +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2006, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + + +import RFIDIOtconfig +import sys +import os + +try: + card= RFIDIOtconfig.card +except: + os._exit(True) + + +card.info('lfxtype v0.1i') +card.select() +ID= card.uid +if ID: + print 'Card ID: ' + ID + print 'Tag type: ' + card.LFXTags[card.tagtype] + if card.tagtype == card.EM4x02: + print ' Unique ID: ' + card.EMToUnique(ID) + card.settagtype(card.Q5) + card.select() + if card.uid: + print ' *** This is a Q5 tag in EM4x02 emulation mode ***' + os._exit(False) +else: + print 'No TAG present!' + os._exit(True) diff --git a/loginall.py b/loginall.py new file mode 100755 index 0000000..f094ee9 --- /dev/null +++ b/loginall.py @@ -0,0 +1,49 @@ +#!/usr/bin/python + +# loginall.py - attempt to login to each sector with transport keys +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2006, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + + +import RFIDIOtconfig + +try: + card= RFIDIOtconfig.card +except: + os._exit(True) + +card.info('loginall v0.1g') + +card.select() +print '\ncard id: ' + card.uid + +block = 0 + +while block < 16: + for X in [ 'AA', 'BB', 'FF' ]: + card.select() + print '%02x %s: ' % (block, X), + if card.login(block, X, ''): + print "success!" + elif card.errorcode: + print "error: " + card.errorcode + else: + print "failed" + block += 1 +os._exit(False) diff --git a/mifare.pdf b/mifare.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8fc1e3f87052381183458f0bc7da97bb8cc92dcf GIT binary patch literal 328329 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≪PyldwEb9&Y>s6@L=6Cxqv)qlNb8Qx< ztQu$`!WJ%mF-2oavNlHCzZe;7?e2<0)CV|!a7*B5m^bsF*0B{eJw^Gqi^&DmsAdTj zAQtm)Wx8J|s@MqP%l1VVSa49*+zxHK%Q= zlhb1NI-#PXs~bj|Fuco!x7@}U!3UK~WyKt?ZX ztq~z_stCZ5qD%JNZywQb5W z^2PEHLMuP1!7!Jt4Fuq`jj3SB(Fxz(5X3F;IB}dgS<<7w*EJn$K z)U?^pN{!x@MTk8;RG2o~wjXyl`#2^s&KJ~m5%Fra9J-2V{hi%6y(h**o9r#t>?RMu6UxhqR?ef-d z&otVt==0o_=op(72gT$^hZtF;#cWFgFpLLfgACj{WToK4PJymcZ$Lo#N5zsv_`jN; zsiAWc5$?Ssk}Rz~io%pKte6Mi-N_BXPyYZvYi9P8leP0n+)3U~4s4Uh4iWaqWLdN< zBN(hD4}MyFt7vXM<;FwX-zKE2n&PersENgZj)^Ga495dE*TAALaDP;P)C@Tdac9RD{uTAeLL=GwJe!0&9m{6@(ypMiumaX|H$@;Pgm6yH=u}H z3kk_f$z3^pRj7~NEhyR!e0n`bAL5zcPW*hwd;9a|V+iH(-ge{lOdxZssqdw3#^-Mi z6}TMxc7{F{3a}P?KU?j(t0|-?uy{+Lie~yXSx~a#5_Phm2?lK(bfcmX*9ZP3cU9kz zoM*&-M{EKo+sgth#k(-DHS9On$9jCna2Pr|`-ln5eWr*!cQ=iT3rPrkx77Y3{UJhH z)U3T2+O49Ci&@AvRy^=*YjL1&)!ZWHW33}F7^45)>C|?KW!JzHwPz&;sLuK=Rm^g4 z-sJctw9ZyH&m1{F>{Vh%-%@GbFXhvvd8UIPaDlS9*ce6FXqE*{q|gfx7(W-;rK|>R zjr|9Y{O|ax6yur)6^}@YhYiUb@dPYNXbv&`A*(`oMr9Yl)pFzp_L=1?sxk{~GIW>b zLO1}>B9}}30F8tP%Ja3{AFK!6yRrrC0c(nK;w@GQpC3KsK`h!LR8DSKbbLscg>FW{ zZy%wy)`aj!A}YR`u+w%#Mx-DWN!}GC5-=^IL40y49q&9O;v*S^qb-mYLYt)65%S6s z%RukO%LZC#tAa+&b0;#~Lg_N2bSH{8)n8K#ND^qPR5lehiQ)ci{$N-WK<2u%YZ{&> zZ-5f1MeyTAy-LKJaWd15qx4n%uNCRsF52HUU#ZO-VW4_IdYq(})?y71h>?wbd+~1Xr5>w;EeO;I@PS|?L`+DSMs;UTfQ*n}J>&fa{ z$qXAsim69LL{c;eHB5@M3!OQU%4}M%X3Z;`PbTug&j7&9XC@WW%+Qsi11&RUdA91o z%Q!}|Rev$XDq9V7-RUf{00o7TQ-Y|Hz-6(5 z1nQA4WNuqv5iC>!CyMGSZZh8E*M56ns?lLi$P}vZYFl6)%%up!M4?Pqz%t0c@{|)m z^lJCn-nh&iz18eQR|9A9j?ZRT^EfJ<_K1&r5CQrbt#EJ>NrFn17N zi%3Aini=+z-33Juf4FNoI*B6lqMu(<{70D;LM2OFYjX4t0!bE_)q%P__0jn;8i($F z7+IwpKUXL-C454cNR|YbGdN=!jU2V^{)w~Ef$XD$Ssu@F*a=iQn%&`{^JEgrGmRlE zg%dv4ashV;*Nob$n|^78Pd$7(;PWL=b6v~QqK!V(N^jXTPXKebW`I{iA-Kd?H*_GG zrM)v*uSA=SimaIKG4k;j2R+S^>X_w5Gt7Ga`Ez240vSWKVe3;!Hh_9hJXj0X+6Z}l zJh>3qc7q{DP~CQ;h#z0y*OXi>GIxsj9j|(2SV}XrrjLMwPlwo&2vMKE0y;e=v;n2= zt8q~!xh@r~v>mkHR}tV{EoRchdB)umrV2WBD4ZoWYH2j=gN{Xca5B38MS% zi2-Fi$SfmSi6?Mb2;0Am2jP9DMWPe==_Pt}qQ>hGiB!uIGG(J+h0k7aBy?EKR_nX) z?Lb_8bWc>f@e~sc5Wu51?kGa{sUvNwi~v79-0sTM7u&AnZ6rOL=nvJb*07sX?|g%D zu2z!x*R$0Wc#Snl>mW(ttaQB?(qrW>!;aWrdGCJAd4~0Z;jW6AgTFj6E)w+Sb-xs|JoFWnY5)Bd90#O8-VWqlYH{*x>=przRUS&$j~;ku zyK(8<*N_G1kwZD<7tHTzKOVJHTlJDej{vvoOVNCbIE}T9wfm+Mr$Bq+6dWGIcC>Zh z0J}xf%2qt~1t=v^-Ts{Wj*Yh710Bqrx0?<&*WzBS8dUQS&dKqrfX+HmgOuP$DOxZq z;@-ccDVL=k$-2!Y_rmwVQ7u7ElKYzwrlmBKWHbT>Wit>iNjbSIg`9oU(u}Yr>1KZc zt5>ky^awxfV+OH&O1miVTn9{R8`7O}+O(Q7_~DO?-S~Mz!P?P)Kc6X9JN=f4e>XVs zXhfhx{mvc;QuiiJc$#CBeKn+!M+^58?!`jxv-0deWwb1uvI;8;%dgQLny4?;&|B37 z$dwcuHVF-wy2cc!jWLR(OxX;uMxa4i?3zCry$E0;eZS04PyXs3@@9A;i+IP3zpc?W zg(CWplI&@3_`T~p7j9!qmHHTE#ZM&06EdiO6~)9e;^HC|(5-8<>G|Oz5zlnV>PY*f zP+i6(<-{oJlHf(eVdbQWQmpT7MqSsNeqBnNzGrp~1%kNtDa-197Z#p#OS&o=B*#e7 zDs)&)ZDH-mapADp+=tMjK>-7Q{keq@@sKonoMd zXiRK6lu}ancQEKjrQ(j;NJfgP9u%BX=o{bY*6K(crpd=Is4R3I2jJV;5qD_fVZFTICPv`e&`42h(k ztK^fZ|A-z2lCc(@*H0Sf&RwdN5uOf7eMf3G$&66uiD0gjmBWbps4o!M)6!%l_}Ss$VthHs}dR$V$82EzboZs#=imvFZ4~x50!<;cP_?s zY5-vK1i=MK!t>a1k0iVeO#;w)R?bKvG2q^Vc{vIf6o(}_#jgO1jtPNXRG`O6)I8C? z=bcK?_#iWVc%}*VF7LYM)rrRa2rZdc%v0xpQd9uUySZUn#g3zHZK25u@OFARFp4UO zs*2-XKY6-;y@cIW(I*^kuPYoE7nC&nDneaYXXJ< z<|Q$VX)Wc?`HC3H^@|$+B|#=Fkd?86t^gK2wseBMl=WXB^C_M@sdYu9QGf~r{+R3^ z35?BRI?= zA>_uf`mm-;*TwyLSOh2No7cI2+{JB`sxx^Qf}HURx%-*2J+BW%_914WyckQ0G?_CG z&lr9td+vzq@-NAh5s6G%AuF0jZ|Xb=mM(&y(t2^}VxP&g($g;L@xLs|?)eJEzPmR$Qfek_y9A*Ye&Al+n>3y~qP|1A zuI%c~6A7iG=KKRCFCv<&C_v3{h?Ark65MQ|LoWdTb?X=fde%5y3z`t>=h#ky=asd7kq_nz9evMk`9deyy% z^`OQmw9*z3qe>WREgDcTbt_8{mokz?!Tro}(DOgQbdKB~sQT9m>d_@?=Np7FQuq&WwwnbzW(vbp zaG21HI-F8&ZIVw+PfiN;Sq=G3t_DZ1lvB4^D!BY~_teAQQ+88vl7e0;#}H^HiAlbw zF?zbJw^HvT`W63CkD&L6LOi#U6%23L1o4E5Y5_Om4k;r}Hg5b!WZkKwKMcfq!cRb9 zF(6`2!4t^{MnxP5tPpH65c92iGnN!&FdEE}4{lJkvzf&!`oXbAMcF zr7qK{zMN!hZvXhK?SoI3glP+Tz?lW2!;r_gP~N3{^);stx;P!Y_c0-WITv> ziG4)1I&vzb><{kE@hZXTcxEoA+x)79a9=Tdsb-5yopUE%nlGiuj}GmKE87wv!v`yK zfgN{X@5ZKWxuNanG!n8x0_mwqJYK8?QSD3%w;?^sva53E-SKGMKO80{yw7k<>71uh z$99D>&CVH~a;`d#M|gu+fw%X;Tp9y~Q)qsGK059j&Hy{YIMSbVI`|`DV5ln- zZ^-phb)$K)&45P%a^_c%Von~Al3!|sHXlDmaE2Fy+rqfVAW*P;B^t@upyodMt8=D? z;`WNOt+|^Ei;y0>5PbmfYr8&Du`g+i)$(@RcwS~v|A%Pla?#y1?jxw&Oc)j1L`3&9 zOjE^6a6j!9Uk@$Mg>HX;7DOn-o8I84BBMXd;^Qa=DW~-MpB048mqSMuW zqiG`{bfSdJaoOk(7WLx$Oc7~-sPMom@IKN*Y`Y_q0$PM-2b~&RLA0nR7#S+8~;|j6^d5 zEJ=)kk*gGbgsku4DJKg9iCwDpCnbZApJmkIs=~QV5S+dzj@N#6p!c43sd^PGn{i6P z$_%6{FOTDa3D2i{D@@V#1KATiJP8akNxR6XIR69qs;f0nrh-;76_S$ws4q+R%UNq+ zsmO>CH!njnY%U-NWOETLz7stnm)c0`LqzOTZc){;oZLXSb^qa9HY+>yzO;|HTsd0T zic7OtT;l5MX9W>0s#pb_BA3b+p6WllZ87SLxrQ=GT4LNFXN1SOMBU*M> z*Mw&}IRC-Sf3us_!GwXw_Qlb5g6wDFB4U+SzZ%(Iq&a6SG`s?jp~(R;wy%~liKHY< z64V5soUK{!aOWSubMp;rPuJF<*K*yqVN^iYzx1+Z0lGwLAGrWKW^lX81kpNdo`j}*ekL^NqqA(Xq zmC_dVMq^HavU@LOg*CDEA@0~NoW3~6_NaBMqUmFy*-3v#)D;b3dH*V8#fW^*C_I|I zD@)N&1@cMn+Un@QjBj6u#k6gl=C00<3T^-L;V>Q*V&R$9 z5@|aIf>VBH$;maz##UI4X(#QmEFb1DeBt)Ym)$LHO7W+rRJ_*Qi`h-Tw-(VMm2L#} zA=PL%rb=5x{|o~q+?mY*#w3GKYuKsV610gm`)YvQAwSs%%NR1v5}l<~EznC;rMV=} zRBFSxJ24`u??m6}!k~WyO+r*jCU_`@c8fl{BjC?O-N|bO)O$*L?aIq{e*snr3X2-; zaHFr%0XHfm=t`@s#uQQ@Phzfz_+Qkw=z@l}4?&4C+}_^8@F!gA+6T*H#f-!C@C@jV3#P-e^qhLxVNL73fBe?>R0?Aptwr7qf^6aQ|)iE zeQqFOFMmG)XeHS6QxGfFoEY4_Lw3JTdUVmN;o}cyA96N~smsDzxhkTT78)cnvnnNIe=sAO zRtz@ATPeGDA-SA`7ETnv#=%x#*kD+RfhR#sY@|vE?VGaXG5(dC2&r+mR;I_a2xtUyz>E4TW2kNW zVTvh`cf>3#FI5PYTLnQq%b9b>R|85f!rPZ0!jux#e!L{f_|UA#iwxsDUd@U~PX4Q& z3777hpm;9N+w=|P3*ao8#>+!ghYr_x0 zzdpBd!ojHQ(#dH6j%^PNuP-QcJLTLC+Kx>Q$BR8Q# z*Gv3G@1evG&pdgxxnYbZ@}>%VwTb^m7OPbe8nqu~c@uA|SFOWqtpcWPH*A4+P~$IB z1d<^Dw?4c))YdfdLp*PIqNe8Nx=$U1Dpkvzjx;PW=l1@g%#_aS1`&yy>eaJQeXC2p zrIB#oBJZi{zC~K%$CJ~*{Es4A9D^0Y0l{bBdvj5A2ozPE!(vF&5sZHdC<;N~Kw3?P zG8oQL?Oj!n_{b<-H)8?EBP3<>Fu}}!l>Y+)0_fNeXbbqydt68F`OIVRa7_FwetmRb zRBNTi$|0!250^USQQ2)kiNT}x$2dx|4IPsi$!^PY57QJyV^UYoq>yJhI<_q(WZg7u zZ`0?`P~k>0Xr{(91ch>oEcma`v5^qjq+FyavKtl z+&<@RrmlVFVEz+m@}xcPmhlP23o5lLD1bKU3pv4}+8DAo_Eu`a2xbpahm8mA>CWBf za;4r?YTbqIo`%abFZ1Znn0DZ--mg(b*5Y@d`I-U2n3a||?meMPWfNK{H~Z}D#_yM? z#pNmmLBcb(B-487;^4!w7o`-~Lj3gi)4TGa>Tr@Mv{q5BzP%ca*-ysya&qlA2V7iF zCS^-SFSx}(D+Y&t%t+O|zH@p%SPO^V>$fbz6n^d;&tX0T5;u zV1a&81%;i_+-FHnCdl$2B{Del(oY;_gX%$5^}X3{b0CR40K>Xd%gFM~QI<6UW*J%* zu}d07p%YCgYOn}f2-8?KQauFA9ihAMtX&sYos&7)_qI<^-kwE&!fwio;ZaqG0EAH| zmz>14q9}W~-czZ$b0?a(PN5C*1RGvdb9Bw(k93uo)hj|QD2-q*mP&&1%(TlZ|OyWA!X|{~(5WcPnQ1*Q-32m8sBz z)h)j7$#8ao(Rf_uLwy7VO9$)FgNj^XiCcQO8?YV!VgHDDO76jM^~YLZNvyyM|K`-l zo-TtmQp6_{o$B=@DGE7ShzZ`OTFiMwsbTv(yD1*hm`PB?J~8!CTjgK5L&HYUUk=)Q zds=Qs9U%&L6*}AmEWWG<{VX&g|1}T#8$mu((n34`*w)yhR@ypi#Ge^%22CU`;6cTk ziBZb##R-M)NpNqQLRADiTac{_ksZ|))!ql;qp0kQpW(~ixACYburuY806`=q3;Q;r z)KF}k;OWssd-ZQ(nnQU4v!X~l&aR z$s;p+>~38&sdmoqZp1l0gqFrDkN9_)_5`9-h|X(3kwt5H??WqxJ%GoUSY@ix8o$5SHQb;C?yGjWcjDl4*HTB#R(tZ{^Yw#BUNGi( zB`1L{Y5_Fdhne1g{$2aZ@Rt7SaUI(yE_j`gXzOUdri{x@1;*V#!4k4%OwHEz6~(3T{-e?6rA|3(v%oxq)?C;jv0kMl#rOM7!tw3bwrLckYj=1p zF#pUk(!IqSM4TdUQJC+ijc<%?;0Hs?tbc$3qDh^@jmEyt7HYPBjPHLE1$Cr%`5lY@ zzPzLU-3@~A&e$~x%^2`)$*=Nm>|eSZFnXV9=gIb-x4fZFfiD_uB%3V5W~hp3pICm3)3C8KWtyE0ijz|+2ZTNlTAob&UlwdLvRs?FYYqZJ?& z1A6Eg$jpRDi|~=Vlcef z3a3FZ3h~I(NcbZGl?wfdnkt_1Y7N^u-xheMW>orYyXg#3>{o?)>h8dTESOFFIof`rVg? zb73MHF5W}Qn-OQqT%h?(MjDmCuWN8MN|7!|wE;j3%SGhzE?^Skf?1`YAF|e zXZk9BD#6f|s2D1^YPA=#zEpixeFAy+khX@-$rKZp)r0WM8(QEjesv(YE?%E#7sR?d zIR#Xy5Z}k;L9&l^R+B?wn2X9oY?+IKiAvNL87*nF_7d#uW1E9b{ijZ}i{i_IxK#oW zc!M3A8+Ml6mr2_ieDBJ@-jsLL2998rJp!?cK3A0?HkAWpBwmQRBnl|UsyJbX0hH#m zws>Ftl5U;5iZKWi$vR&>;Nc9iz(5nLP)~&MtT8L1 zNet1rgN8Ct>2?4A%eDW@xxq01=jFtV4efHy{xEpoy5K)Y+9a=AYG9kQ%*7c)RS~RB zuJ>SdclKy=srd^v50tt^>D$5!$)b@beCL8D9p2@19VXOrv`wb_rrQDy;1q{x zy-QDk)7o&r@$#bvs2rUeOY?co0!~MP1Ql!w>!z3h5%fXkmMC;oXxr zDzd+tweWg5+!df17@fgy>ib9DZI;RIV!-}2?t`zavSH35VK%PIT^@_v1=BuXe-xVWa1_d`c@@3&i2I4FQkCLyhe~y*QIC0#MPs$${YMV@;w_O@+10s5 zG;eF;Fqv_tw`65rV?qM&Yaa^=OXgs&;@@`CiUGK;lP9p%O5H z!nb4am{H;ViPphFX<`z5aFEw4t3hIkNX!VAz;i!Tohc)i;8Rq88eKEfZQ))=lmS=w~?L3i*)b*ZWnXV~4~ zzMO3buE&=$SoOp88l%y+FJ?2UU6N4WhrDyaO49F}7Hu<%r7A3_!kiy5#y6whRj1)4OIzs4o1)}ac);vWejM(v?XOCS5?xR4^KC0zYcf*i>d!tRN(~9z_brJ zHO$2Hf8`AJoz9Pd#+IKnIkW}&0?36CDUe6Y!QVxdg1^$Xsm5%P_zPT7?UXj{Nf54& zIfGB+(d1x#QfBBV8U}DJoIK6(ErNN{Sqd}1EqLi5HO#y(N;G4IOIYSdCVPjOBz>`K zHQb%MWwt;QFBXIg`E6m}{&`#q8JXDHLhhCNapAgn$b_;ppu)Rj=s9-t)az5xAW3Je zU~3Ztv=v1GVm$2M&(llR&&=L}FpW@Q2hRQj;NQ)VJcjB|X1M=cUYU@{$_mJ|;xZVP zu1u5k4L6=N9$i0ImAIPO$%yXed|Vyq55$_^(!WM{N;$u;pm%jfj=TIkI$jA|A!IJ> z`&(Lv)9L89#FavR>|QSw6-OtRpU=UGI2YuFcIYJy@ttCRO{58X$G^dY7$v83E)-KM zan?C5=t{LGEQtKoxy-vxZ_bG~Ko%Ic`rQCN+8VQ7Der4QHT(n@Lth%IV&AmUg>t9B zx!*qk{OMKx`9ABbiTYe>Zi+xotJziLQ^k^1zeB~+8UtOt7HBSmpU%{r4 z|9$Sd9_&&m{R-w^ePtH2f0lV%xYF!5fglBQbW-h!@!FAUkKxK5d#c)Vhc$YeRLZvt ziXnffCecrQ{}{Mpa;3t78#whG@F%uYC{R@M>V~{)X#ZWiYcr#J$2=mz7$?{+<$qjxy~_A*q#BL8BC>ZWtuHc^?*!YP%se| zJIqE|WCiv?37tAw&?vV0_bJCKPW~P~u>5?a|V;5tn@)HI;xb6p(>*O9(jJ^F1^e6r1BzWyB{DpUcH z_Gnz<^Yhb>vq&PRPpE;UnPPJF-InXhI0eGL`oCqp2E6cjr(@8KkV-ZY1={P`@y|!s za1_g}egPuABE$&lsvYa=y3Az!K1qDVpSkaRRKNbZBU2ab&s4<`+x+6_=#+OeEnxVW zgMnVw4A?)_si^JWbu4>GkDcAaQ#nT?16Kpp#-oX2dUx!JS5=;DqR%>sDwLmOS*+Gf zrR9LC`;o`1xLL6+X~2?nlE$%SGF;?}9W>jZyzaYu>vN(P-!JbozNzA9*WkaWROoe# z4CILE#+bSekU8cF-VcKi5PD7uEymiCN#b2R?Ss^!)9r=gw4;c6(~pZwhsz?H($+K- z9XVNO7=uh57m8e)vZLGYO!R66HM%Fi(h2d}r7)MD+_G*4z{g>iDp1se3O*EcB(P7ZThV^Hj48~5|uB?9lCVm#Tr!@wL=b*ehXJVc-! zq5yGGZc9>O1&QJlNh;2?h&JV2BeZZiW!sS?RH@RdPC_88DEGxS9*^ckMs0!)*G+f< znKe;rvk1xdwj$kWN*)2%@oY*F**VD026cl=RU7RN3`-m`EjVLfkEkXoqWhZysaa8Y zF(kkSme>-xfOA-Q``h!cq&>k=rujk}K|%V5RbQ*3ry@6e|B;HzUpn+1ebs9vTS5*N zT7O)$<)oJLH7}Iox!spiNO4!WRrVI@s|t3DiDARPW_>>wsNlozT`2^kqA4`HZ-O*Evu~ zfJ#=t;MN&EPS}d?QUO3?$^R`Bs+e!nC2OLg+7u1cwx!b>CXh}vlLE>4=i4byZg~U= zr?0JlJ*lHD+cXu2gz}sH71$Om(Zf-3ouZ}_Albpm-YNjIiss5oy^kJN2O~)-$}*Fv z>DQh^b(#uOpCK?BAob?YFq!a+KZf|^d?TKNn%1DXKOv`hvjPmLLiK391#3K?xiXdX zzu^@YpckreN{A%sDlgerTU8xFDk+0BeSODITrfFT5->|(Xu$nalWzwvVY<~p#m|-H z!NA59pUg!|V3sCITDgQ;9!mv-c}{uMqHI)@6xXw*SL-$ji?NoV)gUonVk{XfrFKb7 zz3gpnw_aa8fF$~jdOIfa9J!&gR;$VhUHCAVF-BwWP&!ToFDt;>NPlvk2VI05@YH8w zpzbPaO>ZHR_Q@=4#Qzbn{EA%qxLqQZM(Lo}9wTKn2MB*Z1H4nmrnDYB3QTijL* zl~W+Og<2UcXc6McThOkbChJN~9;SI$ofbFRCppGvp)Ue;=1v8jEVY*)m$iQ{O8L+{ zXyfDhdUkE{j%xOz!!&{wBiGkW-jQH};H`2f^Eq2#A6W4vO*`F6^E)kFH}fb)Us~FV zB_J>?s89M`V(3LQaK`t?0!QG?9u}_CJZ})YW6H<&prCIZ7IG#mq8A_mhje*v&R>uE zbWRT2sH>^EgK<{RH1M_91)j+A=Iz^{9I64Pzcn1_k9CGuI_-|tkA8YRvmUXIo3e$a z#$wHbhN)GKAY{w&CScz_uE>%x!K{U^%UgLj&!2U1cJDtvm!hBjQvLxz_ID=lC|_S> z?Wst*vClTiuVwDu8+NHszR(1GdDqSR++kS!A?XK0vaB`ei_q_)5Ym1`mD5)P3c1@l z*SEADJ7wK_<7>~>sulg2N<+KVPd|;8mVU=NTnn+x84Z5{7IP2xUi_u?7^tU1x8-v! z_3CMkyokQW8tj1tB|3`|Kp(9#h3_q9O{g}_zJ7MSXNuX-fe{tgtaQvAw0e@2@cY$$ zecBEG4xM6#v|i-y)O)B>KBiMecs2QWk>*D?uT^D#2H`jS!Cr-YUl-14OeSQzYJ94k z2ssJ5QVCi{#UdTf$9-q9;eQAu$-+;Oob^CR@mNE_S-R0ehl#XH(*vV7eF%>^U$%HN z0I-Au0D{C7P6G{pvCiM1Vana3%!|aBW2K=j*6s|Rs-7UZPX*l=0*=qpwy=1+ujk1hYcf`v@ z`Fzso2A=m*jBIa$s!Wo%o$_!(SaUZ$4^5HMi|PJy?9TK-QycVa_s6dz!uZdKkouC| zP+bK=`5ga7IeLfDv!sbMb2wJu04vPPnb(WTD@hu3-|yeLqQ&-sez(AAmp{ z`w{Iv>rwwLdRN|&?+s3))i75=y8%Qh@5O9WTf;;a?123dy$U#hD9d6zIQM$|yCHCl zio90BIx$h4_&RxAfiew$_qT2+_hVka2Ck{Lo-+$_npTbvNLF6N_8*{`=)7aMXT^P& z%sa?9YRlHkuw|D#bsG})(ajp~ufbvqcne*8Twx-DJfWv7orA_D^0Aez;=Jb zDo0@_ptQKUhQY&QOHoeXxLZ!jZqqglq`uZ1VRy}nbYl5rG`l<@&!gdV&eF0sXJ@kp zmt~VKkBG#JjZ8m}&NX94ygxgrolHBG3WKPb)%Mw-oM$_!<=w_cG8RU zDJa*jGMpG6R3H_iT6jub0vq?Do&~VKT)Vll1qmLY;cngm%Ne6C(Q{?Eko9+6ge)bZ z%O=Akc79U;r9QNMjSx>m6$-y{hr&IhcT(lK<|6GBcK+(dIZ^PakE7zyx}U{l0Y4Qd zV8HB4IRmBDBgyK8rdcM+Jy>L=wig8mceUvOWJ~EJzY->s2kP*VK#F?rQ`ET5-b85> zO`2p6QLtAR8sbKz&AhC__-0A=e7{Fiqt#2q=-{uFRHdgn|0*-@a|aP3!ChB1Yi#a( z&(n%3Umji4slY%5+G0=~6GI^1cQcxy{$xc-|6AuPN;ktzhq{H?OQ<=gV)>%R9C{M3 z=76sVwI^1`sYZB0KS3;bFhQiu!v{>BRqjJa+28D*J-)PaaXVBMkW?`pw6g-7Ntx2cL z!*=`&!*`Y@G zYqK=rfF9W$(kdgS%!Q{h?@)Q@9z4m6at5vQyv zIJ{UGm$ep_S5;WV+)#Q>ko?gn`uTAnBbJ+~>>BI30DWhyOatb#Bo^U85xPjq?}Exp z(F!I-$%(aofm6u%=Fkh+T?s@zAh8Q~VD6zD6bA0SlS{@V<<6kQXy0G|) zT_zhse=sduHOaFkNzD|N*^wS3gzc=o>1uVv45%#B?6-GxWUbSxA2)-U^17IhpKAcP;i{Y^;=YWJXJVE>cC+sO~~`s^3`WChB6u--;>1X*l5x2}&=QhDqLFmA_pnUAcTe2-|Ab{RkKt9&Z# z1l&(a)6QpH-Mu{MKJ(sm5PEr;f9Bz$PZ^`nO0C76J%49^^?nSwJ~C9+9m!+qxu%%3 z$fV5;HC(e)HyjIr9UicR;v6YXy;Lhc1hy5YTd4cPj_&ZT z#AP&m`JzSWz>*RUpW}iNB*(9dW5+-HMarFE12Wn;z>7{QAWMP{#zm&y$N9~tDy>uY zx9OT_=g(l`3eoQEvv{Fv(2fM5ZF};9_B=Z{awcSeI&byc>J(Y+-Duy-ai6}v+_CC8 z+dlvT_chYsKR}d$^*~)rv66j_GD3I8F6%c#*{pLXTGrHcIyMgE2nN+0mh-QSx*%xm z_{%$6>F2dXe;t;LdygKpV#IOD*1~zSox7btv@W~QUSmxR4{A!{ZohGk>$y^0!-|Af zX(PV}^4WAVwRbOCEmV7mx%EHu$t&rW(jd32qUgkz#uJNhy0-Tv+Qc)nG=P2p+7^_daxLF`vV|jn!Dq+{s@HhsNGGu{w z?Q?*LN>e!7MGYIR-fk#8j5>}ajJ6~IzzKj&@tU>OJM89tCA=yd`4jo@j&&U#xyCz@ zzbV7(Z5S)mOT$j>hB-#$+Sdn9sDoZT9)$bOMGpwDd}_n;83rGgg0|L7tKS z3**mZqK~AVZZx>)RkCy4daTgJL=c@AYm)#2Y7)t`er3O%|H$%i-r;(U)w`5W_>Fkr zj1sSj|GBwH=0oy^?V;rOF@Bm!Q_}>Ur6kuF9Zw=qE6Q0j8G7l}-WT!&9>ey6h#s+q zDQSNB8yW>NsO*YwmVR(HzDBcu$X;g#k>qxv+_jtu& zuBhTTUPbInY>t0+*xXFnyExU-C%D8jN?hZk^yQI4%Hv}AcKga*`VlUa)+0K67xUSDNwA&Iq;v0k% z-~zUePf<9GQdl-mxilOhOPR3x57hl5p5PPvY3Q+p0ud{+B^7oyq~O5vbW}XXhz(K) z%@@3%QMCWn(bdI|<%r99TZr=xja5DR&mOuXk z9kv~x4+G{ca|MZWp&*{&pgrLK<`UzbfS5nnH7@d^O&NZ-9$2LR#*ohJ8*lest_>)Z zKbfk>fs(v)bseV_##V-{`pEtR?WKt0AA!BEkbL(h46K%3%qv9f$<#_Or;Oxm*RGK@f7-?tg&idZTT%*^W7Be^Wd;Ly%#0|Ksgb(aJaCg5kOdd1}E3G zQSbKe#@_q2!{0feM!=p<78B;frG)uiewGm=u{K9hUIEWBXD;C+1Aia`0z+ppSRXEg z_J1Tul2;QS*tnhfRH3~V6KFYziQ2&CsjOU1s#FsV9R#G(Iz!B>rx7*V2ZL0B$TVy^QlUBr_1I=?T zYrrXel2lNVfg>4Jlvp22t zZ;9y!si?amKW?1=64B5~rjx&Vz7VR~U~NmUmk!mw#| z9N0!5&!z#zWQTi$n0c^}lRydO=LJ`rC!Bai*B6ObEv?wLQidkD+roVL z-iV=Urwa;k5A2Yu2LY1t|~D1jk;*5b6(}d&2I^;1nneCy?233>jzb@$>}9<=!9M31$ckI_gqza zo8K@IjU9eM(?42J8*!n<$Va_DeP5=F4ueFqtOlBCQlbd4>iTBEB|q)gG~;yc{zx#E zuXjaUtG8!sp>3q(T`k4>yS?lWHN_BbG1Z7FJQ|)|hzU%LKbthX_2>FuJlo83v#E2g zw^fgl*MW8;(?A@3k9E~U)yl`VD5PU;d^&{twf0!j#;c{c!<{PqqIZqT@th7XYt&MvvVwp{q;IWn5>2rLFRlrLTixvVSSkjid;y{FRDVhMcFm4#`4wuXu^B8~e_W z-hPV)rKiJPnwdR@J}*#|1{?VIXVUhtdV5O=sUb$3WRlP72YWMAo&~kV^R{N(IwpJN z??nC9t@G6V`sf}uDX<_EDmO|#Zs0eF_z%BCNHwv} zIR}v5l`IQbg0@K7a2-?IRoxTBg+fclC-asE3AJ9-Nk;&1x1StWr6`CKy#ScJ zu)Y^MaQC@RFW!PDZ~=AfpPGOoHXC@%qJ%t~ZhgU;~_N<3h zG0)}$JG6iG2v)A?Z)9ryvC7!^7|+j%+GPbt8^S$-4;6(lJXk>Mz){b#ssvEDFN?LU z|AAPy?aUS`8WV%I3x2C#e?9uqfax-3``FCFxTyR*CbIPHY4ceyctEL_IB1?fLyYW~ zStoEfHk9eHwQkG2To`ug7nn(URKu9zBhu-8^VPTTe;!CkBo*ADsK?thi#1JkYu3ar#~Q>UY0_9zVK<_L$7T>J&6T>Vn81^b zYw~%-;OF-IV#8W%irxkdsh746NRCEGHRE$CDdQH`)aSo11nxtcGyBdFez*KjY~N3c zEG~{F-Tu&GR!k$C8?o&kTkAL+l?@0tPyL^>fMd77I-$< zzs}|<4W4eK6e6*Rk1jdw=4)3U6$zps#?HLO+Sk2mfwb&Zd@Tl#g}->Zm!GLC-VRy2 zT08nQW{mC==QCJ*j=R(#bmtt^|Msl=FacbnJRjTYptXFjY)4(8P(Niu z)nX|4jx;Qm;}3DR(m-T9^44|TZTWwoscM}0iJu<)0p5o{gAbfz?~U=G722IEZLb>U zqTBZq2&E1n5FCuQ7J`-o;hjx<3E(l~&1lg#v^9;w&r5fOYIrgG54c@XIekR|#b2uf$vRS3;hY0ou|lIm{k zX>033Fiq9y_KCDWl@?L*=?**A8`d_q#Zvx88J~VP_JW(cZ~vn4S{FdlY^dN`4h`HL z=1(M>BK~T)*Z-2&_^f2eqz#`mRtTS%eQ{s$hWs16aWS-u+GBs{Kg7cUO;nGZTtumh zVHvS$$UaOe*kMs{r42x5F>IU=MCFh>Gd0nzEH|}M)eur6r%|yjfb9QIyOB54QB1pHXSeX9dL>iaNYh*#JP}ZM6iuhgPX;#{ya+usJs@5 zknfXTS*t<6>tgAWL28=-|}%gHmuUQkwzT;L^mv$ zi5eD1GI|k(j79gbR4E~h78RcB3TaQbm&JWx#o=)n*Mrqcqs4(4ybjLsZp2}NZCXUXCD%JybA-k!I4!5iFkI6 zHa`%vQSz@Jj=ne5OMqZfriUZ6)bz8{wQwZZ51PQiUx9KsUL5Q0ifwg7Oa#K-QRzF9 z68N4aPz*m7ErN4AHD!Iv+ZhywxDb~Y4A^SHvVeSI+cvZZ=5X6sWw|8A>NY?DPuFE@ zs?}HFL@Jp`*M&OQq5>V+I1{4{^5G4QX73A}CSN`(ShnsEnI=cC?B_E3>ZeE<9auqX zQaUeP(!?7AJ~$^`+K30$_1FjCYw7u{VsE?OjqKdRd2rKIH};^@aD3V2QdX{{ubue) zc&{Ma+)Fi!9{+(PGU|9I3jAlZpvm}3d(W_e(r^cAHuVXD(Uoe{rXOE+oT5b3PukDBtyOYf5e_IRcl%DS`G+Rt?BKLvZ3W4q`x@ zIh=|J!g!#Wo_TYmHnF$`6Dt1zw+h6nE!u2>I-D&{V(I&`GeYk7ZMLdxy3|ea9P$%? zYfwXl&^4Aoa6r8FG@eDC8fdm=`5@~LCTw2n?qYPdyx@LIYK{%+^|POOK^-R+N2DfT zYR{=n$@LNxXFRkaf3-7ZVoAu_%h8VYrZu5Btt$NJzV%2=N;>5MTi`%%W}pEQ#q+9? zVpQYc5Yqgi1{Ba%8KFs^KdR1ljo+0VXG?YLjJh!U)o~H!P8%Wlp_tK-dIM4DcNNOm zNr)=1wvvP>*TBeeN|;o{V+JH*nnsgkP#iz~p{dirBYKL;VnlJAmMkHV5oQbfO1o62 zL%pT&O!c-*VN#(DN|B!~%$V%rix{#+lO*3oM)8&6m+S#0^M0w7r_R~M3&T#i{7g@r z8@$iCvYw7UAx)AE#`F+%I=YQCj@vE57=8&{$#6HOmg}dk&@$!Zq>$Icm>Mf$dXxH3 z3IlUew%2UgWpbZ20ggO+1=ZgQ%(Pgzl~9Qjz_e3%5-xEeDZn!79o>#0ZE5nkN$}+BkJ*`8h?t&UYC)FD1Z020UJnci z{=QMav@}g9wGNjbpGuBF6rQAjZsxu5L~>X#SA^!JD*QoDa1okE(U}YOgHzXG8;&pu zhLcMhhP${?q+|90{e9A4{Vqv{9fwtKgCRXdJ^1&1jyGKRz+J^-$R|>Gn6clgx@w>~ zs=&h$7&kI9rz;8Zc;z0l1T5OPyBYfBn-B%i-d!QP%#XOGV~JcB)YP?kQ_=(#kYmq0 z;Yaf_#(^q$gqFm@abA3mg-ZeV#S+&@o?`|Yyyr#&Go&&lhO6Q)JZ~>R_itcg)pQjw z#DSfJ!+A~@?v!{!hE7o-@WPy6w@d7#oPD+mD7pcU0RAQ3nc~X&}ol2+2 zCmzSTM4vTJ9X>L=AyTHf)_=}EA&!}4#R+ZRc;Nx(xC z`>mC&d8<3pMh={6wlqc&A z`S@Fkh*F`g829|Ohhwl@WGo$wUj%;s@`%lHZmpTa_sKX|JlRlZ)s%X=`a@F^Ce5S)WHL!2@m`v0_q)#F z-y*%>+>GcL(Y_|W2%Fl`5t3lMK79B7DPhc8VPCf4<|fje)g^FB)2^BtJfPWUPe~ZS zTrgCC5rAE@3$^>B(fS5}jSLYvJ`)8jCdt5`@0UysF{OfV;7#~el0RJ!Sp0cAZ%X;M)(TH|rDE;~hZpfjy#!$(i0|{78q*{uyiA%LR#VwRIu6 z{5)gJnS)W67FUaM++`N!iwV*p#oW`qMJn^oCxJdg#0UK7+T#InLG&BjeCt2SQ9=1d zrp^Pz$?U3~CyDtuJ8REPLG*Xb>BY@Z;@a4Q?`ypC=BU9*5qt-t-eJ?Pp5R~`jag56 z?Posoc=+{L7ZdalS+68C>pd(eqxe-B)|KSJ1sl@flxW}34b}|J#uSSJ>3Ih_Y;Hw# z622CqUN}3rf!eHGnj~Q85Q`j|c|cH^lNr+^T@B4um;cJ*TWd7s%{7Wb4L3e~1)mys zn5yTY$BzGyIN%IrO6UetKKvU)4E169kg9b3@`%iJYOC5+pv7SY6lwjN?~9OduI6$# z1y13P0DYCNA*8;;xx~j?u5Y!?|NFl;Yw&YsxLd2+F&@Thu!ggLk4Y3EkQ?0hhLQ+5 zJ9g59@fEWQ z3h*^D3J!OklIIm?6*pH+5W>2=0Q7oNkg;z(vGK-=&0*(+1WLW z_6CR*$oVS|Yu}sM|LJ;bOYtpCi1qdLV38@4 z#NP$)rHT`^VnuV!no)c*QTaHKjb*EY;{R-&S#9XCg z>^|rifmP4^Z@c1X!8VS}JG?7wFEj_`DQLRjhi-2?ZY5Vn5L*oa?6K0dWGm91)5o2{ zqDRVa!Pvc+(9?Z_!VDuGa7NR9c3Fm=iKF7oLBeq{mBP`u?1UD9aW)z{SKeOSYv40l zP`@}%TVm`R9%MD{4s93F$9U*Rp24?7ZN$a5a~qTt+5{x2O_AkTUzd;~$2h}^OU3wY z`$T9uB({GS$zve+(3!xNf+%!9$467WTmHC9FO4S*xuw?%Ci6I6|C=QUr=QSOum80Q zMA^bHvipq38~pk{BXQl~KhP*wz>NEKmm4K9V+-5gbeyw5QPFomDW}eARP%oWvu?o-Q&>h+5waqpQhd09p-Bp`ploeA!DkFn4Md6`rqkuCdjD5Neet$e37)82|AzL){2oRPaoh#OR_= z+`kV?{WAP}CykT*cW!SQ(^kJY87OER!WLPnpi#xg^y529|6&D zluoTho6lo0u+wU#xIpYHpC1W-qhJM!*PbI19+vv|$BJ)y`3eH}Wq%|EO>>Mng-;tQ z7DEFfA1l^g98ug$^}4>@j`j+{t+%&*5MpArlsV*SM z(n`aLUnI@37ZCo0n@2_aJa48d^O4=mY|S-dFLkl3Q0l(?2TL5+ zKLt(%9Z1)oj_Qg|PJ}{64+Qk{<%EPpMShDOWk`4+@N(A-2ian347~0#``a7}O@;{S zX+>iC z3K2$`m@t4XBC3illP&k78tkG~m|fmt+j1n)H&n6b#3QBpQf#yeI>7uaCFuToN8XwCf-glG!QnH>bft$0Z#vWasb|)4FXCBy!)yzYlaNpa>$virq!% zao!t=9JT-MEZ5c2DwN_B#!?>lny>Sym&k6+HiW=h28St8iuA|fe^v9VD1MP!svbfb|bpKOirD8T2M_6wWwEjYQz&kTkU z#_t-wEdYAUijY1#QOv=iQLn!f?&XhBs>^qqB^V4x;=jh zn_c2b#{uD2I%weG4Gz@6zOtcwfmirD=XK#+Cbu-eUvPh+t(R-6@L}1Xi+Q8nRkhsz zfPO*2*b^qsXCFv1XxK&bDYPqtmli#V%jcwb6}k?*%|d7@(ujW_RClVxjso>p8*yoC ziv3AwtG}1YWF{K5wWsnioUEl4wv$O&*y^TuBhsa-_cI+-cv!jK-o@^X34%A@c5XWo zpWdTJ{E>xJh@~5wyM-HplB?1xA}l)r?F{gK#;tTj6*}!v*PJv z@okMOkrhpo`T6&%_!MH_SbezM}Fmt4OXCvXf|y`Vy+!S)BL(M}FM(g(R$ z;u1Z5t*KfoB2{b@XucL0*kJ-ov{yhlR!ZesA3HzdOIX9^E|ghr#9lngxOujvxW2;G zvL4!Qh}@^P2`j2oD1$P_o%bxzweB(HBZ*ILTx`r z_0VaL*?wqZCEgTxuDR#N5u_=wn*(=5dh8-Vt>wWF@s1ColnO18gOi)n*0+V2b0_P`UtsjjxRBaEH*+#PkEh|sYv{`VV(Ab)P^PXeW)u0Zu8b_4} zy|}#Aypcn19B5TAo&$ zGXYnS4|}T#L6~-CL!&#mzk0ZZXL#QA7TGNfxh-($qjMcvOrm|5Bjrk(l%x(7U+7IN zwB4attmB!XAKX-2ayWdqxxOydIE0EB6J3Y^*9eDCUsa}W?`OG_U9}-uX}g;A^XE+2 zvJ$K2@VWM(hlw-`tR%cim}G^ULok-gV5iIxIZsq5cEa1QVrG= zzI2yD&Ng3p>hw@HY^xn@T6C1S@!!vaz}A7U$NgD0fTR<0AK6!WhT_SeUbKErz3u#q zj&no6x?cvo>I44-3gCKpJuy`NC2$0nh(*UFKMBIN>qf#G0zu zROD8eRMgAILqq+ChfR5~%#q=w zWBurnMiX&-%Lom^TsAgY^XgS36KC-Zp1K-G8Ei`4zn`ZPvIQ*|rL~0^O$n474V(8V zSs3Q(H;>h_WZJ%n%z6tJ>S{xL`{B!4zoh=@W?_i&)g6fx*Qtu`YdkdZqs>E#%nYd> z#_*X7B||us0GqdgqJatCic1B#YGF|x|T*hWl5P-<6+K&IxOw*dk*PQ6YcR6 zA(@|dc_W8XIx%9o2M)W$NeERMp6Pq6%#~Vcs&ww1lEkA&!#>lEP&n3X&4@y5zJ+ct z#6Vk{QY+*x^H7PAQZ^v0rL#xQj5|blxoJE>UIV4NEy8u*cj7{&6rL>R^CD=5dQaoN z)CmEOakm|p;$Et5w}VPsG0j`U=uBM(U$$6dK|x}svmd!+Uh~Ha^Yq2S8qj`u10*xz z7n`QZv)+?F#YphM_FKA)^Rdz1s|?Pr$U+)3qJNAIP8scjLBYG%^WYw)bhgTESE9e= zo}%%g+BeX4=fT=vUp^k7`z6coMGeO_+g!=u%}J=s9?ApoTy$G+e@)jM43MyQ8@pT` zR}8$k%YE5Pwb-2+d|KMTn4;GM`5CO*IQcyS=TUk1&I{@W_fd4V`N`4uxn0x zM{_02;gB_@L6tL&47KH@@UNS;Dh+b}cdoa$jDjoyGG!){nBqc`Bq2j^Cl@!$3Wv0e z&ZZg~9L%EG>d!5^BKc`j^a)yV;FzCs(mH45@E9IAQnetEHPtDlK{~BIqSi^Vy$noP zK-Vc!@}ssdTd7nb#@d;jlfR!D!V_~{?F>yLE0O0xof=iy`%r~$0A`7@xe~wjWf*re zVfokVq5EGM8XQu#TXFNXMdrcw#n{da(3RSU_7l%&B^+%Eppcd0zOY8I+*RE`zkT+U#n6#OMl6sF z1K{OCBnFICaA3lRzfcxeu=qnRBq3{II7g>R7#gm~F_<*=V3_^ot1U^3Bep!gt*68!hfHm?oF85@#@hV~H?k z4jVV%a39|d*&l=wczkF6`(^oxXPa-<|M|HqWoKt)g+4>S)C#pSx-i2`9t2XLg;C?N zRO3Swve4BAXO3Zjrw)R@_87IBYkYyzZT)31H*?zqfuH!0*@xtGm9-rncZ%3&`X9&+ zSW+=Wh4$G6vTu&&!}Kl&upQJ?8r^#;ArmMIf6>=2lgkD48@F5?8%e_WUR%vCxgc@e zWQ(AXj*hw@VEH=0BX0A26IX!f*z@5){=<6+WuzHF(m8)Ftli4D4ttr9vV3?TPQ=Fn z#c?|?8!WQ=AO)P#YQNub^H)cLNo3kvYG{UBt@jP}12!4O)JB|1iM|gdmGjVl*v@O6 zq9;66-PnB?g+!)4yW5E|g0IV5kDy4JnTiWMR|fce;78jB`=i_RMvVhiaV{J>rsMsj zVtD1xwrj5$KIgpj^y4q~Gr`ta8~lLPesyT-1CEc|?Yi?Y=XcGIge+{c+1ySPpTl9= zGvwBewS?*=$U@I?Wb`j>SPOc-OmYQQGg@vyVZxptiurHX!v87VflE zE*6rlF7~!<5ORCUimmxt|L(MQz4jNqqxWO*mU>M3H0kn(yraPboFbqdf!78Res8+E z@5hNzY))-8l{a_-q~^D#9vaB(sdI2udsSchk7WYNjEnxQG8I~}poWz13cFgMYAVkz zB`jZRU;~>P*^BVx{x@w1UFMZ`(Tg_lamAe#V8#*hlZbyU{KeWjQMaQdxB3-d6OF>LN;?KrYld26+FPX^wWHT83I!f%Gcq+PVi*KOdbi^(GVewYo(aoC; znnS|q0RKwc6~G3W7fsXDCj6CSjARU_B?&zNo6tRp%RQt76+X1(Z^=Ull&0t(Dg)JX z4#6JWukk=&&$4d%$56E!^&>wc9Me{ud&pFm61DJ`&=sg4cw=hsX0QPTO5Z;~OG1!~ zkowthldXAcvJ$=}Yk=B7ZkNVQ!P-oLHuYc^tU9-$+-I{JlEHnx`pxz516JqaeUkpi zlyu^-qxuI2B?_)fJf5bxR#02Xb3lMpL4QFDWelswn%MRMC4mFDKDC0awgbyo^u*k! zMDOvBNTO(}h_eilnN;&F@ew6hRZ5KSMIXBTEcGST^|ia^JCclj;?}mI-bk}5vtG&T z>QlkxJw~QO|I4k?>%vOU+=ByDq7Nnr0V>>|@3!^XmQT{@BtiPU=yKfR78j>Boya=v zW)kw3!n+2g3kibC(y|*3kkC?u|LDQ?zE};CH#jEwaGE6#hcCo2=KvEQ!-hAM&7jWnq8K zVVcm8Fx@C_{lU>EQgxv+{CT4|nTRV4Q?ghNyU2W&S&<-TTFH@&%$iq2DKbF7tQw0- zYl=Us2ofd~W7fr0XyjL*DSurp_)-;V_Cobyt3lzml>@TD_~2NhQ4CL}8B$hgg1|;^ z^KUMtVS#>9?B6+mD4xziU!1HbFn*-5_&&kW4N>$~t zP)bcugP-%YSbCT$sQJ6deq0YIPV2y;>BHwNEzZ6+vI5c{C_m zTS+3w&x%9Pf7OY4f3|)u;WcRZT`AQKJR71XdGXXXb3*wp^M|ULFK3+k-nY(q36q?X zQwl~hcHA;#C*hsUf+!dRBPyGcG)Relaq~*L((q9XqLqJw)NfS#@f?xbs13f1-V9MH zY72)R;yDnD-Bou|w2sy@6;k?fe)60kQ{%gg!tqy6{rw3P#gL(H za@wTISzCoWQ1*NL>T)PaI)aTqZ_`xy50sb)MX3|QWbVVxe$Y?uw?r@4QE6lT^5Y8L z;*{vx?*4z7>NA9##N^X%Cf4ZwBpH<{up#>dtF# z;u~`Ng{v>(*04}uU864YFx%KvpwX)B<^N7MWh4RGfe3S3Vv@ZDD^4Q@dYds4do2r4 z;!E5UrP?KRW@@-c`vy;t6X3KDH$(j&D3s$P1`V^ImSY9wJmo+E%1(eNly?sc`J8@7 zS!w7AAu`>QH{VN5)-53C8;1z)JZ(-dUj;Gu%ZAhLzM zji5Qxvh9kT171}wSwi0osis0MLDn^5`re|~n7+L-G49vGH{stqAV_;u;kv;Efw#Cx zC-}8={K6A2DEQ`62PS-wmV08l!tU`MF%|X`k8i_Z;wH@l2$Fd@S8eC_P=GNO{YH#( zNU#BATrvQN8!}$8k-C66e!iID1100!al`L|tNmxA9l))O#bX~&Fgv6JtZKbXm#*4) zvIfYo5$wOM$#r;bL#R|aF|E+i;4Q`B?0|EPGS+V=*SGxK2m|U}`MbmAUn4!6?oF#X zyjL@NwrauENc&C_9ydo?OaW>MaUa=56j4X)-mCL_miT3gAhIdQL8~!7LMxq~j1rF` zhoCYtE`a|`{rpe1Cli47EWQ4FgD|?H18h7@A4!`rdaE5XrT)DXJJgbflXZGJE27T_ z?aG*gMl^^C+A=H-PKV~|u0(z}Es0mWwV%7ZVyC2ET%C5?(c(Nxi+7LsoCiX%dEtYa z9{^aU9fl6@O*jvR^l(m3nd*A&d-nHI7nIp7fomn`XC!jegf3eWMKxj8ciF7^_z;FG zfZG7y2QC-`c9p6JuW-Y`;D&p{t6MwpP&wL^|Lz#IfaWrH{aI3dg7^QVe<0uVeI!dKde(e zq(hM)B{E_};K&{kC2OCCGHCeF0%p6lM*7@95ac5^> zz&EyfyJowK+nQ?_z0cip!8ubd)5=v3-=RJl(L8jFt{0cYfkGZ&49JHEe4<{`cVRnT zr`zR#+jhaFSyWMbr&Nizp&GiNF5*eK{|Ts-o8H9*w0O&pnSlKux4O_&oeT|@F=@~y($9r_w zJ?$H7*hoE)yCSdZ+2MiEjhy79Jg)p;tGe}g)@ag@# ze-r;AdQiv3NWa`Fs<^4qo^Y&R>}#5CA>&e}prHi|10S~+Q-gY3@T~x~=L^_*#0}~_ z`U7J(sx51E`j&ke`@Iu0ZTtYsdz@OhB4)+LImE;$akKzki1Tp96A}D@SXytv>V`ARqYxEo!+Jy-Qj7l&;Gdm=?a$woS2VC!C(YGR3>YVGhkIJFP zN&kWRv)&`bD|Kmo8Driz2iqT33~U?jVGUo|_vcf0HU3=j=wq16R7&32dTf;AO1J*# z+)V|KPZqvxk`LRR7~dPwuGEa)G(D0(4$E$pRwoBJ{HRt7BGhf{rmZqz~yzreujVt)z+WGKgS%KfJp@%`K&2`i+# z%?BB1!J+u(*ys~OTrZW4Utm<^8|lvHZT<3$ma@o-syQ+xhZ*=CJCTg&4jgA3B-yTz zO7+f;6Y&A@FyP1J6#}OMjtZ|8Td&$qxmBY7NZ`Umdd(0(o?O458m7QpO`NUuA4tL& zSq$$?96pHj3|Kl&yN?zB#^!M)tqNGTu<}Y#Anzp2m-D8UP}7UhRs&^d2(v2q{EF3z%Rh-M@NIo`@)5M5ai|s244R$zs~6K2!@-1J z^F!+*60i7>(pT|Su!hdIoWoxY-b^bfjwvM{LrBS!rc5&(-TWA^1!eHWxaEzH?c_=d z@@Uh{8&GQzP+=-u8GtJ?ajK3&KSgf82SpH%890t?3?d_a`2AaQO3+`K&NjzRXW=gR@>UC}xvc~uPEmbB z1AH|xhW?Fsu~QUO$X{Qq0?X z7t}h)Ey?ydj&sc53v@!1=~;$;o?AXw>;OgV<|pi@(->n`*C!2R$WF-L2&~jNcUee` zasi*MNOK8nmXY*dI7LBxA`O%oQ6F{r(#+f9% zps|4AtIDe6p!PDSCMIJAXL6mriHgtRSXNPBmr2f+P{bJKawi~J(K^U8il?@UqdbO3OeqTr@A2`rh(wo zv0gHWQBKC6b0LBf=W2s7uUb=ElVl6+Bqj~5G*F1-s4xC2hwgFk)j#3-!mrW3z{CGQ z;Re!nLV*`bo;6UNwM4$f&xF3U^LZ1bdvE)0=SRrD(!)L?LyVzJ1y|gKLG!z~FYNiK ztD9fIL9wOHgC{d}gFDJBfkjbSvDA|?4xLU&8D5E58PH9^D*g(qJ8&_VO=%jf*Y|VwP@kS$+y)cNl(qRxZQkU*(V&piUih>b3CMwG5kFFBAUKs;OY0BoP zQLGK51J#J)UTApf)(cgXT6|j~@@Cym`a>+WM@sX!xo__;{)|xR?vK^uwb2n-i;PdJ zLx6p}$9n%e!>|zV+$Im;Ng#b@Ns0qJ357np_Mjo={=E=cU)Vl!pcH6$<5_r-e*R?Z z$5()dRS6B23R~6qq0Ti)m%QT<+Cj-$J;LgS81J6F#?3ch^^AQGjQ=|b1Kitmz-D!E z=_i(K&HH*kmN9oUfJ03C|J?2WH>&ef;sw!M7!$s|_lX+Kr!JUV;q9LIz_4*WL2!6G zLh)dQJ=(2~*R&B`lZOhE*KhZn`G#Wu%(?zz4o$4_*&O-7+gr0YhcUdepnuL+!&~?I zGOk5Ko80Qgs-EFRj9&k6HBR44+;GP5Kn)cufCsX;DB*m9>}jlPw$|Bes%uxIRt~Sc z#eaHy4}zpnO&}Vc3^Q|Z;GxUk_hI;$%Yv=GIhH{E<(ua3{$DU>NcXeHFbmL-fwZT% zIs1=N{8#t-uI9k*Dp0IvWoXL~31X&iDb(gQ|F2TQg|BX;9`@Go)zoN4-Emf1>-tfBo3;+D+;e=j%JM9f8xh{ ztm_?s{?IUAu+H*^Vt7DF^mz?9rD0V8*^^*#jyPIeV28%nv7T?=jrnRaWSHf?4_AL= z#h*JZJZD+-=-ZGx7>zC8u-)(;`9SjfDX>(*+1cLyfVA`fjpzgt_<->T4g_LK3H~Ug zlWJM?J0r*GyDefth5lg1!%9XItE(gqqt-_m5OXCEVh;kLw#B?yzlQt=vfm}w4_ewG zS`2VJ-1asG|Be28)`0o5%E-7>Qz_hGf!Yg=MkQ&irwWEXRRGED6$_}*&0b4-lNn_*SVq$D2QzsL55_t_OdXn~UpplOcvR};qSUB^rx<)2TrbuCXV+^Cd>b-swH~T64pa%YV_muuCF}laS+VNSZ z3895y-Q@OgCXe+}Ax^P=b8ysR(n+#OqGru7>XQGN+-A20YiQH{c~e~cfUnJ(^g?$P zhWLsf!QC8N))KqKgqwwuchyz+qg{dW;vacCR8(#mI?!h%yz9I0WXXq`cZU}W5~neY zF#QV>zhz(v0McT)e92BmDxMmr4Isv1D%GsQfP3#TUWv@Sj_*{Q%uGV^E}7?}U(84b z{kLe-m|r@#n;QVZ%;LyG^OG_W5A=vrz{Ex7bLW!c%fhWFhPoxQJPp$PuX3wj@td~8 zz_jfzKAV}r77;B{hu?+{dSOCjst%iQtdhJ6bN3e{beiKRcX`V&skr>Kxg6C8-Fsd# zu5Sla`N7}iQ$X3~<9qYen}klBBhrjAM=7pr`AnbUsRvy|?a*==lIm9uf5M!7ikddj zk5rXl_JolI^;fq)z%rmu>qi*k!QyEwTICpveBc=s%wpq#JWlhlSOxw0?+qP}nHm7ZK z+P3YU?rGb$ZS$V_Zu}Ad!+p6=yW-^DJL1&NtX!2VE3+0grL1Lvc&T~iRzoZ6cRGDB znHB}Zlt-mnN78z28U@qe(@nn=jpvdC3D}nsWlRt4iKzMc^Har*e)tJKwMWz#lsRq5 z9x{sU{AvnUk`CP(Aj#8}PfC6Qv5-c)5Xtk%)Jn}3g*ag~tJFXJ0Ek0S0O%oK#A248 zoz+m_AWzy&0c117s5zGsYAWuDk-NVgjg`2xGl6YMQr@S|`Ds1KD=F7mfhX)v`39A#%6aQ3t+=F zMk9Pvh6xh_e-RBB1~I`hoxyf-QJj{Uih;w+~!2LCI+inst6NNdBz=A5@`rGi-B|{Ep||q zKHLG5@W_kE&ckp#qT`u$aYo0#3{P)ZiHEDC-25GE;MwZx_cm!sUANV>R zmHk-kF)Y&A__}j8tdnHt7P;~Zmx+5@wpEz!Lpzm*DH1ftnroXc#9SE|QTB%~Jpje7y|4lfF(y6L+)QV?K3U^Keg7C# zbZ(GM9DTAhx>Mv617{uu+i^Q$B*S1^`<%H&4>P=#4|9z{^OaYaRaR`sn6cuL*^)gh zPk{2Q8x3j@-E7wehxvJ_KW%=Ia+~q@$s2Hxl<*cMk7wQdq#ACMfB!G)KXr13h+&K(R=(^CrEwr1! zNlJ)D>BE=$8t-i&5AHP?mq*m{kk-0IE48lSL7IkZcZC^+9>4*MFpgv>4o)|6&W@Nz zz0e8^anaS*nRBhYBLxD?u3C24E8S0U+<3AT&#`c>T&^T*&o{W$Cb5GMka zx+Nc$U$gJug6+Nmr0EduRNHCNKI`g?v!2P&7Fi_NK@VeElB zDH}^YfTfyQIKo9*lGT=5GF%vjy=o5sQ93xf3E#{J;fK2&u`?q-;e5-}^>V4y8}Ux- z1?X+-zFrYw!;S5BD9cVIvDFuKs4U+CHE{##MyRB)&5)7!kA|x?46FiqkU3Xgalu8K zW?r_ly}yc$DRi`UmnIfRp{fLhh2@}{694E?jM`?K9>@>ve-Sz#y|$MSz8oIH-H2)$ zBU^PP>j)ZRiABN;ULFQ8<)h+-9wmgx!<0jqzT9*F)#oor-kbZ~rl*eH0qEtIsA{2c z5;rCh+lMv{j1{{uXO;sdj?aBhySUIWV%Ddn%p%Q`4AYYAQZ2#K(5t8=tKF3l$ICXs4BU7sqQaN0 z0VAS74uTnM!-@BPt-ygkYWMFz=5J*flG-(OK1R2mLMltQVKX-pC|l_{(|x>UmOKW ziw+G+@lA0pXe}Tr*0jYBsN}LT^K&aT!}4$OkAHnOfafyC z+my?2PXmCpsqZ5rs#M-p$6%@9E~agH02OWI`3joel@KS1k~7XsS%ct`|C;PK+b9_r z7lV3Bx--U9#tx+$IIcfbHx$mr=N(6~Qm>5+FxFj4Hod8h6TJZvXXURV-6_?~)EFhz zw@4l;ru&MWQuK)sMJ2$zp>%`hvd2}CkvMbcI{-<@x*tOu0^lo3>_Cwgv)Yanc@KFC z3NMinDD{89d8tg(#S+W78=Oii@hI}ruv71lF#HVMS|q@Jg85mkyRMwBY>W?KFeF!jx^jEpktcy09MC-_*Gc}v}FT>n%POB zI8bOVm1Bx)FGU5>A=oGH0A5%k5i^iE9|R^Y6E}v~MAVzFDy0wn?R-0yKs@4@956D15VykJ%LhW-R>+bKARk-e&* z1c4I^21>NVHL|hzfrF0dNGCnSzSm>~b{C#^)kamiT~VQ30mlsmkXs#bdZ@pA*=IUO zjmnAb7bE;qvnW4FtTTMs_>ymz$`UA86eS=IZ3vIr$A7a=3~#kw^DnYGS3j3C#s3x8 zf+ZykYi5a#bS%k9{#jliH)W#6NsKXR{ZIvq9@PS9%Gw&kLTFLMTgH3(t0@_-Bx&ha zTXziOG6!+3l%z%(2JdObK~B?vvbWJWRwGCPyb_01BE(=*1E&FMr&&>ZLj2#L=S=Xe z5*58p@k6<{pQv@DMNVv?oIe&GlyaJh;1&S(^ABA@%bm+q%#3W?XYdZ2ZR~GF!PJuo zhUGt^45kkWwebxvm7Gx)3&S8LC^7%-a(svT0C|bQZCmU?J3&a-&EkuJwzNB}&%U&p z#pH5va-3pJ(r0<;{DK&~IeM1_9QGgfBcE+%B;dDP+;8a(8$Wh>bsPpZFTwW5;K8|l zeh{e|6#O$S)h;7`2>9TEWC`CtSTp0^QG4GTGW>TU!FfK+My>%(LjZ)MoBA2#iDlGW zI(|Cw7eoORZiUgLY zyRKR~wa8h6ZGt0;hVyp-T9)Mu)m++;yv>Nf$J)0 z)@=FszdQZkbTUgg;l=@6&gFX>p!efy2{|X&FN-#0v-o=nSXzb9l(JPSE3tD4WNM~) zh)NhLHlX5y5sAITT$ys{-0xLbC{sHVXBQ_^L)-s`_C{8)P_R&J1PlcK4S9G7=w(dp z%v~%9*a5#O!9o!b5YUTR+PIiH0j@TNE~X-;#`Y$ruuyz_uu%W=CXcL3U)eYi@z_n> zUthx6=;>S0)6qWwypZl_oxL&i3V!^PSAD#V2mSkVwjf_3J(YI#(<*bngpaQ31O(VC z#;A!XZteHWZP@ocm!3Zt-`IK9p>fxmP3yjr)Ca_{%_ z?GB@g{zo{)cma^O#Z`|7TVMtRY$b$aUBK%joyPTjUpI<nZd$b#L!8=R!3V$0jw7$QJ-6|ms;ZM^_1e}z&?(R{+Z@m} zpx3W;Aj^P5bXI!xZ`D*1$g%Tc;e|tQo8B!wx9?*xqOE?dPHh~g1mSHbI@RasVI|kX z_e9GP1G{*TD(M!wYwklL_BQaOx9VZRBts~Q<8or)GwF6K3Ula9c7}7`aeub zH1j~#I^}LY60?~0U7y7@{F#X=eK^k6cZ`DMy+=-zeg(!fidSa4kTb^l&EWpn-eC5m+9MP;?|X*>MQUgVR#;0s&v z+h1nkc~ba0xy;&+{LC4Pw$)wBuh416`i{ETFTB)n5p7}@);3TPTmiDzQrJd;76yz= zcvh5+} z??lpfi9`pfXKVUoU!AB(uo(mtWTY`IDY@H~h=eTcR#?YHNGlA7rQH(UvdRodvg#f( zu|FMpQ|%4uu~V2nK9!i_T`X_|NqC;at{6QWGcs_9(_s#<=F5&f^ZwPb5~Z zT2N9VEimlFm|?i9uzfHv$qNiFS(h_%e2VH@h?iCdgzX^R;Nnig3@6sN&yW7uBs7(} zQIkQ>sY)&1Ju)H(`ISB+EIe_pLsKFyGl~_!syd38PZ4z*Uhotb@ya{2?l9DPqX8Kr zeCTzF^GsK{m<_e8^^`^hHk?SUhrMRvG+S&#!eR6@h&I?Xw;Y(cRIB_7t7HX{QQR?U({~0JC@O(-3s8F#=1*(ybZ9q%|ndkWEW!ieWoByW_W;)Erae^r5^4vJkM8NxFuNXv@solAfj; z=^~GKO0q+xXXxM4c<;E;va~gp$)H7$vrRjEXJ6KwkPs|2ab zG_q;JpAs^bpP8Yor&MZ9M6TZ(!ctm}ye;toD5vnd9?*%=?h=YfzSS2~uSC25z_5YN z$Ae=tHKiLr@Tf-*`^H$1a}fJ}H@~~U?+z5nZq)J7WePz6nLjw?}SAl!l0-ahyXZ6yIGC(x&1thxmN)pctqpIdYH? z8!Wm4U#c3kmet`vffN@~qSk$^4NFP_3&bkdPr?znMf-%Mla6)X#;>%LEu2?VH103NoIdGUZ_&KCjQJjhEEA}R*{&mCa$nhA4iQPKz?%0gE`BYC zYQPP5rK-|+vujHzVnh2Re8jsfbWFe^2TD~tw!P9N3L9bjiWZrU`7WD_M4DSsitn=X zO7-#cj&F_7KP=Q2XJ3DC*wuPQBkB+ z=IJ&@Y)s|&$`CTy;Ca~w-uoDg0(lz7arhEcOK@wY@42RQ5eWfv?QR<7JEBs z7-MPF?W`kI?`NUJr`+NJZB4mNz9GTF#Lxe%!Ws4#31}HAyD@kd+W-^yQL)-xH~%Kl z0FpilKgl@Td$w52I||coGhyngn!dWUNjLu}=eFnD3xRKP_qe*{8UFV6y$mpFISuYs z&AlYuI|=2{@S>mlL|nf&w(M9K#TczdcfqolXt&i5R{AdXWO~6USkUpv<5W>=ZzAbd zSgJ&5SNbk`m4X{T;aVrQWNK!VGiEX#6_HydNk>BZN;=ZF_(uoU(ewJqDm8411&>qR zT0>NW?jF@JR~t1>y~39MPC1G3NiJ2C0#m1lWMx_tRGa1;)UCxPwKuiKLYkfhm6y)7 zV#NRfbY`};6bXyVT<7?iz|F-CZ2QhY`~EQd(b6~@+hyN{4)1HeV(&ux#uVA!Rm0Iu z+NK$o_3BMirp`CsK}&aKWcif&P_2y&15!YbA5~dbbcSRd*_T+o?GLG8uv~4cP`>0+ zO3hVY*`+RxNdvqZHl=O#b3&>MwW)WISUgye1{^fY4z(>yg7Xu2VNRoO4qu+VbuQGB zu%SC8xFmqfUX8i}gI7d2G;;fEElktst#>B<^??%AiE=NbkL@+4=fa*Y+II@iwVDjD zAhS&06tT5DOvRmRhXRB7CJeR`iIA<$Z*%2Q*HRa-iB3P$yzx8iqAD0bBtu^**DlR-?+=8590vBWNATwic^9g0zn;GVJ9F_x9>LR z%aT2JEpU}GGdpw6euU;UC|{+@iZGVQlytE*jhZylQ>xpc^hmb5mTvIymob(dV(fSC ztY#z(VBST5H~^^8-#N?D#Dq40ahzgbcKJ0OrTF36ob^To?4~(W+^Dl02)v zb2J;SvH8>zITrJWvMhQMIbm6B!_ z>8S;^omP1)kqni5GxhJ)-5#d(okYU*DYM0Mg8TGMeCn#V))1Y?+e)B^cK;B2$-&kd z(nw1Ot&y=~`Zqo4Qj_XNmt422QfF07l(;r7{=!8mG*$p7f6w;&bNKBQHYYT~E;&>x zKs!0h&=oM4o>%4*x&H$$^U}7e+4;ZcBd*vVl7qZjtDHc}6G>}fZJAatq|Z|9|M(<% zJ)7rUa>cR{uNR*X;Vb7&rV*~hc;EP&nD+Q6q>|!%g5>5x1BlBS zl@ko}SFV><+ehjQY}|oN#nI{lgkrAUAF+mjynKoEaWiMbV%N(=$wR*QvTJV>*7{q* ztVoHOn)4Dim}=ag%Qs`NU1fbLy>L|O76x6C%@ppTknBKgRwGj~){zi>)<(9mYMPpP z&9Pj@)x>7uksgnb22;B>5yPqfxFK~U&r{h^b1AGQ?&eVNM)>6zW_vh?)ROdq6x-gH z7Z(wzM8U6FvZGTmR7DIu^+lSQ;IF%CNNYMaa-ANDWMWv8U<>FOw4s)adJ{%2*(+S( zt&a~z`_s^v+@KHv0A7P~jUZ+R1(VgkvI=`ajC} zJhDyCaDlqzWu_mt4Fa~C1Bt@@t6%oa(;>jHkAh)=?^CSu2{I8^K+&Mg*C8O^jjnK! zukMvrK+(VQvq*bzP5vSzU+j8Ipgp#MxKu|xpNyh4*QK>`*?sJ?Ov^|~r*%OR^YCn* zvQUSGFwkkX24ajihC3{iT>>38fk2l{+@ zsixxD&8pk&;110d42J9J2WuCq@}rwov+xCt6v&?@DJ#c$tlNs!?rg1P5;u;B6IFC( zA!g(^;xpfYmt7u{UBU(F{Qr#l5eVur(&&-FZO~dcQ-;o~+&=pY$(d{R^WO|#=cq;| zQEA@+U|iBP<1^<$8okQFWdEJ-iItI(v1g~o{Y-7+|5GrdW1C~19#RFjgITQ?!k7xkGukK9KUd4{ zZe`*(q0y@mtp#qU%8zF?Ykis(WF403=bX1{*0X1JZnalu864(IdvV$575IvJW!-f+ z#i6JSAuSWl%W*Fa6GDpGHJdpDuz6^Un`_nS1^5>#RvIHxu+k;(0kPNbzs8HtIU3d? z)+6CW7TzUNa>O|I#bOUXqz~5RU!uxyuO-SeL@@n3f1YPc!E$Y`h-pCDrkiG5^IiPZ zbiC(!eR@OOhMzYT>^h|qC3UAY2Qd@5WG{Q>9a6qQ=ofy85IS6&QHDDMMG38H#tsl|E6DNZVJkz1g?-Po005sW0hW(V@hL>}5t< zlwp3E$YwYMJEn4!dNC4csEK|9mVkWpNXW3iR zkgg17nw^wnFNvwTn-^dQ@8L$tVp_*ZxiIX0M?yo#V5oB%Pe?<$Gti)n80BNMBl_d-+YG!%0es28Zjs#py}!}Rf|hP`B8MZ3zx47n62 z2n(`CzY5^(o8+1S?{?wj8I$69KnhvtzuB|+1Y7JGc7r^U>_L=K(Q)uh3p7Zw7f|-; z4b}a(b4aVU-fcF?}VQw`|_ad+9*uZ?tg^{u8xA z7(cBJ1)5JGMFk4ua}5m!D5$}=aRPC{e3&Z@io#!hNmRQHnu4{MJ#zu{!(`DD9Vl1` zs3>UBu(Ejzfn+- z*y+qTit26KHN=hr-eLzD@|VgKm<(I22^?QnAikIushr{YS z-2$J{4%-wOw*0Shx()#A$hCnTTK=iaBy;Qg2ZgNl&Q$xxXB+=m`~2t8OHMQu2J@5~ z_Wt2gJ$=8wrqSEfq&$}H7ftJAv=WWS`VB2H+-Oq!OQ|7c@d{Y3ggsDGjb-u5@pjfK z5)@um(Mfju1*(9oXOhf4kOvS^WwNGP0#YEK!Ik9J7`tp&zf|Y34pYQrbvlIlk~{R|+G0kYacGbc)11#W zD9Cf$6{j#w{5x}155W*(JZ*5HO*OJb8l|($n+^miDaA1bdzyox0tpzwK?nOGZP3Mg{5 zAu8G&Z3P=`nh@oxaA{CTfkV%>3zIrqR^_$AkYsYFz)7{(1%rtc2Vsl(2u{WGQV5?yRl-uO(yWWlX9U+upK13eT%~&7hfHY^#Yvc$z{9bELMLJ(b@^ znGtnSITq`>eGuY!+mUtLXAYCvuL%w*a8SU)Ga}%Z4nT^%SPpMcSS{3-p9o8tudb_-=#?ZJT4j8-RsZMuZQD0Cuk7%#JqU zIEK)<{SQgDq5v>&?`+JUC_wyx61=_TL`=hqQM6&>pAg$uvYymG&ES3gC&p^^S7y$F zKygrzH=-SioTCPy7K6vmQNJr)@Fx4=Xf^~isr$5R1_&zLLmA}#IpBXf5SK3Vw8IO^ z*E)DD7Shu6KR~~u`M$jINuY%p5ZE|E4E+|CO@>`sjDd8~5o_SU0|qHTR>S6Zk7Zz@kZ1Yz&9SUCJ(EYe4FZE+hz!F2Oec#i{*i`vbOM zWr7!ibXkHju!KA_RM#4`LZse8rR8I>%tWCfn=mzp(os64-h0!j(iMk99hb7*P@mgZ zrrv!LR(mx)a^AXT9V}oS9x7uv95^D|4jodEGX9C0gXD?^tJ;|JeL|^kLBUnzZ)f}qzvdd(& z5$~HNM^y#NS}!rxBWaGs8co?GD%~xzEb?OsqE#}^u?P@P21nM;bhhx=X(26Q5+(h|56}xvr5iM2iClEpw zbIJuI<82SsfK{(qfxBCng#bg`#w6QGJ?5G6A-5K%$^fkq%}S+nDw%T^_40eG^74tK z6gWYTQzNc|eX#=xjDoGHYA{S%ErRu}pfk`x^^JxW)78+W*uuW#x6$@u@kYU`{ld_g zx1Ef>K<9B2%W)?~R*w>K`RpuH;##Mvn2G>BqQu9Syij0V9h28v#AB56i z2@XtjDTNi=BpRGqliiS?VUJV_BY(9p%-B`vh)QU`_XVnm3S?ps5g?wDS+iRRd~Hq? zliY(1E4m#tol`6f(5YiL(0n1!+6f^7;1E-h9E3^InVP6TEgA=vY6Vf*W7bAuHo?5% z1Hq;VtOjVMFo1TvCq4sfzi}*sRzxLCOJW}z>=~gWrk?e17*`P;33-D{MXF3Z8LpCW z0&2%S6I2Zjfl9FBEsLz1!4#|A@IWY29LvfwCvNO9-#nm_lUfp2L2@BcX{rEMMPlOW z5NsPfGh%b>jnh_(7y@R^1sDByX&+Xq4^j!OO;Ja3AtWYvUFP|Uci1*w_t@wc;)oFX zPa8WxQPgBoXIDZ0?F-wFiqY<2on)tWXI&8P=tLvy^ne#bI1_hkHe)ko*xm{fICJ9=ku;jdeI_N4fEFC&`gi!5L!Je^59zu4(IUFw1KYtAG0BH)P zrGKIc#dsi~qy&eQCEk2IhCJ(=F~n~A&Hc2>GJeGR&_)mhg{%=dE4mQpm?<;u zmFoKhvnqaJg}zc(z4sy&w6!NGJ2wI;w>H!1NS*h5AUI(iAyn6wZdcs!dW`bo8puagUs@P zq$n@bmD>JOgrdS)G6>_yTL#k%TbG-65r zzo@FVuV?tdhyMxZN-yEl$L5;q00vVO5v9Z0!k{ZjovR#lHFrT3e+_|cVMn|66jOu1 zXFv-N904t7b^O#5G5Yb?)+(F>FS>m-N+F*+TKn4{@MQ|8)EnOAY~LaHK+AHLx8 zQiN8Q@!`hpHd)2FlPQ+tm%f;gn?k6_F--UJXW5|btyI@Ds}b`fb+P=;Ri2AF@9w9v zuO)At!dLCEJWOOPa;ti#Rl`iJ=D&4Xbt@KHkeV5lU5lBtiM?uCVhM7&gr^smgN}PxktH%h z^ok}{ZC^1JF*dk6n}<+b7fj%?<#=ITq4c0Qt2B<|gSIM%C%zi;+*v(quN29GD4rq4}xtV;ng2&m5)iNd=kK zWpJ)@F!isP9kM56s57C^g6Bz1nJvGe-)KV ze_L)sW*eOj2L0nGM{5seGCPM#x?iV#dA8=77BKBCr(1E>Nk7AIPiS3j4_;~gJ-dpV zK84)0*n3I4bt(9_5}|wh{P2>t2zn1j?aQZIi`XkKroVIfOC6_aN9~JuN-w3+f{tHi zy|~+Wj0fhvU!S<2sB?GZIXZOwe4zPlc5ShX0>A2>woiAmCtP=KSC337r2U`d{e@j12#O`3bt~H8ib`zUU5LhB>e% z6Yro5Kq9FqB=&M3FTh-mK>HMeAA8&K=;tjQyxva|H_`~2bpx#4B^fko)kf;*tGiFj z^xGfPv>RV^8~4@IKKQL%=c|B>!0oS>%yoa?SF(+Bo^N;P2?ay5@VM>PNJz>veY~y_TLyueTgK{I^S&$2sl%N+Dhp9NL5w79#db6<1*dA&jW1MO3sAfMzzZ64-xVpKjK4t34l z=MTlKC*kvyDERHOXRErOyh%?*-&?D94gqmyd~+ev_@y~dm|kcQsjyW7bhsL1^0=LRa036bOLErw!XEsQCT|| zGB%|+z^<;&0UO-UG77}IG=$G*4f1~`=MlXPM}2)M8%@qEuB>TF8Qz2s^yk|kL~tFi z0v1uyHu9v#Z0QuGeaSW6h`Nv1G)9XYfzGvA&_N_*tur7LdW|gah{ZO0J=Uc2<5VSR zl>H>`jA4CrtsL=Rv{`Em!xrWEf|Ye9+1eUug-sEQNM}ccHj`1Ub%pi4NC>00Q$x!) zWgmk}CL;^zx~r`-DxZQ48>w9yfA;*!0N2;04f8fr8{AZj+9xb$oHJMz(uB6^sKRfy zTFTbsp;Yw^Sgf||SI);9xG$O-bf&Oy{#)Dn{c$&$>nAKH_5Cr<&t(1a`0>r3YxjN} zH7({iPr2j|N1IQK1A&)Wb@6v2=)^+OE)G_Aea=4f zo|On~0$B7C7rUSBQO(}$$^}EGzrE}(V=Iso}vxP(o4r0Y1zWzF2bi2d-bV5 z7X=#nGHh7?v1og?tT18AKjnGln_BT(YM~eDu~y{sJH5=Dq|#4lfs_}?HCJl4~gf9!YhzLD7n<;KY=2N(#sQf5?iwkNV zM-5P9J&9*p2yYIAhm4R8xV5p*!&*O1M%%e=z&OE5R;h+kr%q&0+8s&D@BNStIl_|e z9vYN>+*7uW4QADpa8TDtvV`b*ayIgNxaEqzUF4<6%d5m`&ECgbuW~IE${2Su?(45N zh#U@WVN0ig#8!l1Ts5KZW0)Tft+74r%?N|B6;Zt3VDYrEunwBit9LmdT|mxQLanY1 z!e46pDbbd(hX#qRBhftZre&*F2nYjf_PEvy=&dxY%XXTt2xxFsSJ0?j+2GLlv@ zZeXgY)h39it_IC3$uROU5-tPI=2W~&&ta<*!8fF3?xi%x-`+7)?9OHAI+-q9VGpuQe};(Y*Z$^MB>Di z*i|PC)zn=^jiug+;U+QKHOf!T^0nKzB*Gk_dikE-A|n#x9QwbDH%^{&%Hq1zXqw;% zG%ev}g%aU7ffdz{l~w7%VwF`1kwsEW^yF&Xl1ru#RxL~9$-dS8LfRUtnuMf?BdyJe z!|{(uNfd2 zHYALa_HoSqvl;DUY8h#P)(xnNyA^~MMf2LFSJx^!e(mGoaV~%P7L_TC^(K|f9c|v_ zW;>{09!7P}6pJElR>+>VJSc7Hb=d|fB1MT-GeQ$Gu2E{JOSSIK$-u;UJJ7jm$*Xu# zi0v1sQ!J^_XcyRa4DwB=hyLOUa~nm34y8q`vQCjgxENsRnRkh4$+E_^#2S z?K>p@CaqK_Aj;`=6cm{{eYZZp8urMu&aH!y*@<)J10XHjpv|D^V=_@V{3qk`mTW&~ z+m{`q3ZRMovw#V1Bs2!-suu)Ts1-I%V2j&N$)C^X3qXig)WO-K_MsX|iq zr92ZWI7h+&8R)38kBa@&*OfU18<)(NX~ZsTL*X=T{^Co-lqatvAJS+nI4m_7455c? zGK}EF6}HET96MU*56*dkb%B`XI$DG+q|nls6vpY&ftSAj1s{1D!Yc+}wi~@;YiGzp z@RD*;Rbc7kWJIf&=F40BgqAf*18oVkz6MCMKT^STL__&)>b`0(7JJw{-mp7@N4fbOAt5zjw zcqu%}JCBq&4fGBk6I#lIda#rOiS{RfgvXkV5oaUmMFgI4&;3yrlu~78PDnoQDoNxl zoOL1upN$dy+*=1$S*e-R43?OFevq?6o;;BJ%UFZofhTtDbOTL9t*EjJTYA#Logl%s&6G%qgQ|_LYZmOL5)3l}zOQ+b%rkK{To06LY|U7wB@j^RqS>YDaMR42s!oBuC-;t(*Va6yP#qZbxI-Ov0ms0a zlp)(J)m7zIxwFH0iz)}=bvWv>Ec1)6a4RYfe4o%YPPBW6>OZU23yAVcGiYI88T@Y& z9}L#dp2!be^4|Z=aJRS0s8~hW=s%ImQ>2%)zOG-K*E4x|aWeNtaB^ZkT^PcOkYv0C zfr+WWgD}ixFw3HVZQU4B*NH_w)PXk8niu31sHadx{sAO=R4~n=1JvjX&!8fc$yUZK zA#9Z6*`${L#>LwZ94Uu=6(9C3+NkcD0&VBG7egu)qTQONw^)~SvzRZbXG4HF1V^I@ zMLIwe7noY=lWUq_q>tvH7LF0`Hk>TqNbDdgayDsYlPEv_f|Y{_Wmx60ZNRfoGNRDg z<60p@s)l=<|ISvrCRNeG7z{sWMBAs2w?8+cry3e#(v%4f@7?^>40sN&hMt%ugiVEc zQ4Rm?7fq&DjE{CeqDl_hEqe~>^ue4~Cb5+m)FmHe_B6^P$m^6N#sPGLP2uJy)QbLEyC+Al>ew4W(^)h6__?m5@zCE+RWP zO2kSG2v4#z5rH&2I04M@Uj{( zG%iFm0Yo&hM0|OUD6(CqZm(-Ykct2;BmijPuVAw!g=~ky)O)n~ctksuEfiMnUC|X9 zA)&g!fa5U<)}>B!FKIgLyJKAH3>6(Z^I8Lr^>`fumM;Bv?_5a;H3oz`8Mo2DkMUtN zHcQ0rWrfvC7!-cQz7f0M5XF$g*Jt$B|_vaJER zKqC8r%yY9fHQ%c>J6Mf<{iuoo@UzzIr>;@hc(QdyKWCS~aJ!3EUivbgw9>lJV-IJq z9V!#R`DjTGl;uZ|p*(F+p+R`_x)iTMaCf0h=myi}~ zF6GT(RxTq!Re2u@=AfHO&e=O`2|&D6!F5EP*Z4IkDo_&_d#`3JkTv<3@dAW@@j zgG{EQ2*xWw%p*e~E`6EQ?dlQ83GVg_{Viv6LJb1tm!J@#(YYC^m|ZBWQlk<!~)PNPO~yoKRZzVpG{RU_EutAiEE0vz&> z=eD~a_vKp6Ju1~~Cxm$8X^9NsVUh2j9c)!l_7;$k$`R8TX@IyG$PpXS*`RUphK-Nw zo2NpnQCyyf@PUL`?&)M6zv3*{*ol3|BIu~zx|?~>VAOr-2gK%+5GK>jlxq|1uG$(| z-2v^VIU5&gWlKAYYoqu`H#R#nZ{xUzlo&Zi>jAn4*$Pl8lw6<-Pm(WiEt#lV_c+#rv}zBwL#`@HGTRHy`)QI{hecSr z*Jqk-(RIKN!4oxHdfWKqETFK8xro>Xh3 zoFZBcKN!xIVq5+z1VVFcgoMFLj3r7b#;n**2AxJD^w0f)^$N;$JjV}t-yk#mPp!Bz zg|s}qJnw&=CdLkxjvCfW4Kic&qje<)|jg(w>1}!(96+F|P{V6z& zN}HGH09tODFNg}V3KZCr3Ro>Md$F7tr)qU_ia$DP>|xeP9xa0Ie15`9AvN*n zk!F*GX!qACwGG0IW2ulCKWYW0y4He>+!q{-IG6B9&0Ajn&hj0l{wN3iWc=H~A-S8X zV_>u^nDO?*wk|z#GbaOa7ypbdmaa^zDxfhYHB9JNe*>@(Xg^jeE=L8P#Ir#=_}nl7 zwyaFMW@KtDd_J}i7ambc(G+(S!e*-7_mgvu@6C+`PEk$02inHLzon3eI-Vi{w9&QwpJ}6E9QAdKKatb3|9R#8w|y*7 zBLs){&x?Ee=aSyJ%U2i9-#vqTxizWQjU@wf*d>a0KG3TqBt@x}RxF^G5RvAv%T(%4 ze#PS_ia{`emm9Frpwe&=faobc)CE%kej#^}r}l{=)6;l3jf8M)pw)2svq$UwXPHfm z0x+4$_dLA#Dn7YFFU*P-bj@k!(m3Pfv;L5Jl(wQ2)^23LW4$f~lw7+VzMZ zAsR#f^=#Umi%}AUVq4Azy2LU3XW}yXK$AHBtpv8B`%TX7mt1Ngv4TtzdIKgvyhibJ z%!XKDZhc=fenSxR;EpyV4JyVO5UL5*iB60J7IcY=ff6Cqsf>V9NwQudErQPIh)5wS z(G{i_Q|H{lF8d}3ekLI;ITpYS7==J@j5Hr7m0C=7RHKg&n-#p9@Oss_ z9EC-2AIXYQCXDR__6P-D__wFPwujVk*cu)qZB_}TjfN8^+u&FO!bI%hmy8hK3oU|6 zJyztE9AC0MHewhJLR6WldMBf8k^)i-Y|y1x1!~f~|NbXd{x=V!AbJ^4$K3&)4PxwT zIdH%Txn7)~FCVrBw>z(*#sKc{HOD21P8s-l7O_!uh7?h9?k9Q*U2k$m4WjzgS8Dxg zJ@Y#q%0Ra|a*$0!f*$yz2eRpIyysMDSO^*|+SU;B5))Z1&I#Zy#2#9^#JET>rOX#Q zqkZ8h1F!hP+ycr#a8b2RDp*5rM-hy@GEFI+ zd4yc;@rTccZ#c=Mxqd*bZHW}|X}OGA;acP4`id^MGFi0!*q**u_8$(bKkM}N+PZlC zya@ARz#@3TW%;YJ;TZL@`&YI(#HP*7S$(!z=F@_TAA0;-v!QqGpR%{PpDvlm{1{k@ zFN;(e;(3p34Q5ZYx%XN78wmUbpifi!!T8T`6nuMfp-(Iao<|#QO$Al&k*VIs)c1=G zj}mg!y1emyjXo>1Zz?=v5spZIFC^yY;@Xw|E?eQf##qJM&wlyT%_-?TRVSC9eX2Vr zLr*v6V^((#A3=VyFK?eh z_7d%j^H%|l2K`yl`EB(k&sqJGT6H%J<#x~b2?=}w#T_@+@PlB-g@ZqqZw9ZQ;lX~# zY=kGdJb7023{t)J3wP5;nbP2He(bQZ5Ow2=qQIse=?3}gpja8|Y#6SW4e=f~{vlJc z2TeQh*-PI8HIMKPpGop<%{JnAIB&)2hj*C6_ulTq>#7s&ZVJQLC*MDwTPNQVYQGA5 zlMW-_O`;G!9|FB?+mn0htG`0%#M_o%$SF&Lt!|qsgcTM}$ zJNoSEj}mMSILW$)r`mVzoHl+|i3q@$9zydmt%dB@jR5MkLyAO(n-(OZ<8v@CDOjS2 z!C?5q|HoNb&^(fT4PDDe;#`s^29|zfU3GLlklYaLH8UCrW9WXjoBh$T%RU zMQB8nhp_8pyVvRIJCWS4AaQ!`6-`Sq1%%Yq#3B=n=Biw)Zu#%9XzN-#gEo6(CdzYF zrQu*d6C>jqTb8L-yvDHZW?UU##ac)A9|J>g$z|hrs$90^(iCLP3#U^a4H3Y6W(n_r z0d7~eE2`Zr{E&f2{MIgqYbJ=%`zsL8o_4V74(TUGzf2kwDV-zl-~{r0t*sBy+o41L zpmseYTYW=tyM4Zm;P{hK{r1&aq6h6^nw#W3$Ky1`nZMy3UH5L_`RNjf0pipaki!Q zVihQG?#RDQ+`A}vE|tg)d|Pb0&FPu_g-X!d^6*FC$++?2NU`aA{=+I%ZIRqQumXV{ z>ATtS4o}+oS9Uk)-WG%3P8q29t#XXL2;AN1e;*BY(&ZN`-wx|69_3z`dLN4ME8nn+ z^hIdEL0BF~=dugRt`dc@InJVkHtYvf3HJeU~p5_zY* z@^35p>K0-2n!NHhS*qTuV&L>o8ucn_{YI(^r^}Q+)YUKb=!e_d%5-qD=&^q{wbtzS z?&qC;y^=wg?q6B7HH^D9`LUd~;(t1lQ+kZ^qqdsSw8BYe&6u z{|m7?R4vh2k?a79W|8!=5<+9`PTp*olatN6>h~YJrw~1l1iks~mbcFPiI$sj%W(oZ zuh$4=e*BB?-}il>WVKqhvVF>)mW6 zh^5B2IeS)+-p}0Q36qdDxjnMlPn62X7)fLNpz_c7fl>jSy6A*wWwIYso@`dqI8t^Q z&Zo24`|UeI^)^SuvgNX4wKTYYNmBWjx=H;)Mpt=I1KSCx9dLGY`;%|J^3H*_EDie3 zhzl(fP|g?Ktir&MrjU)Q@!n4;Oi4$f6c2YL6N%OZ9d$i={fEIL-%EYbB06_BAgX8> zJA_k`*YyfhpBd|K#l0u(>l3ASA(z??>0#qDmsrs9-3I6OBx5@LuXe+=4yS3!Q)vXr zpBkWZ(&C#OuiNOFP4?$_;~LtK*3#^aN?hiH`=o`E=#6|%*6Bl=YKm5C z71YO#Yn`zj$1=E_Sp`{0myk9@d|f)obraQ5X*Jlxx+52GV6;VT zbFqOME3BASEnDU9&8Fbmn8Y{w8z<)vS*-XU^$2gg)z&fpg2_?CJ>JcgHd$0^iQcpI zKOazD*?egz9IcLdK(v@)tFo+Ue$3vHeW6U$vSh z2;)0@x3H9LQ9PmA;rXZcs>5^Q${x6lv-HHZJ+)3aF9bnjBF~?1T+9G_o@NJp5GRb0 zBdr#iUoFigT2IFmF#1?jh3fPr@2#6YftdVIYhrtQ$?8T(Vofx)H7p*&Qy8{eLVPd1 z$C%9XkdtY~DP|O|N%K!N+46>Tni!o^x#Wna?wD`O#(2l`UQg(3LH_JPk80TLzCmc_ zos{J#V^UK6=7!~usf_GCU1YlL1y0I@3Gv<8x|p5?M`MIgSc1t5Mfuf##7!_2US^Qj zl(<+-0IZ=a$aNN-#x*S&vo)bv!nF*MEo1J3iP~&~lRih%1V$AzdZrc2U%V;|d~9+O z>ZuUm&ZAC1?S+^9a`W?iyOB-(+)&`@4{852xNP2}w>TtAS*{{u)9WFWdOBF)2S+B> zqYa`a5#FOCQu%(|x4SB@ktGRCj%oR%KVhvbho}6lvDq>`!poVf-U6@5C*EG9I?gHS zAqbRe3{AaD=`Jl(_W)}>9E3f{IyV;7#FdaWt;x(i<{q^o8rY~{~2|wj#q>@so;s8(iite~CPQqt{J<2xBxUO9lHR*YWj^XQKU9hJM z#4b)HDtx3qITX=OmcF4evk_q2Yp%(5@2F z9G@!HE2T}-KC#i?_*GjSfi7+zD=EG+#O%p2w1j$DtRR>O)@@!H;pZI)+ETZLR(O|P zW%@>;?s)MtiKtI052*)*_$bF3hBW`)8MH7nX`s?pPtp@gr4OTCH>O*xiGqs8%XDx! zieShapoeNbuQc~pE{SFu4Q$@FqDUi1)=$^fur zy)Ofo=)z*&sR;m3?4J%seoNSO>2M}=6ZeTMf{cdR=7|bP2bAm7>QU^X;#DS^V$yJ% zArIST7kjfna*U_InNO!MSw%{xAd*E2_3|Zq2kha?iH6W)kOK6G;V3C};0LAV=_DD4 z8qIS>lQ?q?F?bBJD8Dt^Os05=W&^G@C_MDCg2AC|rIHkuau3F;btW~m&k{!y(@A3| zyer%RqgDY)U4yLQz@&BkIY=bN(0$+*oaY&pQhX9wEPE?;dlyEz-Ukx3^J6WfY2SnV}x z(iiiwr+7@(Ezcq~x+zSoEkS=S0{L^(Kv z)^sbMHi)PS7U!Oq-&LOKJ7HpHm=(}k?(Rt z>Z?Ys57|jKhSUC76`^bkgurnMPA3iOHMr(}Vs?VVqRI765Um}9Dc2}16gOZqGc-Qq zuvmbqS@-Uk89_#&3rnhvvZEgp;ImC#Y)k!_b&g}tB-@ov={$3ty6t%X9(6CdA0K^g zKd~a=d@eY_fkC8CIXNT8wZp2ixNG6OxNB-as*R`SS{Uyb>8PE~gOagT6zi$HXWYlu z+?Qnk0WgmwJTKjLPeU*vn5`1HZG}VsXqC|oTZvk)VXhR^gVr~ffoQuCUWRy!~TZaEHz!cyF z0nAZ!dR=JskFoqF4;qh>111%RRT{2In1x&0YYqdL+*^Tu*02SNPp-mHQe48hAf9fX z{r~WEx_-Bs51!uPrt%L@=Nkq9;FElI4a-; z-Bg2TFyQP&e(>R&byP&NaH@998qe|Coeqs6r3hqOJmApo!Yv}nqpDJFyppj8T(%tY zLH1SJUQXoLF~fs_&ga!e;!(6Qps%BcRI6)p&ee?G^$MK&QMREz5rA;|*Z;!lW_|NZ z2LrlAtmije0cg9DSw*R2Y!kTX(p$aZ<%14vVnJ@GIE?yFR|hBuP`i>9wSWFXP!#N&L1^-2hg z|I-zcKO!_UPbfJW&r?l?WBSB6T|ocihVJQlEm^>GQ--0Nrh{VmMP0x|-^zPLM}^cB ziFP!>`7Dg!M86|pdO&3nHD72X+?raU%)%MH+e0%68-}}8oaC`R;rkZZc5QO9(JY7c z=TLvxg{(##-8pyhdO2a&Ha#2@orLiyO6sqDjws34V!vYlW$9Vq{jF=5aHBhwtJwC< zS*)12Rz!8Co?{G5Lo$%J~XJFW6LKd(O6w?%y2^I^vHGqdJK%D z@b^=5PYL|=X5iW+3swLllf}0IP|!gJM%4Ceq1eDGRfjAk(0il60(SbM6i)J|$)N6c z4V`e=!MV=8e$rCe6Ztfh7BtBO>+!-%^MOP`95-Jzvt15rA&Y}4h;aWHR*fFdT(IwGIzr{o1=>Q6BYYv{X zxe%Cb8mpCVLiI#^O(W-@ROu>K)v5-@ZQ77w1u&)^NoVMYOS>$I*WJYI}MPTg;$f?;;8#(cw0b%YUIyU(%TIO(D%}q*B*9>6xJ&+GUd<6?i z*CEm3A{sXBA!glqJ0PFNWw?sS<^J~zXRi!Q)TCQokW%HjvcIZ z)baB2Zvn(x* zn2?ISo`F|$P;*?m^8;tl*OVsJ#szMnwLzx*)C$g!<3qpm)knr)v}D(H*tO0#&^ynU zj|V048@(U6CO4NWG4CC`9-?+38&&#FM}hrD{!PHGS$0fJtl9U1mz7A5Xqqjm-#UE8 zh-5R1UCFw~Cs1KMXEB`_yabX1;q)>CpyP}Nk)5ch4r-+a8dyy@xP|xP$ihlS5!Q6f zV)P{`xD*edRR}E!3y;Vma9Ro%9VkX60*?vcj`9p1Qf9x>Sl#acVic+|n2Tz)?IFQU zR@d-sOW21qlmf^$%DZW7-20<1XQ1Hvkf8_#p;s;yL5^ryD^9DxPx zLROER&JP?(sHa9Gdnu=h;U@qTX%I)JX43nIqmLnkbay+V#3~th(p8!a++Bk!{=?B3 zJ~(>ya+|4IR*P9FemFR&!>62}EfPnT4bY~^gZa`aJb-7Z^lbJ5J!IjYW-+dVM2K{U!G)Qq&CZyT8gMYLf5e9JHO_oB188xM z&)I^`--dy{vR0x5B`h46PsJXa2q@A1&^tQ0qWl8mWZ=pJX4KS|ohf3Tswzsq-)L{l zte-ODOmtK-&5>wx8oX=re{^aR?YSt2?Xjt{?n7|cAWcd?vL^j*<2w8qd(?3I0n)#R zx&zyS{srkZAds#E0_h0wXwLquE0@%$W%*9V1>0))9npP3#F&_*oQ$4ki&n?V8oXGA zkq<#414B>-X~TDCq;zuh)FB;Mm_Zt2VdZwzv(P?k<&fXKG>X6f1JZ*Qnm#}}EAj2x zG{)KFFo&|nvX+~tW0DKV>7mv3yKxyEieqH{?f?)JS?AiRPmOOi1z7t06S{$0O+SJ{ z6@;VzggM4Md$)~y#N^RYY<$!>>rxR&Exs6@*wMMu??u%xgyhG^)42kOw26f2SYtuQ z_e21I8;CrKPONAP9Otq9GQkvQx_f%m#w}U&AP;%ir(OA&7-zbzC?_+M?)doq4y}TX zV<;(uCbU{UQhd9r)MX2=IZ@a&;xN_znia6|O7*dEtjzYukZc-I>f}fl|CRU)H0hM@ ziPi`qanc7#2c0=p2a)t8zE;rPY*qk~bYk(+tAP0A?wI;`b+nbpo|yQK4(-ExB9gq| zGB^5-qS`bxPk6-TcUQC2vgu;eK%8@KAy-}g>zQj1N%u5CTX>d^(2#4Y(3Ix#@L^^5 zIMw>i)^wU0JHI)1Q#g%&e!e;RC`o8qftwA|>%ONUx^Kcw^om~?=GnNdAltjV_y5z4@Xbh`?{5@Aij$(m|=kB6A42G0pAtN}r5^XPZbxgo0pD`#19!`}_P zjF+}%yB{}r9i`Cb5C(M#l8wV5=2;4La~l-5ne|kG9G2{f1_ z5Qx#Iu!zzBCr5veX9ijKQ++=e(*&7!Sif*8r6XXuGVbPZ=&TaN(c@s7p7~CGOoixe zqiqMr4Y2A6FuU}zvYdc@^$j%2^Wl9G?LWnh22O}8;9ihXEiEKNB?;n!IQmE4`5zp8 z3m6dov0DJ*=vyBgJvzSwG^-I;0*gwKFb5i9 z$v|@-nf|>ZLT^(#T0OzmZP_Ksv_ctGzh1Mpz24wZmp1pMlc=k(g9C3RHnS#5ax8b7 zp_k!sPH|w7SFL{vm>y+Y#w1o^1QGN=y}UOa!_$05;VPkrFCWWolvYo}24%7EDd$6V zr&8HgtV&L8PSPw5y;z~Z5m3KM2+*Ey+_eLRCI4LAY~U@?8k#P@2{$ejm?iOSZIWZ2 z?Wu<6CWq#W#PhV`k=vstHE6=B3~K~0v}r&D833f_=qXRvC>LN==I?`OVQnBG`GfzP zpl>9D2>Jn6n7xW)v%FVI02hV1_Eue@LZUl^v6l6+Nf^jcCV?C!{dlJ)ILJ{tgB&GM z$!_zN+*I!#mS0_vz(hO}mGyEW9AzthaYfrCbaX}gX@wi=dx1dLER=dQ$E8I6egSHJ z{c3qdOX{SQtI1=t>AQ%A`uZVc!2|ZIw&jqLswfup;I&(wK8(` zV+=8a|K!v5SQWUYgy8%2;XX?m;ki8@?R^%0eZ+A?nvqfi!#eC!l!TJT%^32-#UM5K zT>n0SWVL3LP++Uk0|}tVP6kRB3HGUK5LUQ{1gko*B&Y^DreB zuBdB^IO{Eh932wq9h9f4pdT)+3NVVQht*>`3^;z%@M*=tuoGPF@QuZVf7gj=GYeVg zDMgk)F%so4MV@I!&6MwvXrDu;6j;pPH_A!uGNDuA5CSP4N08#-MF1%t_kR`7KD^$m zR^yFD-vNZ!Ik~m3npsQ>EEqKAX4AY9|?{blmY+OtJMghr9D^ecFliw+j25^`)G66CoiLnlV zjcPZR&366a1dZ;ifJIA%gr2Jab%);MJ8{~dQ)9GnKJn-AkEC0m0Mqo}1pPxc{ue?2 z&+KIaeGeV@v*+l?@XLs3$UR;?{VEytzeatbhORB8i@(D6L~+I7dP7q6kD)LqBJd^YzQ9 zbUwYu)<82Q70ZlMe2> zgIf4HgC=dx39l}!R~@}y2tHMS!C3of9z7MwW%u!u8|3*!<4k@iIx_ys*Z9H*{NnPj z3Q0nN09tkXs;tW~L!7?%F@;fmYnwjPbwM95bl{D|}h`;_hEz z&z9&4!DoC*#y*zBkroq{&>A=kn&NM)T~Ns1i$Eu0K)XN7i(OwwbN-4>_q~U#)G(w4 z!;4=~R1l2ENNITfO*^6C=#7u}pP|0`o{U+XK#xu}BB)MY5`$^4|E!2rMqg7l@} zO;&35{q~g!ApV#ZpXn<*C|RLC?9jNCuKX7an4&m_a;N*o_?q*yh59~(Zgh74^XsYZ zn`IiRgRB(W`2){)-gkt7l|}pi9UFa6pntIuCkHp@|Amb>{&Vbq)`NcZu>Sw(SFOJ} zMc(GrV`%itxE9H5qZ52LWSw6DOz`DG*Ct8 z%_L%0b)M?!v5j-r+tY6N-%k6s@2lrWOZTo9ZH;M7Z)N^HZ||CIe;*%>yVBlXf7$a2 z!nX;&tv|j$n(+F!Y>$JU{Ww32(zagrce~8z<;;J}kBUFef2gka1w=oZ^8|VSs)O2D zkDo0IhUkv85&w03k>7Z~zuyHxr$<-6KS$GJJ>6Gbzc!gutB7;&EC5_quYJ)Ty}9gYz~X< z|5~HJ==VHYi+8IiNT0Oj0H#`b_o@*4)ICxW!DB&jcvExGGz_-(=_vW&lX(Cj2(16p z_N#lWXh+^OQU58^=I*0a*5;rpOrBy#?qBXcyIi27pF8JE_Xwq-by1ZntFP-vd1t|N zUp9#xGo$wLt3ehHcOQ4U(zG99GWP_WiY+T^J`YbCkqX_`16;8ep8WWnOO~O`+zqak zjSHitu+3l}$@cRUfMKYdmN9mgxBK0hsh(c%8VQ6tU7e zfTcE`&X2tO2}iMOY#ZfRz8te8;?9eWd4M?hw{o<4)5goOsh zPmSm#nhKWK(WuZU%mr6*0h>iq-lj)@+8q>HTW4gs2*1>A@e~CQq|V0W&Dk%|z57`g zmK5v%esmEU{OAW36c#3UxD@t<`2p9C?HlF|X%{c9Qc}$o z?hb7IcdKWDoJNN@DE)QZwj-Y@DW25G#@kpZr>s5KdpDW8ygZ30&QtQUpTDUJ%5)Et zW@ORlHh^|?)P8cv{(T;q4PVEv)9m(i{XJjRqn`=)-GMUiRn0>cAee+x_#RsR!IOr? zdcutt6(e|tB?}$8=A6BX_hD$4v99)GDHiN?2%5Q!T&t*g(_0`Y+VbYg=o>plhs7`i zvR!h{pkGdfyhz{V9=WBceqM!TjAMzpqvCi! zOe>IF&Y{BnEU*x0tE2*RjPVpg=>}9-Yu+Fvvql)cvu@<2F8pcL73rQ&yLoD~V8^3O zitYRPLBSl)rt9Wq6#`dLj=Drq z5_=9lIN$7f|5*mquapG{B^6(w1LpaC0nM6HlYjguNf!A^3jvO`w zt%*%^j-YwdNGt{mHzvwwY05s@WD)o8P*CTYD&Y}K5etFRPx~BqcNO=--)cNel_pAK zz$`aEcgNc3@|*lBsSanW^eF=M@}VkAu0ARpswja)BVn5{zz<1r2`U-XgLXf&S?HO7 z2!7bOC;^f9s_0tqM;Xk|yz4hfl;}}Ohk%EhpE<6qO6hig0pcuU4`~T*C6TOt1Vs+XIX> z@DigEwAq3UN>Zi(xA6E`p;l779I0HhCWjz~638Rg@dOXkDvORV#;v{Q&ibT=qr$H- zI?EHR0BxEvwT7_lq$06UDJh&hQht55Xe+lxT9e@@ch@9iOVTtGuH>N3f=+Yi_f9NB z{k*nT-9Y7Of)NdV?#3f|OQ`ibQ*Y8Bsg;Kb7$;%!_HJ(r0@&`#^s?UABJN`ITw6G072@(wIen(AjF=Q|*{V zXw}NVXz|a~=Z3E&mI&o<%(BouYt6LSpO}X_xzEsR_7(m!^{aHxjK0WEtKLr{OV$)S zq@T4^Wm%wpjdcjPs^X4d=!>hkNOX!PjU^$#ui)8pjRp-6NVSopHlh+G7#&a|1(0nP zDtBgC0bR>ha$L(4VqBx=E2-y{s^o^{JJ@9AavDe5=J0B{B$q(X1wLulgIUXQm0j=a zyZ%X-fg98n$2a$R-vDCI^pbt7M@*k=Bd9q5+IIBPhU6C? z10|VsGx$o3gL$S!pFIp#mygjy%}=l5pG!2>^rT#dHks__rJz#H3du6P>m@GghuOC1 zkIIE&J?BJI;pJ9U#^1$yUg#tPGN9?SvQF|MBF(Y*k3OdPw(Ha$k(5oQav5S?A7c{+ zD(8Cn-=#aeN;cYm(X<&B#$Bm-F8qO;$2AfCWF?d##Wlyn6;w4F>yX?KV8*88W*Q5^ z=B|es+pLmC=V)w11(^9e4p>cfP7+k`MgNecE*6};w168~u1#q!bn6LW{DKL%KpPV< zK@JRvLu*$p;UCT%I{QP=rqz_rML)|G*9L$^_{WcqTwQ|7=P2d$R%H&?3MgPO$6}9} zKIcO0$Bj>8zAHwbz+|ft7$ec^TPGvz6GWCKnDCEM51sRfIhRK%#00_=%Qj^<>>U6U zjd#fr>X#0tE;|fh2!?ry+O}{c>voNop@XIOf;_W-7)A$d7qUFOcNn}`zK*{sOrMAq zoTtBlPT_dq5ZATFtHNZwicLAwgNhT5p|TOVd7)hrxrq>T*33%7M`-ay*~&0BnLY;f z3&#$MW&ZWaOch*{08lGa>yj;dT?dg?Akp%HFO@0oND?BI^Ufsi;QhJ~+CtWYgao$| zOE4kzvklip3cW9hA}ivabXcHI4=*akXXxAg*di+_!X=QsgP2iRV}PFFsOC`VEnru} zCP8|8HhV(zNy@BXTr?pOJwr-K+5vW%Xd-eW1z()1Rc(_~1~ZBkDRi^K>kFLVPdeqK z5cG9$G#@Qcsb-3ln@kM*Avy<;RFM1_IGV)Z)IpYB`e{{uxg$XfW; zD}-YQ__-DR6dkj5u~dQzt~S$dMxu*FImlRUAq^#yo$TqbQZx^m0;4Ly0kJt^)>4RY z1Rc>b-1tums@DmAa@X@cGs`})TQs+)bHc}hRIcskzFlR(O_ICRnwg`V=M%G;nwZ!Z zy8+pLj&qt7dXAiQ;&4zZ=jbWLzQffUq^KS>SDTghqqPXt2DVTG;m9O;8xR8#!aE4C zyy?&^(e-72s*GHsqs1i%ps7%_zt-?5FO=&)-ZEQbQj273>^iR$G=Euuy+_SD_F>oi zOA;Pk&b~z(QPmWbgI=;3$ttycSSn*Rys%%gl4G2+oGrPoc#c{fJrpo|sCAKnG;yGL zJTnku&)`KR{=@$~=2a+Bol8B+tmHID7$4SSi$?*Wo_C(hu$lPa+;;kKSge$5oG)=z zUH{voN=aL^_#6N5G)Jq-2FF~=c@4Tq?O8*&(i0GIZTZt`4VG$)3~WMh9epD2kJ^j{ zvqJS9tOi|*ki!AZFM)^Z$w#%H;e3JKOai^1Q7XR49aCs>V}s6#-+?O}=MYARHqiXD z77N2yG@9blK|P*vKdS=M6uR* zG73ECM(4~cQRFiS>q_J1QQDh>L0$|j(qG*THGd|1NY0XNMy$)S1_MF0n(m2@UQtC> zBi$TVqIgF`6b9sCiP3v8!RUb&xYGi^(47u`PBW?RD(y-AU`g(B^JVswdH0|CFf+3uIqpUUL;x zQm%wytOMG-HmVV84P;G&AQRIEWRe6yCIbIRAKTy_D=v*K>P^U~$8jXBnq-^!qi0qT zRV#f8RlB3o!?&zLDibbZvS1anH~O{C@F75Jr*Q{6?y`a04|OP zzImC(K7MV3ExgBD4ka!sYJko25O@E#u) zo^5#O*^~quYLDsLgJhh&XgFeSrnhs{A2d9z)Q=%M4sGmZ)qf-@K1S-S* zoDj<1+pbzTc55^}%<*M7wS5V52-AhMl6s0vBNWtX7>iSZULhA+ch9adHc6!ut~<#_ zsDQrPqywd-$fa^lFR0-1pWXCNqIIi)6y}W;P~G*Km@kz?7x{(xL1DILSGRw_)DKT)9z7>8U^RN0)eMxQ4=kX~X4D;EN9LuxEBmaG^D18gjj> z?52r<)CKitxk|9j_r%a$n{0zLw0h~~yo=BkCiTB1;X;3eA`oeFWO^qr%7hwieiH#W zZU@DT?>;*xwVY)ut=v--)LWNUi<;?2y6?8dULT-LvmTENHS6B+N-d@+pJ-U`{{-p( z!k))=#^gl~3!>bOy$@{@QspRsbm%=lK>3gK$qd&@xoKRcuaEr@{t6v^w%DKt9??i& zX`!}h8`Md0-MyEn^{2SM$aI-gg<|@?bGYfYn*gvF?LSAL!&N9@?J#VsH^;a;&M}0d z!$}*wvd=MOv-2jX)^5ae8{CI!B7Pzg(Pgc1*aX>{OAh`;osr*88E zR~fPkiPkjd%Brlf#n^iMv^^&4B-0*BXC&S%<5i)AmS#1Qp>k^;q8s6(`-ca!HXO(!}8> z%Fa=%g_^T^>|PEcP0Qkj6dQ|ORUgU+lZAV<0i$HSERd!m?^Oh2pbbjY z%1i0UbFKqbU?9AXjYz!BzlbXUDHc)#%}h%cgrjtTN&(bvjv^0C&17o_$yP7ZlPM*B zvh&c+`ee&mH1;IB9M=)|oy?_2w^2KwX1aTskO|qdS{pgPNmFcr3e5(#V`NSRfz-1~ zTR1y+WjIMiTn3mpe)?aqYGpto7N%lmy}u}HME17u!d*&FHuVdxy&?gk83*CKJdMNE zcz?y^c>npPE%U~5`mu5{S@m~%`n3ERpQeF8L=b8!91s>k`!dpUc*4iy)MblNaah$) zDq!g5Xf8r~TX`w3WtjSfCPOhpERZPvnE5A;RG80=2D!9pmn83`Hw zK-|Nmlakixt#F{*T*1?*EqE8Gt(7C$RY^=?p$i-d3DGgDlxTGNLkpP@pKv;~SVO8) z)WLu{>Yx`)(#cA?xwFaELK6lfW^P2|j3puh1?r+Z_snhcUIXD)xH7^4OJjjTgK>s! z2d`1{nHpoTP)U1NvmjYG>iOBfJ#2tN?fKX(vPQ=Vwb~YC6zXJcb7;?YS*t$zt^&7b zsGAnn6AYKh_M*GBBrx(^X2bbiF zhLdMfZM)>gNMN1=GN#;3gR=aXSTZ02D2r5x9Z_)hIcTMQfuyx$!ANKtRSYxwDC;lp zKCH>&br851H@Mf`+T)@UyVoUZ{8$qtD^0Pvd9Mr)+P;pF zR0QL*mSRIF7xuqcCM0&&Z+BGH_st)ixjOL%_`76G|tyjbexekgZFu81xH z&LkwH`~II>94MrM3m#l}N)1|~yb$szC7%Wdp&`uSZT0MamwS@v2k+xa5eM7VJ6unq zH#z&lZ(`1QA(TcOpNA;i+UHcOTy-_Kf2#eR*b&0srENaa6o zG5oU%gMu<7e2kZ5IV%4Vwx^l`g3(J%8$iSg{93c>5(Cgt!eCPri8(6z8Hd;CMXumCI17SzHh5c!+zt zF-T%hLFwEu*dLc-1_^G_A>g1-FgnZ3J6M8oEPP9Y6te9=gHjKvZ1J}EWM+&U@hteO z2Ugq=8I??yppl4>m=@NQT9IZ6j||#639bw;)_F=;F6C0d+JULVpvZ!5#{x;A7=oE@ zmFk<=dz5Z+Bfb92F(-4niamjDB@X@oD0-Qz@%}-C-bjnjVTDr#S6j_M_EmN*rqMaHpSW%spJ6*?F zvpS;@`m_bqJhv#0$`a5%Y)0jr@^5muH=Lc##fC%749aKtHaN=m1t0Pa%>yBpnB(fYZ&1S?N8f*aE8$;N-DerAITO@)t#{3rt!{E@D2O z6%a2!B8v^yqVu~vo+g@Pj?EdNGgvNkCdV}x+lazyxCypUO0pgJDUED!PkEd9 z#&q^|T47dmO_PsEQCp{QjggC~xduBX9SpkexD#^~?}WmTf=V{`Krf(@&0_ot*ZCB4 zbg_hU5EQ%6gac*RDZ`5llvD}PANv(K4q#Mt(7;2xpnr_|0D#^E<%~SEZQ%O*tYse{ zVvAJFG?4)5QSGd^MqY%I5rX>+D%rdPHq0DZn;qUN+}o2Z`yA@oGAp@;A?1ui!Hh9C z&2%i0%D z*DM47ob~nZScX(a@_Amg2*ajQ>IssVOfMS4a#fTkLlQ}&CORmg5ssQlmJR0tYPPkS zp|?(9#_%xI(;qkgXk71ge3kHmtoXo9k?3&vdS6k4m+=SkdCLGwjuBB0Qrog^vgJa( zmV@YMo1@%%wFSvPt1t2aX_tK9$L$QBs!Gc>hdw+*je#~uVzjOgVYCg1+nrI8qozZ$UF$j3+FpTN1NnuL`V!{;Au)ck3_K?9zC`z1*-NGjQsTK6 z?KsV8a#fbqvqeQ~(s2{NhV{c@A#alm)nuD}N|kAwUX0wsH$u}TUn6s#M#*Y3y`rzg zbj~w1=He17bU1VK{MO>lV2U;*4@PS<3=ZOoD>OJV16%F8`C(F0t=)^xQ5w}rPD-MZ ze|FQuEME3vkzRb|iq=LR?&T*fNbig%OEb)dp?z`rn`dvjH@f3%6^r~ zV|B{il{RZ5@rn>(hZBDva6K#UQZTeyQ}vL4UsBSw2R;@ZFQ!l5Ybv~9!QE5M7yo?Y z!+rXvu1WITT$-V-Yg(%jndE+$E%5l=v*Y>b%DF~>$W@t1bZ$P3QPNlsT7G^c&OZ|6 z!tt~hY{=87>&2Qh&yryL7(ri)P=IuQ9M4{2(M5Cln8c>XOTn8^Hf0x1$K?wA_%L}@FMUm1k!VpiRs>vg*O zhEQilH*mq%`WZhZtcgD`R@VBsei|40Q;(~V$%)kV!YYmz1#a3KNJ_mucuo|7;2Xtd1zt~z?c>uZX zLWYCdCx^U71SmSKd6T($-6@C{dYjn~K8=0b=)(C{4;u%o6D`;7o}V}`=!aojFR#;F+~}sU9oMV}SSo*QZ_G}T#^t*6@Lz2=&-amfuT>BR z*bEnIyc8%{*@CIc^nDMf_oMa$Bk;$|G|WHBtKB@AHfa@p#JVaoTLv}dLM$bE*7zPP z^Aa^$YTqw>7%Om3wF_T&=LsG4g$qoR0l6zjCx*!%r#0a)vWXeiW?K{5&5zKb2`}LjuUR2??F*n^$TJQ1p`Owqfah zE?5Od)4A#g_Mra!=CTz0txV_pmoTc$E~_=&!Vaz9sv)zxz6HM}sDoW`YKk`I{ zolhrZgmrt{Ehl(lo^W>(Q0-V=LYx-vL~bGt|K1M#ZM&#uVk$(;(+Bom`3nGeaPgZ7a=}x^lQw?7`yzv-{~(5!9%eJ zx=YW*%;Twl^j+@6(+-`Y_63r$?`rxV^mohEYaiF9%tnV&C%mLnJWxq3)65F#dqsr~|6>IzfrwRVPYdkYU?;$)INgMaBc_GV}|LT&;rYH6` z68_gt&RG&XmBt0g-Td7lt_}7c(o|e&)1$vO9f=h z`b?u>C8h6bMo8`kh+GY01Ub)(PIdQe*e-pUl2```h7R|&%(X^9;>kP`Lx~dUH>zE1 z-fwdjTh;ys=g)*#=j50dVzGbM-;Pt=D?JYjyZ~OLb3P*8r6d;kQpmy{Z>NdKqN!G6 zOox8OiTJ45c+Ks^An~J)-7~09}nV8x|5a63e3%F959m*seqDi^h zt?wM#jVMcu${A5g*`OYpC}MM zBM($4wPc=KjSWwoM^6*@Jq+N1oj>zS*a&1LQUVAvM?Pqo}zjp-k9 zQ{Lhz%VbEtk7-G}~AT6gH8lBo$|%|453$6#jeK)%PYWaR&kTeKE$?g(Ju>vNi+S<4r^N9@-n7BRs<{Bd|3leZ0MyYe>!P?@aAy(R-Q5Z9 z?(XjH?he6&ySoOL-~@Mf_W+mtvhAMz-nq~-)2q6sx@V~Bn#Fuo4Ta()_9VC~&(HLO z%&>rn!W&tjX4ZyMO|t4|*!r@dH@xVI7TPl=o(H4D+EF@`%rZa;y+M)90T87lgb*}+ zGUiXu$;yEwDyGefNa6yB8dr%4l;;@sr>FduF9Pks4Le(n3-#@m3i5l2% z%#Q`&m^IWay{qy4N$- zggUcf9n&j_oKj@Jyx-`#$XI1R0i%`9q*j3l6asZDln@H}NBcA)gc~Bm%TqzrW5qYuYweqRYXf zxm7rK975)mRTU$C#*$&N zBUPhf;sqv@+%T;X`8jT&QF}iR5sYYbbdPfnq0r=d91lIuK18Zt!?A9NRb*}(9<09{ zY<}S^MT>Rsf9nn}OzZj3Olz=?OCtvh5|rQXAX%mP6mbc{4a9~j{tC%R{t|%={ErBFT@fDBip|vuxDWZ zyX{{QOTW|F|L+k?11c*qv;1)0u}Qv{Hxfn%KJhBzH7lRpG&RBzfzX3AkY4J#W6AEN zIMbO&XJ^XHPZv#3IfiCo+>YA20h`g7%#S6ZK&%=azHbP+Hk+;+;k}|>{yk#I*}esj zsD})acJv3nJP&w1XG8V=p?sI(!$(jW<{Vp4sPfjew1Ym#vgF|u~W{hI7=$L)B7 z)J03ZHp8A^HUnA>21P3SEsPNTjh`FoL8#~rz)~{&(40dNFNmuPQMg-`j8cU$9iS(} zEy9-WdaBfr_u3QGw5t45v8YfVgFmB7D4!n?om*Jj~q>1VE@&a%5ZN%D6F4LlP$4mqe)5O7jLefU7*z zP}7*vHI>#86M}LDLgb-HZ3{8Mb67{dIDo^E#;Wcz7!zfUFr@oYD=XGf3yEoE)Sq&X ztucuu7Dwyj>ZaWl<;zQkOQO!et(Vm48|~rcPc1J?Ww?AP32s39qZ4&PX~mx)=3iaz zkE^8NT3@-#jobF-S10eLx7j!Pck2tJYFD&pe?xDa+_TPwHZ)8Xg_}-7~ezi57?1 z#NQXF{;7SQ$Or1F*aErTLY91`M3NPT`3AGmR)$=qbWY?PR_xUqDY%^M18X|d{?SY^h1OAdP8 z6W@Rf%pd=Yp8sPt1^|GS@!!;(k>P((bH?9E9{#`78~^~&Ng3OiI+-y57}%JY7~x@f zdEsFkog9qyt>IzZGS|GIlW>RJb(HUL*JgQm)|kA1@bDa*kDq@0o=+IK)MUjn)}wOh`7;wo83;< zEcCh}$LHa3bj5_dG6I#}4t^XQnj%A>`S?9CC-fOhFer!b=Cbt$!hMpGP!qnxXaBU8 zX_DaAsc1>8z$?#V{$;Q4WejtWGbva=UJbd35lPt~Gfy_X)R;>`sT1kfF^pA%QmxXT zAt5Kx`{Lzmgzp=#Ov7ut>|SIlWZ~mY$jWx%aFqna-QrykXRXRwV|~PwQ9@k)!B*#8 zQU#g_z7aFA)H+4w1T9kg^>oPLP_7#+4(U@SA8%m%pD+%2uSF-@o~&#&(<`?GXb8M;#KHu@{DbZZWe!KwSR z=i8reVJbT*yxuQCW8Nq9-45jNVDi~Ut?41)Y9Gg$e4>@tFmycj({GvRQPve4=oGiv zdZcP&<(6F1ZQ6o$mz5l57v&rzX*-6txua-UTsK_Ya5};xZIPdb2$L6t%6d*laAywR z3B~6Lz-AT^!AkF63Fo)6Sb~_}d_T?fbBaStJlB_Rpx*;6tV?8fOOyr0^FlMzCYHGb z2ZhOik%uyG9_&%|oO41SUBRb@Py2jQ!t=a=VR&t#G6FuotulCDsq#m%ve6}N_aAQR zt06wSjm(t~S`z8bi-xzh>d{B7OGi{xr=oc*oFHfU?h#{BN|_Wv(-bdr>zo|&LOEdZ z!)AJUg|7Kfl#@zow#vA;ltVHv$*`%0tTa+;Qt3>i4{plI`I`g^Gzl?PnQ(Hn5QNsa zN`(drEva+(;B2Ar=(%XlQy90w{JfTe>FJpI-7}Q~hyEaZxJXn7+K-akHP1;Ba}L7c zBAK)L<|;4#DohBMxDEs>K}xdIpilY{_1By~Td$uWGMq zyEyiDz{#g&gb?T<_p6E}uN=7W@}SgNJ#*+&HZm2f>Bb6!zTr6VhZ!sM^3#h`$^T5I zgdURVv-0t{gTEi5|9ZA-S`~-B)MGjeqLbn85IJcCVKTGHG`;o1bhC2JGIa#vaUJYV z+IqYD3A5{WQLcjz`N^(pGh2lWX`!s+n499aOkYjH-0}7~9Qm14r`;bVMR9E1)dUM` zG$=N?Fk}?FL4zv&UbvfU`ed7INXm>Bt=64K3A8Dnr`fVr6*x~quv%ExlU^lCI51C+ z-GF_Xf0*1jv(0NXOpa%7uso{)b!IM3j%M~ft_)fj1{~C1jywejUGM&&RHW3xl%?4u zr0l%upQ=W6e^0$@~EFZA!0xQIEFZ`Bs3_< zO)kc3?LONM3qj1HFqBP)snaWKcj8?7xZF7lK?j9&F9Z#q@Ic zM1$ia6Db)rz*H#g$&F={cZotqk@{}Xu`DCto zv@^h@SQ%}J#Y*Ky;0xmGUNgioA<|eOO4YbWd_uf!+D>S*3pyu4i@BYWO1t{u7aMSTr4kZ%>EH~wM(XMjY#rZtuvUD` zd87k@QYaF&40=?nTx>Z4ayu||TVNPS|HV&oxtI9LRTL0qxuH@SC{k67J-hs4!UW0? z2CqDgFt=3ilRs6vPS6dvvtq@LYk<0$)@VuhNKk?++}d@Rys&?S?jm6Gm^7s3kR z3fZ6mrx18v&{LK1Bjx?zPR=nA<)4^|c*_WdS%OOP-T9rg(rRH-Bk50ICdh`}Ax#X( zG(ga&GBeuXF>536NvLeqAhCQ;$`3j;6jkB^?KMy zRRG5$<}TS9{cNfyeapIjcJzwf9Ln#8nDoH!9;@30-AVicNrX+Y;Hxxug$iCbDDvy91^tFOEgL9s=gI5P`U>4GaB7!1r^4r6Dz5>Xs^5y z(=V>-h?s=&0gzAahe6uLd(p{k{Spb55vofh&rpx(JZaT|GJ-2R{#q-b5NBdYCMC+%{fGpP)tLU2M1UReqtWy*HQk`xGwh@tm?}PaPEK=PWR(AWt zSEJhFL5)6P{(h9_GJ8<3gPfXt+#4$6 zzuo&2mE9BM7Q=VY-Q*j{a+prxxaUG!$enu_IAwrHG!IOO|1bp-~kN)%i&%FO`-4;fOy~^Ci2sP zbb_GrY(HsGMwL9jr8Y%b?IrKX#WXYI+p(MI2+IdHl4eVptYrK4ElBh?BJ&V58K`AL z;Ja0?2D;&7wi(a1mu8fa2HFr&$Y&IUSf!#U2wNpp`a${oVL}%<0@7=(7*XLq64KoD z*JW7)X|`9heo>UBLCXCe%i!Bxsa!Jx~b znSq39=2*+I`|-rYm2Vcy+r7q<80sxk%E4Z04Aei{3GGk*Qn{0)!IJ#s)B*jyq00W0lXKai~qQ$na5olWwRY= zm&*4*JeuSBx3P_zH_eG>Dd=&Aacb5IaAl`(uxv4jL|^uD1c#_{*WfqDFG^@T1k_Die{5~NJ1hy%{X zk3WZOmOZxg*tGE!J_}`!*YW|Sw}Voz88gld9%5+Y_D(}>Rv(Vdo8I36gUbd*Y3O`FS z?2C<+%n{ks3q)jv1tOsV)IJro1}7j#XhLU6ILH}LER3TYiEzH&WeThFp~(tkhNT9^ z%WrO!^5K?Z79r3|0tru0id%u~(xfn>!aR>E!NezD*ksa;3P>Y_1k0^57;uQe;=US| zN{BFo)jdufvn|Ee7$VKB3d-n;e7Vxr|MF673?o1GDf}dZ!q1yd+|KU>o<*;?vq$4) zii{sZ2IT979}CV_-~~{4IS}2v5->RJ;Y*;-Iq>vZ;5Ed$CBxj)ud-yd#hkT|u)QP`iJ6!K!+dqA_Pv%K)S-O+FHvE8 zc!gij&BS){Lht%tm84J;+DgGl72IhsWy(yNnk!lBrp7``Pzw?ncs`SVZn?-SQEd9A z&Lt{jKu>;HyEOv8JZfr)b=zyI>UgSnO1~s+Ljp=LJhm7{-k+Pv@Xj>^EL5zuogZxw zHL(piin{|nL4C+U-S8D86G!7G36|I-ciabyV#i5oyQj|j7BH0V!;cKJ;*Af%NcYzC zlDlVpKNfJ)Y|_rAj;WB4uc#NbpHRwAkD6x;#r6Fw-I4;ea&4f^S_;_Fxp!J?$+8Qh z0qLz=9L#OI`rb3!iXu2TwVyTwvGT0`9JFbJufRpzC~Gqi610TKQ}RX(s?#iUfuGf} zR8jy(&*9ZPpW%Bp9`Q=4CwR-yW(s35A)T|E`*Fy;6e#4rAE;xGKS$4J$Sq-n@8P%{ z2u{F<=D7!cv>@6b9$4fNTF)fpUU*QgN`b|@?1$KgQVaDAy1_%82)ipg2#aZdmyh~? zDct)o7kmR^_#we3Jzv>UJA(;AjI;LY{BD*yPQ52zHn`+z6Q-B@vuowTZZ3GDW)f6X@5&E0&(R${9#&yE7_ zo&gg_UEzM}nU-mjFI@-;UD>-?v#{R{{Wij{*J4BlYgL{hYhElPM4UQ_LFMF2qf31@ z+5WUVy75z@2|+H`s_rH?Qo`Rn9mDJnrFx(O@}eI`P= z+GgL;CX<>R>~%&VAMG}T1DU`=nGj_L-1pG$iO@<8)WyaI&MZ2Ah@YpGeX56hM?ttO|{fklXAX=;uR z$9l#V_|xxO1o!AHG{0F=F!7Kjk)mKmVkt?JqDsTy z$WT&p6_EzP6Rx|gA~#;!7;W!U3IzFq8DMp2F@pZE3>>A7iu~T$zEM z*H#E7hRD}tcs~9%J*^oN3!UyoO+W0{nMxyg`_f9)Q{~KyCAp*0znw!l1vUjTC6K$r zkKvaM;el}Yd5R=A)!&QCz#px)1+DaZ82SRMvJ%mP9p8Znl!4sJ+`^tnO%!MTVJgGP zKr=!vcRlxTPoaEw0nY|XBJpDt%9NoImbN;bydPXg{PTJ zo>BPH0EWEkx5bPQ<0OPYh$;Belo^o^8wy?XdDcR1MU*G8|6hkQlTr_o_)2 zWc=&8KMhQK8!)636fqYFD}75dvEU4KBT!otd)X!H3G@Lo)R~QS=U4~vBLy-7A*}

Z?9FxRS&R`5L3^g5n0xgyCg(a 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ˈqRNMGZ?`DNn zk+RwjN8&Zhru>-A(Oh1kz86j@&GgB?LLTnff(h!LfEy>f3bjId8Fwfw+}59H^w2uF zgZ;sy825TH(}LpX^+T~Hi+cM`905f(UFSs0HSUPzDZN6NEH??P#IX@|mgfK8PA4-t z6&o8~u$rGUzO_WL>Xk8T8b<7VC#;~M$kC+%r#J6AIQewhh^JxN{gM4{h{O92C$ZA7 zQCQ3@U{CV+;l)ygZ%Y7u;UKjh7*Stb!@DV=FiiNYJ3oyGDpZHpR2*1#x$;=QkLESk zj~Erg{`#;~SzE|;3e$#UR#F;e{F>`M9&!zxl^c>QCmMrt=laCrt@^2`>R__*Bu&3s zEP1)IIwMEu&s1atw>7aq9n{ z{a!%j{hQn_usjuy3FyzV^iHg(mUU|OYPK5!8Kfy?B>dpFlo#=7IjV}k&dcrc|4Yj+I5B46`dOl-H)VgX}a$i}6CYm3I>~$*6k4se#FWQGP)2sg! z8o818?-UM1PoHP4oG6-8WPY~SapSiWCt~{u;5?2anDRsyB^Uq}?ZCA-cLTlT< z|NJ{>Y1}QDqKI7KQKmL}1(hHkS(xGB3FXjqj_2E@24fc|g@3(Sx|S-3QQeLj_OW6Z zJRQx_js1p=#7GUjgjxt1AQNl-b|u449Ef7#vU;sk2*S)ThH&W*7YkCo)|bPS_Vc*C zVZAvedla5oq>8>RG4=FIEI}1qH`r}E

(lWUej@J=Yy(M^t-gq#wgZp-BZsXb~_# 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%0A8%SMnBpVyhneh_iS3##|z%g%gew?;p?Z|YN- zUfV%9kF00O9zUXPvy`-F;l=saf?Da7TMXG9IoZ2TL1&tn%8_q_avKiUKQd0G4B|MU z1!2}FmOZ#cMxjsxV*>{~|52pM9~0mzgxv`a=F~y!lGF7ztXbVw+=U0~fWRv%pF}qziAwv$^_o zF~x-*%z}rQUWv?|te!wj3p|j(YVC9JZCD&ObI(pUtDqdXKzz?Y{%XOG!_*l|^{MF- z?e5M?eJY4ai@;!4rIZazr|62y`t)I7$SNs?7tA5$#phWJJS&o5Oivgz}pn{-{2x(i%>(G*w=aw)W;<(0qxp z!OGU;KEo@Hws9iNmn_Z9?(wmFLUKo$8X3O6hLpY#-G>a(aVh~}O>67>@NN$4eYMHv zQuR5a7lO%x`#=U4C@@iDJlf*c0T-t(X(!(co0Y2Ul)o{?;W4>LKmx5bMOmrRLG_LF zE{!emgQy12ES~4lNvuGf#a_pcQN{}Hw29~kA!H1dUGP1fVpoU9X>Oh}-m%(c{@hIf z5ZQr=eYP7)2N34NO>x3;pH1?QWXt{r&0(8>r}}l;D;Ejg2;fJ;wE!4I*=WU+IffZz zq*)3OmbxTJ6`&D5bPb}lfEt0>OxAw)zuLSf>3GAB}?65kgF*39h;&LrPKRM{q-YpebNi%s2}i4sP9yG1$Fl$wyNrB zIfpprkBL2DB?qBQ=AYJmsSGC~dV0$|3K6P_Hs0VHlH3~6sAn8+W$up4I=G1?jKhBf zw>_oF?A22814l#Vw=_aoisJeWg5mBg2rI56_)gcKC3f&Br+@IdhsEo_8UKQ6I9H6q zgrsq6-=F9#KFkZ=U-5eV>iq|;U2oV8zPf#dPvm;a?Kn7(eal6J2ZxccTIbs^7Gh;V zz|(@yUjipS3hwQ!n<{=c^p}i#cBGal!k!@Kd3l|waLh<f2$Go9iQw82sX9BSnG z4wb~@Oqz@XzcGwYs!zm4qO;-g$I4=pPe0~ZsW>Cmaa&ul9)H5j*mVlHCLU1TSO-$( zz?GCL7?P1%U)uX7%AK3rG?7}%s0UG=FA!bKehxUz^J}p&1!b#)gC7QknyiSA9`mCK zhQMsva#ZUivSA7I!fZ6)#o{t=?;d*3cj#U>Ag}#+&e9k+LI6*AV{VzL zNKbYKIG8t{fLlzG?&(A+%pc2pTw*Fx9~o`6jwB0rV2lcIrU$210lZRcp_g$@fZ&K_ z#u%q*z~!{B<@g3Y4u)F3arf4i6Q2WR z_hiLfsP;OgX5`7rajP1K)o!RbO1ZsYlpGH;3!*2&VM&XUaR(y;Ykr=nt7(9QLD?|I zC(TS)3t(40(p%m07$^^k9rf*NYv|TDm(6fVGi_B`_d)DMzmdi`SYF@HHkRuA1W7AO za4EEH1xf_%#L1=av8s2IEEx9oh1!Y=>>S{wsGwHv@502R#^8{zvH#$HzKqY;;?y

#`p2bb~~e1v%||r@aKLxXpSuc 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

T)e12<$fzQ{W z5KP-BCrvRei=bLkVf6i(`2KIGv1UF#9Nr(rz}^Y#Ez7GrVd>;5MAB7Fa_Z>^j?tXRhgT!4_?+N$e7@zvlFSznUrB$LSdrjY>Nw;>;m76XTexrza zC<{?YWPvl$U%1FFma^J{^{ES(BW3*MGH=^oZX8W321Yc!i|B{N$lX4IA)tX;7^Fyq{`o` zptRl|2Qy8u(CO1X3gs<5BzWi$(!xO^i=Zw(j#Q7Y|DuxKfx44D`YRop9PaK0Irxsi zo^QDUp>;YwzQSf=TgE8jxMjDT> z{$(2X0*%O9ZY<@WB`Gl}<#kuO^9ZiiG8BcW_cRtZKBq|t_;=q+aS2GxuB(BD^488{ zpOAsmk`+Smv`Io`BcRj*KX9mh`H1IU`-Mql7Why^Ew5@QSAlRL34TrA5FL?TR~gJ4BSj$Avty{+C0hzpw@%**MHWqvo=HR4_nRH~ zI~sDiMNcLZv9v}s)<9?G^Gr~Vk9o{-i<(^I;nnbjo(Q8~^~GKHWh8_r(?4x{)Oyg7 z)=z;lcA!>YB@_ndgLuHzZkwk?1l8+<=u3T$cusD`B8#reP2?=@+w^D-Lak9mN$OE? z+~pr^?r%~zjy(Ie9^$aWW?!bq1E^@5>^v=31Oka{YfkvgPt{?eT3e4`R2q7tr(7sM zLYG#LmnTdJJ3gcg@e8nTor?^N_pq_v58&tR;oEf9BLRTrX<({iYA-^UQW3#k9xD%c zyWhX04Ls#PF$2qela!S|FMI6-r*;c9ocANvZj`MgK!MbrqLPMFAY^?wkfTf_55@5U z#~?Wt-s>q3B4Ah(3{G_<^|89>%*JEk(jhG|J}O3h&;0!3ro|%Ib>5#RnJ7-Ot!MvY zLJ9zJ786%tBE*IaG83OOAXP>_!`a@woL6tg^Q)h7#e2DzC%6WKCX{mk?H8H+8PDLv zAfWF&W0fU43;=yK%u)qvI*l_1CuXyg=s<8@bMVJI$!!+eT5W@bj)Ks*O6aByX_+!f zHJc)5?P>BpeukmsKG1%@2yy$@u^n7Hi)u3XRkHC%?3{65?nV;v*AvRhWIaL1wc#jJ z@!F5|qXeZPu$5UL#@QkT22j&r9X$Y^Q_z3-&PxKvUU8HZycW+USkYDOIXohZre<%- zQ)USDJ$WwHu|j_y(I{h0(&DDTB_;+cE2ziCX)pB=wRtHxO1K766MbgY4rsV^`ejRe zfZ>iF5(#OU+Bd68=?v?-zQ7-Ykbn^iXnn&m^fy*lNJ=bGpDf;gu}#{bw5tu3@hB5qj zns|j>1&R~A=B6$MKC+0baprS{CYvFKSlEDI+IBfU`o)XLg&Ws(zEi*s8!SO|0P9Xr zKJ%N_%?;r930yfs<^+CGxl#&fdu7xIjh;BIyG=OnHc)I4SMUo5R@Wsj6xrbp5~F zd&V-hI`GeLq3w8mh-u>_>pKZM)LlfAln=(8`bBhcG{W_38S~|0*c~X;7?JO_a?x_f?V-ILMdiK)Y^CP(Q zHqWrvUh<(yNfw7SC|SeJ%GSYz;yG#ezbztit<9*5=tM7opo*864zxyiKEb*tZ~4%Z zE!*i@V&kHlt(u$HeV{%}N{O*pH7>%dh__a$tcc@YLfhrzj0A@JhF|)-3$q=DE#cDy zaxw358PJVmhgPA^;m1c3A+TuWnL$SJQgi=!G(6kmw|)nI&szc^aL-Mynd)fI>g1v7 z6$Z7eu~pDvH#C?Hvsw6Bv${Q8&M3cmTMrN|H5lMFc5y$^x4{G6Y~pU({Op`8Mf1u* z=)J2bcDJ3?LBGa`?kIBmgArav|FHwJJpb0GQ7$KB$80O`&mu8x^!{T^IDX%R;5=q~e7%&wHAa2b{%M68Q~w)p=aNH2=X=r zsjWV2(_WPVngg4La*DPl$6Fn7iqPx7Fc8F-?R3wsbxNN(o5mD4AHVVV=fGUE{!(XK z54QGT_PB(Rj4f$FS&Ot9HVsg3kW-8)QhUj-xJ*m6dsIkj3Jdwh=)#=wOZut+R?=inIgjJh5bZ6 zZD4+qw|E{*$2V0cvn5HjSZ4QW4>GWcvYzDl4B_d`xpfPl$BFsAD5>8V#f>!ykQVCc zhaV9}8s>f8O1JI#Q-zHxw`3fmx`#75DFUStEpWXt+ryD`7nJtc*W8Z#0$;8Yp@eFo z+OZc&b00eP79=Bin!o6mxN89DKBOrDVM+S!s>I=(pmgX=Of{B00UjKQGv}tq%K^o4 zEjBg}xbF|U{6705T*UYGa?qrB@BDg;{3&Zo7o*xWbb_MM1Gft99`Rxmm)wo_FT8wN zpl;U=Tu`o(lP!FMxC^hjWi43LbETUdKPyHLz zi_)~Ey8)15Q(8Afl{q#-jrd}}wq&o$z?6U$4pqNXuYR!5MC>y1et)6lb(2mD;F7)@ zhF|uLX!Ypp3ZVunXGHfaT=w}wzZEKkGg-yAiO>mYc`tXulVG9${gJaNFtnkjeZhbU zFUZQDdpf%uHH1+}kY(&d$y9C}JmuE=4bDX$zjV!pe1gXYK%pxX@F{2N_8EQg#Sm;K zjloOTGh_Apsp=w74*Gy0nsD|mnm`oVb_v%uLgg~VZ98Du$>m(aW8cxLtuj-JV(};n zx~|nKs%ev7U_QHhzH41I%k_X$1DKJ<-uHy{(QV0w?<}e$VI;W=hpG46Yf~d!G9#*= zryOK)#e$xHQNThASq*I&bGdz+rLrdIboB69nT4icYgVk_GI+)tI8W%Cb%JD~U_Xfw z3&36>=$5NlzBs35AVN?l$N06;V3;5}J1x&tHyuu6Fl%aj!X{6KIv7%gw*!(6!X(Z^Sx_xu%6;dUK*rW5#COGc;Etb+;*GCpg|2$yUYIGcaF2u(n z=<_rZx0cG1qg?tz5j1n)3&4#$Vd`cN32mxhE_DZm`e)O^SWVCZ_8Tyh_3kk12{8Ti z$x1utt-SoA1vq1j54hm5VdMfTA<7VFTx7&8@zVGjPiGe{X6%t1P?h$;bE|pklLiw} zyfOmcvM1*|@Ae&RME&t`jpzq_t%0JFrPR+$_-nq&xYd@~1_^p$uOkBis_tdq(e(GCMcI`Wlier!=)JM+xu=CW}YbgzDp8 z@deaefmM2SN`O$rca)8<5o{(~@>02T8m9IMR7mlKuyP`u&d_|n+?l)65y8;GU9+Gf zJOnv7Hx^b?LCSs(qe*9b?i+2&{(9VI8eK?SQDn{}EO#wWL1JdA%Xv)=MF3T;8)kB; z3SaNcM6`YZa+f&iCG%v7^S~s6<9zINNE1j{_iXaGJg~ZHaI8XgS5SbY|t$6|QEG0Gx4iT*fH2Y#iMMna3h~ zS%=bUp$-}i@D?_GMw#4rA5Jo7bYEXeN4IsWNSgF(N+* z5H7r9q>Z1~`r>^8bgYo5i1o>*O0}DI&3#VnYwUM8F9y$z$t87|R47l-+T6K%?Z7?C_wht@BPqVd zH$$wl{iwznq@Wio{TN}lOTe^MQ=^8hlUh+8Hk~fhc5m&6M7*`!eV)i}H`RF~_l)rv zkmxzoo}g@{ITfA?72w;%CEHB*iPEt-Q!fKw@C9R2;%0g4K(>Q6=g>!SjO$O)t3rI$2FP}8>}KDvwU5gvV*AY&-`NX6B4%8W15v;$Mbfwb7n z2GZ{{U3f|`_iX&p?r^hSXZV~2wnr%c{>A%-X8>>U++D~RgEH)k9YPzo=C$0Chig+l- znfctIpNDOIbWRRVq{1JS2B-9=$qO~zVbx7dAk7$^=<2APR#$daqwDod$xcefdEqRHL zMXK29v%1JwY{(zD1BsDU*mD~K#4S+GZVe1vGBFaJC+z#YUxhe_YHZTEOx_sIz}-d@ zB4#$-Dkj_;Jm~hv`Y=EteB;zc?}RxDlHrP3dt>usILCL&phz+K%N2Nns>!eb4yqe# zcnCcI)-lZbPA>c0Bn9~V`HT)Ymmkn!KN~IEa1WuP8koMBL_$VPHAk5$v@u3_C_yl- z*Ul|VQFh#{p)-k)Up5kdSCV{uX~i}gIOBvVZiQhuzsVZWn^B4`)YK|)Voyr`d=k0J ziNj;0t*DdA1KxeeWQZ|RDkU4dUL*rk5>y7AQ74Ehsw&L}ox5q6UD%kZM9MRl4;`q? zIKRoG-a>!Na#tG3Yd6e;O$Dg{r|F|}?Bl2_u>Nxk9#;WB48uJ9+hMQp*L7ftY?tuc zhG7(2NxVT3zA4a}SME?fMS(9D7nQ{F{$_iMTS8_oYtxGF<2)zdc1e%ON7Vy;E3!;t z5Ip^(BI$o1RUu}bhkyse9IKLWW7DYv-lJi=qP%H~X%n$5kwan0dU{jP{Z#XOlqGT} z`Ccv`oR(K6-x?*8Pgt;ld&YU)#OY|aCm1dj45GVPWb}oRNMhWE(+=B)r{v+Z+%a(~e5EEQxszkc*8Mzcu!yp*v53BC(V1nzN3hr& zR)>^@cDI5w1~8JCcAYO|>rZ#6Hd~G^oa_Vo1OBN1kYpM_S3M;63g)eTiAX=XH;6{~ zZnjP)ch~g&jUbzr`eL9N=557Xdd|jI{k4!T-0QA6tn?pz4;Jo7Wa6=U_zb`nKZXpP zu-Y@%@F<653?;Bv=C!pgkyCxz(zwT!m2^GcQ&vSAt7B765z2XkQL0)f8^6PF94#|E z-NBxAHwC;Cm#&qm2R}IwCDQZQF$T-yOIXb_DMmBxjF66fM_1)LlVs3}Ds~vsfq#}I zB3?22zSA-HFx>*{IW#79*Zw4~MttKKm3!q*3VJxe^b56>!PEp8t0{c`CtC!Llz2m# zpqFSJer>%ovS`AR9GBS@5%=o_0IzgwUyH?RTL)=X&4Oy{qkHazJ=Tu=!At-?KOfP< zY9*cFcmWbaPxGlI;%?EyH*gzF3eR}nNj%r>$BQ=WcX&|nR^W$7lgjzTD%Wn!z;af=1C5}@X{j3chAQT?cc1nlV zZNpSiRjzglU;umGYFirDEGT)Jublql@d_NtGo%sn@}fWX7kOB&a(~ut^bQd8%GC2Y z1NqvYCrqO=MDaygc9z!%HqOW)IJDz*jdYwsJgC+LjW@bRv zM-hOLo|YaYG*vg9r-beGM5`aF!hI~gX(As!@7QP@LsSvsy}IJdoT71U&|!sT4n z@nlZ0fJyCLjbirL#QPV2sG=Cu^%@Vo58|6_XGH|kdLtAH)p#|HhATDPAYdHq(j3qw z<9|yugC!VWd7j&Jx1QpHOBn43vO;yPhX-6#1Xya2 z5NUkVRsB51GMzr2x^_^dQwQIsSW{RA*k9+WEej?L`fku0ajEr6degCu4I^@egQZhE zIQK^p;d$x#W`GT6JaeAK7NQ+7!&FszL}ee;)r%Ka77VG*LP%%xb=Ww}#`j{I9uVZV z@~*W7<^F)G-xt90$lv4dj}O6!L-=m-h08IMeVp^@U; zN!mBWJQb=V@lOwhmn;L$ulCGGj;+sFWVU79N|#kZTxC|ABz10VG7LIhD6jVi^|8YS zW1g5Abd@LT%ScDCS%q26I)8u64XWo4y=*@hKpcj`feb_O3!r#p}NII+VQLSVYWL74AY z_0S5F%*LXgNDP_$PSh^1AMXHX{p4O z&$&7sbrZQa*H>hwdcCBKk)(onK(eXYL?^3j1djOtuRQ{-o!`3tVocPQ+xaj^`vSn# z^NmdONlu<84v+mLoEx428*MhbR4oki!Ztz$Fy=bF%C*>nh1j?pod2#(OpG``SA+?} z0k#*4@HObXEvJ@VNgGd3ux4E3QIUMop5fVCAA8k&+`4*+na!p1C3itIoe!QdQnT&F zntk%tY?ba5Oq~Mnx`%U9<^~47zS46`T;;gA13re#9IW%;T*_Sf!q;PrGw0Yz;>)`^ z;-FX|&9GPE(!qr;HsGF%VBj8h7WOKcC#q!qnK!!~k|GKJQR8hb5jN}upH2TP20dKq z8$>LYcopb@pko>O)4N2qSk5{gEtCrFIEGe$MAjgsermtE^bv*zrRecqD;`jXvAqoq zqMYv;m7HefXzZ{BrpX=#uYO-dH$FYj7;^HSb6yyw7f3=y65-} z8U$F4MSeZ1}B4Fs%tBe z1(YX0A#~}l0+iLXz@p^{eeXlomUf^rx^9U9Tk_R83ts|~H9(Uma8Oyyix_BI?Sp}s zTrhh*p$a4i2X)p@re0k4JUH|8V5DIOf*gHF`!k~gCAL#y#cHR_V>l25zbIr#O=n-X z@nd#BxC@mFGKhY`7#px8v(Sp$$#NO%kO?8HPtFvG*G34SDpZ z?VNG*e4WJm9Gq)%T=ZJUX!QMo(iZJWj^Odnwh8cC>Tf4>uNBad)TuLVXy1}ETwr*_ zTw)FOmD?3rxBDA(8LHRq!s<&-f+srkuPYKoORY~`qI68~f1doRQmawtn&|M7biT0v ziBiNF{WQfIqce(5vr;VuTt)BAU`L6H!R{k%vM#JsIWMB&AOb6RYh`_{kZxFswcc)1&!pc@T zt~x)6TAo#1z$jHpV!TMi3=6`rRJOuR0 zZnnk*^rAM_j>5(chW6&RjyCqNPy_^j(_{^-KDn~yR$rYQ6m6_zZ8)WA6^%`u{va$3 zOdSYV{s;c$i;XLR76C0I8wUX`69*#!BLg!70Vfj+fzIC`2}c7F_kU00H{rMZ-xMJOTXAD^Q?q}8lpKw%R0%l#sQ5j5QS(og%#5F=DZ)bW z@%@*h(%=2{v!8Veg6+87xV{FeX6sjeA1ixbvs94Ns% z9e`T_Nk55dNUmcT^)U#HA&0+uGnmtN*Cx&j5Mzhe4{t=@bx1PK$wYqMYB@>F^h0%` z@4|pXCt_i1@9A<`FXCv<7U!ku=Ee0mqWJxQg$7+k2S+aC%yfQe$H_;7PPOSN8sBE= z{m#IZ4?TUtUIC@t#~Qs|XDE7CiYqJox%&CK`f544c&G63y2EY1eCT29!1QAkeko6z zu5oR5nO~0eV_ENgz3YBJa_$#iR1>xAQgqu||A+SV&%If6Rz2lgXAbD~BM;_g` zw{Pc~!2=f^KeS_a-7Fpo^*=oh9$CEqhF-b0^Wa4%5A9uDyYlFMJ_TTG@5$gNYt7bd|>IGrY#Yiyyj7;Rznv|G%&xNz=6-AYJ`=0OexHw;``V zcm%V2$%m0ILEM0{2;{nvo8kYe_Ww5+jqH-UFxNaDM7ad=-LPw69)+w>@=@d~VK<^I z3b}sdga17fNr&7i^O&pjOn6B`r?~X2wQAlh@60G8J$oo$@55P}%&&~~0D#mFDW@PL z<`6h4wjWJl4N@M7f)#6LU7Xv0-5kiNrGFaHaZX!7CRm$#UbFHXA}!uDQ_?hv&;pZb zi0R0^vpCHTfXAisDE)-1{7iZY=i2Qe4Q^RNgS#~K1CwR821Y}!c!p{II~2<* z&z-DXF{9|b6EemOnmLhQ?2`s_90p=;Dz>rFP$-0q6hiJvbM&Gq`v-J+Q&QDQW#&VK zWK*m8z|LA-_w8a?KzMw4Ciiqk)VInqr(Ts;EGGxniIbAWQcZTXIUpD1HK&iQg7j4xD-{Ht#p|a$k2Q|4$)}_1{A?5xbeln!T5J*_&*0QWg8W1^S`cm=HD^mGjjYD_{{Ac9EHpb?EeW-(guI} zOst=g^fyG!+{n?);WN}R{P*iGF2R4({%HCCMrC4UAYf)@CSYOUAYf%+_^WVy_SxAv ze$)Sq^BeKI{Doj+A^1n?cg@7k@dx#f{Qsf;f1y6*e#;WDe(L+Du(13coC!X|G$R2! z6B7X&8~b17uQnqi0V^xpZ~7lyzo~!Cjf3-V^e2MjPy2sRe`NmB^SjOViTW-1yKw$z z#D6HixxaP(p4;!5g^lyK-T(5$=Pdv7#J}r5-uah}|FQ4WtH0a7J^IHp|3dwy{A0mi z-uSZy|C;?D%J0!wI6ifK_J7x`ET11je>DC7wLAWv+du2c__-GUv_I>AS;6=@pMSjZ zmo>ll%BQD4%YSD0FUi05&!+_I=dNP?N6%lr{%zwQ?=muc8v57UU%LOJ=O6w5oB9vt zH|LL6|Eu*$|AY9W_Fr!MkNSVn{^j1^o9g#6vi)<({lWjUm45S?8UG{uA8o(4@88tl zi~HI7!}({ae{Z_qj`{QY!~G|V`R{D$bDwk{CBqVKiNt~tCKvAUK40|n~*K$x{iNaTU#GM zfQKzC()vc`_Qi#uf18D^i^r4B%jUzy+u4G$%)`prxzoL_sne9QlTsX2Ib(PVcM1?G z?zs-8Myk3iU^oG{5<5C8vWJ_fN={*VN1y=i4FhLv*^z{XDP#kP;9lfId=tdqxLGVRLHjot*ZKP2RZ$26v*zV!B&O1KjhJlQ1-tpH=E)It)4B+kUfk#6>)OxoN zdb_?R38(>(Lt{h3Q*#3xg8;avXfS@%|7a~HzT_8J^ywwLKkVF$f{OxxM%F^lp&tf% zeDK}=`E?f#q?xM?(5vHPtGg>2L0tpzkbqgumvS1(DEgBAzRMtIz$Xe-T02t^4}ce~ zs7n(_{r%~6Dz!(CJ%xLD&Hde?H7^Vo;nIigonJ{<1eXV3GqU1b8$guJnhk&*p4&Gh z9(?=dm*q!(pO3{QzJf9Gw9mruHN7aN2Q)YU^_b87wOaqjDR}q!do~~V^y{93W_K6u zWs@YhS`6l8wZ;2T`ZG-ijgQsSL-(_f`~BAChuy^ofBkzO={@{hm-cfPRf1!E!yCMa z`MZ)rx1NL!z_qWBaKoEN&^s~B^d_j;dl$3JxjW7py zKz(HS$kw6oIjz+PDsYRqKacO>D1jPt`N!>!4w!p`_K0ssz4b6w1 z{05Yfi6OkB-Ba7bhacd_>cQ3ofq~7TK3rWBNJ+~(YkOGz(+9}A-^6-G=Et`W{Iw7E zhpr*CoGw-%KRf|u5VhQ2DNQDg#;q;*)uZ2W=V=^Q4MO{#M>^|Y&E#&UIXfRB_eN?? zLQ1`pf)>rrtua`1zo~6``Ych=muATLFrNriO zH}2yuSjpMY!<2dD)9{aObeYD1k627Y9Rc5qE@JG@rXQ${TQe0qApY=|CFnfaL|zr% zes*%2H4b`p8Dv78%0u|t1ijH6E7;=HyLgyO)C8fuww}*>PBcL8<(3X?D4d&AK9U4r zrz^26^davl-oL-&-mOu(VFgG`Qjd`Lo${(5xuG3YGWHD1hqv`exUEL25HwvS&v|e( zGb4s*79B(*1rmj|_D*r*-F5hb4ihi?{lqR0+t*&@fMko%--NX9~S+iX=k|Dth&r&32`|G+(xZg04gFuOz9bi2d;C}={Iw;Gt z>G$c2qNdjlQkBF>r+q$?tI8)QB9(M1z|a%LMFZ6duiD$~L|uiO3rQhTs%Z&K)dr+wAN2${Njp{*$mD$%btXbOY@& zMCo;~A-#ow=OvjVyU)dF@$WqrDPm_g0lAyl@8DwtDlJMXB8Ph_tEZrzt7Vi&>q`)& z$RW@ZVz?b~6IOXrU1Nd!oGo-sR;$&RY+zRN#>fdQ3|#HhQtZ{Z^qc+@reCqJ4!yxe zC|qKdCPC;hosZc{_FW;oNupCmK~7b%R}}SBfjr#DyhCMUt3jAI8h8(9A1-4{qcW63ntRde0(_h?5R( zi<#AjYErP>6BopZdh~6sV2d6xkzIL+N1!@ow9ng|sY9krX(>1l+V70e3qx7PCFIt3 zb~O35zykX1>oW`{W7n&{{N#55juk?%guEac>o^48n+1Pph-!8v9h`~+W~-mKb16R= z(4`Plvq?env~vQENXmWJSR_{?o*4x-s|GTuLFrb;G(Jl=b!_x%4JV@ z`-7fFhXK0d!)yzUZ@=>HAT<~YtOi{ybWwD?%SHB&dHjDUo|Y;T-7A2({`!Iw7B~JQ zT_zz0;xaWQK>wCi>KC`AM(;@9e7@GQ+E^LqfW#ZVQHC-UPKl{FPU$vRK;$*Kfm!#2j93x<1l}uW$yz*5_FL z7|mcxVrP-ry^)4L^3301VTFj0Ia5Z`YrSLwR)tMt3s_d@CduP_KV}V^ZxQc}I{45Y zWzoEAay;l0Z&MJt=FU#nji^O=-E}MNEI6h2xGyDY*mwe^Bhh4q7BS^5A2nE2=AOdj zSzJRqBBZ>xc1&u0Hv^dcsR(~2exn1TvAye!h&mr{&-t;AQEz-LKkme@kY2P7vfZYm zR?SbY2u&Eu+vtI}-=lB8Y-v)zc~1`5<`J*gNXZLS&4ATCmJG`upbW%dmhyB@G}Ra+ z;n7uRU17xv*k{|QfeCzuiYu!H;?K8+x|20!rv)wDi>FOwclee+Mh0ov%6yg>Gt|Ft z8>*U;xTrX8WHI*AfLI{G1qq%G6jwak5yTz5Oe5Z=3$qeYrtZ?Z@gvY+e$!i(^wFtm zx4kq_JmH(VjDR(X+m;CN!Susf9M4CvrZLS8MOBPOt;9e$skGRcd$AHqw}`%^s#;1I z13b~%YQpN%;9DDgJqD!^A?|{>V!zwf7;ZY-4ghjKf8y)HNiRI8NXIvo9_VYa5bzAZ zHdj2ZqrTb_>Aa*0g@&XZ_pmfWh@W0%AgAr*Kl|~ZzU(vb%$?)t!dm99Gf6cj*^r(U zRU#wJo3(I6g}r>qkPqfqiE>jrJA?1{ujULAjp+1N@QZ(QXvgex-jnuJ`c}ItUn|0l z+)oAn)%XCZ`eM-1g`0n@d|zKDHr}?lN&Z_pf;CYbGK*3I*dF$X>5txP>A)T*rbB%$wihe$Jg%;4Qwgt!%k}j#@;lvRClSNRLUr7*AVLSyBmm*Bk-yfK1bafV{YIf zekwpJFEzf_6ylk_be1EsPx>+(M#4Vdj=ok?q<4ZJwS3i@LoisAc7!PxT>Cl02r@Yl*^c?#|CNL@4vdHPC zQ@s&dmmLxQRJzWiYG|sL_&9=3iu5h`ocSP=z*Z}ji(r8-fMKV9PpTA0<~a4?4G@WQ z-J)4INjW+ACdy%~MtDy(eGthH409~fy5_x@2G=4f&!Q8QMq!u+^2nM$szIld=-0sJ z5oi;=eUKXVs9O0ryg5^UOAtY;oLHnqbRD$zq%yyYIoh|>U$j8mPA((qQ^#|26zFem zVtps8T0r6FXBiU8Vuj2*44WNTCCkneT}nGWNfV9nlvG1QHz7QbR6T61)Z*j1%)2zw zSG3W>D}z2W$m?g2xDbfhR+uM@tAq1IEK@y1s#lPkeye(^sg}V!JFj&Nf zB}%s`GV*z_VZ;uL@Tk{0O;^kbJ*03nETm23hHsQK`_kFKD%&E$c_uuk$a=AAY=-jg zk5IxLdRc3Yw9XUM7um@)WDR4RsylNB@s^z|szzR}A#bvF(#0Kt6B8UnX;3qZ@-yT- zlnGxlZ9yZ5ToQJ!M?kmyW4Y)7TxQWuZJEh|%#!*q&9-YrLqfbJQsb)On>JtMHq7ger_#ovscP1T$?h@K9%IoH%~DKwH1bNDjX@S( zbB%iG*HBx9$@g-$WX!WX9*Rk~ec{jb+~g+}Llu2p5T1_@-yA42G2nkoA) z=&;0n_!!7~j8@7o5M9UgAa$x1TMuy%w?xLtE%^f?)qf0fG#G|%|Q zCn7MhssFj68mtXWn0t5`H|JyRLcMceYL4kXFVs%fDy4_j%0huU<;^s%D0XeSUwu(T z>WKvP5QQUHi$3n^(EAfw!*;{J#((wH(MU+7?OoWwc+G_Y!61R)am51_Wl49@mMEG`lxDkpSkd2`oMr#W2tD4-cU z?x_#;=Bs-Nm;wo?*Df?Zk)yVY+Q;Cwv5s2(Ou&m4mRA^MvwT?89^d+>)C@#DzhU0w zmA>gm7Qqn0b~-BE*YJ!>72`N5@F?$^c5fOZbZFZqDgq~c$pQZ~IBghPsE2Id@w%W8 zaIpLV8c$W3%)-2zA4!*D@r4(0)Go@neFp>yB&|KB^UgkL)Bdv>Lz>|fWxSXj0Ykfe zFYK+AN_5Sd)5 zY|oC`KybU^B5Z+I!uz~_qRoR96yj;ko@F~`Lb_8&V~RFkRC{Bl$H{Mmk{_uLgb033 zdiP4S7160jI}wcOH+!tIdKJPkTo1!Ih8B>HtQTH9d}@C;+OBM`5B~JYE!1+>@N#IF zg)repFIe?PJx}rf(4P^hDOn?%4sV9nb}5%J_%%B9eMe62k{sKqD|~Tw-99_tl&I!s z$?!wDyiz$k$a~ykNo%IqlY?|oM~e_BnM*!g`Xy`p2`OTx9kEf;!MzNlIkm7-<>o|q zOi5vg=MLUKuYqq>?!%WUE&!o_U+>D%JdfH`>77rMP_-&~D-It*Q^^SKOEBtu6G9(p9%9oCx1MmPjz~(D`$Yu+pX%BX6s&>Q$P7 z{mcLzv+mvp*@#i$O4M5p##5qc!Fj_L#&hW$-^I<%3^sg>_G+k7in6!J3+D7q3vqQ_W2FOYx@`bD9eN z;EiCI-&)+&SerT4ya8MIyoCWbTRJ+P3jAxMY<)U?68rLxAAyivOPJj!5Z$%bLlN(o z=O=0l1Xaf7U*6F$gRNG(hkhe#M^h8)`TX;ft`Qr-m1VDTpKPu!oZ{06Mne)6?1-t9cbevt$(L>LKiNV%CiN~hsdC6#%fZLEp8Vw z$}ib$2^|5JMRNY-=RNbLz^-IPj}fYSOutlJef<#feZnP*j)={ec_;Xc(dsC668n5h z=gP-Yq9vN*pvX@yOuv6b%N4+1J`m^eJCSbC7Zc7lyWcx`$Bu#sA(>KnH-g}rn_>x9 ztw*GEK+p2yh6BCR#>iMQqZS719w~TDr5v!gR%2g>5nOLG6bZ>#$ra~94_GJd%y}Ez zF;+SbW3W(PRDy^OB7uD+grnz(=TW~Enl zx8p13Z9}BCvE#>Kdk3AFOb3U(iqDZ+8o86wn8Wex{^C?c4$Dr=NO(P^2A;<;2g1CI zN**(;z;M>GL^Y;wb|9}g=L8lo965p2RzL)fOe(d!Y2eL%o}ZU8E^gZIuaJbT zRcjcF;bK^9z_M`35)i4R$^(z~ufW)|Ba9BuTip%b-_4s^aOnMshOEA6_@MDAP~!cX zQ*`~@d8TxS)`k{X00JH9UC~aF62@#qgvLAiga}o9G`f1*c_)E*p5Rxqxg>>?3Jra9 z1X0p}3DquW+4c4qrq6oN$5BL_N+q2EwOGq1Q|2vrxQ(^ycJuNC@^}5xNijVBVW$xI z>LSRtCQiEbGKG8y4jL$$bp0!I;08JkV{l#-CqFw+ zz<8yG2+Z~US+A1bBV&0@Ux2!_BG>y7PVL3)NUOFWi%m-7O;e*=^9i8&b%(N3F+`RCMq18utF?)wgSg5+NF(x{yy1K(FS;6VoL_2)f_e)wA4NK8I-gb z2|W*&RW8Ek7((4ghLfta(9(fOUAIV&$gf{#>q|vr6@+rtd`u5GW{lLovwaO5RpUh( zLh^cz!vTwGf#vl`f2fdJJ4Jx}vxCnR*zhS6=<}}iy(QBIw`o+Zm4i`P6JWQ zeO>@!LDrr81V_TiQlkkr&`0tz7pAJZTPCg?%PLAHC}KNBwa2)OnAaJ#NBy8`SQH|gF(5| z^#hlEJ36gnffEHR^V8hiF^)%y|0JSWpX(`f^Vroup_>cy=r2Bq(V0&pR&v%o(ojiJ zzF4eOs8fj~zAMjUoXgvYFkHL9Mn|_rQC_ICSDoC>R-N2gSQ4vQLK69Jj=22?PiGHb zf*%KL7hz|gQ4ypy(s8?X&AtVj-ne@gq>BO~5XH2vUqJ_)h%|&h?VmZU;=g;!Lfm{k z`{RlNA0(sIon@QC>od{|&1l}Utv-n#>Es1+c^IY64c?j_gNR<&ZCPd)a!WsQO@vZ5 zaZ!&}nb}viJCO+mnhFFqKgubAIaY2;VVJSQ`Si&$a`|@7-pK5l?OF`Yq`raDk*p+_ z)VLh()^R$}O2bnMQD&iHb+#B6XfIGsFd&B?#IxhAx8k%3Umd+GZSJ`Oj_&$HNFRD64vx;gEk zDKJX8*(oUrwKjqSPmv^cPV$34aROY7_8rYd8cI&V&)xi1Dqi)^zgSqq{g)?#O}}MS zaRC)y_zMuG>WmXP8Jh_$XyHA*BBa52O@%8&zR09fEVn_^$$EX)^+bZKI*wwN#j+?x zSZh|iUKjkc3iy7e+Q6(T9~>uOb)Jc?YsIAF!ffv9rF2ZIokvyk19!bu932HLM5n($ zn<-BD4AdkWH~1h*vH+iDO(&`0RD6;7)2lq17QYr&Y|gcdMuC1QyO_@$^W?kdyGVT9 zJ`P|eB4Y_ognA6?Die&V?BEVz8HOi+KZrz?KIQ-oCb=Dq2_on~zLidmOg;OH$^Z!- z0obLOwjv`p&KXLF?JPiR_znevR3KH|*2=ywMbkIWSV=!wz8S^{OmWk;*d4q#L7%x+ohB}dy?9`;JM{~t!ch_xV-Phq!*_COE@+W zeCcL4SD9>JTW8=ipArLmh(o<)$h>I6TE^IU>>>7 zTDMKln%0L=SnAa13q3EX*Fj&}pKog!9aCusGusIL1vj($1mnvVheNV(Er>QKYj5;j z=}_lIvJpF4L$>EPPlG#`YAwmd2@VPtVsSqH*TSJD6(a-25M}GutBmQ14FK8#tKMYd zZZ9nSV#vtvm^M{;%SSWN2mCB-F0L0zCV$kHLezAxk9ifaT`pq9%4j)T${-cDJl6<> zW27-AaIZ@55=|;HijhOwyB;*tlA49Kg&H}@f;y02142k~^i=63HRt*_yl>;4V|&;S zAbDJ(tS1F^~d0Uoi;394maQ(|8 zLYIgLHS~km@fxw6GBRki+W;P>8|(keM|Ymmq~3k zE>fR!aQ?a@5{8pD){>)a6C_oXD_51`{xg9|dc32Vwcj~rLd>)iZfdxAPn9GM?*;v6 z?^`fuuJMvMX#0?b(pMUtp5^K*1yk8uPsx=nQcl+BAsSdjh2a9lEQC9u7X|k97T6AF zEyoQTOa*X*#8;HdOki9ZY8E)ffq`Mk;J3LeCRBX^-&`@uD=j^>fYa$cUf$O5YcPEM zBslNy{~-x8`NQIknp(Dj7GRz;lIML{5m;W}{&g4Zgx_(`ZW`g&d73)z8uwyi@vDI* zgCX3&w9T5*uP)dbMh)xYXr(RwQqwR8!N4duep>^!9~E*MJ=z>={L{0k#N^ig1{VJF zFfU|bRu~Y;D;Y9KPA{*!m!$y2l0nFP4puI=xkPu;8nuji^YVPVYFuEWE$Q~bw54%U ztMa%7Lo{AhSTu@n2G-#*l&kJX?gBHFeo0fajlVt?T!6(twBL;hy+&~oujH-1V>)1VO{#)CX3~1< zPSlT|p$Y$_nxo=Fk^5;-VDw>Gu-<{j>70(+ypucXd3#&cIN7m)3P+HfGw(qxc*frz zq!)(fyP08r+);sfagxrUa6{=qrS12Iu!v+PFr8ZG!R+>Jkc4jSbc>XkvJ9n31C%PY zNk=I(nT6Wy0G754&?VJlU+JR{qgfLWMmSaR+q+PcGmn4F(szJG_6w#QD3I2QWVku! z*2l4e!NW+a)V|4Nc@tO``ph=gP6;mY?zd>Pf9k0SDkG@O&sI}U#a5P*I!_1%SB}1} zZWV~S{3Wc47dWJtA@Lc^R^J~ z!}*&@p5N+une#;K&>88IyJ!CHT?0OHcVLE2UfOy&rve#?Ed3Bl)n2>OnX5MPYStmTTr6q`wv15#7t0t))YrWM%$BYy1&ALuUrArx zE0jq^@VhCsv|ZA4jI?d@^?t8*e#SVj;`xOPlisX z9F|Ag6{G2&;oNOPQa<-OPSy@kqO`>+L%(UNSV_Ok-Q%x8g`nkHVOWmb4CC2$Y@Yop5lYUt#h0Gu1 zL5npwaFo7)jc^@Sv-j%nBhFw|kKHJko(eoE>u3aTfho2-(84|+xQDh`P2p!yi&+y{ zs(Q3KGEiG+YK%X%941}1AZW$eEJjMHV&+FcZBh%8xEX@r>@`dMRzco5jp%!3s)S>B80+b=EMi{%tzf5|9GH>#hon9Ja+Rt|w9aDI zHK5Qfu#kkYtXRYLsc}>N1sunKCK^0cqm6wmg&EAW;hM1!mU>fyY?gUw{3q+snzwKX~D-Y~n7*fGKsGc^<*l*c^WTxD_PInC4qfA$Wx$5cvrn1LvT%3^c58D1(?ckGS(d-H@gwCK|bYPZT@}~y?qlw2z zGTF?yDXQh1rb;A(w%cYaRV5pMY%Ab0>Zdp`F0_RntGa zy2`YwhyI6WFc|)r*_`mW|_=vIa z*F@Qev@FI))gtgElf>!vYYJxYi32;z*Z$r+6A_=*>SCN1igRQc2RXWkW9C~#PvTUW z8R;aU5#qP^DxN2W95i$)qQwLp$bXc?w|Nu2;s2Yd6vv;Idu33Fr7FD{&3V zLo1n5M&bP=dc<9NwlrH#kc#emBe#%wTL8d#oX4M($azgLkX$&IUz(i{7R6v)%Sl6pAiv(a@# zk1pQsk0B%4`WS*VTr0Ppr3+1K^rSqy_N1uak1BMX)H}&~8(H)5J9T>ptE_Z@M%-dq zh0Qi%+G)rOHdn(r@BrLpV_pudmix=1G5GHVcKS&F7>wv6v;Y*X`T@AP`HOer!M=l) zomZsgm-%TP*PZMOhGzLfnn16E8lkHTnInJlyx?&Ko21Ca$?UI*d2>}%E}}F-wKHzv zKg?Tw4#pq#K_B(FKZMb%WbgT)dvJX}?5gAy2e?b@dcdNRJ?-|G62%YGY4zxxUy z_)1uii5JOAWbC@hK5N(1CQQnw&Sk70+b{MN#ertcUL5gRw6UyXO{Aebr=S>t0cn(8XIoQe@J*=5DOD@B6tG(rVWvFT)qJGugVVfpVQ29Bp$-t`xIc)Mvih_ zcZB>@j^1;b(mCqlzxk^};*w~?yfm5Qs^#_PYPoF?_iLp&tO%N@d$Rc6*rO7Sado~~ zz^g;QTvjA-EFSr^TjTn_m(YufrF7ay4_4JPcJw(ZV^_lt+(ont6Ckkm!bquYYWq0M zT_R%b@PK*Z)Y+f=;b|9qm)7{@t1D!D06wvlA^$7z*JicfGc}fbuiKXm?WS(5N|& zllKmY@9@-L2_+nhQiNe-Haitjg-eshY6v&uDqb7Gm&jZXi^@gjzvJw#Nya|%25IMW z7>7Cpbn9aHC^ik}GCr3S8W878ix!#5$Kv*a)>>Hhf%OU}_3!ZqwA)dQJ~1M67}rk< zS{we(;PC1no!N{F`E!?v-gJFl)sWwZ1(7fYnGp^WIJ^%)wBQ{r$8C7HRltYdvzz%K z%~t_M2A(RQhJKxBHaZ@WmdO3m3puIoHZs=JEB0q8Z!pXECkRn#$@Rr*VY-bj7LjFw z#I?%f0rV?<0^+lzsvk=1nqFDIkmJ#2eTLCh{nMJzcwq%fj|i;*zk7B2ebZ9Dq2BWM zs|cZ!@=dEso9$9h_;ZQR6?3yDG~Be)*i@+ioP0$EOZEJ5ppsvkZjG#WP?t)kS$0@a zyZo1=v#jl5_7}M;_s~r2z&7T z@-=hGMkA6H>4dJE9BJEm1&P=}`kD;M6uH$X`m56G)yWlPwT8in+i~y(j(n=yU=Qs; zODcRTTxGTc1SaUCSZAD^Hil%OMNiHzP?fcN*iXQyp@N0aDO+3)f+jc|c7Y{cUpY-o zxwmxEgz59oJl%^Z8u*}vlE12s#EBZ+>AMw(?{9^QV}AT1co zJKnZ+owUeAM_E#eWl$GIDPYvUiYkG%j zn2~BfW>(d-yeN9a?Vlk`k>EF%5Z+aI&zdvlyzu?yrh)6?(02>iNMVK~;6Rkmo|>Ym z4Z3xkPCQYwg*=eTMUobzFH0G_LZnr%CYLKeI6&y#J~~O0fDw``za&g{LyP^{bs1SK z3IYH6Q<_HEzM8t{S8Tdyj^wr*oEOK1bvL_I?$~yU249`!*?h577Q41Z=yRzUThq%4 z>F+}lP`QxMRorGiTUHAZ2nE9AMXKn-`-?e=R31tJmMbPx5X5J><*)HgEcW~qhmxrH z9Z%m@gZO0WQ}zYT@;qwoeaYRpm`Mup8mS@QQ6L-$qTf%Z-1Qj3_Mqm_-=#zDRE<{N zpl*a^CW<5kQW-XM_qgj$#HSANES0YC#%4dcZQfeA7?J9HB^4JueTd@DM2-sgjHr3j zb9E_!%C_8gnVVILiQ8|$ZI8a8#l<6=AYDTt=7q%mC(|7G1TRB$w;vabnv*{P=UThO z2VzX5O_S$CPKo1X8Ubcs-_TGZxzLGz1t+w_*Y!7HqfM%ldg^zcdV1bdj|ty{^S4sW z&Cvt|fz`6asF?it39#skHlfi#Fc9;e>bzz%@d@xP)W&}C836Mtv{fV^$*U5=nY%nn ze8dO&D)Bb^n!E~Np4#V}WV?kXz`^b1?8<+NwC|ysfEJc#x=f;3;MiQ8Z-?_i6#CY+|$h zT^3GOgDmL(AnHWR>V@vQmdmgmKFJ~=@)nkVEg6Hd?-Z7b(B{SW%{Eqjr&IXQo#FVCNuVyvIx$2F8aST*7!WHYpDBFOb@?kN{nLR7d%Usi3 zDv;=(D-<8v zek8?|dPuH@T?Rd(nTV#yi5sxU@$&#owrs*9yHXV1*6_l_Jb5aMsD4feZ3C|rbH|_v zJrJD3F#DSOyyKpf-8G~GziHQAyYEUQ7}y(B+iM6a;JIhkzRg!iAQvGJM~eye$~h@8 z+H}B1J5Y`7us5&AUu7z|(96#lLu&plUD>DTtB_eRA5FH3Nv~|@=F2~-Te8#tR;Lb( z`e^usx_!Q1Uw;|b*tj)g$RzU2M>hD8ws8ehqA%2Z!2oafI7K@##GZ+?+82Y z++FBH^F=Vv3(8h&Bz-a7p)CF|hP7grf!yeSeP z@B0H>)CF_BCvj}64QC(2i|z#@>d+w>n=G5%W04wNOBcnIO^c#Ie=pBo+1ePYQ1s*L zvG)u&G0GgjWx`W!1-*%FHzU%U0ZBA%CI9_Q$fvmR8ZBdQQ^u>NPZf64Y5n7H;XjP? zzRpuVfld+-Fa(5E4}uGFi$^lpfb>wMIN1akRMIvi1@B9s`BeJETVZ-eYh;)i%pb!Y z2A;_m=WnVFP4+DG0zRxtIwV zK_!+@4NiU?rp565=Lc6*jIiF#&70Wj8YJj0 z$J4Qic$@|Xzhp1K1L#DqAo-ZwUy>1!*0;Yx1i){R*SlSmiT>Dtw+j2gUd{C9sjJS5 zJ#J`}R#E1zE%P$GnNlA#06|*n5R6tgzqRs<1T#Ihx6XM;_{i;@W!FC5#1j>|1l}!R zJjZr&egpYjh#b~{lFX8Q;tOsaTy20lDi+KRD|np3Z+9}wE2%<~^2D@g$BdG}X@t|W zsp$B03#?0i34q#op0~isU~{`_>EyaiNsxbm7m?DBwN7lbYqx-iKwY7PQh^T|CMFm4 zJwhwo!{Jx|Q1LXr$O&Adubt!#`>!sh=8>OA>G~^0@eRi#i4OG-PghiF>=`DM{A?UO zO1QP0UX#*R|_ zrAWhn_aNJ=YgtNUMs*El zZ*q^H+TvuHc&T`UY&o$z^ZPtgZdHL>tdFY|m(7kkdL(t`7avO7tCYE^5ylAu1N zV_FcIhM{HUo{vFO+xTQ2bk|Ja>M#ED@}a7sRo^$%JpS-y&U4r}`%Nmx%US#|IQGL&bwpUW2%b*S& zwQND6NbHuG^mIka@2HtSqEy2r%;zd;CBIeA(1GnQwa@v^+V%HnR1%;DM^rhPEv~c# z3EE!MF0RybCw00wn12SG+7}_xyaWCyqucn)$s~y~<`mBFcl`v@@gQKkeB9 z0}>`!Y2g)F(xztO8vA4_Gmt9GVeQ*aLMYC40VAhYn=+V~dTRnOo;W+}j)ViPl2Eo& zwuU(0Lp1M3=A5UBA+9rezw;pvKJ_o{`MQ$GQ8~T4Vd=-*%;NIUN%lv z`2c^(@kQ2HEIbqGu=#g>2jFM25OaRZCW0pPf(PBg@}%I18WnB~)5(o(Qb8sSy5W-g zZ&K6b!(VQf%#!6Hk7EZ+zw`zL-B*zp3)JQS)ygON>dj_1QRWUkJig`KH)h|>s6zOj zfR(XZ(f8J&9P6LBt^(4hjw@iKixye9PQiyUXgQQJ=BP|uspE|~tE|BcdyUxUvE$6; zaH+!E>VdVytuI!C1MJT*^4xoR(r%!3E|#l#k?YMb$W*Sks-k|?aQ1edluII3&{CK6 z^I<)IgzBI=x%)Sbj=sxhCRkr*AwPm!a=^rivC|evq@G?5f4r%{jp|$^yP}MYQp(Gu`uMoA|y^c0pL)%`b_!w^S+Q$h+1Pr)?o1Fd5Tvy-Mtkm5KsEZ#B zqc6&=J<;XhCnJ^v2nwyqNf}c4={F&2xTJq7V9AkQmqUhMcc$wtQrNi3-;m9RT7fRC z#X6no-`B$AU+>t~$(JPDS)>ZyGP76Ou*c{q^9BYB!3isBz)D2Q4U-_yX*HIbyWUhR z^&n>7n3Dp8ucl72dV92JvFhAP+6-Z0IL;J*NKo$A<-B)5#CVD*Nd#; zzC9?N`qQ^R3tnBh8|xlohauWdWPw^--^%J5ei3BGPIntjF-ty?H-}5VWq!2F^_Po^ zqQpgUk_=W>XBQQqTKP429C#7)v>E%f%m@+pV!g9he|_uO;P=Te^2>`snpZZau9Snl z?aklGdbLq~73egrxU;e+TyV^-XQZ%5a~RdS*`tr3_?>*5=BeR0H#ffqs6kS`ly`=v8`$zd9CRdL zH!_;Jp_`@`r+Iaw823Z!>ArfjyycQ9fkV~Wi{Xrl#)^@-4y=u}9QNH66Blam7h32* zI2&Lu<>;j5YAZom4d}2Naiec*hoX$C4PKjJYz&tHW>QFWV7Nay1In}>y5{f7me!j@ z4%L)PYp=9yVo?`dZ`NWQ4V^NO5jOhD?slGE|Gj^Vk*|a5R@cFB8M5hZN7iqv-e0gd z4ekTY<~n#z01fP308A(Gi=zUx(FO17`;OTX0oFy{+RgHD0 zh<)drA1uy?kNi{1Bn?);2%5sk6d|HC?hr{B?q*lv>iZ1rKqA7+F-Wce4&*WLEihJz zp->Y8B`H@M+V9<7*yJHUp;}@Gn4L!J^hMkHD>LKhRDjH#*lKLSnnw<6hlT$<~u`^=c&tjKJeJ2Ergp`1P~| zbOM{wKdbs98}C!PcpH91e6`2YMMX+m&4fB*Sm#|uzpw4_&>0ZT2!|?e&X>u0F7bwe zuy4;Sa_v=#bSi}DmmZ)vCRDLeN`Fq>wfMs}g4Y5da*y6|x z;02-=snaopchQ7U_`6BC4KEF$W)WuQrLxIj<9U%+VI;&aR;O*I9|?BFb7rQ4JwaMz z;9yOIo@3|Q95uai>NK=CM^tTc(s*ozx?R%-byYyeZp^gewjA%6O4lRS13+2+-Krpf}5IC48hNxf9sWYRK1@~*DShijJV-%R-%^nPT5G}c+zAs>KWWmH#6v3P zn>2ocd~tlURJ!q*5O6ETVb6p~>F`94h$kIynrrkCJlX<%&vSGvEz~DJX~yJ<>}(MY z2E77N|7!>;7;H`Ai9TqXyh3%UCMH=#bh|I2Cg0AkNBI1^v#P&Ub^Sb142y3;7qkfa zT=-obK((PP+K!DbAC3>2u@+)z)0_BY`GELMA9~S?#p2{EulIo0IncSvhs&+H@}YF1 zs3D^N2myVhulju#c4mqJqXAJMA8w1~ z%{~~Mb4cITqwIAy$f+$iwPSi_W_RI%@WxFS94K7onhOz^KtcPR|4_(uLqev zwI+E(lk;u;=E68^rtJB3De+S;&Ljl{o&L#1N8DT|6r0 zZcg*vUR}OO_n*Yl(FQ&4gTKyy1td31l;0rYbtQr+zVi#6j(H(T3Ma?k%x0S+6~=He z?WpZx#LqmhYB`WPb&E}{)_H+9+g`en1hcF_8wn&pd*L94Fi>35uVX%w8R-aNK2wM9 zjt0$Rr{z8s)U7U%I~sV4=8Kw}zCvnsBb*EDtE3-BBiTg{p1{c3SJ&`~bEfAmp~-5& z7M3_>QuZ`VGzMx@xgmjds~4ZhoKgG72TDe+WL~H~ukI12IDOI)6URY3AO!e?!b&Y1P-cN*|R!S2IRo zy1Gkhvl9v)B@QP|;-eJz<)>5oxHWPEr`>^GvV@HacK|C?fTx->6!!an00uz$zrIB? z81P@TaPc7GLRfAmp~6`toW0oG{HRlU}tJ_MLf_ zn3^Yc<7emn(ZNZL-pGiA%`_T$*W8O*ff{n(Cy2g|*UpHZlx63*G%r30YgC{{>1^_q zZJB0lrD*#-HY4B_xR_Loe$ph6CAb9MZXTHX4Nl@t+_itxC&`Rp(u2 zt!wD}5L7noN5SLs9s^96)X|cNgm>*l>zICxdp=R7SlXRpg6Fw$d0jAUOV=$P0m@pZ z1Mf`WObKrwR22x}v?a_q;n9X@&fx)`pqc2_q41@cREPNk-W!YRAta#x*-IE6#!22* zrz}mIy!+@KeVhyWfLbk^!1Q}U*>r?7c!#Q(&{RHuezg3EslmA&Q`k%oA8CO#^(~3$ zm3+^NNz1>*D&=-kAmb@65OX1k=?1ksF>RPL*9rOcYY95Ew3B zq`*2ko6q6fSm`MlcX%!K#SVS? z9pRdtypbfGv(_}SxPOeF-*@iYULl6$4U>PAZl#cA2v`?;gkp94(!kqrSZAK006=1M z3EA|$A3ldKm1KL)s>j1PuSh#Cuy7RjxWb)G&0sX;V2)b$1H}}!d$dDJIUj-v7=E=ngkz!PmrqRYm*?J~WxHlXa3e4a<1qsj7*U>>Z!0De+w`h8 zmLkR|jugql|DrSG)Xq<{mH=kuN14G(d$MjKjKelvO%S=?LdF7%kvY5fC$g+{xroM& z5Vbf6tD;WP@lH3W~kEn9=!|a$|tZL9t+q4K%O&tkHB9tjMO1L zk_sA+C0a?Cdupl*SN(yyzM8l7m@DB!Vj7u+^n%GVPQojWff{usS|3$ivnRbSMIC;m z81^u;@y{0nvf+0$<`>OTg8c*Tv6d2;LH(tFF2Z3*qE23V?bVO*x6*gk9gQTXeeWG- z@arj2pQxcQz`O4tKhoI2a3_;o(`@p(Sl70TKeUH+NFN7dk*_$!auu$4c?S9nZ zaCNDM8N&r&GB6;7lK8ioTeamSS_Ti!v*Vq&>~Spbav6w2C8uMDm=|Bmi@p}nZA^|_ zxo<>y(P39ekGc7j{S1xa80pTLmJbWCqy_!T5t|LPGsPOCuhqeYZ@H%932Gynzl9L}1bV%3lr(&ide zy!}6lBt*Wp1 zE$+nWN-vwhe>0+iLi9gg^N6plKg{C>y9JPM_anz z_8$F-ngNY&6mSRdM}4%XfTcji8ibdPSn&K^>>jqswNjcHjwsUhSXZrVd#$o>jM&rEJ63h#!S1AZ^Xjf_}L0&=8p@ss%Y|=EOl8g?+HI%Da5!wIMX4;kD7aA$w03e2+t6YaGL44hO-f9pG;57pM zfTqIkqBbV!UO%-1o_cTFd}+>}M#g@ojXRn-?dje(zLzh<w2ke$SR^cVv{6w8|=h&kaK^1hqyOsD)USh z@wnb+70fn>2NMgj>auSTvZ`1o9;`icsF>qb{6AC6!!ynHgc*2r)}`x6 z1rLPpIxMH5vFRPjEF#{HewR5Hx4@7t+41e)W{Ii(!=$o?c9NW?L4fy^VTJ&Z*lCq1 zPM6h8p<$WzuqW!}KvqdojE7~lS(Y`7ytGwV>04yd3=YQTBv}^k(k1ZE_!;9p^URGm z<}5+c_HsuO>PoY-)Dhyq$ubP1k2b$gx4|9y(2X{K{KA~A83H9YhgOX5e@rGu zyGljq@-=Ap{@&F+jnG4IYyW8$ow>%KO`f0|F8)BrW!t)aGZwdkU zEy+JAQYRuA8>{1Z6Z3ixu4$ax<&dYmnOZ>(;LndvliV_Iw2Bzx|6yaMI{t{Q%?%yK zBTt2uu@EmhrJ1Gw0N>Sf6x?}AAuPa}x36~N0ZOs|?*ASjiIpyW4bvY5q{n!5g|pOrRg)r z5_2E{qlC~CPuY+LCVmAVzSTcdg(Ro+njid#@G=fSR30UiQTNTttzYk0Wj@?QbjFD$ z;0bcx4z#t7f$5J=ygSBJUG?OwL`y zGqd9PSnszj+@gHbP9w75qe3=NXb@I*imA6CjQ!WSV?^W%Z&H;fX8Mmd7r6GKrMq+) zpJC_rKUQ!bCFrD4TCLx0aL?Z%%H+Md!zVw4Z($TQ+!~(hf`5Kl4S&bN_Jhb zgmiPZRkTTmzD&?-hI2x^V8E?3nnxSZx-gfk_ugf{C%t)l?d#s4Pd<>ECq|n$emda; zetWdKe064H>g(vDq&;W`%r6lNW9eca12i3`K$Fo#xsv~K@N8HA;NVeAXF26^z3Tpq z0Nsa&@y?{zOeo4MMOn$nLd7Nzp35p_OJu9gf01%6(b@v3XxlEJF5!YT$QOwiG1`Vo zVFIUaPHRGzKUqA5V5nUv;QdnFiK04R)0Yh>g3yuH1XN|Zy-{Bnj{rMB#J@c@9d%Ea zxv0kDL<2h@FT#t^9^St>l|fFijozIiOawBnMR=Fk=2t&X268JTmVd6OlO0pc#D+hQ zPAoF9bf^4RKVfBF!nGHy4v)M6dS-bNw~U1xL+H#!E1gD0ya0`OUoC#;X}C~XS~2;_ z6I`@(*F9v<-`S8 z&M-7dS-ON8b~mta)mIOYhahx&RhFZJ2e8Sn+YxNIzFkP`h{*C}*Zzk#-u`XnC_*I)=r)eQ*mi4Zwa9vt=2_ zOgw?>bY{24(z8qH-tqlpfiCK>ahpY4St|@@p21aJvE98C)SN<8GTyfYzFUNMVfCV7 zO%-wCdQvjm;8o!x9ns;<#ZSD{?yoQNzyq?FgX#hgIQt)MlKc9(5tZ()cM#oo>1%Au zz!!q>9?M7pXDodVovfWhO~hEwt2fAmZ&z=OHHMj(e%WgM~%cm`6ixI{m`ABcJd zbnNJBuipMbs`n7vF_-Yt248~9&Occ|uMMC?8#2a}tU8Cn7;_8g3`KS!v+x}kSOgPPZOClwc)2SkYyJy?!7UaDxMd!cuHr|T2>Uy0LSee1 z%8lzAHO>1kGYB^_Io?mIX^*qr>rE5b`8*!~%sypR9cYLhXOC$ZLn@v2!_tsV`5)&L zjK_QGfXcEE%DRa&?hp-~YDd~LB3KfrA7&{@HlH~DM%*&-cfB#FhIPo_29gug7)JDi zz_UTK3cu>4^7^4zXy(UTmHaF%u7uw*XE?kGonfjEanXlL<_pJqx#v#DuLA^29DtCw9nDL3&_d46FRw z>>eqaggBO*tw(n7)_J9#|;?F!snmmr0v0z zhXH2LroO9|7eWqYvB#KRo!3OjbZ4n5c|>L7WKgGi_(wl95CbYEpF7TY)(g2NNZecr zQ++#6!io2lhRU6^O7lO9CPbT#Q%(YocG~|D*0(YB2qx!1Y-* z^CvnvL3yE^|Y4M?#L{+B^@zE-hD|KWJCAZl6n>90aI=G$@fR>(r|$J&zIog zS#7hK-Qx%N=%Zr5-=99Bd}ZpnN!i@p3WBEM%;1)BR3eMCKj@V;~6xZ~{ z!V3N^1{&)54pXA240_t1J)f86lCZ|IYI%o_k@D=+4;!or9gO#StCe^3_C` zN{i#s9jMtG&Io#{LzO+fus{;x?h#G+ui-la>n)M$-Rb$|XDUOFJ3sKowrH3^;6fd? zDso_`$ej!yIVlUG+stelMT4UN(EbZoQ_`W)Fp~l_hrQ8`yfOa%_<>R=}bMk@pLLB!%7}zA`-Q<;?*lq7IeRW?*-QLtKbph%!YTBBn~wC z@7w0|VyUJ+ednsebP>iKvW4zh&Jj`U%g7P;OURgG09VK(x;p>E3r}}0Rg(*JbgTXUIHGg+KyCW40D5cEl($pc=cgmq5eS! znL0&(?{0ql%0+nW8i#Uk1*c;{!4U=QVV+2svU|y5O=E;XN|gpzFPneTT{Iz-mpw#u z!b3lK_+w=se~`<{>n;th5|jGSJdepzEtXp22wBkTdcpJEn?@>)@c+jc8@F04l#UgN#08%pEb7qzGTJ7-HWulWP=8b3i6W^^+!nqm>Z4>j z4X0{i9?m^e6tULY$a6GoFOAaq9jht3dqJ)b^q^@{Vo;X&HC|e($wyvQ-Juy$sy?#M z(4AT6dfQR*a4CqupK7FG3YZmtBSH30WPdeN!XR>BAyF)!@8rP+LsoB}%jUCHN2Fjy znn2uz?yVj2UI07?DjP0^PqMrJJ5Q$RirvUX$3zrI#Ezm#ZL<|7xnn{$On_j*|mRUkGmjrWk{xj#F4_Rx%9w|WL4FI@z zQj*`Dp!g@u91t=&sCWP(*-#l#&C*pYmoua_%fx9H+E>Fe;K6zElC(k<6bCRFBzf%W z*%VNg-J*JwY&I_&(cXcjbI2fTi&u~bYC~W^7q}K64`@xBFf;yHU;R-dCMeX?rA&)o_gaD^afbF_Sz8%Ia` z$hDf7h8d1;v9!oUt+c#cxDw+-{S``YoStoy zAwwnk9gSqEJ#n??Z^6*1R-uEGx|>#rmRinH8x3Xu0l{g`j9Ws_aAq;(jHN3UaM6UO z<#y|F4K?HBwoa9y%6`ND1PQZc9r1b)c2Z&ZbcMXVoN$=*KqG!wcCLb8Aa1czMH>Ka54qW}zOm9V1SB-3Y=(dL@iDySUjUu2Jsw(oiS zV>y4)>r;Te+=oi83QLN;w;ul0%G#$!foJ{!*yZ$hJ1qYNqIa?~h44R|b+RiSVu!UYAR*vfa|e-nuEX~+^(o3soR zUzD)UIdldEyy`6Lt8r)v2d8s_Et>-r^5qKcP};1BOUv+1f}s=__r5K@hJS=w z!a^Ck$isZ8ao1qEmD&%QDR?>q^WDSfaFxX3Z?KkKYiAbW#c#E{V&RNKWOte`!95nw zD~ON-_LgY8B(F{a7R;*~iN#n-Y5D2`4Tn9rJ+>s`e%RxubG|gg@*;N0v`vICWT+=3 zfg8@lzfUH*yleK`mXlf13`^Or%i{-7rK6!JE+ubiL!@E}0R-d@W5G@0ZH17h!R@oN ztI+M*2uKCE24A-g%NnJ7QmZs=kg$DbP#S4Xo1!k5fd?{}KU*K!=Kb2Ht@L79Xfyb> z2*hh-*JolEOJa)4c4=>lf|v!@w&VL?A;d)HzGSXzUB~2%3Nb&JGcLTwVlft+^w$4H zRgikyK0>d-83^#@tHc@j11HC(@hTL}|FQ2;f=jEC;s8o}4u9dU+t14@QmkSzC$iR; zHen@)D7}DDKJ5`>xF=|1=k)hCXnR12?k#W-)LSCTN10L_>!G3hfUR~N8^jC*^DKYf z=yKeB3;cbRpsjy#)%PQQ9W#Qlt;K9a1`9Kb2rAQ1g<@o!&3lPO^Wjb8fUq+1yrp*> znD*F?Qvm7gyh%oV+EJkk9&!A*zcuzO7MEhrxSK}&zL8~{(1K|D%SicR;Xn#z31ffcbQi^>q+V;)j2 zegSBC!T$Ar8?HNWi4SX96&3zI@9wxhx4BFdt0CdKRL*DX!WId59nA#fB!aeN z$7oIoZ|rJ6dUZ;=h^+e3o{IzTX;2!Z)O}et{sz|0EU$w`Gpd&pqd7>N@X954)P_gx zolPgm#T$TNil&(0!qx?&H%qj$$5ffC7CjA0VL79r%WY#x^}x1CXoRk3EiuZR5dKcb zCMg=DgQEXr)}E{hAlK$lh3{2fS`wp0 z@@zBDFe<5>XjQR6hbs~f#nei;-k*ED&>`|z1xU2zF&MT*ZPZ;<{U;y8T)3OBjjhL! zV~kaqMrIJX+6-(qv0u{-06~Dw<$jroHEv7wuK1ok#&&MuN&W8yn5wQ{Nb7J6A_7n7Tl3tW}amkoP(n)U> zm0u%RTe_6ErdShcpek$3Lq}*)^ksYpk4O@keZFoCn%(%9==EgIIgPBae7KD^3qc75 zXtnqTsJ5{49;>%sj;o6z)oX1P2OwA}1~FL?Ws{gO6BPSnyxdA)NbkT&7W3TfrRg@G&(T;v);k@bc?@kcES*CI7}jW8)J-333;@GESkdL?m5K+axfwdV(^c8P?f zosGh7xw107P4C{Q-7orL8X)FtMwAE@o!>(rPwSjp!Qk|D@H7dM|G4$e!K_Cvix;)t z8xe8-10Fub9`IclWth?&W&C;Lw7`bZnR4RJpL+N44z22(h&Kg)aR$aPW6&HQR~w&6 zvtyxFR()1e{RTM)<*rQ^8xIrkHnd$?Lry87#x*&ecW+*43l~)1{Mm6wP=t3)lU0jQWfH{6>T7nOQ z0ZVvh?s8``yc3Z0W3%aZ08nYrIM9c+0%Ge4jHJs>|b7Qy>uH<5bM z`NbEiyq|yH{_0=6*UK=kQgakj%HJ)oNjVFk&Aq(h25PaL?7i8-3lVYVJgW*A>-7dQ znf)P{0a^zXXnt@sv9pOA4YWo{;3v6gH{}*gbZRqlpoD-&b5cxps^6ev!51DT1sJal zIM4h4cV|Cx;P;OCKK{t+F&Q20(X1dz=nT@;DB6K`oEKlB#bD!)AD_(92Z&G?Lu$GE zNgQ^y1$_c4S8$#(0jaJp9CHNhID3d=pYiIQ!)t|X>jgAhvc}{(C=B>pJ*zY&N;SG> z|KTFNCC|;Jf$t;y>+dnaNJ?(@?G^TC(rBLWv6Reepi1q649A%lKwMGhybS5~I75=4 zvPpQnPoPP6x_LT}J0FBJJuLt3AD2#l-H&RFrp|uZKJyzPm<_uB|}f&Sv- z0OBr7XK!nzA9TcylL`;^Jw5p0FO9rGi1Qw3w8eZQWTrV9r+J(VSQMV(sTy_0$7nC=Cs5hBi;<04KWHc z0`~V6kbYP|U@Cv0_rLvVIS{weAx#OAh~RMYt-A;oh#5k2!FJW;OQ9eCd49U^+`fj( zMyP+l24JKFaVuNAU%Un}7218ypa@(04hI5{ zY{eQR!u*SYWw9yAVWo@g(1-?qb^`n-JZ5DEOp)T|(S^!uEPKl9-pU25)bS8RX zttPWz$;u-`FwkeU<=iX5bEXm|e<%WXrUC87MU@dhES09LS;!X7o*WQ*S1J&1RM z#jka#ZP)r07}l;~c?w!w1u)qv99s+L41rkXDRn3ubhHs}x}2`g%$CPD@572I$K<%Y z3R#j05#d1g<6TyPJEsZpZ@h$8)hK5WR<8%vONSRe{_fm=+`GU?d28U))1c8ol>W^* z#>fY=+DwTsr!?mXswO-Fx&7JC`R_ET8_9(RP6O$xH3`ki?(U)97~|A(}$XnbDbL$46}%z!lm zkZ|PYiwalfl!tw*v><|Smas8+LPv;mY&cZ{6HY(ofEc%`89}hi`lu?LtrCiOJ%U&Ws8txVNJ5otmJ}d zX6LNNbR05K^d-YPxd|^akQxva3@|C&x8^XAvyQoc4Tud(OD7X46c|9^>&d@qKl6D4 zD4T8hnn*x-HN?!*_F#4121rn*3JCSE1bq*9x*z1?#;G)ioke99h$C+eI5PKJ2F1Hh zN=3=4Kr^`HWx!7{G|B>mQeed>b^CI92tRtCWwM&*AxI$!l*?Gv#C`^9F(gfdmcBY=Gy= z;y>8*=ZK$;Y#~!e)^Ub0eJq`)cF6kWDYSf2+@LW63+J$3zozI`pi&OR%?xY$D)Q%l$T~ z4RLo@*e@_b$J;?C(US0i)&1DLwrzqeyC1%nr@ z;kZ4*XNrzLFSfjeBJP+Rc8~vcd-e>YQ3xlmB*rd#I94fqqZDY0V~Z{w*iu*APHYs9 z{o;ANm@V|;v<-=WjHn-q1Wozor5h{tk$nn#jvDZ**-gIwExN&AfmDr4kmUOoev|xG zznRo8|4I)<*3&hR9qL|p7rp2`BvMzG+qLm_KS7RASo4pp`LICt#&Xh4d#pSSG+n^=K-lW&Ml z$v@{P{bI}=Z0Qgcv|8Ya6ZB)rCSdH6VvEmvC=BdE3A*Bn6{SG*Fjwrs{T}OqF~ZzT zdGHhY)wWr>+8i?#=+2Hj*%;Ym$HUoQ$9U0;;m1nanMN^=2hq`5fH@wDsGo~E@nZqK+w8P>a^pdl%_euZKb-kg2R=ixiw%W1DEV3VBMa|#1@XLHBc@Lsz91pn3344}m;JVKb zs+?alylrMgtGfQmeQ=mbatR$_^`c=9bUaXZ{AU7TAZ+V&uEjbI+?dZ~4MPjq5`CFF zvW2e|-a=e{VtQmt0dtEBH8L!}BEX)7ye|C&ZOcp>Sc_Cx^0Qmn(Z zD+A9F9grZYc$f_RwnfZJ=S>x8UbI1}>-~mzi+Q@6+X~E5-_}gL=aNK`nZm#H1j7A5o^MgEt1_dG=Q=~D6pNjnp)(u$EzVe;$3y7QhF?n?KW zr5#WParb1j$bH`oerJ2bDzQ3c>!B-LLYcn}Ll(joH2b)8rxGZwb89lU0rE!8YWh33 z+i4V=^x|!_Vsp|P?V7> zXaYhNIl9aXD_099rp(N^OPg)2g=K7lg!<~f29%EHR*ge3vx+*0`JtXyYtE=>$Z7gf zwiCcdY=6*}Qt3j5iT!FI<&D%9vedbW9`tg9w#Z;ixrHnblwoV{P0%W0Svpw6j&OA1 z)!WGD5vks~azo_wPK5@V9p6)un&C-b?2zvNmCR4Vw38x8>z>;@3{qmX@B?oZ$9 z4Argf&U;_*N9wdLiMu>AlRJngrJhx*!Exd(MBS1oq|#85mFaqsdb<8M*dXuDB27+Y z`R1uAy3rR`-;t8vOVHJz+y-%<$G~Hm3L@@F2d56*@p~vd7C+(&QCmzrW||T1pC+w* z4MwCt-Y82_q&B4Kh|%^J-05m%`{P|8`|i&G#e}2Ap)-1pl%Ru?eIYC{1-ZT6VWdrD zPM3;-g=0>Afx7f-dxnABZ*gwT8Yd$2wv1|7_I}}oRZJihmsCR_CgXZsCG*Yuz*CY& z*qDW_oID~UCcW<;ET*2h;ytXVtJaWqokjLMB7qStUkSceISwN z*1#putVadn%kuJqB>MO9NVQI7f;2R~rSN@kqx}mPqX&pLEvU%S%h06A@+KgR=R{6Q zuk!z#uu3)QP4uxZW!XFxi;840SjC4r;uk~2VY`?>@gENF`7kDU2C4IM+Zd z5rFKxjTJoB7n6rP5X&IlD<2m>s!}Oi?R8lu7X9Nm#bv$cK;!z_=h#a=r#MkfsdA~@GCrLjPBEy4;2;L2E&2PUsgfdMCr*N8fpTJiL2f(R= zt?W|Npb?tS2(o0dX2onGs6V0q*5f_aKmK$Lf&)jM2F#sAL$z+BWTta28!*8*$C1a7 zn+Z`1Ze0rp9(~vAv#k`9ztdoKJ7n>y@RMZLA0H5d4D#}fA+bm(krnTWsjBNR&^eCM zFzcMXb9+^Z9%9dT<0TiA=P= zUq$SD>Aeb|;c&im!;EIGs2LqQD$gE?KzGh1UjDCnOJ;%IAU5l|nz0Q}ybDYgq6gh? zgGO7;`X)H<3+|uIL?Y2axjyAA>xBK~!QK^;Nz*PV{&xEfzX*@!xmgxs@-<)Zv!rj+ z)QG5>7bH5=LF{gt<2&XOCJ={_&|EH%0g?(VDWud z6dC&-_i-z3m^zvAC0)YNl#+yMfKn1QoeG@r(&YE^e4 z6*cze!@7I1am6q}m8p@r#{P}oY!tMdp;8VHVtoO-Mn0p?@zWB19*D3DQI-QI)S_kE z;Z?wsE8K#{HVwrlC_!T>%{4aO-4;tc8%w~}vI}`%6Od~Cmo-{;@%4oIsZOKVfoGja zDsaZKen+IAy#X3!H>jK#pdAej9e+SbzK{8Jro`<1mtqWWnr@bh5|GqmX*nz~A z&q=Qg;ItBb0rd0}?s3AdFCV?6I4CB3bDsFfS1-F{ z&p>Z+{KM-17V^Fv`-(|myN`i0hcQ=P1;T|jqbPv%#gFoH^T`gwNb&5|4>MM2RS|Z4 zsJ!hsTApe>1#8JJ(8#sAeL{{y9lrQG=JJ&AVTHFS2D@tcp>o%T&^_QNve|?a zpSqP%a{R0^ncIWBkLiMlpESRKhd63B5+{i8y&eEiD2dU^Fl|EyTXV9q3`(gq;IW9U3VF;I=#?>rreD**^v#@5^^@xxy)^yloGwq~{6 ztc!?RUt;MFui?wJt`ur7I*qk>>Pm<{+}5u zDr^JOh*_r!92SVKkCwEcsNt;l52Cfzut}!BmMs3n>aLvLGrl^6>X$#%Z%=s}! z9FmqGNd_f4A5;SY|tD zyw}2ZK{a?emFo6Hau`k)=j|9G9DZ?*{ooA(&!yYp0? zh?XtphKJp~Hxv0yZ3ncu;U4U)Hn&9ZJUFUe*dtX8;B_60>pLr^TFl`j@=wpY4cEqn zapf9&usCT2DAVU6eW>BO(ZB{<_iI<2$4P6+%~=rarS38NRdsVTU2u$z*_>VjO#KN11htV4>rfDM_<2698d7Xs}nnPunhejRHlwFK; z9`NLk$fJ~0;HF4|ZRHzo4To52H9aG|Wl~*^9H};m!@TpBNG{Zm$gE!tZZ?~$XoOfa z>`nqb#Cc2!7h1z{R>>xq+2fnQ1%zOdJHVA4Rmh;FDLin_kM1GsSRZf;hI<1=7vli~ zJ^{j%!-!N^w50)tym`CjskeBeDLmCOg0oN%vDPA_PuzK5owA_3@r%NjVQ;gr4VAq=*tnx|O_vbRg4Nu~-)0rqxeh^54==DQ`% zw7k5*LRXO_4X+>q5U?cgF08EzkyD+}xB6txBN`CIps2&HqMy(oBbU9yXHaNvO(dJD z{|y@uF(xHJla4nxz#yo&OfY(CcH({WR2g>nxbc9+ecM*pDel%QnvgKIJA^q z=j$SkUr+Y)p!wz>SMW`IT{kvIVE^yt2ZHu^27FqTDaH3Kkg^n} z&{CwoJ&BkLdug56qN+~aQ8>hFb9%x~WD4luswpUYcCZybg!A*MUqQPvGYwimAcha? z6!?luS01n~LT-L|0_3<(jvs4r%So3LG{sJDv(;Gt~4ztq|?8+<$OzhwaZI9U&NDp$Vg}*%mtbW~O z=a)17%4QqI*#a*TjzE}3$}h+U-Flz$jcPzZ>d`ftdT5 zP6>)aw7cp&vWz0;lb+oq%B>E%k`^j#C)WD6&=9t>odXSK;CrL`XF7=L; zX1gJ9o)upfML_{9=KlptwFdsG8Cd$J^)GqOjgji=b`W3zs0?};OHzu~m>fULNlOBi z%Kfw5DivVRk50Dj$O}Mk3QEPSY&nU5=66;04LdL__GA$UB^8M4`K9ZDTeIs+xpXAF zx(%!d#HdPk_LZj}?`fpr{S1&ZLmJd}VTyp=ipIX6ZS(}xOYcY0INqrn_z&OR89j3v zo|2;R0tND*HFo6&NuQ7AEQVAt^_oyeyrA0L&qSEwPMBz=CsqF&ts*QPqXU7CZI_!WL&uSn;Y?3OT zrdSQOLF2+NL6+m+1@@YVYl}G=!ig&MzrX52btFwd@2@Fz;t}5lH-8_>b=>ke#V3>c zs~>~jRktP|^gP(KJb#(QTWj^N6^sLWH?8e~bJJ!G7ZpK{h2Tl6S^+O-aa&=h_HM@m z=S<%RmS0T>T)%yWqp5XOv;0m)FDP!7&S<5tVpdi6oV|U(AY%vhU+>{1_wPr#>RGDY ztz8okzg=!wC?vbEeJJdi35>wHttqUycQfX$}sZoa6Z+ zoHwf8MmzoLwO?eA!-ai3d)gaUXwA12akax?FRVM@V(wrx^o2)Ocm|7`2a~16E5UlD zq$$j7g#bUB&$9J@jVtFTbIa6dahlsiOq|6iMA5z@G4We>%;Mf~_$=!q^&ezo6$%JY zdXOsV+kVQRmc55!YxLbpM9g+o=a?6s85t_E>$11s(8ld2hiZmnOiXux%gv(7fm5ho08=4X z23f)Mgi|qNr0jvw6O?ChoCfEUi7_|6F_4~(h_Z0l*F?W36pQ=H%Ny3L#Zy~`O_}JQ z;sX*r-L;(D9w*Z@CQgs%uK3O>h#vX0S!ZhwG3FiO=q+2?m1~1cY^GXUKS@K*5$*j> z8I%s2i^~b9ILOP?Z}o7g2WyD|gY}RicvN;g0{Zb&$m9>tft_z3ZpGMX75%Wskj9#Kmn znse0+qX2*pdOmXfn^hi)8G8jh-o^W7@ELOU#oz<_Qd-o>M$f5bQ8Z_kPZ^Wu>WA^Jx^$6WYSh5 zyE+EZ6$M&R52;(JDC3>+YLj$0|Ep;n^=6Ln^w3hoZFNYR3op2Ul}9NMVD}BvgMR5B zd}0iwOW9cE>BMN+02)B$zk3B%EISI=I7X|)7}D7s15el85IfWxv(U-17U7U^a3puY zkl;@K=vkT<`MG^^*Y(2+bK$Lpf)y#^-9z4V6Q530o1vCKxCm0iy(H?BB#YS6gdVqu zx^eJ9m)hu5%2<^#VuPNxONJgZ-XzVgOTB7g|2t%T@~$Y^uJf3mehM$GVQ6y8-B;$- zfaEotWmEVX8X@z<8mqr+#%LEdXXk+>#{Il|99qe3I4Q%I2YqD)ug9?ci29h?`pz{tyV;CgXvP2 z6O5x=iypU^ZL42d`=M1N5!OBOGFP(U)|H%G57$AyAxuw9z2X_PR#;jbW=Jh4xyjG% z>*R|mRM;8^X|eh5+5T|YlrEEj2;lw9|0x(!_XWb311X^{!hlZ&T*0D+N0ZFMBxns| zc7*tt)CW)iprnR#0^#*-HM}QSBi(sZ0q20XAd`^n^waTa$(=tO5vTNp-;zf_y_eR| zv-nT_4eu*_LnZQO!9NL@nj^rWWA;4Z2PDEK3ZrzC^Xs z%~qE-`YGkap&imlf!8_+-LP48r9ih#$15R%$a?EtJo&%KhU!+AQ7E9=XgPd7Sdt78 zeUyb7R2Ha$H!Y*qZC9o~t2QlV0oW^mA+RR=U6J!QFMzYeAeyV}B|RF-XPL^90jg(C zN8<~}lTTzK)**EaNIZclBu#Ba1#1)#Wf-TzMGO&F>nC9dl3F#paj^OuE#%Rao`tFM zfn1w2`t3rgGBO>a2bn&j4syyV&|nvLAE^@8deN$$(d*ksf@F@cg%2RZ0_FCN8!ZE7 z0y@`v?h*jxJt<~{77-49^JusX0r`%a z$}ZTRfmXnTF_cx|b~XGF- z>S({ejQL9Rb~oc(Dmmv3ZEhNsZFHxKqco9*S%6wDp=SPCKE!qz-#Vf5B3jVt{4kAY zs)Q?IlA{Kid2M$`TpW6UaXxj@=Pdvfnq4+|P9eTf-j=O9(e3E*T&P-S-0n=cvfiyQ zYSdsdxLP%6sHgj{#eZjknW~i=MD_x5V`iPAas0TAsE8B>pRaZfQOsV)Xc-;w8R0+b zd#Qh6xW35#K80C;QSk`DMm44uYT5Qr^=;mG_W$!RS8CKGsdJrb@G>ob4u^z(q6WGh zNPd|pD2w7q=Naz_-CR+fU-$oU;xxJa(hL~)D|vvlUWgS)Mt5FzQp(O>gD?}W^+)j_ zo}@6zdv7Zg8{IBL0r=9(&Jit^YF9G&rJmA(D{rgH`>Yv+P1)!ZuU5#)Rtr7#>0`(Y zk+CVqO$ZT># z`_%ECZ}I4oi>iiIzIc`gdo4tW&N3}5?x;;~Ny9JRh&ed0H>D;;7v|`t2Lp_NiynyL z?AuzN3DtQBE>mz+!QUdNA&QjE!)*hel1JcK%=p!H92@KP>hb^p`iLiHgN0rV`))_0 z^k0i*>)L*r>N?;;C1%Wy0Q%l8i3X}sOs$9XB01r)`BlG|`~#n8>E@oAVo@}?!-~yz znGoV(i;|rNpQ@<%*!^20CUmB$&n0`)s`8DC>tu5V)RR(jt?P`f8ETvwk5&ta#lES) zi6{F?VtkyjazTG1M%^c!h*6lz@4}pUR6L2F#tvBgJ$*BB(^P)WcK!Q~>ZOsK2334M z^|j9`u0MjfNkvU^Ha~$%)!EB^Q67UdYbxt`w35K!S^liwY{m%df7vVrFO>ffi_~A2eowwre$&<9{Og!x&i!3a7%W#4+%9_h(08RyAO<>v~&JrX4hH=giub%(Nz^A`l53ab;PrUUQ$vY&OFk?dwqzaFOo(y>JbBia}z%1%gCB2;i z9v6&5EwTl6RU4dy9mGyZz?tys2oyX&dlB3(QX+WSJ5g0#bOm&2*oq$e@DhAl@D3X) zJpZve$a3=Q@I}^%E|h!@N47puw;!qO`CWgrUZ1EBg3<5kZ! zR<#t8Asyg1B`7sSrNo3G3&OScq1JxiKU3D>`qC1PeGc2wx&V?5>Bmu*m?J{?E}eGs z8iyide>|vDx3444oJ#PljqS!HhRAVnd7s}f1RSGb*rwy$I1^X0D|@+hlW~IY6|LM? zPoDTsCX0;I$4NVcee!Eu-h~-N(!BRe(Dx!Vm4Ff}z7k2M1T2wZiSR3_n zCB9xO;1=>YnJ*kxCy76(^wS=`dA@FnothnfI~rf9*6($kSl!ahAcbFl$4Xtq;y|yq zKm-JD`>z6NV+_3H(i<7Dq?e;Zr65HlDGfm|#i%7j_Z}MF8N|1Sj=!ni_Y5PW%D_@J zr^$$_R}jm{*|ty#I`1oy?ewXLLfa=t`+}VoJRRgVadw%571_v1K9NNUR zB`zh|J!mqBn>VXtyj9xZj@L)ma;V zeLmzqJY0tL*vhp0aDD%k{`d!h? z7P_=_(mQiBuCPe%#;CdW<}QtTY7bk8$WH%2rbaHa3@i^LN@M4eDmfTORK_T8g?2XO z2zJP0ge9hA<84`fAJ93H?cLtRF5!1W)d#|!RnEB!5QJETEal*6-~-y?;qqk^dR;l; z7mGmI?FN1{Wi5j8XI75n4}p$v@9ekae3fw8Y6XgVAY_tf|5Egt)5W|A?? zdZ@$KV}prtA$gn@6@Lo3WI}=W!#lOVF{vUp1;d5dGutLiw1FOm5-1h@B%TBV_EPJ% zf5Ph&j$PAc)>6y(tr2x*f{v$?UZF{owUU~0I2|*A8~r_LRz4Q;D3-mkP&&Xu>`$yd zR9z|zjJG3n8)7v7Uo!6PtBeT=SXCdBm75rkk4;d zc6JLCxHulSQU;|JL0<2oF$ZL0fAP{Bj>lp<@Vvr9_)I_>-#NAv7Rir#X|i7Zj;}tg zDG!P@z2?hw+n#;($-KtyQjEsdWxA98?JY){(c5?vK=!$ABvl1L+kd#wb)u-+DFGU; zkTNt+H5L`IB)Ew>DB=-R<{xJQS6A@TXFGlhLoFO0P>;j3tVL?AV|^`InztE`g>$>X zgFi#TZPaq94~)NVU@!?ADZw4;_^|(2mP1l*G)r1vatB-;TR&EWZMX+ejkOS=^^`G- zN`iXdm2S!v4>T9z9al8{AyR)gi7L-EEAX|?vVZIQAyVfC!U)X60ZB3Pi&}J5kfGc& zy@<4FG%2f>v+dt~?Pi_iD%;8Tf^F?`d&+4Cbw~N0d5|o_19^QemzLnu{9O&jJI1$+ zZgb#?dE)6^I<}@>S$HvUe~U%VP6}e08dB!V9qfgI6V>AS8W`#Q zRMlKN>>}==oz*Aye~znkEc=UT59RS}a6)_dxFnhEoXLeQ6a@~w*(H5LL6oy`Wl@gB zh~K+x4+>2n_5&-wwA9@s!nmvOvDVCKTF{BWFo(*njwO@D23bfqf+pCl;6~fF5wD3$ zRgO6?AvMhj-Hmh`+}pT*aHa(qrn0G+PAA6e&_G9)mBz^mrVy(>Ri`xDmhvVVut?i8 zE9U@=&+l|O0RYUTG*Ce4!cD0ta^F{O5SP7a@DkngMUK9Tav-jv) zAAVzUSA$guZdMwIBiX~^xW20?kjdhb6o8P9IJR|p7tQbpM-warlw*rP@r;Lw^&t^? zwB7;(*Rfz0mN*>^32&_{@#gcZB}6U3FKsD>$xKbs(cJths>V*{Bp%gKo1JgA2Rs8z zyw(Q}Mq&|k5i}Lq58#eeTl&0rLG0UJl6eU`mRD3HBoaah@mB z>qJrRfV^@S%Y6C0#MNdm&RMC_Hy;Gw?7pN>0j$6D&2~Jqe56QopVhjpim$E5NVIfs z7)PU&HK+J~td|DRrMK9qRtTcrSf(0(-AYH9e{ZewbN|MvbP8IGr^5?5uKo@D*C;34 zAkM0QU+|>oTPF8>%aYQ)$vGl5kB zXOVM#D|xiQu62bQn7=M(IM!Ro#tHvfO#R>Ipc*8>f8Ao#mI`-ebC_>Ar$e#Dzk3#Hm*A3z$o(AQI6c6~InV(wGSh z942{kHU~Hi&DiH5p(fm!;W%0YeU5DN5WGybCYS5^dV-^36a}f%UvQvrBJFb~kENK1uRq_xyFeE? zr68M%9`WBcC|Fhw)3dY0FoT&&;f1^qS$m^?DdNX@hQw268+obSi1x|7Pmpo>5e!zx zbu2@f+BfZe45thr19{_qnOGdOP1 zXM$Y=nbtPaM0$VZTMKw6kFHb5j9_D{T>#_}g5yD}-Bd&IWSyCXk6Wjy@Ix29lXAqz zZ6@V=1>!|hT}>@{>mj5pa(0AU(r=INJNxX z=Sgm8i=|RiYV}`#DFys&c4U2R>F_%i5i+kIazt+?6%=A&!Aq%Tq}+@Q(u(UOsj+{P zs>gi3HyLKg2M7IV5$NQ?afQ+NdW(Z{cfI9w5~{Mgx#6z0xlja=MnkidIUH)o?5j=r zj!@5@yNH9YlMHppQxvk@>FO}^Ay9yf8c%P30L59^nwzq(K)f3~PF2zH9Av1u_OXBc zELy%>HA|Er>D28GS`l;#2pUC4TX9?dq0B6^;QGJ48qSt~p)u8&)0UazS(9dX&Uz*q zYdgLaNv zc?dxkevY0BG+(c@PXs(WSA;>|u6ePIv(_e?>)Yn{4~%ArIU z)O^~^XY~>oLV5e33@lEe*;HSd4p>Q z=t+*epE`O}pcaBDM(pND6xz@j-BY~m5;B9-I{WB$mH9zDsSFjGwig7`o|>CEw>qx| zz{5Asv(Y86MV?+50DXkjl3ggDpp%rydQGr>6UMJBOll%Fm;mR5dnHtC0N1o$j$n_) zPRZ2ol&J-RLM14ZUF0aCY8V#e6nnK~68WRzFbjkmW6hKt-C51X+!{78Wwfz6GLydP z2iYmr~mON!7>a8PUM4{(5F%zN!h!XyO@i=ju@uhtNnycR|zB)(sNdeN{s!gLI7TJLoEu{m-mf9`P@KD7W zN+6&bF-}azI&o58Ef3@$EW=3GBv`fvecM|HOpJkxjUiz}XWk)+6PbB>Yh)0l2q#6I zo^s<;&*Ir$>Xdk!EVto~+<^`DvMnoD5s`m5DxzxtP>j70)G!}R*CVYBW09(tw{x)+ zhJ!Ga_s@~+=~JIU_pq;2qpI>jesC6I&Z3n~*WCeH`i35!nUFql(gQB!mqTfa$IRj< zBpRffa^4lL?s`0jQYs=cPs-Hx7zh|CgSqs}KvlRdDpNk3P{9rHqM_~Ek5q&*-}1E_ z(F)1fL??fp_|g&4n?8IIo4rw1NKw5Ceg-0O*zMW4<77=AgNIHtzlJ65JRFttOiTU~ zJ@|d5XC^3?fbTb4*SgINhlO%_5{)CJ=|#@NYbcbfxnRz13S{pjgj8<-%!(3%-e1a8 zUp&kt%>}I8`tW`%{GsRd6ew)c5PQWN`X`Vc1lQbY_6PEMO{qQY zFYwyu{{UW^vKYgq*2xZEJOum=)FS+CG40KH1Z7^1*E`*WJSSZIwBO#8H@#3|a2=d8 zG>!D2KIaKVuA1`yhI8B^s{vo_^vk)_rqV!WOXa3WGqDGrd}Ov+t@JtmD9o@}&Q}Gb zUiHPrgAxRI)a{_9RWU8fWReHI;`yH z{_1n+)(HQG{@j#o+Kq~W7|5jrt=)qda1nQdUAA$@r8$bW4-l0(LFMp%BI;U%(59;u ze51{wFAY^qMnWI#embWHH&ULF4s%k^Kc48rzbi^@hxjO6>PAGcIgT>KH(065SSa-# zg>^}@Y!u3l95hrbqfveU-f*cL4M0ou5;5=e>L$!I2Y;6 zSCfJEt;Q}|&dSvb3=XDLBe5|u0iy+fTi2v(fv35NE`Gy1XfO89FL z6xxr$6P}QHxA^pH$UV~bdwE#KK1=)`v`McmLhzwai5>6)01Rsalozt(IPO$b#^kV< zU>b+Vlxc!xSTu~jAhi%*WsLRNdJ!||Lk;UqUHY1wJwcQs0KAL@PMh6@)_}c2Uu~-A z9h@!y*;-JQ=qc7y`!9Lz{vj>dBNsJD>6N>Ut>ttkjGIp38nQ#jH&an|1ijLGAW3`- zqxiq5=cL-z7b{x$`bx#goe3|myoWquFxB!W?gTRI-1`C;2t5Y%>XIi^m+|L!5dCJp zF>BdNm=n>mTK|sYGy1qXw>CjqWt>u>ZgN#uW?9BG^86-1t|QlxYr+UDoZt%GfXDmQ1?r4#_GU;e^V?xV#_*_+7{4R zgC%iMCzxw+L#rUY7VN+pr(>FQ)fxNxegm-1rHH=I0fw*lU*!6pMxhWPElz}c4BL!1 z8*13TbY24PT!5I0kw#&qDyKTRq{iT87XLaqqX20K5|%OfoAuZl)5XI(p8a%mVy1P_ z2T{fTnB&%iE?b-tr?}*>k@2jB4bP+CYSPCx{HOnLQMulL**ka(+Eqa~K|=9B0S$hP z>Ra)#r|4{KN)85bJMh+8?opLSPK|VET55cb8%~I-XA4yPhiPT{v`2SV(`(2X$FoS% z@MuDLBFR3}i2}%C>*PDbsF6Ls14(uEG1J$UFF}a;Og?x_ZNGDQuK@Vmrw@k^_KpBh z)EEZkg+(#sA9r3T2gqqh_}&f@F6%Qd< zsrAkpWdsc~=pg|VvX=k6F95K1=#xw{j6^DU&6dH6x96sAsD0O|^(&ujC&4uHk`*eHLH&D0jqNlR<6M*j zJ?;w}c_D$|eR67M@Fd{bpO|tMpx)%lPajNsF$krnJoYHaFs{z*ztayp4L~hOT_W|d z9>ts5i#yTxmtz|>0GutNR*lM5(v|0|Ap2_+3voy3VGvK3Gh>^-Fh@3+CGUt{%QKZ< z@&G-oL$vvTAO({iPzU3IxsM&3!eDuj0_hlJ^pup-6qf<~@P-F=gKT5hsfiZw_b<$# zVPn5g8BjvZwYh|M3#=4k&V)L!k*@z;Vh%OTL}h@;VA`|Lwsr;@MnRKG6DD zkKq3}4mY=6N>9<(5ItcD)9W!6{`=~p?|#`j69F(h!G{t||!qIUU#gD06PCq*v za_!jD*0w|;_}ALj^sZm^=k1al3a|z6)MmsEDP@-Dt+s|iR%gPMB&0e>d=}5ytAl_g z;)&My=ub9e`n5aFr+5>)?oLi^s}qF|%}r>`+!;xqJ7cg9k5VGMhg23G3dC2S|2R}XZ zhgFWvmmOduZ{G4|DI!pyWt#hK_o1T&Um?T#CrMMLT39cL4xG0VEHwViU*s)kbY!XD znxN|v)Dc5Qs5T>o1h9a;#XQAEw^Qz_R|FT;%Vq}&$yRj0I0{;;fJ*|%^&HHc0%E{% za5o`5xG^=IfXLJ!9j9x#jn&(C9R1VGFk=BZ+)j{kB}~;e0{*z+ySO~sQzQ&W(QG@u z$u#>d-x_fT^x#pM<@pi3(@Bc~KHyc?#y(FAzpsh+;b?a#1*)ln<Y;$x!l7z9<9E!g}q;+f8p#Sg+yue4FUlD94!2@nmb? z$uL)2z~qf(XmcBRx&7|5kx2f^<<>ZoL4hBQ>sFjsVY7 z5%Gc0VGr6X*L$3;9@A!)b7HtHpoKV?;V=dDULRiW-w{PgcqBE?u*da<)*Y+aZ#G~)LHQS z%RW()wONT1QIC@)bMT0)R-F=6C-?#z!L{{wMAMhh3s8=O9qzeVLq(Fm8*AMDUYuDh zeH!=qE*OnS;skK4+H+)8+jYeTyw;reac>`;k`^GI$3#PzW-}@CD^3K_;G-LpFAux* z=L8jRB6Q#u(=M=>ZxVW2j=^?o+Q=U}(Gan$7^|`W=E3f(DRWHUR9T9FloK)@kMmYb z4Y*9rdLnrJMr?WR<@UiTWGBg@JlN_g!FV-Uz^E7WTdPp3aaE`qDuh_%weZCO>#L%p z=Mm`!2TUPDj5JKq%59+FJMx@97DcAniZzXkeOJAdbTs49NLAh}Auf%DDe@+ABlgd# z;Lot}8UhDd;(Nc!&BtK#{PO2WAef0jCIEwHbs|+DZ8Ejljpl}04CN_6gqf{lJFg-3 ziv>pcWg2ELH2z7+w^z5jMJuNfi>v{meQr6M)cVkA=i&=QjTf{YuniX1W6kR{WH%9o z2`rkvbG6|VGRG5EkQlwG+ELDh^}7LYz+35Onb`wu?dTvwNt$cygUaNU9J!sT-1NDK z+nd>*?5j2TSHn7gn&6r2vZH*zo(Z5~r;Ntck};hakUAt+v({RqTdL~o>{+DLWeLhD zG$r6fW*fFc(o~pp_#Zr_@%;2lJ7ZyJ>_%~aA2=5}BK&~Mu395H^pBsjgK8{A?2>tDIT~Dw1i%G~bW$AD;TP=R5y_}*41);% z6PHdzU^5&}lxJQg+RgD*?5Z;A;DzwGg{f7I9HOj>!)52>wDZ8faRsS;riPJV&V*R$oZ_xjeFci7^Sk`ebNPxI#s@e%qm{UO396FTTY z%8){Qn~T3mC;x`AXR7kyeZd+}ae5(V56*byz02y&y?ak>oIuI$@YXiY z&0(84JLydwQu^;`$zmSQ(|=Dm`CrW}vhklY$jeerb-pQzPtW4Gn;%z;IuGVHtT)Q+ z8}@Dvs~EX%611&odg9Q)Kfx@>h8&KU?LQbPGb?@l-fY#RU;~5g zF-lz2UPv{##-t-O%RP-;Y2Be|P0#ywSzoA3tw74NOE7lna2vrwoT2Hg>uXPjU&*6# z9pTgQvXLcC_ScX{Yo!%Wy{)od9C$?uxGy=qr(8yEIdpl?NK80zCe&uQ@|OJMfl0k_ zujvk8`87icD~B2w#F}ptwwS0;45NWqevR(54YB@}l|N^l{%1#kr}y@DEOz3VX8wx{ zI5k+fD{`(@!G+AF^19uGjB`hd%bPVJ?wxL6Je82snSqBycU$EpGG>s|nHp6>9~-;5 zu9Uj}rqaO&fyh)*CiML80h7%dqiJCg))hEP6>+_Y)@LeXxv;~uArj))t)(Yl9U6^| zIYq$~Rs;$5SC#6M2~$QZJC_c54<-2o?MS+cNfrgNO=m;Xh5bS|0!R% zmI$A;-LKVU{vGudQcO-!IB72-qT|mBolzU>3%xd0KaDr$tp58%B4OfP8wl~prfs>p z3TVms}_V<_a`1n@k%+3SPL^vXb_SSLA#+aBgwDmQhm#+_+%T z_;9sO^?n`;Au)V1$Jrjfs;@~R*L+om1Hdr}PujU-Ta;+ttxCs!T91_lkL(#u;^R^V zfQ@_@ks;AVu3;ok*Usq4^Os+9gwNT2hx_Uf-t?Hr=7r$|)NN%=jD67t`)jN$Qn*;A z-|f(}PFJoxBj5|<5u~g0ML{yAZOG2B4P=o9xT zL*rm{jpcpT-;qaD&zGgs7+k9U{>ig#mlZa~Kl4}FPoi5qqws37H|>i>`J+~IW(W<7 z(7X1Cc42tjPe!F`(14_p-9bQ$efk3QZ9&1H(1Lmqbiu?xmWBI2BjoC=C9^R`euHUC z`)Hd+)8Jrq$itGL`YfL1=IUE1X0M!aunq*sOzha`v*OO**Q=hXQz%ubv4v_wPWO{7 z&YF?$ogzS?jGaqc&GZNz>lptwm$-1Ks(=(a%K%iA4zB6B;k)v|(a(qAXm{+?tqM5l zn~nHA`aLd5UEuz|o*KvC2+Z=*{_zwTr4dy;cybaoWU#nxEoFFRD2t?R1ei6xXbpJn zm`Od&jD5)bZ~i*;PZ0=&CsSeugBd`zPQxdpQL)!e}g&tzt?wK z$Sw-%q&c;PQXvhK9E_eUy{4gCKx2Zt!^afWAmd^FCnMlWdbP+6by4+fWU^$^a@8nE z0AoGp#V~BG#C{sCg~``9MTT_Tv2krg49$Pxzfy9_wDu!YHYC7DFq@#&z)|rv-UulY z9kz~fL#URaUPQCsMIM`OS`{XIXM|G43KYw)AoUKCs2vBLJG+4e?aL^bRXoImo`@rO zMugwdYNOvR_j`DqRe*=zkM^527e>tVJ_8yX3kv&qFtvZvW}xVob0s;_BM9E`;R|;Y zLyGClZz{lUwoy1SSC4_U$*PbKjq?>5S-F|9GIHY*U~?EJTx*)V^AQ$^y->6iS>xwn z{Y)Yc^|B=jB&?;0Ue1g}G0xoFqdiM+3!9pWC#H<}=Ndh`r!lTz`B25P3*PNI3FP)? z`_0_t7!)g&o&kyX^eZe$xo1BOY{AJN$&UZ5`$VA+75kGc%>Ty0ulGo}Qa@ZW912pa z$_0)aNXzOdpF*OO?@SkGb1q8Mm>_a8j;G)r-RcQuO1iHeI+YSIL@T(`9W{6kd3LlC z0R=03;O&gyK4?6&OG{_-M}V;wlL;d8ad7dA#0i%qU#^jWPQp(^4omQ*Cab z!=1F!m>X6@&~cz_EpnDD_urLZ(7I(&gW4ay&^#IIhsiEj zrpnOQ2^-Gbm&lDJ!6eP;#gQCwCX4BM-k_lv>0WkI{=dm4c)$mQSnZH>dq+>*cMQw* zj(Cos?ZQf@o;Gh(zB>%>+8swH#Hqm(ZZ!SXnTr2o2j~%poDJhCP%Vmp)in-%Q}$j*|bu!pN~=N z|2oMB09UzJXo^R*{_j;T@qFs|(II#B*|JPNoJ0Ok-_9_A{7FhNoDd2P!KXI(7HtE1 zE>hRwX|y!Q%Mr?WcA2f1m(j_n&Wh+Sm9i)YLT5)WE@75YvrDQq)?vIY5aI}mAn7Zc zWdEmNJ|EJP+2gOF8HsQlnXv(HYuTk_(&Cf~tLP!Ij&`A@{|wy$lCTMe<}j2Omt=9($lbJZQfx{u`GZ27Fe62c>U0DT2BKCUP42*2r~<&#S!tw9Uu9MsTsm z^a!8v$ANkP+&~xp?{s5U*JB-Ox7l!R0DFmR5n3YjeMT}puP2|9L(6-^WM zLkh9j4nG1H@80D;v;6F+8yI(7Hzq1L?1Rdm84?bz%9HS={Fa-J=MO%YIFzsH*e%ai z&YnZVexd3uN-F_S9xOYpK`6oM+7r)-4RWepFubNAUw8N)24K5BCDKf}qO6~CPu}JdtgQbGO zZLYnAirvqvWt0J$Z?*I2>n%LTogF^_pal0diwSxVC7qbqvlZ+-2sIjp}C3PNXv|gTW%p zDsgB%t&bx=FDkt%LCBzzJ=1Zp}v9^NZ4^dpDJf7uL8q23GM#9+Ppj;Z%fp0j;XIK^!NJJ zFyvxK;s4*va;pE8%gXEN743=&yTW4%k!)7UPC^%7VhTwygU6G>zE^hlDi#l2%^#Z7 zr~uSptPz?f%|X3N)<^mU_)gBlaM6#&;X@ z!4Sh$>Wor7fe%?Q6%Ek(?U|`)q5fVre(Oh57qpR!y2#222H;Yk0iA$A+H?sE8Y@Xo zP`~|aBF^&Ljjk!Td~Nks$g~;p4j`Nyb|YIoctM`nqCgSK6rY}CfDL(>ii_-9q)KDg zcUr54Lk9zsA1NC~SGSux)(-7kejv4w#6<9-BV!_~s8-HosmHp#yr|LiqU|J;n6Hd_ z#~^@181=-rK}cZQ>BoIwiQzaAyO_xz4$*k73a|1q|n~#!Gay1 zU3%D-7%VVE-We9?1ZY)L1u25|%elOB#t82X4I$v)Q`1Ij@*H30&8Phw)|0FGtavHU z^W-gnL09lV1o>0yffiR33!eT4suzn2Y-%Q_{B;xt`G19UXR+aH$Mjb5*Q2N5`=m{5 zth7z#`cfA3L_HyK2Lfv4uR!-deG23b1rI#?ZIx_xQ-^BWJC`Wwn6*S04KmI0fj~=S zYC=ugMlZ3BEw-X|TrT$1?KZVxmoDGut_BU81VNAo{RC2SFgz*p{PQ|X)^RhYQ|FtY zDC)dSc?(q>v`%5kc6}VY;M1!H&lG$!K6Bl*xJ8>`#vNbV2=I2$hov5B;hO2J0H^PP z_Btn&%>hAW#i|Qk#H9`m*(;{_`85#>D+3kkDuGc)qz6Q0#H~J!(3kWPbfX8l!6-7j zVeo0#HyTV7hXcsA5A6h1&GQ&xrA8ue(CdDvR(2kG|iPUSrCh% zKz8e+(?oU4)r4tPOTNL15~{npTG+?kWFkzrH%#$V2{4Y51;!onDquOQ`>ph7IW_C< zn_l!*!TXjoj!<{FN|(j+zJM{qYhhFK_FI8u(PhE<`(Rrvnik!!m9#c_J8N^CiCP!Z z0{b!CrcOV< zR!63u_xQg6S$*dJ zrVb-}u@9W84^~J^r5@>Wn97hdEcy-y+nNU+3LaAh?uv2RQsX>OmF(q5VS+60gp0wU zHkOXa@@h-u3i#Lz^Icv*h&Jjl8$fK~z`1L^-B+#A-wB>R^8_Z(E-#EnwJ4vT#f$&q zxC%?zgeAD->SrieKf+ng7&sW38ZCl#wQWeXzXKe2LMOQcaa8@|@o(PP+${jiFyu-- z@`(&1Vbb3a?C)uRW+V3MsaAn(*rFLtp}vJLrUAcrI16Sp;qF%2RX(#=>}87>k?+;FI-jH2S&*QSF*ziLVO)!(_{J_=0r*RRl4P*xN0fLOT}(DDRMbDJcE^>5eW zAZLq;-OY}xD>FA=c-8zxmxbsOg5%iD1U%!9Sa@;YMDyW#f0{NVFxLuDxA9bfWpsNP zl7?W2@KIFef>2-#oo&;rQO`i;ZW`tvV@7%m5-Fxko3rEv(##UJZQX=5r^t!qkno$I z(q0ksk}H^@cAFjzs(gk(<~dbfBkc;_`bIQ*z2AZcU7+*7=f$gSMkCpih{ZIJf&PLB zX$NWGG-VNg-x#kVH|`Nd9>zJMH652Z!M>?W`eEBD`EeHz-H> z!tMd;sW8$cB=EcE#o~Xpn~~&|(#<{Fe!SwU(B06=lS}jQrx;A%8uuPprE9GVvZM1s zr8&@y9b!vC(#;!8EHUGaxRopF9$u908p@mZM>Tg015ENg1Tk_p@~M-N{!_h5MVr<_ z^G{EM*HLxbXd-$}nJ{5H;9^x@&}Kx7P6`r+qE2bQu+T7@OKhE71b@D!26Wx;$u+Rn zD(;miylMj9XM){aL@OaILphzqa%N2+v{*N?QmBRgz2K|8Ys$zqny&6upQL%xb;alo zZ*rQG4tXV4L^7@En)>{SsDkx(T@iDE6l)4iWy*O7^#L-P#zRPO(iM_4u2PTkx5o_= z&UC{0X1f&aCJ)GHVA{_?Fmxy%T=HNL6FfEg;FO(m9+$xM|JMR}F&Sb=L5I9RS{}cH z5>9RC`4&QouF+L%Wk5qrZT3$bDj!*Cb71F*pEsO>cTdcjqQzbou!CAC^7wE}LdI+3 z74kf~*_0Fa-&m2a_$R59PktoXdTy8r@rX5?^$5)KDV0+^az@Hmp7*I#X&V`=lb=yE z^ZuvjBoQbPi9@bg6x7+Yv+ob@I)Xr2qiJF z00lt$zm#DA%OIs{Dz}Kgz=WyvQB55Px~beRbZFS$oCR+#+v)0?)O-u!tBwsYYd^f= zzhqqTBlZ;gS6n0CNx@}6F`L-02UG~R{Sj#^?PCA5h%;y;kKWWyw$m>+obztO9w`8Y zxiKZPcO`|T+{j_FK#t6%8w1+^+cHz1MWw%>;uT}7avtYM6;|W##48uqTKpROIWLWq+WBE& z;ov3OCYnk~0rxJkW-K)0$zMid=Dx)B0By?5El6jcBBQ-^n14DBA&Rt+d5E%OQf8uf zPewTRBuh!mT>mlL)&f-NFs**Q$21GXPd`F2we*EXmOqvfj_?^)Nrd%|nIv^7Dx5;~ z<~r8vs>j?blQtu+(Fy8J&{K~?JcRULY-1<4p100{`z5vLA^GG1^RV#xWM}#b5h@lH%rghN< z|7LT!DOY8{yZ|tHZG@86g=100o1lu`vgXdivhQk*Pz8FAY}{3)cC#4yE}sA4^ra1& zoj6@GvT^Zd$_-NRe!7Xc_fSEKCX!x&SZ)gwjNuu8b^O(>uiOtCyHw{J`<4qY(dvBV z05qq65&PQflOJ%j-lhQ=giQYRBTozx%fSM5GN(=w@gES{9xnW0M z7l$fDM5txkm(%+LL$ZmY9iZ=ELKU*}yFh9ZSJk-^MVbJ+CIP#QwP-1rws;lnQ*G97 z^Y&+b$^}N^p5OEcqo{IgyjXXwqI}?HKr{@sj*Z2^82hdfU5ppQ7MXjpjTcI5*s={& zNf*Do!W)H4yCfcDUu|o_RSs49;oooDG3571eBNp6#QkL9t}!fk5nx!{8G9SQ14^Nr zFqq`pSV^P4&o?U?{3{CRuzqs>B(4rcrmh%a1=iTY27wtvDXVqszepsNw6yAo-PmMJ z_9insE@Vva_oC{oa3FLcgxwo4AXKVZESf1oKeM@$T`4LT{OjvfZb3a%+CX>d@3E6_ zKjbbv$nq|NSuM4O66fA zS-bMpIdnGFbc7*HNDcuh2T$Nr0B8LmK81B{>>0&zkI-G53uGYyF;|SaMdWoy>y5qUxdy9@Jj5p3uPL{*vCLJtgzOB`ti&e zF@hpv6d~BDvPAkSdH*cnK1Voo;XYY)KLU{dlLwfmJUdy_(+Hb6pE{J+-Nx{_ zeXS&47?&?Y_`n^t_h9v+K+<6ve5G(}H-ck1LdKQ;@Rii)3gjoKja@RzYkCUhJCm0(HB}=76^bKN>Nq{U3rRG4Kewa{c zj%1SM3M}Ndrg$AOpZn36$B?qZbI*<;|wWy zUGlD!Ecn!)ehu>-dhM9WS;q6Ry~-;uIdY4})-SrJMwoP>dulRtZ-x_I)Smng1!fbj z@}pXNMh`7le3>e|&aG>ca? z*V!)0Z=dQ>h9WH&S(s-U6A~(u+-1MZ()0b&Sf$hNX<`HWjD(IR9vMmE#>=LQ!5dJ9 z#9r`LnMJVt7*z`aPH5@@#Z=Kt8!!$dyI3*;)6$S`?{AbL-dYn#q3T;%;h)Y+=+MH=G!pdG&Tr6qo-iT#xk|3`hfr>p?qlS1oP zM=~k-&>iSv$57}$m}ePPQKge3TLPEpLCO2&f1y~Wu)7!TzYJ=O@ktD4^}-R(Qf-f@UTIU5?`K}u~O`BiT5_1-l%Cl2+!ZO zxhI^2@M9&g5yqN`cGRKcSeZ zaNlt%!=of`wm#}MFEq#3l>$RXfT0t36?-Nd6taPMTIg1sBcB~^cgc33@Id!rEQ*=! zyfJsJi`7rPCdl4oYuUS2<@H6I3?#kiiWCb9DjL^UHz(~p)Pv1(Vip~5f4Sn*G#!@X z2B1FFLb_~8TTIq3e3UxrL|{fVXQA&5!~3?}2$ylI!VzreTTx`cBelpdZw2m?b9fh0 zoZ4H27&;XpOTj%m4E+hMZj4jgXz`gWh*>is1%C}5*|%hbmKzUk=jJaB9GP5grtz%q z$f&pO9Pws1VGbZ-$QN=4oH!0ewdDYEj8B`WPMOHM2*il%fsWm;P`oF9rD zfa=-3kqWy57P}T)a(ab3c4A{md}Yli=3ZUrqa)Ne^;v8tQl^8d%t1uk_OKpL zTRuNlteJYtHx>6YN^xI0P?W!?10!Pe0d&ut_FRTeSV?5K){Doee&_JZ(I@`LEbvWu zqKe1U@^LmuN$>A>S(13kh^{@GHG0Y3JIxivO@voRm4AZAvr@p6A)7{J^j(wM%d)s zI-XYzRwm&+!-{^h`J?6=_k*vP76No}DO?J5q=_&Sbb24Yv}7vm&vMa^&`Zetwi$=X zt{OuVmSN;agZEt4QjN+^uT_z#xV%n#*D0O0uD-r?ueid~*xiq}SPssA#(qdCBR4LK z>p34ZQ7|A`ix8q@Vriq?D`UTA7W6ueOJ>&iS$Cq&gA5!#uj#lyS_SH$IubRn>P^4q zO(m-Yh#NQd^t02AeAhqSjFAM7q|><5%@EZ|SGENrNsFjAy|=_`&Y3QeemVtRSMP)- z8TBM*PKKm$jXOz4ms_1)fScOjKo>f@x?1maQ>a<;9{)OLe^DR2?%|@(^K(nu{-vol zE{=Goi`bdz$8bHw5{39o#p)5d-K1g-iMK0Xjt0 z6vUI`@(9cER){fZErZYaG{PpQ9~mHkmz?+%X=C}d=(+#SQB`E}MSl>RS@;`1X-t+m zUTqqCQ0YgjZ~I&pihUn?O>15BC_|ms!s-xFvX&i{_p7nyb`7gBi}s^e^+y^(>0Vrj zF0A+5GUz)Pm&0Ou&^OA%x{3eeeA75xTWXFQlj~Tn&JG$zBMZ_>lx~93H$t+uXbE<9 zKtbfbghye}jy#@(2M=e&2f8JtjSn#K$A&6IK2hL%SFuL?facK=$CtAk06##$zrQb% zjX2xrh&HG6+iz=NBvjHumXDFFvs2I88lIj3@C39?n?Dbzw*1&NnY zTm3odm#L+xPtR4#YXDQ;oo?nVy!B!GWmGq}dqa6)O9HxjfAys`Y`V!>@A)Pbv&u9C z`ec64=%Ja}1gZxIf*-BGZjfPl_lEg!uE7ldz5UdSdk09 zo?fP+WGy#V<8w%r-7!JM5)^99ccQgAHY4zHU`DOPpbzf>h=dyGw|Xdn)}jYyW9TTH zaT9vTE8+uk_k4?GKbS6!{i4^VKR@{G4EI#J4CxNVQTRA9*;5ICRZhMO>g;c_mRK$X z|g*vQ5;`HNGyer97iZH1?RLVzS`d8 zkCKP`QCA5u*beA`%&dWjUSYGyeRk7tdRS+Fth6^EbPu3`9}iz9JE^_g_EFd+O}WwO z&&yc33>fSbZD=^!pQ%X(lfKJ(DO`Zr30J+I@UHxj?cyuz-8}vsv-eV&6cJW;DLO}2 z4$t+_tqTC3D0PRcL_uGVb^Nwzx7uxTsb@nR-iKCylom)*(t9bV^rfbdTtFU2PWnhy zfSfdHdG4j=Pwmy=sE3x-9yP^6uf}lA)9sSzADkd5*;%_fYQqHa^lPObf)dlYYk#wv z5B{!vZFXo8yIeAu1If19G=4^D@YV!o`>s}>mEbuI-Gb(OO{0XZu*h}D4*dqEzn=7T zVj>)2npr`?y)fNSYTioW79Ov`t1*mS6hAabpqRv6T&lEP^!8;H(JLJ2moaza(wZwU z)WJ7F+zU{(KDNBwWO}7U%+YAyVb)|HLJ(c3eeBp{FF~!|> z^457Wkm>t0Orn^q9@jVGQUmaqn6U869|XPn!eHdf{_h*_r_?HH-v3#^<5jm3Q^+@( zh16zMR3`H4rVqMq(X)t7;<2e)xLQr>zWRYH*F||I--;(l4ahVke|DTsQHDVZ?Wp;|d^Lew{XA9ZbP zwiu8O6Ctx%&O&aT!)13B)K-oUFrA zb)bReEJe|@ zb1Nx^8`97%#i?^&zS|$hedXWj8(CL~bCWK^$$EDGCA54OmCzR93qQyc?fi$zB9Pac zy;394?VdPsE-0*SCN5e)L#XYI4uEdm$*V5~Y&EBGXVDLN5yG_jR8!PtBvY5wCWm1N zSZca~i=1wT{+IDA6SHdUZV{$3I?~l5?Y^Ik-OF#X&tx4JL@qe6NnZbYv7T)vA zE|oAzGyFgg);4;z_A7!wM40h%(CaF%DQaiz*w7TM!Oy@y02TnMnpFDB>@rFt@Uaaj z^%B9GLwbxArYq;;+%TqnVs2A`NkpM_sK7KmxR6Ea{9vV>vlc|5Q;z3YL77k-d8di# z=GFyAqlAL_QojXfB$oe>iPb8i^S?3YW?@F4HY}U0vc_?%g2c3Zi9=g10CQT$fLRGE z2oD_{2o&OL;J?V+3rKKc2=~%%;n6jSN}Mjln(B;LBDql>;dgAsyHC*F798s%4D&97 z-f@S65^st56milnBZjgZMp>_a)-V5g8mip30ntQz`$tgSFAEiHUw2qR2r4FB5+c$4 ztF*fDzw$!_tba2*e|7GzM=_*Vv%$I7w^@ozt$FkL;6RQGHJO3zlMbHc$l8FwkhcB) zIaWW5saLC;t8~;R$neb7VGabFAuZnmcf`?<8~g7e7QC~*Gu;Hw{p9FoW-NqxARI|t zCA=x&GIG!@J2+WsidcsYYN|eI8Ga4^+d@@rb3uqwp!N6iI6+T*&t+d@!C;%yKfhyP zg4lf@^M-IqbC}g|C&Fw6f{pUa&ZuqkO?Sf8!vTk(A^>p9XpQo+gRxA_OQ z&x~MGhcWxpuqw|Yxkkyd_EK&(P;}K`go5NS4CNke|9ylO_g2~lp>ZB@s!zdc^<@Dr>p*Ek*|rs z^zXfmGDG}fEgoM1*nToWB+-b%o2O+zHLaXF!Gd}rc=GFy&cBq&j@s@t+AP}AURxwH zdT++brY|=6%Z3;?34>x5y&Z2U_2{rWk-X<7lj(Ca@e>eYOE;i(^0Q6$LHkLt`Yj)} zfU=(x+1kr5A2;+>n=lnRmnDFMmt(m*En zuA32;hY%msX``UeDLhVtxluC1O6h8E}yWb zJnWve#nEXjUvSk55iSK~XK&x6NDM+n)UZ=I;|K;YkFh@ei4sr8ZKat@$N*65iZiEH zr*VOQ#B8Q3&(n|ttT3>E7jUDUTC$iN>t)TeRt*rgg4=y1gU#61$}2w-Dgn?U?`c7RE;{05_+zZ}Nn!lf;|ATWOx4za$2-{6vTuz;TNb?$1_b>Ub^%T9YA8u< zu$!`rJKY^Wu3Q0ci9!w+N>qj|Tp4U0OLmLe5u$Qe*BQ8sbT zPd*pV5rX1;e_x=*!51LqRN0dt#h=>?n7MvZerHI2%DS|~uMm}8(93~^a4F6pbfBeK zkehZ5E2#i^A{$`3p1*j3IKf`tmD*34`w(R9I2JbklLW42*&9ywwydhmQ(0KIil&6B z{JO1_|Go_3&upb-3PHq)!kx0Iqv|5zhS1IqeAhA+OTvXbfcnt3U1Sa`174b&euHqL zt4tQek&F{?Fk@LYTHE|JHpbiJ&a1f6EmfvJUd^U1svsJIN;wpjWSG`n~Vz!{L9T9DKL;* z(|_pk9dh0k-s|WpD+Iu!GHi-o3V2V=UnoojK$(dsO;&KOz7-Y$x4uH+v|7Kt1S4Fn8&pI$z-oOnAPMSfdiA?twaR zqj2=A5Rdo?*feC3ua0MT6;H96&UvBHfcMp5eH8IfkWJP72U5|J8= zL%D&nyOU%f>!tdJ?E&`JQ=QGs|q+ zSLcPN?+!z-V=<)Tquo#y?Vh|i^H&Rf)|IsxHQl-9>AQve?^ZvP&f72hBAixnz)$0J z{!rZ@V?A({)A0p2E%M`^5Vwspgiewu*jpaam{KCs5B-y-Au=7?(k4C6hVF}&2#j2y zboPjSTeLU_-u0o}ZQFbFUVM|o47JQB^l|AfssUmfNtNG(o zT&J3tJG!*+;wg+Tfr#E9;0}wASOnBSXKHEFW&2z3cu*00R%*#!bg=jxal6hVtQM>;yf7ii{4za0^W72=t_0i$XPL3V-`HQL& zbA~x*_Vef52NgLX%f#nAP(!Pmp-}sR^5n_Ape3`n(G+RbW#xMnjF|}BzMETU7aUSDhg_yYU6BN#b`&g*ko>bgI zSedn8$mNt%iv~X9DS7MSI6l+O0%Mv=Qr=N~_vJeZ;Hs5KD5aY}nB^`tw8<4;e#fMi z_08i??1i2yA^}v_z^T>Yhek{(RoG` zWcH2@0{}R;`8f`2Rac8%En+gpi-h36(> zxOz)Q=h7&fKIlUBbB~M9HcOJhPMad*B5=S!5$2m!MyxC|5%g<77>;VJzgL6-l+LbM z&Ea1C5X;DvJRH1iemysP3~u)3e8(TmsbJA56HV1o<@I7uQd-eoppP9c8PX$ox00f2 z)Exo5P8`xbm)I2#iR_M;poI!RrXERIh+PmE(IUoGh?x2A%*SLKrLzq|mhFzr7DGK; zpE$Mb-+oONt^_9wBd8f-DiwFr+S>e5A+C>E*TDx0r=F7&bP ztm4k#D*gD@Askf?z|f@)ZIHTEcc6;d79HJ3-}al6hLP~2sPLc^)(q~K1a}|Qa|hr2 zZ8e!#Q>sny>Zb`g0|tk*Z!!2Jq(Uns5Oml6X90=BCxOI<@wv2KKPYS8fak!X7A(Oy ztx4oD=zj+V;R<53b)J^hzZt;7(1S3e_6<(e1AOiyjiyg2EeMt~_1d9fIjoU>fUWz^ zTx<Rv90Y=))%(GoGnAnP)v;(nY}6*$Lg@^u#eXMlPnmq;~cN0L9kps9Xc4S$bh>PVgTim5IT@z!0@Xf>t#Oz z#XwM76s^-wU08NnCLX)LU;++Dp>?r0MLJn{-C`plhV111TS=--@u4|pmsNVLe9!cF z>X#^9dua=Q)OmeHxv+y3zgnaq8&g5hm*NUKjMLXEdGRTZ6|P->Y~OKTRFE?G7&BN+ z{q=EYo;0kJq%z3Jflby+m?rF@Q;fc27WFES#DoV+V}wzUz(b&&SvaWerHmZkt*d#% zQ!%9ajDzH*?0@@-Yr0+X-$(}Ljpx`;&mtELO`*!+)6#dFimcXFQ`MHSdcOl9r^(%j z5LA`xY>46e)?F7PC;0n@^8d2xcfASmk!}#g(3qODOJ^}G6qtvu5@j{?xC&(fGnxoN zVB@k=La7d6T@w=PZMA}KG7``qOBBxaEk?N?4LObzR5eS}yaF~Z!;5yR`+>1>ro~40 zk29KE^g((Ru$a7(B?6zeyCs+EP|Lm?h8TgA0fMs12rx)SZLVPMqW%xCFqNh?QQV+PuqYX}?O;E|`(gyu2dqD?9>(Ji@o)HNJi}_ZALLjMv z_4uf@WAA|XzYlRF1IoQ)cJ87mZ1~8K*4uofABi-Q{zWq^AQnR=+GlHCwRA=cIi=7# zc8P&FJt+Lg(Gd-q2`%DvY!7wnMDccr!)tMuET zygO5kq;3Lqym8!n%qZclEuWOGiUmC@)PWv5DnBJ`Sp3XE>!`s5>w=l}e`Oqtj%`gE zk7l7UBT$c_Z<(M4v#MtXGk3Jeg-@`c&$q^2#aX%GDyRCXy@SdDZayg z!sN^xFZJE$zt<%!_mg=~`IZOPTPFkF?o8u0MEb*QWVViAm)SYgVwMS9wjYjmB6`oj zpPja*Y#vm13y`AGU;Vn{owv8L~|-m z)Zpwlr^M%LfLrZI!$Na$5L;G*W=LBqO0h#Gt8q7qs( zX5u@sg0@_JO*e9Z`af6w52a`rGCg|&A&B(-ER=j2^<8jR?T%EmD^b1Ktyc{p)nFvvpIk*SZT=y+SY zHp8o9tx~zTej)d1m1&mxiSvbbO(6VyW_?5VKfDb?XbF6BFii;Va&50v(sGnKNNtVK z7uy&IbmUk3_7FNccE3TJIaOjG>s)3M5eZv+@6fNE06Hjuw z$(WIB^Z5IeM@x(i(l(4L>*D7x^{ zIgfYZ4!mT5%&77hxHo#go&H&$D0@f`^NVEJ3^Qeuv;`dMHEZHDvQ=79UXyLTmh<>&n)s7w^$fRcsM8%mclK<1_G^ ziFoBoXIY7B^!Ge)r0tey%Jg}v*w$@ZdBqZ^beyB`F@<5QLwxh$E#7X!bs7Qem(^!}C&WJVW&N0iWmKB! zeLP;={jYMjnnqP@^5SQ2qpJHHLu8V-(!T_)&^yhl}NPKPBp+mMVDsLu#07_ z7J!H@f6O*r!YAO^aJpUba8Q-l-hvfmjwchJ(*|Gw4Qh*_99_4?5*jsIp)o_|5|Qg5 z842_2<6r^dPCWbBuRN6Arf%!t+C1bgISjafz6;{w=C4gy?SMPJ^fVuq51QI)?}D6s z4!_jd6FZaD&AHQ(ooI&zy96~n8R(e6rAKzHr=|7AULt45b#0Do!fg-H?XCYOqoVXa*)RR#hmol$CW<#|uTT_K1m-vY$ewzy)_>O@h$HIN5w^kjuWp3T>WDsn+KLE&R=T01A<8P3 z%3f~p9X8xfx02c1MzebV3~X}|JH|8vjkW;{fS5iT_>PegJ4X50Orf?`F@MJm9*|S zQz;0&j`rF>#n&^)RDEy*d?@P_E)VNs8$UExplp5x3~UAI2jZP+PVfIO|Lu>&Y$O>0 z`1N8hFN@v1DPyCejO6}9MB4=gDlG?EF^@}N#;<$PomRQ_`qvhTZ6=|9_ZsEKdK#+t zYe|_`eWwjRv+9M60*NKD&Lt*=S7dG3u2loZqDdjjH1@j6OKe~v=4U+lyP0yd1sy?o z2GGULrZsXgWo$pyuT-h+%ZMdGnVR}pzWwQAGe;m1OAJ1X@S-rOef1S2t4WGM?t`<@ zO7RHZkupKse$)57J&6r!0#Qoq|e?CPgju(KW}NeLYF*aATUt)FBh9xlV_9D<3gnI{d?wz zPTZHdjoPPBsHm(>2&)1QJ|385X_S?wH(qCpYOI^b!{O*$}TSXB3_)mZjnj2ZV8Aqn3^9c7zQn9wM=ds3LY?Iq94OY7xX8~ z?PTRzr~Y0Zd|=8!SLujg$IvGoEqo?|l9WO&z3|3qtqfnjs|W1Qh-!7dd)`#mo9ncHIAHTAD#&%T+Ox?uuvB}+bnkV`orm$}4U9zg#@*W5yAdwH z&v5VE*!tcb8Nw__cgzvTVDk-9C6~J8hu`k|Zup7T#*DI4GK4mOB{IZ2b;_uZfQ!7Y zJn;0XOt9(f2JoyCf`x}%j{2yin*L{VG@nYBXrJzzwycmrJme@L`+Po|KHY&IQvshS z%>L{t@%;W5spek$dsq*S0`?0Qwq5q`C_7cd#^WQ!{8|CIkuvfb7tZ_qo<%sMJi3s9 zJkX@%I!{o-VyCs}Xvcv-E7+kN^ohF5<2Og6<8Z0myzilUd`L!y|0xID8TGwF4vb7w zYn&_QD65NZcZIfjvj|JrCHmCT>mtVRf^XkHb5ZpS@Jd-X`MUJB_?26KBj3SxTw~p+ z6O(VHDyXmve_yogF0aLWemECAv8`D;K!?`o;!>(6QGES829TqqGqu;`2)*GocGGKXT;j6`u0KyJ!&uZo6Qw zl;qpSZR6-omQ|+GZ^MBdH^~ylnt}zr6}DTtr>JSW%)-0xQAQmgkDxEN34s59XD_yeS%4D_08ygg95z5h-u!B7OPAyV@`w#T|Fstaz_hIUmFc))9anXxSA-MHa zETC~EZ^mTmmjd6#!~eAaqh5>40SO%rPfET6K9)AgUOO#nWwft7J%x}5+(s(CG#7;y ze7kd~#3Z24UU6!2tO5)jsbGxi1&?RHNlynCwVDBZzgBG)%0K~J52CR%(TEJ<4lU-SYY@oUqEsNam$ zPNSl$h?-i6BAdoijM{vHudhCYM)b6|ZFkm?BikoH@1Upl+~XXV_YE!jx<3g;crt>+ zu7@hC2O+)#Z_vIlsY6`hsW_U?r2Cm$vmkb^XA7DZHn;)uNR`TAHWhwL|IqX-=Qav< zCsLFDmrf+ET*w&n`Ygl4UD$)BjESrkmKxQ>RTrRSq`EnhWQ1mLViVh+Y*Eu|vUA$Z zXDKLsVmR+|d|Is7X^heH_q24smz#frL+4_uaFJnztQtSo`S$rdw4<1u4AcIOJp=ao zd?r0eq~m70oWOuY%Tg&K4cDIPSl?wO;G4tM1BZT4-NVPZyrGe1u51AP${QKMo`KSS zZMJ{}**^IxQJxQr@@Z0K0(k)`T+6|Tw9H-MFq65=yoV_mcps7hj$PcCJqt5}4)qPvMp zb6FwJ+-*GY1Gi(Ym1Cz{Nu-7r%^ynmeJknrcRl$Hx3!vRLj8V+1rfuJbN*g5@&SF! zQ5Hi;pBNNHc1ep;%pL_H1ZG`6wurj>z`@#M+FY6nCsvjNuogcON=Xw(*oQE-d%j z~YXL9xNi4bRVT)M$+i{yGvuK z%jj=&Lo$7Y(x}>nXeGg7lcsl;Rl;nro6%+HA=b!>Pc@WaSg+tqCn@KkD-o4KeR?JLz$g%X;j85ZU2{s^1+r4fNo@F);2?F`q}<2)BjFTkWCyD z?7h^e<-UNbIerTu*so)YXJ2W}IO0B>)!?%%=@}Luxnl-GUg>NTph@HCHYmOs04J`? zQVjHu-`<){NiBGU69=s8j>Ak0Kr_1iIZk*J(s~vH2h-lUd%9O#{<(8?!9PCS4Vde$ zVbBw+XYPZUpYYy8@~7(9O+1$dMFHOGk4Q}PYNZ7jb03LD z>&A%&{^#C!Z<3oSWPDN(vzzmUZ z_>5v}EfO?De%z;i3c4blwHKr6M2*&OM_;Q2#sUV3vJyz~3lbI^Tb146iST&8cm~HK zvmffBbtO!dz;R<85vEX)(v{M{YwsGxkVjhJu$6!9;{?+gqXCBS1Q^sc?U6@u@bB4}!S&?^dw>h>?(ktqpeHDWsWZvClLY6*m04!p~ar1`% z)HqQ|uj7(v-Xz9l6_iq^adeB(o=0T1C7Fl_hvW`*4(DqRpy*THhuwZr7Rkjcq5>y@LU{0F}#Gw$AQcA9 zvXr3N3xo5zw>0}Hp&m8_taO^rZjHDssW+?u)9Rf~Q2;+nv>2SE7mDG?48cgBB12X< z7vkxTMY~^nt8A1!;UfedLwr;uR4ybrS$)JY{+t6`6WJ6?=U#^HJ_ zBU^6!2&S%b0zrvyJ5n#oUf=5XBd%r$X0VO^MeBYwR&+(dXuz!vkDcsUN|pUn=N|up zhh^1b(j&2s9|3XC4?!6Z!{d0vEQx?=wmvs5c8hC9j7|nb4}%)5F67O-%U`*leO(Nd zU7X3RJp|$5k3m)*6~=$Eras)+6b9~}xq;nKm;8o~$A*l=Fd=R^43hpCc&dVr>}j&n zYKWP523E64rbjP9-DqbuY}~3m-noUP2tD3mjMr@fNBVC2jg>28v z64UW#GLH1brgG#fE zfvZg!o-Q~O1oShYPIiR+30;sSe%XRxF`|1lis`<)`Z(l~xFvKlWYPzIuIq47gyQJI z$bd8Uwa*#Jv;piaQ6clRaT?M<_YRq>GS76&*Z564HB1I3=tb(vV>nC*V7@N`sjYfn zj7qavTQ1Z`va;AavxQ-!U-KOlB_|Z5LlB{48iOKN7iyfbqv_0^6Mzh^e>LNZv9xWF z@3+3I24JMth56Bjq`UysRa7KW3~8Pe^$Z2Hq-4_kb@d|P@t9jy+!~xBRE*lwP*XL& zD0uh(CR@~&`L{J+JmQBV#PH$A$aGF1#ml8?AvNG5#wukQM!lLl5v}4^QM}6TJDM4+ znx04ca{p7uR1l*_jY07NbT8u#h_RmO^t+Y}-Q59haiJA8Z_{v*Mxo zb0z@ewHv_R_Z>u2k>0-)(EgI1{Yc+@`2{(^pu<4lv#MM8o9FA|1SjxuDr3A(nu0wQ zLvMzGKW4u=jfyT{VD}*%Ze20YFkMHK3X)BsnEO+34!f-N0AJe%Wzkh>pX9h)9 zig?9j)|&6(b>RDLCxM&y{&DRxz=vdlsfDWe)Df%eEwfI@W4uTeJqq#Bpt9Yk#{uy znJQ1OBCSb!k3ihCC6YeJw}a-p$yZ)Su6|d&=;6Iifn++>o9BH;T8EYGrBQ(&720U5 zd}`opKQyUuDBOQMzvTg=)%MTrfKsi&dTglMixhp*eO?KtSoZ17AB#x)!=PT!R83B- zQLfO&VTC&}hVx2`O=WKDL#lXT=M3{!jpLq}NSuR&JTYl_kns}&CYWE=$kZr_r!h&W zK-RzK=%ChPKT{NG<_Ft_Xj)+UKqrH1i~*2E$>4+MaJ9-s!ZH7}vszQVJBG}j{XC`Q z@s@R*h3K{v{cM#rY76TFxlGXeGCDZZf^du922!Kl)Y`E_#;QrpC1`e_0iYhqzrZq4 z3wmVo1WUT&EcZIU(4-P94`60T>>(KxLg&;m&DTNc^Dz;4>Dhd1f~2}Y_fu#aiOm>2 z``d&km@K*RgeyUmjSO=vxA+d1H7mKzBF(ao{4dptB6{bDA(ld1CU$0vGjtBL*Vbi7 z_PS!)O|M`sY}>#OU&1@u#&Eym4jBRcrYRz)f*`qii}ehXVg-fnHzE`HBGKUi|9cL|(G5K|6EdKouhg`p|RZ@Z}QwYV{NB0@qDX zsinuxM{g9c#=XSrEpxRQXt*heT4^b+7q?$QuTrbvt{u)MclnJmI@$D2>vTCZa1Go& z;j{GiT=`7XmSj4WY5!306L$i?DkFJ!b_arjL#qV(l zHx9+3tO_OPh|r?y7fE=fF?ic6DR-srz(r=}`~)**R6+DfYFu)DR6pxxQeSCnnY4id zI3#M^-p&I6dj`Ij&V|0DQfAyz9oq6DGYEc|qgNe;7-;bZ@%R8$;>l(P;-C;nRLwAm z8tFOoXkyEvFD8yDldr*Y>(%a!n~nWm*e-T?Erv}(8Xm^7cTB1=T_zs34n+(9O_<+z zG)>>%Od;CHJ1zjI?Ka;urLFhAZmbe3^PVJ~u2aPAk+vjmQ*JKb(2h>hN`c~jkZvPv z$Qj8c5IS3lrZyo;!!K9ikmSOafUxBFjV0p+pxD)IjFE;$Dngod;Q7gA0(k} z!2Z;@!C{9gPo%!}lg6X*8^PzP6%lG^6OM8!FF$wSLw38|cOV$S1B!{Rgfk^$Y^vsh z$qI9_5({v4{1JOj7tEtto{-jbG|z(`B3R*0YSSdqG;7^8yQ5c1F;ao*^apkzJGN{7pQtH>INYFym8_gxgGm!K#c&+LHoovh5($~Bme zXoHa^fe)Im@sx0e!hBk%cfqO|idv2rK?M=)qc{Wv3;72=uF8KP5~EXL(jOXy5^KzI z|5ad#ZYFQ)>_-b-`Gs!v*YLentw4DwL$(UxTD z?6E4kWTMC|>GLjbRk?MI0M2@Qi(>9_4P=Q4z}R};lOl+_J)caw0}vSUL!Au z*9RAHhpeP>&s_gfXGQ$MSdo4KPaUx0hF>20ZEN8bPTEdi4XHV(y}^=rE|jI9Dfo+i z7%su0Cu6+$%SNoF+~)}S)Q2fmRC<(I5&8B7-8WIswEtpxKXec}?WrPG$YnAfWZAVb zoU7Aw=!MCD4Y3K=mC;H@N!h&AyrXoHH5>@TeRu**B>_<-#HoE3>#cgS-04w_vEEJ~ z?D9oC2q)Q1DKJ6ayl13AG=d%KxFn?`j<}7hz!g6J0DS zfKH?TdW2GJ2e$~aIgf*6vsRZyvaqf<36s;!{R6^DLGu@uqzOZTD z-yMpVGU&Ywr{5TQA6j^FPTQ?sL&6C*I2BQ0Do)v}4|_UHVg zj|VTO;Vi95D>(GGk3Q(@$ZZ9w$+w%c4^lAmACcszdNw%n>b?ZG{v26{h}@ZXfpR{4oWag1PN#SlDB~X@g;i7F(c`sojnq`6 zL=0JrLY>!rUY4ovGQtBHF$Y@r@X8zE5vsL*3iU=ES5p5eydl^xHDz{6x!v2s8iwb^ z6_*PBJrFZ9?+t~NE7LI__u8X7lSIo-6)}hp3PR16pAE70zU!ALI3K8 zJw{68@GS*EfI!H|{-OXbB|V8-zAveWur`6hoqlJ|>)Ne~!D_ zXxl;YAVO4>#?cKyU6ohWs?ESj_%N-#=QF<7ho#%lJeCrm;;eP~`Z|PRuwa*E@VYi$ zkM`u?A6gbD6WgL?K0QVslBK?*NdkHkZLG@UWpE9FavE?GgGq83qnuFb2X8=xaj)>aFE0Je# z*^iypv&Oa(S-HMk!1h_h;oTRs1@e^m-1Iu!5p{&T1XZL8!t`DRPngB9p|lwS`Z4!- z=EkJNoIZd)Apqt(nx|zdw#znkh+G_AIa#t+f*N-aKDF59jZonf)npC-c;k-S=z?I2<0l z1*CZJAhL-g2fAkilHQ)Vk}<9acl7ceN2G{Ey{O7i^f;NnwnGe;6zhx|24FTbW!?lS zMFa@{pjzDr|2`q31#pR5&C+5Yi7}L3?SS>X?)%%2GPty#nL|ZJTbCNHwlf3@T6uIjOd;}Z2`bs}u$O-aX`7!nk+{FxxoX5`;WY+gAaT;-Le~j|QUK{vu2}pbDs@i~ zvGT9lS=!TKT{(5PzGZEKt?sO?y82K=iK^qVyX?e8!(I3`1xk?&JjY9Y#oVROeo_#C zU9VDq^}gftfMfy}5fkV5zTRq0BlZTfe_GqRuLI70TaWvOTgal(poCn5jC>0!>~ZAXM+ zfgDXtakM0*D4ofR-am)m&Q5D}3aiD&=6SnGwy)x(AEdE*KUzAmJnk9urw0%$fDIZk zE}{tGT$Iotjd_XG6Xt#Z>3|rnh0AnSbb4ujauz=S(6f9m=zIw_b_=@(D<2i~QIJmb z+(7^x=x0EkVl7h-*HYIMsfh=1kSK(Em!-}Ncx~o8`TVZ zzn%moLkx*mLJ6l4Zn;j_i^!e)4DijUi-Iy~Uq$?b5se?ieM?NXnWMLI+APt?nOW5| zj3pi1hC|lqoc-!>Vv52lqQbi@cw&hX1GggU4MaP3T-F4*^=v8dkp55xt}-F8uDc>g z#Ndv-%&2DGqPce;(Mpt>c|`MT34r+D80kYR1ijIh*c`;?fvtpnS!%yrz^jSFmSQ5; zCzlA=eCa60{1$*3#&sl|N+UkhitwNv{5j4Hr&SG;2(PwUU(8{Y3@ydvNIX4P$`Hj- z5%&auRaXW%KK*uMuT=xh8W2Zky$(xqUQs2LaKKJ7La)gbIs&!?F zNUn{X=kLB&o{R`{b?SVQf-uohg__{wQmn__>T!wn_k)I+^vn9IS zlVoin!lbQWT^qN~tkRhHHpr60w#23z^ygl~%Tz0Dd{hUC%du_rug-dP~3k^MTU^UI73sq(S z#!q9|l#fg{Ul>OnZ_2Eo74Of4yn>$7JE6V9LLRva$Iuk)Hw10C(M6Z%U?s}$lOSHI zWn#vsIB1yjd8vQqiTKp-GOHsY?3Cr2wTrCr;fRY=*miZ1w1Rl+zZGrF9|mIm9_xq2 zH{h+^|FGbeF8@&~Qf`+pTA$ciC zG(2b1k}WG2Jgz#CJj4xCA+uoROjcai)-qjiZKibjz``=fps3d!zsb(*Yg6?EU*V)A zrvIaH6v{h*=y^ZDbdy_W4vGh$-zW_xa<%N3Vr)1aY#mtp`*e`eoT;damRozrL1r+= zoMvSFacDQX!68fkSjA8aBxMqv$I=!sVD{-OWuCVSFfrYA7Y^*>7Bu`*Es#@!)Blf? z4&d|Xy=BKi4nb0FK9Y*34j+xM?f(;>%*_PXufug{1i$ByVE(x=c7J59yU=UOdb-9# z*-}!4Qa6kT$>{A9LV~`oT5CeYP5)m`!1B{NGpq7l zmdplS8h3}a==p~2(!jHnx2cw8!pn1UOgf>X`P4w0=0ykg_S(Kj`rN2mp>(N7 zQQF0!Wsr$gxNm_;+Rq1$E7kIiFInCM@4R=P0t3`WZdGdXjIJNfa6#FM^CMvQAok9@ z0U1QWjjTjg4&fhhgVtk$?=EfBghnb$SnADwJXLUs0lRE*PpBc{J%cagk4Jpe7hqMp z-`9-<_EnTp<>OAO^7I{K_y-%rhck<>!=vK(A~olmGmZ zZpdv=!7UQSfBBdFYP+z9pt^Na9%@_~9Z8^C{|XouxA> zV0xWo0W>id0tU*EsJznRy4oqM12ewOo`=RD^*_w^d`60evB z1a@$@T>qr&v>`r{QZXpgPL$~5;Keh*eVOHh8+6NNcHdrJXH&l6-o)($$0WO#KS#$L zloCZ8$LsdU@C^2P3yJIwlz8>^x=IZzM$5nWc;F>R3sj*cn40Eaj$$iH7T1*=>dYujZ`@lk zsyNkLVrb%4)lGW7{XY4a?|u4wGPbRwKem9GtFDfWOC!}Zey@{>ITQ3vT#QovdcJE@ z-&v?-Rk6`0c&*t-!2@%99*k)v=0GJq=!;Mo4Tc48zVs4$v zkqip(i*oSctX5~A>uK(LZqMsT>0SseN{e{ZQ!qjeij`Q95 zCTf$%X%9$cXxpPb7pX1Jr#_FJxqSg8o&3V%8ZI?1SwbTD+PIWv_BB!3Sb~W~r^%@C z+<}-Zv7_*I!98Rnei`9Qn%l%nVu!;n*wi>!t}p7Isgvy9r(EGb z$7eWG4ra0j5e^rvL z#bW~OnUC(F8m}KtYF1M%f4E^5Diw?tyUF^QYoPGx(}?cGU3S`^Djt_*Ze=QFwO@}^IkY=) zf5dGQ`)aW>6>{MDuEv@`WoSme?@(B_Iz%eUE@PL{&oZ`feq>`PT$S2$Snrz9iEnt` zMD}+dKdn2;-hR6C=KVKC1tXuUy4qj5lhWdCDwU~k%D4|6?q<;!wa$NFUzfQ1#ho|5 zM7}xWDi$oYp4L&+yK{Sa9?`TsQd*5Q?9FL)#hyuTSWJ65h3=mDB4fTSj{g0;=KPvZDJA%ea40g1rDJN zn`zEFm9%egyY8_pIebc~@nRTUL0Zhs?Z{QRX+z;%j@6q~I%`>;KDw96a_-QV{63K~ z1-+*A{5mndY*7a2%1Z)PoqKoQrqYBr2djLEkXS#QuGS_Tqt(LPlHqUM8SVXaf-vt? z_S*Sf>OE)6z$~+GZ!0gN{8Ol#J>1_3zIxb3j|#tF+)ta}XX@g+$9%~e^O}wN*zoSm zUU@a94`l(5#OEW}3SOa_l*e8_Cs}>t#}Cy(JD$MM1o5q$DxLe%cr~@9p*FlH ze;J(HI%jtORR4_0@F~G9g4@4qe!+MXVl{BB4_O@KT8HpfB?0=x={S~Kc2iZmrZ9C+ z{1;M5NBb0g0y7FBkWXPLMbT#J>XQ$rU9WDwQ~7~&y=t5gpLv?{;mJv)hktH!_dd+n z_V1&HgVs#2_N2^OaTXr}^W?8CGA1xCownD}<$jh7#6Y_S`*O=0s5!q)2E6uALq~XGbY?bf zUAV@vJwYYqvg_8j*eUSe3Fpgan|M!C9jw6~^xXaB2uEVw+1Yx&9qAWY3xw_8xJD*j z;x{XH@Kdw6V_RUodw2ZF)HB>R_=mjswwMsV#0KicXUO)xulrjQh7$sXsk-+oc@DO) zwF#t)bH8MDie>R~shd12^Qb6UzLG&D|%whl;+luJ5S&7;vg=hdlPl`-tlTkxbNKqdmrnp;ez3>@5OH zAaR*oICcSC|L3q1TSG+k{I9BBqdT&R5f1l+)C<=WBXdts7MO_k4hY#T;?YwRsgcah zkjis{fo1#3bK&PuE8(>=^4W=Q^%d;7he&jjU-+f<$?uRG00 z(A%XGx}aW{b=9$c|amo6@&;z(1*6&6~;=YXkoOR@V z^z3TNzQcBVuBUvP*7dvXYPC@QF|q>zo40OCAe_0K#kJ*CW!R*tNHw$!#$MOGPS6ms z11o#4{>=^UJJ0ZC?jw&6dR^No@P4rMa^u{?Qy&$aBAhnNV;UlUMQl5NHX0_mtp|Df z5odj7O8WI8i6hzZ2D;Hntg>HhS~UD4Qn5Qi72;=dv8S=Xe{{ud5I=2t$6gP?E5z&t zxgJS;`KY?=)~RURJ4Gtxq> z*ef<@El@1hU#Rmx|E_XcCAlao+q{oJZ(M64(x z!bs)n6J^mG)fa|pUSz0V4t+c-FJPv1UZTfA|4Xq+e8W`yn}Cav#d`-DCp@b>#rLp$ z^*)NN=5)L_NGiFVvMWh?vS4rD3yb0L?LV}}pV5()Jbel8W<6(&!^=OgOIx}cy1acg znq+WYyz+C$!|p+!{Bd8YVMmEl2mUXk<6RYHuaXMY%HEa_WJK*@Z`%+&R>H#jfW@bE z?^daDeQ#aCmRc#|qulj$qxMZ6+7-fI?yb*u)1I+S;`iC6m2>hCyI-u@f}||Zx$ZpP z8-Dr0$)obzDVUiLI(#>0>yC@uck|v&XyPu-nW)b-z&+Au8JjEmQEWmu$W-Gwg>Pco z6{MQ_+5wHaNiZ+gBCor==*xS!UzFjH^D;zyGi7)K}b9nyM_g0;#Rf`-(bYJIy`!&U3M?Kg)LYvZgr!nQ3oFyBaCECwYO$(x-m=@oyb7X8 z&uC9(i{X?A&y48j-0eu7BQ-_DR)R>L;BhPct7$?ixe?!4l1h&-I}`;;yNncn|M_NL zwBXhAp2x22pW&Sou73JhtYfZ1=n$Jb+L;Uf;ipMk^4a_Ww2S=sInIeDNPGM(l%V(v z@uI*#lTLGr8kMiba>Mf^~FSR4FW z`KG@<92r*?IqGcDv(2JjbGu7b!B_Dh;|-*N=daQ6+*Kl+=eJr$-kbaom3fg#Pm4|8 zGSVz@edK}CKFK-#!hsJD*h2@;ZPmXGpUi%xJLPbmm-}NYp+h&WUlVs9837GV@>BA_KSXYi=(qg%#K_M`w(26J*>27L2mqnDMYQceFX%%00u2{krL= zoBT#mHqeo&mvt(r{gK#fzaVH!PHh81pX7t+buzG+4ESu3|Z{Vlx{BY z=dRqO&2RC}3%I>~?0k4CUj0MpLo*KpK|$4EZ%V-t2k$dJ;%SlZ*gsAe`v&Vt8{D2&+2$56KFvNPu{~WNSynHhS-KayJOu3i+}v`eO)oA+HNyaD3F`*YID3<-h&T$ z^2aYPq?+a%!1UQHTlCH~o1DW768CTZ%pZ6%g?PtRtez?uKk&@(_Dv1xbVHvJ3u!;f zp%iI^@WXLvY72+5U%Aekx|1xU#a>a>I63#NG$ylHa??gQJrA=b^H}Vg@bkv6?$OFQ z5@sJnSbAts2scW&r#6y2KNXSeZWYge{BgQH2Chpcymt955BOH*pkj2qD%uz!- z8}8wtI^12{d5OR9)AWv)_Dm2Va$nq#k(mM{Pv**{@rn_i&eOynl5Afi<<#uqD*MrA zxq@f2qjy}2B)*8vFHvob9>+q$y2p|fV)s-0^-o=ZvA+7WYw}uBCGm^s^k95|0e|+H zsfNrtbb6BG-uI#>(+w~4PE6!H6zG`Ka(!ax6&+V1oP6+mX|lDk%`|V0{bzk1F0zZA zJB>;vIWulukDcJ*;^9JIAW#V79)m!j(5t$z%UyUpMA6viBpITp>FPq$AiLXADJN;J zR30t}WRa#%at30Cz8E81o<1UrsnDmIRcS~AZFqg$rO8s z6`(^jva=}!yCed%UXucd1hhgSMm${m_N^K)ZsmY=$u9OZ2M87mTa*qk(#TX`xK1P* zS%YlrYDb2E{L4P|jTWzKK2tb0LP6`778N3Dp~ZT>?a9v??ZzRA;KJSd??Xyk zs~0Rs2fE&hqO6U_J+a$n3hU=zhs<=QSSz5(#qwyYLMw&o&dC`ElL*~YzWZidLJI5q zdq*GWe)HrCe>z~xg-SF%$(q)|d9;!vm7haZvR%!})bZje%|YR2R!srPopx%b4jk8? z25Wk8$#2hAI}r6^%VqlWu3@Z?xP)v-Ro7QH?^rx8YQAE`JwVzuD6zjq2)$$H%BQOfAoc&}xZmeF5wzsccl zzg_I@czTlcf~Vz<^O>k`Ofu&M5|S^6erE~3Q7vNF%4r}VhuYlPc2L>Bjl%3vT>W|@ zt*2CX4JJ95G?`ceL#zBpt=zba=OKd0LE1%zM_oHF&S=?%TaeS^c{2#YcSx zV|ZJ%%#|~u7sQZnb2pe1)84?Fu5%$j;wSWpX)w(u55Y}uL?}|{Z%mmZZf@F`7`=sT zN58_Mr^BDvTNKi4k6GZ(DvY?_n|LB)7;=7qqptUJoDV}nm*XL>v} znFU@7pc;D9n=>phhZ~u_j9WbxK1VhZOWQTimSM%)=_5a1CPa#q?bdbWOiNstIOdMC zM!L?ADH4x$zWf&%4GCSjihrlA#Z`RB;~0&R$-s`Km90i{CxfZs-y|?&R}&WsW62}H zBmtxjM&_eX-D&C$BC9twk?5!fXZNCo8}VNeDd)QrX=80Zx!fDd?DC1Y702D7-W zKz?fjG%VW&wg@Z=^vaS(z$gFs3DmkgR)!V-pg<~!WS{{Q*n)N~q1So>@FXZ}Nyg%6 zaErSVgz?1C1R^j<2pk5^1(M;j|7ONlMA3f`GvLfGj}Zp!_RIG`yJ8eD6owHWVDECQ zEZMw-`t2zO2JkCD0oEEEi&^2>e|P~oSU8k%0W%9-TxnpuFC+faF3ACVe&c@M0rVvV zD0$WQ9~=KpUBn}xpj^gx2@j40j4QAojsq9=%3df0Tt|yEaAtwKB|b13z&HkCkI}B? zTdrYN*GfPb0uuhHIgl`b%Ba8ac!qREO&A1$T7)uk46r~}J_54|s$0%LNC;x-adj<& zg#VTcQPgq5kmdStcI6{B7*Ee!z9MGqK|s9<5onB*mGMr3za52`KM9fAbS zU83rd?I@(hN)mJe1_w+B4z=w615{U!lMt|WJp|~Wl3d(Rf`V;*Ad2b$MT6`~u_YTF zPy?)l(5N1Z)g0(@pl+oB2Dn3(hP&Eya59$M^*?O-Uklv76!iatUSHZ^S9=}y|07~x ze+?NJxc2_{V+OXAXV!TBpA+KmW8nW_)WH6lUKu&$FP?W_%ryT|S^-lE7D#>ugYlZz#9VKXb-__5;QfTP$UitMQcEz7$|VTfqN9tBESnc z`YHmOfSrdea4KF#0~{a{M4<@)H}H%`FI}J{fD`P41AS-^3rYkSfCh?yEz%JH4(tb? z5kLn52Wn!NMR9e}01n>}ISnO{5sHLkVOSU%2Hy>Z$wHyBJJ;!vz#7fX4t8 zOCJatg#xx%um|`cfLa|3Y#-nU;_@2@2WEH`4F<)afwa1o25dU3Xn;Hn8koRUI4l|m zU(1U_!BK1U0=ziX8hKCvhgl;J21Ou&`eT(21RM>rK^$1atfIl7FfdK6qyfAL zu#Q+sg8?|O)L2c!p`dHV0q~-LQ^g-R#F}xy;LyK}7nprum8{YM;Qkmd0*b?~9UB-R zYk3hcIB-^2Ee`?1fEDE`9SArK_XiG+`;!-r!>sWY3;{6zArFB9-zlxufxv(T(@Gi~ ziN>MV_zI-0wFMXi9IO{t^8#h+8lS?EIOJLy0uIOh85?jMSi!E8hkzqsYi&Wmkub3M zT#Z8^5NpN@#3^#^`T%L@KX7Oqcwk;F56lq1X*4PccqK>W;bK&36kjr9wE`(r(a6;m zs5yZlFV}loF2`Mg1I!A729SyIP*pVy0glE1VU0y;Vl?0gw3-I6#tE7zbqtPx1@gyo gM*a`L%f+fYjYOp_CJ+Q11H=mtmy8U7sL8|if3}4ctN;K2 literal 0 HcmV?d00001 diff --git a/mifarekeys.py b/mifarekeys.py new file mode 100755 index 0000000..5b05f3f --- /dev/null +++ b/mifarekeys.py @@ -0,0 +1,127 @@ +#!/usr/bin/python + + +# mifarekeys.py - calculate 3DES key for Mifare access on JCOP cards +# as per Philips Application Note AN02105 +# http://www.nxp.com/acrobat_download/other/identification/067512.pdf +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2008, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# +# 22/07/08 - version 1.0 - Mifare to 3DES key mapping working, but not final MifarePWD +# 23/07/08 - version 1.1 - Fix 3DES ciphering FTW! +# 24/07/08 - version 1.2 - Add some usage text + +import sys +from Crypto.Cipher import DES3 +from Crypto.Cipher import DES + +def HexArray(data): + # first check array is all hex digits + try: + int(data,16) + except: + return False, [] + # check array is 4 hex digit pairs + if len(data) != 12: + return False, [] + # now break into array of hex pairs + out= [] + for x in range(0,len(data),2): + out.append(data[x:x+2]) + return True, out + +### main ### +print('mifarekeys v0.1b') + +if len(sys.argv) != 3: + print + print "Usage:" + print "\t%s " % sys.argv[0] + print + print "\tCreate MifarePWD for access to Mifare protected memory on Dual Interface IC" + print "\t(JCOP) cards. Output is DKeyA, DKeyB and MifarePWD. DKeyA and DKeyB are used as" + print "\tthe DES3 keys to generate MifarePWD with an IV of (binary) '00000000', a" + print "\tChallenge of (also binary) '00000000', and a key of DKeyA+DKeyB+DKeyA." + print + print "\tExample:" + print + print "\tUsing KeyA of A0A1A2A3A4A5 and KeyB of B0B1B2B3B4B5 should give the result:" + print + print "\t\tDKeyA: 40424446484A7E00" + print "\t\tDKeyB: 007E60626466686A" + print + print "\t\tMifarePWD: 8C7F46D76CE01266" + print + sys.exit(True) + +# break keyA and keyB into 2 digit hex arrays +ret, keyA= HexArray(sys.argv[1]) +if not ret: + print "Invalid HEX string:", sys.argv[1] + sys.exit(True) +ret, keyB= HexArray(sys.argv[2]) +if not ret: + print "Invalid HEX string:", sys.argv[2] + sys.exit(True) + +# now expand 48 bit Mifare keys to 64 bits for DES by adding 2 bytes +# one is all zeros and the other is derived from the 48 Mifare key bits + +### KeyA ### +# first left shift 1 to create a 0 trailing bit (masked to keep it a single byte) +newkeyA= '' +for n in range(6): + newkeyA += "%02X" % ((int(keyA[n],16) << 1) & 0xff) +# now create byte 6 from bit 7 of original bytes 0-5, shifted to the correct bit position +newkeyAbyte6= 0x00 +for n in range(6): + newkeyAbyte6 |= ((int(keyA[n],16) >> n + 1) & pow(2,7 - (n + 1))) +newkeyA += "%02X" % newkeyAbyte6 +# and finally add a 0x00 to the end +newkeyA += '00' +print +print " DKeyA: ", newkeyA + +### KeyB ### +# now do keyB, which is basically the same but starting at byte 2 and prepending new bytes +newkeyB= '00' +# now create byte 1 from bit 7 of original bytes 0-5, shifted to the correct bit position, which is +# the reverse of byte6 in KeyA +newkeyBbyte1= 0x00 +for n in range(6): + newkeyBbyte1 |= ((int(keyB[n],16) >> 7 - (n + 1)) & pow(2,n + 1)) +newkeyB += "%02X" % newkeyBbyte1 +# left shift 1 to create a 0 trailing bit (masked to keep it a single byte) +for n in range(6): + newkeyB += "%02X" % ((int(keyB[n],16) << 1) & 0xff) +print " DKeyB: ", newkeyB + +# now create triple-DES key +deskeyABA= '' +# build key MSB first +for n in range(len(newkeyA+newkeyB+newkeyA)-2,-2,-2): + deskeyABA += chr(int((newkeyA+newkeyB+newkeyA)[n:n + 2],16)) +des3= DES3.new(deskeyABA,DES.MODE_CBC) +mifarePWD= des3.encrypt('\0\0\0\0\0\0\0\0') +# reverse LSB/MSB for final output +mifarePWDout= '' +for n in range(len(mifarePWD)-1,-1,-1): + mifarePWDout += "%02X" % int(ord(mifarePWD[n])) +print +print " MifarePWD: ", mifarePWDout +print diff --git a/mrpkey.py b/mrpkey.py new file mode 100755 index 0000000..c9e89d5 --- /dev/null +++ b/mrpkey.py @@ -0,0 +1,1580 @@ +#!/usr/bin/python + + +# mrpkey.py - calculate 3DES key for Machine Readable Passport +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2006, 2007, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + +STRIP_INDEX=True +DEBUG= False +Filetype= '' +DocumentType= '?' +Fields= () +FieldNames= () +FieldLengths= () +FieldKeys= () + +# this needs fixing - MAX should be able to go up to size supported by device +MAXCHUNK= 118 + +import RFIDIOtconfig +import sys +import os +import commands +from Crypto.Hash import SHA +from Crypto.Cipher import DES3 +from Crypto.Cipher import DES +import string +from operator import * +import StringIO +from Tkinter import * +import Image +import ImageTk + +# Machine Readable Document types +DOC_UNDEF= { + '?':'Undefined', + } + +DOC_ID= { + 'I<':'ID Card', + 'IR':'ID Card', + } + +DOC_PASS= { + 'P<':'Passport', + 'PM':'Passport', + 'PA':'Passport', + 'PV':'Passport', + } + +DOCUMENT_TYPE= {} + +# TEST data +TEST_MRZ= 'L898902C<3UTO6908061F9406236ZE184226B<<<<<14' +TEST_rnd_ifd= '781723860C06C226' +TEST_rnd_icc= '4608F91988702212' +TEST_Kifd= '0B795240CB7049B01C19B33E32804F0B' +TEST_respdata= '46B9342A41396CD7386BF5803104D7CEDC122B9132139BAF2EEDC94EE178534F2F2D235D074D7449' +MRZ_WEIGHT= [7,3,1] +APDU_OK= '9000' +APDU_BAC= '6982' + +# Data Groups and Elements +EF_COM= '60' +EF_DG1= '61' +EF_DG2= '75' +EF_DG3= '63' +EF_DG4= '76' +EF_DG5= '65' +EF_DG6= '66' +EF_DG7= '67' +EF_DG8= '68' +EF_DG9= '69' +EF_DG10= '6a' +EF_DG11= '6b' +EF_DG12= '6c' +EF_DG13= '6d' +EF_DG14= '6e' +EF_DG15= '6f' +EF_DG16= '70' +EF_SOD= '77' +EF_TAGS= '5c' + +# Data Group Names +TAG_NAME= {EF_COM:'EF.COM Data Group Presence Map',\ + EF_DG1:'EF.DG1 Data Recorded in MRZ',\ + EF_DG2:'EF.DG2 Encoded Identification Features - FACE',\ + EF_DG3:'EF.DG3 Encoded Identification Features - FINGER(s)',\ + EF_DG4:'EF.DG4 Encoded Identification Features - IRIS(s)',\ + EF_DG5:'EF.DG5 Displayed Identification Feature(s) - PORTRAIT',\ + EF_DG6:'EF.DG6 Reserved for future use',\ + EF_DG7:'EF.DG7 Displayed Identification Features - SIGNATURE or USUAL MARK',\ + EF_DG8:'EF.DG8 Encoded Security Features - DATA FEATURE(s)',\ + EF_DG9:'EF.DG9 Encoded Security Features - STRUCTURE FEATURE(s)',\ + EF_DG10:'EF.DG10 Encoded Security Features - SUBSTANCE FEATURE(s)',\ + EF_DG11:'EF.DG11 Additional Personal Detail(s)',\ + EF_DG12:'EF.DG12 Additional Document Detail(s)',\ + EF_DG13:'EF.DG13 Optional Detail(s)',\ + EF_DG14:'EF.DG14 Reserved for Future Use',\ + EF_DG15:'EF.DG15 Active Authentication Public Key Info',\ + EF_DG16:'EF.DG16 Person(s) to Notify',\ + EF_SOD:'EF.SOD Document Security Object',\ + EF_TAGS:'Tag List'} + +# Data Group Passport Application Long FID +TAG_FID= {EF_COM:'011E',\ + EF_DG1:'0101',\ + EF_DG2:'0102',\ + EF_DG3:'0103',\ + EF_DG4:'0104',\ + EF_DG5:'0105',\ + EF_DG6:'0106',\ + EF_DG7:'0107',\ + EF_DG8:'0108',\ + EF_DG9:'0109',\ + EF_DG10:'010A',\ + EF_DG11:'010B',\ + EF_DG12:'010C',\ + EF_DG13:'010D',\ + EF_DG14:'010E',\ + EF_DG15:'010F',\ + EF_DG16:'0110',\ + EF_SOD:'011D'} + +# Filesystem paths +tempfiles= '/tmp/' +filespath= '' + +# Data Group filenames for local storage +TAG_FILE= {EF_COM:'EF_COM.BIN',\ + EF_DG1:'EF_DG1.BIN',\ + EF_DG2:'EF_DG2.BIN',\ + EF_DG3:'EF_DG3.BIN',\ + EF_DG4:'EF_DG4.BIN',\ + EF_DG5:'EF_DG5.BIN',\ + EF_DG6:'EF_DG6.BIN',\ + EF_DG7:'EF_DG7.BIN',\ + EF_DG8:'EF_DG8.BIN',\ + EF_DG9:'EF_DG9.BIN',\ + EF_DG10:'EF_DG10.BIN',\ + EF_DG11:'EF_DG11.BIN',\ + EF_DG12:'EF_DG12.BIN',\ + EF_DG13:'EF_DG13.BIN',\ + EF_DG14:'EF_DG14.BIN',\ + EF_DG15:'EF_DG15.BIN',\ + EF_DG16:'EF_DG16.BIN',\ + EF_SOD:'EF_SOD.BIN'} + +# Flags filenames for local storage +NOBAC_FILE='NOBAC' + +# Data Group 1 Elements +DG1_ELEMENTS= {EF_DG1:'EF.DG1',\ + '5f01':'LDS Version number with format aabb, where aa defines the version of the LDS and bb defines the update level',\ + '5f36':'Unicode Version number with format aabbcc, where aa defines the Major version, bb defines the Minor version and cc defines the release level',\ + '5c':'Tag list. List of all Data Groups present.'} +# Data Group 2 Elements +BDB= '5f2e' +BDB1= '7f2e' +FAC= '46414300' +DG2_ELEMENTS= {EF_DG2:'EF.DG2',\ + '7f61':'Biometric Information Group Template',\ + '02':'Integer - Number of instances of this type of biometric',\ + '7f60':'1st Biometric Information Template',\ + 'a1':'Biometric Header Template (BHT)',\ + '80':'ICAO header version [01 00] (Optional) - Version of the CBEFF patron header format',\ + '81':'Biometric type (Optional)',\ + '82':'Biometric feature (Optional for DG2, mandatory for DG3, DG4.)',\ + '83':'Creation date and time (Optional)',\ + '84':'Validity period (from through) (Optional)',\ + '86':'Creator of the biometric reference data (PID) (Optional)',\ + '87':'Format owner (Mandatory)',\ + '88':'Format type (Mandatory)',\ + BDB:'Biometric data (encoded according to Format Owner) also called the biometric data block (BDB).',\ + BDB1:'Biometric data (encoded according to Format Owner) also called the biometric data block (BDB).',\ + '7f60':'2nd Biometric Information Template',\ + FAC:'Format Identifier ASCII FAC\0'} +# Data Group 2 field types +TEMPLATE= 0 +SUB= 1 +DG2_TYPE= {EF_DG2:TEMPLATE,\ + '7f61':TEMPLATE,\ + '02':SUB,\ + '7f60':TEMPLATE,\ + 'a1':TEMPLATE,\ + '80':SUB,\ + '81':SUB,\ + '82':SUB,\ + '83':SUB,\ + '84':SUB,\ + '86':SUB,\ + '87':SUB,\ + '88':SUB,\ + '5f2e':TEMPLATE,\ + '7f2e':TEMPLATE,\ + '7f60':TEMPLATE} + +# ISO 19794_5 (Biometric identifiers) +ISO19794_5_GENDER= { '00':'Unpecified',\ + '01':'Male',\ + '02':'Female',\ + '03':'Unknown',\ + 'ff':'Other'} + +ISO19794_5_EYECOLOUR= { '00':'Unspecified',\ + '01':'Black',\ + '02':'Blue',\ + '03':'Brown',\ + '04':'Grey',\ + '05':'Green',\ + '06':'Multi',\ + '07':'Pink',\ + '08':'Other'} + +ISO19794_5_HAIRCOLOUR= { '00':'Unspecified',\ + '01':'Bald',\ + '02':'Black',\ + '03':'Blonde',\ + '04':'Brown',\ + '05':'Grey',\ + '06':'White',\ + '07':'Red',\ + '08':'Green',\ + '09':'Blue',\ + 'ff':'Other'} + +ISO19794_5_FEATURE= {0x01:'Specified',\ + 0x02:'Glasses',\ + 0x04:'Moustache',\ + 0x08:'Beard',\ + 0x10:'Teeth Visible',\ + 0x20:'Blink',\ + 0x40:'Mouth Open',\ + 0x80:'Left Eyepatch',\ + 0x100:'Right Eyepatch',\ + 0x200:'Dark Glasses',\ + 0x400:'Distorted'} + +ISO19794_5_EXPRESSION= {'0000':'Unspecified',\ + '0001':'Neutral',\ + '0002':'Smile Closed',\ + '0003':'Smile Open',\ + '0004':'Raised Eyebrow',\ + '0005':'Looking Away',\ + '0006':'Squinting',\ + '0007':'Frowning'} + +ISO19794_5_IMG_TYPE= {'00':'Unspecified (Front)',\ + '01':'Basic',\ + '02':'Full Front',\ + '03':'Token Front',\ + '04':'Other'} + +ISO19794_5_IMG_DTYPE= {'00':'JPEG',\ + '01':'JPEG 2000'} + +ISO19794_5_IMG_FTYPE= {'00':'JPG',\ + '01':'JP2'} + +ISO19794_5_IMG_CSPACE= {'00':'Unspecified',\ + '01':'RGB24',\ + '02':'YUV422',\ + '03':'GREY8BIT',\ + '04':'Other'} + +ISO19794_5_IMG_SOURCE= {'00':'Unspecified',\ + '01':'Static Unspecified',\ + '02':'Static Digital',\ + '03':'Static Scan',\ + '04':'Video Unknown',\ + '05':'Video Analogue',\ + '06':'Video Digital',\ + '07':'Unknown'} + +ISO19794_5_IMG_QUALITY= {'00':'Unspecified'} + +DG7_ELEMENTS= {EF_DG7:'EF.DG7',\ + '5f43':'Displayed signature or mark',\ + '02':'Integer - Number of instances of this type of displayed image'} + +# display options +# standard document +MRZ_FIELD_NAMES= ('Document code','Issuing State or Organisation','Name','Passport Number','Check Digit','Nationality','Date of Birth','Check Digit','Sex','Date of Expiry','Check Digit','Personal Number or other optional elements','Check Digit','Composite Check Digit') +MRZ_FIELD_LENGTHS= (2,3,39,9,1,3,6,1,1,6,1,14,1,1) +MRZ_FIELD_DISPLAY= (0,3,1,2,5,6,8,9,11) +MRZ_FIELD_KEYS= (44,57,65) +# id card +MRZ_FIELD_NAMES_ID= ('Document code','Issuing State or Organisation','Document Number','Check Digit','Personal Number or other optional elements','Check Digit','Date of Birth','Check Digit','Sex','Date of Expiry','Check Digit','Nationality','Check Digit','Name') +MRZ_FIELD_LENGTHS_ID= (2,3,9,1,14,1,6,1,1,6,1,14,1,30) +MRZ_FIELD_DISPLAY_ID= (0,2,1,13,11,6,8,9,4) +MRZ_FIELD_KEYS_ID= (5,30,38) + +# Global Send Sequence Counter +SSC= '' + +# Global bruteforce vars +num= [] +map= [] +brnum= 0 + +def mrzspaces(data, fill): + out= '' + for x in range(len(data)): + if data[x] == '<': + out += fill + else: + out += data[x] + return out + +Displayed= False +Display_DG7= False +def drawfeatures(face,features): + global Displayed + global Style + + face.delete("feature") + if Displayed: + Displayed= False; + return + for item in features: + x= int(item[4:8],16) + y= int(item[8:12],16) + if Style == 'Arrow': + face.create_line(0,0,x,y,fill="Red",arrow="last",width=2,tags="feature") + if Style == 'Cross': + face.create_line(x-6,y-6,x+6,y+6,fill="Red",width=2, tags="feature") + face.create_line(x-6,y+6,x+6,y-6,fill="Red",width=2, tags= "feature") + if Style == 'Target': + face.create_line(x,y-15,x,y+15,fill="Red",width=3, tags="feature") + face.create_line(x-15,y,x+15,y,fill="Red",width=3, tags="feature") + face.create_oval(x-6,y-6,x+6,y+6,fill="Red",tags="feature") + if Style == 'Circle': + face.create_oval(x-6,y-6,x+6,y+6,fill="Red", tags="feature") + Displayed= True + +def changestyle(style,face,features): + global Style + global Displayed + + Style= style + if Displayed: + Displayed= False + drawfeatures(face,features) + +def secure_select_file(keyenc, keymac,file): + "secure select file" + global SSC + + cla= '0c' + ins= passport.ISOAPDU['SELECT_FILE'] + p1= '02' + p2= '0c' + command= passport.PADBlock(passport.ToBinary(cla + ins + p1 + p2)) + data= passport.PADBlock(passport.ToBinary(file)) + tdes= DES3.new(keyenc,DES.MODE_CBC,passport.DES_IV) + encdata= tdes.encrypt(data) + if DEBUG: + print 'Encrypted data: ', + passport.HexPrint(encdata) + do87= passport.ToBinary(passport.DO87) + encdata + m= command + do87 + if DEBUG: + print 'DO87: ', + passport.HexPrint(m) + SSC= passport.SSCIncrement(SSC) + n= SSC + m + cc= passport.DESMAC(n,keymac,'') + if DEBUG: + print 'CC: ', + passport.HexPrint(cc) + do8e= passport.ToBinary(passport.DO8E) + cc + if DEBUG: + print 'DO8E: ', + passport.HexPrint(do8e) + lc= "%02x" % (len(do87) + len(do8e)) + le= '00' + data= passport.ToHex(do87 + do8e) + if DEBUG: + print + print 'Protected APDU: ', + print cla+ins+p1+p2+lc+data+le + ins= 'SELECT_FILE' + if passport.send_apdu('','','','',cla,ins,p1,p2,lc,data,le): + out= passport.data + if DEBUG: + print 'Secure Select:', + if passport.errorcode == APDU_OK: + if DEBUG: + print 'OK' + check_cc(keymac,out) + return True, out + else: + return False, passport.errorcode + +def secure_read_binary(keymac,bytes,offset): + "secure read binary data" + global SSC + + cla= '0c' + ins= passport.ISOAPDU['READ_BINARY'] + hexoffset= '%04x' % offset + p1= hexoffset[0:2] + p2= hexoffset[2:4] + le= '%02x' % bytes + command= passport.PADBlock(passport.ToBinary(cla + ins + p1 + p2)) + do97= passport.ToBinary(passport.DO97 + le) + m= command + do97 + SSC= passport.SSCIncrement(SSC) + n= SSC + m + cc= passport.DESMAC(n,keymac,'') + do8e= passport.ToBinary(passport.DO8E) + cc + lc= "%02x" % (len(do97) + len(do8e)) + le= '00' + data= passport.ToHex(do97 + do8e) + if DEBUG: + print + print 'Protected APDU: ', + print cla+ins+p1+p2+lc+data+le + ins= 'READ_BINARY' + if passport.send_apdu('','','','',cla,ins,p1,p2,lc,data,le): + out= passport.data + if DEBUG: + print 'Secure Read Binary (%02d bytes): ' % bytes, + if passport.errorcode == APDU_OK: + if DEBUG: + print 'OK:', out + check_cc(keymac,out) + return True, out + else: + return False, passport.errorcode + +def calculate_check_digit(data): + "calculate ICAO 9303 check digit" + cd= n= 0 + for d in data: + if 'A' <= d <= 'Z': + value = ord(d)-55 + elif d == '<': + value = 0 + else: + value = int(d) + cd += value * MRZ_WEIGHT[n % 3] + n += 1 + return '%s' % (cd % 10) + +def check_cc(key,rapdu): + "Check Cryptographic Checksum" + global SSC + + SSC= passport.SSCIncrement(SSC) + k= SSC + length= 0 + # check if DO87 present + if rapdu[0:2] == "87": + length= 4 + int(rapdu[2:4],16) * 2 + k += passport.ToBinary(rapdu[:length]) + # check if DO99 present + if rapdu[length:length + 2] == "99": + length2= 4 + int(rapdu[length + 2:length + 4],16) * 2 + k += passport.ToBinary(rapdu[length:length + length2]) + + if DEBUG: + print 'K: ', + passport.HexPrint(k) + cc= passport.DESMAC(k,key,'') + if DEBUG: + print 'CC: ', + print passport.ToHex(cc), + if cc == passport.ToBinary(rapdu[len(rapdu) - len(cc) *2:]): + if DEBUG: + print '(verified)' + return True + else: + print 'Cryptographic Checksum failed!' + print 'Expected CC: ', + passport.HexPrint(cc) + print 'Received CC: ', + print rapdu[len(rapdu) - len(cc) * 2:] + os._exit(True) + +def decode_ef_com(data): + TAG_PAD= '80' + + # set up array for Data Groups to be read + ef_groups= [] + + "display contents of EF.COM" + hexdata= passport.ToHex(data) + # skip header + pos= 2 + # EF.COM length + print 'Length: ', asn1datalength(hexdata[pos:]) + pos += asn1fieldlength(hexdata[pos:]) + while pos < len(hexdata): + # end of data + if hexdata[pos:pos+2] == TAG_PAD: + return + # LDS & Unicode Versions + decoded= False + for length in 2,4: + if DG1_ELEMENTS.has_key(hexdata[pos:pos + length]): + decoded= True + print ' tag:',hexdata[pos:pos + length],'('+DG1_ELEMENTS[hexdata[pos:pos+length]]+')' + # decode tag list (stored objects) + if hexdata[pos:pos+length] == EF_TAGS: + pos += 2 + print ' length: ', + length= asn1datalength(hexdata[pos:]) + print length + pos += asn1fieldlength(hexdata[pos:]) + for n in range(length): + print ' Data Group: ', + print hexdata[pos:pos+2] + ' (' + TAG_NAME[hexdata[pos:pos+2]] + ')' + ef_groups.append(hexdata[pos:pos+2]) + pos += 2 + else: + pos += length + fieldlength= asn1datalength(hexdata[pos:]) + print ' length:',fieldlength + pos += asn1fieldlength(hexdata[pos:]) + print ' data:',hexdata[pos:pos+fieldlength*2] + pos += fieldlength*2 + if not decoded: + print 'Unrecognised element:', hexdata[pos:pos+4] + os._exit(True) + return ef_groups + +def read_file(file): + if not passport.iso_7816_select_file(file,passport.ISO_7816_SELECT_BY_EF,'0C'): + return False, '' + readlen= 4 + offset= 4 + if not passport.iso_7816_read_binary(readlen,0): + return False, '' + data= passport.data + # get file length + tag= data[:2] + datalen= asn1datalength(data[2:]) + print 'File Length:', datalen + # deduct length field and header from what we've already read + readlen= datalen - (3 - asn1fieldlength(data[2:]) / 2) + print 'Remaining data length:', readlen + # read remaining bytes + while readlen > 0: + if readlen > MAXCHUNK: + toread= MAXCHUNK + else: + toread= readlen + if not passport.iso_7816_read_binary(toread,offset): + return False, '' + data+=passport.data + offset += toread + readlen -= toread + print 'Reading: %05d\r' % readlen, + sys.stdout.flush() + print + return True, data.decode('hex') + +def asn1fieldlength(data): + #return length of number field according to asn.1 rules (doubled as we normally care about the hex version) + if int(data[:2],16) <= 0x7f: + return 2 + if int(data[:2],16) == 0x81: + return 4 + if int(data[:2],16) == 0x82: + return 6 + +def asn1datalength(data): + #return actual length represented by asn.1 field + if int(data[:2],16) <= 0x7f: + return int(data[:2],16) + if int(data[:2],16) == 0x81: + return int(data[2:4],16) + if int(data[:2],16) == 0x82: + return int(data[2:6],16) + +def secure_read_file(keyenc,keymac,file): +# MAXCHUNK= int(passport.ISO_FRAMESIZE[passport.framesize]) + + status, rapdu= secure_select_file(keyenc,keymac,file) + if not status: + return False, rapdu + # secure read file header (header byte plus up to 3 bytes of field length) + readlen= 4 + offset= 4 + status, rapdu= secure_read_binary(keymac,readlen,0) + if not status: + return False, rapdu + do87= rapdu[6:22] + if DEBUG: + print 'DO87: ' + do87 + print 'Decrypted DO87: ', + tdes= DES3.new(keyenc,DES.MODE_CBC,passport.DES_IV) + decdo87= tdes.decrypt(passport.ToBinary(do87))[:readlen] + if DEBUG: + passport.HexPrint(decdo87) + + # get file length + do87hex= passport.ToHex(decdo87) + tag= do87hex[:2] + datalen= asn1datalength(do87hex[2:]) + print 'File Length:', datalen + # deduct length field and header from what we've already read + readlen= datalen - (3 - asn1fieldlength(do87hex[2:]) / 2) + print 'Remaining data length:', readlen + # secure read remaining bytes + while readlen > 0: + if readlen > MAXCHUNK: + toread= MAXCHUNK + else: + toread= readlen + status, rapdu= secure_read_binary(keymac,toread,offset) + if not status: + return rapdu + do87= rapdu[6:(toread + (8 - toread % 8)) * 2 + 6] + tdes= DES3.new(keyenc,DES.MODE_CBC,passport.DES_IV) + decdo87 += tdes.decrypt(passport.ToBinary(do87))[:toread] + offset += toread + readlen -= toread + print 'Reading: %05d\r' % readlen, + sys.stdout.flush() + print + return True, decdo87 + +def decode_ef_dg1(data): + global DocumentType + global DOCUMENT_TYPE + global Fields + global FieldNames + global FieldLengths + global FieldKeys + + length= int(passport.ToHex(data[4]),16) + print 'Data Length: ', + print length + pointer= 5 + out= '' + while pointer < len(data): + if data[pointer] == chr(0x80): + break + out += '%s' % chr(int(passport.ToHex(data[pointer]),16)) + pointer += 1 + print ' Decoded Data: ' + out + DocumentType= out[0:2] + if DOC_ID.has_key(DocumentType): + print ' Document type: %s' % DOC_ID[DocumentType] + DOCUMENT_TYPE= DOC_ID + Fields= MRZ_FIELD_DISPLAY_ID + FieldNames= MRZ_FIELD_NAMES_ID + FieldLengths= MRZ_FIELD_LENGTHS_ID + FieldKeys= MRZ_FIELD_KEYS_ID + else: + print ' Document type: %s' % DOC_PASS[DocumentType] + DOCUMENT_TYPE= DOC_PASS + Fields= MRZ_FIELD_DISPLAY + FieldNames= MRZ_FIELD_NAMES + FieldLengths= MRZ_FIELD_LENGTHS + FieldKeys= MRZ_FIELD_KEYS + pointer= 0 + for n in range(len(FieldNames)): + print ' ' + FieldNames[n] + ': ', + print out[pointer:pointer + FieldLengths[n]] + pointer += FieldLengths[n] + return(out) + +def decode_ef_dg2(data): + global Filetype + img_features= [] + + datahex= passport.ToHex(data) + position= 0 + end= len(datahex) + while position < end: + decoded= False + # check for presence of tags + for length in 4,2: + if DG2_ELEMENTS.has_key(datahex[position:position + length]): + decoded= True + tag= datahex[position:position + length] + print ' Tag:', tag, '('+DG2_ELEMENTS[tag]+')' + # don't skip TEMPLATE fields as they contain sub-fields + # except BDB which is a special case (CBEFF formatted) so for now + # just try and extract the image which is 65 bytes in + if DG2_TYPE[tag] == TEMPLATE: + position += length + fieldlength= asn1datalength(datahex[position:]) + print ' length:', fieldlength + if tag == BDB or tag == BDB1: + # process CBEFF block + position += asn1fieldlength(datahex[position:]) + # FACE header + length= len(FAC) + tag= datahex[position:position + length] + if not tag == FAC: + print 'Missing FAC in CBEFF block: %s' % tag + os._exit(True) + position += length + # FACE version + print ' FACE version: %s' % passport.ToBinary(datahex[position:position + 6]) + position += 8 + # Image length + print ' Record Length: %d' % int(datahex[position:position + 8],16) + imagelength= int(datahex[position:position + 8],16) + position += 8 + # Number of Images + images= int(datahex[position:position + 4],16) + print ' Number of Images: %d' % images + position += 4 + # Facial Image block + print ' Block Length: %d' % int(datahex[position:position + 8],16) + position += 8 + features= int(datahex[position:position + 4],16) + print ' Number of Features: %d' % features + position += 4 + print ' Gender: %s' % ISO19794_5_GENDER[datahex[position:position + 2]] + position += 2 + print ' Eye Colour: %s' % ISO19794_5_EYECOLOUR[datahex[position:position + 2]] + position += 2 + print ' Hair Colour: %s' % ISO19794_5_HAIRCOLOUR[datahex[position:position + 2]] + position += 2 + mask= int(datahex[position:position + 6],16) + print ' Feature Mask: %s' % datahex[position:position + 6] + position += 6 + if features: + print ' Features:' + for m, d in ISO19794_5_FEATURE.items(): + if and_(mask,m): + print ' : %s' % d + print ' Expression: %s' % ISO19794_5_EXPRESSION[datahex[position:position + 4]] + position += 4 + print ' Pose Angle: %s' % datahex[position:position + 6] + position += 6 + print ' Pose Angle Uncertainty: %s' % datahex[position:position + 6] + position += 6 + while features > 0: + print ' Feature block: %s' % datahex[position:position + 16] + img_features.append(datahex[position:position + 16]) + features -= 1 + position += 16 + print ' Image Type: %s' % ISO19794_5_IMG_TYPE[datahex[position:position + 2]] + position += 2 + print ' Image Data Type: %s' % ISO19794_5_IMG_DTYPE[datahex[position:position + 2]] + Filetype= ISO19794_5_IMG_FTYPE[datahex[position:position + 2]] + position += 2 + print ' Image Width: %d' % int(datahex[position:position + 4],16) + position += 4 + print ' Image Height: %d' % int(datahex[position:position + 4],16) + position += 4 + print ' Image Colour Space: %s' % ISO19794_5_IMG_CSPACE[datahex[position:position + 2]] + position += 2 + print ' Image Source Type: %s' % ISO19794_5_IMG_SOURCE[datahex[position:position + 2]] + position += 2 + print ' Image Device Type: %s' % datahex[position:position + 6] + position += 6 + print ' Image Quality: %s' % ISO19794_5_IMG_QUALITY[datahex[position:position + 2]] + position += 2 + img= open(tempfiles+'EF_DG2.' + Filetype,'wb+') + img.write(data[position / 2:position + imagelength]) + img.flush() + img.close() + print ' JPEG image stored in %sEF_DG2.%s' % (tempfiles,Filetype) + position += imagelength * 2 + else: + position += asn1fieldlength(datahex[position:]) + else: + position += length + fieldlength= asn1datalength(datahex[position:]) + print ' length:', fieldlength + position += asn1fieldlength(datahex[position:]) + print ' data:', datahex[position:position + fieldlength * 2] + position += fieldlength * 2 + if not decoded: + print 'Unrecognised element:', datahex[position:position + 4] + os._exit(True) + return img_features + +def decode_ef_dg7(data): + global Filetype + global Display_DG7 + datahex= passport.ToHex(data) + position= 0 + end= len(datahex) + while position < end: + decoded= False + # check for presence of tags + for length in 4,2: + if DG7_ELEMENTS.has_key(datahex[position:position + length]): + decoded= True + tag= datahex[position:position + length] + print ' Tag:', tag, '('+DG7_ELEMENTS[tag]+')' + position += length + fieldlength= asn1datalength(datahex[position:]) + print ' length:', fieldlength + if tag == '67': + position += asn1fieldlength(datahex[position:]) + elif tag == '02': + position += asn1fieldlength(datahex[position:]) + print ' content: %i instance(s)' % int(datahex[position:position + fieldlength * 2], 16) + # note that for now we don't support decoding several instances... + position += fieldlength * 2 + elif tag == '5f43': + position += asn1fieldlength(datahex[position:]) + img= open(tempfiles+'EF_DG7.' + Filetype,'wb+') + img.write(data[position / 2:position + fieldlength]) + img.flush() + img.close() + print ' JPEG image stored in %sEF_DG7.%s' % (tempfiles,Filetype) + Display_DG7= True + position += fieldlength * 2 + if not decoded: + print 'Unrecognised element:', datahex[position:position + 4] + os._exit(True) + return + +def jmrtd_create_file(file,length): + "create JMRTD file" + ins= 'CREATE_FILE' + p1= '00' + p2= '00' + le= '06' # length is always 6 + data= "6304" + "%04x" % length + file + if passport.send_apdu('','','','','',ins,p1,p2,le,data,''): + return + if passport.errorcode == '6D00': + # could be a vonJeek card + print "create file failed - assuming vonJeek emulator" + return + passport.iso_7816_fail(passport.errorcode) + +def jmrtd_select_file(file): + "select JMRTD file" + ins= 'SELECT_FILE' + p1= '00' + p2= '00' + data= "02" + file + if passport.send_apdu('','','','','',ins,p1,p2,'',data,''): + return + if passport.errorcode == '6982': + # try vonJeek + print "selecting vonJeek file" + ins= 'VONJEEK_SELECT_FILE' + cla= '10' + if passport.send_apdu('','','','',cla,ins,p1,p2,'',data,''): + return + passport.iso_7816_fail(passport.errorcode) + +def jmrtd_write_file(file,data): + "write data to JMRTD file" + jmrtd_select_file(file) + offset= 0 + towrite= len(data) + while towrite: + if towrite > MAXCHUNK: + chunk= MAXCHUNK + else: + chunk= towrite + print "\rwriting %d bytes " % towrite, + sys.stdout.flush() + jmrtd_update_binary(offset,data[offset:offset + chunk]) + offset += chunk + towrite -= chunk + print + +def jmrtd_update_binary(offset,data): + "write a chunk of data to an offset within the currently selected JMRTD file" + hexoff= "%04x" % offset + ins= 'UPDATE_BINARY' + p1= hexoff[0:2] + p2= hexoff[2:4] + lc= "%02x" % len(data) + data= passport.ToHex(data) + if passport.send_apdu('','','','','',ins,p1,p2,lc,data,''): + return + if passport.errorcode == '6D00': + # vonJeek + print "(vonJeek)", + ins= 'VONJEEK_UPDATE_BINARY' + cla= '10' + if passport.send_apdu('','','','',cla,ins,p1,p2,lc,data,''): + return + passport.iso_7816_fail(passport.errorcode) + +def jmrtd_personalise(documentnumber,dob,expiry): + "set the secret key for JMRTD document" + ins= 'PUT_DATA' + p1= '00' + p2= '62' + data= '621B04' + "%02x" % len(documentnumber) + passport.ToHex(documentnumber) + '04' + "%02x" % len(dob) + passport.ToHex(dob) + '04' + "%02X" % len(expiry) + passport.ToHex(expiry) + lc= "%02X" % (len(data) / 2) + if passport.send_apdu('','','','','',ins,p1,p2,lc,data,''): + return + if passport.errorcode == '6D00': + # vonJeek + cla= '10' + ins= 'VONJEEK_SET_MRZ' + data= passport.ToHex(documentnumber) + passport.ToHex(calculate_check_digit(documentnumber)) + passport.ToHex(dob) + passport.ToHex(calculate_check_digit(dob)) + passport.ToHex(expiry) + passport.ToHex(calculate_check_digit(expiry)) + lc= "%02X" % (len(data) / 2) + if passport.send_apdu('','','','',cla,ins,p1,p2,lc,data,''): + # see if we need to set BAC or not, hacky way for now... + if os.access(filespath+NOBAC_FILE,os.F_OK): + BAC=False + else: + BAC=True + if BAC: + vonjeek_setBAC() + else: + vonjeek_unsetBAC() + return + passport.iso_7816_fail(passport.errorcode) + +def vonjeek_setBAC(): + "enable BAC on vonjeek emulator card" + # Setting BAC works only on recent vonJeek emulators, older have only BAC anyway + print "Forcing BAC mode to ENABLED" + if passport.send_apdu('','','','','10','VONJEEK_SET_BAC','00','01','00','',''): + return + else: + print "ERROR Could not enable BAC, make sure you are using a recent vonJeek emulator" + os._exit(True) + +def vonjeek_unsetBAC(): + "disable BAC on vonjeek emulator card" + print "Forcing BAC mode to DISABLED" + if passport.send_apdu('','','','','10','VONJEEK_SET_BAC','00','00','00','',''): + return + else: + print "ERROR Could not disable BAC, make sure you are using a recent vonJeek emulator" + os._exit(True) + +def jmrtd_lock(): + "set the JMRTD to Read Only" + ins= 'PUT_DATA' + p1= 'de' + p2= 'ad' + lc= '00' + if passport.send_apdu('','','','','',ins,p1,p2,lc,'',''): + return + passport.iso_7816_fail(passport.errorcode) + +def bruteno(init): + global num + global map + global brnum + global width + + if init: + # set up brute force and return number of iterations required + width= 0 + for x in range(len(init)): + if init[x] == '?': + width += 1 + num.append(0) + map.append(True) + else: + num.append(init[x]) + map.append(False) + return pow(10, width) + else: + out= '' + bruted= False + for x in range(len(num)): + if map[x]: + if bruted: + continue + else: + bruted= True + out += '%0*d' % (width, brnum) + brnum += 1 + else: + out += num[x] + return out + +try: + passport= RFIDIOtconfig.card +except: + os._exit(True) + +args= RFIDIOtconfig.args +Help= RFIDIOtconfig.help +Nogui= RFIDIOtconfig.nogui +DEBUG= RFIDIOtconfig.debug + +myver= 'mrpkey v0.1s' +passport.info(myver) + +TEST= False +FILES= False +bruteforce= False +bruteforceno= False +Jmrtd= False +JmrtdLock= False +MRZ=True +BAC=True +SETBAC=False +UNSETBAC=False + +def help(): + print + print 'Usage:' + print '\t' + sys.argv[0] + ' [OPTIONS] [WRITE|WRITELOCK]' + print + print '\tSpecify the Lower MRZ as a quoted string or the word TEST to use sample data.' + print '\tLower MRZ can be full line or shortened to the essentials: chars 1-9;14-19;22-27' + print '\tSpecify the word PLAIN if the passport doesn\'t have BAC (shorthand for dummy MRZ)' + print '\tSpecify the word CHECK to check if the device is a passport.' + print '\tSpecify a PATH to use files that were previously read from a passport.' + print '\tSpecify the option WRITE after a PATH to initialise a JMRTD or vonJeek emulator \'blank\'.' + print '\tSpecify the option WRITELOCK after a PATH to initialise a JMRTD emulator \'blank\' and set to Read Only.' + print '\tSpecify the option WRITE/WRITELOCK after a MRZ or PLAIN to clone a passport to a JMRTD or vonJeek emulator.' + print '\tSpecify the option SETBAC to enable BAC on a (already configured) vonJeek emulator card.' + print '\tSpecify the option UNSETBAC to disable BAC on a (already configured) vonJeek emulator card.' + print '\tSpecify \'?\' for check digits if not known and they will be calculated.' + print '\tSpecify \'?\' in the passport number field for bruteforce of that portion.' + print '\tNote: only one contiguous portion of the field may be bruteforced.' + print '\tPadding character \'<\' should be used for unknown fields.' + print + os._exit(True) + +if len(args) == 0 or Help: + help() + +if not(len(args[0]) == 44 or len(args[0]) == 21 or args[0] == 'TEST' or args[0] == 'CHECK' or args[0] == 'PLAIN' or args[0] == 'SETBAC' or args[0] == 'UNSETBAC' or os.access(args[0],os.F_OK)) or len(args) > 2: + help() + +if len(args) == 2: + if not (args[1] == 'WRITE' or args[1] == 'WRITELOCK'): + help() + +print + +# check if we are reading from files +if os.access(args[0],os.F_OK): + FILES= True + filespath= args[0] + if not filespath[len(filespath) - 1] == '/': + filespath += '/' + try: + passfile= open(filespath + 'EF_COM.BIN','rb') + except: + print "Can't open %s" % (filespath + 'EF_COM.BIN') + os._exit(True) + data= passfile.read() + eflist= decode_ef_com(data) + raw_efcom= data + passfile.close() + +if args[0] == 'PLAIN' or len(args[0]) == 44 or len(args[0]) == 21 or FILES: + if len(args) == 2: + if args[1] == "WRITE": + Jmrtd= True + if args[1] == "WRITELOCK": + Jmrtd= True + JmrtdLock= True + +if args[0] == 'TEST': + TEST= True + +if args[0] == 'SETBAC': + MRZ=False + SETBAC= True + +if args[0] == 'UNSETBAC': + MRZ=False + UNSETBAC= True + +if args[0] == 'CHECK': + while not passport.hsselect('08'): + print 'Waiting for passport... (%s)' % passport.errorcode + if passport.iso_7816_select_file(passport.AID_MRTD,passport.ISO_7816_SELECT_BY_NAME,'0C'): + print 'Device is a Machine Readable Document' + os._exit(False) + else: + print 'Device is NOT a Machine Readable Document' + os._exit(True) + +if args[0] == 'PLAIN': + MRZ=False + +if TEST: + passport.MRPmrzl(TEST_MRZ) + print 'Test MRZ: ' + TEST_MRZ +if not TEST and not FILES and MRZ: + key=args[0] + # expands short MRZ version if needed + if len(key) == 21: + key= key[0:9] + 'xxxx' + key[9:15] + 'xx' + key[15:21] + 'xxxxxxxxxxxxxxxxx' + passport.MRPmrzl(string.upper(key)) + +if not FILES and not TEST: + # set communication speed + # 01 = 106 kBaud + # 02 = 212 kBaud + # 04 = 414 kBaud + # 08 = 818 kBaud + while not passport.hsselect('08'): + print 'Waiting for passport... (%s)' % passport.errorcode + print 'Device set to %s transfers' % passport.ISO_SPEED[passport.speed] + print 'Device supports %s Byte transfers' % passport.ISO_FRAMESIZE[passport.framesize] + print + print 'Select Passport Application (AID): ', + if passport.iso_7816_select_file(passport.AID_MRTD,passport.ISO_7816_SELECT_BY_NAME,'0C'): + print 'OK' + else: + passport.iso_7816_fail(passport.errorcode) + print 'Select Master File: ', + if passport.iso_7816_select_file(TAG_FID[EF_COM],passport.ISO_7816_SELECT_BY_EF,'0C'): + # try forcing BAC by reading a file + status, data= read_file(TAG_FID[EF_DG1]) + if not status and passport.errorcode == APDU_BAC: + BAC=True + else: + print 'No Basic Access Control!' + print passport.errorcode + BAC=False +if BAC: + print 'Basic Acces Control Enforced!' + +if SETBAC: + vonjeek_setBAC() + os._exit(True) + +if UNSETBAC: + vonjeek_unsetBAC() + os._exit(True) + +if BAC and not MRZ: + print 'Please provide a MRZ!' + os._exit(True) + +if not FILES and BAC: + print 'Passport number: ' + passport.MRPnumber + if passport.MRPnumber.find('?') >= 0: + bruteforce= True + bruteforceno= True + # initialise bruteforce for number + iterations= bruteno(passport.MRPnumber) + print 'Bruteforcing Passport Number (%d iterations)' % iterations + else: + iterations= 1 + print 'Nationality: ' + passport.MRPnationality + print 'Date Of Birth: ' + passport.MRPdob + print 'Sex: ' + passport.MRPsex + print 'Expiry: ' + passport.MRPexpiry + print 'Optional: ' + passport.MRPoptional + + # loop until successful login breaks us out or we've tried all possibilities + while iterations: + iterations -= 1 + if bruteforceno: + passport.MRPnumber= bruteno('') + # always calculate check digits (makes bruteforcing easier) + passport.MRPnumbercd= calculate_check_digit(passport.MRPnumber) + passport.MRPdobcd= calculate_check_digit(passport.MRPdob) + passport.MRPexpirycd= calculate_check_digit(passport.MRPexpiry) + passport.MRPoptionalcd= calculate_check_digit(passport.MRPoptional) + passport.MRPcompsoitecd= calculate_check_digit(passport.MRPnumber + passport.MRPnumbercd + passport.MRPdob + passport.MRPdobcd + passport.MRPexpiry + passport.MRPexpirycd + passport.MRPoptional + passport.MRPoptionalcd) + + kmrz= passport.MRPnumber + passport.MRPnumbercd + passport.MRPdob + passport.MRPdobcd + passport.MRPexpiry + passport.MRPexpirycd + + print + print 'Generate local keys:' + print + if not TEST: + print 'Supplied MRZ: ' + string.upper(args[0]) + print 'Corrected MRZ: ' + passport.MRPnumber + passport.MRPnumbercd + passport.MRPnationality + passport.MRPdob + passport.MRPdobcd + passport.MRPsex + passport.MRPexpiry + passport.MRPexpirycd + passport.MRPoptional + passport.MRPoptionalcd+passport.MRPcompsoitecd + print 'Key MRZ Info (kmrz): ' + kmrz + print + kseedhash= SHA.new(kmrz) + kseed= kseedhash.digest()[:16] + if DEBUG: + print 'Kseed (SHA1 hash digest of kmrz): ' + kseedhash.hexdigest()[:32] + + # calculate Kenc & Kmac + Kenc= passport.DESKey(kseed,passport.KENC,16) + if DEBUG: + print 'Kenc: ', + passport.HexPrint(Kenc) + Kmac= passport.DESKey(kseed,passport.KMAC,16) + if DEBUG: + print 'Kmac: ', + passport.HexPrint(Kmac) + print + + if TEST: + rnd_ifd= TEST_rnd_ifd + rnd_icc= TEST_rnd_icc + Kifd= TEST_Kifd + else: + if DEBUG: + print 'Get Challenge from Passport (rnd_icc): ', + if passport.iso_7816_get_challenge(8): + rnd_icc= passport.data + else: + passport.iso_7816_fail(passport.errorcode) + if DEBUG: + passport.HexPrint(rnd_icc) + rnd_ifd= passport.GetRandom(8) + Kifd= passport.GetRandom(16) + + if DEBUG or TEST: + print 'Generate local random Challenge (rnd_ifd): ' + rnd_ifd + print 'Generate local random Challenge (Kifd): ' + Kifd + print + + S= passport.ToBinary(rnd_ifd + rnd_icc + Kifd) + + if DEBUG or TEST: + print 'S: ', + passport.HexPrint(S) + + if DEBUG or TEST: + print 'Kenc: ', + passport.HexPrint(Kenc) + + + tdes= DES3.new(Kenc,DES.MODE_CBC,passport.DES_IV) + Eifd= tdes.encrypt(S) + if DEBUG or TEST: + print 'Eifd: ', + passport.HexPrint(Eifd) + print 'Kmac: ', + passport.HexPrint(Kmac) + Mifd= passport.DESMAC(Eifd,Kmac,'') + if DEBUG or TEST: + print 'Mifd: ', + passport.HexPrint(Mifd) + + cmd_data= Eifd + Mifd + if DEBUG or TEST: + print 'cmd_data: ', + passport.HexPrint(cmd_data) + print + + if TEST: + respdata= TEST_respdata + else: + print 'Authenticating: ', + if passport.iso_7816_external_authenticate(passport.ToHex(cmd_data),Kmac): + respdata= passport.data + else: + # failures allowed if we're brute forcing + if brnum: + respdata= '' + else: + passport.iso_7816_fail(passport.errorcode) + if DEBUG or TEST: + print 'Auth Response: ' + respdata + resp= respdata[:64] + respmac= respdata[64:80] + if DEBUG or TEST: + print 'Auth message: ' + resp + print 'Auth MAC: ' + respmac + ' (verified)' + decresp= passport.ToHex(tdes.decrypt(passport.ToBinary(resp))) + if DEBUG or TEST: + print 'Decrypted Auth Response: ' + decresp + print 'Decrypted rnd_icc: ' + decresp[:16] + recifd= decresp[16:32] + if DEBUG or TEST: + print 'Decrypted rnd_ifd: ' + recifd, + # check returned rnd_ifd matches our challenge + if not passport.ToBinary(recifd) == passport.ToBinary(rnd_ifd): + print 'Challenge failed!' + print 'Expected rnd_ifd: ', rnd_ifd + print 'Received rnd_ifd: ', recifd + if not bruteforce or iterations == 0: + os._exit(True) + else: + if DEBUG or TEST: + print '(verified)' + # challenge succeeded, so break + break + + kicc= decresp[32:64] + if DEBUG or TEST: + print 'Decrypted Kicc: ' + kicc + + # generate session keys + print + print 'Generate session keys: ' + print + kseedhex= "%032x" % xor(int(Kifd,16),int(kicc,16)) + kseed= passport.ToBinary(kseedhex) + print 'Kifd XOR Kicc (kseed): ', + passport.HexPrint(kseed) + KSenc= passport.DESKey(kseed,passport.KENC,16) + print 'Session Key ENC: ', + passport.HexPrint(KSenc) + KSmac= passport.DESKey(kseed,passport.KMAC,16) + print 'Session Key MAC: ', + passport.HexPrint(KSmac) + + print + # calculate Send Sequence Counter + print 'Calculate Send Sequence Counter: ' + print + SSC= passport.ToBinary(rnd_icc[8:16] + rnd_ifd[8:16]) + print 'SSC: ', + passport.HexPrint(SSC) + + # secure select master file + if TEST: + KSmac= passport.ToBinary('F1CB1F1FB5ADF208806B89DC579DC1F8') + rapdu= '990290008E08FA855A5D4C50A8ED9000' + # ran out of steam on testing here! + os._exit(False) + else: + status, data= secure_read_file(KSenc,KSmac,TAG_FID[EF_COM]) + if not status: + passport.iso_7816_fail(data) + + # secure read file header + #if TEST: + # KSmac= passport.ToBinary('F1CB1F1FB5ADF208806B89DC579DC1F8') + # rapdu= '8709019FF0EC34F9922651990290008E08AD55CC17140B2DED9000' + #else: + # readlen= 4 + # rapdu= secure_read_binary(KSmac,readlen,0) + + print 'EF.COM: ', + if DEBUG: + passport.HexPrint(data) + eflist= decode_ef_com(data) + raw_efcom= data + efcom= open(tempfiles+TAG_FILE[EF_COM],'wb+') + efcom.write(data) + efcom.flush() + efcom.close() + print 'EF.COM stored in', tempfiles+TAG_FILE[EF_COM] + +if not FILES and not BAC: + status, data= read_file(TAG_FID[EF_COM]) + if not status: + passport.iso_7816_fail(passport.errorcode) + + print 'EF.COM: ', + if DEBUG: + passport.HexPrint(data) + eflist= decode_ef_com(data) + raw_efcom= data + bacfile= open(tempfiles+NOBAC_FILE,'wb+') + bacfile.close() + efcom= open(tempfiles+TAG_FILE[EF_COM],'wb+') + efcom.write(data) + efcom.flush() + efcom.close() + print 'EF.COM stored in', tempfiles+TAG_FILE[EF_COM] + +# get SOD +#print +#print 'Select EF.SOD: ', +#data= secure_read_file(KSenc,KSmac,TAG_FID[EF_SOD]) +#if DEBUG: +# passport.HexPrint(data) +#sod= open(tempfiles+TAG_FILE[EF_SOD],'w+') +#sod.write(data) +#sod.flush() +#sod.close() +#print 'EF.SOD stored in', tempfiles+TAG_FILE[EF_SOD] + +# Add Security Object and Main Directory to list for reading +eflist.insert(0,EF_SOD) +eflist.insert(0,EF_COM) +# now get everything else +for tag in eflist: + print 'Reading:', TAG_NAME[tag] + if not FILES: + if BAC: + status, data= secure_read_file(KSenc,KSmac,TAG_FID[tag]) + else: + status, data= read_file(TAG_FID[tag]) + if not status: + print "skipping (%s)" % passport.ISO7816ErrorCodes[data] + continue + else: + try: + passfile= open(filespath+TAG_FILE[tag],'rb') + except: + print "*** Warning! Can't open %s" % filespath+TAG_FILE[tag] + continue + data= passfile.read() + + if DEBUG: + passport.HexPrint(data) + outfile= open(tempfiles+TAG_FILE[tag],'wb+') + outfile.write(data) + outfile.flush() + outfile.close() + print ' Stored in', tempfiles+TAG_FILE[tag] + # special cases + if tag == EF_SOD: + # extract DER file (should be at offset 4 - if not, use sod.py to find it in EF_SOD.BIN + # temporary evil hack until I have time to decode EF.SOD properly + outfile= open(tempfiles+"EF_SOD.TMP",'wb+') + outfile.write(data[4:]) + outfile.flush() + outfile.close() + exitstatus= os.system("openssl pkcs7 -text -print_certs -in %sEF_SOD.TMP -inform DER" % tempfiles) + if not exitstatus: + exitstatus= os.system("openssl pkcs7 -in %sEF_SOD.TMP -out %sEF_SOD.PEM -inform DER" % (tempfiles,tempfiles)) + exitstatus= os.system("openssl pkcs7 -text -print_certs -in %sEF_SOD.PEM" % tempfiles) + print + print 'Certificate stored in %sEF_SOD.PEM' % tempfiles + if tag == EF_DG1: + mrz= decode_ef_dg1(data) + if tag == EF_DG2: + dg2_features= decode_ef_dg2(data) + if tag == EF_DG7: + decode_ef_dg7(data) + +#initialise app if we are going to WRITE JMRTD +if Jmrtd: + if not FILES: + filespath=tempfiles + print + raw_input('Please replace passport with a JMRTD or vonJeek emulator card and press ENTER when ready...') + if not passport.hsselect('08') or not passport.iso_7816_select_file(passport.AID_MRTD,passport.ISO_7816_SELECT_BY_NAME,'0C'): + print "Couldn't select JMRTD!" + os._exit(True) + print "Initialising JMRTD or vonJeek..." + if STRIP_INDEX: + print 'Stripping AA & EAC files' + print 'old EF.COM: '+raw_efcom.encode('hex') + # DG.COM tag & length + total_length= ord(raw_efcom[1]) + new_total_length= ord(raw_efcom[1]) + i= 2 + tmp= '' + while i-2 < total_length-1: + # next tag + tag= raw_efcom[i] + tmp+= raw_efcom[i] + # not sure how to distinguish 2-byte tags... + if raw_efcom[i]==chr(0x5F) or raw_efcom[i]==chr(0x7F): + i+= 1 + tag+= raw_efcom[i] + tmp+= raw_efcom[i] + i+= 1 + length= ord(raw_efcom[i]) + i+= 1 + if tag=='5C'.decode('hex'): + # Keeping only known files in the tag index + oldindex=raw_efcom[i:i+length] + clearDGs=[chr(0x61), chr(0x75), chr(0x67), chr(0x6b), chr(0x6c), chr(0x6d), chr(0x63)] + newindex=''.join(filter(lambda x: x in clearDGs, list(oldindex))) + newlength=len(newindex) + tmp+= chr(newlength)+newindex + i+= newlength + # Fixing total length: + new_total_length= total_length-(length-newlength) + else: + tmp+= chr(length)+raw_efcom[i:i+length] + i+= length + raw_efcom= raw_efcom[0]+chr(new_total_length)+tmp + print 'new EF.COM: '+raw_efcom.encode('hex') + eflist= decode_ef_com(raw_efcom) + eflist.insert(0,EF_SOD) + eflist.insert(0,EF_COM) + for tag in eflist: + print 'Reading:', TAG_NAME[tag] + if tag == EF_COM and STRIP_INDEX: + data= raw_efcom + else: + try: + passfile= open(filespath+TAG_FILE[tag],'rb') + except: + print "*** Warning! Can't open %s" % filespath+TAG_FILE[tag] + continue + data= passfile.read() + print "Creating JMRTD", TAG_NAME[tag], "Length", len(data) + jmrtd_create_file(TAG_FID[tag],len(data)) + print "Writing JMRTD", TAG_NAME[tag] + jmrtd_write_file(TAG_FID[tag],data) + # set private key + # second line of MRZ is second half of decoded mrz from DG1 + passport.MRPmrzl(mrz[len(mrz) / 2:]) + print "Setting 3DES key" + jmrtd_personalise(mrz[FieldKeys[0]:FieldKeys[0]+9],mrz[FieldKeys[1]:FieldKeys[1]+6],mrz[FieldKeys[2]:FieldKeys[2]+6]) + print "JMRTD/vonJeek 3DES key set to: " + mrz[FieldKeys[0]:FieldKeys[0]+9] + mrz[FieldKeys[1]:FieldKeys[1]+6] + mrz[FieldKeys[2]:FieldKeys[2]+6] +if JmrtdLock: + jmrtd_lock() + +# image read is nasty hacky bodge to see if image display without interpreting the headers +# start of image location may change - look for JPEG header bytes 'FF D8 FF E0' +# german is JP2 format, not JPG - look for '00 00 00 0C 6A 50' +# in /tmp/EF_DG2.BIN + +def do_command(func, *args, **kw): + def _wrapper(*wargs): + return func(*(wargs + args), **kw) + return _wrapper + +# display data and image in gui window +Style= 'Arrow' +if not Nogui: + root = Tk() + + font= 'fixed 22' + fonta= 'fixed 22' + + frame = Frame(root, colormap="new", visual='truecolor').grid() + root.title('%s (RFIDIOt v%s)' % (myver,passport.VERSION)) + if Filetype == "JP2": + # nasty hack to deal with JPEG 2000 until PIL support comes along + exitstatus= os.system("convert %sJP2 %sJPG" % (tempfiles+'EF_DG2.',tempfiles+'EF_DG2.')) + print " (converted %sJP2 to %sJPG for display)" % (tempfiles+'EF_DG2.',tempfiles+'EF_DG2.') + if exitstatus: + print 'Could not convert JPEG 2000 image (%d) - please install ImageMagick' % exitstatus + os._exit(True) + elif Display_DG7: + os.system("convert %sJP2 %sJPG" % (tempfiles+'EF_DG7.',tempfiles+'EF_DG7.')) + print " (converted %sJP2 to %sJPG for display)" % (tempfiles+'EF_DG7.',tempfiles+'EF_DG7.') + Filetype= 'JPG' + imagedata = ImageTk.PhotoImage(file=tempfiles + 'EF_DG2.' + Filetype) + canvas= Canvas(frame, height= imagedata.height(), width= imagedata.width()) + canvas.grid(row= 3, sticky= NW, rowspan= 20) + canvasimage= canvas.create_image(0,0,image=imagedata, anchor=NW) + featurebutton= Checkbutton(frame, text="Show Features", command=do_command(drawfeatures,canvas,dg2_features)) + featurebutton.grid(row= 1, column=0, sticky= W, rowspan= 1) + featurestyle= Radiobutton(frame, text="Arrow",command=do_command(changestyle,"Arrow",canvas,dg2_features)) + featurestyle.grid(row= 1, column=0, rowspan= 1) + featurestyle.select() + featurestyle2= Radiobutton(frame, text="Cross",command=do_command(changestyle,"Cross",canvas,dg2_features)) + featurestyle2.grid(row= 2, column=0, rowspan= 1) + featurestyle2.deselect() + featurestyle3= Radiobutton(frame, text="Circle ",command=do_command(changestyle,"Circle",canvas,dg2_features)) + featurestyle3.grid(row= 1, column=0, rowspan= 1, sticky= E) + featurestyle3.deselect() + featurestyle4= Radiobutton(frame, text="Target",command=do_command(changestyle,"Target",canvas,dg2_features)) + featurestyle4.grid(row= 2, column=0, rowspan= 1, sticky= E) + featurestyle4.deselect() + quitbutton= Button(frame, text="Quit", command=root.quit) + quitbutton.grid(row= 1, column=3, sticky= NE, rowspan= 2) + Label(frame, text='Type').grid(row= 1, sticky= W, column= 1) + Label(frame, text=DOCUMENT_TYPE[DocumentType], font= font).grid(row= 2, sticky= W, column= 1) + row= 3 + for item in Fields: + Label(frame, text=FieldNames[item]).grid(row= row, sticky= W, column= 1) + row += 1 + mrzoffset= 0 + for x in range(item): + mrzoffset += FieldLengths[x] + if FieldNames[item] == "Issuing State or Organisation" or FieldNames[item] == "Nationality": + Label(frame, text= mrzspaces(mrz[mrzoffset:mrzoffset + FieldLengths[item]],' ') + ' ' + passport.ISO3166CountryCodesAlpha[mrzspaces(mrz[mrzoffset:mrzoffset + FieldLengths[item]],'')], font= font).grid(row= row, sticky= W, column= 1) + else: + Label(frame, text=mrzspaces(mrz[mrzoffset:mrzoffset + FieldLengths[item]],' '), font= font).grid(row= row, sticky= W, column= 1) + row += 1 + Label(frame, text=' ' + mrz[:len(mrz) / 2], font= fonta, justify= 'left').grid(row= row, sticky= W, columnspan= 4) + row += 1 + Label(frame, text=' ' + mrz[len(mrz) / 2:], font= fonta, justify= 'left').grid(row= row, sticky= W, columnspan= 4) + row += 1 + if Display_DG7: + im = Image.open(tempfiles + 'EF_DG7.' + Filetype) + width, height = im.size + im=im.resize((300, 300 * height / width)) + pic=ImageTk.PhotoImage(im) + width, height = im.size + signcanvas= Canvas(frame, height= height, width= width) + signcanvas.create_image(0,0,image=pic, anchor=NW) + signcanvas.grid(row= row, sticky= NW, columnspan=4) + row += 1 + Label(frame, text='http://rfidiot.org').grid(row= row, sticky= W, column= 1) + root.mainloop() +passport.shutdown() +os._exit(False) diff --git a/multiselect.py b/multiselect.py new file mode 100755 index 0000000..ca9c446 --- /dev/null +++ b/multiselect.py @@ -0,0 +1,57 @@ +#!/usr/bin/python + + +# multiselect.py - continuously select card and display ID +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2006, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + + +import RFIDIOtconfig +import sys +import os +import time +import string + +try: + card= RFIDIOtconfig.card +except: + os._exit(True) + +args= RFIDIOtconfig.args + +card.info('multiselect v0.1m') + +# force card type if specified +if len(args) == 1: + if not card.settagtype(args[0]): + print 'Could not set tag type' + os._exit(True) +else: + card.settagtype(card.ALL) + +while 42: + if card.select(): + print ' Tag ID: ' + card.uid, + if (card.readertype == card.READER_ACG and string.find(card.readername,"LFX") == 0): + print " Tag Type:" + card.LFXTags[card.tagtype] + else: + print + else: + print ' No card present\r', + sys.stdout.flush() diff --git a/pn532.py b/pn532.py new file mode 100755 index 0000000..f494911 --- /dev/null +++ b/pn532.py @@ -0,0 +1,140 @@ +#!/usr/bin/python + +# pn532.py - NXP PN532 definitions for restricted functions +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2009, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + + +PN532_APDU= { + 'GET_GENERAL_STATUS' : ['d4','04'], + 'GET_PN532_FIRMWARE' : ['d4','02'], + 'IN_ATR' : ['d4','50'], + 'IN_AUTO_POLL' : ['d4','60'], + 'IN_COMMUNICATE_THRU' : ['d4','42'], + 'IN_DATA_EXCHANGE' : ['d4','40'], + 'IN_LIST_PASSIVE_TARGET' : ['d4','4a'], + 'IN_SELECT' : ['d4','54'], + 'TG_GET_DATA' : ['d4','86'], + 'TG_INIT_AS_TARGET' : ['d4','8c'], + 'TG_SET_DATA' : ['d4','8e'], + } + +PN532_FUNCTIONS= { + 0x01 : 'ISO/IEC 14443 Type A', + 0x02 : 'ISO/IEC 14443 Type B', + 0x04 : 'ISO/IEC 18092', + } + +PN532_OK= 'D503' + +PN532_BAUDRATES= { + 0x00 : '106 kbps', + 0x01 : '212 kbps', + 0x02 : '424 kbps', + 0x10 : '212 kbps', + 0x20 : '424 kbps', + } + +PN532_FRAMING= { + 0x00 : 'Mifare', + 0x01 : 'Active mode', + 0x02 : 'FeliCa', + } + +PN532_TARGETS= { + '00' : 'Generic passive 106kbps (ISO/IEC1443-4A,mifare,DEP)', + '10' : 'mifare card', + } + +PN532_MODULATION= { + 0x00 : 'Mifare, ISO/IEC 14443-3 Type A/B, ISO/IEC 18092 passive 106 kbps', + 0x01 : 'ISO/IEC 18092 active', + 0x02 : 'Innovision Jewel', + 0x10 : 'FeliCa, ISO/IEC 18092 passive 212/424 kbps', + } + +PN532_ERRORS= { + 0x00 : 'No Error', + 0x01 : 'Time Out', + 0x02 : 'CRC Error', + 0x03 : 'Parity Error', + 0x04 : 'Erroneous Bit Count during Aticollision/Select (ISO 14443-3/ISO 18092 106kbps)', + 0x05 : 'Mifare Framing Error', + 0x06 : 'Abnormal Bit Collision during Bitwise Anticollision (106 kbps)', + 0x07 : 'Communication Buffer Size Insufficient', + 0x09 : 'RF Buffer Overflow (Register CIU_ERROR BufferOvfl)', + 0x0a : 'Active Communication RF Timeout', + 0x0b : 'RF Protocol Error', + 0x0d : 'Antenna Overheat', + 0x0e : 'Internal Buffer Overflow', + 0x10 : 'Invalid Parameter', + 0x12 : 'DEP protocol - initiator command not supported', + 0x13 : 'DEP protocol - data format out of spec', + 0x14 : 'Mifare authentication error', + 0x23 : 'ISO/IEC 14443-3 UID check byte wrong', + 0x25 : 'DEP protocol - invalid device state', + 0x26 : 'Operation not allowed in this configuration', + 0x27 : 'Command out of context', + 0x29 : 'Target released by Initiator', + 0x2a : 'ID mismatch - card has been exchanged', + 0x2b : 'Activated card missing', + 0x2c : 'NFCID3 mismatch', + 0x2d : 'Over-current event detected', + 0x2e : 'NAD missing in DEP frame', + } + +PN532_RF= { + 0x00 : 'Not present', + 0x01 : 'Present', + } + +# pn532 functions + +# print pn532 firmware details +def pn532_print_firmware(data): + if not data[:4] == PN532_OK: + print ' Bad data from PN532:', data + else: + print ' IC:', data[4:6] + print ' Rev: %d.%d' % (int(data[6:8],16),int(data[8:10])) + print ' Support:', + support= int(data[10:12],16) + spacing= '' + for n in PN532_FUNCTIONS.keys(): + if support & n: + print spacing + PN532_FUNCTIONS[n] + spacing= ' ' + print + +# print pn532 antenna status and return number of tags in field +def pn532_print_status(data): + print ' Reader PN532 Status:' + print ' Last error:', PN532_ERRORS[int(data[4:6])] + print ' External RF:', PN532_RF[int(data[6:8],16)] + tags= int(data[8:10],16) + print ' TAGS present:', tags + for n in range(tags): + print ' Tag number %d:' % (n + 1) + print ' Logical number:', data[10 + n * 2:12 + n * 2] + print ' RX Baudrate:', PN532_BAUDRATES[int(data[12 + n * 2:14 + n * 2],16)] + print ' TX Baudrate:', PN532_BAUDRATES[int(data[14 + n * 2:16 + n * 2],16)] + print ' Modulation:', PN532_MODULATION[int(data[16 + n * 2:18 + n * 2],16)] + print ' SAM Status:', data[18 + n * 2:20 + n * 2] + print + return tags diff --git a/pn532emulate.py b/pn532emulate.py new file mode 100755 index 0000000..e5c7162 --- /dev/null +++ b/pn532emulate.py @@ -0,0 +1,171 @@ +#!/usr/bin/python + +# pn532emulate.py - switch NXP PN532 reader chip into TAG emulation mode +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2009, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + + +from pn532 import * + +import RFIDIOtconfig +import sys +import os + +try: + card= RFIDIOtconfig.card +except: + os._exit(True) + +args= RFIDIOtconfig.args +help= RFIDIOtconfig.help + +card.info('pn532emulate v0.1c') + +if help or len(args) < 6: + print sys.argv[0] + ' - Switch NXP PN532 chip into emulation mode' + print + print 'Usage: ' + sys.argv[0] + ' [General Bytes] [Historical Bytes]' + print + print ' The NXP PN532 chip inside some readers (such as ACS/Tikitag) are capable of emulating the following tags:' + print + print ' ISO-14443-3' + print ' ISO-14443-4' + print ' Mifare' + print ' FeliCa' + print + print ' Arguments should be specified as follows:' + print + print ' MODE (BitField, last 3 bits only):' + print ' -----000 - Any' + print ' -----001 - Passive only' + print ' -----010 - DEP only' + print ' -----100 - PICC only' + print + print ' SENS_RES:' + print ' 2 Bytes, LSB first, as defined in ISO 14443-3.' + print + print ' NFCID1t:' + print " UID Last 6 HEX digits ('08' will be prepended)." + print + print ' SEL_RES:' + print ' 1 Byte, as defined in ISO14443-4.' + print + print ' NFCID2t:' + print " 8 Bytes target NFC ID2. Must start with '01fe'." + print + print ' PAD:' + print ' 8 Bytes.' + print + print ' SYSTEM_CODE:' + print ' 2 Bytes, returned in the POL_RES frame if the 4th byte of the incoming POL_REQ' + print ' command frame is 0x01.' + print + print ' NFCID3t:' + print ' 10 Bytes, used in the ATR_RES in case of ATR_REQ received from the initiator.' + print + print ' General Bytes:' + print ' Optional, Max 47 Bytes, to be used in the ATR_RES.' + print + print ' Historical Bytes:' + print ' Optional, Max 48 Bytes, to be used in the ATS when in ISO/IEC 14443-4 PICC' + print ' emulation mode.' + print + print ' Example:' + print + print ' ' + sys.argv[0] + ' 00 0800 dc4420 60 01fea2a3a4a5a6a7c0c1c2c3c4c5c6c7ffff aa998877665544332211 00 52464944494f7420504e353332' + print + print ' In ISO/IEC 14443-4 PICC emulation mode, the emulator will wait for initiator, then wait for an APDU,' + print " to which it will reply '9000' and exit." + print + os._exit(True) + +if not card.readersubtype == card.READER_ACS: + print ' Reader type not supported!' + os._exit(True) + +# switch off auto-polling (for ACS v1 firmware only) (doesn't seem to help anyway!) +#if not card.acs_send_apdu(card.PCSC_APDU['ACS_DISABLE_AUTO_POLL']): +# print ' Could not disable auto-polling' +# os._exit(True) + +if card.acs_send_apdu(PN532_APDU['GET_PN532_FIRMWARE']): + print ' NXP PN532 Firmware:' + pn532_print_firmware(card.data) + +if card.acs_send_apdu(PN532_APDU['GET_GENERAL_STATUS']): + pn532_print_status(card.data) + +mode= [args[0]] +sens_res= [args[1]] +uid= [args[2]] +sel_res= [args[3]] +felica= [args[4]] +nfcid= [args[5]] +try: + lengt= ['%02x' % (len(args[6]) / 2)] + gt= [args[6]] +except: + lengt= ['00'] + gt= [''] +try: + lentk= ['%02x' % (len(args[7]) / 2)] + tk= [args[7]] +except: + lentk= ['00'] + tk= [''] + +print ' Waiting for activation...' +card.acs_send_apdu(card.PCSC_APDU['ACS_LED_RED']) +status= card.acs_send_apdu(PN532_APDU['TG_INIT_AS_TARGET']+mode+sens_res+uid+sel_res+felica+nfcid+lengt+gt+lentk+tk) +if not status or not card.data[:4] == 'D58D': + print 'Target Init failed:', card.errorcode + os._exit(True) +mode= int(card.data[4:6],16) +baudrate= mode & 0x70 +print ' Emulator activated:' +print ' UID: 08%s' % uid[0] +print ' Baudrate:', PN532_BAUDRATES[baudrate] +print ' Mode:', +if mode & 0x08: + print 'ISO/IEC 14443-4 PICC' +if mode & 0x04: + print 'DEP' +framing= mode & 0x03 +print ' Framing:', PN532_FRAMING[framing] +print ' Initiator:', card.data[6:] +print + +print ' Waiting for APDU...' +status= card.acs_send_apdu(PN532_APDU['TG_GET_DATA']) +if not status or not card.data[:4] == 'D587': + print 'Target Get Data failed:', card.errorcode + print 'Data:',card.data + os._exit(True) +errorcode= int(card.data[4:6],16) +if not errorcode == 0x00: + print 'Error:',PN532_ERRORS[errorcode] + os._exit(True) +print '<<', card.data[6:] + +print '>>', card.ISO_OK +status= card.acs_send_apdu(PN532_APDU['TG_SET_DATA']+[card.ISO_OK]) +if not status: + os._exit(True) +else: + os._exit(False) diff --git a/pn532mitm.py b/pn532mitm.py new file mode 100755 index 0000000..1332d3a --- /dev/null +++ b/pn532mitm.py @@ -0,0 +1,412 @@ +#!/usr/bin/python + +# pn532mitm.py - NXP PN532 Man-In-The_Middle - log conversations between TAG and external reader +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2009, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + +from pn532 import * + +import RFIDIOtconfig +import RFIDIOt +import sys +import os +import string +import socket +import time +import random +import operator + +# try to connect to remote host. if that fails, alternately listen and connect. +def connect_to(host,port,type): + peer= socket.socket() + random.seed() + first= True + while 42: + peer.settimeout(random.randint(1,10)) + print ' Paging %s %s \r' % (host, port), + sys.stdout.flush() + time.sleep(1) + if peer.connect_ex((host,port)) == 0: + print ' Connected to %s port %d ' % (host,port) + send_data(peer,type) + data= recv_data(peer) + connection= peer + break + try: + print ' Listening for REMOTE on port %s \r' % port, + sys.stdout.flush() + if first: + peer.bind(('0.0.0.0',port)) + peer.listen(1) + first= False + conn, addr= peer.accept() + if conn: + print ' Connected to %s port %d ' % (addr[0],addr[1]) + data= recv_data(conn) + send_data(conn,type) + connection= conn + break + except socket.timeout: + pass + except Exception, exc: + print 'Could not open local socket: ' + print exc + os._exit(True) + if data == type: + print ' Handshake failed - both ends are set to', type + time.sleep(1) + connection.close() + os._exit(True) + print ' Remote is', data + print + return connection + +# send data with 3 digit length and 2 digit CRC +def send_data(host, data): + lrc= 0 + length= '%03x' % (len(data) + 2) + for x in length + data: + lrc= operator.xor(lrc,ord(x)) + host.send(length) + host.send(data) + host.send('%02x' % lrc) + +# receive data of specified length and check CRC +def recv_data(host): + out= '' + while len(out) < 3: + out += host.recv(3 - len(out)) + length= int(out,16) + lrc= 0 + for x in out: + lrc= operator.xor(lrc,ord(x)) + out= '' + while len(out) < length: + out += host.recv(length - len(out)) + for x in out[:-2]: + lrc= operator.xor(lrc,ord(x)) + if not lrc == int(out[-2:],16): + print ' Remote socket CRC failed!' + host.close() + os._exit(True) + return out[:-2] + +try: + card= RFIDIOtconfig.card +except: + os._exit(True) + +args= RFIDIOtconfig.args +help= RFIDIOtconfig.help + +card.info('pn532mitm v0.1d') + +if help or len(args) < 1: + print sys.argv[0] + ' - NXP PN532 Man-In-The-Middle' + print + print '\tUsage: ' + sys.argv[0] + " [LOG FILE] ['QUIET']" + print + print '\t Default PCSC reader will be the READER. Specify reader number to use as an EMULATOR as' + print '\t the argument.' + print + print "\t To utilise a REMOTE device, use a string in the form 'emulator:HOST:PORT' or 'reader:HOST:PORT'." + print + print '\t COMMANDS and RESPONSES will be relayed between the READER and the EMULATOR, and relayed' + print '\t traffic will be displayed (and logged if [LOG FILE] is specified).' + print + print "\t If the 'QUIET' option is specified, traffic log will not be displayed on screen." + print + print '\t Logging is in the format:' + print + print '\t << DATA... - HEX APDU received by EMULATOR and relayed to READER' + print '\t >> DATA... SW1SW2 - HEX response and STATUS received by READER and relayed to EMULATOR' + print + print '\t Examples:' + print + print '\t Use device no. 2 as the READER and device no. 3 as the EMULATOR:' + print + print '\t ' + sys.argv[0] + ' -r 2 3' + print + print '\t Use device no. 2 as the EMULATOR and remote system on 192.168.1.3 port 5000 as the READER:' + print + print '\t ' + sys.argv[0] + ' -r 2 reader:192.168.1.3:5000' + print + os._exit(True) + +logging= False +if len(args) > 1: + try: + logfile= open(args[1],'r') + x= string.upper(raw_input(' *** Warning! File already exists! Overwrite (y/n)? ')) + if not x == 'Y': + os._exit(True) + logfile.close() + except: + pass + try: + logfile= open(args[1],'w') + logging= True + except: + print " Couldn't create logfile:", args[1] + os._exit(True) + +try: + if args[2] == 'QUIET': + quiet= True +except: + quiet= False + +if len(args) < 1: + print 'No EMULATOR or REMOTE specified' + os._exit(True) + +# check if we are using a REMOTE system +remote= '' +remote_type= '' +if string.find(args[0],'emulator:') == 0: + remote= args[0][9:] + em_remote= True + remote_type= 'EMULATOR' +else: + em_remote= False +if string.find(args[0],'reader:') == 0: + remote= args[0][7:] + rd_remote= True + remote_type= 'READER' + emulator= card +else: + rd_remote= False + + +if remote: + host= remote[:string.find(remote,':')] + port= int(remote[string.find(remote,':') + 1:]) + connection= connect_to(host, port, remote_type) +else: + try: + readernum= int(args[0]) + emulator= RFIDIOt.rfidiot(readernum,card.readertype,'','','','','','') + print ' Emulator:', + emulator.info('') + if not emulator.readersubtype == card.READER_ACS: + print "EMULATOR is not an ACS" + os._exit(True) + except: + print "Couldn't initialise EMULATOR on reader", args[0] + os._exit(True) + +# always check at least one device locally +if not card.readersubtype == card.READER_ACS: + print "READER is not an ACS" + if remote: + connection.close() + os._exit(True) + +if card.acs_send_apdu(PN532_APDU['GET_PN532_FIRMWARE']): + if remote: + send_data(connection,card.data) + print ' Local NXP PN532 Firmware:' + else: + print ' Reader NXP PN532 Firmware:' + if not card.data[:4] == PN532_OK: + print ' Bad data from PN532:', card.data + if remote: + connection.close() + os._exit(True) + else: + pn532_print_firmware(card.data) + +if remote: + data= recv_data(connection) + print ' Remote NXP PN532 Firmware:' +else: + if emulator.acs_send_apdu(PN532_APDU['GET_PN532_FIRMWARE']): + data= card.data + emulator.acs_send_apdu(card.PCSC_APDU['ACS_LED_ORANGE']) + print ' Emulator NXP PN532 Firmware:' + +if not data[:4] == PN532_OK: + print ' Bad data from PN532:', data + if remote: + connection.close() + os._exit(True) +else: + pn532_print_firmware(data) + +if not remote or remote_type == 'EMULATOR': + card.waitfortag(' Waiting for source TAG...') + full_uid= card.uid + sens_res= [card.sens_res] + sel_res= [card.sel_res] +if remote: + if remote_type == 'READER': + print ' Waiting for remote TAG...' + connection.settimeout(None) + full_uid= recv_data(connection) + sens_res= [recv_data(connection)] + sel_res= [recv_data(connection)] + else: + send_data(connection,card.uid) + send_data(connection,card.sens_res) + send_data(connection,card.sel_res) + +mode= ['00'] +print ' UID:', full_uid +uid= [full_uid[2:]] +print ' sens_res:', sens_res[0] +print ' sel_res:', sel_res[0] +print +felica= ['01fea2a3a4a5a6a7c0c1c2c3c4c5c6c7ffff'] +nfcid= ['aa998877665544332211'] +try: + lengt= ['%02x' % (len(args[6]) / 2)] + gt= [args[6]] +except: + lengt= ['00'] + gt= [''] +try: + lentk= ['%02x' % (len(args[7]) / 2)] + tk= [args[7]] +except: + lentk= ['00'] + tk= [''] + +if not remote or remote_type == 'EMULATOR': + if card.acs_send_apdu(PN532_APDU['GET_GENERAL_STATUS']): + data= card.data +if remote: + if remote_type == 'EMULATOR': + send_data(connection,data) + else: + data= recv_data(connection) + +tags= pn532_print_status(data) +if tags > 1: + print ' Too many TAGS to EMULATE!' + if remote: + connection.close() + os._exit(True) + +#emulator.acs_send_apdu(emulator.PCSC_APDU['ACS_SET_PARAMETERS']+['14']) + +if not remote or remote_type == 'READER': + print ' Waiting for EMULATOR activation...' + status= emulator.acs_send_apdu(PN532_APDU['TG_INIT_AS_TARGET']+mode+sens_res+uid+sel_res+felica+nfcid+lengt+gt+lentk+tk) + if not status or not emulator.data[:4] == 'D58D': + print 'Target Init failed:', emulator.errorcode, emulator.ISO7816ErrorCodes[emulator.errorcode] + if remote: + connection.close() + os._exit(True) + data= emulator.data +if remote: + if remote_type == 'READER': + send_data(connection,data) + else: + print ' Waiting for remote EMULATOR activation...' + connection.settimeout(None) + data= recv_data(connection) + +mode= int(data[4:6],16) +baudrate= mode & 0x70 +print ' Emulator activated:' +print ' UID: 08%s' % uid[0] +print ' Baudrate:', PN532_BAUDRATES[baudrate] +print ' Mode:', +if mode & 0x08: + print 'ISO/IEC 14443-4 PICC' +if mode & 0x04: + print 'DEP' +framing= mode & 0x03 +print ' Framing:', PN532_FRAMING[framing] +initiator= data[6:] +print ' Initiator:', initiator +print + +print ' Waiting for APDU...' +started= False +try: + while 42: + # wait for emulator to receive a command + if not remote or remote_type == 'READER': + status= emulator.acs_send_apdu(PN532_APDU['TG_GET_DATA']) + data= emulator.data + #if not status or not emulator.data[:4] == 'D587': + if not status: + print 'Target Get Data failed:', emulator.errorcode, emulator.ISO7816ErrorCodes[emulator.errorcode] + print 'Data:', emulator.data + if remote: + connection.close() + os._exit(True) + if remote: + if remote_type == 'READER': + send_data(connection,data) + else: + connection.settimeout(None) + data= recv_data(connection) + errorcode= int(data[4:6],16) + if not errorcode == 0x00: + if remote: + connection.close() + if errorcode == 0x29: + if logging: + logfile.close() + print ' Session ended: EMULATOR released by Initiator' + if not remote or remote_type == 'READER': + emulator.acs_send_apdu(card.PCSC_APDU['ACS_LED_GREEN']) + os._exit(False) + print 'Error:',PN532_ERRORS[errorcode] + os._exit(True) + if not quiet: + print '<<', data[6:] + else: + if not started: + print ' Logging started...' + started= True + if logging: + logfile.write('<< %s\n' % data[6:]) + logfile.flush() + # relay command to tag + if not remote or remote_type == 'EMULATOR': + status= card.acs_send_direct_apdu(data[6:]) + data= card.data + errorcode= card.errorcode + if remote: + if remote_type == 'EMULATOR': + send_data(connection,data) + send_data(connection,errorcode) + else: + data= recv_data(connection) + errorcode= recv_data(connection) + if not quiet: + print '>>', data, errorcode + if logging: + logfile.write('>> %s %s\n' % (data,errorcode)) + logfile.flush + # relay tag's response back via emulator + if not remote or remote_type == 'READER': + status= emulator.acs_send_apdu(PN532_APDU['TG_SET_DATA']+[data]+[errorcode]) +except: + if logging: + logfile.close() + print ' Session ended with possible errors' + if remote: + connection.close() + if not remote or remote_type == 'READER': + emulator.acs_send_apdu(card.PCSC_APDU['ACS_LED_GREEN']) + os._exit(True) diff --git a/pynfc.py b/pynfc.py new file mode 100755 index 0000000..e2d2d5d --- /dev/null +++ b/pynfc.py @@ -0,0 +1,269 @@ +#!/usr/bin/python + +# +# pynfc.py - Python wrapper for libnfc +# version 0.2 (should work with libnfc 1.2.1 and 1.3.0) +# Nick von Dadelszen (nick@lateralsecurity.com) +# Lateral Security (www.lateralsecurity.com) + +# Thanks to metlstorm for python help :) +# +# This code is copyright (c) Nick von Dadelszen, 2009, All rights reserved. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU 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 General Public License +# along with this program. If not, see . +# + +from ctypes import * +import binascii +import logging +import time +import readline +import RFIDIOtconfig + +DCO_HANDLE_CRC = 0x00 +DCO_HANDLE_PARITY = 0x01 +DCO_ACTIVATE_FIELD = 0x10 +DCO_INFINITE_LIST_PASSIVE = 0x20 +DCO_INFINITE_SELECT = 0x20 +DCO_ACCEPT_INVALID_FRAMES = 0x30 +DCO_ACCEPT_MULTIPLE_FRAMES = 0x31 + +IM_ISO14443A_106 = 0x00 +IM_FELICA_212 = 0x01 +IM_FELICA_424 = 0x02 +IM_ISO14443B_106 = 0x03 +IM_JEWEL_106 = 0x04 + +MAX_FRAME_LEN = 264 +MAX_DEVICES = 16 +BUFSIZ = 8192 + +class TAG_INFO_ISO14443A(Structure): + _fields_ = [('abtAtqa', c_ubyte * 2), + ('btSak', c_ubyte), + ('uiUidLen', c_ulong), + ('abtUid', c_ubyte * 10), + ('uiAtsLen', c_ulong), + ('abtAts', c_ubyte * 36)] + +class NFC_DEVICE_DESC_T(Structure): + _fields_ = [('acDevice',c_char * BUFSIZ), + ('pcDriver',c_char_p), + ('pcPort',c_char_p), + ('uiSpeed',c_ulong), + ('uiBusIndex',c_ulong)] + +NFC_DEVICE_LIST = NFC_DEVICE_DESC_T * MAX_DEVICES + +class ISO14443A(object): + def __init__(self, ti): + self.uid = "".join(["%02x" % x for x in ti.abtUid[:ti.uiUidLen]]) + if ti.uiAtsLen: + self.atr = "".join(["%02x" % x for x in ti.abtAts[:ti.uiAtsLen]]) + else: + self.atr = "" + + def __str__(self): + rv = "ISO14443A(uid='%s', atr='%s')" % (self.uid, self.atr) + return rv + +class NFC(object): + + def __init__(self): + self.LIB = "/usr/local/lib/libnfc.so" + #self.LIB = "/usr/local/lib/libnfc_26102009.so.0.0.0" + #self.LIB = "./libnfc_nvd.so.0.0.0" + #self.LIB = "./libnfc_26102009.so.0.0.0" + #self.LIB = "/data/RFID/libnfc/libnfc-svn-1.3.0/src/lib/.libs/libnfc.so" + self.device = None + self.poweredUp = False + + if RFIDIOtconfig.debug: + self.initLog() + self.LIBNFC_VER= self.initlibnfc() + if RFIDIOtconfig.debug: + self.log.debug("libnfc %s" % self.LIBNFC_VER) + self.configure() + + def __del__(self): + self.deconfigure() + + def initLog(self, level=logging.DEBUG): +# def initLog(self, level=logging.INFO): + self.log = logging.getLogger("pynfc") + self.log.setLevel(level) + sh = logging.StreamHandler() + sh.setLevel(level) + f = logging.Formatter("%(asctime)s: %(levelname)s - %(message)s") + sh.setFormatter(f) + self.log.addHandler(sh) + + def initlibnfc(self): + if RFIDIOtconfig.debug: + self.log.debug("Loading %s" % self.LIB) + self.libnfc = CDLL(self.LIB) + self.libnfc.nfc_version.restype = c_char_p + return self.libnfc.nfc_version() + + def listreaders(self, target): + devices = NFC_DEVICE_LIST() + nfc_num_devices = c_ulong() + self.libnfc.nfc_list_devices(byref(devices),MAX_DEVICES,byref(nfc_num_devices)) + if target != None: + if nfc_num_devices.value <= target: + print 'Reader number %d not found!' % target + return None + i= 0 + for dev in devices: + if i == target: + return dev + i= i + 1 + print 'LibNFC ver' , self.libnfc.nfc_version(), 'devices:' + if nfc_num_devices.value == 0: + print '\t', 'no supported devices!' + return + i= 0 + for dev in devices: + if dev.acDevice: + print ' No: %d\t\t%s' % (i,dev.acDevice) + print ' \t\t\t\tDriver:',dev.pcDriver + if dev.pcPort != None: + print ' \t\t\t\tPort:', dev.pcPort + print ' \t\t\t\tSpeed:', dev.uiSpeed + i = i + 1 + + def configure(self): + if RFIDIOtconfig.debug: + self.log.debug("NFC Readers:") + self.listreaders(None) + self.log.debug("Connecting to NFC reader") + if RFIDIOtconfig.nfcreader: + target= self.listreaders(RFIDIOtconfig.nfcreader) + else: + target= None + if target: + target= byref(target) + self.device = self.libnfc.nfc_connect(target) + self.libnfc.nfc_device_name.restype = c_char_p + self.LIBNFC_READER= self.libnfc.nfc_device_name(self.device) + if RFIDIOtconfig.debug: + if self.device == None: + self.log.error("Error opening NFC reader") + else: + self.log.debug("Opened NFC reader " + self.LIBNFC_READER) + self.log.debug("Initing NFC reader") + self.libnfc.nfc_initiator_init(self.device) + if RFIDIOtconfig.debug: + self.log.debug("Configuring NFC reader") + + # Drop the field for a while + self.libnfc.nfc_configure(self.device,DCO_ACTIVATE_FIELD,False); + + # Let the reader only try once to find a tag + self.libnfc.nfc_configure(self.device,DCO_INFINITE_SELECT,False); + self.libnfc.nfc_configure(self.device,DCO_HANDLE_CRC,True); + self.libnfc.nfc_configure(self.device,DCO_HANDLE_PARITY,True); + self.libnfc.nfc_configure(self.device,DCO_ACCEPT_INVALID_FRAMES, True); + # Enable field so more power consuming cards can power themselves up + self.libnfc.nfc_configure(self.device,DCO_ACTIVATE_FIELD,True); + + def deconfigure(self): + if self.device != None: + if RFIDIOtconfig.debug: + self.log.debug("Deconfiguring NFC reader") + #self.powerOff() + self.libnfc.nfc_disconnect(self.device) + if RFIDIOtconfig.debug: + self.log.debug("Disconnected NFC reader") + self.device == None + + def powerOn(self): + self.libnfc.nfc_configure(self.device, DCO_ACTIVATE_FIELD, True) + if RFIDIOtconfig.debug: + self.log.debug("Powered up field") + self.poweredUp = True + + def powerOff(self): + self.libnfc.nfc_configure(self.device, DCO_ACTIVATE_FIELD, False) + if RFIDIOtconfig.debug: + self.log.debug("Powered down field") + self.poweredUp = False + + def readISO14443A(self): + """Detect and read an ISO14443A card, returns an ISO14443A() object.""" + if RFIDIOtconfig.debug: + self.log.debug("Polling for ISO14443A cards") + ti = TAG_INFO_ISO14443A() + r = self.libnfc.nfc_initiator_select_tag(self.device, IM_ISO14443A_106, None, None, byref(ti)) + if RFIDIOtconfig.debug: + self.log.debug('card Select r: ' + str(r)) + if r == None or r ==0: + if RFIDIOtconfig.debug: + self.log.error("No cards found, trying again") + time.sleep(1) + result = self.readISO14443A() + return result + else: + if RFIDIOtconfig.debug: + self.log.debug("Card found") + return ISO14443A(ti) + + def sendAPDU(self, apdu): + txData = [] + for i in range(0, len(apdu), 2): + txData.append(int(apdu[i:i+2], 16)) + + txAPDU = c_ubyte * len(txData) + tx = txAPDU(*txData) + + rxAPDU = c_ubyte * MAX_FRAME_LEN + rx = rxAPDU() + rxlen = c_ulong() + + + if RFIDIOtconfig.debug: + self.log.debug("Sending %d byte APDU: %s" % (len(tx),"".join(["%02x" % x for x in tx]))) + r = self.libnfc.nfc_initiator_transceive_dep_bytes(self.device, byref(tx), c_ulong(len(tx)), byref(rx), byref(rxlen)) + if RFIDIOtconfig.debug: + self.log.debug('APDU r =' + str(r)) + if r == 0: + if RFIDIOtconfig.debug: + self.log.error("Error sending/recieving APDU") + + result = False + return result + else: + rxAPDU = "".join(["%02x" % x for x in rx[:rxlen.value]]) + if RFIDIOtconfig.debug: + self.log.debug("Recieved %d byte APDU: %s" % (rxlen.value, rxAPDU)) + return rxAPDU + +if __name__ == "__main__": + n = NFC() + n.powerOn() + c = n.readISO14443A() + print 'UID: ' + c.uid + print 'ATR: ' + c.atr + + cont = True + while cont: + apdu = raw_input("enter the apdu to send now:") + if apdu == 'exit': + cont = False + else: + r = n.sendAPDU(apdu) + print r + + print 'Ending now ...' + n.deconfigure() diff --git a/q5reset.py b/q5reset.py new file mode 100755 index 0000000..ab0af87 --- /dev/null +++ b/q5reset.py @@ -0,0 +1,87 @@ +#!/usr/bin/python + +# q5reset.py - plooking too hard on your Q5? this should sort it out. +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2006, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + + +import RFIDIOtconfig +import sys +import os +import string + +try: + card= RFIDIOtconfig.card +except: + os._exit(True) + +args= RFIDIOtconfig.args +help= RFIDIOtconfig.help + +card.info('q5reset v0.1f') + +# standard config block +CFB='e601f004' +B1='ff801bc2' +B2='52500006' + +if help or len(args) == 0 or len(args) > 2: + print sys.argv[0] + ' - sooth and heal a sorely mistreated Q5 tag' + print 'Usage: ' + sys.argv[0] + ' [OPTIONS] [ID]' + print + print '\tIf the optional 8 HEX-digit ID argument is specified, the' + print '\tQ5 tag will be programmed to that ID. Otherwise, only the' + print '\tcontrol block will be written. If the literal \'ID\' is used' + print '\tthen a default ID will be programmed.' + print + print '\tNote that not all Q5 chips allow programming of their ID!' + print + os._exit(True) + +if args[0] == 'CONTROL': + card.settagtype(card.ALL) + while True: + print + card.select() + print ' Card ID: ' + card.uid + x= string.upper(raw_input(' *** Warning! This will overwrite TAG! Place defective card and proceed (y/n)? ')) + if x == 'N': + os._exit(False) + if x == 'Y': + break + print 'Writing...' + card.settagtype(card.Q5) + card.select() + if not card.writeblock(0,CFB): + print 'Write failed!' + os._exit(True) + else: + if len(args) > 1: + if not args[1] == 'ID': + out= card.Unique64Bit(card.HexToQ5(args[1] + '00')) + B1= '%08x' % int(out[:32],2) + B2= '%08x' % int(out[32:64],2) + if not card.writeblock(1,B1) or not card.writeblock(2,B2): + print 'Write failed!' + os._exit(True) + print 'Done!' + card.select() + print ' Card ID: ' + card.data + card.settagtype(card.ALL) +os._exit(False) diff --git a/readlfx.py b/readlfx.py new file mode 100755 index 0000000..74a89b4 --- /dev/null +++ b/readlfx.py @@ -0,0 +1,174 @@ +#!/usr/bin/python + +# readlfx.py - read all sectors from a LFX reader +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2006, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + +# usage: readlfx [KEY] +# +# specifiy KEY for protected tags. If not specified, TRANSPORT key will be tried. + +import RFIDIOtconfig +import sys +import os + +try: + card= RFIDIOtconfig.card +except: + os._exit(True) + +args= RFIDIOtconfig.args +help= RFIDIOtconfig.help + +Q5Mod= { '000':'Manchester',\ + '001':'PSK 1',\ + '010':'PSK 2',\ + '011':'PSK 3',\ + '100':'FSK 1 (a = 0)',\ + '101':'FSK 2 (a = 0)',\ + '110':'Biphase',\ + '111':'NRZ / direct'} + +card.info('readlfx v0.1m') + +# force card type if specified +if len(args) > 0: + print 'Setting tag type:', args[0] + card.settagtype(args[0]) +else: + card.settagtype(card.ALL) +card.select() +ID= card.uid +print 'Card ID: ' + ID +print 'Tag type: ' + card.LFXTags[card.tagtype] + +# set key if specified +if len(args) > 1: + key= args[1] +else: + key= '' + +# Login to Hitag2 +if card.tagtype == card.HITAG2 and card.readertype == card.READER_ACG: + if not key: + key= card.HITAG2_TRANSPORT_RWD + print ' Logging in with key: ' + key + if not card.login('','',key): + print 'Login failed!' + os._exit(True) + +# Interpret EM4x05 ID structure +if card.tagtype == card.EM4x05: + card.FDXBIDPrint(ID) + +# Q5 cards can emulate other cards, so check if this one responds as Q5 +if card.tagtype == card.EM4x02 or card.tagtype == card.Q5 or card.tagtype == card.EM4x05: + print ' Checking for Q5' + card.settagtype(card.Q5) + card.select() + Q5ID= card.uid + if card.tagtype == card.Q5: + print ' Q5 ID: ' + Q5ID + print + card.readblock(0) + print ' Config Block: ', + print card.ToHex(card.binary) + print ' Config Binary: ', + configbin= card.ToBinaryString(card.binary) + print configbin + print ' Reserved: ' + configbin[:12] + print ' Page Select: ' + configbin[12] + print ' Fast Write: ' + configbin[13] + print ' Data Bit Rate n5: ' + configbin[14] + print ' Data Bit Rate n4: ' + configbin[15] + print ' Data Bit Rate n3: ' + configbin[16] + print ' Data Bit Rate n2: ' + configbin[17] + print ' Data Bit Rate n1: ' + configbin[18] + print ' Data Bit Rate n0: ' + configbin[19] + print ' (Field Clocks/Bit: %d)' % (2 * int(configbin[14:20],2) + 2) + print ' Use AOR: ' + configbin[20] + print ' Use PWD: ' + configbin[21] + print ' PSK Carrier Freq: ' + configbin[22] + configbin[23] + print ' Inverse data out: ' + configbin[24] + print ' Modulation: ' + configbin[25] + configbin[26] + configbin[27] + " (%s)" % Q5Mod[configbin[25] + configbin[26] + configbin[27]] + print ' Maxblock: ' + configbin[28] + configbin[29] + configbin[30] + " (%d)" % int (configbin[28] + configbin[29] + configbin[30],2) + print ' Terminator: ' + configbin[31] + print + # Emulated ID is contained in 'traceability data' + print ' Traceability Data 1: ', + card.readblock(1) + td1= card.binary +# to test a hardwired number, uncomment following line (and td2 below) +# td1= chr(0xff) + chr(0x98) + chr(0xa6) + chr(0x4a) + print card.ToHex(td1) + print ' Traceability Data 2: ', + card.readblock(2) + td2= card.binary +# don't forget to set column parity! +# td2= chr(0x98) + chr(0xf8) + chr(0xc8) + chr(0x06) + print card.ToHex(td2) + print ' Traceability Binary: ', + tdbin= card.ToBinaryString(td1 + td2) + print tdbin + # traceability is broken into 4 bit chunks with even parity + print + print ' Header:', + print tdbin[:9] + print ' Parity (even)' + print ' D00-D03: ' + tdbin[9:13] + ' ' + tdbin[13] + print ' D10-D13: ' + tdbin[14:18] + ' ' + tdbin[18] + print ' D20-D23: ' + tdbin[19:23] + ' ' + tdbin[23] + print ' D30-D33: ' + tdbin[24:28] + ' ' + tdbin[28] + print ' D40-D43: ' + tdbin[29:33] + ' ' + tdbin[33] + print ' D50-D53: ' + tdbin[34:38] + ' ' + tdbin[38] + print ' D60-D63: ' + tdbin[39:43] + ' ' + tdbin[43] + print ' D70-D73: ' + tdbin[44:48] + ' ' + tdbin[48] + print ' D80-D83: ' + tdbin[49:53] + ' ' + tdbin[53] + print ' D90-D93: ' + tdbin[54:58] + ' ' + tdbin[58] + print ' ' + tdbin[59:63] + ' ' + tdbin[63] + ' Column Parity & Stop Bit' + # reconstruct data bytes + d0= chr(int(tdbin[9:13] + tdbin[14:18],2)) + d1= chr(int(tdbin[19:23] + tdbin[24:28],2)) + d2= chr(int(tdbin[29:33] + tdbin[34:38],2)) + d3= chr(int(tdbin[39:43] + tdbin[44:48],2)) + d4= chr(int(tdbin[49:53] + tdbin[54:58],2)) + print + print ' Reconstructed data D00-D93 (UNIQUE ID): ', + card.HexPrint(d0 + d1 + d2 + d3 + d4) + # set ID to Q5ID so block reading works + ID= Q5ID + print + else: + print ' Native - UNIQUE ID: ' + card.EMToUnique(ID) + +sector = 0 +while sector < card.LFXTagBlocks[card.tagtype]: + print ' sector %02x: ' % sector, + if card.readblock(sector): + print card.data + else: + print 'Read error: ' + card.errorcode + sector += 1 +print + +# set reader back to all cards +card.settagtype(card.ALL) +card.select() +print +os._exit(False) diff --git a/readmifare1k.py b/readmifare1k.py new file mode 100755 index 0000000..90b654b --- /dev/null +++ b/readmifare1k.py @@ -0,0 +1,104 @@ +#!/usr/bin/python + +# readmifare1k.py - read all sectors from a mifare standard tag +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2006, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + + +import RFIDIOtconfig +import sys +import os + +try: + card= RFIDIOtconfig.card +except: + os._exit(True) + +card.info('readmifare1k v0.1i') +card.select() +print 'Card ID: ' + card.uid + +blocksread= 0 +blockslocked= 0 +lockedblocks= [] + +for type in ['AA', 'BB', 'FF']: + card.select() + if card.login(0,type,''): + if card.readMIFAREblock(0): + card.MIFAREmfb(card.MIFAREdata) + else: + print 'Read error: %s %s' % (card.errorcode , card.ISO7816ErrorCodes[card.errorcode]) + os._exit(True) + print "\nMIFARE data (keytype %s):" % type + print "\tSerial number:\t\t%s\n\tCheck byte:\t\t%s\n\tManufacturer data:\t%s" % (card.MIFAREserialnumber, card.MIFAREcheckbyte, card.MIFAREmanufacturerdata) +print + +sector = 1 +while sector < 16: + locked= True + for type in ['AA', 'BB', 'FF']: + print ' sector %02x: Keytype: %s' % (sector,type), + card.select() + if card.login(sector * 4,type,''): + locked= False + blocksread += 1 + print 'Login OK. Data:' + print + print ' ', + for block in range(4): + # card.login(sector,type,'') + if card.readMIFAREblock((sector * 4) + block): + print card.MIFAREdata, + sys.stdout.flush() + else: + print 'Read error: %s %s' % (card.errorcode , card.ISO7816ErrorCodes[card.errorcode]) + os._exit(True) + print + card.MIFAREkb(card.MIFAREdata) + print " Access Block User Data Byte: " + card.MIFAREaccessconditionsuserbyte + print + print "\tKey A (non-readable):\t%s\n\tKey B:\t\t\t%s\n\tAccess conditions:\t%s" % (card.MIFAREkeyA, card.MIFAREkeyB, card.MIFAREaccessconditions) + print "\t\tMIFAREC1:\t%s\n\t\tMIFAREC2:\t%s\n\t\tMIFAREC3:\t%s" % (hex(card.MIFAREC1)[2:], hex(card.MIFAREC2)[2:], hex(card.MIFAREC3)[2:]) + print "\t\tMIFAREblock0AC: " + card.MIFAREblock0AC + print "\t\t\t" + card.MIFAREACDB[card.MIFAREblock0AC] + print "\t\tMIFAREblock1AC: " + card.MIFAREblock1AC + print "\t\t\t" + card.MIFAREACDB[card.MIFAREblock1AC] + print "\t\tMIFAREblock2AC: " + card.MIFAREblock2AC + print "\t\t\t" + card.MIFAREACDB[card.MIFAREblock2AC] + print "\t\tMIFAREblock3AC: " + card.MIFAREblock3AC + print "\t\t\t" + card.MIFAREACKB[card.MIFAREblock3AC] + print + continue + elif card.errorcode != '': + print 'Login Error: %s %s' % (card.errorcode , card.ISO7816ErrorCodes[card.errorcode]) + elif type == 'FF': + print 'Login failed' + print '\r', + sys.stdout.flush() + if locked: + blockslocked += 1 + lockedblocks.append(sector) + sector += 1 +print +print ' Total blocks read: %d' % blocksread +print ' Total blocks locked: %d' % blockslocked +if lockedblocks > 0: + print ' Locked block numbers:', lockedblocks +os._exit(False) diff --git a/readmifaresimple.py b/readmifaresimple.py new file mode 100755 index 0000000..7c3acbf --- /dev/null +++ b/readmifaresimple.py @@ -0,0 +1,279 @@ +#!/usr/bin/python + +# readmifaresimple.py - read all sectors from a mifare tag +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2006, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + +import sys +import os +import RFIDIOtconfig +import time +import string + +try: + card= RFIDIOtconfig.card +except: + os._exit(False) + +args= RFIDIOtconfig.args +help= RFIDIOtconfig.help + +blocksread= 0 +blockslocked= 0 +lockedblocks= [] +DEFAULT_KEY= 'FFFFFFFFFFFF' +KEYS= ['FFFFFFFFFFFF','A0A1A2A3A4A5','B0B1B2B3B4B5','000000000000','ABCDEF012345','4D3A99C351DD','1A982C7E459A','D3F7D3F7D3F7','AABBCCDDEEFF'] +KEYTYPES=['AA','BB','FF'] +DEFAULT_KEYTYPE= 'AA' +BLOCKS_PER_SECT= 4 +START_BLOCK= 0 +END_BLOCK= 63 +CloneData= [] +RESET_DATA= '00000000000000000000000000000000' +RESET_TRAILER= 'FFFFFFFFFFFFFF078069FFFFFFFFFFFF' + +if help or len(args) > 6: + print sys.argv[0] + ' - read mifare tags' + print 'Usage: ' + sys.argv[0] + ' [START BLOCK] [END BLOCK] [KEY] [KEYTYPE] [COPY|RESET]' + print + print '\tRead Mifare sector numbers [START BLOCK] to [END BLOCK], using' + print '\t[KEY] to authenticate. Keys can be truncated to \'AA\' for transport' + print '\tkey \'A0A1A2A3A4A5\', \'BB\' for transport key \'B0B1B2B3B4B5\' or \'FF\'' + print '\tfor transport key \'FFFFFFFFFFFF\'.' + print + print '\tSTART BLOCK defaults to 0 and END BLOCK to 63. If not specified, KEY' + print '\tdefaults to \'FFFFFFFFFFFF\', and KEYTYPE defaults to \'AA\'. All known' + print '\talternative keys are tried in the event of a login failure.' + print + print '\tIf the option \'RESET\' is specified, the card will be programmed to' + print '\tfactory defaults after reading.' + print + print '\tIf the option \'COPY\' is specified, a card will be programmed with' + print '\twith the data blocks read (note that block 0 cannot normally be written)' + print + os._exit(True) + +card.info('readmifaresimple v0.1g') + +if not card.select(): + card.waitfortag('waiting for Mifare TAG...') + +# set options + +reset= False +copy= False + +try: + if args[4] == 'RESET': + reset= True +except: + pass + +try: + if args[4] == 'COPY': + copy= True +except: + pass + +if copy: + try: + otherkey= args[5] + except: + pass + +try: + keytype= string.upper(args[3]) + KEYTYPES.remove(keytype) + trykeytype= [keytype] + KEYTYPES +except: + keytype= DEFAULT_KEYTYPE + trykeytype= KEYTYPES + +try: + key= string.upper(args[2]) + trykey= [key] + KEYS +except: + key= DEFAULT_KEY + trykey= KEYS + +try: + endblock= int(args[1]) +except: + endblock= END_BLOCK + +try: + startblock= int(args[0]) +except: + startblock= 0 + +if not reset: + print ' Card ID:', card.uid + print + print ' Reading from %02d to %02d, key %s (%s)\n' % (startblock, endblock, key, keytype) + +# see if key is an abbreviation +# if so, only need to set keytype and login will use transport keys +for d in ['AA','BB','FF']: + if key == d: + keytype= key + key= '' + +if len(key) > 12: + print 'Invalid key: ', key + os._exit(True) + +block= startblock +while block <= endblock and not reset: + locked= True + print ' Block %03i:' % block, + # ACG requires a login only to the base 'sector', so block number must be divided + # by BLOCKS_PER_SECT + if card.readertype == card.READER_ACG: + loginblock= block / BLOCKS_PER_SECT + else: + loginblock= block + loggedin= False + for y in trykey: + if loggedin: + break + for x in trykeytype: + if card.login(loginblock,x,y): + loggedin= True + goodkey= y + goodkeytype= x + break + else: + # clear the error + card.select() + + if loggedin: + print 'OK (%s %s) Data:' % (goodkey,goodkeytype), + locked= False + if card.readMIFAREblock(block): + blocksread += 1 + print card.MIFAREdata, + print card.ReadablePrint(card.ToBinary(card.MIFAREdata)) + CloneData += [card.MIFAREdata] + else: + print 'Read error: %s %s' % (card.errorcode , card.ISO7816ErrorCodes[card.errorcode]) + else: + print 'Login error: %s %s' % (card.errorcode , card.ISO7816ErrorCodes[card.errorcode]) + locked= True + blockslocked += 1 + lockedblocks.append(block) + # ACG requires re-select to clear error condition after failed login + if card.readertype == card.READER_ACG: + card.select() + block += 1 + +if not reset: + print + print ' Total blocks read: %d' % blocksread + print ' Total blocks locked: %d' % blockslocked + if blockslocked > 0: + print ' Locked block numbers:', lockedblocks + print + +if not reset and not copy: + os._exit(False) + +raw_input('Place tag to be written and hit to proceed') + +while True: + print + card.select() + print ' Card ID: ' + card.uid + print + if not reset: + if keytype == 'AA': + print ' KeyA will be set to', key + ', KeyB will be set to %s' % otherkey + else: + print ' KeyA will be set to %s,' % otherkey, 'KeyB will be set to', key + else: + print ' KeyA will be set to FFFFFFFFFFFF, KeyB will be set to FFFFFFFFFFFF' + print + x= string.upper(raw_input(' *** Warning! This will overwrite TAG! Proceed (y/n) or to select new TAG? ')) + if x == 'N': + os._exit(False) + if x == 'Y': + print + break + +block= startblock +outblock= 0 +while block <= endblock: + # block 0 is not writeable + if block == 0: + block += 1 + outblock += 1 + continue + print ' Block %03i: ' % block, + # ACG requires a login only to the base 'sector', so block number must be divided + # by BLOCKS_PER_SECT + if card.readertype == card.READER_ACG: + loginblock= block / BLOCKS_PER_SECT + else: + loginblock= block + loggedin= False + if not reset: + # assume we're writing to a factory blank, so try default keys first + trykey= KEYS + [key] + trykeytype= ['AA','BB'] + for y in trykey: + if loggedin: + break + for x in trykeytype: + if card.login(loginblock,x,y): + loggedin= True + goodkey= y + goodkeytype= x + break + else: + # clear the error + card.select() + + if loggedin: + if (block + 1) % 4: + if reset: + blockdata= RESET_DATA + else: + blockdata= CloneData[outblock] + else: + if reset: + blockdata= RESET_TRAILER + else: + if keytype == 'BB': + # only ACL is useful from original data + blockdata= RESET_TRAILER[:12] + CloneData[outblock][12:20] + key + else: + # ACL plus KeyB + blockdata= key + CloneData[outblock][12:20] + otherkey + print 'OK (%s %s), writing: %s' % (goodkey,goodkeytype,blockdata), + if card.writeblock(block,blockdata): + print 'OK' + else: + print 'Write error: %s %s' % (card.errorcode , card.ISO7816ErrorCodes[card.errorcode]) + else: + print 'Login error: %s %s' % (card.errorcode , card.ISO7816ErrorCodes[card.errorcode]) + # ACG requires re-select to clear error condition after failed login + if card.readertype == card.READER_ACG: + card.select() + block += 1 + outblock += 1 +os._exit(False) diff --git a/readmifareultra.py b/readmifareultra.py new file mode 100755 index 0000000..d7da797 --- /dev/null +++ b/readmifareultra.py @@ -0,0 +1,140 @@ +#!/usr/bin/python + +# readmifareultra.py - read all sectors from a Ultralight tag +# +# Keith Howell +# built on the code by: +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2006, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + + +import RFIDIOtconfig +import sys +import os + +try: + card= RFIDIOtconfig.card +except: + os._exit(True) + +help= RFIDIOtconfig.help + +if help: + print sys.argv[0] + ' - read mifare ultralight tags' + print 'Usage: ' + sys.argv[0] + print + os._exit(True) + +card.info('readmifareultra v0.1a') +card.waitfortag('Waiting for Mifare Ultralight...') + +blocks=16 + +print '\n ID: ' + card.uid +print 'Type: ' + card.tagtype + +card.select() +# pull header block information from the tag +if card.readblock(0): + sn0=card.data[0:2] + sn1=card.data[2:4] + sn2=card.data[4:6] + bcc0=card.data[6:8] +else: + print 'read error: %s' % card.errorcode + +if card.readblock(1): + sn3=card.data[0:2] + sn4=card.data[2:4] + sn5=card.data[4:6] + sn6=card.data[6:8] +else: + print 'read error: %s' % card.errorcode + +if card.readblock(2): + bcc1=card.data[0:2] + internal=card.data[2:4] + lock0=card.data[4:6] + lock1=card.data[6:8] +else: + print 'read error: %s' % card.errorcode + +if card.readblock(3): + otp0=card.data[0:2] + otp1=card.data[2:4] + otp2=card.data[4:6] + otp3=card.data[6:8] +else: + print 'read error: %s' % card.errorcode + +# convert lock bytes to binary for later use +lbits0=card.ToBinaryString(card.ToBinary(lock0)) +lbits1=card.ToBinaryString(card.ToBinary(lock1)) +lbits=lbits1 + lbits0 + +y=0 +plock='' +for x in range(15,-1,-1): + plock = lbits[y:y+1] + plock + y += 1 + +# show status of the OTP area on the tag +print 'OTP area is', +if int(plock[3:4]) == 1: + print 'locked and', +else: + print 'unlocked and', +if int(plock[0:1]) == 1: + print 'cannot be changed' +else: + print 'can be changed' + +print 'If locked, blocks 4 through 9', +if int(plock[1:2]) == 1: + print 'cannot be unlocked' +else: + print 'can be unlocked' + +print 'If locked, blocks 0a through 0f', +if int(plock[2:3]) == 1: + print 'cannot be unlocked' +else: + print 'can be unlocked' + +print '\nTag Data:' +# DATA0 byte starts on page/block 4 +for x in range(blocks): + print ' Block %02x:' % x, + if card.readblock(x): + print card.data[:8], + print card.ReadablePrint(card.ToBinary(card.data[:8])), + if x > 2: + if int(plock[x:x+1]) == 1: + print ' locked' + else: + print ' unlocked' + else: + print ' -' + else: + print 'read error: %s' % card.errorcode +print + +if x > 0: + os._exit(False) +else: + os._exit(True) diff --git a/readtag.py b/readtag.py new file mode 100755 index 0000000..2b5c924 --- /dev/null +++ b/readtag.py @@ -0,0 +1,52 @@ +#!/usr/bin/python + +# readtag.py - read all sectors from a standard tag +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2006, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + + +import RFIDIOtconfig +import sys +import os + +try: + card= RFIDIOtconfig.card +except: + os._exit(True) + +card.info('readtag v0.1e') +card.select() +print '\nID: ' + card.uid +print ' Data:' + +card.select() +for x in range(255): + print ' Block %02x:' % x, + if card.readblock(x): + print card.data, + print card.ReadablePrint(card.ToBinary(card.data)) + else: + print 'read error: %s, %s' % (card.errorcode, card.ISO7816ErrorCodes[card.errorcode]) + +print '\n Total blocks: ', +print x +if x > 0: + os._exit(False) +else: + os._exit(True) diff --git a/sod.py b/sod.py new file mode 100755 index 0000000..c993df3 --- /dev/null +++ b/sod.py @@ -0,0 +1,45 @@ +#!/usr/bin/python + + +# sod.py - try to find X509 data in EF.SOD +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2007, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# +import commands +import sys +import os + +x= 0 +if len(sys.argv) > 1: + sod= open(sys.argv[1],"r") +else: + sod= open("/tmp/EF_SOD.BIN","r") +data= sod.read() +while x < len(data): + out= open("/tmp/SOD","w") + out.write(data[x:]) + out.flush() + out.close() + (exitstatus, outtext) = commands.getstatusoutput("openssl pkcs7 -text -print_certs -in /tmp/SOD -inform DER") + if not exitstatus and len(outtext) > 0: + print 'PKCS7 certificate found at offset %d:' % x + print + print outtext + os._exit(False) + x += 1 +os._exit(True) diff --git a/testacg.sh b/testacg.sh new file mode 100755 index 0000000..ebc136f --- /dev/null +++ b/testacg.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +xterm -T 'ACG port /dev/ttyUSB0' -e python ./multiselect.py -s 9600 -l /dev/ttyUSB0 -R RFIDIOt.rfidiot.READER_ACG & diff --git a/testlahf.sh b/testlahf.sh new file mode 100755 index 0000000..33cd3fa --- /dev/null +++ b/testlahf.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +xterm -T 'LAHF port /dev/ttyUSB0' -e python ./multiselect.py -s 9600 -l /dev/ttyUSB0 -R RFIDIOt.rfidiot.READER_ACG & +xterm -T 'LAHF port /dev/ttyUSB1' -e python ./multiselect.py -s 9600 -l /dev/ttyUSB1 -R RFIDIOt.rfidiot.READER_ACG & diff --git a/transit.py b/transit.py new file mode 100755 index 0000000..2f72e94 --- /dev/null +++ b/transit.py @@ -0,0 +1,133 @@ +#!/usr/bin/python + +# transit.py - generate / decode FDI Matalec Transit 500 and Transit 999 UIDs +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2009, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + + +import RFIDIOtconfig +import sys +import os +import string + +try: + card= RFIDIOtconfig.card +except: + os._exit(True) + +args= RFIDIOtconfig.args +help= RFIDIOtconfig.help + +card.info('transit v0.1a') + +precoded= False + +if not help and len(args) > 0 and len(args[0]) == 64: + print "\nDecode: ", + card.TRANSITIDPrint(args[0]) + if len(args) == 2: + if args[1] == 'WRITE': + precoded= True + else: + print 'Unrecognised option: ' + args[1] + os._exit(True) + else: + print + os._exit(False) + +if not help and ((len(args) > 0 and len(args[0]) == 8) or precoded): + if precoded: + out= args[0] + else: + print "\nEncode: ", + out= card.TRANSITIDEncode(args[0]) + print out + if (len(args) == 2 and args[1] == 'WRITE') or precoded: + while True: + # Q5 must be forced into Q5 mode to be sure of detection so try that first + if card.readertype == card.READER_ACG: + card.settagtype(card.Q5) + card.select() + if card.readertype == card.READER_ACG: + if not card.tagtype == card.Q5: + card.settagtype(card.ALL) + card.waitfortag('Waiting for blank tag...') + print ' Tag ID: ' + card.data + if card.tagtype == card.Q5: + x= string.upper(raw_input(' *** Warning! This will overwrite TAG! Proceed (y/n)? ')) + if x == 'N': + os._exit(False) + if x == 'Y': + break + else: + x= raw_input(' Incompatible TAG! Hit to retry...') + writetag= True + print + else: + writetag= False + # now turn it all back to 4 byte hex blocks for writing + outbin= '' + outhex= ['','','','',''] + # control block for Q5: + # carrier 32 (2 * 15 + 2) + # rf/? (don't care) - set to 00 + # data not inverted + # manchester + # maxblock 2 + print ' Q5 Control Block: ', + q5control= '6000F004' + print q5control + for x in range(0,len(out),8): + outbin += chr(int(out[x:x + 8],2)) + for x in range(0,len(outbin),4): + print ' Q5 Data Block %02d:' % (x / 4 + 1), + outhex[x / 4 + 1]= card.ToHex(outbin[x:x+4]) + print outhex[x / 4 + 1] + if writetag == True: + print + outhex[0]= q5control + for x in range(2,-1,-1): + if(x != 0): + print " Writing block %02x:" % x, + if not card.writeblock(x,outhex[x]): + # we expect a Q5 to fail after writing the control block as it re-reads + # it before trying to verify the write and switches mode so is now no longer in Q5 mode + if x == 0: + print ' Control: ' + outhex[x] + print + print ' Done!' + else: + print 'Write failed!' + os._exit(True) + else: + print outhex[x] + if card.readertype == card.READER_ACG: + card.settagtype(card.ALL) + print + os._exit(False) +print +print sys.argv[0] + ' - Q5 encode / decode TRANSIT compliant IDs' +print '\nUsage: ' + sys.argv[0] + ' [OPTIONS] [WRITE]' +print +print '\tIf a single 64 Bit BINARY UID is provided, it will be decoded according to the TRANSIT standard.' +print '\tAlternatively, specifying a 8 HEX digit UID will encode the 64 Bit BINARY with LRC and sentinels.' +print +print '\tIf the WRITE option is specified, a Q5 will be programmed to emulate a TRANSIT tag.' +print +os._exit(True) diff --git a/unique.py b/unique.py new file mode 100755 index 0000000..c897b1e --- /dev/null +++ b/unique.py @@ -0,0 +1,152 @@ +#!/usr/bin/python + +# unique.py - generate EM4x02 and/or UNIQUE compliant IDs +# these can then be written to a Q5 tag to emulate EM4x02 +# by transmitting data blocks 1 & 2 (MAXBLOCK == 2), +# or Hitag2 in Public Mode A with data stored in blocks +# 4 and 5. +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2006, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + + +import RFIDIOtconfig +import sys +import os +import string +import time + +try: + card= RFIDIOtconfig.card +except: + os._exit(True) + +args= RFIDIOtconfig.args +help= RFIDIOtconfig.help + +card.info('unique v0.1k') + +# Q5 config block +Q5CFB='e601f004' +# Hitag2 config block +H2CFB= card.HITAG2_PUBLIC_A + card.HITAG2_TRANSPORT_TAG + +if len(args) < 1 or len(args) > 3 or help: + print + print sys.argv[0] + ' - generate EM4x02 and/or UNIQUE compliant ID data blocks' + print '\nUsage: ' + sys.argv[0] + ' [OPTIONS] [\"WRITE\"]' + print ' ' + sys.argv[0] + ' [OPTIONS] <\"CLONE\">' + print + print '\t10 digit HEX ID will be translated to valid data for blocks 1 & 2' + print '\tfor a Q5 tag running in EM4x02 emulation mode, and blocks 4 & 5 for' + print '\ta Hitag2, where TYPE is U for UNIQUE code and E for EM4x02. For ' + print '\tguidance, standard emulation control blocks (0 & 3 respectively)' + print '\twill also be displayed.' + print + print '\tIf the optional WRITE argument is specified, programming a Q5 or' + print '\tHitag2 tag will be initiated.' + print + print '\tIf the single word CLONE is specified, the reader will scan for' + print '\ta Unique tag and then wait for a suitable blank to be presented' + print '\tfor writing. No prompting will take place before the target is' + print '\toverwritten.' + os._exit(True) + + +if len(args) == 1 and string.upper(args[0]) == "CLONE": + type= 'UNIQUE' + clone= True + card.settagtype(card.EM4x02) + card.waitfortag('Waiting for Unique tag...') + id= card.uid + idbin= card.UniqueToEM(card.HexReverse(id)) +else: + clone= False + if len(args[1]) != 10: + print 'ID must be 10 HEX digits!' + os._exit(True) + id= args[1] + +if args[0] == 'E': + type= 'EM4x02' + idbin= card.UniqueToEM(card.HexReverse(id)) +else: + if args[0] == 'U': + type= 'UNIQUE' + idbin= card.ToBinaryString(card.ToBinary(id)) + else: + if not clone: + print 'Unknown TYPE: ' + args[0] + os._exit(True) + + +out= card.Unique64Bit(idbin) +manchester= card.BinaryToManchester(out) +db1= '%08x' % int(out[:32],2) +db2= '%08x' % int(out[32:64],2) +print +print ' ' + type + ' ID: ' + id +print ' Q5 ID: ' + '%08x' % int(idbin[:32],2) +if type == 'EM4x02': + print ' UNIQUE ID: ' + '%10x' % int(idbin,2) +else: + print ' EM4x02 ID: ' + ('%10x' % int(card.UniqueToEM(id),2))[::-1] +print ' Binary traceablility data: ' + out +print ' Manchester Encoded: ' + manchester +print +print ' Q5 Config Block (0): ' + Q5CFB +print ' Data Block 1: ' + db1 +print ' Data Block 2: ' + db2 +print +print ' Hitag2 Config Block (3): ' + H2CFB +print ' Data Block 4: ' + db1 +print ' Data Block 5: ' + db2 + +if (len(args) == 3 and string.upper(args[2]) == 'WRITE') or clone: + # check for Q5 first` + if card.readertype == card.READER_ACG: + card.settagtype(card.Q5) + if not card.select(): + card.settagtype(card.ALL) + while not (card.tagtype == card.Q5 or card.tagtype == card.HITAG2): + card.waitfortag('Waiting for blank tag (Q5 or Hitag2)...') + print 'Tag ID: ' + card.uid + if not clone: + x= string.upper(raw_input(' *** Warning! This will overwrite TAG! Proceed (y/n)? ')) + if x != 'Y': + os._exit(False) + # allow blank to settle + time.sleep(2) + print 'Writing...' + if card.tagtype == card.Q5: + if not card.writeblock(0,Q5CFB) or not card.writeblock(1,db1) or not card.writeblock(2,db2): + print 'Write failed!' + os._exit(True) + if card.tagtype == card.HITAG2: + if card.readertype == card.READER_ACG: + card.login('','',card.HITAG2_TRANSPORT_RWD) + if not card.writeblock(3,H2CFB) or not card.writeblock(4,db1) or not card.writeblock(5,db2): + print 'Write failed!' + os._exit(True) + card.settagtype(card.EM4x02) + card.select() + print 'Card ID: ' + card.uid + print ' Unique ID: ' + card.EMToUnique(card.uid) + print 'Done!' + card.settagtype(card.ALL) +os._exit(False) diff --git a/upload2cosmo.gpsh b/upload2cosmo.gpsh new file mode 100644 index 0000000..8025e74 --- /dev/null +++ b/upload2cosmo.gpsh @@ -0,0 +1,14 @@ +// script to install epassport.cap to cosmo card using gpshell (http://sourceforge.net/projects/globalplatform/) +mode_211 +establish_context +// edit the following line to match your PCSC reader +//card_connect -readerNumber 2 +card_connect -reader "OMNIKEY CardMan 5x21 00 01" +//card_connect -reader "OMNIKEY CardMan 5x21 (USB iClass Reader) 00 01" +//card_connect -reader "ACS ACR 38U-CCID 00 00" +select -AID A0000001510000 +open_sc -security 3 -mac_key 404142434445464748494A4B4C4D4E4F -enc_key 404142434445464748494A4B4C4D4E4F -kek_key 404142434445464748494A4B4C4D4E4F // Open secure channel +delete -AID A00000024710 +install -file epassport.cap -priv 2 +card_disconnect +release_context diff --git a/upload2jcop.gpsh b/upload2jcop.gpsh new file mode 100644 index 0000000..1144384 --- /dev/null +++ b/upload2jcop.gpsh @@ -0,0 +1,15 @@ +// script to install epassport.cap to jcop card using gpshell (http://sourceforge.net/projects/globalplatform/) +mode_211 +establish_context +// edit the following line to match your PCSC reader +//card_connect -readerNumber 2 +//card_connect -reader "OMNIKEY CardMan 5x21 00 01" +//card_connect -reader "OMNIKEY CardMan 5x21 (USB iClass Reader) 00 01" +card_connect +//card_connect -reader "ACS ACR 38U-CCID 00 00" +select -AID A000000003000000 +open_sc -security 3 -mac_key 404142434445464748494A4B4C4D4E4F -enc_key 404142434445464748494A4B4C4D4E4F -kek_key 404142434445464748494A4B4C4D4E4F // Open secure channel +delete -AID A00000024710 +install -file epassport.cap -priv 2 +card_disconnect +release_context diff --git a/upload2nokia.gpsh b/upload2nokia.gpsh new file mode 100644 index 0000000..7b4ba48 --- /dev/null +++ b/upload2nokia.gpsh @@ -0,0 +1,15 @@ +// script to install epassport.cap to jcop card using gpshell (http://sourceforge.net/projects/globalplatform/) +// note that the secure element must be unlocked, see the Nokia website at +// http://www.forum.nokia.com/info/sw.nokia.com/id/a796065d-fa6a-449f-b3de-70d46ff99f19/NFC_Unlock.zip.html +mode_211 +establish_context +// edit the following line to match your PCSC reader +//card_connect -readerNumber 3 +//card_connect -reader "OMNIKEY CardMan 5x21 00 01" +card_connect -reader "OMNIKEY CardMan 5x21 (USB iClass Reader) 00 01" +select -AID A000000003000000 +open_sc -security 3 -keyver 42 -mac_key 404142434445464748494a4b4c4d4e4f -enc_key 404142434445464748494a4b4c4d4e4f -kek_key 404142434445464748494a4b4c4d4e4f +delete -AID A00000024710 +install -file epassport.cap -priv 2 +card_disconnect +release_context diff --git a/upload2smartcafe.gpsh b/upload2smartcafe.gpsh new file mode 100644 index 0000000..031ab4f --- /dev/null +++ b/upload2smartcafe.gpsh @@ -0,0 +1,13 @@ +// script to install epassport.cap to jcop card using gpshell (http://sourceforge.net/projects/globalplatform/) +mode_211 +establish_context +// edit the following line to match your PCSC reader +//card_connect -readerNumber 2 +//card_connect -reader "OMNIKEY CardMan 5x21 (USB iClass Reader) 00 01" +card_connect +select -AID A000000003000000 +open_sc -scp 2 -scpimpl 0x15 -security 1 -keyind 0 -keyver 0 -keyDerivation emvcps11 -key 404142434445464748494A4B4C4D4E4F +delete -AID A00000024710 +install -file epassport.cap -priv 2 +card_disconnect +release_context diff --git a/writelfx.py b/writelfx.py new file mode 100755 index 0000000..bb444a9 --- /dev/null +++ b/writelfx.py @@ -0,0 +1,178 @@ +#!/usr/bin/python + +# writelfx.py - read and then write all sectors from a LFX reader +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2009, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + +import RFIDIOtconfig +import sys +import os + +try: + card= RFIDIOtconfig.card +except: + os._exit(True) + +args= RFIDIOtconfig.args +help= RFIDIOtconfig.help + +Q5Mod= { '000':'Manchester',\ + '001':'PSK 1',\ + '010':'PSK 2',\ + '011':'PSK 3',\ + '100':'FSK 1 (a = 0)',\ + '101':'FSK 2 (a = 0)',\ + '110':'Biphase',\ + '111':'NRZ / direct'} + +card.info('writelfx v0.1b') + +# force card type if specified +if len(args) > 0: + print 'Setting tag type:', args[0] + card.settagtype(args[0]) +else: + card.settagtype(card.ALL) +card.select() +ID= card.uid +print 'Card ID: ' + ID +print 'Tag type: ' + card.LFXTags[card.tagtype] + +# set key if specified +if len(args) > 1: + key= args[1] +else: + key= '' + +# Login to Hitag2 +if card.tagtype == card.HITAG2 and card.readertype == card.READER_ACG: + if not key: + key= card.HITAG2_TRANSPORT_RWD + print ' Logging in with key: ' + key + if not card.login('','',key): + print 'Login failed!' + os._exit(True) + +# Interpret EM4x05 ID structure +if card.tagtype == card.EM4x05: + card.FDXBIDPrint(ID) + +# Q5 cards can emulate other cards, so check if this one responds as Q5 +if card.tagtype == card.EM4x02 or card.tagtype == card.Q5 or card.tagtype == card.EM4x05: + print ' Checking for Q5' + card.settagtype(card.Q5) + card.select() + Q5ID= card.uid + if card.tagtype == card.Q5: + print ' Q5 ID: ' + Q5ID + print + card.readblock(0) + print ' Config Block: ', + print card.ToHex(card.binary) + print ' Config Binary: ', + configbin= card.ToBinaryString(card.binary) + print configbin + print ' Reserved: ' + configbin[:12] + print ' Page Select: ' + configbin[12] + print ' Fast Write: ' + configbin[13] + print ' Data Bit Rate n5: ' + configbin[14] + print ' Data Bit Rate n4: ' + configbin[15] + print ' Data Bit Rate n3: ' + configbin[16] + print ' Data Bit Rate n2: ' + configbin[17] + print ' Data Bit Rate n1: ' + configbin[18] + print ' Data Bit Rate n0: ' + configbin[19] + print ' (Field Clocks/Bit: %d)' % (2 * int(configbin[14:20],2) + 2) + print ' Use AOR: ' + configbin[20] + print ' Use PWD: ' + configbin[21] + print ' PSK Carrier Freq: ' + configbin[22] + configbin[23] + print ' Inverse data out: ' + configbin[24] + print ' Modulation: ' + configbin[25] + configbin[26] + configbin[27] + " (%s)" % Q5Mod[configbin[25] + configbin[26] + configbin[27]] + print ' Maxblock: ' + configbin[28] + configbin[29] + configbin[30] + " (%d)" % int (configbin[28] + configbin[29] + configbin[30],2) + print ' Terminator: ' + configbin[31] + print + # Emulated ID is contained in 'traceability data' + print ' Traceability Data 1: ', + card.readblock(1) + td1= card.binary +# to test a hardwired number, uncomment following line (and td2 below) +# td1= chr(0xff) + chr(0x98) + chr(0xa6) + chr(0x4a) + print card.ToHex(td1) + print ' Traceability Data 2: ', + card.readblock(2) + td2= card.binary +# don't forget to set column parity! +# td2= chr(0x98) + chr(0xf8) + chr(0xc8) + chr(0x06) + print card.ToHex(td2) + print ' Traceability Binary: ', + tdbin= card.ToBinaryString(td1 + td2) + print tdbin + # traceability is broken into 4 bit chunks with even parity + print + print ' Header:', + print tdbin[:9] + print ' Parity (even)' + print ' D00-D03: ' + tdbin[9:13] + ' ' + tdbin[13] + print ' D10-D13: ' + tdbin[14:18] + ' ' + tdbin[18] + print ' D20-D23: ' + tdbin[19:23] + ' ' + tdbin[23] + print ' D30-D33: ' + tdbin[24:28] + ' ' + tdbin[28] + print ' D40-D43: ' + tdbin[29:33] + ' ' + tdbin[33] + print ' D50-D53: ' + tdbin[34:38] + ' ' + tdbin[38] + print ' D60-D63: ' + tdbin[39:43] + ' ' + tdbin[43] + print ' D70-D73: ' + tdbin[44:48] + ' ' + tdbin[48] + print ' D80-D83: ' + tdbin[49:53] + ' ' + tdbin[53] + print ' D90-D93: ' + tdbin[54:58] + ' ' + tdbin[58] + print ' ' + tdbin[59:63] + ' ' + tdbin[63] + ' Column Parity & Stop Bit' + # reconstruct data bytes + d0= chr(int(tdbin[9:13] + tdbin[14:18],2)) + d1= chr(int(tdbin[19:23] + tdbin[24:28],2)) + d2= chr(int(tdbin[29:33] + tdbin[34:38],2)) + d3= chr(int(tdbin[39:43] + tdbin[44:48],2)) + d4= chr(int(tdbin[49:53] + tdbin[54:58],2)) + print + print ' Reconstructed data D00-D93 (UNIQUE ID): ', + card.HexPrint(d0 + d1 + d2 + d3 + d4) + # set ID to Q5ID so block reading works + ID= Q5ID + print + else: + print ' Native - UNIQUE ID: ' + card.EMToUnique(ID) + +sector = 0 +print +print ' Writing...' +print +while sector < card.LFXTagBlocks[card.tagtype]: + print ' sector %02x: ' % sector, + if card.readblock(sector): + print card.data, + if not card.writeblock(sector,card.data): + print 'Write failed' + else: + print 'OK' + else: + print 'Read error: ' + card.errorcode + sector += 1 +print + +# set reader back to all cards +card.settagtype(card.ALL) +card.select() +print +os._exit(False) + diff --git a/writemifare1k.py b/writemifare1k.py new file mode 100755 index 0000000..156aa17 --- /dev/null +++ b/writemifare1k.py @@ -0,0 +1,72 @@ +#!/usr/bin/python + +# writemifare1k.py - write all blocks on a mifare standard tag +# +# Adam Laurie +# http://rfidiot.org/ +# +# This code is copyright (c) Adam Laurie, 2006, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# + + +import RFIDIOtconfig +import sys +import random +import string +import os + +try: + card= RFIDIOtconfig.card +except: + os._exit(True) + +args= RFIDIOtconfig.args +help= RFIDIOtconfig.help + +card.info('writemifare1k v0.1e') +card.select() +print 'Card ID: ' + card.uid +while True: + x= string.upper(raw_input('\n*** Warning! This will overwrite all data blocks! Proceed (y/n)? ')) + if x == 'N': + os._exit(False) + if x == 'Y': + break + +sector = 1 +while sector < 0x10: + for type in ['AA', 'BB', 'FF']: + card.select() + print ' sector %02x: Keytype: %s' % (sector, type), + if card.login(sector,type,'FFFFFFFFFFFF'): + for block in range(3): + print '\n block %02x: ' % ((sector * 4) + block), + if len(args) == 1: + data= args[0] + else: + data = '%032x' % random.getrandbits(128) + print 'Data: ' + data, + if card.writeblock((sector * 4) + block,data): + print ' OK' + elif card.errorcode: + print 'error %s %s' % (card.errorcode , card.ISO7816ErrorCodes[card.errorcode]) + elif type == 'FF': + print 'login failed' + print '\r', + sys.stdout.flush() + sector += 1 + print +print +os._exit(False)